Seventh chapter list box and sort in this chapter, will be touched to some new controls and database operation skills. Let's make a form to display all records in the Contact database, then create a drop-down box for sequencing standard, finally add code to sort, and make the newly created or modified records can also be properly arranged in the list. Save the project to save the project according to our habit, steps below: 1. Run a Windows browser; 2. Find a folder that saves existing projects; 3. Press CTRL-C replication; 4. Select the destination folder; 5. Press CTRL-V to paste existing projects to the destination folder; 6. Renn it as a name that is easy to remember, I name it with Contacts Ch.6. The list box list box can display a number of text entries and allow you to choose from it, we can browse or choose each entry. We can also browse a list or select an entry by calling a function. Of course, we can draw your own list box, in the default, the list box is drawn by the PALM system. Contacts.rsrc's content Add in this section, we will add a new form to the application. This form will display database records in the form of a list and allow us to browse or select. If one of the records is selected, the recording content will be displayed on the Contact Detail form. The content of the Contact Detail Form is added because the Contact Detail form is called to return a record content, so we need to add a DONE button. The steps are as follows: 1. Running constructor; 2. Open the resource file Contact.RSRC, which is located in the SRC folder of the project folder; 3. Double click to open; 4. Select Windows | Catalog Open Control Templates; 5. Drag a button to the Contact Detail form; 6. Set button properties Object Identifier is DONE, LEFT Origin = 1, Top Origin = 147, Label is DONE; 7. After the addition is completed, the result button is shown below, select the X button in the upper right corner to turn off the editor. Creating a menu bar on a Contact List form By Creating a Contact List Form, we create a menu first; then when setting the properties of the form, you can write directly to the ID of this menu bar. 1. Select Menu Bars in the Resource Type and Name list box and press Ctrl-K, so a new menu is generated, rename it as Contact List; 2. Double click to open; 3. Drag the Option menu to the Contact List menu bar; 4. After completion, you should click on the X button in the upper right corner to close the menu bar. Creating a Contact List Form Now let's create a Contact List form, steps below: 1. Click Forms and press Ctrl-K to create a new form; 2. Click the Form Name to name it Contact List; 3. Double-click to open the Contact List form in the Form Editor; Add a title for the Contact List and match the ID of the menu bar's ID and the ID of the menu bar of the Contact List form.
Create a list box (List) 1. Drag a list box (List) into the form from the Catalog window. Its attribute is shown in Table 7-1; Object Identifier In the resource header file, the resource ID of the resource ID list in the resource header file is the most left-end position of the control of the control of the control of the control of the control of the control of the control. The width of the top position width list determines if this control can be visible, if not, you can also display the number of font Visible items list boxes that can be visible through the function call. Note that the actual list of entries can be more than this value. List Items dynamic initialization list entry, if you want to add an entry, press CTRL-K 2. Setting attribute Object Identifier is List, Left Origin = 0, TOP Origin = 12, Width = 160, Visible Items = 12; 3. After completing the form, as shown below, press the X button in the upper right corner to turn off the form and select File | Save Save the modification. Add a memory error warning in this chapter, we will start adding an error handling code. When the memory overflows, the system will issue a warning message: 1. Click on Alerts in the Resource Type and Name list and click Ctrl-K to create a new alert; 2. Name it MemoryError; 3. Double-click, open in the editor; 4. Setting Properties Alert Type for ERROR, Title (Title) is Fatal Error, Information (Message) is "I Have Run Out of Memory." 5. After completing, as shown below: Contact.c's content adds the code to complete the function of the new form. First, add the code to an abnormality processing in the previous chapter. Abnormal Processing In this chapter, there are many places that may cause memory overflow. Therefore, we must add a strict exception handler. Below we will use the error manager to complete, the exception processor is discussed in detail in the Palm OS SDK Reference. First, before pilotmain (), define an abnormal exit (exit) macro: // ch.7 The error exit macro #define errorexit (Alert) {Errthrow (Alert);} Exception handler errthrow () Some of them are different, it With macro ERRTRY and ERRCATCH (), there is a form of exception handling in the form of C and Java. If any code in the Errtry module calls the Errthrow () function, the control will immediately call the Errcatch () function. Let's take a look at what modifications do in pilotmain ().
Adding Hong Errtry: // ch.7 Begin The Try Block Errtry {// Ch.2 Our Errt loop DO {Errcatch () Function: // Ch.7 End The Try Block And do the catch block} {// ch.7 Display the appropriate Alert Frmalert;} ErrendCatch This function displays its corresponding abnormal alert according to the ID number transmitted to ErRCATCH () in ErrtHROW. Then, the application will properly exit after the CATCH module, and perform the operation of the shutdown database so as to do normally. Finally, pilotmain () returns the control to Palm OS. Switch below the Contact List Form, we add code to implement the switch between the Contact List form and the Contact Detail form. In Pilotmain (), the code to modify the initial Contact Detail form is as follows: // ch.7 choاarting page // ch.5 if there is no records, create one if (numercords == 0) {newRecord (); FrMgotoform (ContactDetailForm); Else frmgotoform (ContactListForm); Judging if there is no record in the database, it is necessary to create a new record in the database; in general, we can be directly Call the Contact List form. For Contact List form correctly initialized, frmLoadEvent event-handling code in the loop in case items to add: // CH.7 Contact List form case ContactListForm: form = FrmInitForm (ContactListForm); FrmSetEventHandler (form, contactListHandleEvent); Break; The following code is how to deal with Done button in the Contact Detail form: // Ch.7 Done Button Case ContactDetAildoneButton: {// ch.7 loading the contact list frmgotoform (contactListform);} Break; in the form Switching, rather than popping up, we used the function frmgotoform () to initialize. This function first calls frMCOSEEvent to close the previous form, then call FRMLoadEvent and frmopnevent to open a new form. Contact List Form Event Processing Functions Let's add event handlers to a Contact List form.
First, add prototypes at the beginning of the file: Static Boolean ContactListHandleEvent (EventPtr Event); In addition, two variables are required to represent the handle of memory to the list box: // ch.7 Contact List variables static voidhand hchoices; // ch.7 handle to packed choices static VoidHand hpchoices; // cH.7 handle to pointers Here is the event handler: // cH.7 Our Contact List form event handler function static Boolean contactListHandleEvent (EventPtr event) {FormPtr form; // cH.7 A Form Structure Pointer // Ch.7 Get Our Form Pointer Form = frMgetActiveForm (); // ch.7 Parse Events Switch (Event-> ETYPE) {// Ch.7 Form Open Event Case FRMOPENEVENEVENEVENT: {//C. 7 Draw the form frmdrawform (form); // ch.7 buildate the list buildlist ();} Break; // ch.7 Form Close Event Case FRMCLOSEEVENT: {// ch.7 Unlock and Free Things Here MemHenLock (HPChoices) ; MemHandleFree (hpchoices); MemHandleUnlock (hchoices); MemHandleFree (hchoices); hchoices = 0;} break; // cH.7 Respond to a list selection case lstSelectEvent: {// cH.7 Set the database cursor to the selected contact Cursor = Event-> Data .lstselect.selection; // ch.7 Go to contact Details frMotoform (contactDetailform);} Break; // ch.7 respond to a menu event case menuevent: return (menueventhandler (event)); // ch.7 respond to The popup trigger case popselect: {// 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. Break;} // ch.7 end of the event switch statement // ch.7 We're done return (false)
} The event handler starts in a very standard form: first establish a form pointer variable, which will be called when calling frMopnevent; then the form is drawn and called a new function buildlist (), this function will be in the next part Discussed in detail. We call FRMCLOSEEVENT release to store the memory of the list box content. The reason why the memory is released in a closed (Close) event is because the form is in use, this part of the memory has been used by Palm OS. At this point, the list controls and other controls are different, and all the general controls have their own memory, and for the list control, it needs to be assigned memory. Here, a new event handler is described - LstselectEvent. This event will be triggered when the entry of the list is selected. Variable Selection indicates which entry is selected, and the list is recorded from 0. This is very convenient to use, as long as the current record is equal to the value of the list variable Selection, then call the Contact Detail form, the form will use the previously set variable Cursor to generate the correct record. Finally, the processing of this program menu event and the menu processing of the Contact Detail form is almost the same. In this way, the handler for the Contact List form is completed. Function BuildList () Let's discuss this utility buildlist (), this function creates a text string for each record by browsing the database, and then pops the various entries of the List object. Static void buildlist (void) {Formptr form; // ch.6 a form structure pointer int kiice; // ch.7 the list choice we're doing charptr code; // ch.7 Pointer to a record char Listchoice [dateStringLength 1 // cH.7 We timeStringLength 1 // build DB_FIRST_NAME_SIZE // list DB_LAST_NAME_SIZE]; // choices here // cH.7 The current list choice CharPtr pchoices; // cH.7 Pointer to packed choices UInt Offset; // ch.7 Offset Into Packed Strings Voidptr Ppchoices; // Ch.7 Pointer TO POINTERS To Choices // Ch.6 Get Our Form Pointer FormGGTACTIVEFORM (); After declaring variables, the function icon is usually the same as the window Body pointer. The most interesting variables in this is char Array, which will guarantee the continuity of the list string. In order to make the array (array) to save any Palm OS system time and date, Palm OS constants can be used to use Palm OS constants DatestringLength and TimeStringLength.
// CH.7 Put the list choices in a packed string for (choice = 0; choice // ch.7 allocate memory for the list entry string /// ch.7 if this is the first choff (hchoices == 0) {// ch.7 allocate the storage for the choiceiff (hchoices = memagelen " (ListChoice) 1)) == 0) ErroREXIT (MemoryErRoralert); // ch.7 Initial Offset Points to the start offset = 0;} The above is the code that is not created, and the memory is allocated to the string. If the new error handling function is called if it fails. Then we initialize the offset (Offset) Description Where to set the character to zero. The following is a string has been created: Else // Ch.7 if this is a subsequent choice {// ch. Listchoice) 1)) ErrorExit (MemoryErrorlert);} Here we will lock the memory unlock to save the next string. Then we write a string to the list list (Packed List): // ch.7 lock pchoices = MemHandLock (hchoices); // ch.7 Copy The String Into The Memory StRCopy (PChoices Offset, Listchoice); Offset = Strlen (Listchoice) 1; // Ch.7 Unlock The Record MemHandleunlock (HRECORD);} First we lock the master block (CHUNK) and copy the new string. After doing these work, unlock the database record handle we use. Note that we didn't call the function DmreleaseRecord () because we used the function DMQueryRecord () instead of DMGETRECORD (). After the loop operation is complete, we have a list string package that contains each database record. Now we will send these options to display a list of objects: // CH.7 Create a pointer array from the packed string list if ((hpchoices = SysFormPointerArrayToStrings (pchoices, numRecords)) == 0) errorExit (MemoryErrorAlert); ppchoices = MemHandleLock ( hpchoices); // cH.7 Set the list choices LstSetListChoices (getObject (form, ContactListListList), ppchoices, numRecords); // cH.7 Draw the list LstDrawList (getObject (form, ContactListListList)); // cH.7 We 'Re Done Return;} We use the function sysformpointerarraytostrings () to establish a pointer to the list of the list. Such a pointer to such a list is determined. Function LSTSETLISTCHOICE () uses a pointer to the package list to populate each entry. Note that this function will empty all existing entries, so when sending a list item to this function, it must be a full package list entry. Remember to release all memory through the Form Close event through the form closure event. At this end, we draw the list box. Debugging in debugging in this class, it is best to use single-step debugging to check if the program can run normally. First debug the new function buillist (). Set a breakpoint in the beginning of the function, single step run, see if the program can run smoothly, pay special attention to whether there is finally released. Select a record from the list to see if you can correctly display from the Contact Detail form, then press the Done button on the Contact Detail form to return. Add a record on the Contact Detail form, select this record from the list to see if it can be displayed correctly on the Contact Detail form. Sorting This section is in addition to the ordering, it will also come into contact with two new controls, which is pop-up, Pop-Up Triggers and pop-up lists. Join the pop-up list box to the Contact Detail form Allows us to select different sort options as the sort standard. Finally, we create or modify the corresponding record of the Contact Detail form to insert it into the correct position according to the sort standard. The pop-up trigger button (POP-UP TRIGGERS) pops up the trigger button is similar to the normal button. They have a label and respond to the trigger event. Its special is that it can be associated with a pop-up list box. When the bomb is pressed, the pop-up list box will be displayed. If an entry on the list box is selected, an event popselectrevent is generated. Of course, we can also do some other work with the pop-up trigger button and the list box. Adding the Contacts.RSRC content Now adds a pop-up list box to the Contact List form. It will allow us to sort in three sort standards: time date, last name, name (Last Name). 1. Operating the constructor; 2. Open the resource file contact.RSRC, which is located in the SRC folder of the project folder; 3. Double-click to open the Contact List form; 4. Select Windows | Catalog Open the control panel; 5. From the Catalog window Drag a label to the form. Modify the properties: left origin = 0, TOP Origin = 149, Label is Sort By :; 6. Drag a pop-up list box to the form. Modify attribute dimensions: Object Identifier is Sortlist, Left Origin = 40, TOP Origin = 125, Visible Items = 3. In the following we will see its location and newly added pop-up trigger button is aligned; 7. Click List items, then press CTRL-K to generate the first entry. Modifying entry text is DATE AND TIME; 8. Click List items and then press Ctrl-K to generate a second entry. Modifying entry text is First Name; 9. Click to select List items, then press CTRL-K to generate a third entry. Modifying entry text is Last Name. Create a pop-up trigger button 1. Drag a pop-up trigger button to the Contact List form, its property is shown in Table 7-2; Object Identifier In the resource header file, the resource ID Popup ID pops up the resource ID popup ID popup ID Left Origin horizontal direction The control of the left-end position TOP Origin vertical direction The control of the top position of the control The Width button The Height button is used to define whether the control is visible and available. If not set, you can also call it through the function. Implementation ANCHOR LEFT decisions When the text length changes, the button text is expanded on the left or the right side, and the button text will be extended to the right. Font Label text The text content list ID on the font label tab and the ID 2 of the pop-up list box that pops up the trigger button. Modify the attribute to: Object Identifier is Trigger, Left Origin = 40, TOP Origin = 149, Width = 80, Label for Date and Time, set the List ID to the id of the pop-up box that just created; 3. The chart is shown below in the CONTACT LIST form. Click the X button in the upper right corner of the Form Editor to close, select File | Save Save Make changes. Add first to Contacts.c content, the variables and constants necessary to add sorting in the file header: // ch.7 the sort order variable and constants static int sortby; // ch.7 Note: The Popup List Entries ! #define sortby_date_time 0 #define sortby_first_name 1 #define sortby_last_name 2 Variable Sortby represents the current value in three sort standards. Three constants represent three sort criteria in the pop-up list box, pay attention to each option of them and the list boxes. Sort Initialization To establish a sort standard, you must add a code to handle the POPSELECTEVENT event. This event is triggered when the list option of the pop-up list box is selected. The following code is how to sort according to the selected item: Case popselect: {// 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 data by the new criteria dmquicksort (ContactSDB, (DMComparf *) sortfunc, sortby); // ch.7 Rebuild the list buildlist ();} Break; First, avoid repeated sorting when the sort standard has not changed. The sorting standard is then saved, because after a new record or modification record in the Contact Detail form, the sort has changed, and the sorting standard is used. Finally call the function dmquicksort (), this function passes the sort standard Sortby to the function sortfunc (). After the database sort is complete, the list box is displayed. Below we have studied the function sortfunc (). This function can compare the size of the two entries before and after, and after comparing two records in the database, the function returns an integer. If this is greater than zero, the first record is ranked in front; if it is less than zero, the second record is in front; if zero, the two records are the same. // ch.7 This function is Called by Palm OS To Sort Records Static Int Sortfunc (Charptr PRecord1, Charptr Precord2, Int Sortby) {Int SortResult; // Ch.7 Switch Based On Sort Criteria Switch (Sortby) {//C .7 Sort by date and time case SORTBY_DATE_TIME: {DateTimePtr pdateTime1; DateTimePtr pdateTime2; Long lDiff; pdateTime1 = (DateTimePtr) (precord1 DB_DATE_TIME_START); pdateTime2 = (DateTimePtr) (precord2 DB_DATE_TIME_START); // cH.7 Compare the dates And Times LDIFF = (timdatetime1) / 60) - (timdatetime2) / 60); // ch.7 date / time # 1 is lateriff> 0) SortResult = 1; Else // ch.7 Date / Time # 2 is Later IF (LDIFF <0) SortResult = -1; Else // Ch.7 They Are Equal SortResult = 0;} Break; Code first time and date from the selected Take it in the record and convert it into seconds to compare. In this algorithm, the dateless record will be placed on the top of the list box, and there is no time to record the bottom of the same date. Since the converted second value is likely to exceed 16-bit integer, we use a 32-bit long integer to compare, and the corresponding set variable SortResult is 16 bits. // CH.7 Sort by first name case SORTBY_FIRST_NAME: {sortResult = StrCompare (precord1 DB_FIRST_NAME_START, precord2 DB_FIRST_NAME_START);} break; // CH.7 Sort by last name case SORTBY_LAST_NAME: {sortResult = StrCompare (precord1 DB_LAST_NAME_START, Precord2 DB_LAST_NAME_START);} Break; call function strcompare () is sorted for First Name and Last Name, which is defined in the developing Palm OS 3.0 Applications Part II: Sytem Management. It is very similar to the function strcmp () of ANSI C and returns a total value directly to the Palm OS. Sort records Write list box When moving to another record or exits the Contact Detail form, the function getfield () is called, so the code should be added to this function to ensure that the new record or modify the record Sort. To achieve the requirements, these code should be added to the location where the memory has been released but "dirty" bit has not been cleared. // ch.5 if the record; {charptr precord; // ch.5 points to the db record // ch.7 Detach The Record from The Database DMDetachRecord (ContactSDB, Cursor, & HRecord); / / cH.5 Lock the record precord = MemHandleLock (hrecord); // cH.5 Get the text for the First Name field getText (getObject (form, ContactDetailFirstNameField), precord, DB_FIRST_NAME_START); // cH.5 Get the text for the Last Name field getText (getObject (form, ContactDetailLastNameField), precord, DB_LAST_NAME_START); // cH.5 Get the text for the Phone Number field getText (getObject (form, ContactDetailPhoneNumberField), precord, DB_PHONE_NUMBER_START); // cH.7 Find the proper position cursor = DmFindSortPosition (contactsDB, precord, NULL, (DmComparF *) sortFunc, sortBy); // cH.5 Unlock the record MemHandleUnlock (hrecord); // cH.7 Reattach the record DmAttachRecord (contactsDB, & cursor, HRECORD, NULL);} Since it is only reordered when the record is changed, it can be used to determine whether the recording has been modified first. Then, "dirty" records are temporarily separated from the database and lock it, and finally call the function DMFINDSortPosition () decision should be inserted in the list box. The prerequisites for this function have been properly sorted, and because we only change or add a record, you can meet the requirements. Finally unlock the record and insert it into the new location of the database. Debug Sets the breakpoint at the top of the top of the popselectEvent event, the top of the function sortby (), the start of the function getfield () of the IF statement. Check if the program can run normally. Here are some of the features test: l Sort by three sorting standards, see if the database can be sorted according to each standard; l Add a record to the database to see if it can be placed in the correct position; l Modify an existing record See if it can be placed in the correct position. What to do next will be made Next, we will add more new controls, such as tables and browsing bars. This chapter uses the list box to display the record information of the program. The next chapter will modify the program and use the table control to do this. Program list // ch.2 The super-incrude for the palm OS #include / 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 Contact List variables static videhand hchoices; // ch.7 handle to packed choices static voidhand hpchoices; // ch.7 handle to pointers // 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.2 The Main Entry Point DWord Pilotmain (Word CMD, PTR, WORD) {DWORD ROMVERSION; // Ch.4 ROM VERSION FORMPTR FORM; // Ch.2 a Pointer to Our Form Structure EventType Event; // Ch.2 Our Event Structure Word Error; // ch.3 Error Word // Ch.4 get the rom version romversion = 0; FTRGET (SYS FtrCreator, sysFtrNumROMVersion, & romVersion); // CH.4 If we are below our minimum acceptable ROM revision if (romVersion dmErrAlreadyExists) && (error = 0)) {// CH.5 Handle db creation error FrmAlert (DBCreationErrorAlert); return (0);}! // CH.5 Open the database contactsDB = DmOpenDatabaseByTypeCreator ( 'ctct', 'PPGU' , DMModeReadwrite; // 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 no records, create one if (numrecords == 0) {newrecord (); frMgotoform (contactDetailform);} else frmGotoform (ContactListForm); // ch.7 begin the try block errtry {// ch.2 Our Event loop do {// ch.2 get the next este 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 ou form switch (Event.Data.frmload.form) {//CH .4 Contact Detail Form Case Contactde tailForm: 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 ' Sa stop event, exit} while (Event.Type! = appstopevent); // ch.7 end the try block and do the catch block} errcatch (errolert) {// ch.7 Display the appropriate Alert Frmarert (Erroralert) } ErrendCatch // Ch.5 Close All Open FormCloseallForms (); // 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 // cH.3 Get our form pointer form = FRMGETACTIVEFORM (); // ch. the database fields setFields ();} break; // cH.5 Form close event case frmCloseEvent: {// cH.5 Store away any modified fields getFields (); // cH.5 Parse the button events case ctlSelectEvent;} break : {//Ch.5 Store Any Fiel D changes getfields (); switch (event-> data.ctlselect.control) {// ch.5 First Button Case ContactDetailfirstButton: {// ch.5 set the cursor to the first recordiffiffness f (cursor> 0) CURSOR = 0 } Break; // ch.5 Previous Button Case ContactDetailPrevbutton: {// ch.5 Move The Cursor Back One Record IF (Cursor> 0) Cursor ---;} Break; // 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; // CH.5 Delete button case ContactDetailDeleteButton: {// CH.5 Remove the record from the 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 is 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 number frMgotoform (ContactListForm);} Break; // ch.6 Date Selector Trigger case ContactDetailDateSelTrigger: {// cH.6 Initialize the date if necessary if (dateTime.year == NO_DATE) {DateTimeType currentDate; // cH.6 Get the current date TimSecondsToDateTime (TimGetSeconds (), & currentDate); // cH.6 Copy it datetime.year = currentdate.year; datetime.mont = currentdate.mont; datetime.day = currentdate.day;} // ch.6 pop up the system date Selection form Selectday (SELECTDAY Byday, & (DateTime.month), & (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 (); // ch.5 responde 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 our form pointer form = FrmGetActiveForm (); // ch.4 respond to the open event if (event-> eType == frmopenevent) {// ch.4 draw the form frmdrawform (form);} // ch.4 return to the calling formix Event-> ETYPE == CTLSELECTEVENT) {frmreturntoform (0); // ch.4 Always Return True in this case (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 Case FRMOPENEVENT: {// 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 at: // 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 EnterTimeMinuteOnesPushButton: {// 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 (time ") CTLSetValue (getObject (form, timeselect), false; // ch.6 set the New Selection CTLSetValue (GetObject (Form, Button), True) } Break; // ch.6 Up button entertimetimeuprepeating: {// ch.6 if there is no time, do nothingiff (datetime.hip == no_time) Break; // ch.6 based on What Push Button IS SELECTED SWITCH (TIMESELECT) {// ch.6 Increase Hours Case Entertainment Hourspushbutton: {// ch.6 Increment Hours DateTime.Hour ; // Ch.6 IT WAS 11 AM, Make IT 12 amix (DateTime. Hour == 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 EntertainmentTimeTenspushButton: {/ / 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 EntertimeMinuteOnSpushButton: {// 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 ();} Break; // 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 successd switch (timeselect) {// ch.6 Decrease Hours EntertimeHourspushbutton: {// 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 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 EntertimeMinuteNespushButton: {// Ch.6 Decrement Minutes DateTime.minute -; // 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 controls setTimeControls ();} Break; // ch.6 am Button Case EntertainmentAmpushbut TON: {// ch.6 if no time WAS set if (datetime.hour == 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 EntertainmentPmpmpmpushbutton: {// 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.hip <12) {// ch.6 change to PM DateTime.Hour = 12; // ch.6 set the controls setTimeControls ();}} Break; // ch.6 no time checkbox case entertimenotimeCheckbox: {// ch.6 ife wE all unchecking the box if (datetime.hip == no_time ) {// ch.6 set the time = 12; datetime.minute = 0; // ch.6 set the controls setTimeControls (); // ch.6 set the new selection timeselect = entertimeHourspushbutton; 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 Entertainment: {// 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 EnterTimeOKButton: {VoidPtr precord; // cH.6 Points to the record // 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 U nlock 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 static boolean contactListHandleevent (EventPtr) Event) {formptr form; // ch.7 a form structure Pointer // ch.7 get outade form (); // ch.7 parse Events switch (event-> eType) {// ch.7 Form Open Event Case FRMOPENEVENEVENEVENT: {// ch.7 draw the form frmdrawform (form); // ch.7 build the list buildlist (); // CH.7 Form close event case frmCloseEvent: {// CH.7 Unlock and free things here MemHandleUnlock (hpchoices); MemHandleFree (hpchoices); MemHandleUnlock (hchoices); MemHandleFree (hchoices); hchoices = 0;} break; / / cH.7 Respond to a list selection case lstSelectEvent: {// cH.7 Set the database cursor to the selected contact cursor = event-> data.lstSelect.selection; // cH.7 Go to contact details FrmGotoForm (ContactDetailForm) } Break; // ch.7 respond to a menu Event case: {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 thee Contact Database by The New Criteria Dmquicksort (ContactSDB, (DMComparf *) sortfunc, sortby); // ch.7 rebuild The list buildlist ();} Break;} // ch.7 end of the event switch statement // ch. 7 We're Done R eturn (false);} // CH.3 Handle menu events Boolean menuEventHandler (EventPtr event) {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 outactiveform (); // 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 (ABOUTFORM); Return (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.7 CREATE THE DATABASE RECORD AND GET A HANDLE TO IT IF ((HRECORD = DMNewRecord (ContactSDB, & Cursor, db_record_size) == NULL) ErroRexit (MemoryErrorAL); // 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, SizeOf (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.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 (FrmGetObjectPtr (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 detail Form Charptr PRecord; // Ch.5 A Record Pointer Word Index; // Ch.5 The Object Index // Ch.5 Get The Contact Detail Form Pointer Form = frMgetActiveForm (); // ch.5 Get The Current Record HRecord= DmQueryRecord (contactsDB, cursor); // CH.6 Initialize the date and time variable precord = MemHandleLock (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.5 We're Done Return } // ch.5 Puts any field change, {formptr form; // ch.5 the contact detail form // ch.5 get the contact detail form Pointer form = frMgetActiveform (); / / Ch.5 TURN OFF FOCUS FRMSETFOCUS (Form, -1); // Ch.5 if The Record Has Been Modified if (isdirty) {Charptr Protod; // Ch.5 Points To The DB Record //CH.7 Detach the record from the database DmDetachRecord (contactsDB, cursor, & hrecord); // cH.5 Lock the record precord = MemHandleLock (hrecord); // cH.5 Get the text for the First Name field getText (getObject (form, ContactDetailFirstNameField) PRECORD , DB_FIRST_NAME_START); // CH.5 Get the text for the Last Name field getText (getObject (form, ContactDetailLastNameField), precord, DB_LAST_NAME_START); // CH.5 Get the text for the Phone Number field getText (getObject (form, ContactDetailPhoneNumberField), precord, DB_PHONE_NUMBER_START); // cH.7 Find the proper position cursor = DmFindSortPosition (contactsDB, precord, NULL, (DmComparF *) sortFunc, sortBy); // cH.5 Unlock the record MemHandleUnlock (hrecord); / / Ch.7 Reattach The Record DmattachRecord (ContactsDB, & Cursor, HRECORD, NULL);} // ch.5 reset the dirty bit isdirty = false; // ch.5 we're done return } // ch.5 set the text in a field static void settext (Fieldptr Field, Charptr Text) {voidhand Hfield; // Ch.5 Handle Of Field Text Charptr Pfield; // Ch.5 Pointer To Field Text //CH .5 get the current Field Handle Hfield = FldgetTextHandle (Field); // Ch.5 IF WE HAVE A HANDLE IF (Hfield! = NULL) {// Ch.5 Resize IT IF (MemHandleresize (Hfield, Strlen (Text) 1)! = 0) ErroROREXIT (Memoryerrorlert);} else // ch.5 allocate a handle for the string {hfield = MemHandleNew (Strlen 1); if (hfield == null) errorexit (MemoryErrorlert);} // ch.5 Lock it Pfield = MemHandLock (Hfield); // Ch.5 Copy The String Strcopy (Pfield, Text); // Ch.5 Unlock It MemHandleUnlock (Hfield); // Ch.5 Give It to the the THE Field FldsetTextHandle (Field, Hfield); // Ch.5 Draw the Field Flddrawfield (Field); // Ch.5 We're Done Return;} // ch.5 Get The Text From A Field Static Void GetText (FieldPtr Field , Voidptr Precord, Word Offset {Charptr Pfield; // Ch.5 Pointer To Field Text //CH.5 Get the text pointer pfield = fldgettextptr (field); // ch.5 Copy IT DMWRITE (Pfield); // ch.5 we're done return;} // ch.6 set the Contact Detail date selector trigger static void setDateTrigger (void) {FormPtr form; // cH.5 The contact detail form // cH.6 Get the contact detail form pointer form = FrmGetActiveForm (); // cH.6 If there is No Date IF (DateTime.Year == NO_DATE) {CTLSetLabel (GetObject (Form, ContactDetAildateSeltrigger), "");} else // ch.6 if there is a date {char datestring [dateStringLength]; // CH.6 Get the date string DateToAscii (dateTime.month, dateTime.day, dateTime.year, (DateFormatType) PrefGetPreference (prefDateFormat), dateString); // CH.6 Set the selector trigger label CtlSetLabel (getObject (form, ContactDetailDateSelTrigger), dateString);} // cH.6 We're done return;} // cH.6 Set the Contact Detail time selector trigger static void setTimeTrigger (void) {FormPtr form; // cH.5 The contact detail form // ch.6 get the contact detail form Pointer form = frMgetActiveForm (); // ch.6 f == no_time) {CTLSetLabel (GetObject (Form, ContactDetailTimeSeltrigger, "") else // cH.6 If there is a time {Char timeString [timeStringLength]; // cH.6 Get the time string TimeToAscii (dateTime.hour, dateTime.minute, (TimeFormatType) PrefGetPreference (prefTimeFormat), timeString); // Ch.6 Set The Selector Trigger Label CTLSetLabel (Form, ContactDetailtimeSeltrigger, TimeString);} // ch.6 we're done return;} // ch.6 set the controls in the Enter Time form based on dateTime static void setTimeControls (void) {FormPtr form; ControlPtr hourButton; ControlPtr minuteTensButton; ControlPtr minuteOnesButton; ControlPtr amButton; ControlPtr pmButton; ControlPtr noTimeCheckbox; Char labelString [3]; SWord hour; // CH .6 Get the form form = FrmGetActiveForm (); // cH.6 Get the control pointers hourButton = getObject (form, EnterTimeHoursPushButton); minuteTensButton = getObject (form, EnterTimeMinuteTensPushButton); minuteOnesButton = getObject (form, EnterTimeMinuteOnesPushButton); amButton = getObject (Form, Entertainment); Pmbutton = getObject (form, entertimePmpushbutton); NotimeCheckbox = GetObject (Form, Entertainment); // ch.6 if there is a time if (datetime.Hour! = no_time) {// ch.6 Update the hour hour = datetime.Hour% 12; if (Hour == 0) hour = 12; CtlSetLabel (hourButton, strIToA (labelString, hour)); // cH.6 Update the minute tens CtlSetLabel (minuteTensButton, strIToA (labelString, dateTime.minute / 10)); // cH.6 Update the minute ones CTLSetLabel (MinuteneesButton, Stritoa (Labelstring, DateTime.minute% 10)); // Ch.6 Update AM CTLSetValue (Ambutton, (DateTime.Hour <12)); // Ch.6 Update PM CTLSetValue (Pmbutton, (DateTime). Hour> 11)); // ch.6 Uncheck the no time Checkbox CTLSetValue (NOTIMECHECKBOX, FALSE);} else // if there is no time {// ch.6 Update the Hour CTLSetValue (Hourbutton, False); CTLSetLabel hourButton, ""); // cH.6 Update the minute tens CtlSetValue (minuteTensButton, false); CtlSetLabel (minuteTensButton, ""); // cH.6 Update the minute ones CtlSetValue (minuteOnesButton, false); CtlSetLabel (minuteOnesButton, "" " // ch.6 Update AM CTLSetValue (Ambutton, False); // Ch.6 Update PM CTLSetValue (Pmbutton, false); // Ch.6 Uncheck The no time Checkbox CTLSetValue (NOTIMECHECKBOX, TRUE);} // ch. 6 we're done return;} // ch.7 builds the contact list static void buildlist (void) {Formptr form; // ch.6 a form structure pointer int choice; // ch.7 The List Choice We're doing CharPtr precord; // cH.7 Pointer to a record Char listChoice [dateStringLength 1 // cH.7 We timeStringLength 1 // build DB_FIRST_NAME_SIZE // list DB_LAST_NAME_SIZE]; // choices here // cH.7 THE CURRENT LIST Choice Charptr Pchoices; // Ch.7 Pointer to Packed Choices Uint Offset // ch.7 Offset Into Packed Strings Voidptr Ppchoices; // Ch.7 Pointer To Pointers To Choices // Ch.6 Get Our Form Pointer Form = frMgetActiveform (); // Ch.7 Put The List Choices in a Packed String for (choice = 0; choice StrLen (listChoice) 1)) errorExit (MemoryErrorAlert);} // CH.7 Lock pchoices = MemHandleLock (hchoices); // CH.7 Copy the string into the memory StrCopy (pchoices offset, listChoice); offset = StrLen (listChoice) 1; // cH.7 Unlock the record MemHandleUnlock (hrecord);} // cH.7 Create a pointer array from the packed string list if ((hpchoices = SysFormPointerArrayToStrings (pchoices, numRecords)) == 0 ) errorExit (MemoryErrorAlert); ppchoices = MemHandleLock (hpchoices); // cH.7 Set the list choices LstSetListChoices (getObject (form, ContactListListList), ppchoices, numRecords); // cH.7 Draw the list LstDrawList (getObject (form, ContactListListList); // ch.7 We're Done Return;} // ch.7 This function is Called by Palm OS To Sort Records Static Int Sortfunc (Charptr Procord1, Charptr Precord2, Int Sortby) {Int SortResult; // Ch.7 Switch Based on Sort criteria switch (sortby) {// ch.7 sort by date and time case sortby_date_time: {datetimeptr pDatetime1; dat eTimePtr pdateTime2; Long lDiff; pdateTime1 = (DateTimePtr) (precord1 DB_DATE_TIME_START); pdateTime2 = (DateTimePtr) (precord2 DB_DATE_TIME_START); // CH.7 Compare the dates and times lDiff = (Long) (TimDateTimeToSeconds (pdateTime1) / 60 ) - (timdatetime2) / 60); // ch.7 date / time # 1 is latter if (ldiff> 0) sortresult = 1; else // ch.7 date / time # 2 is later if (lDiff <0) sortResult = -1; else // cH.7 They are equal sortResult = 0;} break; // cH.7 Sort by first name case SORTBY_FIRST_NAME: {sortResult = StrCompare (precord1 DB_FIRST_NAME_START, precord2 DB_FIRST_NAME_START Break;