The Global Find facility allows a user to search all databases on the device for a particular string and then jump to the application that supports the database containing the matching record he or she wants to see.
Find is launched when the user taps the Find icon in the status bar. To integrate your application with the Global Find facility, you must do the following:
- Create an
APP_LAUNCH_PREFS_RESOURCE
with ID 0 in your application's resource file. See the book Resource File Formats for more information on this resource.
- In the resource, set the
ALPF_FLAG_NOTIFY_FIND
attribute totrue
. - In your application's
PilotMain()
function, implement responses to these launch codes:
Implementing sysAppLaunchCmdFind
The Find Manager sends each application registered to receive it the sysAppLaunchCmdFind
launch code. This launch code is a signal that you should search your application's databases for the string passed to you in the launch code's parameter block. To implement the sysAppLaunchCmdFind
launch code, you should do the following:
- Open your application's record database using the open mode parameter that is passed to your application in the launch code's parameter block.
- Use
FindDrawHeader()
to pass back to the Find Manager a string that is used to delineate your application's search results from those of all other applications in the Find Results dialog. This string is only used if your application has matching results. - Search each record using
TxtFindString()
.TxtFindString()
accurately matches single-byte characters with their corresponding multi-byte characters, and it passes back the length of the matched text. You'll need to know the length of the matching text to highlight it when the system requests that you display the matching record. - If a match is found, do the following:
- Send information about the matching result back to the Find Manager using
FindSaveMatch()
. The parameters you pass toFindSaveMatch()
are used in thesysAppLaunchCmdGoTo
parameter block if the user selects your record in the Find Results dialog. - If
FindSaveMatch()
returnsfalse
, it means that there is room to display the matching record. CallWinDrawChars()
to send the text to the Find Results dialog. UseFindGetLineBounds()
to determine where to draw, and useWinDrawChars()
to do the drawing.When a Find is in progress,
WinDrawChars()
does not draw directly to the screen. The Find Manager traps allWinDrawChars()
calls and copies the string into a buffer that it later draws to the screen.
- Send information about the matching result back to the Find Manager using
IMPORTANT: Do not use
GcDrawTextAt()
in conjunction with Global Find. If you do, your string is not displayed.
- If your database is potentially large, you should occasionally check the event queue to see if there is another event pending. For example, the user might start a find but then press one of the device's hard keys, which would generate a
keyDownEvent
. Listing 2.1 shows an example of a search that checks the event queue every 16 records.
Listing 2.1 shows an example of a function that should be called in response to the sysAppLaunchCmdFind
launch code.
Listing 2.1 Implementing Global Find
static void Search (FindParamsType *findParams) { uint16_t pos; char * header; uint16_t recordNum; MemHandle recordH; MemHandle headerStringH; RectangleType r; Boolean done; Boolean match; DmOpenRef dbP; status_t err; DatabaseID dbID; MemoDBRecordPtr memoRecP; size_t longPos; size_t matchLength; // Locate the Memo database. dbID = DmFindDatabaseByTypeCreator(ty, cr, dmFindAllDB, NULL); if (!dbID) { findParams->more = false; return; } dbP = DmOpenDatabase(dbID, (DmOpenModeType)findParams->dbAccesMode); if (!dbP) { findParams->more = false; return; } // Display the heading line. headerStringH = DmGetResource(gAppResDBRef, strRsc, FindMemoHeaderStr); header = MemHandleLock(headerStringH); done = FindDrawHeader(findParams, header); MemHandleUnlock(headerStringH); DmReleaseResource(headerStringH); if (done) goto Exit; // Search the memos for the "find" string. recordNum = findParams->recordNum; while (true) { if ((recordNum & 0x000f) == 0 && // every 16th record EvtSysEventAvail(true)) { // Stop the search process. findParams->more = true; break; } recordH = DmQueryNextInCategory (dbP, &recordNum, dmAllCategories); // Have we run out of records? if (!recordH) { findParams->more = false; break; } memoRecP = MemHandleLock (recordH); // Search for the string passed, if it's found display // the title of the memo. match = TxtFindString (&(memoRecP->note), findParams->strToFind, &longPos, &matchLength); pos = longPos; if (match) { // Add the match to the find paramter block, if // there is no room to display the match the // following function will return true. done = FindSaveMatch (findParams, recordNum, pos, 0, matchLength, cardNo, dbID); if (!done) { // Get the bounds of the region where we will // draw the results. FindGetLineBounds (findParams, &r); // Display the title of the description. DrawMemoTitle (&(memoRecP->note), r.topLeft.x+1, r.topLeft.y, r.extent.x-2); findParams->lineNumber++; } } MemHandleUnlock(recordH); if (done) break; recordNum++; } Exit: DmCloseDatabase (dbP); }
Implementing sysAppLaunchCmdGoTo
When the user taps one of the results displayed in the Find Results dialog, the system sends a sysAppLaunchCmdGoTo
launch code to the application containing the matching record. In most cases, your application should use the information in the launch code's parameter block to locate the matching record and display it, with the matching text highlighted.
Listing 2.2 shows how the Memo sample application responds to the sysAppLaunchCmdGoto
launch code. It enqueues a frmGotoEvent
for its Edit form, passing to this event information about which record to display. See the Memo sample application in the SDK for full source code.
Listing 2.2 Displaying the matching record
static void GoToRecord (GoToParamsPtr goToParams, Boolean launchingApp) { uint16_t recordNum; EventType event; recordNum = goToParams->recordNum; ... // Send an event to goto a form and select the // matching text. MemSet (&event, sizeof(EventType), 0); event.eType = frmLoadEvent; event.data.frmLoad.formID = EditView; EvtAddEventToQueue (&event); MemSet (&event, sizeof(EventType), 0); event.eType = frmGotoEvent; event.data.frmGoto.recordNum = recordNum; event.data.frmGoto.matchPos = goToParams->matchPos; event.data.formGoto.matchLen = goToParams->matchLen; event.data.frmGoto.matchFieldNum = goToParams->matchFieldNum; event.data.frmGoto.formID = EditView; EvtAddEventToQueue (&event); ... }
Implementing sysAppLaunchCmdSaveData
Your application receives the sysAppLaunchCmdSaveData
launch code only if it is the current application when the user taps the Find icon. This launch code gives your application a chance to save any data that the user is in the process of entering before the Find is launched so that the search results are what the user expects.
For example, suppose that the user is editing a contact in the Address Book to change the first name from "Ted" to "Theodore." Before tapping the Done button, the user decides to do a search for any other records that contain "Ted." The user expects that the current record will not appear in the Find Results dialog.
For this reason, Palm OS® sends the Address Book the sysAppLaunchCmdSaveData
launch code to give the application a chance to clean up any activity before the Find begins. Address Book responds to this launch code by calling FrmSaveAllForms()
. FrmSaveAllForms()
enqueues a frmSaveEvent
for each open form. The event handler for the Edit form responds to the frmSaveEvent
by saving the field that is currently being edited to the database. This way, its response to the sysAppLaunchCmdFind
launch code will successfully pass over this record.