Figure 22-0

Display 22-0

SwatDisplay 22-0

Table 22-0
Figure 22-2 Preferences Buttons
Figure 22-3 PrefTocList in Action
Figure 22-1 Preferences Dialog
Advanced Topic
22Config Library

Not all users are the same; not all computing environments are the same. 
Often some part of an application allows the user to tailor the UI to his 
wishes. The user may wish to specify some preferences at a system level 
rather than on an application level. To do this, the user will look to the 
Preferences Manager application. The config library provides mechanisms 
for writing Preferences modules.

Normally the purpose of a Preferences module's gadgetry is to set up some 
information in the user's .INI file. If you are encouraging other developers to 
take advantage of the configuration done by your module, be sure to 
document the .INI file categories and keys affected. 

There are three main tasks involved in writing a Preferences module:

u	Providing a tree of UI gadgetry which will act as the module's dialog box.

u	Providing information Preferences will use to construct the module's 
button.

u	Providing any underlying functionality other than writing information to 
the .INI file.

The first two routines exported in the module's .gp file will be responsible for 
carrying out the first two of these tasks. The final task is difficult to define-it 
will depend on the nature of your module.

Preferences modules written in C will need one more thing. To indicate that 
the preferences module is compatible with the standard GEOS 2.0 
Preferences Manager applications, the module will need a local.mk makefile 
for compilation, and that local.mk file should include a line that reads:

_PROTO = 2.0

For information about local.mk files, see "Using Tools," Chapter 10 of the 
Tools book.

Before you try to write a Preference module, there are some things you 
should already know. Each module is a library, so be sure to read "Libraries," 
Chapter C of the Concepts Book to find out how to write a GEOS library. To 
use the information your module will write to the .INI file, read the 
Initialization File section of "Applications and Geodes," Chapter 6 of the 
Concepts Book.

	22.1	Providing the UI

There are two steps to providing the UI for your preferences module. The first 
is to declare the objects you will use. The second is to write and export a 
routine which returns the head of the tree of UI gadgetry.

	22.1.1	Designing the UI Tree.

Preferences modules present a dialog box to the user. You will need to design 
the dialog for your module. As you do so, you may find objects of the following 
classes useful. Each of the following classes is based upon one of the Generic 
UI classes.

GenClass provides mechanisms by which objects will read and write values 
to the .INI file. Pref- classes provide further functionality, such as storing an 
"original" value to revert to if the user decides to Cancel their changes. 

The following Pref- classes are available:

PrefDialogClass
This class specializes in acting as the root of a Pref module's UI 
tree. It has been set up to provide "OK" and "Cancel" triggers by 
default. It has a mechanism to restart the system on an Apply 
if the module specifies that such a reset will be necessary to 
implement the user's changes.
Often a module will use a subclass of PrefDialogClass as the 
head of its UI tree-often the subclass intercepts 
MSG_GEN_APPLY to ensure valid user choices and to take other 
appropriate actions.

PrefValueClass
PrefValue objects act like objects of GenValueClass. However, 
these objects have the ability to reset their values to that 
originally stored in the .INI file (in case the user wants to 
cancel). They can handle only word-length values.

PrefItemGroupClass
Objects of this class act like GenItemGroups. In addition, this 
class has the ability to store an "original" value. 

PrefStringItemClass
A PrefItemGroup normally writes data about selected items in 
the form of an integer. However, if its children are 
PrefStringItem objects, the PrefItemGroup can instead write 
out a special string associated with each PrefStringItem, 
resulting in a more readable .INI file.

PrefBooleanGroupClass
This class acts like GenBooleanGroupClass with the ability 
to store an "original" value.

PrefDynamicListClass
Use objects of this class where you might otherwise use a 
GenDynamicList. This class will not work together with 
PrefStringItem objects.

PrefTocListClass
This class presents an alphabetized list of files or driver names. 
It is used to provide lists of devices, such as the list of available 
printers. It can also be used to show a list of files with a given 
token in a given directory.

TitledGlyphClass
This specialized subclass of GenGlyph shows both an icon and 
a text moniker. It is used to present the icon and name at the 
top of a typical Preferences module dialog box.

PrefInteractionClass
This class acts like GenInteractionClass, but also will relay 
certain messages to its children which GenInteraction would 
not. Those objects which will need to work with the .INI file 
should receive these messages, so any Gen- objects which will 
work with the .INI file and any Pref objects should be grouped 
under PrefInteractions instead of GenInteractions.

PrefTextClass
This class behaves like GenTextClass, except that it will load 
and save its value based upon that stored in the .INI file.

PrefTriggerClass
This class acts as does GenTriggerClass, but has an extra 
action, so that two separate messages will be sent when the 
trigger is activated.

PrefControlClass
This class acts as a cross between PrefClass and 
GenControlClass.

PrefTimeDateControlClass
This class allows the user to set the system date and time.

PrefClass	PrefClass is something like GenClass-while never used 
directly, it is the superclass of all the other Pref- classes, and 
sets up several of the mechanisms which all will use.

There aren't any special restrictions on what sorts of objects appear in the UI 
tree of a Preferences module. However, the following rules will prove useful 
in constructing modules that correctly write their data to the .INI file and 
have a look and feel consistent with existing modules. For an example, see 
Code Display 22-1.

u	As a rule of thumb, the top object of the tree should be a PrefDialog.

u	For a consistent look, the upper portion of the dialog should contain the 
module's icon, name, and some brief help text. This is normally done 
using a TitledGlyph and a GenText object. 

u	Generally the easiest way to specify the .INI file category and key to use 
is by means of ATTR_GEN_INIT_FILE_CATEGORY and 
ATTR_GEN_INIT_FILE_KEY. Assuming that the module will work with 
just one category, it is sufficient to declare an ATTR_GEN_INIT_FILE field 
for just the top object of the module's UI tree.

u	Because of the way messages are relayed, you should use 
PrefInteractions instead of GenInteractions if any objects under the 
interaction will be saving data to the .INI file.

Code Display 22-1 Pref Module UI Framework

@object MPMDialogClass MPMRoot = {
	GI_states = @default & ~GS_USABLE;
	GI_comp = @MPMTitleGroup, @MPMOtherStuff;
	HINT_INTERACTION_SINGLE_USAGE;
	HINT_INTERACTION_COMPLEX_PROPERTIES;
	HINT_ORIENT_CHILDREN_VERTICALLY;
	HINT_LEFT_JUSTIFY_CHILDREN;
	ATTR_GEN_HELP_CONTEXT = "myPrefModule";
	ATTR_GEN_INIT_FILE_CATEGORY = "myPref";
}

@object GenInteractionClass MPMTitleGroup = {
	GI_comp = @MPMTitle, @MPMHelp;
	HINT_ORIENT_CHILDREN_HORIZONTALLY;
	HINT_EXPAND_WIDTH_TO_FIT_PARENT;
}

@object TitledGlyphClass MPMTitle = {
	GI_visMoniker = list {
		@FontTextMoniker, @FontLCMoniker, @FontLMMoniker, @FontLCGAMoniker
 	}
}

@object GenTextClass MPMHelp = {
	GI_attrs = @default | GA_READ_ONLY;
	GTXI_text = "Do such and such to configure your so and so.";
	HINT_EXPAND_WIDTH_TO_FIT_PARENT; 
	/* Might want HINT_MINIMUM_SIZE */
}



	22.1.2	UI Fetch Routine

Once you have constructed the UI tree, write a routine that returns the head 
of that tree. This should be the first routine exported in your module's .gp file. 
The Preferences Manager knows to call the first exported routine when it 
needs the UI tree for a module.

The routine should have the parameters

optr _pascal (void)

For an example of such a routine, see Code Display 22-2.

Code Display 22-2 UI Fetch Routine

optr _pascal MPMGetPrefUITree(void) { return (@MPMRoot);}



	22.2	Module Information Routine

The Preferences application needs to know some information about each 
module to present the trigger which allows the user into said module. 
Preferences does this by calling the second routine exported by the module 
library. Preferences expects this routine to fill in a blank PrefModuleInfo 
structure.

By filling in this structure, the routine describes the button that Preferences 
will use to represent the module. Also, you may specify a User Level for the 
module, so it will be presented only to users with enough expertise to use it. 
You may also ask that only users with certain privileges be given access to 
the module; without the proper privileges, they won't see it.

The Routine should have the parameters 

void _pascal (*PrefModuleInfo)

For an example of such a routine, see Code Display 22-3.

Code Display 22-3 Module Information Routine

void _pascal MPMGetModuleInfo(PrefModuleInfo *info)

{	/* We'll set up this structure for both system and user information */
 	moduleInfo->PMI_requiredFeatures = 0;
 	moduleInfo->PMI_prohibitedFeatures = 0;
 	moduleInfo->PMI_minLevel = UIIL_ADVANCED;
 	moduleInfo->PMI_maxLevel = UIIL_MAX_LEVEL;
 	moduleInfo->PMI_monikerList = @MPMMonikerList;
 	moduleInfo->PMI_monikerToken = moduleToken;
}

const GeodeToken moduleToken = { "MyPf", MANUFACTURER_ID_MINE };



The following information about the PrefModuleInfo structure may prove 
useful when writing the routine:

PMI_requiredFeatures
This field allows you to restrict the display of your module so 
that it will only appear to users which have certain privileges.

    PMF_HARDWARE 
These settings are for a user who has permissions to actually 
change the configuration of the workstation. In a network 
environment where users log in to different machines at 
different times, normal users would be prevented from 
changing the mouse drivers, video drivers, etc. 

    PMF_SYSTEM 
These changes are more complex and potentially more 
damaging than the basic "user" changes, therefore, some users 
may be prevented from using these settings. 

    PMF_NETWORK 
These are network settings. Generally only the system 
administrator should see these settings, as they affect the 
entire network.

    PMF_USER	These are basic user changes. These settings are the most basic 
and least dangerous, controlling user preferences such as 
background color and screen saver types.

PMI_prohibitedFeatures
This flag field allows you to restrict the display of your module 
so that it will only appear to users who don't have certain 
privileges. If you had two modules, advanced UI and simple UI, 
one might be for the sysop and the other for normal users-this 
would keep the sysop's Preferences from being "cluttered" by 
the plain user module. The flags available are the same as 
those for PMI_requiredFeatures.

PMI_minLevel
This field allows you to specify the minimum User UI Level in 
which your module should appear. If the module's UI is very 
complicated and will confuse novice users, use this field to hide 
it from them.

PMI_maxLevel; 
This field allows you to specify the maximum User UI Level in 
which your module should appear. If you have two versions of 
your module-one for advanced users and one for novice 
users-use this field to hide the novice module from the 
advanced users.

PMI_monikerList
Like the moniker list for an application, this will provide the 
icon shown on the main Preferences screen. Create this using 
the Icon editor. You should have icon formats 64x40 color, 
64x40 monochrome, and 64x18 monochrome. 

PMI_monikerToken
A four character token and manufacturer ID by which to 
recognize the module.

	22.3	Important Messages

If you're going to create a subclass for use in a Preferences module, the 
following messages will be of special interest, as they signal that the user is 
carrying out certain important actions.

MSG_PREF_INIT
When a dialog box containing Pref objects is brought up, each 
object will receive MSG_PREF_INIT. Developers can subclass 
MSG_PREF_INIT to perform basic initialization procedures.

MSG_META_LOAD_OPTIONS 
Generic objects (and by inheritance, Pref- objects) respond to 
MSG_META_LOAD_OPTIONS by loading a value from the 
appropriate place in the .INI file. This place is determined by 
the object's ATTR_GEN_INIT_FILE_KEY field. If the object has 
an ATTR_GEN_INIT_FILE_CATEGORY field, that category will 
be used; otherwise the default handler will look up the Generic 
tree for an object with a ATTR_GEN_INIT_FILE_CATEGORY 
field defined and will use the value stored there.
Unlike a GenInteraction, a PrefInteraction will relay this 
message to its children. When it's time to load options from the 
.INI file, a MSG_META_LOAD_OPTIONS will be sent to the top 
level of the module's UI tree. This message should be 
propagated down the tree to reach all objects which should read 
their values from the .INI file. This is why it is suggested that 
you use PrefInteractions in place of GenInteractions if any 
Pref- objects will be underneath. If you create a new sort of 
object which should relay this message to other objects, be sure 
to do so.

MSG_META_SAVE_OPTIONS 
Generic objects (and by inheritance, Pref- objects) respond to 
MSG_META_SAVE_OPTIONS by saving a value to the 
appropriate place in the .INI file. This place is determined by 
the object's ATTR_GEN_INIT_FILE_KEY field. If the object has 
an ATTR_GEN_INIT_FILE_CATEGORY field, that category will 
be used; otherwise the default handler will look up the Generic 
tree for an object with a ATTR_GEN_INIT_FILE_CATEGORY 
field defined and will use the value stored there.
Unlike a GenInteraction, a PrefInteraction will relay this 
message to its children. When it's time to save options to the 
.INI file, a MSG_META_SAVE_OPTIONS will be sent to the top 
level of the module's UI tree. This message should be 
propagated down the tree to reach all objects which should 
write their values to the .INI file. This is why it is suggested 
that you use PrefInteractions in place of GenInteractions if any 
Pref- objects will be underneath. If you create a new sort of 
object which should relay this message to other objects, be sure 
to do so.
Often, interceptors of this message will carry out whatever 
actions are necessary to carry out the user's wishes in those 
cases where writing something to the .INI file is not enough to 
bring the change about. 

MSG_GEN_RESET 
If the user clicks on a "Revert" or "Cancel" button, all values in 
UI gadgets should revert to those values originally stored in the 
.INI file. You will note that many Pref- objects have an extra 
field to store their "original" value. If you create your own kind 
of Preferences object, keep in mind that it will need some way 
to store the "original" value (probably initialized while 
handling MSG_META_LOAD_OPTIONS) and should intercept 
MSG_GEN_RESET to replace its value with the "original" value.

MSG_GEN_LOAD_OPTIONS 
Generic objects (and thus Pref- objects) respond to 
MSG_META_LOAD_OPTIONS by sending themselves this 
message. This message is only meant to be used for managing 
the .INI file; any other functionality should occur in the 
handling of MSG_META_LOAD_OPTIONS.

MSG_GEN_SAVE_OPTIONS 
Generic objects (and thus Pref- objects) respond to 
MSG_META_SAVE_OPTIONS by sending themselves this 
message. This message is only meant to be used for managing 
the .INI file; any other functionality should occur in the 
handling of MSG_META_SAVE_OPTIONS.

If you are interested in detecting certain user actions, the following list may 
prove useful:

User Opens Dialog
Any objects which need to do some initialization when the user 
first opens the module's dialog box should intercept 
MSG_PREF_INIT. After receiving MSG_PREF_INIT, each object 
will receive a MSG_META_LOAD_OPTIONS.

User clicks Apply
Assuming the top object of the module's UI tree is a PrefDialog 
and that the dialog has been set up as a Properties dialog (as it 
is by default), then this object will receive a MSG_GEN_APPLY 
when the user activates the Apply trigger. The Pref dialog 
responds to this message by sending itself a 
MSG_META_SAVE_OPTIONS, which it will then pass on to its 
children. 

User clicks Cancel or Reset
All Gen- and Pref- objects within the UI tree will receive a 
MSG_GEN_RESET.

	22.4	Object Class Reference

If you will use any of the Pref- classes in your module, the following reference 
material may prove useful.

	22.4.1	PrefClass


PrefClass defines several fields of instance data which will be used by all of 
its subclasses.

There are several variable data fields which make sure that a given 
Preference gadget will not be usable by those without the proper privileges; 
other fields make sure that the gadgets will not appear to those users whose 
UI levels indicate that they would be confused by the presence of such an 
object. Keep in mind that if all gadgets in your module demand some 
privilege or User level, you should reflect this in the routine which provide 
Preferences module information about your module.

Code Display 22-4 PrefClass Instance Data

@class PrefClass, GenClass, master, variant;

@instance PrefAttributes PI_attrs = (PA_LOAD_IF_USABLE|PA_SAVE_IF_USABLE);

typedef ByteFlags PrefAttributes;
#define PA_REBOOT_IF_CHANGED 0x80
/* This bit signals that changes in the state of 
 * this object requires a system reboot to take effect. */

#define PA_LOAD_IF_USABLE 0x40
 /* Load options only if this object is usable (this is ON by default). */

#define PA_SAVE_IF_USABLE 0x20
 /* Save options only if this object is usable (this is ON by default) */

#define PA_SAVE_IF_ENABLED 0x10
 /* Save options only if this object is enabled */

#define PA_SAVE_IF_CHANGED 0x08
 /* Save options only if this object has changed. */

@vardata optr ATTR_PREF_REBOOT_STRING;
/* This is the string passed to MSG_PREF_MGR_DO_REBOOT_DIALOG -- the
 * string must be in a SHARABLE LMEM resource and is incorporated into the
 * middle of another sentence. In English, the sentence is "The system software
 * will shut down and restart to change the <reboot_string>. Do you wish
 * to proceed?"
 *
 * If an object has its PA_REBOOT_IF_CHANGED attribute set, it will scan for
 * this attribute, first in itself, then up the generic tree, until it
 * finds one. */
@reloc ATTR_PREF_REBOOT_STRING, 0, optr; 

@vardata PrefMgrFeatures ATTR_PREF_REQUIRED_FEATURES;
/* features bits that must be set for this object to be usable. */

@vardata PrefMgrFeatures ATTR_PREF_PROHIBITED_FEATURES;
/* features bits that must not be set for this object to be usable. */

@vardata UIInterfaceLevel ATTR_PREF_MIN_LEVEL;
/* interface level below which this object will not be usable */

@vardata UIInterfaceLevel ATTR_PREF_MAX_LEVEL;
/* interface level above which this object will not be usable */



In addition to the instance data fields, there are messages which allow for the 
dynamic retrieval and updating of these fields.

n	MSG_PREF_SET_INIT_FILE_CATEGORY



void MSG_PREF_SET_INIT_FILE_CATEGORY(

	const char *category);

Pref objects automatically respond to this message by changing the value of 
the ATTR_GEN_INIT_FILE_CATEGORY variable data field. This is a utility 
message; similar functionality is available via MSG_META_ADD_VAR_DATA.

Source:	Anywhere

Destination:	Any PrefClass object.

Parameters:	category	Null-terminated category string.

Return:	Nothing.

n	MSG_PREF_HAS_STATE_CHANGED



Boolean MSG_PREF_HAS_STATE_CHANGED();

This message determines whether the object or any of its children have 
changed since the last time options were loaded. This is used in conjunction 
with the PA_REBOOT_IF_CHANGED flag that can be set for an object-this 
flag signals that, if the object changes state, then GEOS should be restarted 
for the change to go into effect.

Source:	PrefClass object sends this message to itself when handling 
MSG_PREF_GET_REBOOT_INFO.

Destination:	Self.

Parameters:	None.

Return:	Should return true (i.e. non-zero) if state has changed, false (i.e. zero) 
otherwise.

Interception:	New PrefClass subclasses will use this to compare the object's current 
state against the "original" state, assuming one was stored.

n	MSG_PREF_GET_REBOOT_INFO



optr MSG_PREF_GET_REBOOT_INFO();

This message asks whether the system will need to reboot as a result of 
changes. If a reboot is necessary, the dialog will then notify the user of the 
reboot, and give the user a chance to abort (since the system reset may be a 
lengthy process on some systems). 

Source:	PrefDialog sends this message to all children on an apply.

Destination:	Any Pref- object.

Interception:	Subclasses should check the PA_REBOOT_IF_CHANGED flag-if the 
flag is set, the object should compare its present value to its "original" 
value. If a reboot is never needed, then do nothing.
If a reboot is necessary, the method should return the string to use 
when rebooting. The standard way to do this is via 
MSG_PREF_GET_REBOOT_STRING.
If possible, developers should try to avoid situations where a system 
reset is necessary, and use mechanisms such as the General Change 
Notification instead.

Parameters:	None.

Return:	Zero if no reboot needed; otherwise the OD of string to insert in the 
confirm-shutdown dialog box. The string will be inserted into a 
sentence; the string should describe what is changing. You may wish to 
use the value of ATTR_PREF_REBOOT_STRING.

See Also:	MSG_PREF_GET_REBOOT_STRING, ATTR_PREF_REBOOT_STRING.

n	MSG_PREF_GET_REBOOT_STRING



optr MSG_PREF_GET_REBOOT_STRING();

This message should return the optr of a string in a sharable local memory 
resource. This string will display in the dialog box which asks the user if it is 
all right to shut down. If a given object doesn't know of a good string to supply, 
it should forward the message on to its parent in the generic tree. The default 
PrefClass handler returns the value stored in 
ATTR_PREF_REBOOT_STRING.

Source:	Pref- object sends this message to itself.

Destination:	Any PrefClass object.

Parameters:	Nothing.

Return:	Object Pointer of string (or NULL if not found).

n	MSG_PREF_INIT



void MSG_PREF_INIT(

	PrefMgrFeatures 		features,

	UIInterfaceLevel 		level);

This message initializes the object. The object should perform any required 
set-up to appear on-screen. The PrefDialog object will send this object to all 
of its children when the dialog is first initiated.

Source:	Generic Parent

Destination:	Any Pref- object.

Parameters:	features	This structure tells you a bit what sorts of changes 
the user prepared to make.

level	The user's User Level.

Return:	None.

Interception:	Default behavior is to determine whether to make the object usable 
based on UI level and features (or will be, when implemented). Subclass 
should call superclass before handling, as subclass behavior may be 
different if the object is not usable,

n	MSG_PREF_NOTIFY_DIALOG_CHANGE



void MSG_PREF_NOTIFY_DIALOG_CHANGE(

	PrefDialogChangeType 			type);

This message goes out via PDGCNLT_DIALOG_CHANGE list bound to 
PrefDialog object telling interested parties of a change in the box's state. It is 
sent when the dialog box is being opened, closed, or destroyed.

Source:	PrefDialog object.

Destination:	Any Pref- object registered on PDCGCNLT_DIALOG_CHANGE.

Interception:	This message has no default handler; it must be intercepted to do 
anything useful.

Structures:	

typedef enum {
	PDCT_OPEN,
	PDCT_CLOSE,
	PDCT_DESTROY,
	PDCT_RESTART,
	PDCT_SHUTDOWN
} PrefDialogChangeType;

n	MSG_PREF_SET_ORIGINAL_STATE



void MSG_PREF_SET_ORIGINAL_STATE();

This message copies the object's current state to the "original" value field, 
which will be used on a MSG_GEN_RESET.

Parameters:	None.

Return:	Nothing.

Interception:	Subclasses should respond to this message by copying their current 
value to whatever instance field represents the "original" state of the 
object.

	22.4.2	PrefValueClass


PrefValue objects behave like GenValue objects except that they have the 
ability to store an "original" value in the PVI_originalValue instance data 
field. Unlike GenValue objects, PrefValues can only handle single word 
values.

Code Display 22-5 PrefValueClass Instance Data

@class PrefValueClass, PrefClass;

@instance word PVI_originalValue = 0;

@vardata void ATTR_PREF_VALUE_ORIG_IS_INDETERMINATE;

@vardata word ATTR_PREF_VALUE_ROUND;
/* Specifies an integer value to which the value will be rounded on .INI file
 * reads/writes. For instance, with a value for 10 here, the value would always
 * round off to the nearest 10. Note that the user can still type in an illegal
 * value, but this value will be rounded before saving to the .INI file.*/



n	MSG_PREF_VALUE_SET_ORIGINAL_VALUE



void MSG_PREF_VALUE_SET_ORIGINAL_VALUE(

	word 		orig,

	Boolean 		indeterminate);

This message sets the "original" value of the object-it also sets the value 
stored by the superclass.

	22.4.3	PrefItemGroupClass


This class acts like GenItemGroupClass, but has extended functionality 
for reading/writing strings to the .INI file. When the user triggers the dialog's 
Apply trigger (when the PrefItemGroup receives a 
MSG_META_SAVE_OPTIONS), the PrefItemGroup will respond by writing out 
strings based upon which of its children items are presently selected. These 
strings can be taken from the items' monikers or from strings used with 
PrefStringItemClass objects.

When loading options, if either the PIFF_USE_ITEM_MONIKERS or 
PIFF_USE_ITEM_STRINGS is set for the item group, then it will select its 
children based on the strings stored in the .INI file.Otherwise, the item group 
will default to the GenItemGroupClass functionality, which uses the 
identifier of the items as a basis for selection.

Code Display 22-6 PrefItemGroupClass Instance Data

@instance PrefInitFileFlags PIGI_initFileFlags = 0;

typedef ByteFlags PrefInitFileFlags;
#define PIFF_USE_ITEM_STRINGS 0x80
/* 
 * If set, then the item group's children must be of class PrefStringItemClass,
 * and their strings will be used to interact with the .INI file. */

#define PIFF_USE_ITEM_MONIKERS 0x40
/* If set, the monikers of the items are used to interact with the .INI file. */

#define PIFF_APPEND_TO_KEY 0x20
/* If set, the strings in this list will be ADDED 
 * to strings that may already exist for this key */

#define PIFF_ABSENT_KEY_OVERRIDES_DEFAULTS 0x10
/* If set, an absent key for the item group will cause it to
 * behave as if an empty key were in the .INI file, effectively
 * overriding any default values stored with the group when it was
 * compiled. Used primarily by those groups where one subclasses
 * MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY to determine the
 * initial setting, rather than looking in the .INI file. */

@instance word PIGI_originalSelection = 0;

@instance word PIGI_originalNumSelections = 0;

@instance byte PIGI_suspendCount = 0;
/* When suspend count is nonzero, the item group won't update text objects, nor
 * will it enable/disable objects when the selection changes. */

@vardata PrefEnableData ATTR_PREF_ITEM_GROUP_ENABLE;
/* allows setting up an object to be enabled/disabled when
 * settings are made in the item group */

typedef struct {
	word 		PED_item; 		/* Identifier of the item that controls
					 * enabling/disabling of object. If this
					 * is GIGS_NONE, then the action will be
					 * performed if no items are selected. */
	ChunkHandle 		PED_lptr; 		/* Object to be enabled/disabled */
 	PrefEnableFlags 		PED_flags;
} PrefEnableData;

typedef ByteFlags PrefEnableFlags;

#define PEF_DISABLE_IF_SELECTED 0x80
/* Disable the object if the associated item is selected,
 * otherwise do the opposite. */

#define PEF_DISABLE_IF_NONE 0x40
/* If this flag is set, then the PED_item field is ignored.
 * Instead, the item group will disable the specified object if
 * no items are selected -- or if there are no items in the list. */

@vardata word ATTR_PREF_ITEM_GROUP_OVERRIDE;
/* This attribute is used to specify an item which will act
 * as an OVERRIDE for all other items (in a non-exclusive
 * item group).
 *
 * When the specified item is SELECTED, then all other items are DE-SELECTED.
 * When any other item is SELECTED, the override item is DE-SELECTED.
 * If all items are DE-SELECTED, the override item becomes SELECTED */

@vardata ChunkHandle ATTR_PREF_ITEM_GROUP_TEXT_DISPLAY;
/* chunk handle of a GenText object that will be updated
 * with the moniker of the currently selected item on APPLY/RESET. */

@vardata ChunkHandle ATTR_PREF_ITEM_GROUP_STATUS_TEXT;
/* chunk handle of a GenText object that will be updated every
 * time this object receives a MSG_GEN_ITEM_GROUP_SEND_STATUS_MSG. */

@vardata char ATTR_PREF_ITEM_GROUP_EXTRA_STRING_SECTION[];
/* If a string is given -- the string will always be written
 * out as the FIRST string section for the .INI file key. For
 * example, the "Memory" item group uses this to always insert
 * "disk.geo" whenever writing out the memory types. Note:
 * this item is only written if the item group has either the
 * PIFF_USE_ITEM_MONIKERS or PIFF_USE_ITEM_STRINGS set. */



n	MSG_PREF_ITEM_GROUP_SET_ORIGINAL_SELECTION



void 	MSG_PREF_ITEM_GROUP_SET_ORIGINAL_SELECTION(
word selection);

Set the "originalSelection" field of the item group. Also, sends 
MSG_GEN_ITEM_GROUP_SET_SINGLE_SELECTION to superclass. This 
message can only be used with exclusive item groups-for other types, use 
the GenItemGroup messages, and then send the object 
MSG_PREF_SET_ORIGINAL_STATE.

Source:	Anywhere.

Destination:	An exclusive PrefItemGroup.

Parameters:	selection	Which item to select.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PREF_ITEM_GROUP_GET_SELECTED_ITEM_TEXT



word 	MSG_PREF_ITEM_GROUP_GET_SELECTED_ITEM_TEXT(

	char 	*buffer,

	word 	bufSize);

This message returns the text in the moniker of the selected item. The value 
returned is the number of characters in the string. If nothing is selected, the 
value returned is zero.

n	MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER



word MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER(@stack 

	char 	*buffer,

	word 	bufSize,

	word 	identifier);

This message retrieves the text in the moniker of one of the items of a 
PrefItemGroup. This works for dynamic list subclasses as well. The message 
returns the number of characters in the returned text.

Parameters:	buffer	Buffer in which to return moniker text.

bufSize	Size of buffer.

identifier	Identifier of item whose moniker should be 
returned.

Return:	Size of item's moniker.

n	MSG_PREF_ITEM_GROUP_UPDATE_TEXT



void MSG_PREF_ITEM_GROUP_UPDATE_TEXT(

	ChunkHandle 		textObject);

This message updates the specified text object with text of the currently 
selected item. The PrefItemGroup sends this to itself in conjunction with 
either ATTR_PREF_ITEM_GROUP_TEXT_DISPLAY or 
ATTR_PREF_ITEM_GROUP_STATUS_TEXT. By default, this message will also 
cause the text object to send out its status message.

Source:	Self.

Destination:	Self.

Interception:	Default behavior is to fetch the moniker of the current selection, and 
sent that to the text. The subclasser can use different text, if desired.

	22.4.4	PrefStringItemClass


Objects of this class, used together with a PrefItemGroup with its 
PIFF_USE_ITEM_STRINGS flag set, allow for objects which act like GenItems 
but can write an arbitrary string out to the .INI file if the user selects them.

Code Display 22-7 PrefStringItemClass Instance Data

@class PrefStringItemClass, GenItemClass;

@instance ChunkHandle PSII_initFileString = 0;



n	MSG_PREF_STRING_ITEM_SET_INIT_FILE_STRING



void MSG_PREF_STRING_ITEM_SET_INIT_FILE_STRING(

	const char 		*str);

This message sets the string that is read and written in the .INI file.

Interception:	Unlikely but possible. Default behavior sets the string and then checks 
again with the .INI file to make sure item should be selected.

n	MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY



Boolean MSG_PREF_STRING_ITEM_CHECK_IF_IN_INIT_FILE_KEY(

	PrefItemGroupStringVars 				*vars);

This message checks the passed string to see if the string bound to this item 
is in one of its pieces. A PrefItemGroup will send this object when loading 
options if it has the PIFF_USE_ITEM_STRINGS flag set.

Source:	PrefItemGroup object.

Destination:	PrefStringItem object.

Interception:	May be intercepted if the subclass needs to check other things to decide 
whether it should be selected. If you also wish to have the default 
behavior, you may call the superclass either before or after you've made 
your own decision, as appropriate.

Parameters:	vars	Information about the string.

Return:	Returns true (i.e. non-zero) if bound string is within the string stored 
in the local variable.

	22.4.5	PrefBooleanGroupClass


This class behaves as GenBooleanGroup, except that it saves its original 
state and handles MSG_GEN_RESET appropriately. It can also use item 
strings or monikers when loading and saving options.

Code Display 22-8 PrefBooleanGroupClass Instance Data

@class PrefBooleanGroupClass, PrefClass;
@instance PrefInitFileFlags 				PBGI_initFileFlags = 0;
@instance word 				PBGI_originalState = 0;



n	MSG_PREF_BOOLEAN_GROUP_SET_ORIGINAL_STATE



void MSG_PREF_BOOLEAN_GROUP_SET_ORIGINAL_STATE(

	word state);

This message sets the "original" state of the Boolean group.

	22.4.6	PrefDynamicListClass


Objects of PrefDynamicListClass act like GenDynamicList objects, but 
with the intelligence to write their data to the .INI file. Developers will 
generally not use this class directory, but instead will subclass, intercepting 
the following messages:

MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY
To build the data structures which will be used when later 
supplying monikers

MSG_PREF_ITEM_GROUP_GET_ITEM_MONIKER
To supply the moniker for a requested item on demand

MSG_PREF_DYNAMIC_LIST_FIND_ITEM
To find a given item when passed its moniker.

By handling these three messages, the developer will have a dynamic list 
that can properly load and save its selection and will scroll to the correct item 
in response to keyboard input.

The load and save mechanism is currently only configured for exclusive 
dynamic lists.

n	MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY



void MSG_PREF_DYNAMIC_LIST_BUILD_ARRAY();

The PrefDynamicList sends itself this message in response to 
MSG_PREF_INIT.

Source:	Self.

Destination:	Self.

Interception:	The subclasser should create an array of item strings, ideally sorted.

n	MSG_PREF_DYNAMIC_LIST_FIND_ITEM



 Boolean MSG_PREF_DYNAMIC_LIST_FIND_ITEM(

	word 		*itemPtr,

	const char 		*str,

	Boolean 		ignoreCase);

This message finds an item given a moniker. It is normally used in the 
context of loading options.

Source:	Self.

Destination:	Self.

Parameters:	itemPtr	Pointer to space in which to return a word.

str	String to search for.

ignoreCase	Flag requesting a case-insensitive search.

Return:	Returns true (i.e. non-zero) if an item was found with a moniker 
matching the passed string; itmPtr will have been filled with the 
number of the matching item. If no such item was found, the message 
will return false (i.e. zero) and itmPtr will point to the first item after 
the requested item.

	22.4.7	TitledGlyphClass


Each TitledGlyph displays two monikers, one icon and one text. 
TitledGlyphClass has no unique instance data or messages. You are 
expected to provide a list of monikers in the GI_visMoniker field, and this list 
should include several icons and a text moniker. Normally the same icon is 
used as by the routine providing Preferences module information.

	22.4.8	PrefInteractionClass


If you have any Preferences objects which write out their values to the .INI 
file, and if you would normally have those objects appear under a 
GenInteraction, then you should have them appear under a PrefInteraction 
instead. 

Objects of the class will forward the following messages on to their children:

MSG_PREF_INIT, MSG_PREF_SET_ORIGINAL_STATE
Object will send these messages to every child that is a subclass 
of PrefClass.

MSG_META_LOAD_OPTIONS
Object will send this to each child that satisfies the appropriate 
criteria (based on the child's PA_LOAD_IF_USABLE flag).

MSG_META_SAVE_OPTIONS
Object will send this to each child that satisfies the appropriate 
criteria (based on the child's PA_SAVE_IF_CHANGED, 
PA_SAVE_IF_ENABLED, and PA_SAVE_IF_USABLE flags).

MSG_PREF_HAS_STATE_CHANGED, MSG_PREF_GET_REBOOT_INFO
Propagates these messages to children, returning the value 
from the first child that returns a non-null response.

Code Display 22-9 PrefInteractionClass Instance Data

@class PrefInteractionClass, PrefClass;

@instance PrefInteractionAttrs PII_attrs = 0;

typedef ByteFlags PrefInteractionAttrs;

#define PIA_LOAD_OPTIONS_ON_INITIATE 0x80
/* If set, then the dialog will send MSG_PREF_INIT, 
 * followed by MSG_META_LOAD_OPTIONS to itself when it 
 * receives a MSG_GEN_INTERACTION_INITIATE */ 

#define PIA_SAVE_OPTIONS_ON_APPLY 0x40
/* This flag is normally OFF to allow non-dialog prefInteractions to reside inside
 * other interactions without duplicate SAVE_OPTIONS messages being sent. */



	22.4.9	PrefDialogClass


The top level object of any Preferences module UI tree should probably be a 
PrefDialog object. If the user's changes will necessitate a system reset, then 
this object will be responsible for rebooting the system.

On receiving MSG_GEN_APPLY, the dialog sends 
MSG_PREF_GET_REBOOT_INFO to itself (which is propagated to all children 
via PrefInteractionClass). If any children require reboot, then the dialog 
sends MSG_PREF_DIALOG_CONFIRM_REBOOT to itself, which normally puts 
up a confirmation dialog box (this can be subclassed to provide other 
behavior). If user confirms reboot, then the dialog will send 
MSG_PREF_DIALOG_REBOOT to itself, and MSG_GEN_APPLY to its 
superclass to save the options (it being assumed that the 
PIA_SAVE_OPTIONS_ON_APPLY flag is set).

Code Display 22-10 PrefDialogClass Instance Data

@class PrefDialogClass, PrefInteractionClass;

@default GII_visibility = GIV_DIALOG;

@default GII_type = GIT_PROPERTIES;

@default GII_attrs = @default | GIA_NOT_USER_INITIATABLE | GIA_MODAL;

@default PII_attrs = @default | PIA_SAVE_OPTIONS_ON_APPLY;



n	MSG_PREF_DIALOG_CONFIRM_REBOOT



@message Boolean MSG_PREF_DIALOG_CONFIRM_REBOOT(

	optr 		string);

By default, the PrefDialog will respond to this message by putting up a dialog 
box asking the user if he wants to restart the system.

Parameters:	

string	Object pointer of string which will be inserted in a 
sentence of the form "The system software will shut 
down and restart to change the string."

Return:	Returns true (i.e. non-zero) to confirm the reboot; returns false (i.e. zero 
to deny the reboot.

n	MSG_PREF_DIALOG_REBOOT



void MSG_PREF_DIALOG_REBOOT();

This message causes GEOS to restart. This message does not return.

	22.4.10	PrefTextClass


Objects of this class act like GenText objects, but have mechanisms to write 
their text string to the .INI file.

Code Display 22-11 PrefTextClass Instance Data

@class PrefTextClass, PrefClass;

@instance ChunkHandle PTI_originalText = 0;

@vardata ChunkHandle ATTR_PREF_TEXT_INIT_FILE_CATEGORY_TARGET;
/* PrefTextClass has the wonderful ability that it can set another
 * object's .INI. file category from its text. Category gets updated
 * whenever the text object receives MSG_GEN_TEXT_SEND_STATUS_MSG.
 * To make this happen, put the ChunkHandle of the object in this field. */



	22.4.11	PrefControlClass


This subclass of PrefInteractionClass acts like GenControl, but bypasses 
some GenControl functionality. This class is assumed not to be on notification 
lists. Objects of this class will pass MSG_GEN_RESET to their children as a 
normal GenInteraction would. This class has no special instance data or 
messages.

	22.4.12	PrefTimeDateControlClass


This controller allows the user to set the system time. This class' messages 
and instance data fields are internal. To get and set the date and time 
directly, use TimerGetDateAndTime() and TimerSetDateAndTime().

	22.4.13	PrefTriggerClass


PrefTriggerClass is not a subclass of PrefClass; it is a subclass of 
GenTriggerClass. It acts like a normal trigger except that when activated 
it will send a second message if it has been set up with a 
ATTR_PREF_TRIGGER_ACTION field.

Code Display 22-12 PrefTriggerClass Instance Data

@class PrefTriggerClass, GenTriggerClass;

@vardata PrefTriggerAction ATTR_PREF_TRIGGER_ACTION;

typedef struct {
 	word PTA_message;
 	optr PTA_dest;
} PrefTriggerAction;



	22.4.14	PrefTocListClass


PrefTocListClass allows users to choose a device driver by selecting the 
appropriate device name. It is assumed that each driver can control one or 
more types of devices. When you set up each driver, you will set up strings to 
describe which devices that driver can control.

This class is a fully functional subclass of PrefDynamicListClass (i.e. it 
handles those messages one must intercept to create a viable Preferences 
dynamic list class). It can be used to scan a list of files (not necessarily device 
drivers), as long as all of those files are in the same directory and have the 
same characters in their token. 

Code Display 22-13 PrefTocListClass Instance Data

@class PrefTocListClass, PrefDynamicListClass; 

@instance TocUpdateCategoryFlags PTLI_flags = 0;


typedef WordFlags TocUpdateCategoryFlags;
#define TUCF_EXTENDED_DEVICE_DRIVERS 0x8000
/* Files being enumerated are assumed to be extended device drivers. */

#define TUCF_CUSTOM_FILES 0x4000
/* The TUCP_fileArrayElementSize field will be used when creating the files array.
 * Otherwise, each element of the files array will be of size TocFileStruct. 
 * NOTE: If this flag is used, the data structure used for each file
 * element MUST contain TocFileStruct as its first element. */

#define TUCF_ADD_CALLBACK 0x2000
/* TUCP_addCallback contains a fptr to a callback
 * routine that will be called when a file is added to the files array. */

#define TUCF_DIRECTORY_NOT_FOUND 0x1000
/* Don't actually scan the directory, because it doesn't exist.
 * Just create the category, and leave it empty. */

@instance TocCategoryStruct PTLI_tocInfo = {"", 0, 0};

typedef struct {
	TokenChars 		TCS_tokenChars;
	DBGroupAndItem 		TCS_files; 		/* file name array */
	DBGroupAndItem 		TCS_devices; 		/* device name array--only if
					 * TCF_EXTENDED_DEVICE_DRIVERS is set. */
} TocCategoryStruct;

@vardata PrefTocExtraEntry ATTR_PREF_TOC_LIST_EXTRA_ENTRY_1; 
@vardata PrefTocExtraEntry ATTR_PREF_TOC_LIST_EXTRA_ENTRY_2;

typedef struct {
	ChunkHandle 		PTEE_item;
	/* lptr of item name. For device lists, this is 
	 * the device. For others, this is the file name */

	ChunkHandle 		PTEE_driver;
	/* Driver name (for device lists ONLY) */

	word 		PTEE_info;
	/* Extra word of information */
} PrefTocExtraEntry;

@vardata char ATTR_PREF_TOC_LIST_INFO_KEY[];
/* This key is used to write the "info" word for device lists */



Use ATTR_GEN_PATH_DATA to tell the PrefTocList object in which directory 
to look for the device drivers. The PrefTocList will store the list of device 
drivers and device names in DB items referenced in the PTLI_toolInfo field's 
TCS_files and TCS_devices slots-it will do this automatically, so if you wish 
the default behavior, leave these as zero when declaring the object. If you will 
write your own handlers for keeping track of driver information, you may 
wish to work with these fields as there are some routines which have been set 
up to work with them.

Code Display 22-14 PrefTocList Framework

@object PrefTocListClass MyPrefToc = {
	PTLI_flags		= @default | TUCF_EXTENDED_DEVICE_DRIVERS;
	PTLI_tocInfo		= {"TOKN", 0, 0};
	HINT_ITEM_GROUP_SCROLLABLE;
	ATTR_GEN_INIT_FILE_KEY = "keyName";
	ATTR_GEN_PATH_DATA = { file path containing drivers };
}



The following routines are rather esoteric. You should only use them if you 
wish to subclass PrefTocListClass' behavior and wish to continue using its 
existing data structures.

TocSortedNameArrayAdd()
Use this routine to add a new name to a sorted array. The new 
name will be inserted in its correct place in the alphabetically 
sorted array.

TocSortedNameArrayFind()
This routine determines whether a given name is in the TOC 
List's sorted name array, and will return the position of the 
name's element if found.

TocFindCategory()
Use this routine to find a category in the Toc file. The 
TCS_tokenChars field of the passed TocCategoryStruct 
should already be filled in; this routine will fill in the other 
fields.

TocNameArrayFind()
Use this routine to get back the token associated with a name 
in the name array. If the name is not found, the routine will 
return CA_NULL_ELEMENT.

TocNameArrayGetElement()
This routine takes a name array and a token and returns 
information about the name array element with the given 
token.

TocUpdateCategory()
This complicated routine will update a category using a 
callback routine which you must write. It updates the file lists 
by scanning the current directory.

TocNameArrayAdd()
Use this routine to add an element to a Toc name array.

TocDBLock()
Use this routine to lock one of the name arrays.

TocDBLockGetRef()
This C-only routine does the same thing as TocDBLock() and 
also returns the DB item's pointer and optr.

TocGetFileHandle()
Use this routine to get the handle of the file in which all Toc 
device driver information is stored.

n	MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_INFO



Boolean MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_INFO(

	word 		*infoPtr);

This message returns the info word about the currently selected item. This 
message can only be used with PrefTocList objects that have the 
TCF_EXTENDED_DEVICE_DRIVERS flag set.

Parameters:	infoPtr	Pointer to a word in which to return the info word.

Return:	Returns false (i.e. zero) if item was found, returns true (i.e. non-zero) if 
item was not found.

n	MSG_PREF_TOC_LIST_GET_SELECTED_DRIVER_NAME



word MSG_PREF_TOC_LIST_GET_SELECTED_DRIVER_NAME(

	char 		*buf,

	word 		bufSize);

This message returns the name of the driver for the selected device. This 
message may only be used if TCF_EXTENDED_DEVICE_DRIVERS is set. 

Parameters:	buf	Pointer to buffer to fill with the driver name.

bufSize	The size of buf.

Return:	If the device name fits in buf, then buf is filled and the message returns 
the size of the device name. If the device name doesn't fit, then the 
message will return zero.

n	MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_PATH



void MSG_PREF_TOC_LIST_GET_SELECTED_ITEM_PATH(

	TocItemPath 		*data);

This message returns the absolute path of the selected driver or file.

Structures:	

typedef struct {
  word 			TIP_disk; /* disk handle */

  MemHandle 			TIP_pathBlock; 
	/* Handle of block holding path (locked) */

  char 			*TIP_path; 
	/* Locked null-terminated absolute path */

} TocItemPath;

n	MSG_PREF_TOC_LIST_CHECK_DEVICE_AVAILABLE



Boolean MSG_PREF_TOC_LIST_CHECK_DEVICE_AVAILABLE(

	word 		*retvalPtr);

This message determines if the device selected by the list is available in the 
machine. This message may only be called for PrefTocList objects that have 
the TCF_EXTENDED_DEVICE_DRIVERS flag set.

Parameters:	retValPtr	Pointer to a word which will be filled with the 
return value.

Return:	Returns true (i.e. non-zero if the device is available; if the driver is a 
video driver, *retValPtr is set to the device's DisplayType. If the device 
is not available, returns false (i.e. zero); retValPtr is zero of the device 
doesn't exist; otherwise it will be the value of the appropriate 
GeodeLoadError plus one. 
