Palmos Development Tutorial - 5

zhaozj2021-02-08  239

Chapter 5 Database Palm OS All Contents are expressed in the form of a database in its memory. Let's start learning to create and use the database. We will continue to write the Contacts program and write it into a database. Delete Work To prepare to add a database to Contacts, you should first delete the previous demonstration statement. Backup Contacts programs first should back up the current Contacts program. I named it as CH.4. The steps are as follows: 1. Run a Windows browser; 2. Find and select the Contacts Engineering folder; 3. Press the Ctrl-C replication folder; 4. Click the folder you want to back up; 5. Press CTRL-V to attach the Contacts project folder; 6. Click on the name of Contacts to rename it to Contact Ch.4. From the resource file to remove some resources we are no longer used from the project from the project. The steps are as follows: 1. Open the resource constructor; 2. Open the Contacts.RSRC file in the Contacts engineering SRC folder; 3. Check the field of the fieldinit string resource, press DELECT to delete; 4. Close and save Contacts.RSRC. Deleting the code After deleting the resource, we have to delete and reorganize the code to make the program run normally.

The steps are as follows: 1. Run the Code Warrior integrated development environment; 2. Open Contacts.mcp from the Contacts Engineering Folder; 3. Remove the following code from the just beginning of Pilotmain (): // ch.3 Our Field Memory Handle Static Htext; // Ch.3 Handle to TEXT IUR EDIT FIELD #define htext_size 81 // ch.3 Size of Our Edit Field 4. () Near the top delete the following code from PilotMain: // CH.3 Get the initialization string resource handle hsrc = DmGetResource (strRsc, FieldInitString); // CH.3 Lock the resource, get the pointer psrc = MemHandleLock (hsrc) ; // ch.3 Allocate Our Field Chunk htext = MemHandLenew (hText_size); if (htext == null) Return (0); // ch.3 Lock the memory, get the point ptext = memhandLock (htext); /////////////// cH.3 Initialize it StrCopy (ptext, psrc); // cH.3 Unlock the field's memory MemHandleUnlock (htext); // cH.3 Unlock the resource's memory MemHandleUnlock (hsrc); // cH.3 Release the string resource DmReleaseResource (HSRC); 5. From contactDetailEventHandler () in the event processing section frmOPenEvent delete the following code: // CH.3 Get the index of our field index = FrmGetObjectIndex (form, ContactDetailFirstNameField); // CH.3 Get the pointer to our field field = FrmGetObjectPtr ( Form, index); // ch.3 set the Editable Text FldSettextHandle (Field, HText); // Ch.2 Draw The form frmdrawform (form); // ch.3 set the focus to outfield Field FRMSETFOCUS (Form, INDEX ); Remove frmCloseEvent; 7 from ContactDetaileventHandler (). Delete the function call from Pilotmain () delete MemHandlefree (). Adding a database Now adds a database. First, add some buttons to the Contact Detail form to browse database records; add a helper information and new warnings; then add program code that can be created and modified. There are many types of database technology and terminology databases. There is also an easily confusing term associated with them. I will explore the basic terminology and explain other terms we will contact. The most basic unit called record in the database, and some are also called "rows".

A record usually has some data composition: for example, a person's name, address, and phone number, etc. Each data can be called a field, also known as "table element" or "column". Usually "columns" refers to data that provides similar information in all records, such as all "last name" in the database. You can regard the database as an information table consisting of rows and columns. Each line represents a separate entry. Each line represents a special type of data collection associated with all entries. For example: You can represent a person with each line of the database. In this case, the column may be the name of the owner. If the line represents a date, then the column can represent the date of the date, the date time, etc. Under normal circumstances, you can only view a record of the database at a time. The row being being viewed is generally called "Cursor). Palm OS is called "index". Mobile is called "navigation" in a row of rows in the database. The database is generally divided into a flat-file database and a relational database. The plane database is composed of a separate table. Palm OS uses this simple database type. Relational databases are composed of many different tables, and they can be linked to different ways. Most of the database is now a relational database. In some Palm OS programs, you can work like a relationship database by building some flat databases. Databases can provide you with a subset of your query statements. This is like you ask a database a problem then the database is the same as the answer you want. This approach is called "query" in the database. By query the result set or the Solution Set. The database in the Palm OS is much flexible than a general flat database because its record is the memory block in the memory. You can explain them according to your needs. As a result, you have such a database: its records have different formats and lengths. Adding the content of Contacts.RSRC files Now we will see how the database works by programming. First, create a button to add (add) and delete (delete) records and browse (NaviGate) databases for the Contact Detail Form: 1. Open the resource constructor; 2. Open the Contacts.RSRC file in the SRC folder in the Contacts project; 3. Select the Contact Detail Form Double-click Open; 4. Select Window | Catalog to generate a Catalog window; 5. Drag three labels to the Contact Detail form. Set their properties according to the following table: Left Origin Top Origin Text 20 15 First Name 21 30 Last Name 2 45 Phone Number Note: The way I generate Left Origin NumBers is: First Press Shift to click the left button, then from the constructor On the menu, select Arrange | Align Right Edge. 6. Drag at least two input boxes on the Contact Detail form. Set their properties according to the table below: Object Identifier Leftorigin Toporigin Width Maxcharacters Auto Shift Firstname 80 15 79 15 Yes Lastname 80 30 79 15 Yes Phonenumer 80 45 79 15 Yes 7. Drag six buttons to the Contact Detail form.

Set their properties according to the following table: ObjectIdentifier Leftorigin Toporigin Width Maxcharacters First 1 130 28 First Prev 45 130 28 Prev next 88 130 28 NEXT LAST 131 130 28 Last delete 103 146 36 Delete New 53 146 36 New Note: I use this Tips make the button horizontally: Press SHIFT, select First, Prev, Next and Last buttons, then select Arrange | Spread Horizontally on the menu. When you finish the above steps, the obtained Contact Detail form should be shown in Figure 5-1. Figure 5-1: Added the Contact Detail Form Create alert to all elements to create a warning message When your Palm OS version is below 2.0 (Pilot 1000, Pilot 5000), you need to add a warning message. The steps are as follows: 1. Select the Alert resource and press Ctrl-K to create a new warning message; 2. Click its name to change it to LowromVersionError; 3. Double-click to open a new warning message; 4. Set the attribute Alert Type as Error; 5. Set the property title to Fatal Error; 6. Place the attribute Message as "The Version of Palm Device You Have CAN't Run This Software." Your warning message should be shown in Figure 5-2. When the new database cannot be created, you should also add a warning message to process. The steps are as follows: 1. Select the Alert resource and press Ctrl-K to create a new warning message; 2. Click its name to change it to dbcreationError; 3. Double-click to open a new warning message; 4. Set the attribute Alert Type as Error; 5. Set the property title to Fatal Error; 6. Set the attribute message as "The Contacts Database Could Not Be create." Your warning message should be shown in Figure 5-2; Figure 5-2: "Low version of the ROM Error" warning box 7. Close and save the Contacts.RSRC file. Figure 5-3: "Errors when the database creation" warning box Contacts. CF file content Add I will gradually explain the contents of the Contacts.c file and the content of the change, in this part will have a detail List. First we deserves the definition of the ROM version number used in pilotmain (). The new code to join will ensure that the Palm OS operation is more stable. // CH.4 Constants for ROM revision #define ROM_VERSION_2 0x02003000 #define ROM_VERSION_MIN ROM_VERSION_2 Note: For digital ROM_VERSION_2 use is to find from the Palm OS SDK Reference (CodeWarrior Docoumentation folder of Reference.pdf), this information is in 1012 . All problems encountered in this book can almost take these documents to do this. Six new utility prototypes are defined in the header of the file.

The newRecord () function generates a new record for the database; getRecord () function connection function frMgetObjectIndex () and functions frMgetObjectPtr (); function setFields () copy the contents of the database to the upper three text boxes. Functions getFields () populates the contents of the text box on the form to the database. Function setText () Sets text box content; function text box Get text from the text box and writes a locked database. We will browse these functions first and then fully master them. // 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); five new variables will be used in the database. When the database is open, the variable contactsdb allows us to operate it; variable NumRecords defines the current record number in the database; variable Cursor represents records displayed on the Contact Detail form; variable isdiRTY defines whether the current record has been modified, this is allowed We don't need to mark the records that have not changed, keep synchronization; variable hrecord represents the current recorded handle. // ch.5 Our open Database Reference Static DMopenRef ContactSDB; Static Ulong NumRecords; Static Uint Cursor; Static Boolean Isdirty; Static VoidHand HRecord; Our database record length is fixed (but not necessarily in Palm OS) ). In order to save records accurately, the start points of each record should be carefully defined. Sake of easy, a large number of constants are defined: // 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) above As shown, all the start points and lengths of each record in the database are defined. By the way, the recording length is defined for the entire record. In the previous chapter, we define memory blocks in the head of pilotmain (), and now we create and initialize a database.

Let us check the operating system version of the Palm device before writing a formal code. Since some database functions, especially sorting functions have changed since Palm OS 1.0, we need to join some code to ensure that the program works on Palm OS2.0 or higher. // 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

This function will call the original or existing database that is now created. Since the existence of the database has been guaranteed, the program will function properly. To call this database later, we save the database in ContactSDB. // CH.5 Get the number of records in the database numRecords = DmNumRecords (contactsDB); // CH.5 Initialize the record number cursor = 0; // CH.5 If there are no records, create one if (numRecords = = 0) NewRecord (); if the record that does not exist will cause the Palm OS crash, you must clear the records and ensure that you must be legally recorded in the program. You can do it according to the above code. First variable NumRecords is initialized. It gives you the range of you can address your record. In general, the first record number of the database is zero, so our initial Cursor is a legal record that can be accessed even if there is only one record in the database. If there is no record in the database, we created a record in the function newrecord (). // ch.5 Close All Open FormCloseallForms (); // ch.5 Close The Database DMCloseDatabase (ContactSDB); All records should release memory and turn off the database at the end of the program. To release the last record, we call the frmCloseEvent in the Contact Detail event handle function again. Whenever you change the form, we usually call this event. To ensure that all open forms can be turned off, it is a good way to call frmcloseallForms at the end of the program. After calling this function, we guarantee that all records have been turned off. Then call the function DMCloseDatabase () to turn off the database. // CH.5 This function creates and initializes a new record static void newRecord (void) {VoidPtr precord; // CH.5 Pointer to the record // CH.5 Create the database record and get a handle to it hrecord = DmNewRecord (ContactSDB, & Cursor, DB_Record_size); // 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. 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 isdioty = true; // ch.5 We're done return;} The function newrecord () Now we end the discussion of the event handle, then we start to analyze the active functions just added. A function newrecord () will add a new record in the current index location of the database defined by the variable CURSOR. DMNewRecord () creates a new record at the beginning of the function. But this record is just some spam.

So we must initialize the record and use a blank record instead of it. Like the second chapter, let's lock the record first, then add initialization information. In this case, we initialize the record information is all zero. Note that when you write zero, we use the function DMSET () instead of writing zero or calling strcopy (). This is because the memory block where the database is located is specially protected from the corruption, but only the function DMSET () can be used to change its content. If you want to write other non-zero characters, you need to call the function DMWRITE (). The return value for DmnewRecord () should check if it is zero, although we have not checked in the above case. If the return value is zero, it indicates that the record has overflows the memory. I will fix this negligence in Chapter 7, in which chapter will fully introduce fatal error resolution strategy. Like the previous FRMCLOSEEVENT, we call DMRELESERECORD () to release the memory occupied by DMGETRECORD () and DMQueryRecord () results. Because of the addition of a new record, the variable NumRecords plus one. The isDiRTY location "true" is to access data immediately in the ATTachFields () function below. Function getObject () Function getObject () can save a lot of time by associating frMgetObjectIndex () and frMgetObjectPtr (). Since we often use field pointers and other controls, this function allows us to make us more simple and future code, so more readability. // CH.5 A time saver: Gets object pointers based on their ID static VoidPtr getObject (FormPtr form, Word objectID) {Word index; // CH.5 The object index // CH.5 Get the index index = FrmGetObjectIndex ( FORM, ObjectID; // ch.5 Return The Pointer Return (FormGetObjectPtr (Form, Index));} Fields and Database records Most computer systems have a RAM and hard drives. RAM is used to save temporary data, and the hard disk is used to save data. Since the read and write speed of the RAM is much more than the hard disk, many of the programmakers do to read data from the hard disk or write data on the hard disk. Because the data in Palm is in memory, the access speed is very fast, and the access time is reduced to the minimum. In theory, when any data in Palm is read, it is not necessary to move the data to other special faster access as other computers. On the PC, when we accesses the database, you first assign memory for a new record in the RAM, and then write it into the database of the hard disk. This process will change faster in Palm, because the permanent memory in the PALM allocates memory, and then writes directly to the memory. Palm OS The process is visualized by calling the function fldSettext (), allowing you to contact a database directly with a database area. This area is called edit in place. Unfortunately, there are two main defects in the editing domain in the nearest Palm OS version. One is a field that can only access it for each record. If you have access to more than one record, the program may run for a while, but the final program will be locked. The second is that the fields behind the editing field edit field sometimes become zero fields. This is difficult to track in your code. Let's know this, so when you edit the database record, remember to put the field you want to edit into the last record. Due to this defect in the editing domain, we cannot apply it to the Contacts program to implement the necessary features.

However, it is also necessary to remember it (especially its defect), because when you edit a large single editing field, you may use it when you edit a large single-editing field. Function setFields () In function setfields (), records in the database are copied into the three editing boxes of the Contacts Detail form. // 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 replaord 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); precord = MemHandleLock (hrecord ); // 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 (isdioty) {// ch.3 get the index of out = frMgetObjectinde X (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; first, let's activate the form and record first. For each edit box, the function setText () copies the corresponding part of the record in the database to the inside. Tips: How to get a record has two ways to get an existing record. One is the function dmgetRecord () one is the function dmqueryRecord (). For DMGETRECORD (), it is important to note that the function DMRELESERECORD () release record must be invoked when the recording operation is completed. If you have forgotten this, then when you get the handle of this record, you get 0. New records we can identify by setting the "dirty" bit.

When new records appear, we set the cursor on the First name this field. The setting focus here is the same as those in the third chapter. The function setgrfstate () is required for setting upper Shift ON. This is because when this field sets the focus, even if we set the auto shift property, the system will not set up Upper Shift ON. Write the header file graffti.h in front of the header file pilot.h before calling the function setgrfstate (). // ch.5 added for the call to grfsetState () #include function getfields () function getfields () will be written to the current record in the field in the edit box. // CH.5 Wipes out field references to the record and releases it // static void detachFields (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.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.5 Unlock The Record MemHandleunlock (HRECORD);} // ch.5 reset the dirty bit isdirty = false; // ch.5 We're done return;} First move the focus to the recorded record. This means that a record should be specified before this, and this record is "dirty". Step 2, collect data from the form and enter the record you want to modify. Finally, we must clear the "dirty" bit. Otherwise, after the first record is modified, all records will be "dirty". The "dirty" bit is reset only when the user specifies the modification of the record or calling the function newrecord (). Like the string resource into the field in the Function SetText () and Chapter 3, the function setFields () passes a string and field pointer to the specified record.

// 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 MemHandleresize (Hfield, Strlen (Text) 1); } else // ch.5 allocate a Handle for the string Hfield = MemHandleNew (Strlen (text) 1); // ch.5 Lock it Pfield = MemHandLock (Hfield); // Ch.5 Copy THE STRING STRCOPY (Pfield , Text); // Ch.5 Unlock it MemHndleunlock (Hfield); // Ch.5 Give It to the Field FldSettextHandle (Field, Hfield); // Ch.5 Draw The Field FlddrawField (Field); // CH. 5 We're done return;} First we get the handle of the field. If the field has a handle, then we adjust it to fit the string. If the field has no handle, we assign one with a suitable size. It is very dangerous to judge whether the success of the adjustment and assignment handle is very dangerous. Separating newRecord (), we will supplement these code when you introduce the error handling in Chapter 7. When you get the correct handle, you are very familiar with you. Lock the handle, copy the string, unlock the string and write to the field. Finally, we draw (DRAW) editing box to reflect its changes. Function getText () getText () copies the contents of the field to the record of the database. // 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 (PRCORD, OFFSET, PFIELD, STRLEN (Pfield)); // Ch.5 We're Done Return;} First we get the pointer of the field string. Because this is the pointer inside the field, so we don't have to unlock it! We call the function DMWRITE () to copy the string of the field to the appropriate database records based on the offset given. Since we limit the number of characters that can write to the fields and specifies the size of the corresponding records, this copy can be guaranteed. This is why no error checking is added. The function ContactDetailHandleEvent () is added in the first CASE statement, that is, the FRMopeneVent statement, and then call the function setFields () after the function frmdrawform (), used to copy the current record to the database. The reason after the frmdrawform () is to draw the edit box before drawing the other parts of the form. Otherwise, the program will run errors.

// ch.4 form Open Event Case FRMopenevent: {// ch.2 Draw the form frmdrawform (form); // ch.5 draw the database fields setfields ();} Break; additional, some new events need to be deal with. The first is FRMCLOSEEVENT. // ch.5 form Close Event Case FRMCLOSEEVENT: {// ch.5 Store Away Any Modified Fields getfields ();} Break; This CASE statement is to get the last modified field before turning off the form. content. Next we like to handle other button events like previously handled button events CTLSelectEvent. This time we should know which button is pressed. To do this, we use the Switch ... case statement to determine as the identifier as the identifier. // 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; We handed the first button, the first button, this button is the first record of the database. . Before processed all button events, the function getFields () gets the current record to ensure that all changes can be saved before moving new records. In this button processing, we move the cursor to the first record. After processing all button events, we call setFields () to get new current records and copy them into each field. The next button event is the previous button, which moves the record to the previous one. // ch.5 Previous Button Case ContactDetailPrevbutton: {// ch.5 Move The Cursor Back One Record if (Cursor> 0) CURSOR -;} Break; After checking whether to reach the first record (arrive at the first record Afterwards, we cannot move forward), we will move ahead and associate new records and fields. The Next button is processed below. // ch.5 Next Button Case ContactDetailNextButton: {// ch.5 Move The Cursor Up One Record IF (Cursor <(NumRecords - 1)) CURSOR ;} Break; This code block and the process block are very similar. After the last record is determined, the record is moved. Why do we care about the handling of variables CURSOR? This is because if we pass the function DMQueryRecord () to visit a non-existing record, the program will immediately crash. Therefore, it is necessary to ensure that the CURSOR must be a valid value. Now let's take a look at the process of the button Last, this button will move to the last one.

// ch.5 Last Button Case ContactDetaillastButton: {// ch.5 Move The Cursor to the last recordiffness (Cursor <(NumRecords - 1)) CURSOR = NumRecords - 1;} Break; This code and PreviOS button event processing The code is somewhat similar. Note Because the first record number is 0, the last record should be minus one number of records. The following is a process Delete buttons: // CH.5 Delete button case ContactDetailDeleteButton: {// CH.5 Remove the record from the database DmRemoveRecord (contactsDB, cursor); of records numRecords-- // CH.5 Decrease the number; // 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; To ensure SETFIELDS () The successful call of the DMQueryRecord () function must ensure that at least one record is restored. Therefore, when checking to a record, call newRecord () creates a new record. Database applications that do not do a record is also possible, but must not be invoked to call the function DMQueryRecord (). Finally, we deal with the New button. // ch.5 New Button Case ContactDetailnewButton: {// ch.5 Create a new record newrecord ();} break;} // ch.5 sync the current record to the fields setfields ();} Break; or above And other browsing button code is similar. Only did not move the cursor to an existing record, but the marker refers to a record we just created. As a result, the record is automatically added as the current record and the recording number thereafter. Call setfields () after all button events are processed. Its role is to get and copy the current record (perhaps new records, perhaps no records that have not been modified) into each field. In addition, there is an event that needs to be processed. That is, if the record is modified, its "dirty" bit should be identified. One of these methods is to determine if the editing domain is called to identify whether the record is modified. // ch.5 respond to Field TAP Case Fldenterevent: isdirty = true; break; Here we use the "dirty" bit by judging whether the editing domain is selected. Debugged Once you have finished all the changes to the Contacts.c program, you can start using the Debug tool to debug the program. 1. Open the Code Warrior IDE integration development environment. 2. Open the Contacts.mcp Project File 3 under the Contacts directory. Select the Project | Make menu item. If your program can't succeed in compiling the connection, go back and check the last modified code. If your problem is named in some resources, remember to check the contacts_res.h file to make sure the .h file and the resource naming in the .c file match. 4. Synchronize Palm with PC, which is mainly to protect other data in Palm even if you do not lose data. 5. Exit HotSync Synchronization Software.

6. Once your code is compiled and connected, you can choose PROJECT | Debug to open the debugger. You can implement the progressive line of operation of the program by continuously pressing the single-step debug button at the top of the debugger window (like a large bracket below the right arrow). When you first run the program, the ContactSDB database will be established, and the DMCreatedTabase () function will return 0. When you run again, the return value of the DMCreatedatabase () function will be 537, that means that the database already exists (DMERRALRALREADYEXISTS). After each button processing event, the first row of the CASE statement sets breakpoints. After doing this, a small red point will appear on the left side of the appended statement. Click the button to the right arrow at the top of the debugger to make the program freedom. The Contact Detail form will appear, enter some data in the text box on the form, then click the "New" button to add a new record. I think you need to break away here to observe the implementation of the program. In order to enter the function newrecord (), we can first break the point in the previous statement of the function statement. When the program runs to this breakpoint, click the "Jump INTO" button at the top of the debugger (like a downward arrow, There is a parentheus outside the outside to enter the internal operation of the function. Then follow the statements in the run function in the single-step debug button to ensure that the function is running is normal. After the function is run, press the "Run" button at the top of the debugger. Run the program to create a minimum of 4 records for your database. Then you can start testing your "navigation" button. The debugging step is the same as the above, when you debug the corresponding function, make sure you can click on the small red point on the left of the broke-point statement to cancel the breakpoint settings, so your program is Will stop on the code that has already been debugged. When you ensure that "first", "previous", "Next", and "Last", you can start debugging the "Delete" button. Single step tracking the code of the "Delete" button, delete all records, and finally create a record to ensure that there is at least one record in the database. What is the next step? In Chapter 6 we will continue to improve the Contacts application. We will add time and date input to the Contacts Detail Form, which will use more controls. Source Procedure List The source code for Contacts.c modified by this chapter. The source code of this chapter has a complete copy in the CH.5 directory of the disc belonging to the book.

// ch.2 The super-incrude for the palm OS #include // ch.5 added for the call to grfsetstate () #include // 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 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); // 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.2 The main entry point DWord PilotMain (Word cmd, Ptr, Word) {DWord romVersion; // cH.4 ROM version FormPtr Form; // ch.2 a Pointer to out; // 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

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;} FrmSetActiveForm (form);} // CH.2 Handle form events FrmDispatchEvent (& event); // CH.2 If it's a stop event, exit} while (event.type! = Appstopevent); // ch.5 close all open forms frmCloseallForms (); // ch.5 close the database DmCloseDatabase (contactsdb); // ch.2 WE 'reed return (0);} // ch.4 Our contact static boolean contactdetailhandleevent (eventptr80) {Formptr form; // ch.3 a Pointer to Our Form structure // ch.3 Get Our Form Pointer form = frMgetActiveform (); // ch.4 Parse Events switch (event-> eType) {// ch.4 form Open Event Case FRMOPENEVENEVENT: {// ch.2 Draw The Formdrawfo RM (Form); // ch.5 draw the database fields setfields ();} Break; // ch.5 Form Close Event Case frmCloseEvent: {// ch.5 store offer 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 f (cursor> 0) Cursor = 0;} Break; // ch.5 Previous Button Case ContactDetailPrevButton: {// ch.5 Move The Cursor Back One Recordiffiffness (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.5 sync the current record to the fields setfields ();} break; // ch.5 respond to Field TAP Case Fldenterevent: Isdirty = True; Break; // ch.3 Parse menu Events case mevevent: return (menueventhandler (event)); Break;} // ch.2 We're done return (false);} // ch.4 Our About Form Event Handler Function Static Boolean AboutHandleevent (Eventptr Even T) {FormPtr form; // ch.4 a Pointer to out 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 form if (event-> eType == ctlselectEvent) {frmreturntoform (0); // ch.4 Always Return True in this case return (true);} // ch.4 We're done return (false);} // ch.3 Handle Menu Events Boolean MenueventHandler (eventptr 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 as a Dialog FRMPOPFORM (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 success, 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; Brea K; // ch.3 Paste Case EditPaste: Fldpaste (Field); Break; // Ch.3 Select All Case Editslectall: {// 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; return (false);} // ch.3 Select the whole string fldsetSelection (Field, 0, Length);} BREAK ; // ch.3 BRING UPTKEYBOARD TOOL CASE EditKeyboard: SyskeyboardDialogv10 (); Break;} // ch.3 We're Done Return (TRUE);} // ch.5 this function creates and initializes a new record static Void newRecord (void) {voidptr precord;

// CH.5 Pointer to the record // CH.5 Create the database record and get a handle to it hrecord = DmNewRecord (contactsDB, & cursor, DB_RECORD_SIZE); // 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.5 Unlock The Record MemHandleunlock (HRECORD); // Ch.5 Clear The Busy Bit and Set The Dirty Bit DMRELESERECORD (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 (formgetObjectptr (formgetObjectptr (form, index));} // ch.5 gets the current database record and displays it // in the detail fields static void setfields (void) {FormPtr form; / / Ch.5 the Contact Detail Form Charptr PRecord; // Ch.5 A Record Pointer Word Index; // Ch.5 The Object Index // Ch.5 Get The Contact Detail Form Pointer FormGGTACTIVEFORM (); // ch.5 Get The Current record hrecord = DmQueryRecord (contactsDB, cursor); precord = MemHandleLock (hrecord); // 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 up Upper Shift On GrfsetState (false, false, true);} // ch.5 we're done return;} / / Ch.5 Puts any Field Changes in The Record Void getfields (void) {Formptr form; // ch.5 The Contact Detail Form // 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.5 Lock The Record Prism 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, ContactDetaillastname Field), 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.5 Unlock the record MemHandleUnlock (hrecord);} / / 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 fit) {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 HANDE IF (Hfield ! = Null) {// ch.5 Resize it MemHandleresize (Hfield, Strlen (Text) 1);

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

New Post(0)