Palmos Development Tutorial - 8

zhaozj2021-02-08  265

Chapter 8 Table and Rolling Bar In this chapter, we will discuss two important user interface elements of Palm OS: tables and scroll bars. The table can display or edit larger data. It is widely used in embedded applications. The function of the scroll bar is excellent, but since the scroll bar does not support the 1.0 version of the Palm OS system, the scroll bar can only be used if you do not want to support earlier Pilot 1000 and 5000. We will add a scroll button at the same time (it can be used by all PALM devices) and scroll bar (please do not use in a real application! You may have the system crash!), However these is not enough, we will also discuss How to support the PAGE UP and PAGE DOWN keys. Save the project Now you have this habit, 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 the folder; 4. Select a folder to save a copy; 5. Press CTRL V to paste the project copy into the backup folder; 6. Name the project name as your name, I named it as CH.7. Deleting the old resource Since the used table replaces the list box in the Contact List form, we need to delete the list box. 1. Run MetrowerKs constructor; 2. Open the resource file Contacts.RSRC. It is located in the SRC folder in the project folder; 3. Double-click to open the Contact List form; 4. Click the resource named list in the resource list, press the Delete key to delete; 5. The Contact List window is now appeared as shown in Figure 8-1. Deleting old code has been deleted since the list box has been deleted, and the function buildlist () and deletelist () will no longer need it. Find and delete these two functions and their related content. You can place the cursor in the beginning of the file, select Search | Find in the menu bar, enter buildlist. After deleting all the contents related to BuildList, you can do a similar operation to DeleteList. In addition, delete the code in response to a 1stselectEvent event in the Contact List event handler. These codes are: // ch.7 respond to a list selection case lstselect: {// ch.7 set the database cursor to the successd company = event-> data.lstselect.selection; // ch.7 go to contact Details frmgotoform (contactDetailform);} Break; Table is a container relative to other UI (user interface) elements. The UI element in the table is not the same in the Palm OS system of the table. Each unit (Cell) (line column) in the table can have different types, that is, it supports different types of UI elements, which will be described in the next section. The table also is different when capturing the event and passing the data in the unit to the UI element. Some ordinary functions can perform some special functions to the epigenetic. Table looks like a static Palm OS, just to ensure that each type of UI element is distinguished. Unfortunately, many UI elements that can be used in many tables are not used in my project.

In this chapter, I just make some discussion on how to establish my own custom unit. Each unit in the entry type table has its own type. For example, the unit can be a word editing box resource or a checkbox resource. Table 8-1 is the outline of these types and its operations. Type Use CheckboxtableItem In addition to the text and options boxes, this type of unit operation and the general option box can be selected or cleared by calling TBLSETITEMINT (), and 0 means no selected, 1 is selected. CustomTableItem This is a very useful unit type. You must define a custom function for each column of this type. In this section, we tell how to handle this type of content. This type is editable. DateTableItem displayed in this unit is defined as a DateType format, and the TBLSETITEMPTR () function will be used to pass to the DateType's cursor. The disadvantage of this format is that an exclamation mark will be written after any dates of the past. Sometimes this may be a good thing, but sometimes it will force you to use the custom date. This type is not editable. LabelTableItem It is used to display a label. Pass the string to the table using TBLSETITEMPTR (). The disadvantage of this format is that the table is often coupled with a colon (:) after the transmitted string, and the text is usually right. That's why in this chapter, we want to use custom type. This type is not editable. NumericTableItem displays a rightmost number. This type is very good, not add some weird content. You can call the TBLSETETETEMINT () function to set the number. This type is not editable. PopupTriggertableItem This type is similar to pop-up trigger button. Using the TBLSETITEMPTR () function You can point to the cursor of the list box to display the list box, use TBLSETITEMINT () to set which entry in the list. TEXTTABLEITEM This type is similar to the edit box, which is editable. The length of the edit box can be changed and overlap. Use TBLSETLOADDATAPROCEDURE () to define a custom import function to pass the handle of the edit box to the table. Using TBLSetsaveDataProcedure () Defines a save function to save data in the edit box of this handle. So you must write these two custom functions to support the edit box operation in the table. TEXTWITHNOTETABLEM This type will join a small prompt icon at the right of a general text entry. This prompt icon looks alone is selected separately. When the unit is selected, you must call TBLEDITING () Take a look at whether the edit box is an editable mode. If not, the Note icon has been selected, you have to switch to your Note form. In addition to the defining the size of the space in addition to the size of the space, NarrowTextTableItem can be defined at the end of the field. For example, in a calendar form, in order to place a small warning clock icon on the right side of the entry, the Date Book program uses this type to provide space. Because all existing types have their own specialties, only their own custom types can complete the functions they want to implement. Attributes Table 8-2 of the table is a description of the table. Like other resource properties, after selecting a list of resources in the form, you can edit in the constructor. Name Description Object Identifier In the resource header file, the resource ID number of the resource ID Table ID table is used in the resource header file. Left Origin Horizontal Direction In the leftmost position of the control of the control of the Top ORIGIN vertical direction in the uppermost position of the control. The width of the width table of the Width table. Editable Definitions You can enter the number of rows visible in the ROWS table by the user.

COLUMN WIDTHS The width of each column, if you want to define a new column, press CTRL-K to add a table Now add the table to the Contact List form: 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 Open the Contact List form. 4. Select Window | Catalog in the menu to open Catalog; 5. Drag the table resources to the form; 6. Setting table properties: Object Identifier = Table, Left Origin = 0, TOP Origin = 15, Width = 153, Height = 130. This is enough to place ten lines, then set up ROWS 10, so setting can also leave enough space to place scroll bar on the right side of the form; Define column widths. Set Column Width from 1 to 40. Select Column Width 1, create a new column by ctrl-k. Set this column width from 2 to 40. Select Column Width 2 and create a third column in Ctrl-K. Set Column Width from 3 to 73; 8. The Contact List form looks as shown in Figure 8-2. Displaying the records in the table Two basic functions we will add a table: DrawTable () and Drawcell (). DrawTable () draws a table in the current state of the cursor. The function Drawcell () is a customized unit input function that performs this function when the Palm OS wants to enter an entry to the table. Let's join these functions: Static Void DrawTable (Void); Static Void Drawcell (Voidptr Table, Word Row, Word Column, Rectangleptr Bounds); Drawcell () function prototype must be the feedback function prototype input by the custom word unit match. There is a definition of this prototype in the TBLSETCUSTOMDRAWPROCEDURE () of Palm OS literature. For reasons of cleanliness, preferably at the beginning of the definition of the constants file: // 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 gray_up_arrow "/ x03" #define gray_down_arrow "/ x04" constant Table_NUM_COLUMNS and TABLE_NUM_ROWS Define the size of the form display in the form, which is related to the future operations. The next three constant table_column_date, table_column_time, and Table_Column_name define the information filled in each column. The last four constants Black_up_arrow, black_down_arrow, gray_up_arrow, and gray_down_arrow are the ASCII values ​​representing these diagrams in the Symol 7 font in the Palm OS. When the scroll bar reaches the top or bottom, we use these constants to add a dusty arrow.

It is worth noting that in the Palm OS, only this control can add dust. Function ContactListHandleEvent () Change Find event handler contactListListLeevent () in the Contact List form, you need to add a DrawTable () function call: // ch.7 form Open Event Case FRMOPENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENT: {// ch.7 draw the form frmdrawform (FORM); // ch.8 Populate ();} Break; Next, the recorded operation is recorded in the processing table, and the Contact Detail form should be called to display its details after selecting a record. Note that these codes are similar to the code that is selected by the list box. In order to display the corresponding record, we set the cursor variable. // ch.7 respond to a list selection case tblselectevent: {// ch.7 set the database cursor to the selected contact cursor = event-> data.tblselect.row; // ch.7 Go to contact Details frMgotoform ContactDetailForm);} Break; Because the database is sorted according to different standards, you must re-draw a new record order after each sort is sorted. To do this, add the drastable () function after DMQUicksort () to respond to the popselectEvent event. // ch.7 Sort the Contact Database by The New Criteria Dmquicksort (ContactSDB, (DMComparf *) sortfunc, sortby; // ch.8 rebuild the table drawtable ();} Break; This is completed for this function. . Add a DrawTable () function below to add a DRAWTABLE () function. First define some variables and get pointers of the table. // CH.8 Draw our list of choices using a table object static void drawTable (void) {FormPtr form; TablePtr table; Int column; Int count; ControlPtr upArrow; ControlPtr downArrow; // CH.8 Get the form pointer form = FRMGETACTIVEFORM (); // ch.8 Get the table Pointer Table = getObject (form, contactlisttable); we will do two things to the columns in the table. First, each column must have a custom rule (ROUTINE). Although the entry type is based on the unit, if the unit is customized, each unit uses the same rule on a particular column. In the example, we will create a custom rule - Drawcell (), which will use this rule in each unit of the table. Another thing to do is that it is visible. The default value of the column is invisible, and it is visible to displaying it.

// CH.8 For all columns for (column = 0; column

// ch.8 Calculate = curusor row; // ch.8 get u h = DMQueryRecord (ContactSDB, Record); Precord = MemHandLock (HRECORD); // Ch.8 Get The Date and Time MEMMOVE & DateTime, Precord DB_DATE_TIME_STARD DB_DATE_TIME_START, SIZEOF (DateTime)); First, we get a record associated with this line, then extract the date and time, making it easier to input. // ch.8 Switch on The Column Switch (Column) {// Ch.8 Handle Dates Case Table_Column_date: {IF (DateTime.Year! = NO_DATE) {DateToascii (DateTime.month, DateTime.day, Datetime.year, ( DateFormattype) prefgetPReference (prefdateformat), string;} else strcopy;} Break; according to the type of column, we created the string to display. For the date, the functions used in the function and the list box are the same, and the dates will be represented in the place where there is no date. // CH.8 Handle times case TABLE_COLUMN_TIME: {if (! DateTime.hour = NO_TIME) {TimeToAscii (dateTime.hour, dateTime.minute, (TimeFormatType) PrefGetPreference (prefTimeFormat), string);} else StrCopy (string, "- ");} Break; the next column shows time. It is the same as the function of the display time in the list box. If there is no date, we replace it in a short line. // CH.8 Handle names case TABLE_COLUMN_NAME: {StrCopy (string, precord DB_FIRST_NAME_START); StrCat (string, ""); StrCat (string, precord DB_LAST_NAME_START);} break; the third column is the last column and last name . We wrote the text created for the unit. // Ch.8 Unlock The Record MemHandleUnlock (HRECORD); Because we have created a suitable text string, you can now write the record unlock (unlock). Note that this method does not use permanently memory storage unit data, so this function can work very well regardless of how much record in the database. The benefits of writing data using the custom functions are used in the table and list boxes.

// CH.8 Set the text mode WinSetUnderlineMode (noUnderline); FntSetFont (stdFont); // CH.8 Truncate the string if necessary width = bounds-> extent.x; len = StrLen (string); noFit = false; FntCharsInWidth (String, & Width, & Len, & Nofit); Below, in order to make WinDrawchars () can meet our requirements, you must set the text mode. The name may not be fully displayed in the space that can be displayed on the screen, so you need to check the string to avoid not too long and exceed the range of units. If it is too long, we have to remove the extra part. In fact, if you have a lot of imagination, you can find a way to add an omitted number (...) in the end of the string. // ch.8 Draw the Cell WineraseRectangle (Bounds, 0); Windrawchars (String, Len, Bounds-> Topleft.x, Bounds-> Topleft.y); // Ch.8 We're Done Return;} final, Write the string after clearing the previous content on the screen. This is done so that the function is completed. It is easy to write and have high flexibility. Debugging When running in the first time, it is best to perform a DrawTable () and Drawcell () functions. If the row is not needed is not closed, the system may crash because Drawcell () will try to access the records that do not exist. Remember the continuous switching test between Detail forms and tables, and use the drop-down box using different sort standards. The Contact List form looks as shown in Figure 8-3. Three rolling bars are commonly used in the Palm OS. The first is the scroll button, which is a pair of upward and down repetition buttons, we have used in the Enter Time form, they can be used in all PALM OS versions; the second is the scroll bar; in addition to not In addition to the use of Piolt1000 or Piolt5000, you can use in other Palm OS versions; the third is the Page Up and Page Down. Below, we will add resources and code to support these three scroll bars in the Contact List form. But this is usually not a good idea. Scrolling Bar Properties Table 8-3 is an attribute of a scroll bar: Name Description Object Identifier In the resource header file, the ID number of the resource ID scrollbar id scroll bar is used in the resource header. The Left Origin Level direction The control of the leftmost position TOP ORIGIN vertical direction The width value of the top position of the control is the width of the width scroll bar. Height scroll bar's height value. USABLE Defines whether the scroll bar is visible. The initial value of the Value scroll bar. Minimum Value The minimum value of the scroll bar. The minimum of the Maximum Value scroll bar. The size of the row or notepad of the scroll bar is used, which is used to set the size of the box in the scroll bar. Orientation Defines the Rolling Bar Is the horizontal or vertical direction to add a scroll button and a scroll bar resource to add two scroll buttons and a scroll bar to support the three scroll bar types. 1. Run MetrowerKs constructor; 2. Open Resource File Contacts.RSRC, which is located in the SRC folder in your project folder; 3. Double-click to open the Contact List form; 4. Select Window | Catalog to open Catalog in the menu.

5. Drag a scroll bar to the form; 6. Modify the properties of the scroll bar: Object Identifier = scrollbar, left origin = 153, TOP Origin = 15, Width = 7, Height = 130. Such a scroll bar is just on the right side of the table, close to the right boundary of the form. 7. Add the scroll button. You can copy the scroll button from the Enter Time Form, open the Enter Time form. Drag the scrolling button into the Contact List form from the Enter Time form. Set the Left Origin of the upward arrow to 149, Top ORIGIN is 145, change Object Identifier to Recordup; set the down arrow Left Origin 149, Top Origin is 152, change Object Identifier to RecordDown. 8. The Contact List form looks as shown in Figure 8-4. Let the scroll button work in an example, the primary work to do is to make the cursor variable to the top position of the table. Further, the upward arrow is beat ash when reaching the top of the record, and the down button is grayed when the bottom of the record is reached. In the first added contactListHandleEvent () code: // CH.8 Respond to arrows case ctlRepeatEvent: {switch (event-> data.ctlRepeat.controlID) {// CH.8 Up arrow case ContactListRecordUpRepeating: if (cursor> 0) cursor -; break; // CH.8 Down arrow case ContactListRecordDownRepeating: if ((numRecords> TABLE_NUM_ROWS) && (cursor

To accomplish this operation, in response drawTable () of the button event add the following code: // CH.8 Get pointers to the arrow buttons upArrow = getObject (form, ContactListRecordUpRepeating); downArrow = getObject (form, ContactListRecordDownRepeating); // CH .8 Update the arrow buttons and scrollbars if (numRecords> TABLE_NUM_ROWS) {// CH.8 Show the up arrow if (cursor> 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 (cursor> = numRecords - TABLE_NUM_ROWS) {CtlSetLabel (downArrow, GRAY_DOWN_ARROW); CtlSetEnabled (downArrow, false) ;} else {CtlSetLabel (downArrow, BLACK_DOWN_ARROW); CtlSetEnabled (downArrow, true);} CtlShowControl (downArrow); // CH.8 Show the scrollbar FrmShowObject (form, FrmGetObjectIndex (form, ContactListScrollbarScrollBar)); SclSetScrollBar (getObject (form, ContactListscrollbarscrollbar, Cursor, 0, NumRecords - Table_Num_Rows, Tab LE_NUM_ROWS);} else {// CH.8 Hide the arrows CtlHideControl (upArrow); CtlHideControl (downArrow); // CH.8 Hide the scrollbar FrmHideObject (form, FrmGetObjectIndex (form, ContactListScrollbarScrollBar));} // CH.8 We're done return;} If the location of the table is at the beginning or at the end, we put the repeated button to make it unavailable. This prevents the cursor from being set to a no value. This is completed, and the functions of the rolling strip arrows of the table are repeatedly refurbished. Support for Page Up and Page Down keys To capture the Page Up and Page Down keys, you must first add code in KeyDownevent. Mathematical knowledge can give us some tips. It is best to leave a common line on the page when the page is turned down or turned down. When moving records should not be moved to table_num_rows, and should be moved to Table_Num_ROWS-1. Since the up and down pages cannot be made unavailable, it must be guaranteed that the scope of the cursor will not be exceeded when pressed. In addition, the cursors and NumRecords are unsigned, so they must be checked prior to mathematical operations, avoiding them from doing a negative value.

This requires some modifications to ContactListHandleevent (): // 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 : IF (CURSOR> TABLE_NUM_ROWS - 1) CURSOR - = Table_Num_Rows - 1; Else Cursor = 0; BREAK; It is quite simple to turn the top. If the upward page does not make the record cursor less than zero, turn up a whole page; otherwise, turn it back to zero recording. // CH.8 Down arrow hard key case pageDownChr: if ((numRecords> 2 * TABLE_NUM_ROWS - 1) && (cursor Data.SCLEXIT .newValue; DrawTable (); break; to the cursor and new rolling strips, you can respond to scroll scrolling events. If the range of scrolling scrolling the scroll bar is properly set, it is guaranteed that the cursor will not get the value of the error. When assigning values ​​for the cursor, you need to refresh the table and scroll bar like the other scroll bar. Here, look at the code added in drawtable (). Add the code behind the scroll button code if (numRecords> TABLE_NUM_ROWS) statement: // CH.8 Show the scrollbar FrmShowObject (form, FrmGetObjectIndex (form, ContactListScrollbarScrollBar)); SclSetScrollBar (getObject (form, ContactListScrollbarScrollBar), cursor, 0, NumRecords - Table_Num_Rows, Table_Num_Rows; The scroll bar is displayed here and sets it accurately. Because we already know that the records existing in the table are more than the number of lines visible, NumRecords-Table_Num_Rows does not generate an error result.

But if this is not the case, but the record is less than the number of lines visible, it is necessary to hide the scroll bar: // ch.8 hide the scrollbar frMhideObject (Form, FRMGETOBASDEX (Form, ContactListScrollbarscrollbar);} Support scroll bar code The modification is completed. Debugging and before, first debugging the code just added. In addition, all scroll strips are moved to the first and last record, and the number of times should not be too small (some records are not displayed) or too much (the system crashes). The Contact List form looks as shown in Figure 8-5. What is going on next in the next chapter, we will end the basic knowledge of this book by adding other excellent functions in Contacts, such as system search, classification, confidentiality record, etc..

Listing This is the changeds.c: // ch.2 the super-incrude for the palim os #include // ch.5 added for the call to grfsetstate () #include < Graffiti.h> // cH.3 Our resource file #include "Contacts_res.h" // cH.4 Prototypes for our event handler functions static Boolean contactDetailHandleEvent (EventPtr event); static Boolean aboutHandleEvent (EventPtr event); static Boolean enterTimeHandleEvent ( EventPtr event); static Boolean contactListHandleEvent (EventPtr event); static Boolean menuEventHandler (EventPtr event); // cH.4 Constants for ROM revision #define ROM_VERSION_2 0x02003000 #define ROM_VERSION_MIN ROM_VERSION_2 // cH.5 Prototypes for utility functions static void newRecord ( void); static VoidPtr getObject (FormPtr, Word); static void setFields (void); static void getFields (void); static void setText (FieldPtr, CharPtr); static void getText (FieldPtr, VoidPtr, Word); static void setDateTrigger ( Void); static void settimetrigger (void); static void setTimeControl s (void); static Int sortFunc (CharPtr, CharPtr, Int); static void drawTable (void); static void drawCell (VoidPtr table, Word row, Word column, RectanglePtr bounds); // CH.5 Our open database reference static DmOpenRef contactsDB; static ULong numRecords; static UInt cursor; static Boolean isDirty; static VoidHand hrecord; // cH.5 Constants that define the database record #define DB_ID_START 0 #define DB_ID_SIZE (sizeof (ULong)) #define DB_DATE_TIME_START (DB_ID_START / DB_ID_SIZE) #define DB_DATE_TIME_SIZE (sizeof (DateTimeType)) #define DB_FIRST_NAME_START (DB_DATE_TIME_START / 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 Timeselectr; #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 gray_up_arrow "/ x03" #define gray_down_arrow "/ x04" // 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 (Sysftrcreator, SysftrnumromVersion, & Romversion); // Ch.4 IF WE Are Below Our Minimum Acceptable Rom Revision if (Romversion

} // ch.2 if this is not a normal launch, don't launch if (cmd! = Sysapplaunchcmdnormallaunch) Return (0); // ch.5 Create a new database in case there isn't one ing ((((((( Error = DMCREATEDATABASE (0, "ContactSDB-PPGU", 'PPGU', 'CTCT', FALSE))! = DMERRALREADYEXISTS) && (Error! = 0)) {// ch.5 Handle DB Creation Error FRMALERT (DBCRETIONERRORALERT); 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 choise outing page // ch.5 if there is all no records, create one if (numericords == 0) {newrecord (); frMgotoform (ContactDetailform) } else 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 Load 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 EntertainmentForm: form = frminitform; 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 // cH.3 Get our Form Pointer form = frMgetActiveform (); // ch.4 Parse events switch (event-> eType) {// ch.4 form Open event case frMopENEvent: {// ch.2 draw the form frmdrawform (for M); // 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.controlID) {// cH.5 First button case ContactDetailFirstButton: {// CH .5 set the cursor to the first recordiffiffness;} 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 ContactDetaillastButton: {// ch.5 Move The Cursor to the Last Recordix (NumRecords - 1)) CURSOR = NUMRECORDS - 1; } break; // 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 The are no records left, create one if (numercords == 0) newrecord ();} Break; // ch.5 New Button Case ContactDetailNewButton: {// ch.5 Create a new record newrecord ();} Break; // ch.7 Done Button Case ContactDetails;} 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 (), & cu rrentDate); // 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.Mont), & (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;

// 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 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 Entertimeh AndleEvent (eventptr energy) {FormPtr form; // ch.6 a form standture; // ch.6 The Original Time // Ch.6 get outform (); // 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 at: // ch.6 if a button at: {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. 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 FRMOPENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENEVENT: {// ch.7 Draw The form DrawTable ();

} Break; // ch.7 respond to a list selection case tblsectevent: {// ch.7 set the database cursor to the success-> 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 Rebuild the table drawTable ();} break; // CH.8 Respond to arrows case ctlRepeatEvent: {switch (event-> data.ctlRepeat.controlID) {// CH.8 Up arrow case ContactListRecordUpRepeating: if (cursor> 0) cursor--; break; // CH.8 Down arrow case ContactListRecordDownRepeating: if ((numRecords> Table_num_rows && (Cursor Data.keyDown.chr) {// ch.8 Up arrow hard key case Pageupchr: IF (CURSOR> TABLE_NUM_ROWS - 1) CURSOR - = Table_Num_Rows - 1; Else Cursor = 0; Break; // Ch.8 Down Arrow hard key case pageDownChr: if ((numRecords> 2 * TABLE_NUM_ROWS - 1) && (cursor

// ch.8 respond to Scrollbar Events case SCLREPEATEVENT: CURSOR = Event-> Data.SCLEXIT.NEWVALUE; DRAWTABLE (); Break;} // ch.7 end of the event switch statement // ch.7 We're DONE return (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) ; // ch.3 Do The Edit Command Switch (Event-> Data.Menu.itemid) {// Ch.3 undo Case Editundo: Fldundo (Field); Break; // Ch.3 Cut Case Editcut: Fldcut (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 = fldgettextLength (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 the 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 (voidptr precord; // ch.5 Pointer to the record // ch.7 crete the database record and get a handle to it if ((hrecord = DmNewRecord (contactsDB, & cursor, DB_RECORD_SIZE)) == NULL) errorExit (MemoryErrorAlert); // cH.5 Lock down the record to modify it precord = MemHandleLock (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 (contac TSDB, 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; //C .5 The Contact Detail Form Charptr Precord; // Ch.5 A Record Pointer Word INDEX;

// ch.5 the Object index // ch.5 get the contact detil 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 out @ = frm GetObjectIndex (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 Changes in The Record Static Void getfields (void) {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 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 MemHenLock (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 (Fieldptr Field, Charptr Text) {voidhand Hfield; //C .5 Handle of Field Text Charptr Pfield; // Ch.5 Pointer To Field Text // Ch.5 Get The Current Field Handle Hfield = FLDGettex THANDE (Field); // ch.5 if we have a handethiff = null) {// ch.5 resize it if (MemHandleresize (Hfield, Strlen (Text) 1)! = 0) errorexit (MemoryErroralert) } 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 MemHenlock (Hfield); // Ch.5 Give It to 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 Pfield = FldgetTextPtr (Field); // ch.5 Copy It DMWRITE (PRLORD, OFFSET, PFIELD, STRLEN (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 {CTLSetLabel (GetObject (Form, ContactDetAildateSeltrigger, "");} else // ch.6 if there is a date {char 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 if there's no time if (datetime.Hour == 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 (getObject (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, EnterTimeAMPushButton); pmButton = getObject (form, EnterTimePMPushButton); noTimeCheckbox = getObject (form, EnterTimeNoTimeCheckbox); // cH.6 If there Is a time if (datetime.Hour! = no_time) {// ch.6 Update the hour = datetime.Hour% 12; if (hour == 0) Hour = 12; CTLSetLabel (Hourbutton, Stritoa (Labelstring, Hour) ); // ch.6 Update the minute ten s CtlSetLabel (minuteTensButton, StrIToA (labelString, dateTime.minute / 10)); // CH.6 Update the minute ones CtlSetLabel (minuteOnesButton, 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, true;} // ch.6 we're done return;} // ch.7 this function is caled 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) {// cH.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 = (Long) (TimDateTimeToSeconds (pdateTime1) / 60) - (Long (Timdatetime2) / 60); // ch.7 date / time # 1 is latter if (ldiff> 0) SortResult = 1; Else // Ch.7 Date / Time # 2 is Later IF (LDIF f <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; // cH.7 Sort by last name case SORTBY_LAST_NAME: {sortResult = StrCompare (precord1 DB_LAST_NAME_START, precord2 DB_LAST_NAME_START);} break;} // cH.7 We're done return (sortResult);} / / CH.8 Draw our list of choices using a table object static void drawTable (void) {FormPtr form; TablePtr table; Int column; Int count; ControlPtr upArrow; ControlPtr downArrow; // CH.8 Get the form pointer form = FrmGetActiveForm ();

// CH.8 Get the table pointer table = getObject (form, ContactListTableTable); // CH.8 For all columns for (column = 0; column TABLE_NUM_ROWS) {// CH.8 Show the up arrow if (cursor> 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 (cursor> = numRecords - TABLE_NUM_ROWS) {CtlSetLabel (downArrow, GRAY_DOWN_ARROW); CtlSetEnabled (downArrow, false);} else {CtlSetLabel (downArrow , BLACK_DOWN_ARROW); CtlSetEnabled (downArrow, true);} CtlShowControl (downArrow); // CH.8 Show the scrollbar FrmShowObject (form, FrmGetObjectIndex (form, ContactListScrollbarScrollBar));

SclSetScrollBar (getObject (form, ContactListScrollbarScrollBar), cursor, 0, numRecords - TABLE_NUM_ROWS, TABLE_NUM_ROWS);} else {// CH.8 Hide the arrows CtlHideControl (upArrow); CtlHideControl (downArrow); // CH.8 Hide the scrollbar FrmHideObject (form, FrmGetObjectIndex (form, ContactListScrollbarScrollBar));} // CH.8 We're done return;} // CH.8 The custom drawing routine for a table cell static void drawCell (VoidPtr table, Word row, Word column, RectanglePtr bounds) {Int record; CharPtr precord; Char string [DB_FIRST_NAME_SIZE DB_LAST_NAME_SIZE]; SWord width; SWord len; Boolean noFit; // CH.8 Calculate our record record = cursor row; // CH.8 Get our record hrecord = DmQueryRecord (contactsDB, record); precord = MemHandleLock (hrecord); // CH.8 Get the date and time memMove (& dateTime, precord DB_DATE_TIME_START, sizeof (dateTime)); // CH.8 Switch on the column switch ( Column) {// ch. teToAscii (dateTime.month, dateTime.day, dateTime.year, (DateFormatType) PrefGetPreference (prefDateFormat), string);} else StrCopy (string, "-");} break; // CH.8 Handle times case TABLE_COLUMN_TIME: { IF (DateTime.Hour! = NO_TIME) {TimeToascii (DateTime. Hour, DateTime.minute, (TimeFormattype) prefgetPreference (preftimeformat), string;} else strcopy (string, "-");} Break; // ch.8 Handle names case TABLE_COLUMN_NAME: {StrCopy (string, precord DB_FIRST_NAME_START); StrCat (string, ""); StrCat (string, precord DB_LAST_NAME_START);} break;} // CH.8 Unlock the record MemHandleUnlock (hrecord);

转载请注明原文地址:https://www.9cbs.com/read-2726.html

New Post(0)