Copyright 2001 Object Technology International, Inc.
 Eclipse Corner Article

Preferences in the Eclipse Workbench UI

Summary
In the Eclipse Platform plug-in developers define preference pages for their plug-ins for use in the Workbench Preferences Dialog. This article explains when to use a preference and some of the features the Eclipse Platform provides to support preferences.

By Tod Creasey, OTI
December 15, 2001


Introduction

The Eclipse Platform has support for user defined preferences that are persisted along with the workbench. This article will discuss what type of data should be stored as a preference and will show how to develop and register a user interface to allow the user to set these preferences. It will also cover how to initialize and retrieve preferences for use by other other plug-ins that use your plug-in. This functionality will be shown using an example that searches documents for bad words. We will set our preferences for this tool using two preference pages, one simple one to set a highlight color and one more complex one to set the list of words.

When to Use a Preference

A preference is data that is persisted between workbench sessions to allow the user to keep the state of a plug-in consistent between Eclipse sessions. Typical preferences are default values for new instances, colors for editors and paths, or processing information for operations performed on data (such as a build path for a Java™ file).

Preferences are not intended to reference any resource currently defined in the workspace and instead should be used for editors, views or other objects that perform operations on a resource. Data persisted on a resource instance is better suited to be a property which will be discussed in a later article.

A preference can be made available to any plug-in that has your plug-in as a prerequisite. The usual way to do that is to provide API on your plug-in that allows for access to the preferences you want to make available. The values of these preferences are stored in the .metadata/.plugins directory of the workbench on a per plug-in basis. We demonstrate how to do this below.

The Preference Store and the Plug-in

Every UI plug-in has it's own preference store provided by the workbench. For this example we will define a plug-in and use its preference store for our preferences. As we are going to use this plug-in within the UI we define it as a subclass of AbstractUIPlugin. Our constructor (see ) will create a singleton to allow easy access to the plug-in instance in the workbench. We also implement the method initializeDefaultPreferences() to set up our default values for our two preferences. We are defining a preference for the bad words and a preference for the color of the highlight. Each preference value is looked up using a given key. In the code below the keys we are using are defined by the constants in .

The default value should be set for all preferences to be sure that there is a value to use at all times. A default value also ensures that the UI can provide a way to reset a preference value back to a reasonable initial setting via the Restore Defaults button.The default value of the preference should be initialized in the plug-in so that it is set before any of the UI is created. 

IAbstractWorkbenchPlugin defines a method called initializeDefaultPreferences(IPreferenceStore) which is called when the preference store is created the first time. In this method (see ) you should set the default value for all values that you will be using the preference store for. We set a default color using the helper methods in the PreferenceConverter which allows the plug-in developer to set and get values for a preference of commonly stored types like FontData, Point etc. This API is provided because preferences are stored and retrieved as Strings in a human readable format in order to leverage the java properties mechanism.  Our more complex bad words preference is initialized using a set of preselected bad words defined in the format we are going to store them in as we do not have API on the PreferenceConvertor to store or retreive arrays of Strings.

Color color= Display.getDefault().getSystemColor(SWT.COLOR_BLUE);
PreferenceConverter.setDefault(store, HIGHLIGHT_PREFERENCE, color.getRGB());
public class BadWordCheckerPlugin extends AbstractUIPlugin {
	//The shared instance.
	private static BadWordCheckerPlugin plugin;

	//The identifiers for the preferences	
	public static final String BAD_WORDS_PREFERENCE =
		"org.eclipse.ui.articles.badwordchecker.badwords";
	public static final String HIGHLIGHT_PREFERENCE =
		"org.eclipse.ui.articles.badwordchecker.highlight";

	//The default values for the preferences
	public static final String DEFAULT_BAD_WORDS = "bug;bogus;hack;";
	public static final int DEFAULT_HIGHLIGHT = SWT.COLOR_BLUE;

	/**
	 * The constructor.
	 */
	public BadWordCheckerPlugin(IPluginDescriptor descriptor) {
		super(descriptor);
		plugin = this;
	}

	/**
	 * Returns the shared instance.
	 */
	public static BadWordCheckerPlugin getDefault() {
		return plugin;
	}

	/** 
	 * Initializes a preference store with default preference values 
	 * for this plug-in.
	 * @param store the preference store to fill
	 */
	protected void initializeDefaultPreferences(IPreferenceStore store) {
		store.setDefault(BAD_WORDS_PREFERENCE, DEFAULT_BAD_WORDS);
		Color color= Display.getDefault().getSystemColor(DEFAULT_HIGHLIGHT);
		PreferenceConverter.setDefault(store,  HIGHLIGHT_PREFERENCE, color.getRGB());

	}
}

Defining Preference Pages in plugin.xml

Now that we have defined the preference we want to provide a way for the user to set the preference value. Preference pages for the workbench can be found in the preferences dialog. The preferences dialog is accessible via the Window->Preferences menu group. Plug-in developers should add their preference pages to this dialog using the plugin.xml of their plug-in in order to maintain a consistent look and feel with other Eclipse plug-ins. The definition of the preference pages within plugin.xml looks like this:

<extension point="org.eclipse.ui.preferencePages">
 	<page id="org.eclipse.ui.articles.BadWordsPreference"
 		name="Bad Words"
    		class="org.eclipse.ui.articles.badwordchecker.BadWordsPreferencePage">
   	</page>
   	
 	<page id="org.eclipse.ui.articles.BadWordsColorPreference"
  	      	name="Colors"
	      	class="org.eclipse.ui.articles.badwordchecker.BadWordsColorPreferencePage"
		category="org.eclipse.ui.articles.BadWordsPreference">
   	</page>
</extension>

The definition above sets the name () of the preference page for use in the list of pages in the preference dialog and also specifies the class() to be instantiated for creating the preference page. This class must conform to IWorkbenchPreferencePage.

In the second definition there is a category () tag which is used to make one page the child of another in the list in the preferences dialog. Preference pages can be stored as the children of other pages. This is useful for keeping a series of pages together that are related to each other and also reduces the clutter in the workbench preferences page. A page can be made the child of another page by setting the id of the parent page as the value of the category field in the plugin.xml. A page with no parent is displayed as a child with no root.

With the above declarations in our plugin.xml the list of preference pages shown in the preference dialog will look like:

The Preference Dialog Tree with the Bad Words and Color Page Added

The Color Preference Page

The color preference page is an example of a simple page that uses a single JFace field editor to manage its values. Initially a preference page class is defined.  All classes used in the preference dialog must conform to IWorkbenchPreferencePage. Eclipse includes the class PreferencePage which implements most of the necessary API for a preference page. The class definition for our preference page is:

BadWordsColorPreferencePage
	extends PreferencePage
	implements IWorkbenchPreferencePage

Once we have defined the page we want to initialize it.  IWorkbenchPreferencePage specifies a message init(IWorkbench) for this purpose. We will not use the Workbench argument for this page. Our implementation only sets the preference store for the page.

public void init(IWorkbench workbench) {
	//Initialize the preference store we wish to use
	setPreferenceStore(BadWordCheckerPlugin.getDefault().getPreferenceStore());
}

The other required method we must implement is createContents(). All we are going to do is use a ColorFieldEditor to set our preference. It is also suggested that performDefaults is implemented so that the current state can be reset to the defaults defined in the plug-in. We also need to implement performOK so that the settings defined by the user are stored in the preference store for our plug-in. Our implementation is simple as the ColorFieldEditor has the code to load defaults and store the results of an apply for a preference already defined and performOK and performDefaults can call the corresponding methods on ColorFieldEditor.

/**
 * Performs special processing when this page's Restore Defaults button has 
 * been pressed.
 * Sets the contents of the color field to the default value in the preference
 * store.
 */
protected void performDefaults() {
	colorEditor.loadDefault();
}
/** 
 * Method declared on IPreferencePage. Save the
 * color preference to the preference store.
 */
public boolean performOk() {
	colorEditor.store();
	return super.performOk();
}

 

The Color Preference Page

The Bad Words Preference Page

We have seen how to do a simple preference page with just a color and categorize it. Now we will show how to use a complex object as a preference and still have it persisted by the preference store and editable in a preference page. For this example we are going to add a bad words preference which is an array of Strings.

As the PreferenceConverter does not have API for conversion of arrays of Strings we will implement it ourselves in the BadWordCheckerPlugin. By implementing it in the plug-in we put the API for the use of the preference in a place visible to all objects that have access to this plug-in. Normally we would use the PreferenceConverter for conversion to and from the storage format.

Methods for getting the default value of the preference and a getter and a setter are defined first - getBadWordsDefaultPreference (which returns an array of Strings)  getBadWordsPreference (which also returns an array of Strings) and setBadWordsPreference which takes an array of Strings as its argument. The String array is stored in the preference store as a single string separated by semicolons. We choose semicolons as this are only ever used as punctuation and will therefore never be part of a word we are searching for.

/**
 * Return the bad words preference default
 * as an array of Strings.
 * @return String[]
 */
public String[] getDefaultBadWordsPreference(){
	return convert(getPreferenceStore().getDefaultString(BAD_WORDS_PREFERENCE));
}

/**
 * Return the bad words preference as an array of
 * Strings.
 * @return String[]
 */
public String[] getBadWordsPreference() {
	return convert(getPreferenceStore().getString(BAD_WORDS_PREFERENCE));
}
	
/**
 * Convert the supplied PREFERENCE_DELIMITER delimited
 * String to a String array.
 * @return String[]
 */
private String[] convert(String preferenceValue) {
	StringTokenizer tokenizer =
		new StringTokenizer(preferenceValue, PREFERENCE_DELIMITER);
	int tokenCount = tokenizer.countTokens();
	String[] elements = new String[tokenCount];
		for (int i = 0; i < tokenCount; i++) {
			elements[i] = tokenizer.nextToken();
	}

	return elements;
}

/**
 * Set the bad words preference
 * @param String [] elements - the Strings to be 
 * 	converted to the preference value
 */
public void setBadWordsPreference(String[] elements) {
	StringBuffer buffer = new StringBuffer();
	for (int i = 0; i < elements.length; i++) {
		buffer.append(elements[i]);
		buffer.append(PREFERENCE_DELIMITER);
	}
	getPreferenceStore().setValue(BAD_WORDS_PREFERENCE, buffer.toString());
}

There is no field editor defined in JFace for  editing String arrays so we will define a list that shows the items with widgets to add and remove them. Our performOK method will send the current contents of the list to the setBadWordsPreference method and the performDefaults method will reset the list of strings to be the result of getDefaultBadWordsPreference from BadWordCheckerPlugin. As a List widget takes an array of Strings as its content we can use the results of these helper methods directly in conjunction with the methods we defined for the bad words preference in the plug-in. The performOK and performDefaults for this preference page use these methods to update the preference and reset the values in the list widget.

/**
 * Performs special processing when this page's Restore Defaults button has been pressed.
 * Sets the contents of the nameEntry field to
 * be the default 
 */
protected void performDefaults() {
	badWordList.setItems(BadWordCheckerPlugin.getDefault().getDefaultBadWordsPreference());
}
/** 
 * Method declared on IPreferencePage. Save the
 * author name to the preference store.
 */
public boolean performOk() {
	BadWordCheckerPlugin.getDefault().setBadWordsPreference(badWordList.getItems());
	return super.performOk();
}

The Bad Word Preference Dialog

Conclusions

In this article we have demonstrated how to use the preferences store and preferences pages provided by Eclipse to allow a plug-in to maintain and update preferences between Eclipse sessions. By use of the preference store in conjunction with the preferences dialog and provided field editors a plug-in developer can quickly put together a user interface for managing preferences. To find out more about the preferences that Eclipse provides see the Platform Plug-in Developers Guide in the Help Perspective. The help information is in the Programmers Guide Preferences and Properties section.

The full implementation of the example in this article can be found in preferences.zip.

Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.