/******************************************************************************* * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text of * such license is available at www.eclipse.org. ******************************************************************************/ package org.eclipse.buckminster.ui.prefs; import org.eclipse.buckminster.ui.Messages; import org.eclipse.core.runtime.Assert; import org.eclipse.equinox.security.storage.ISecurePreferences; import org.eclipse.equinox.security.storage.SecurePreferencesFactory; import org.eclipse.equinox.security.storage.StorageException; import org.eclipse.jface.preference.StringFieldEditor; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Text; /** * A field editor for a password type preference. The key ring is used for * storing the editor value. * * @author Karel Brezina * */ public class PasswordFieldEditor extends StringFieldEditor { // fake URL - need it for saving to the key ring private final static String BUCKMINSTER_NODE = "buckminster"; //$NON-NLS-1$ private String keyRingRealm; private Text textField; /** * Text limit of text field in characters; initially unlimited. */ private int textLimit = UNLIMITED; /** * The validation strategy; <code>VALIDATE_ON_KEY_STROKE</code> by default. */ private int validateStrategy = VALIDATE_ON_KEY_STROKE; /** * Creates a password field editor of unlimited width. Use the method * <code>setTextLimit</code> to limit the text. * * @param name * the name of the preference this field editor works on * @param labelText * the label text of the field editor * @param parent * the parent of the field editor's control */ public PasswordFieldEditor(String name, String labelText, Composite parent, String keyRingRealm) { this(name, labelText, UNLIMITED, parent, keyRingRealm); } /** * Creates a password field editor. Use the method <code>setTextLimit</code> * to limit the text. * * @param name * the name of the preference this field editor works on * @param labelText * the label text of the field editor * @param width * the width of the text input field in characters, or * <code>UNLIMITED</code> for no limit * @param parent * the parent of the field editor's control */ public PasswordFieldEditor(String name, String labelText, int width, Composite parent, String keyRingRealm) { this(name, labelText, width, VALIDATE_ON_KEY_STROKE, parent, keyRingRealm); } /** * Creates a password field editor. Use the method <code>setTextLimit</code> * to limit the text. * * @param name * the name of the preference this field editor works on * @param labelText * the label text of the field editor * @param width * the width of the text input field in characters, or * <code>UNLIMITED</code> for no limit * @param strategy * either <code>VALIDATE_ON_KEY_STROKE</code> to perform on the * fly checking (the default), or * <code>VALIDATE_ON_FOCUS_LOST</code> to perform validation only * after the text has been typed in * @param parent * the parent of the field editor's control * @since 2.0 */ public PasswordFieldEditor(String name, String labelText, int width, int strategy, Composite parent, String keyRingRealm) { super(name, labelText, width, strategy, parent); this.keyRingRealm = keyRingRealm; } /** * Returns the field editor's value. * * @return the current value */ @Override public String getStringValue() { if (textField != null) { return textField.getText(); } return getPasswordFromKeyRing(); } @Override public Text getTextControl(Composite parent) { if (textField == null) { textField = new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.PASSWORD); textField.setFont(parent.getFont()); switch (validateStrategy) { case VALIDATE_ON_KEY_STROKE: textField.addKeyListener(new KeyAdapter() { /* * (non-Javadoc) * * @see * org.eclipse.swt.events.KeyAdapter#keyReleased(org * .eclipse.swt.events.KeyEvent) */ @Override public void keyReleased(KeyEvent e) { valueChanged(); } }); break; case VALIDATE_ON_FOCUS_LOST: textField.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { clearErrorMessage(); } }); textField.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { refreshValidState(); } @Override public void focusLost(FocusEvent e) { valueChanged(); clearErrorMessage(); } }); break; default: Assert.isTrue(false, "Unknown validate strategy");//$NON-NLS-1$ } textField.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent event) { textField = null; } }); if (textLimit > 0) {// Only set limits above 0 - see SWT spec textField.setTextLimit(textLimit); } } else { checkParent(textField, parent); } return textField; } /** * Sets this text field's text limit. * * @param limit * the limit on the number of character in the text input field, * or <code>UNLIMITED</code> for no limit */ @Override public void setTextLimit(int limit) { textLimit = limit; if (textField != null) { textField.setTextLimit(limit); } } /** * Sets the strategy for validating the text. * <p> * Calling this method has no effect after <code>createPartControl</code> is * called. Thus this method is really only useful for subclasses to call in * their constructor. However, it has public visibility for backward * compatibility. * </p> * * @param value * either <code>VALIDATE_ON_KEY_STROKE</code> to perform on the * fly checking (the default), or * <code>VALIDATE_ON_FOCUS_LOST</code> to perform validation only * after the text has been typed in */ @Override public void setValidateStrategy(int value) { Assert.isTrue(value == VALIDATE_ON_FOCUS_LOST || value == VALIDATE_ON_KEY_STROKE); validateStrategy = value; } /* * (non-Javadoc) Method declared on FieldEditor. */ @Override protected void doLoad() { if (textField != null) { String value = getPasswordFromKeyRing(); textField.setText(value); oldValue = value; } } /* * (non-Javadoc) Method declared on FieldEditor. */ @Override protected void doLoadDefault() { if (textField != null) { String value = ""; //$NON-NLS-1$ textField.setText(value); } valueChanged(); } /* * (non-Javadoc) Method declared on FieldEditor. */ @Override protected void doStore() { setPasswordToKeyRing(textField.getText()); } /** * Informs this field editor's listener, if it has one, about a change to * the value (<code>VALUE</code> property) provided that the old and new * values are different. * <p> * This hook is <em>not</em> called when the text is initialized (or reset * to the default value) from the preference store. * </p> */ @Override protected void valueChanged() { setPresentsDefaultValue(false); boolean oldState = isValid(); refreshValidState(); if (isValid() != oldState) { fireStateChanged(IS_VALID, oldState, isValid()); } String newValue = textField.getText(); if (!newValue.equals(oldValue)) { fireValueChanged(VALUE, oldValue, newValue); oldValue = newValue; } } private String getPasswordFromKeyRing() { ISecurePreferences info = SecurePreferencesFactory.getDefault().node(BUCKMINSTER_NODE).node(keyRingRealm); try { return info.get(getPreferenceName(), ""); //$NON-NLS-1$ } catch (StorageException e) { return ""; //$NON-NLS-1$ } } private void setPasswordToKeyRing(String password) { ISecurePreferences info = SecurePreferencesFactory.getDefault().node(BUCKMINSTER_NODE).node(keyRingRealm); try { info.put(getPreferenceName(), password, true); info.flush(); } catch (Exception e) { throw new RuntimeException(Messages.cannot_save_password, e); } } }