Previous Page
Next Page

12.2. Preference Page APIs

Before modifing the preference page to suit our purposes, examine what was generated by the wizard (see Figure 12-4). The plug-in manifest contains the name, identifier, and fully qualified name of the class defining the page's content and behavior, as listed in the previous section. Preference pages must implement the org.eclipse.ui.IWorkbenchPreferencePage interface, and the abstract classes, org.eclipse.jface.preference.PreferencePage and org.eclipse.jface.preference.FieldEditorPreferencePage, provide much of the infrastructure for that purpose.

Figure 12-4. Preference page declaration, classes, and presentation.


In addition to the FavoritesPreferencePage class, the wizard also created two additional classes, PreferenceConstants and PreferenceInitializer, which will be dicussed later in this chapter.

12.2.1. FieldEditorPreferencePage

The preference page extends FieldEditorPreferencePage, which, along with the various editor classes in the org.eclipse.jface.preference.* package, provide a quick and easy way to present and capture simple preferences. Subclasses of FieldEditorPreferencePage need only to implement the createFieldEditors() and init() methods to display a simple preference page; however, the following are several other methods of which you need to be aware for more involved preference pages.

addField(FieldEditor) Called from the createFieldEditors() method to add a field to the page.

checkState() Called by FieldEditorPreferencePage to validate the page content. The FieldEditorPreferencePage implementation of this method asks each field to validate its content and calls setValid() with the result. Override this method to perform additional page validation.

createContents(Composite) Creates the composite in which field editors appear. Typically, subclasses override the createFieldEditors() method instead.

createFieldEditors() Creates the field editors that appear in the preference page. Subclasses should call getFieldEditorParent() and addField() once for each field created. The parent returned by getFieldEditorParent() should not be used for more than one editor as the parent may change for each field editor depending on the layout style of the page.

dispose() Cleans up any native resources allocated by this page. Typically, there is no need to override this method because the FieldEditorPreferencePage implementation of dispose() handles cleanup for all fields.

getFieldEditorParent() Returns the parent to be used when creating a field editor. The parent returned should not be used for more than one editor as the parent may change for each field editor depending on the page's layout style.

initialize() Called by createContents() after the fields have been created to initialize the field contents. Typically, there is no need to override this method because FieldEditorPreferencePage asks each field to initialize itself.

isValid() Returns whether the contents of this preference page are currently valid.

performDefaults() Loads all fields with their default values. Typically, there is no need to override this method because FieldEditorPreferencePage asks each field to reset its content to its default value.

performOk() Saves the field editor values in the preferences store. Typically, there is no need to override this method because FieldEditorPreferencePage asks each field to save its contents.

setValid(boolean) Sets whether the contents of this preference page are currently valid.

setVisible(boolean) Called to show or hide the page. Subclasses may extend this method.

12.2.2. Field editors

A field editor is designed to load, display, edit, and save a particular preference setting. The org.eclipse.jface.preference package provides many different field editors, some of which have already been discussed. Some editors contain a single control, while others contain several. Every editor has FieldEditor as its common superclass, providing FieldEditorPreferencePage with a common way to access editors. Table 12-1 displays a list of the field editors in the org.eclipse.jface.preference package, plus others available as public APIs elsewhere throughout Eclipse.

Table 12-1. PreferencePage Field Editors

Field Editor

Description

BooleanFieldEditor

A checkbox representation of a Boolean.

ColorFieldEditor

A label and button where the button displays the color preference and opens a color chooser when clicked.

DirectoryFieldEditor

A label, text field, and button for choosing a directory. The button opens a directory chooser when clicked.

FileFieldEditor

A label, text field, and button for selecting a file preference. The button opens a file chooser when clicked. The editor can optionally enforce an absolute file path and filter against specific file extensions.

FontFieldEditor

A label, font name, and button for selecting a font. The button opens a font chooser when clicked.

IntegerFieldEditor

A label and text field for selecting an integer. This editor can optionally enforce a value within a range.

PathEditor

A label, list, and group of buttons for selecting zero or more paths. The New... button opens a directory chooser, while the other buttons manipulate paths already in the list.

RadioGroupFieldEditor

A label and series of radio buttons for selecting one of several properties. Optionally, the radio buttons can be grouped and displayed in multiple columns.

ScaleFieldEditor

A label and slider for selecting a range of integer values.

StringFieldEditor

A label and text field for entering a string value.


Field editors are designed around the concept of create them and forget them. In other words, you create a field editor with all that it needs to know about the preferences it is to represent, and then the field editor, in combination with the FieldEditorPreferencePage, handles the rest.

Field editors excel at presenting and manipulating simple types of preferences such as strings, integers, colors, and so on. If your preferences lend themselves to simple values such as these, then field editors will save you the hassle of writing code to load, display, validate, and store these simple preferences. If the data you wish to present is more structured and complex, then you may need to build your preference page without field editors, subclassing PreferencePage rather than FieldEditorPreferencePage. If you need to interact with a field editor directly or to create a new type of field editor, here are some of the field editor methods you might need to know:

adjustForNumColumns(int) Adjusts the horizontal span of the field editor's basic controls.

dispose() Cleans up any native resources allocated by this editor.

doFillIntoGrid(Composite, int) Creates the controls comprising the editor.

doLoad() Initializes the editor content with the current value from the preferences store.

doLoadDefault() Initializes the editor content with the default value.

doStore() Saves the current editor value into the preferences store.

fireStateChanged(String, boolean, boolean) Informs the field editor's listener, if it has one, about a change to Boolean-valued properties. Does nothing if the old and new values are the same.

fireValueChanged(String, Object, Object) Informs the field editor's listener, if it has one, about a change to a property.

getLabelControl() Returns the label that is part of the editor or null if none.

getLabelControl(Composite) Returns the label that is part of the editor. Creates the label if label text has been specified either in the constructor or the setLabelText() method.

getLabelText() Returns the label text specified either in the constructor or the setLabelText() method.

getNumberOfControls() Returns the number of controls comprising the editor. This value is passed to the doFillIntoGrid(Composite, int) method.

getPreferenceName() Returns the name/key of the preference displayed by the editor.

getPreferenceStore() Returns the preferences store containing the preference being edited.

isValid() Returns whether the editor's contents are valid. Subclasses should override this method along with the presentsDefaultValue() method.

load() Loads the current value from the preferences store into the editor. Subclasses should override the doLoad() method rather than this method.

loadDefault() Loads the default value into the editor. Subclasses should override the doLoadDefault() method rather than this method.

presentsDefaultValue() Returns whether the editor is currently displaying the default value.

refreshValidState() Determines whether the editor's content is valid. Subclasses should override this method to perform the validation and the isValid() method to return the state.

setFocus() Sets focus to the editor. Subclasses may override this method to set focus to a particular control within the editor.

setLabelText(String) Sets the text to appear in the label associated with the editor.

setPreferenceName(String) Sets the name of the preference being displayed by the editor.

setPreferenceStore(IPreferenceStore) Sets the preferences store in which the editor's value is saved.

setPresentsDefaultValue(boolean) Sets whether the editor is displaying the default value.

setPropertyChangeListener(IPropertyChangeListener) Sets the property change listener that should be notified via the fireStateChanged() or fireValueChanged() methods when the editor's content has changed.

showErrorMessage(String) Convenient method for displaying an error message at the top of the preference page.

showMessage(String) Convenient method for displaying a message at the top of the preference page.

store() Saves the editor's current value into the preferences store. Subclasses should override doStore() rather than this method.

12.2.3. PreferencePage

FieldEditorPreferencePage assumes that all the preferences on the page are field editors and handles most of the work involved in loading, validating, and saving field editor content. For more complex preference pages, you can use PreferencePage, which is the superclass of FieldEditorPreferencePage, instead. The downside is that you must do more of the work yourself.

createContents(Composite) Creates the controls for the preference page.

doGetPreferenceStore() Answers a page-specific preferences store or null to use the container's preferences store. Subclasses may override this method as necessary.

getPreferenceStore() Answers the preferences store for this preference page.

isValid() Returns whether the contents of the preference page are currently valid.

performDefaults() Loads all fields with their default values.

performOk() Saves all field values in the preferences store.

setErrorMessage(String) Used to display an error message at the top of the preference page when a field's value is invalid.

setMessage(String, int) Used to display a message at the top of the preference page.

setValid(boolean) Sets whether the contents of the preference page are currently valid.

If you use PreferencePage, you can still use the various types of field editors, but you must do more of the workloading, validating, and saving valuesyourself. The extra work involves adding some method calls when the field editors are constructed; for example:

protected Control createContents(Composite parent) {
   ...
   editor = new BooleanFieldEditor(
      "boolean", "Boolean", parent);
   editor.setPreferencePage(this);
   editor.setPreferenceStore(getPreferenceStore());
   editor.load();
   ...
}

and when the user resets the values to their defaults:

protected void performDefaults() {
   editor.loadDefault();
   ...
   super.performDefaults();
}

and when the user decides to save the current preference value:

public boolean performOk() {
   ...
   editor.store();
   return true;
}

and to perform any additional validation other than what is enforced by the field.

12.2.4. Favorites preference page

For the Favorites view, you need one Boolean preference for every column, indicating whether that column is to be visible in the Favorites view. First, modify the generated PreferenceConstants class to define preference constants that can be shared by various classes in the Favorites product.

public class PreferenceConstants
{
    public static final String
       FAVORITES_VIEW_NAME_COLUMN_VISIBLE =
          "favorites.view.name.column.visible";
    public static final String
       FAVORITES_VIEW_LOCATION_COLUMN_VISIBLE =
          "favorites.view.location.column.visible";
}

The FavoritesPreferencePage is then modified to display these two preferences using Boolean preference field editors.

public class FavoritesPreferencePage
   extends FieldEditorPreferencePage
   implements IWorkbenchPreferencePage
{
   private BooleanFieldEditor namePrefEditor;
   private BooleanFieldEditor locationPrefEditor;

   public FavoritesPreferencePage() {
      super(GRID);
      setPreferenceStore(
         FavoritesPlugin.getDefault().getPreferenceStore());
      setDescription("Favorites view column visibility:");
   }

   public void init(IWorkbench workbench) {
   }

   public void createFieldEditors() {
      namePrefEditor = new BooleanFieldEditor(
         PreferenceConstants.FAVORITES_VIEW_NAME_COLUMN_VISIBLE,
         "Show name column", getFieldEditorParent());
      addField(namePrefEditor);
      locationPrefEditor = new BooleanFieldEditor(
         PreferenceConstants.FAVORITES_VIEW_LOCATION_COLUMN_VISIBLE,
         "Show location column", getFieldEditorParent());
      addField(locationPrefEditor);
   }
}

Now, when the Favorites preference page is displayed, it shows the two-column visibility preferences (see Figure 12-5).

Figure 12-5. Favorites preference page with column visibility.


12.2.5. Validation

The preference page looks good (see Figure 12-5), but there are two problems. First, the visibility for the name and location columns should default to true; that problem is addressed in Section 12.3.3, Specifying default values programmatically, on page 471 and Section 12.3.4, Specifying default values in a file, on page 472. Second, at least one column should be visible at all times. Field editors enforce local validation of their own contents based on the type of editor and the parameters specified during creation. If you want validation between various editors, then you must enforce it yourself in the PreferencePage class by overriding the FieldEditorPreferencePage checkState() method.

protected void checkState() {
   super.checkState();
   if (!isValid())
      return;
   if (!namePrefEditor.getBooleanValue()
      && !locationPrefEditor.getBooleanValue()) {
      setErrorMessage("Must have at least one column visible");
      setValid(false);
   }
   else {
      setErrorMessage(null);
      setValid(true);
   }
}

The FieldEditorPropertyPage listens for FieldEditor.IS_VALID property change events and then calls checkState() and setValid() as necessary. The Boolean field editors are never in an invalid state and thus do not issue FieldEditor.IS_VALID property change events, only FieldEditor.VALUE property change events. You must override the FieldEditorPreferencePage propertyChange() method to call the checkState() method when the FieldEditor.VALUE property change event is received.

public void propertyChange(PropertyChangeEvent event) {
   super.propertyChange(event);
   if (event.getProperty().equals(FieldEditor.VALUE)) {
      if (event.getSource() == namePrefEditor
         || event.getSource() == locationPrefEditor)
         checkState();
   }
}

Now, when both preferences are unchecked, an error message is displayed across the top of the preference page and the Apply and OK buttons are disabled (see Figure 12-6).

Figure 12-6. Favorites preference page with error message.


12.2.6. Nested preference pages

Nested preference pages provide a mechanism for hierarchically organizing related preference pages when a single page is not enough. Typically, the parent page contains root-level preferences or even just information, while the child preference pages focus on specific aspects.

To create a nested preference page in the Favorites product (see Figure 12-7), add a new declaration in the plug-in manifest where the category attribute specifies the parent preference page (see the category attribute in Section 12.1, Creating a Preference Page, on page 451). If Eclipse cannot find a parent page with the specified identifier, the preference page appears at the root level.

<page
   name="Nested Prefs"
   category="com.qualityeclipse.favorites.prefs.view"
   class="com.qualityeclipse.favorites
      .preferences.NestedPreferencePage"
   id="com.qualityeclipse.favorites.prefs.nested"/>

Figure 12-7. Nested preference pages.


Preference pages can be nested any number of levels deep by specifying the identifier for the parent preference page, prefixed by the identifier for the grandparent preference page, prefixed by the identifier for the great-grandparent preference page, and so on, each separated by the '/' character. For instance, to add a Favorites preference page nested two levels deep, the declaration might look like this:

<page
   name="Nested Prefs 2"
   category="com.qualityeclipse.favorites.prefs.view
      /com.qualityeclipse.favorites.prefs.nested"
   class="com.qualityeclipse.favorites
      .preferences.NestedPreferencePage2"
   id="com.qualityeclipse.favorites.prefs.nested2"/>

Tip

The root preference page can contain basic information about the product, while the child preference pages contain the actual preferences. For example, the root preference page in Figure 12-8 contains information about the product, including version and build date, where the product is installed, information about the company producing the product, and buttons for generating email.


Figure 12-8. Root-level preference page.


12.2.7. Tabbed preference pages

Tabbed preference pages are another approach for organizing more preferences than will fit on a page (see Figure 12-9). In this case, tabs across the top of the preference page (see Section 4.2.6.10, Tab folder, on page 164) provide separation between groups of related preferences. The advantage is that tabbed preference pages are located on a single page, and thus one page can handle any interrelated field validation.

Figure 12-9. Tabbed preference page.


The disadvantage is that the FieldEditorPreferencePage cannot be used for this purpose, so you must do more of the work yourself, basing your preference page on the PreferencePage class instead (see Section 12.2.3, PreferencePage, on page 460). Of course, both nested pages and tabbed pages can be used in the same product as needed.


Previous Page
Next Page