Chapter 9 Classification and Find in this chapter, we add categories in the Contacts program. Classification allows Contacts to be divided into groups such as Business and Personal. You can view these groups separately or unified. You can also add, delete, or change the category name for the Contacts application. You can assign each record to a group. We will also add code in Contacts to use the Palm OS System Find (Find) function to find relevant content in the Contacts database. You can enter a full name of a short date or a person, find a matching record in a database stored in different fields and formats. You can also select any found entry to make it displayed in the Contact Detail form. Save the project before making the next step, or remind you this. The steps are as follows: 1. Run a Windows browser; 2. Find the folder stored in the project; 3. Check the folder, press Ctrl C to copy it; 4. Select a folder to save a copy; 5. Press Ctrl V to paste the copy into the backup folder; 6. Name the project name as the name, I named it as CH.8. Classification If you want to put your app in a group, the classification can be implemented. You can define 15 groups for an application, so many groups are enough for general applications. On my Palm, there are no more than 6 groups in any application. Palm OS has a lot of classification. Once the application information is created, the Categories Manager will remain in that place. And create a drop-down box to manage, allowing creating, modifying, and deleting classification. Our main job is to separate records in the classification and browse. Prior to this, the method of processing records is exactly the same. Check this check whether it belongs to the current classification before the recording is displayed. This will trigger some questions from the scroll bar. Contacts.rsrc's Content Add three categories in the Constructor: ◆ Add pop-up list box to the Contact List form to filter the list of Contacts Forms. It must be consistent with the requirements of Category Manager; ◆ Add pop-up list box in the Contact Detail form to select the classification where the current record is located; ◆ Create an App Info String List resource, define the application in the application Initial classification; Contact List Form's content Add Now to the Contact List form, let people look at our specific categories. 1. Run MetrowerKs constructor; 2. Open the resource file Contacts.RSRC, which is located in the SRC folder in the project folder; 3. Double-click to open the Contact List form; 4. Select WINDOW | Catalog from the menu to open Catalog; 5. Drag a list box (list) to the form; 6. Setting list box Properties: Object Identifier = CategoryList, Left Origin = 86, Top Origin = 1, Width = 72, does not check the usable item, because we do not want the form to display the list, Visible Items = 0.
As part of the classification service, Palm OS will dynamically create a list (according to our requirements). 7. Drag a pop-up trigger to the form; 8. Name the trigger categoryPopup. LEFT Origin = 160, Top Origin = 0, Width = 0. Do not check Anchor Left, which makes the trigger tag text (from the left 160 pixels) and the right right side of the screen. We will quickly delete or add the text in the tag in the program. 9. After completion, the Contact List form looks as shown in Figure 9-1. Note: You don't need to set the ID ID with the general pop-up trigger, although there is no improper nothing. You will see that we are not calling the trigger's CTLSELECTEVENT event, but calls a specific classification function. Contact Detail Form Add Now We modify the Contact Detail Form, allowing people to classify specific entries. 10. Double-click to open the Contact Detail List form in the Resource Type and name list; 11. Drag a label to the form. Set the tag properties: Text is Category, select bold. Top Origin = 90, select all the labels for (holding the Shift click), then select Arrange | Align Right EDGES to align it with other right aligned labels; 12. Drag a list box control into the form; 13. Settings list box properties: Object Identifier = CategoryList. LEFT Origin = 80, Top Origin = 90, Width = 80. Not checking usable because we do not want the form box to display when the form is displayed. Set Visible Items to 0. Like other classification lists, Palm OS will dynamically establish this list; 14. Drag the pop-up trigger to the form; 15. Similar to the Contact List form, name the trigger CategoryPopup. LEFT Origin = 80, Top Origin = 89, Width = 80. As before, you can reserve or delete the text in the tag; 16. After completion, the Contact Detail form appears as shown in Figure 9-2; 17. Finally, you need to create a new App Info String List resource to initialize the category name. Click App Info String Lists in the list of constructors, and create a new one in Ctrl-K. Be careful not to create an old String List; 18. Name the app info string list Category Labels, double click to open; 19. Enter: Unfiled, Business, and Personal in the top three entries of the list: UNFILED, Business and Personal. 20. Press 12 ENTER to create 16 entries. This will initialize other classification as blank instead of rubbish. After completing, the list looks as shown in Figure 9-3. I reiterated: You must initialize all 16 entries; otherwise, some of the plenty of things will appear. This will be done for the modifications you need to support the resource file to support the classification.
Turn off Constuctor and save the resource file in time. CONTACTS.C is modified to support classification, and four tasks are required in Contacts.c: ◆ Create a data structure to comply with the requirements of Category Manager; ◆ Support for the CONTACT DETAIL form; ◆ Support Contact List Form Classification pop-up list; ◆ Contact List Forms Handling scroll bars events at different categories; initialization classification classification information is usually saved on the information module of the application master database. To do this, you need to create a database after pilotmain (), add some code. Add some new variables to the top of the function: localid dbid; // ch.9 local id of the database uint cardnum; // ch.9 card number localid appinfoid; // ch.9 local id of the app info block vidhand Happinfo; // ch.9 Handle to the app info block appinfoptr PAPPINFO; // ch.9 Points to the app info block then, look at the application information block: // ch.9 get the id and card number DmopendatabaseInfo CONTACTSDB, & DBID, NULL, NULL, & CARDNUM, NULL); //CH.9 Get The App Info Pointer IF ANY DMDATABASEINFO (CardNum, DBID, NULL, NULL, NULL, NULL, NULL, NULL, & APPINFOID, NULL, NULL , NULL); In-depth: Program Information Module (App Info Block) Each Palm OS database has a special area called App Info Block. You can save anything in this area. If you have saved the classification, you can also insert the specific data at the end of the classification structure, and the Palm OS will process this. I often use this space to save priority (preferences) or global variables in the database. For example, it can be used to store the database structure, whereby the code can be used to handle different types of databases. Tips notice that we are in detail here, rather than only specify the card number 0. Although the card number 0 card is usually working properly on Palm Compting equipment, but for some third-party hardware, especially Handspring Visor and Trgpro, they have more memory cards, in order to support a wider Palm memory unit, The correct handling card number will become important in the program. If we don't find the application information block that has created a classification, go back to the beginning of the new one.
// ch.9 if there is no application info block, create one if (appinfoid == 0) {// ch.9 allocate an application info block if ((happinfo = dmnewhandle (contactsdb, sizeof (appinfotype))) == NULL) errorExit (MemoryErrorAlert); // CH.9 Translate the handle to a local ID appInfoID = MemHandleToLocalID (hAppInfo); // CH.9 Set the application info block DmSetDatabaseInfo (cardNum, dbID, NULL, NULL, NULL, NULL, NULL, NULL, NULL, & appInfoID, NULL, NULL, NULL); // CH.9 Translate the local ID to a pointer pAppInfo = MemLocalIDToLockedPtr (appInfoID, cardNum); // CH.9 Clear it DmSet (pAppInfo, 0, sizeof (AppInfoType), 0); // CH.9 Initialize the categories CategoryInitialize (pAppInfo, CategoryLabelsAppInfoStr); // CH.9 Unlock the application info block MemPtrUnlock (pAppInfo);} when the function call when the CategoryInitialize Palm OS (), Palm OS automatically initializes classification. You need to pass the App INFO STRING LIST ID number created in the Constructor to this function. Ensure that the string used is defined in the previous part. If not, the above classification will not display or display some garbage, but categoryInitialize () will not return an error. Tips You can save anything in the application information module. If you have saved the classification, you only need to extend the classification structure in the application information module, then add the data you want to save. However, the data added to the application information module must meet the classification structure. For the support of Contact Detail Form When we browse records, the Contact Detail form should correctly display the category where each record is located, when we flip the record. It should also allow us to set the classification to a record. Unlike the Pop-Up Trigger, in order to use the Category Manager to process the event, you must capture the initial value of the classification pop-up trigger button to trigger the CTLSELECTEVENT event, and then use the Palm OS function categoryselectElect. () Complete all other work. We let the event return true to prevent Palm OS from being triggered to a new year's operation to a pop-up, and try to pop up a list box. Here, all the list boxes, pop-up, and closing are done by calling categoryselect (). In the beginning of the program, we define three variables. Variable Detailcat represents the classification of the current record in the Contact Detail form. Listcat represents the current classification of the Contact List form. The third variable TalbeIndex is defined in order to browse records in a specific classification, which will be explained in the next section.
// CH.9 Category variables static Word listCat = dmAllCategories; // CH.9 The current category ID static Word detailCat; // CH.9 Category ID for details static UInt tableIndex [TABLE_NUM_ROWS]; // CH.9 Record indexes for Rows At the top of the ContactDetailHandleevent () event, Rows requires the name of the memory to save the category: char CATNAME [DMCATEGORYLENGTH]; // ch.9 Category Name The following is the support classification pop-up trigger button code, add it to CTLSelectEvent () event: // CH.9 Catch a tap on the category trigger case ContactDetailCategoryPopupPopTrigger: {UInt recAttrs; // CH.9 the record attribs // CH.9 Palm OS will present the popup list for us CategorySelect (contactsDB. , form, ContactDetailCategoryPopupPopTrigger, ContactDetailCategoryListList, false, & detailCat, catName, 1, 0); // CH.9 Get the record attributes DmRecordInfo (contactsDB, cursor, & recAttrs, NULL, NULL); // CH.9 Put in the category bits Recattrs & = ~ DMRECATTRCATEGORYMASK; Recattrs | = Detailcat; // Ch.9 Set The Record Attributes DMsetRecordInfo (ContactSDB, Cursor, & Rettrs, null);} // ch.9 set Fields and Ret URN TRUE IN this case setfields (); return (true);} Here, categoryselect () almost all difficulties work, including creation and management list boxes and list box entries that may be edited. Note that the fifth parameter in categorySelect () is false, this parameter prompt categorySelect () We are sorted using the list box to select a classification rather than sorting the entries for the list box. This makes categoryselect () remove the all options in the list box. The eighth parameter value in categoryselect () (one of the CatName) is 1, prompt categoryselect () to prevent the first classification unfiled to be modified. Using this way, many categories can not be modified. You can specify a string as the title of the classification editing dialog using the last parameter to replace the original Edit Categories. In all examples, so far, I think this default title is the best, so I always set it to 0. Note: Palm OS1.0 has its own version of categoryselect () - categoryselectv10 (). If you are interested in supporting the original Pilot1000 to 5000 units, you need to use this command. After calling CategorySelect (), we set the value of the classified value on the currently recorded property.
First of all, DmRecordInfo () Get the properties bits. After doing some bit operation, set the new value by DMRecordInfo (). These are these during the event handling process. In the newRecord () function, we initialize its classification for each record created. At the top of the function, you need to define a variable temporary save record attribute. Uint recattrs; // ch.9 The Record's Attributes At the end of the function, as in the event handler, we get and set the properties of the record. If the classification we have selected in the Contact List is all, we don't know which one of the categories will be put this record, then we will set the classification to unfiled. If there is a specific classification above the Contact List, then put the record in this classification. // Ch.9 Get the record attribute bits DmRecordInfo (contactsDB, cursor, & recAttrs, NULL, NULL); // CH.9 Clear the category bits recAttrs & = ~ dmRecAttrCategoryMask; // CH.9 Set the category to the appropriate category if (listCat == dmAllCategories) recAttrs | = dmUnfiledCategory; else recAttrs | = listCat; // CH.9 set the record attributes DmSetRecordInfo (contactsDB, cursor, & recAttrs, NULL); in attachFields (), we need to set the pop-up trigger press Tag text. First, some new variables are defined at the top of the function. Uint recattrs; // ch.9 The record attribute bits char catname [DMCategoryLength]; // ch.6 The category name At the end of the function, we use some special classification management function settings and re-label text. // CH.9 Get the record attributes DmRecordInfo (contactsDB, cursor, & recAttrs, NULL, NULL); // CH.9 Get the category detailCat = recAttrs & dmRecAttrCategoryMask; // CH.9 Set the category popup trigger label CategoryGetName (contactsDB , Detailcat, Catname; CategorySettriggerLabel (GetObject (Form, ContactDetailcateGoryPopuppopTrigger); This is completed by modifying the code modification work that the classification can work properly on the Contact List form is completed, including the specific recording write classification. Support for Contact List Form To make more changes to the Contact List form, because we need to display only records only in a given classification or all classification. The problem is that the previous code is largely dependent on each record in the database, for example, we can display the next record via CURSOR 1. But in the classification, we can't do this, because there may be only some records to be displayed in the classification. It is to know a way that the current classification has this record is to package the record function, so that we can browse records like it before.
In the beginning of the program, declare the prototype of three new functions: static void inIndexes (void); static void scrollIndexes (int Amount); Static Uint FindIndex; Function InitIndexes () is associated with it from the current classification The index of the table TableIndex. In this way, we can browse from beginning to tail in the specified table, replacing blindly browsing between all records. Functions ScrollIndexes () allow us to move or move on a window with a special classification. Function FindIndex () returns to us a cursor location recorded in the classification. In the later part, you will see how these functions work, now we just call them to implement the features of the event List form event and other functions. What should I do at the Contact List Form Event Processing function ContactListHandleEvent ()? First, save the name of the classification at the beginning of the function. Char catname [dmcategoryLength]; // ch.9 Category Name Modify Forms Open Events, setting the label text that triggered the button, and calls InitIndexes () to initialize the list box. // CH.7 Form open event case frmOpenEvent: {// CH.7 Draw the form FrmDrawForm (form); // CH.9 Set the category popup trigger label CategoryGetName (contactsDB, listCat, catName); CategorySetTriggerLabel (getObject (form , ContactListCategoryPopupPopTrigger), catName); // CH.8 The cursor starts at the beginning cursor = 0; // CH.9 Initialize the table indexes initIndexes (); // CH.8 Populate and draw the table drawTable ();} Break; When a recording is selected to switch to the Contact Detail form, we look for the selected recorded cursor position in the TableIndex array. // ch.7 respond to a list selection case tblselect: {// ch.7 set the database cursor = TableIndex [Event-> data.tblselect.row]; // ch.7 Go to Contact Details FRMGOTOFORM (ContactDetailform);} Break; In the popselectEvent event, since the record is re-sorted according to different criteria, the TableIndex must be reinitial according to the resulting result. // ch.7 sort the contact database by the new criteria dmquicksort (ContactSDB, (DMcomparf *) sortfunc, sortby); // ch.8 Cursor Starts at Zero Cursor = 0; // ch.9 Initialize the Table Indexes InitIndexes ); // ch.8 rebuild the table DrawTable (); we call scrollIndexes () instead, down the math operation made by the down arrow repeat button.
// CH.8 Up arrow case ContactListRecordUpRepeating: scrollIndexes (-1); break; // CH.8 Down arrow case ContactListRecordDownRepeating: scrollIndexes (1); break; the same, we use the cursor keys instead of the event by calling scrollIndexes () Operation. // ch.8 UP Arrow Hard Key Case Pageupchr: ScrollIndexes (- (Table_Num_Rows - 1)); Break; // Ch.8 Down Arrow Hard Key Case Pagedownr: ScrollIndexes (Table_Num_Rows - 1); Break; In order to handle the scroll bar, We need to do two things. First, since the time to refresh the recording based on the scroll bar becomes longer, then we replace the response SCLRepeArtEvent by responding to the sclexitevent time. Second, use FindIndex () to find the cursor value of any record in the subset of classified records. We must only process scroll bars based on the current classification record. If you scroll through the table, the returned number is between the records of 0 and the top of the table. We use FindIndex () to convert this number into an absolute wage value. Once you know the actual location of the table record, we call inIntexes () to assign the remaining value to the TableIndex array. // ch.8 respond to Scrollbar Events case sclexitevent: {//ch.9 Find the recordin @ @> data.sclexit.newvalue); // ch.9 initialize ince index list inIntexes () ; // ch.8 draw the table DrawTable ();} Break; The following is the code that handles the classification pop-up trigger button. Compared to the Contact Detail form, we capture the CTLSELECTEVENT event for the trigger and pass the control to categoryselect (), and the function returns TRUE to the Palm OS in order to prevent the default operation of the pop-up trigger. Note that we use the True parameter in categoryselect (), which will be added to the Category list box to add all options. Once new categories are selected, we need to re-index and re-draw tables. // CH.9 Catch a tap on the category trigger case ctlSelectEvent:. {// CH.9 Palm OS will present the popup list for us CategorySelect (contactsDB, form, ContactListCategoryPopupPopTrigger, ContactListCategoryListList, true, & listCat, catName, 1, 0 ); // ch.9 Cursor Starts at zero cursor = 0; // ch.9 initialize the indexes initIndexes (); // ch.9 draw the table drawtable ();} // ch.9 don't let the OS Generate Other Events from this return (TRUE); DrawTable () changes include modifying a row code and clipping a whole piece of code. In initIndexes () and scrollIndex (), you can manage your duplicate buttons and scroll bars, and it is easier to know what the next information is.
Similarly, we can also use these two functions to process tables and how to reach the end of the end. Unlike Chapter 8. If the last one records the last one, all the variables are not available, check the TalbeIndex array to see if there are other records associated with this line. // ch.8 initialize the table deskles for (count = 0; count It is not much changed compared to the original drastable () function. This time it uses dmnumRecordsIncategory () and dmpositionincategory () to implement the operation of the desert button and the scroll bar. // CH.8 Get pointers to the arrow buttons upArrow = getObject (form, ContactListRecordUpRepeating); downArrow = getObject (form, ContactListRecordDownRepeating); // CH.8 Update the arrow buttons and scrollbars numRecsInCategory = DmNumRecordsInCategory (contactsDB, listCat); if (numRecsInCategory> TABLE_NUM_ROWS) {UInt position = DmPositionInCategory (contactsDB, cursor, listCat); // CH.8 Show the up arrow if (position> 0) {CtlSetLabel (upArrow, BLACK_UP_ARROW); CtlSetEnabled (upArrow, true);} else {CtlSetLabel (upArrow, GRAY_UP_ARROW); CtlSetEnabled (upArrow, false);} CtlShowControl (upArrow); // CH.8 Show the down arrow if (position> = numRecsInCategory - TABLE_NUM_ROWS) {CtlSetLabel (downArrow, GRAY_DOWN_ARROW); CtlSetEnabled (downArrow , false);} else {CtlSetLabel (downArrow, BLACK_DOWN_ARROW); CtlSetEnabled (downArrow, true);} CtlShowControl (downArrow); // CH.9 Show the scrollbar SclSetScrollBar (getObject (form, ContactListScrollbarScrollBar), positio n, 0, numRecsInCategory - TABLE_NUM_ROWS, TABLE_NUM_ROWS);} else {// CH.8 Hide the arrows CtlHideControl (upArrow); CtlHideControl (downArrow); // CH.8 Hide the scrollbar SclSetScrollBar (getObject (form, ContactListScrollbarScrollBar), 0 , 0, 0, 0);}} scrollIndexes () Function ScrollIndexes () Modified TableIndex () array, know that we may only browse only a small part, so Most of the values are still easy. Let's define some variables and get the current form pointer. We use the form pointer to get a pointer to the upwardup button. static void scrollIndexes (Int amount) {FormPtr form; UInt count; UInt index; ControlPtr downArrow; ControlPtr upArrow; UInt numRecsInCategory; // CH.9 Get the current form form = FrmGetActiveForm (); // CH.9 Get pointers to the Arrow Buttons Uparrow = getObject (Form, ContactListRecorduprepeating); DOWNARROW = GetObject (Form, ContactListRecordDowNrepeating); The remaining function is divided into two parts, one is browsing up, one is browsing down. The first is to browse down, we have been looping until our request. // ch.9 if We're Scrolling Down if (Amount> 0) {// Ch.9 While the is stock an Amount To Scroll While (Amount--) {In the loop, a record is moved each time. If we have not found a record, make the down arrows are dusty. We add the index in the list and get the latest index at the bottom of the program. // ch.9 get a new index after the last one index = TableIndex [Table_Num_Rows - 1]; IF (DMSeekRecordIncategory (ContactSDB, & INDEX, 1, DMSEEKFORWARD, LISTCAT) {// ch.9 no more records. We're done scrolling CtlSetLabel (downArrow, GRAY_DOWN_ARROW); CtlSetEnabled (downArrow, false); return;} // CH.9 Move current indexes up one for (count = 0; count Else // Ch.9 if We're Scrolling Up if (Amount <0) {// Ch.9 While The IS Still An Amount To Scroll While (Amount ) {// ch.9 Get A New INDEX BEFORE The First ONE index = tableIndex [0]; if (DmSeekRecordInCategory (contactsDB, & index, 1, dmSeekBackward, listCat)) {// CH.9 No more records We're done scrolling CtlSetLabel (upArrow, GRAY_UP_ARROW);. CtlSetEnabled (upArrow, false) Return;} // ch.9 Move Current Indexes Down One for (count = Table_Num_Rows - 1; count> 0; count -) TableIndex [count] = TableIndex [count - 1]; // ch.9 Put the index number in the array tableIndex [count] = index;} // CH.9 Disable the up arrow if needed if (DmSeekRecordInCategory (contactsDB, & index, 1, dmSeekBackward, listCat)) {CtlSetLabel (upArrow, GRAY_UP_ARROW); CtlSetEnabled (upArrow, False);} // ch.9 Enable the down arrow ctlsetlabel (Downarrow, Black_down_arrow); CTLSETENABED (DOWNARROW, TRUE);} After the browsing process in both directions, we point the snap to the top of the index and update the scroll bar. // CH.9 Set the cursor cursor = tableIndex [0]; // CH.9 Set the scrollbar numRecsInCategory = DmNumRecordsInCategory (contactsDB, listCat); SclSetScrollBar (getObject (form, ContactListScrollbarScrollBar), DmPositionInCategory (contactsDB, cursor, listCat), 0, NUMRECORDS - TABLE_NUM_ROWS, TABLE_NUM_ROWS; // ch.9 We're Done Return;} Findex () function function Findex () is quite simple, which should be attributed to the Palm OS's DMSeekRecordIncateGory () function. After setting the parameters, this function is basically corresponding to dmppensionincategory (). // CH.9 Find a particular index static UInt findIndex (UInt scrollValue) {UInt index = 0; // CH.9 Seek from zero to the scrollvalue DmSeekRecordInCategory (contactsDB, & index, scrollValue, dmSeekForward, listCat); // We ' Re Done Return (INDEX); This type of function will not return an error message, so the only way to find a wrong way is to check if the input and output of the function is correct. I believe that these functions can work after receiving the information and then find errors according to the sent things. The mistake I often made is not categoryInitialize () correctly define the App Info String List resource, which will cause a lot of blame. If you use String List to replace App Info String List to categoryInitialize (), it will not have to do anything. Similarly, remember to define 16 strings in this list, although most of them may be empty. Otherwise, you will get a number of "garbage" category names. Add monitoring if you cleave the application information module before passing to categoryInitialize (). Figures 9-4 and 9-5 are running schematic of the Contacts application. Figure 9-4 is a schematic diagram of opening a classification list in a Contact Detail form. Figure 9-5 is a schematic diagram of the pop-up trigger button in the Contact List form. The process of confidentiality record is similar to classification. One bit can define if it is confidential in each record. We can add a checkbox in the Contact Detail form to set or clear this bit. Then, if you never use the mathematical method to browse records, but use DMSeekRecordIncategory () or similar functions, the confidential record will not appear from the inside. The changes you need are mainly in the Contacts form. So you must add the selection box in the form and write the corresponding code setting record in the corresponding code setting record. Then use a similar function in the Contact List form to replace all of the mathematical methods in the Contact Detail form. Otherwise, the confidentiality record will be seen in the browsing button in the Contact Detail form. Find a lot of text if there are many text in your application, I highly recommend it to find (find). Regardless of the use of how much it is used, all specified PALM OS applications should support findings. In Contacts, we combine the entire record as a string, which can be easier to find the date, time, or full name information in the database. For the modification of Contacts.c To support Find, we will join a simple function. Declare its function prototype at the top of the program. Static uint FindIndex; This function does not use global variables to find in our database. Before running (Lunch) code, we only care about the location after the application is started, because we start here. Now our application is also triggered when calling the lookup. Similarly, if someone selects a established record, let's let the form jump to that record. This can be implemented by calling the Lunch Code. For these new code, we need to view pilotmain () to pass more information. Therefore, the variable params is defined. In-depth unused parameters we still ignore the last parameter type word. If you define a function parameter but don't use it, some compilers will give a warning message. It knows what you will do when you don't use these parameters. DWORD PILOTMAIN (WORD CMD, PTR Params, Word) {The first type of our response is the default search: Switch (cmd) {// ch.2 Normal Launch Case SysapplaunchCmdnormAllaunch: Break; if we call the system's lookup Then another application will occupy the application area. Therefore, global variables are useless to us. Then you can only save or distribute memory in the stack, and there are too many ways to use. Therefore, it is best to package the lookup operation in the functions of the functions. // ch.9 System Find Case Sysapplaunch; Return (0); If the user selects an entry for finding results, we will receive the running ID. In this case, the application has been activated, and global variables can be used at this time. // ch.9 go to item from find case sysapplaunchcmdgoto: Break; if we don't deal with the run code of the lookup, return it. // ch.2 We don't handle what's being asseted for default: return (0);} In addition to our handling, there are some running code, but the above is the most important. Other running code and a detailed description in the Palm OS SDK Reference, which is included in the CodeWarrior Lite version in the CD behind this book. In Pilotmain, we also need to consider that when we are running a program, the system FIND will reuse the previous stack. If it can't find it, the system lookup will be called at the end of the copy stack. In order to prevent this, we need to add a global variable to determine the current state. When initialization lookup, we cannot access global variables, but in the following GOTO LUNCH statement, you can access it. // ch.9 goto variable static boolean Upstack; First checking our database is open, if the database is turned on, it is best not to open again. At this point, the variable Upstack is set to TRUE to identify the program that has started running. // CH.9 Open the database if it is not already open if (contactsDB == NULL) {contactsDB = DmOpenDatabaseByTypeCreator ( 'ctct', 'PPGU', dmModeReadWrite);} else upStack = true; when selected in the search result A concrete entry is switched to the Contact Detail form. In order to do this, you can add a situation in the logic of the form initialization. If we have already run the result, move to the corresponding record in the database and display it in the Contact Detail form. The most important thing is to turn off all open forms to avoid activation when running the system lookup. Otherwise, when trying to open a form that has been opened, our app will die. Similarly, if we just start running the program, you need to return the original situation after setting the cursor and switch to the Detail form. This code is written between two frmGOTOFORM () commands. Else // ch.9 we are going to a particular recordix {// ch.9 in case our app was running before the Find frmCloseallForms (); // ch.9 Point the cursor to the found item cursor = ((GoToParamsPtr) params) -> recordNum; // CH.9 Go to the details page FrmGotoForm (ContactDetailForm); // CH.9 If we are running on top of ourselves, // return to the original event loop IF (Upstack) {UPSTACK = false; return (0);}} Find () function at the top of the function, Find () has its own individual variable list: static void find (Ptr params) {FindParamsptr Findparams = (Findparamsptr) Params; // ch. // ch.9 card Number uint cursor; // ch.9 The current record voidhand hRecord; // ch.9 handle to the record charptr precord; // ch.9 Pointer to the record DateTimetype DateTime; // ch.9 Date and time in this record char TEXTRECORD [dateStringLength 1 // C H.9 We timeStringLength 1 // build DB_FIRST_NAME_SIZE // text DB_LAST_NAME_SIZE // record here DB_PHONE_NUMBER_SIZE]; Char lcText [dateStringLength 1 // CH.9 Copy timeStringLength 1 // lower DB_FIRST_NAME_SIZE // case DB_LAST_NAME_SIZE // text here DB_PHONE_NUMBER_SIZE]; Word offset; // CH.9 offset of the match RectangleType bounds; // CH.9 Bounding rect for text SWord width; // CH.9 width of the bounds rect SWord len; / / Ch.9 Text Length Boolean Nofit; // Ch.9 DOES IT IT is distinguished from other applications, first add a title for Find. // ch.9 Draw A Title for out temis // ch.9 if there is no more room, returnix (Findparams, "Contacts") == true) return; then we open the database, get some slight The information about the findings will be used. Since we don't want to change the information in the database in the lookup operation, it is best to open the database with read-only mode. // ch.9 Open the database forreadiff ((ContactSDB = DMOPENDATABASEBYTYPECREATOR ('ctct', 'PPGU', DMModeReadOnly) == NULL) RETURN; we look for matching records through loop lookup. Note that we do not necessarily start from record 0. Start Depending on Find Manager is the first lookup or look up again in the lookup results. After the lookup is completed, a screen can only display information for a lookup. In-depth, if you support the recorded confidentiality, when you look up, you must ensure that the confidential record will not be found. In this case, we cannot find records one by one, but should use a function called DMSeekRecordIncateGory (). This function will automatically exclude the confidential record. Because the type of information in the record is varied, this requires the transformation of the data record type to the type of lookup. For example, we are in a string to look up for a string, if the user enters John Smith as a lookup string, John Smith will be found in the database. If we don't combine records in a big string, it is a recorded lookup, even if there is John Smith record in the database, we will not find it. Therefore, it is necessary to convert the date, time, etc. to text and put them in a large string. Since the embedded application is field-based, it does not support direct search. // CH.9 For each record for (cursor = findParams-> recordNum; cursor // ch.9 Copy and Convert To Lower Case StrtLower (LCText, TextRecord); // Ch.9 if There's No Match, Move ON IF ((Findstrinstr (LCTEXT, FINDPARAMS-> StRTOFIND, & Offset) == false) Continue If a matching record is found and there is space on the screen, then it is displayed on the screen, then continue to look for the next record. // ch.9 Send it to find // ch.9 if there's no more room, returnixiff (Findsavematch (FindParams, Cursor, Offset, 0, NULL, CARDNUM, DBID) == True) Break; // CH .9 Get the rectangle for our line of text FindGetLineBounds (findParams, & bounds); // CH.9 Truncate the string if necessary width = bounds.extent.x; len = StrLen (textRecord); noFit = false; FntCharsInWidth (textRecord, & width, & len, & nofit; // Ch.9 Draw the text WineraseRectangle (& Bounds, 0); Windrawchars (TextRecord, Len, Bounds.topleft.x, Bounds.topleft.y); // We buy a line in the Find Dialog (Findparams-> Linenumber) ;} // ch.9 Close The Database DMCloseDatabase (ContactSDB); // Ch.9 We're Done Return;} Turns off the database when all records are found. Debug I mainly debugged the parameters I passed in the Find function and see if I can modify it. For example, don't believe that STRTOFIND is the string you are looking for. Also, pay attention to the Palm OS does not allow the LINENUMBER value to increase. Figure 9-6 shows the results after the function of the search function. What to do next is what introduces the last chapter of the Palm OS Basics. In the following sections, some content related to software design is introduced. Chapter 10 describes how to design the user's user operation interface of Palm OS. Chapter 11 describes some of the tools used by the Palm OS application. Chapter 12 describes how to organize and modify the code to enhance its reusability. The list below is the latest version of Contacts.c, including all the modifications we are in this chapter. // ch.2 the super-incrude for the palm OS #include / DB_DATE_TIME_SIZE) #define DB_FIRST_NAME_SIZE 16 #define DB_LAST_NAME_START (DB_FIRST_NAME_START / DB_FIRST_NAME_SIZE) #define DB_LAST_NAME_SIZE 16 #define DB_PHONE_NUMBER_START (DB_LAST_NAME_START / DB_LAST_NAME_SIZE) #define DB_PHONE_NUMBER_SIZE 16 #define DB_RECORD_SIZE (DB_PHONE_NUMBER_START / DB_PHONE_NUMBER_SIZE) // CH.6 Storage for the record's date and time in expanded form static DateTimeType dateTime; static Word timeSelect; #define NO_DATE 0 #define NO_TIME 0x7fff // cH.7 The error exit macro #define errorExit (alert) {ErrThrow (alert);} // cH.7 The sort order variable and constants static Int sortBy; // cH.7 NOTE: These items match the popup list entries #define SORTBY_DATE_TIME 0 #define SORTBY_FIRST_NAME 1 #define SORTBY_LAST_NAME 2 // CH.8 Table constants #define TABLE_NUM_COLUMNS 3 #define! TABLE_NUM_ROWS 11 #define table_column_date 0 #define table_column_time 1 #define table_column_name 2 #define black_up_arrow "/ x01" #define black_down_arrow "/ x02" #define Grava Y_UP_ARROW "/ x03" #define GRAY_DOWN_ARROW "/ x04" // CH.9 Category variables static Word listCat = dmAllCategories; // CH.9 The current category ID static Word detailCat; // CH.9 Category ID for details static UInt tableIndex [Table_Num_Rows]; // Ch.9 Record Indexes For Rows // Ch.9 Goto Variable Static Boolean Upstack; // Ch.2 The Main Entries Point DWORD PILOTMAIN (Word CMD, Ptr Params, Word) {DWORD ROMVERSION; // Ch.4 ROM Version Localid DBID; // Ch.9 Local ID of The Database Uint CardNum; // Ch.9 Card Number Localid AppInfoid; // Ch.9 Local ID of The App Info Block Voidhand Happinfo; // CH. 9 Handle to the app info block appinfoptr Pappinfo; // ch.9 Points to the app info block formptr form; // ch.2 a Pointer to out; // ch. // ch.3 Error Word // CH. 4 Get the ROM version romVersion = 0; FtrGet (sysFtrCreator, sysFtrNumROMVersion, & romVersion); // cH.4 If we are below our minimum acceptable ROM revision if (romVersion AppInfoid, NULL, NULL, NULL); // Ch.5 Get The Number of Records in The Database NumRecords = DmnumRecords (ContactSDB); // Ch.5 Initialize The Record Number Cursor = 0; // Ch.7 Choose Our Starting Page // ch.5 if there is all no records, create one if (numercords == 0) {newrecord (); frMgotoform (contactDetailform);} else // ch.9 we are going to a particular recordiffiffness (cmd == Sysapplaunchcmdgoto) {// ch.9 in case our app WAS Running Before The Find frmCloseallForms (); // ch.9 Point the cursor to the found item cursor = (gotoparamsptr) params -> Recordnum; // ch. Go to the details page frMotoform (ContactDetailform); // ch.9 if we are running on top of oorselves, // Return to the original event loop if (upstack) {UPSTACK = FALSE; RETURN (0);}} ELSE / / Ch.7 Display the List frmgotoform (contactListform); // ch.7 begin the try block errtry {// ch.2 get the next esent evtgetEvent (& Event, -1); // ch.2 Handle System Events IF (Syshandleevent (& Event) )) Continue; // ch.3 Handle Menu Events if (MenuHandleevent (Null, & Event, & Error)) Continue; // Ch.4 Handle Form loading Events if (Event.Type == frmloadevent) {// ch.4 Initialize our form switch (event.data.frmLoad.formID) {// cH.4 Contact Detail form case ContactDetailForm: form = FrmInitForm (ContactDetailForm); FrmSetEventHandler (form, contactDetailHandleEvent); break; // cH.4 About form case AboutForm: form = FrmInitForm (AboutForm); FrmSetEventHandler (form, aboutHandleEvent); break; // cH.6 Enter Time form case EnterTimeForm: form = FrmInitForm (EnterTimeForm); FrmSetEventHandler (form, enterTimeHandleEvent); break; // CH.7 Contact List form case ContactListForm: form = FrmInitForm (ContactListForm); FrmSetEventHandler (form, contactListHandleEvent); break;} FrmSetActiveForm (form);} // CH.2 Handle form events FrmDispatchEvent (& event); // CH .2 if it's a stop event, exit} while (event.type! = Appstopevent); // ch.7 end The try block and do the catch block} errcatch (errooralert) {// ch.7 Display the appropriate Alert frMalert (Erroralert);} ErrendCatch // Ch.5 Close All Open Forms frmCloseallForms (); // ch.5 Close the Database DmCloseDatabase (ContactSDB); // ch.2 We're done return (0);} // ch .4 Our Contact Detail form handler function static Boolean contactDetailHandleEvent (EventPtr event) {FormPtr form; // cH.3 A pointer to our form structure VoidPtr precord; // cH.6 Points to a database record Char catName [dmCategoryLength]; / / Ch.9 category name //Ch.3 get u p FRMOPENEVENT: {// ch.2 Draw the form frmdrawform (form); // ch.5 draw the database fields setfields ();} Break; // ch.5 Form Close Event Case FRMCLOSEEVENT: {//CH.5 Store Away Any Modified Fields getfields ();} Break; // ch.5 Parse The Button Events Case CTLSelectEvent: {// ch.5 Store Any Field Changes getfields (); switch (event-> data.ctlselect.control) {/ / Ch.5 First Button Case ContactDetailFirstButton: {// ch.5 set the cursor to the first recordiffiffiffness {0) CURSOR = 0;} Break; // ch.5 Previous Button Case ContactDetailPrevbutton: {// ch. 5 Move The Cursor Back One Record IF (Cursor> 0) CURSOR ---; // ch.5 Next Button Case ContactDetailNextButton: {// Ch.5 Move The Cursor Up One Record IF (Cursor <(NumRecords - 1)) CURSOR ;} Break; // Ch.5 Last Button Case ContactDetailstButton: {// Ch.5 Move The Cursor To The Last Record IF (Cursor <(NumRecords - 1)) Cursor = NumRecords - 1;} Break; // Ch.5 Delete Button Case ContactDetLetebutton: {// ch.5 Remove The Record from the Record from T Database DMREMOVERECORD (ContactSDB, Cursor); // Ch.5 Decrease The Number of Records NumRecords -; // Ch.5 Place The Cursor At The First Record Cursor = 0; // CH.5 If There No Records LEFT, Create One if (NumRecords == 0) newRecord ();} Break; // ch.5 New Button Case ContactDetailnewButton: {// ch.5 Create a new record newrecord ();} Break; // ch.7 Done Button case ContactDetailDoneButton: {// cH.7 Load the contact list FrmGotoForm (ContactListForm);} break; // cH.6 Date selector trigger case ContactDetailDateSelTrigger: {// cH.6 Initialize the date if necessary if (dateTime.year == NO_DAT E) {DateTimeType currentDate; // CH.6 Get the current date TimSecondsToDateTime (TimGetSeconds (), ¤tDate); // CH.6 Copy it dateTime.year = currentDate.year; dateTime.month = currentDate.month; dateTime. Day = currentdate.day;} // ch.6 POP Up The System Date Selection Form Selectday (SelectdayByDay, & (DateTime.day), & (DateTime.year), "Enter Date"); // cH.6 Get the record hrecord = DmQueryRecord (contactsDB, cursor); // cH.6 Lock it down precord = MemHandleLock (hrecord); // cH.6 Write the date time field DmWrite (precord, DB_DATE_TIME_START, & dateTime, SizeOf (datetimetype)); // ch.6 Unlock The Record MemHandleunlock (HRECORD); // CH.6 Mark the record dirty isDirty = true;} break; // CH.6 Time selector trigger case ContactDetailTimeSelTrigger: {// CH.6 Pop up our selection form FrmPopupForm (EnterTimeForm);} break; // CH. 5 Sync the current record to the fields setFields ();} break; // cH.5 Respond to field tap case fldEnterEvent: isDirty = true; break; // cH.3 Parse menu events case menuEvent: return (menuEventHandler (event) Break;} // ch.2 We're Done Return (false);} // ch.4 Our About form Event Handler Function Static Boolean AboutHandleEvent (EventPtr Event) {FormPtr form; // ch.4 a Pointer To Our form structure /// ch.4 get = frMgetActiveform (); // ch.4 respond to the open event if (event-> eType == frmopnevent) {// ch.4 Draw the FormdrawForm (Form) } // ch.4 return to the calling form if (event-> eType == ctlselectEvent) {frmreturntoform (0); // ch.4 always returnim in this case return (true);} // ch. 4 We're Done Return (false);} // ch.6 Our Enter Time form event handler function static Boolean enterTimeHandleEvent (EventPtr event) {FormPtr form; // CH.6 A form structure pointer static DateTimeType oldTime; // CH.6 The original time // CH.6 Get our form pointer form = FrmGetActiveForm () ; // ch.6 Switch on the Event switch (event-> eType) {// ch.6 initialize the form, frmopnevent: {// ch.6 store the time value oldtime = datetime; // ch.6 Draw IT FrmDrawForm (form); // cH.6 Set the time controls setTimeControls ();} break; // cH.6 If a button was repeated case ctlRepeatEvent: // cH.6 If a button was pushed case ctlSelectEvent: {Word buttonID ; // ch.6 the id of the button // ch.6 set the ID buttonid = Event-> data.ctlSelect.controlID; // CH.6 Switch on button ID switch (buttonID) {// CH.6 Hours button case EnterTimeHoursPushButton: // CH.6 Minute Tens button case EnterTimeMinuteTensPushButton: // CH.6 Minute Ones button case Entertainmentton: {// ch.6 if no time WAS set if (datetime.Hour == no_time) {// ch.6 set the time to 12 pm DateTime.Hour = 12; DateTime.minute = 0; // CH. 6 Set the controls setTimeControls ();} // cH.6 Clear the old selection if any if (timeSelect) CtlSetValue (getObject (form, timeSelect), false); // cH.6 Set the new selection CtlSetValue (getObject (form , buttonID, True; TimeSelect = ButtonId;} Break; // ch.6 Up button entertimetimeuprepeating: {// ch.6 if there's no time, do nothingiff (datetime.hour == no_time) Break; // Ch.6 based on what putton is successd switch (timeselect) {// ch.6 increable hours entertimeHourspushbutton: {// ch.6 increment Hours DateTime.Hour ; // ch.6 if IT WAS 11 AM, MAKE IT 12 am if (datetime.h Our == 12) DateTime.Hour = 0; // Ch.6 if IT WAS 11 PM, Make IT 12 PM IF (DateTime.Hour == 24) DateTime.Hour = 12;} Break; // Ch.6 Increase Tens of minutes case entertimeMinuteTenspushbutton: {// ch.6 increment minutes datetime.minute = 10; // ch.6 if it WAS 5X, Roll Over if (DateTime.minute> 59) DateTime.minute - = 60;} Break ; // ch.6 Increase Minutes Case Entertainment: {// ch.6 Increment Minutes DateTime.minute ; // Ch.6 if IT IS ZERO, SUBTRACT TEN IF ((DateTime.minute% 10) == 0) DateTime. Minute - = 10;} Break;} // Revise the controls setTimeControls (); // CH.6 Down button case EnterTimeTimeDownRepeating: {// CH.6 If there's no time, do nothing if (dateTime.hour == NO_TIME) break; // CH.6 Based on what push button is selected switch (timeSelect) {// ch.6 Decrease Hours Case Entertainment Hourspushbutton: {// Ch.6 Decrement Hours DateTime.Hour -; // Ch.6 If IT WAS 12 AM, Make IT 11 AM IF (DateTime.Hour == -1) DateTime.Hour = 11; // Ch.6 if IT WAS 12 PM, Make IT 11 PM IF (DateTime.Hour == 11) DateTime.Hour = 23;} Break; // Ch.6 Decrease Tens of Minutes Case EntertimeMinuteTenspushbutton : {// Ch.6 Decrement Minutes DateTime.minute - = 10; // Ch.6 IF IT WAS 0X, Roll Over IF (DateTime.minute <0) DateTime.minute = 60;} Break; // CH. 6 Decrease MINUTES CASE EntertimeminuteOnSpushButton: {// ch.6 Decrement -; // Ch.6 if IT IS 9, ADD TEN IF ((DateTime.minute% 10) == 9) DateTime.minute = 10; // ch.6 if Less Than Zero, Make It 9 IF (DateTime.minute <0) DateTime.minute = 9;} Break;} // ch.6 Revise THE Contro LS setTimeControls ();} Break; // ch.6 am Button case entertimeampushbutton: {// ch.6 if no time WAS set if (datetime.hip == no_time) {// ch.6 set the time to 12 am DateTime.Hour = 0; datetime.minute = 0; // ch.6 set the controls setTimeControls ();} // ch.6 if IT IS PM if (DateTime.Hour> 11) {// ch.6 Change To Am DateTime.Hour - = 12; // Ch.6 set the controls settimecontrols ();}} Break; // ch.6 Pm Button Case EntertimePmpmpushbutton: {// ch.6 if no time WAS SET IF (DateTime. Hour == NO_TIME) {// ch.6 set the time to 12 pm DateTime.Hour = 12; datetime.minute = 0; // ch.6 set the controls setTimeControls (); } // ch.6 if it is amix (datetime.Hour <12) {// ch.6 change to PM datetime.Hour = 12; // ch.6 set the controls settimeControls ();}} // ch.6 no time checkbox case entertimenotimeCheckbox: {// ch.6 if we are unchecking the box if (datetime.hour == no_time) {// ch.6 set the time to 12 pm DateTime.Hour = 12; dateTime.minute = 0; // cH.6 Set the controls setTimeControls (); timeSelect = EnterTimeHoursPushButton // cH.6 Set the new selection; CtlSetValue (getObject (form, timeSelect), true);} else // cH.6 If we are checking the box dateTime.hour = NO_TIME; // cH.6 Set the controls setTimeControls ();} break; // cH.6 Cancel button case EnterTimeCancelButton: {// cH.6 Restore time dateTime = oldTime; / / Ch.6 return to calling form frmreturntoform (0);} // ch.6 always return true return (true); // ch.6 ok button Case Entertainment; // ch.6 points to the record // ch.6 Lock it Down Precord = MemHandLock (HRECORD); // Ch.6 Write The Date Time Field DmWrite (precord, DB_DATE_TIME_START, & dateTime, sizeof (DateTimeType)); // CH.6 Unlock the record MemHandleUnlock (hrecord); // CH.6 Mark the record dirty isDirty = true; // CH.6 Return to the Contact Details Form frmreturntoform (0); // ch.6 Update the field setTimetrigger ();} // ch.6 always return true return (true);}} Break;} // ch.6 We're Done Return (False) ;} // cH.7 Our Contact List form event handler function static Boolean contactListHandleEvent (EventPtr event) {FormPtr form; // cH.7 A form structure pointer Char catName [dmCategoryLength]; // CH.9 Category name // CH .7 get outactiveform (); // ch.7 Parse Events switch (event-> eType) {// CH.7 Form open event case frmOpenEvent: {// CH.7 Draw the form FrmDrawForm (form); // CH.9 Set the category popup trigger label CategoryGetName (contactsDB, listCat, catName); CategorySetTriggerLabel ( getObject (form, ContactListCategoryPopupPopTrigger), catName); // CH.8 The cursor starts at the beginning cursor = 0; // CH.9 Initialize the table indexes initIndexes (); // CH.8 Populate and draw the table drawTable ( );} Break; // ch.7 respond to a list selection case tblsectevent: {// ch.7 set the Database Cursor to the successdund Cursor = TableIndex [Event-> Data.TBLSelect.Row]; // ch. 7 Go to contact details FrmGotoForm (ContactDetailForm);} break; // cH.7 Respond to a menu event case menuEvent: return (menuEventHandler (event)); // cH.7 Respond to the popup trigger case popSelectEvent: {// Ch.7 if there is no change, we're done if (sortby == event-> data.popselect.selection) Return (TRUE); // ch.7 modify sort order variable sortby = event-> data.popselect .Selection; // ch.7 sort the contact database by the new criteria DMQUICKSORT (ContactSDB, (DMComparf *) sortfunc, sortby); // ch.8 Cursor Starts At Zero Cursor = 0; // Ch.9 Initialize The Table Indexes InitIndexes (); // Ch.8 Rebuild The Table DrawTable ();} Break; // Ch. arrow case ContactListRecordUpRepeating: scrollIndexes (-1); break; // CH.8 Down arrow case ContactListRecordDownRepeating: scrollIndexes (1); break;} // CH.8 Now refresh the table drawTable ();} break; // CH. 8 Respond to Up and Down Arrow Hard Keys Case Keydownevent: {Switch (Event-> data.keyDown.chr) {// CH.8 Up arrow hard key case pageUpChr: scrollIndexes (- (TABLE_NUM_ROWS - 1)); break; // CH.8 Down arrow hard key case pageDownChr: scrollIndexes (TABLE_NUM_ROWS - 1); Break;} // ch.8 count;} Break; // ch.8 respond to scrollbar events case sclexitevent: {//ch.9 Find The record in in @ @ (Event-> Data.SCLEXIT.NEWVALUE); // ch.9 Initialize out, index listindexes (); // ch.8 draw the table drawtable ();} Break; // ch.9 catch a Tap on the category Trigger Case CTLSEventEvent: . {// CH.9 Palm OS will present the popup list for us CategorySelect (contactsDB, form, ContactListCategoryPopupPopTrigger, ContactListCategoryListList, true, & listCat, catName, 1, 0); // CH.9 Cursor starts at zero cursor = 0; // ch.9 initialize the indexes initIndexes (); // ch.9 draw the Table DrawTable ();} // ch.9 don't let the os generate};} // ch .7 end of the Event switch statement // ch.7 WE 'reed return (false);} // ch.3 Handle Menu Events Boolean MenueventHandler (eventptr form) {FormPtr form; // ch.3 a Pointer to Our Form Structure Word Index; // Ch.3 A General Purpose Control Index Fieldptr Field; // Ch.3 Used for Manipulating Fields // Ch.3 Get Our Form Pointer Form = frMgetActiveform (); // Ch.3 Erase The Menu Status from The Display Menuerasestatus (NULL); //CH.4 Handle Options Menu if (Event-> Data.Menu.ItemId == OptionsAboutContacts) {// ch.4 POP Up The About Form as a Dialog FRMPOPUPFORM (TRUE);} // ch.3 Handle Graffiti Help IF (event-> data.Menu.Itemid == EditGraffitiHelp) {// CH.3 Pop up the graffiti reference based on // the graffiti state SysGraffitiReferenceDialog (referenceDefault); return (true);} // CH.3 Get the index of our field index = FrmGetFocus (form); / / Ch.3 if there is no field selected, we're done if (index == nofocus) Return (false); // ch.3 get the pointer of out = frMgetObjectPtr (Form, Index); //C .3 Do The Edit Command Switch (Event-> Data.Menu.Itemid) {// Ch.3 undo Case Editun: Fldundo (Field); Break; // Ch.3 Cut Case Editcut: Fld Cut (Field); Break; // ch.3 Copy Case EditCopy: FldCopy; Break; // Ch.3 Paste Case EditPaste: FldPaste (Field); Break; // Ch.3 Select All Case EditsElectall: {// ch.3 Get The Length of the string in the field word length = fldTextLength (Field); // ch.3 Sound an error if appropriate if (length == 0) {SndPlaySystemsound (SNDERROR); return (false);} // ch.3 SELECT The whole string fldsetSelection (Field, 0, Length);} Break; // ch.3 Bring Up T he keyboard tool case EditKeyboard: SysKeyboardDialogV10 (); break;} // CH.3 We're done return (true);} // CH.5 This function creates and initializes a new record static void newRecord (void) {VoidPtr precord ; // cH.5 Pointer to the record UInt recAttrs; // CH.9 The record's attributes // cH.7 Create the database record and get a handle to it if ((hrecord = DmNewRecord (contactsDB, & cursor, DB_RECORD_SIZE)) == NULL) ErroRexit (MemoryErrorlert); // Ch.5 Lock Down The Record To Modify It PRecord = MemHandLock (HRECORD); // Ch.5 Clear The Record DMSET (PRecord, 0, DB_Record_size, 0); //CH .6 Initialize the date and time memset (& DateTime), 0) dateTime.year = NO_DATE; dateTime.hour = NO_TIME; DmWrite (precord, DB_DATE_TIME_START, & dateTime, sizeof (DateTimeType)); // CH.5 Unlock the record MemHandleUnlock (hrecord); // CH.5 Clear the busy bit and set the dirty bit DmReleaseRecord (contactsDB, cursor, true); // cH.5 Increment the total record count numRecords ; // cH.5 Set the dirty bit isDirty = true; // Ch.9 Get the record attribute bits DmRecordInfo (contactsDB , cursor, & recAttrs, NULL, NULL); // CH.9 Clear the category bits recAttrs & = ~ dmRecAttrCategoryMask; // CH.9 Set the category to the appropriate category if (listCat == dmAllCategories) recAttrs | = dmUnfiledCategory; else Recattrs | = listcat; // ch.9 Set The Record Attributes DMsetRecordInfo (ContactSDB, Cursor, & Rettrs, Null); // Ch.5 We're Done Return;} // ch.5 a Time Saver: Gets Object Pointers Based On Their ID Static voidptr getObject (FormPtr form, word objectid) {Word index; // ch.5 the object index // ch.5 get the index index = frMgetObjectIndex (FORM, ObjectID); // ch.5 return the pointer return (formgetObjectptr (form, index));} // ch.5 Gets The Current Database Record and Displays It // in The Detail Fields Static Void SetFields (VOID) {Formptr form; // ch.5 the contact; // ch.6 a record Pointer Word Index; // ch.5 The Object Index uint recattrs; // ch.9 The record attribute bits char catname DMCategoryLength]; // ch.6 The category name // ch.5 get the contact detail form Pointer form = frMgetActiveform (); // ch.5 get the current record hRecord = DMQueryRecord (ContactSDB, CURSOR); //C. 6 INITIALIZE The Date and Time Variable PRecord = MemHandLock (HRECORD); MemMove (& dateTime, precord DB_DATE_TIME_START, sizeof (dateTime)); // CH.6 Initialize the date control setDateTrigger (); // CH.6 Initialize the time control setTimeTrigger (); // CH.5 Set the text for the First Name field setText (getObject (form, ContactDetailFirstNameField), precord DB_FIRST_NAME_START); // cH.5 Set the text for the Last Name field setText (getObject (form, ContactDetailLastNameField), precord DB_LAST_NAME_START); // cH.5 Set the text for the Phone Number field setText (getObject (form, ContactDetailPhoneNumberField), precord DB_PHONE_NUMBER_START); MemHandleUnlock (hrecord); // cH.5 If the record is already dirty, it's new, so set focus if (isDirty) {/ / cH.3 Get the index of our field index = FrmGetObjectIndex (form, ContactDetailFirstNameField); // cH.3 Set the focus to the First Name field FrmSetFocus (form, index); // cH.5 Set upper shift on GrfSetState ( False, false, true;} // ch.9 Get The Record Attributes DMRecordinfo (ContactSDB, CURSO r, & recAttrs, NULL, NULL); // CH.9 Get the category detailCat = recAttrs & dmRecAttrCategoryMask; // CH.9 Set the category popup trigger label CategoryGetName (contactsDB, detailCat, catName); CategorySetTriggerLabel (getObject (form, ContactDetailCategoryPopupPopTrigger ), CATNAME); // ch.5 We're done return;} // ch.5 Puts any Field Changes in The Record Void getfields (void) {Formptr form; // ch.5 the Contact Detail Form //C .5 Get the Contact Detail form Pointer form = frMgetActiveForm (); // ch.5 Turn Off Focus frMsetfocus (form, -1); // ch.5 i t Record HAS been Modified if (isdirty) {Charptr Precord