Controls allow for user interaction when you add them to the forms in your application. Events in controls are handled by CtlHandleEvent()
. This chapter describes the following types of controls:
Command Buttons
Repeating Buttons
Check Boxes
Push Buttons
Sliders
Selector Triggers
Pop-Up Triggers
Category Controls
Scroll Bars
All of these except the category controls and the scroll bars are managed by the functions described in Chapter 16, "Control Reference." The category controls are a special case of the selector trigger and pop-up trigger controls that are only used to display database categories. Scroll bars are managed using a separate scroll bar API that is described in Chapter 31, "Scroll Bar Reference."
Command Buttons
Command buttons (see Figure 3.1) display a text or graphic label in a rounded rectangle. The button frame can be changed although doing so is discouraged.

When the user taps a command button with the pen, the command button highlights until the user releases the pen or drags it outside the bounds of the command button.
Applications use command buttons to allow the user to perform an action. Applications respond to the ctlSelectEvent
for a command button to perform its action. You may return either true
or false
after the ctlSelectEvent
because Palm OS® does not handle this event.
Table 3.1 shows the system events generated when the user interacts with the button and CtlHandleEvent()
's response to the events.
Table 3.1 Event flow for command buttons
|
||
|
||
|
||
|
Adds the |
Repeating Buttons
A repeating button can look like a command button. In contrast to a command button, however, the repeating button is selected repeatedly until the pen is lifted. The most common use of repeating buttons are for the scroll buttons displayed in the bottom right corner of many forms.
Applications respond to the ctlRepeatEvent
for repeating buttons to perform the action of the button. Return false
after handling the event to ensure that more repeat events are sent.
Table 3.2 shows the system events generated when the user interacts with the repeating button and CtlHandleEvent()
's response to the events.
Table 3.2 Event flow for repeating buttons
Check Boxes
Check boxes (see Figure 3.2) display a setting, either on (checked) or off (unchecked). Touching a check box with the pen toggles the setting. The check box appears as a square, which contains a check mark if the check box's setting is on. A check box can and should have a text label attached to it; selecting the label also toggles the check box. A check box may also be assigned a group ID to make the group of check boxes mutually exclusive; however, doing so is strongly discouraged. Use push buttons for a mutually exclusive set of options.

Applications generally manage check boxes in the following ways:
- Respond to the
ctlSelectEvent
for the check box. Upon receiving this event, retrieve the value of the check box usingCtlGetValue()
and perform an action based on that value. You may return eithertrue
orfalse
after thectlSelectEvent
because Palm OS does not handle this event. - If you want to set the initial value of a check box when the form is first displayed, use
CtlSetValue()
orFrmSetControlValue()
in response to thefrmOpenEvent
.
Table 3.3 shows the system events generated when the user interacts with the check box and CtlHandleEvent()
's response to the events.
Table 3.3 Event flow for check boxes
|
||
|
||
|
Push Buttons
Push buttons (see Figure 3.3) have square corners. Touching a push button with the pen inverts the bounds. If the pen is released within the bounds, the button remains inverted. Push buttons are intended to be used to present several mutually exclusive options. When you create the push button resources, assign a nonzero group ID to all push buttons within a group. Doing so ensures that only one push button can be selected at a time.

Applications manage push buttons using the functions FrmGetControlGroupSelection()
and FrmSetControlGroupSelection()
to retrieve and set which push button in the sequence is selected.
Table 3.4 shows the system events generated when the user interacts with the push button and CtlHandleEvent()
's response to the events.
Table 3.4 Event flow for push buttons
Sliders
Sliders (see Figure 3.4) represent a value that falls within a particular range. For example, a slider might represent a value that can be between 0 and 10.

There are four attributes that are unique to slider controls:
- The minimum value the slider can represent
- The maximum value the slider can represent
- The current value
- The page jump value, or the amount by which the value is increased or decreased when the user clicks to the left or right of the slider thumb
Palm OS supports two types of sliders: regular slider and feedback slider. Sliders and feedback sliders look alike but behave differently. Specifically, a regular slider control does not send events while the user is dragging its thumb. A feedback slider control sends an event each time the thumb moves one pixel, whether the pen has been lifted or not.
Applications generally respond to the ctlSelectEvent
for sliders or ctlRepeatEvent
for feedback sliders. Upon receiving either one of these events, check the value of the slider using CtlGetValue()
and perform an action based on that value. You may return either true
or false
after the ctlSelectEvent
because Palm OS does not handle this event. Return false
for the ctlRepeatEvent
.
Table 3.5 shows the system events generated when the user interfaces with a slider and how CtlHandleEvent()
responds to the events.
Table 3.5 Event flow for sliders
Table 3.6 shows the system events generated when the user interacts with a feedback slider and CtlHandleEvent()
's response to the events.
Table 3.6 Event flow for feedback sliders
Sliders are drawn using two bitmaps: one for the slider background, and the other for the thumb. You may use the default bitmaps to draw sliders, or you may specify your own bitmaps when you create the slider.
The background bitmap you provide can be smaller than the slider's bounding rectangle. This allows you to provide one bitmap for sliders of several different sizes. If the background bitmap isn't as tall as the slider's bounding rectangle, it's vertically centered in the rectangle. If the bitmap isn't as wide as the slider's bounding rectangle, the bitmap is drawn twice. First, it's drawn left-justified in the left half of the bounding rectangle and clipped to exactly half of the rectangle's width. Then, it's drawn right-justified in the right half of the bounding rectangle and clipped to exactly half of the rectangle's width. (See Figure 3.5.) Note that this means that the bitmap you provide must be at least half the width of the bounding rectangle.
Figure 3.5 Drawing a slider background

Selector Triggers
A selector trigger (see Figure 3.6) displays a text label surrounded by a gray rectangular frame. If the text label changes, the width of the control expands or contracts to the width of the new label.

When the selector trigger is tapped, it should display a dialog from which the user selects a new value to display in the trigger. Applications are responsible for implementing most of this behavior:
- Upon receiving the
frmOpenEvent
for the form that contains the selector trigger, set the initial value of the trigger usingCtlSetLabel()
. - Upon receiving a
ctlSelectEvent
for a selector trigger, callFrmDoDialog()
as described in "Displaying Dialogs." If the user then dismisses the dialog by tapping the OK button, useCtlSetLabel()
to set the new value of the selector trigger.
Some forms that display category information for a single record use a selector trigger to do so. This selector trigger displays a pop-up list when tapped instead of a dialog. Such controls are considered exceptions to the rule of how selector triggers should behave and are not meant to be imitated. See "Category Controls" for information on how to work with a category selector trigger.
Note that selector triggers, unlike the button controls and slider controls, do not have a numeric value associated with them, so you should not use CtlSetValue()
or CtlGetValue()
on them.
Table 3.7 shows the system events generated when the user interacts with the selector trigger and CtlHandleEvent()
's response to the events.
Table 3.7 Event flow for selector triggers
|
||
|
Pop-Up Triggers
A pop-up trigger (see Figure 3.7) displays a text label and a graphic element (always on the left) that signifies the control initiates a pop-up list. If the text label changes, the width of the control expands or contracts to the width of the new label plus the graphic element.

To create a pop-up list in a resource editor, you must create both a pop-up trigger resource and a list resource. Do the following:
- Add a pop-up trigger to your form. Use 0 as the width to have the pop-up trigger automatically resize based on the label.
- Add a list at the same coordinates.
- Specify that the list is unusable.
- Set the List ID in the pop-up trigger to match the ID of the list resource.
Applications generally manage pop-up triggers in the following ways:
- Set the initial label of the pop-up trigger when the form is first opened. Respond to the
frmOpenEvent
by callingCtlSetLabel()
. - If the list items were statically created in the resource editor, respond to the
popSelectEvent
for a pop-up trigger and perform an action based on the selection passed in the event structure. Returnfalse
to allow the system to set the pop-up trigger's label to the text of the selected list item. - If you're dynamically creating the list elements at run-time, the system cannot set the trigger label for you. Respond to the
popSelectEvent
to both perform an action based on the selection and to set the trigger label usingCtlSetLabel()
. Returntrue
to bypass the system default behavior. Otherwise, the application will crash.
IMPORTANT: Do not return
false
in response to the ctlSelectEvent
for a pop-up trigger. If you do, the list won't pop up.
Some forms use a pop-up list to display database categories. See "Category Controls" for information on how to work with such a list.
Table 3.8 shows the system events generated when the user interacts with the pop-up trigger and CtlHandleEvent()
's response to the events. Because pop-up triggers are used to display lists, also see "Lists."
Table 3.8 Event flow for pop-up triggers
|
||
|
||
Adds a |
||
|
Category Controls
Palm OS databases use categories to allow the user to group records logically into manageable lists. In the user interface, categories typically appear:
- In a pop-up list in a form's title bar if the form displays multiple database records.
- In a pop-up list in dialogs (such as the Details dialog) that allow you to edit a single database record.
- In a selector trigger in a form's title bar if the form allows you to edit a single database record.
The Category Manager allows you to easily create and work with these standard controls used for categories. The Category Manager contains two separate sets of APIs: one for non-schema Palm OS databases, and one for schema databases. This section describes how to use the Category Manager functions to manage category controls. For information on manipulating categories in a database, see Exploring Palm OS: Memory, Databases, and Files.
Creating the Category Controls
You create a category pop-up list the same way you create any pop-up list. In your resource editor, do the following:
- Create a pop-up trigger resource with a width of 0.
- Add a list resource at the same coordinates as the pop-up trigger. Set the list to unusable.
- Assign the list ID attribute of the pop-up trigger to be the resource ID of the list you created in the previous step.
To crate a category selector trigger, do the following:
- Create a selector trigger with a width of 0.
- Add a list at the same coordinates as the pop-up trigger. Set the list to unusable.
Because selector triggers are not normally associated with pop-up lists, the selector trigger does not have a list ID attribute for you to assign. The pop-up list for the selector trigger is managed through the Category Manager.
Category Controls for Non-schema Database Databases
For the most part, you can handle category controls for classic Palm OS databases using only these calls:
- Call
CategoryInitialize()
when you create a new database as described in "Initializing Categories in a Non-schema Database" below). - Call
CategorySetTriggerLabel()
to set the category pop-up or selector trigger's label when the form is opened (as described in "Initializing the Category Trigger for Non-schema Databases"). - Call
CategorySelect()
when the user selects the category trigger (as described in "Managing a Category Pop-up List for a Non-schema Database").
You typically don't need to use the other Category Manager functions unless you want more control over what happens when the user selects the category trigger.
Initializing Categories in a Non-schema Database
Before you can use the category functions on a non-schema database, you must set up the database appropriately. The category functions expect to find information at a certain location. If the information is not there, the functions will fail.
Category information is stored in the AppInfoType
structure within the non-schema database's application info block. The application info block may contain any information that your database needs. If you want to use the Category Manager, the first field in the application info block must be an AppInfoType
structure.
The AppInfoType
structure maps category names to category indexes and category unique IDs. Category names are displayed in the user interface. Category indexes are used to associate a database record with a category. That is, the database record's attributes contain the index of the category to which the record belongs. Category unique IDs are used when synchronizing the database with the desktop computer.
To initialize the AppInfoType
structure, you call CategoryInitialize()
, passing a string list resource containing category names. This function creates as many category indexes and unique IDs as are necessary. You only need to make this call when the database is first created or when you newly assign the application info block to the database.
The string list resource is an appInfoStringsRsc
('tAIS'
) resource. It contains predefined categories that new users see when they start the application for the first time. Note that the call to CategoryInitialize()
is the only place where you use an appInfoStringsRsc
. Follow these guidelines when creating the resource:
- Place any categories that you don't want the user to be able to change at the beginning of the list. For example, it's common to have at least one uneditable category named Unfiled, so it should be the first item in the list.
- The string list must have 16 entries. Typically, you don't want to predefine 16 categories. You might define one or two and leave the remaining entries blank. The unused slots should have 0 length.
- Keep in mind that there is a limit of 16 categories. That includes both the predefined categories and the categories your users will create.
- Each category name has a maximum length defined by the
dmCategoryLength
constant (currently, 16 bytes). - Don't include strings for "All" or "Edit Categories." While these two items often appear in category lists, they are not categories, and they are treated differently by the category functions.
Listing 3.1 shows an example function that creates and initializes a database with an application info block. Notice that because the application info block is stored with the database, you allocate memory for it using DmNewHandle()
, not with MemHandleNew()
.
Listing 3.1 Creating a database with an app info block
typedef struct { AppInfoType appInfo; uint16_t myCustomAppInfo; } MyAppInfoType; DatabaseID gDbID; DmOpenRef gDbP; Err CreateAndOpenDatabase(DmOpenRef *dbPP, uint16_t mode) { status_t error = errNone; DmOpenRef dbP; MemHandle appInfoHandle; DatabaseID dbID; MyAppInfoType *appInfoP; DmDatabaseInfoType myDbInfo; // Create the database. error = DmCreateDatabase (MyDBName, MyDBCreator, MyDBType, false); if (error < 0) return error; // Open the database. dbP = DmOpenDatabaseByTypeCreator(MyDBType, MyDBCreator, mode); if (!dbP) return (dmErrCantOpen); // Get database ID so we can initialize app info block. if (DmGetOpenInfo(dbP, &dbID, NULL, NULL, NULL)) return dmErrInvalidParam; // Allocate app info in storage heap. appInfoHandle = DmNewHandle(dbP, sizeof(MyAppInfoType)); if (!appInfoHandle) return dmErrMemError; // Associate app info with database. error = DmDatabaseInfo(dbID, &myDbInfo); if (error < 0) return error; myDbInfo->pAppInfoHandle = appInfoHandle; DmSetDatabaseInfo(dbID, &myDbInfo); // Initialize app info block to 0. appInfoP = MemHandleLock(h); DmSet(appInfoP, 0, sizeof(MyAppInfoType), 0); // Get a DmOpenRef to the resource database that contains the app info // strings resource. if (!gDbP) { if ((error = SysGetModuleDatabase(SysGetRefNum(), &gDbID, &gDbP)) < errNone) return error; } // Initialize the categories. CategoryInitialize ((AppInfoPtr)appInfoP, gDbP, MyLocalizedAppInfoStr); // Unlock the app info block. MemPtrUnlock(appInfoP); // Set the output parameter and return. *dbPP = dbP; return error; }
Initializing the Category Trigger for Non-schema Databases
When a form is opened, you need to set the text that the category trigger (whether pop-up or selector trigger) should display. To do this, use CategoryGetName()
to look up the name in the AppInfoType
structure and then use CategorySetTriggerLabel()
to set the label.
For the main form of the application, it's common to store the index of the previously selected category in a preference and restore it when the application starts up again.
Forms that display information from a single record may show that record's category in a selector trigger or pop-up list. You can retrieve the record's category using DmGetRecordCategory()
.
Listing 3.2 shows how to set the trigger label to match the category for a particular database record.
Listing 3.2 Setting the category trigger label (non-schema database)
uint8_t category; char categoryName [dmCategoryLength]; ControlType *ctl; // If current category is All, we need to look // up category. if (CurrentCategory == dmAllCategories) DmGetRecordCategory (AddrDB, CurrentRecord, &category); else category = CurrentCategory; CategoryGetName (AddrDB, category, categoryName); ctl = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, objectID)); CategorySetTriggerLabel (ctl, categoryName);
Managing a Category Pop-up List for a Non-schema Database
When the user taps the category pop-up trigger, call CategorySelect()
. That is, call CategorySelect()
in response to a ctlSelectEvent
when the ID stored in the event matches the ID of the category's trigger. The CategorySelect()
function displays the pop-up list, manages the user selection, displays the Edit Categories modal dialog as necessary, and sets the trigger label to the item the user selected.
Listing 3.3 shows a typical call to CategorySelect()
:
Listing 3.3 Calling CategorySelect()
categoryEdited = CategorySelect (AddrDB, frm, ListCategoryTrigger, ListCategoryList, true, &category, CategoryName, 1, NULL, categoryDefaultEditCategoryString);
This example uses the following as parameters:
-
AddrDB
is the database with the categories to be displayed. -
frm
,ListCategoryTrigger
, andListCategoryList
identify the form, pop-up or selector trigger resource, and list resource. -
true
indicates that the list should contain an "All" item. The "All" item should appear only in forms that display multiple records. It should not appear in forms that display a single record because selecting it would have no meaning. -
category
andCategoryName
are pointers to the index and name of the currently selected category. When you call this function, these two parameters should specify the category currently displayed in the pop-up trigger. Unfiled is the default. - The number 1 is the number of uneditable categories.
CategorySelect()
needs this information when the user chooses the Edit Categories list item. Categories that the user cannot edit should not appear in the Edit Categories dialog.Because uneditable categories are assumed to be at the beginning of the category list, passing 1 for this parameter means that
CategorySelect()
does not allow the user to edit the category at index 0. -
NULL
is passed because this call uses the default title for the Edit Categories list item. If you want to change the name of this list item to something else, you must pass the resource database that contains the string resource you want to use for this list item. -
categoryDefaultEditCategoryString
is a constant that means include an Edit Categories item in the list and use the default string for its name ("Edit Categories" on US English ROMs).To use a different name (for example, if you don't have enough room for the default name), pass the ID of a string resource containing the desired name and pass a reference to its resource database in the parameter before it.
In some cases, you might not want to include the Edit Categories item. If so, pass the constant
categoryHideEditCategory
.
The CategorySelect()
return value is somewhat tricky: CategorySelect()
returns true
if the user edited the category list, false
otherwise. That is, if the user chose the Edit Categories item and added, deleted, or changed category names, the function returns true
. If the user never selects Edit Categories, the function returns false
. In most cases, a user simply selects a different category from the existing list without editing categories. In such cases, CategorySelect()
returns false
.
This means you should not rely solely on the return value to see if you need to take action. Instead, you should store the value that you passed for the category index and compare it to the index that CategorySelect()
passes back. For example:
Listing 3.4 CategorySelect() return value
int16_t category; Boolean categoryEdited; category = CurrentCategory; categoryEdited = CategorySelect (AddrDB, frm, ListCategoryTrigger, ListCategoryList, true, &category, CategoryName, 1, NULL, categoryDefaultEditCategoryString); if ( categoryEdited || (category != CurrentCategory)) { /* user changed category selection or edited category list. Do something. */ }
If the user has selected a different category, you probably want to do one of two things:
- Update the display so that only records in that category are displayed. See the function
ListViewUpdateRecords()
in the Address Book example application for sample code. - Change the current record's category from the previous category to the newly selected category. See the function
EditViewSelectCategory()
in the Address Book example application for sample code.
Note that the CategorySelect()
function handles the results of the Edit Categories dialog for you. It adds, deletes, and renames items in the database's AppInfoType
structure. If the user deletes a category that contains records, it moves those records to the Unfiled category. If the user changes the name of an existing category to the name of another existing category, it prompts the user and, if confirmed, moves the records from the old category to the new category. Therefore, you never have to worry about managing the category list after a call to CategorySelect()
.
Category Controls for Schema Databases
The category rules differ between non-schema databases and schema databases in the following ways:
- In schema databases, you can have up to 255 categories. In non-schema databases, the limit is 16.
- In schema databases, a database record can be assigned to multiple categories. In non-schema databases, a record can only belong to one category.
- In schema databases, the category information is stored in a private structure within the database. In non-schema databases, you must define an
AppInfoType
structure and store it in the database. - In schema databases, the Database Manager keeps track of whether a category can be modified or not. In non-schema databases, the Database Manager has no notion of whether the category can be edited.
For the most part, you can handle category controls for schema Palm OS databases using only these calls:
- Call
CatMgrInitialize()
when you create a new database as described in "Initializing Categories in a Schema Database" below). - Call
CatMgrSetTriggerLabel()
to set the category pop-up or selector trigger's label when the form is opened (as described in "Initializing the Category Trigger for Non-schema Databases"). - Call
CatMgrSelectFilter()
when the user selects the pop-up list on a form that uses categories to filter the display (as described in "Managing a Category Pop-up List for a Non-schema Database"). - Call
CatMgrSelectEdit()
when the user selects a selector trigger or pop-up list on a form that changes a database record's category.
You typically don't need to use the other Category Manager functions unless you want more control over what happens when the user selects the category trigger.
Initializing Categories in a Schema Database
Before you can use the category functions, you must set up the schema database appropriately. The category functions expect to find information at a certain location. If the information is not there, the functions will fail.
Category information is stored in a private structure within the schema database. This structure maps category names to category unique IDs. Category names are displayed in the user interface. Category IDs are used when storing category information in each database record and when synchronizing the schema database with the desktop computer.
To initialize this private structure, you call CatMgrInitialize()
, passing a string list resource containing category names. This function creates as many category IDs as are necessary. You only need to make this call when the database is first created.
The string list resource is an appInfoStringsRsc
('tAIS'
) resource. It contains predefined categories that new users see when they start the application for the first time. Note that the call to CatMgrInitialize()
is the only place where you use an appInfoStringsRsc
. Follow these guidelines when creating the resource:
- The string list may contain up to 255 entries. Typically you don't want to predefine 255 categories.
- Each category name has a maximum length defined by the catCategoryNameLength constant (currently 31+1 bytes).
- It's common to have at least one category named Unfiled as the first item in the list. Records that aren't a member of any category are displayed under "Unfiled."
- Don't include strings for the "All" or "Edit Categories" items that you see in a categories pop-up list. When displaying the category list, the category manager will automatically generated list entries for these items.
- If you want any of the predefined categories to be uneditable by the user, call
CatMgrSetEditable()
and pass false as the editable attribute.
Listing 3.1 shows an example function that creates and initializes a schema database with category information.
Listing 3.5 Creating a schema database with categories
DatabaseID gDbID; DmOpenRef gDbP; Err CreateAndOpenDatabase(DmOpenRef *dbPP, uint16_t mode) { status_t error = errNone; DmOpenRef dbP; DatabaseID dbID; DmDatabaseInfoType *myDbInfo; MemHandle myAppInfoStringsH; // Create the database. error = DbCreateDatabase (MyDBName, MyDBCreator, MyDBType, numSchemas, schemaList, &dbID); if (error < 0) return error; // Open the database. dbP = DbOpenDatabase(dbID, mode, dbShareNone); if (!dbP) return (dmErrCantOpen); // Get a DmOpenRef to the resource database that contains the app info // strings resource. Lock down the resource. if (!gDbP) { if ((error = SysGetModuleDatabase(SysGetRefNum(), &gDbID, &gDbP)) < errNone) return error; } myAppInfoStringsH = DmGetResource(gDbP, appInfoStringRsc, MyLocalizedAppInfoStr); // Initialize the categories. CatMgrInitialize(dbP, myAppInfoStringsH); // Set the output parameter and return. *dbPP = dbP; return error; }
Initializing the Category Trigger for a Schema Database
When a form is opened, you need to set the text that the category trigger (whether pop-up or selector trigger) should display. To do this, use CatMgrSetTriggerLabel()
.
For the main form of the application, it's common to store the ID of the previously selected category in a preference and restore it when the application starts up again.
Forms that display information from a single record from a schema database may show that record's category membership in a selector trigger or pop-up list. You can retrieve the record's category using DbGetCategory()
. Keep in mind that the record may belong to many categories. The Category Manager functions display multiple categories as a comma-separated list.
Listing 3.2 shows how to set the trigger label to match the category for a particular database record.
Listing 3.6 Setting the category trigger label (schema database)
CategoryID categoryIDs[]; uint32_t numCategories; ControlType *ctl; char *nameP; // Allocate memory for the nameP string, which will // contain the trigger's label. nameP = (char *)MemPtrNew(catCategoryNameLength); // If current category is All or Multiple, we need to look // up category membership if ((CurrentCategory == catIDAll) || (CurrentCategory == catIDMultiple)) DbGetCategory (schemaDB, CurrentRecord, &numCategories, &categoryIDs); else category = CurrentCategory; ctl = FrmGetObjectPtr(frm, FrmGetObjectIndex(frm, objectID)); CategorySetTriggerLabel (schemaDB, categoryIDs, numCategories, ctl, nameP); // Upon frmCloseEvent, free nameP and call // DbReleaseStorage() if categoryIDs is not NULL.
Managing a Category Pop-up List for a Schema Database
When the user taps the category pop-up trigger on a form that manages a schema database, call CatMgrSelectFilter()
if the pop-up list filters the display of multiple records or CatMgrSelectEdit()
if the pop-up list chooses a new category for a single record. That is, call one of these functions in response to a ctlSelectEvent
when the ID stored in the event matches the ID of the category's trigger. These functions display the pop-up list, manage the user selection, display the Edit Categories modal dialog as necessary, and set the trigger label to the item the user selected.
Listing 3.7 shows a typical call to CatMgrSelectEdit()
. The call to CatMgrSelectFilter()
is similar.
Listing 3.7 Calling CatMgrSelectEdit()
uint8_t category; CategoryID inCategoryIDs[], outCategoryIDs[]; uint32_t numInCategories, numOutCategories; ControlType *ctl; char *labelString; //Allocate space for labelString. outCategoryIDs space is // allocated for you. labelString = (char *)MemPtrNew(catCategoryNameLength); categoryEdited = CatMgrSelectEdit (schemaDB, frm, ListCategoryTrigger, labelString, ListCategoryList, true inCategoryIDs, numInCategories, outCategoryIDs, &numOutCategories, true, NULL); // Upon frmCloseEvent, deallocate labelString and call // CatMgrFreeSelectedCategories() to free the category // list allocated by CatMgrSelectEdit().
This example uses the following as parameters to CatMgrSelectEdit()
:
-
schemaDB
is the schema database with the categories to be displayed. -
frm
,ListCategoryTrigger
, andListCategoryList
identify the form, pop-up or selector trigger resource, and list resource. -
labelString
contains the text displayed in the category label. This string must be at leastcatCategoryNameLength
. If you've got a lot of space to display the name, you might allocate a larger string. -
true
indicates that a record can belong to multiple categories. The Category Manager adds a "Multiple" item to the list. If the user selects this item, it displays a dialog from which the user selects multiple categories.To disallow this behavior, pass
false
instead. Note thatCatMgrSelectFilter()
does not take this parameter. It does not allow the selection of multiple categories to display. -
inCategoryIDs
contains a list of the category IDs to which the record currently belongs andnumInCategories
contains the number of elements ininCategoryIDs
. - Upon return,
outCategoryIDs
contains a list of the category IDs that the user selected, andnumOutCategories
contains the number of elements inoutCategoryIDs
. If the user does not change the selection,outCategoryIDs
isNULL
. -
true
indicates that the pop-up list should contain the Edit Categories list item. -
NULL
indicates that the default string should be used for the Edit Categories list item.
When the pop-up list is displayed, inCategoryIDs
represents the record's category membership. If the user selects items in the list, this category membership changes, the function returns true
, and outCategoryIDs
reflects the new set of categories to which the record should now belong. This list supersedes the current category membership list. Your application is responsible for updating the record to reflect the new membership. In the case of CatMgrSelectFilter()
, your application is responsible for updating the display to reflect the new selection.
Note that the CatMgrSelectEdit()
and CatMgrSelectFilter()
functions handle the results of the Edit Categories dialog for you. They add, delete, and rename items in the database's private category structure. If the user deletes a category that contains records, they remove membership in that category from any existing records. If the user changes the name of an existing category to the name of another existing category, they prompt the user and, if confirmed, moves the records from the old category to the new category. Therefore, you never have to worry about managing the category list after a call to CatMgrSelectEdit()
or CatMgrSelectFilter()
.
Scroll Bars
A scroll bar is a control that is used to scroll user interface elements that are longer or wider than the screen. You can attach scroll bars to fields or tables and the system sends the appropriate events when the end user interacts with the scroll bar (see Figure 3.8).

To do to include a scroll bar in your user interface, do the following:
- Create a scroll bar UI resource in your resource file.
Provide the ID and the bounds for the scroll bar rectangle. The height has to match the UI element you want to attach it to. The width should be 7 standard coordinates.
- Provide a minimum and maximum value as well as a page size.
- Minimum is usually 0.
- Maximum is usually 0 and set programmatically.
- The page size determines how many lines the scroll bar moves when the text scrolls.
- In the UI element that is to be scrolled, set the attribute that indicates that it has a scroll bar attached to it. (You can also do this programmatically.)
There are two ways in which the scroll bar and the user interface element that it's attached to need to interact:
- When the user adds or removes text, the scroll bar needs to know about the change in size.
If the "has scroll bar" attribute is set for a field, you'll receive a
fldChangedEvent
whenever the field's size changes. Your application should handle these events by computing new values for the scroll bar's minimum, maximum, and current position and then useSclSetScrollBar()
to update it.If a table has a scroll bar attached, you should keep track of when the table's size changes. Whenever it does, you should compute new values for the scroll bar's minimum, maximum, and current position and then use
SclSetScrollBar()
to update it.You should also call
SclSetScrollBar()
when the form is initialized to set the current position of the scroll bar. - When the user moves the scroll bar, the text needs to move accordingly. This can either happen dynamically (as the user moves the scroll bar) or statically (after the user has released the scroll bar).
The system sends the following scroll bar events:
-
sclEnterEvent
is sent when apenDownEvent
occurs within the bounds of the scroll bar. -
sclRepeatEvent
is sent when the user drags the scroll bar. -
sclExitEvent
is sent when the user lifts the pen. This event is sent regardless of previoussclRepeatEvent
s.Applications that want to support immediate-mode scrolling (that is, scrolling happens as the user drags the pen) need to watch for occurrences of
sclRepeatEvent
. In response to this event, call the scrolling function associated with the UI element (FldScrollField()
or your own scrolling function in the case of tables).Applications that don't support immediate-mode scrolling should ignore occurrences of
sclRepeatEvent
and wait only for thesclExitEvent
.
-
Summary of Control Functions
|
|