Documentation  |   Table of Contents   |  < Previous   |  Next >   |  Index

3    Working with Controls

User Interface

Exploring Palm OS®

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 ^TOP^

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.

Figure 3.1  Buttons

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 

User Action

System Response

CtlHandleEvent() Response

Pen goes down on a button.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with button's ID number.

Inverts the button's display.

Pen is lifted from button.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlSelectEvent to the event queue.

Pen is lifted outside button.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlExitEvent to the event queue.

Repeating Buttons ^TOP^

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 

User Action

System Response

CtlHandleEvent() Response

Pen goes down on a repeating button.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with button's ID number.

Adds the ctlRepeatEvent to the event queue.

Pen remains on repeating button.

ctlRepeatEvent

Tracks the pen for a period of time, then sends another ctlRepeatEvent if the pen is still within the bounds of the control.

Pen is dragged off the repeating button.

No ctlRepeatEvent occurs.

Pen is dragged back onto the button.

ctlRepeatEvent

See above.

Pen is lifted from button.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlExitEvent to the event queue.

Check Boxes ^TOP^

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.

Figure 3.2  Check boxes

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 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.
  • If you want to set the initial value of a check box when the form is first displayed, use CtlSetValue() or FrmSetControlValue() in response to the frmOpenEvent.

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 

User Action

Event Generated

CtlHandleEvent() Response

Pen goes down on check box.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with check box's ID number.

Tracks the pen until the user lifts it.

Pen is lifted from check box.

penUpEvent with the x and y coordinates stored in EventType.

  • If the check box is unchecked, a check appears.
  • If the check box is already checked and is grouped, there is no change in appearance.
  • If the check box is already checked and is ungrouped, the check disappears.

Adds the ctlSelectEvent to the event queue.

Pen is lifted outside box.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlExitEvent to the event queue.

Push Buttons ^TOP^

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.

Figure 3.3  Push buttons

.

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 

User Action

System Response

CtlHandleEvent() Response

Pen goes down on a push button.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with push button's ID number.

If push button is grouped and highlighted, no change. If push button is ungrouped and highlighted, it becomes unhighlighted.

Pen is lifted from push button.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlSelectEvent to the event queue.

Sliders ^TOP^

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.

Figure 3.4  Slider

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 

User Action

System Response

CtlHandleEvent() Response

Pen tap on slider's background.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with slider's ID number.

Adds or subtracts the slider's page jump value from its current value, and adds a ctlSelectEvent with the new value to the event queue.

Pen goes down on the slider's thumb.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with slider's ID number.

Tracks the pen.

Pen drags slider's thumb to the left or right.

Continues tracking the pen.

Pen is lifted from slider.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlSelectEvent with the slider's ID number and new value if the coordinates are within the bounds of the slider.

Adds the ctlExitEvent if the coordinates are outside of the slider's bounds.

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 

User Action

System Response

CtlHandleEvent() Response

Pen tap on slider's background.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with slider's ID number.

Adds or subtracts the slider's page jump value from its current value and then sends a ctlRepeatEvent with the slider's new value.

ctlRepeatEvent

Adds or subtracts the slider's page jump value from its current value repeatedly until the thumb reaches the pen position or the slider's minimum or maximum. Then sends a ctlSelectEvent with slider's ID number and new value.

Pen goes down on the slider's thumb.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with slider's ID number.

Tracks the pen and updates the display.

Pen drags slider's thumb to the left or right.

ctlRepeatEvent with slider's ID number and new value.

Tracks the pen. Each time pen moves to the left or right, sends another ctlRepeatEvent if the pen is still within the bounds of the control.

Pen is dragged off the slider vertically.

ctlRepeatEvent with the slider's ID number and old value.

Pen is dragged back onto the slider.

ctlRepeatEvent with the slider's ID number and new value.

Pen is lifted from slider.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlExitEvent to the event queue.

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 ^TOP^

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.

Figure 3.6  Selector trigger

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 using CtlSetLabel().
  • Upon receiving a ctlSelectEvent for a selector trigger, call FrmDoDialog() as described in "Displaying Dialogs." If the user then dismisses the dialog by tapping the OK button, use CtlSetLabel() 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 

User Action

System Response

CtlHandleEvent() Response

Pen goes down on a selector trigger.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with selector trigger's ID number.

Inverts the button's display.

Pen is lifted from the selector trigger.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlSelectEvent to the event queue.

Pop-Up Triggers ^TOP^

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.

Figure 3.7  Pop-up trigger

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:

  1. 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.
  2. Add a list at the same coordinates.
  3. Specify that the list is unusable.
  4. 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 calling CtlSetLabel().
  • 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. Return false 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 using CtlSetLabel(). Return true 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 

User Action

System Response

CtlHandleEvent() Response

Pen goes down on the pop-up trigger.

penDownEvent with the x and y coordinates stored in EventType.

Adds the ctlEnterEvent to the event queue.

ctlEnterEvent with pop-up trigger's ID number.

Inverts the trigger's display.

Pen is lifted from button.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlSelectEvent to the event queue.

ctlSelectEvent with pop-up trigger's ID number.

Adds a winEnterEvent for the list's window to the event queue. Control passes to FrmHandleEvent(), which displays the list and adds a popSelectEvent to the event queue. Control then passes to LstHandleEvent().

Pen is lifted outside button.

penUpEvent with the x and y coordinates stored in EventType.

Adds the ctlExitEvent to the event queue.

Category Controls ^TOP^

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 ^TOP^

You create a category pop-up list the same way you create any pop-up list. In your resource editor, do the following:

  1. Create a pop-up trigger resource with a width of 0.
  2. Add a list resource at the same coordinates as the pop-up trigger. Set the list to unusable.
  3. 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:

  1. Create a selector trigger with a width of 0.
  2. 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 ^TOP^

For the most part, you can handle category controls for classic Palm OS databases using only these calls:

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, and ListCategoryList 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 and CategoryName 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 ^TOP^

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:

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, and ListCategoryList 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 least catCategoryNameLength. 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 that CatMgrSelectFilter() 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 and numInCategories contains the number of elements in inCategoryIDs.
  • Upon return, outCategoryIDs contains a list of the category IDs that the user selected, and numOutCategories contains the number of elements in outCategoryIDs. If the user does not change the selection, outCategoryIDs is NULL.
  • 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 ^TOP^

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).

Figure 3.8  Scroll bar

To do to include a scroll bar in your user interface, do the following:

  1. 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.

  2. Provide a minimum and maximum value as well as a page size.
  3. Minimum is usually 0.
  4. Maximum is usually 0 and set programmatically.
  5. The page size determines how many lines the scroll bar moves when the text scrolls.
  6. 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 use SclSetScrollBar() 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 a penDownEvent 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 previous sclRepeatEvents.

      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 the sclExitEvent.

Summary of Control Functions ^TOP^