📄 quiklist.c
字号:
/********************************************************************* FILE: QuikList.c** DESCRIPTION: The QuikList project module. This version of QuikList shows you* how to implement a database-based list with multi-character* Graffiti lookup and category support. The category support* is for read-only databases. ** VERSION: 3.0**********************************************************************/#include <PalmOS.h>#include <TxtGlue.h>#include "QuikList.h"#include "QuikListRsc.h"#include "Utils.h"#include "UtilsRsc.h"/*********************************************************************** * FUNCTION: StartGraffitiLookup * * DESCRIPTION: Initiate a record lookup using a user-entered keystroke, * The lookup uses a two-part timing mechanism: one setting * for the EvtGetEvent timeout value, and a secondary counter. * * RETURNED: Nothing. * * SIDE EFFECT: sets three global variables: * gFindStr (the lookup string), the EvtGetEvent timeout setting, * and the lookup counter. ***********************************************************************/static void StartGraffitiLookup( Char chr // ( in ) The next char to put in the lookup string){ Int32 fStrLen = StrLen ( gFindStr ); // Turn on lookup indicator so user knows a lookup is taking place FrmShowObject ( FrmGetActiveForm (), FrmGetObjectIndex ( FrmGetActiveForm (), MainLookupIndicatorLabel )); // Set up the lookup string gFindStr [ fStrLen ] = chr; gFindStr [ fStrLen + 1 ] = NULL; // Arm (rearm) the nilEvent timer and its counter. We need a value > 1 in// gNilEventCnt because nilEvents may appear for reasons other than// the timer. gEventTimeout = LOOKUP_TIMER_CNT; gNilEventCnt = NIL_EVENT_CNT;}/*********************************************************************** * FUNCTION: StopGrafftiLookup * * DESCRIPTION: Check to see if there's an active Graffiti lookup going on. * If so, check for termination. This gets called every * LOOKUP_TIMER_CNT ticks until gNilEventCnt is zero. * * RETURNED: Nothing. ***********************************************************************/static void StopGrafftiLookup( void){ gNilEventCnt--; if ( gNilEventCnt == 0 ) {// Hide the lookup indicator, clear the lookup string, and // turn off nilEvent generation. FrmHideObject ( FrmGetActiveForm (), FrmGetObjectIndex ( FrmGetActiveForm (), MainLookupIndicatorLabel )); gFindStr [ 0 ] = NULL; gEventTimeout = evtWaitForever; }}#pragma mark ----------------/*********************************************************************** * FUNCTION: DBGetNthRecInCategory * * DESCRIPTION: Find the Nth record in a category and return its * physical record number in database. * * RETURNED: The record index or -1 if it can't be found. ***********************************************************************/static Int16 DBGetNthRecInCategory( DmOpenRef dbRef, // ( in ) the database to search UInt16 recNum, // ( in ) the relative rec # to find UInt16 curCat, // ( in ) the current category UInt16 numRecs // ( in ) # recs in the current category){ Err error; Int16 result = -1; UInt16 realRecNum; UInt16 offset; Int16 direction; // Some preventative error checking. Should never happen. if ( recNum < numRecs ) { // Set up forward search for records in 1st half of category.// Start at 1st record and search forward if ( recNum < ( numRecs / 2 )) { realRecNum = 0; offset = recNum; direction = dmSeekForward; } else // Backward search for 1st half of category records.// Start at last record and search backward. { realRecNum = dmMaxRecordIndex; offset = numRecs - recNum - 1; direction = dmSeekBackward; }// Look for the record. error = DmSeekRecordInCategory ( dbRef, &realRecNum, offset, direction, curCat ); if ( error == 0 ) result = ( Int16 ) realRecNum; } return ( result );}/*********************************************************************** * FUNCTION: DBSearch * * DESCRIPTION: Search a database for the first record alphabetically that * matches a specific string. NOTE: This routine assumes that * the database records are sorted in ascending order. * * RETURNED: -1 if no match is found, the record index otherwise. ***********************************************************************/static Int32 DBSearch( Char * fStrP, // ( in ) the string to match against DmOpenRef dbRef, // ( in ) the database to search UInt16 curCat // ( in ) the active category){ Char recData [ MAX_NAME_LENGTH ]; Int16 compare, result = -1; Int16 realRecNum; UInt16 mid, lower = 0; UInt16 fStrLen = ( UInt16 ) StrLen ( fStrP ); UInt16 upper = ( UInt16 ) gListSize; // Binary search thru the database looking for a match do { // Get the next record number mid = (( lower + upper ) / 2 ); realRecNum = DBGetNthRecInCategory ( dbRef, mid, curCat, ( UInt16 ) gListSize ); if ( realRecNum >= 0 ) { // Check for a match GetRecordData ( dbRef, ( UInt16 ) realRecNum, recData ); compare = TxtCaselessCompare ( fStrP, fStrLen, NULL, recData, fStrLen, NULL );// Found, return record # and get out the binary search loop if ( compare == 0 ) { result = ( Int16 ) mid; break; } }// Check for termination if ( mid == lower || mid == upper ) break; // Not found, move search location else if ( compare < 0 ) upper = mid; else lower = mid; } while ( 1 );// If a match, look at the predecessor records sequentially// to find the lowest record # that still matches. if ( result > 0 ) { do { // Save current match for return value to avoid an extra record lookup at end of this loop result--; realRecNum = DBGetNthRecInCategory ( dbRef, ( UInt16 ) result, curCat, ( UInt16 ) gListSize ); if ( realRecNum >= 0 ) { // Compare the previous logical record against the lookup string GetRecordData ( dbRef, ( UInt16 ) realRecNum, recData ); compare = TxtCaselessCompare ( fStrP, fStrLen, NULL, recData, fStrLen, NULL ); // When we hit the first record that doesn't match, set the return value// to the lowest physical record # that does match and exit. if ( compare > 0 ) { result++; break; } } } while ( result != 0 ); } return ( result );}#pragma mark ----------------/*********************************************************************** * FUNCTION: MainFormDrawList ** DESCRIPTION: Draw the main form list. In between each line,* check for a new incoming keystroke. If there is one, do* another Graffiti lookup. * * RETURNED: nothing ***********************************************************************/static void MainFormDrawList( void){#define NUM_REDRAWS 10 UInt16 i, j; Coord x, y; EventType event; Char name [ MAX_NAME_LENGTH ]; Int16 result;// Erase the list's rectangle WinEraseRectangle ( &gListBounds, 0 ); for ( i = 0; i < VIS_ITEMS; i++ ) { // Check for keystroke in middle of list redraw.// If there is one, do a lookup to see if there is a new list location// match. If so, stop redrawing until the next redraw kicks in.// StopRedraw is set in ProcessKeystroke. if ( ! gStopDrawing ) { if ( EvtSysEventAvail ( false )) { EvtGetEvent ( &event, 0 ); SysHandleEvent ( &event ); if ( event.eType == keyDownEvent ) { Boolean handled = MainFormProcessKeystroke ( ( UInt8 ) event.data.keyDown.chr, true ); if ( gStopDrawing ) return; } }// No event available, get the correct record *name = NULL; result = ( Int16 ) DBGetNthRecInCategory ( gDBRef, ( UInt16 ) ( gTopItem + i ), gCurCatNum, ( UInt16 ) gListSize ); if ( result >= 0 ) { GetRecordData ( gDBRef, ( UInt16 ) result, name );// Draw the current line x = ( Coord ) ( gListBounds.topLeft.x + 2 ); y = ( Coord ) ( gListBounds.topLeft.y + ( i * FONT_HEIGHT )); for ( j = 0; j < NUM_REDRAWS; j++ ) WinDrawChars ( name, StrLen ( name ), x, y ); } } }}/*********************************************************************** * FUNCTION: FitToListBoundaries * * DESCRIPTION: Check an incoming list position to make sure it* it fits within the legal boundaries for the list. * * RETURNED: A valid list position ***********************************************************************/static Int16 FitToListBoundaries( Int16 topItem // ( in ) the list item to check){ Int16 result = topItem; if ( result < 0 ) result = 0; if ( result > gListSize - VIS_ITEMS ) result = gListSize - VIS_ITEMS; return ( result );}/*********************************************************************** * FUNCTION: MainFormProcessKeystroke * * DESCRIPTION: Process a keystroke event for the Main form. Two types * of events are handled--hardware scroll up and down buttons * and Graffiti-based record lookup. * * RETURNED: True if the keystroke is handled. ***********************************************************************/static Boolean MainFormProcessKeystroke( UInt8 chr, // ( in ) Keystroke to process Boolean drawing // ( in ) Currently drawing a list?){ Boolean handled = false; ListPtr listP = GetObjectPtr ( MainNamesList ); if ( listP == NULL ) return handled;// First, check for scroll up/down buttons if ( chr == vchrPageUp ) { if ( gTopItem != 0 ) { gTopItem = FitToListBoundaries ( gTopItem - VIS_ITEMS + 1 ); MainFormDrawList (); } handled = true; } else if ( chr == vchrPageDown ) { if ( gTopItem != gListSize - VIS_ITEMS ) { gTopItem = FitToListBoundaries ( gTopItem + VIS_ITEMS - 1 ); MainFormDrawList (); } handled = true; } else // If it's a valid char, start Graffiti lookup // if ( TxtGlueCharIsAlNum ( chr ) && DmNumRecordsInCategory ( gDBRef, gCurCatNum ) > 0 ) if ( TxtGlueCharIsPrint ( chr ) && gListSize > 0 ) { Int32 recLoc = -1; handled = true;// Only do the lookup if there's room in the lookup string if ( StrLen ( gFindStr ) < MAX_MATCH_CHAR ) { StartGraffitiLookup (( Char ) chr ); // Lookup started, check for a record that starts with the string recLoc = DBSearch ( gFindStr, gDBRef, gCurCatNum ); if ( recLoc >= 0 ) { // Found a record, redraw the list so the matched record is the topmost// visible item. If this is a draw for a new multi-char match (if drawing is true),// this redraw takes place before the previous list drawing completes. gTopItem = FitToListBoundaries (( Int16 ) recLoc ); MainFormDrawList (); // Disable drawing the previous list if one is in progress. If not, enable drawing// in case this is the 1st list drawing of a multi-char match that previously// disable redrawing. gStopDrawing = drawing; } } } return ( handled );}#pragma mark ----------------/*********************************************************************** * FUNCTION: MainFormHandleRWCategory * * DESCRIPTION: Handle a category popup event for a Read/Write database. * * RETURNED: nothing * ***********************************************************************/static void MainFormHandleRWCategory
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -