12.2. Preference Page APIsBefore 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. FieldEditorPreferencePageThe 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.
12.2.2. Field editorsA 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. 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:
12.2.3. PreferencePage
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 pageFor 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. ValidationThe 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 pagesNested 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 pagesTabbed 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. |