| Online Eiffel Documentation

EiffelStudio

# Initialization

This document details how to setup preferences for your application.

## Initializating the preferences

The first thing you must do to setup preferences for your application is create a PREFERENCES object. Ideally any access to preference information should be done through this object. There are 3 creation routines available to initialize the preferences:

- make
- make_with_location
- make_with_defaults_and_location

The first and most simple option (make) will create a PREFERENCE object for you with no specific underlying datastore location, and no default preference values. In this scenario an underlying data store location will be created for you automatically based on the current system settings. For example, if you are using the Windows Registry as your data store, and your program is is called my_program.exe, then a registry key will be created in HKEY_CURRENT_USER\Software\myprogram.exe. In between user sessions the preference name-value pairs will be stored in this key location. This particular method of creating preferences is ideal if you are not particularly concerned where the values are stored, and if you do not need to specify any default values for the preferences. You may think of it as the "it just works" mode, and it is ideal for development and simple use.

The second option (make_with_location) will take a location value to use for the underlying data store. Values will be stored to and retrieved from this location between sessions. As with make no defaults values are provided.

The final option make_with_defaults_and_location is similar to make_with_location except an additional parameter is given to retrieve one or more default file locations. This will be one or more XML files on disk containing the default values to use for some or all of your preferences. It is a convenient way to initialize your application with all the default values required `out of the box' for correct or preferred functioning. Those files also contain additional attributes for preference configuration such as more detailed descriptions of the preference. If two files list the same preference, the last one to mention it takes precedence.

The format of the XML default file is very simple:

    <EIFFEL_DOCUMENT>
    	<PREF NAME="some_manager.some_name" DESCRIPTION="Preference description here" HIDDEN="True/False" RESTART="True/False"><TYPE>Value here</TYPE></PREF>
    	...More preferences...
    <EIFFEL_DOCUMENT>

The "HIDDEN" attribute indicates if this preference is to be hidden from view and is used for preferences that you do not wish for your users to modify or see. The "RESTART" attribute allows for you to flag that when a particular preference is changed it may be necessary to restart the application for the changes to take effect. You can handle this in your application by querying for this value in the preference and dispalying appropriate dialogs and messages for your application.

## Managers

Once you have created a PREFERENCES object using one of the creation routines described above you will create a PREFERENCE_MANAGER to store related preference values. For example, if your application is a web browser control that has preferences for user favorites and also a user browsing history you could create 2 managers, one for the favorites and one for the history. It is not mandatory that you create 2 managers and you could just create one and have all preferences stored therein, but for reasons of management 2 would be better. Each separate manager must have a unique name to distinguish it from the other managers in the system. We may have one called "favorites" and one called "history".

## Creating preferences

Now you have your preferences and at least one manager to put them in you can create the actual preferences. Preferences are generic, and all preferences inherit the class TYPED_PREFERENCE [G]. G denotes the type of the preference, for example INTEGER for a preference containing an integer value. Depending on the type you will use a factory class to actually create the new preference. By default the library supports 6 types of preferences, 4 basic type ones and 2 graphical types. The table below clearly indicates the currently available preferences and the factory class you should use to create them:

Preference type | Description | Factory class | Factory method
BOOLEAN_PREFERENCE | Stores boolean value (true or false) | BASIC_PREFERENCE_FACTORY | new_boolean_resource_value
INTEGER_PREFERENCE | Stores an integer value | BASIC_PREFERENCE_FACTORY | new_integer_resource_value
STRING_PREFERENCE | Stores a string value | BASIC_PREFERENCE_FACTORY | new_string_resource_value
ARRAY_PREFERENCE | Stores a list of string values | BASIC_PREFERENCE_FACTORY | new_array_resource_value
COLOR_PREFERENCE | Stores a color value (rgb) | GRAPHICAL_PREFERENCE_FACTORY | new_color_resource_value
FONT_PREFERENCE | Stores a font value (font name, face, height, etc) | GRAPHICAL_PREFERENCE_FACTORY | new_font_resource_value

As you can see creating preferences is very easy. Also you will notice that color and font preferences use the GRAPHICAL_PREFERENCE_FACTORY. If you wish to use these preferences in your application you will need to use EiffelVision2 since these preference values are stored as EV_COLOR and EV_FONT types. If you wish to store preference value that are different to those offered by the 6 available preferences it also very easy to extend the design and add your own custom types. Simply inherit TYPED_PREFERENCE [G] and implement the deferred features for your specific preference. There is an example of this in the sample application.

## Default Values

When you create a preference using a factory class you will provide a manager, a name for the preference, and a value. For example, in BASIC_PREFERENCE_FACTORY you create an integer preference by calling new_integer_resource_value:

    	new_integer_resource_value (a_manager: PREFERENCE_MANAGER; a_name: STRING; a_fallback_value: INTEGER): INTEGER_PREFERENCE is
    			-- Add a new integer resource with name `a_name'.  If preference cannot be found in
    			-- underlying datastore or in a default values then `a_fallback_value' is used for the value.
    		require
    			name_valid: a_name /= Void
    			name_not_empty: not a_name.is_empty
    			not_has_resource: not a_manager.known_resource (a_name)
    		ensure
    			has_result: Result /= Void
    			resource_name_set: Result.name.is_equal (a_name)
    			resource_added: a_manager.preferences.has_resource (a_name)
    		end

An appropriate example in code of this could be:

    window_width_preference: INTEGER_PREFERENCE
    		-- Preference holding value for width of application main window

    initialize_my_preferences is
    		-- Initialize the application preferences
    	local
    		factory: BASIC_PREFERENCE_FACTORY
    	do
    		create factory
    		window_width_preference := factory.new_integer_resource_value (my_manager, "window_width", 480)
    	end

This will create a new preference, which you can then use in your application to get, set and save the corresponding value when necessary. The issue to be aware of here though involves the value that the preference will contain when it is created. You see in the code above we pass the integer value 480. This does not mean, however, the initial value of the preference will be 480. This may sound odd, so let me explain...

The value of a preference when initialized is determined by a number of factors. The first of these is the underlying data store value. If a preference value was changed in a previous session, by the user or by the application directly, and was saved to the underlying data store, then this value will be given priority. This makes sense, since if a user changes their preferences they don't want to have to do it every time they use your program. So, if they want the default window width to be larger, say 600, then this will be the value of the preference named "window_width" when initialized next time. Following this, if there is no previously saved value then the library will look for a default value to use. If a default file was given when the preferences were created (see above), and this default specifies a default value of 240 for the integer preference called "window_width", then this will be used. Finally, if no preference value was previously stored and no value is provided as a default value then the supplied value in the code is used - our 480 value from the example above. Although this process may seem confusing it is infact very simple and intuitive way to initialze the preferences. The process chart below illustrates more clearly the various permutations.

## Using and Manipulating Preferences

Now you have preferences created you may use them from your application. Using the example preference above, window_width_preference, you can query the value of the preferences by simply querying the preference directly:

    window_width := window_width_preference.value

Or for a value which should always be associated to the preference:

    window_width: INTEGER is
    		-- Width of window
    	do
    		Result := window_width_preference.value
    	end

If you need to react when a preference value is changed you can hook up an agent to the change_actions of the preference:

    window_width_preference.change_actions.extend (agent my_agent_routine)

To manually set the value of the preference call set_value or set_value_from_string, and to set a value for the default value call set_default_value. To reset a preference value back to it's original default value use reset.

To save the current preference to the underlying data store you must call save_resource on the PREFERENCES object. This will persist the value to the data store for retrieval in the next session. Remeber, if you change a preference value during execution and do not save it then the value will be lost when execution has finished. The preference window interface provided with the library will allow users to set theie own preference values and will save the values upon confirmation. However, if you re using preferences in your code and do not wish to provide an interface for preference modification you must remeber to manually save the preferences. You can save all preferences at once my calling save_resources in PREFERENCES, or individually with save_resource.