diff --git a/.cdtbuild b/.cdtbuild index 9107c1f..850d03e 100644 --- a/.cdtbuild +++ b/.cdtbuild @@ -19,9 +19,11 @@ + + @@ -40,6 +42,10 @@ + + + + diff --git a/rsc/AppResources.xrd b/rsc/AppResources.xrd index 848e467..30a26eb 100644 --- a/rsc/AppResources.xrd +++ b/rsc/AppResources.xrd @@ -13,6 +13,45 @@ + + ERROR_ALERT + 0 + 0 + "Scanner Error" + + "There was an error closing the scanner.\n\nYou may need to reset the device." + + + "OK" + + + + + ERROR_ALERT + 0 + 0 + "Scanner Error" + + "There was an error enabling the scanner.\n\nYou may need to reset the device." + + + "OK" + + + + + ERROR_ALERT + 0 + 0 + "Scanner Error" + + "There was an error opening the scanner.\n\nYou may need to reset the device." + + + "OK" + + + @@ -256,7 +295,7 @@ 1102 38 - 27 + 26 TRUE LARGE_BOLD_FONT @@ -265,18 +304,18 @@ 1103 - 16 - 78 + 28 + 71 TRUE STD_FONT - "Copyright © 2012 Lance Edgar" + "Copyright © 2012 - 2013" 1104 46 - 53 + 47 TRUE BOLD_FONT @@ -301,12 +340,22 @@ 1000 26 - 105 + 111 TRUE STD_FONT "http://rattail.edbob.org/" + + 1001 + + 54 + 88 + + TRUE + STD_FONT + "Lance Edgar" + diff --git a/src/AppMain.c b/src/AppMain.c index f94f7f7..b2df71d 100644 --- a/src/AppMain.c +++ b/src/AppMain.c @@ -1,13 +1,38 @@ -/****************************************************************************** +/* -*- coding: utf-8 -*- */ +/***************************************************************************** * - * Copyright (c) 2007, ACCESS Systems Americas, Inc. All Rights Reserved. + * Rattail -- Retail Software Framework + * Copyright © 2010-2013 Lance Edgar * - * File: AppMain.c + * This file is part of Rattail. + * + * Rattail is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Rattail is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Rattail. If not, see . * *****************************************************************************/ + +/************************************************************ + * Palm Includes + ************************************************************/ + #include + +/************************************************************ + * Scanner Includes + ************************************************************/ + #ifdef SCANLIB_BCS2 #include "BCS2ScannerLib.h" #endif @@ -19,47 +44,47 @@ #ifdef SCANLIB_SYMBOL #include "Symbol/ScanMgrDef.h" +#include "Symbol/ScanMgrStruct.h" +#include "Symbol/ScanMgr.h" #endif -// Rattail App + +/************************************************************ + * Rattail Includes + ************************************************************/ + #include "AppResources.h" -#include "AppMain.h" +/* #include "AppMain.h" */ -/*********************************************************************** - * - * Entry Points - * - ***********************************************************************/ +/************************************************************ + * Palm App Constants + ************************************************************/ + +#define appFileCreator 'RTTL' +#define appVersionNum 0x01 +#define appPrefID 0x00 +#define appPrefVersionNum 0x01 -/*********************************************************************** - * - * Internal Constants - * - ***********************************************************************/ -#define appFileCreator 'RTTL' -#define appVersionNum 0x01 -#define appPrefID 0x00 -#define appPrefVersionNum 0x01 +/************************************************************ + * Rattail App Constants + ************************************************************/ + #define appDbRattail "Rattail" -#define appDbRattailScan "Rattail_Scan" +#define dbScanRecords "Rattail_Scan" -#define COLUMN_BARCODE 0 +/* UInt16 cardNo = 0; */ +#define cardNumber 0 + +#define COLUMN_BARCODE 0 #define COLUMN_CASES 1 #define COLUMN_UNITS 2 -/*********************************************************************** - * - * Global Variables - * - ***********************************************************************/ -UInt16 cardNo = 0; -//UInt16 gNumCols = 3; -//UInt16 gNumRows = 11; -//MemHandle gHandles[11][3]; -Int16 gTopVisibleRecord = -1; +/************************************************************ + * Scanner App Variables + ************************************************************/ #ifdef SCANLIB_BCS2 UInt16 gBcs2RefNum = 0; @@ -69,6 +94,33 @@ UInt16 gBcs2RefNum = 0; Boolean gBabbo = false; #endif +#ifdef SCANLIB_SYMBOL +// These are used for the data coming from the scanner. +/* MemHandle hScanData; */ +/* UInt8* pScanData; */ +/* Int16 scanDataLength; */ +#endif + + +/************************************************************ + * Rattail App Variables + ************************************************************/ + +//UInt16 gNumCols = 3; +//UInt16 gNumRows = 11; +//MemHandle gHandles[11][3]; +Int16 gTopVisibleRecord = -1; + +// This handle serves the barcode field. +/* MemHandle hBarcode; */ + +// This string is used for the barcode field. +/* Char gBarcodeText[15] = ""; */ + +// These track whether we have a working scanner, and whether it is currently +// enabled. +Boolean bHaveScanner = false; + // This stores the resource ID of the selector trigger associated with the // quantity currently being edited with the Quantity Form. Its value will be // either MainFormCases or MainFormUnits. @@ -92,6 +144,250 @@ Char* gQuantity; Boolean gQuantityTouched; +/************************************************************ + * Type Definitions + ************************************************************/ + +typedef struct { + Char barcode[15]; + Char cases[4]; + Char units[4]; +} RatScanDbRecord; + + +/************************************************************ + * Function Declarations + ************************************************************/ + +// Application +static Err AppStart(); +static void AppEventLoop(); +static void AppStop(); +static Boolean AppHandleEvent(EventPtr event); + +// Database +static DmOpenRef CreateScanDatabase(); +static DmOpenRef OpenScanDatabase(); + +// Scanner +static void EnableScanner(Boolean enable); + +// Main Form +static Boolean MainFormHandleEvent(EventPtr event); +static void MainFormInit(FormPtr form); +static Boolean MainFormDoCommand(UInt16 command); + +// Main Form Table +static void MainFormLoadTable(TablePtr table, Boolean updateScroll); +static void DrawCustomTableItem(void* table, Int16 row, Int16 col, RectangleType* bounds); + +static void FocusBarcode(); +#ifdef SCANLIB_SYMBOL +static void FetchScanData(); +#endif +static void ProcessScan(); +static void StoreScanData(); +static Boolean IsChecked(FormPtr form, UInt16 controlID); +static void ShowQuantityForm(UInt16 triggerID, Boolean manual); + +// Quantity Form +static Boolean QuantityFormHandleEvent(EventPtr event); +static void QuantityFormInit(FormPtr form); +static void AppendQuantityDigit(UInt16 digit); +static void UpdateQuantityDisplay(); +static void QuantityFormAccept(); + +// Scanner-Specific +#ifdef SCANLIB_BCS2 +static Boolean OpenBCS2Scanner(); +static void CloseBCS2Scanner(); +#endif + + +/************************************************************ + * + * FUNCTION: PilotMain + * + ************************************************************/ + +UInt32 PilotMain(UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags) +{ + Err error = errNone; + +#ifdef SCANLIB_BCS2 + SysNotifyParamType* notifyParams = (SysNotifyParamType*) cmdPBP; + Char * scancode; + FormPtr form; + FieldPtr barcode; + MemHandle barcodeH; + MemPtr barcodeP; +#endif + + switch (cmd) { + case sysAppLaunchCmdNormalLaunch: + if ((error = AppStart()) == 0) { + AppEventLoop(); + AppStop(); + } + break; + +#ifdef SCANLIB_BCS2 + case sysAppLaunchCmdNotify: + if (notifyParams->notifyType == BCS2BarCodeReadyNotification) { + + // Get pointer to scanned data. + scancode = (Char*) notifyParams->notifyDetailsP + 1; + + // Verify we got a good read from the scanner. + if (StrCompareAscii(scancode, "NO READ") == 0) { + SndPlaySystemSound(sndWarning); + + } else { + SndPlaySystemSound(sndConfirmation); + + form = FrmGetFormPtr(MainForm); + barcode = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormBarcode)); + + barcodeH = FldGetTextHandle(barcode); + FldSetTextHandle(barcode, NULL); + if (! barcodeH) { + barcodeH = MemHandleNew(15); // ugh + } + + barcodeP = MemHandleLock(barcodeH); + StrCopy((Char*) barcodeP, scancode); + MemHandleUnlock(barcodeH); + + FldSetTextHandle(barcode, barcodeH); + FldDrawField(barcode); + + ProcessScan(); + } + } + break; +#endif + + default: + break; + } + + return error; +} + + +/************************************************************ + * + * FUNCTION: AppStart + * + ************************************************************/ + +static Err AppStart() +{ +#ifdef SCANLIB_BCS2 + OpenBCS2Scanner(); +#endif + +#ifdef SCANLIB_JANAM + //OpenBabboScanner(); +#endif + +#ifdef SCANLIB_SYMBOL + if (ScanOpenDecoder() == STATUS_OK) { + bHaveScanner = true; + } else { + FrmAlert(AlertScannerOpenError); + } +#endif + + EnableScanner(false); + + // Initialize the barcode memory handle; needs 14 characters. + /* hBarcode = MemHandleNew(15); */ + + FrmGotoForm(MainForm); + return errNone; +} + + +/************************************************************ + * + * FUNCTION: AppStop + * + ************************************************************/ + +static void AppStop() +{ + EnableScanner(false); + +#ifdef SCANLIB_BCS2 + CloseBCS2Scanner(); +#endif + +#ifdef SCANLIB_JANAM + if (bHaveScanner) { + ScnCloseDecoder(); + } +#endif + +#ifdef SCANLIB_SYMBOL + if (bHaveScanner) { + if (ScanCloseDecoder() != 0) { + FrmAlert(AlertScannerCloseError); + } + } +#endif + + /* if (hBarcode) { */ + /* MemHandleFree(hBarcode); */ + /* } */ + + FrmCloseAllForms(); +} + + +/************************************************************ + * + * FUNCTION: AppEventLoop + * + ************************************************************/ + +static void AppEventLoop() +{ + Err error; + EventType event; + Boolean defaultSysHandler; + + do { + EvtGetEvent(&event, evtWaitForever); + defaultSysHandler = true; + +#ifdef SCANLIB_BCS2 + // The "center" hardware key on Aceeca Meazura devices normally is + // handled by the default system handler. But we want to use it to + // trigger the scanner, so we must bypass the system handler and let + // the event bubble up to the application handler instead. This is + // possibly very bad form, but as of this writing I'm not sure how else + // to accomplish using that button for a trigger. + if (gBcs2RefNum && (event.eType == keyDownEvent) && (event.data.keyDown.chr == MzVCentreKey)) { + defaultSysHandler = false; + } +#endif + + if (defaultSysHandler && SysHandleEvent(&event)) + continue; + + if (MenuHandleEvent(0, &event, &error)) + continue; + + if (AppHandleEvent(&event)) + continue; + + FrmDispatchEvent(&event); + + } while (event.eType != appStopEvent); +} + + /************************************************************ * * FUNCTION: AppHandleEvent @@ -130,243 +426,6 @@ static Boolean AppHandleEvent(EventPtr event) } -/************************************************************ - * - * FUNCTION: AppStart - * - ************************************************************/ - -static Err AppStart(void) -{ -#ifdef SCANLIB_BCS2 - OpenBCS2Scanner(); -#endif - -#ifdef SCANLIB_JANAM - //OpenBabboScanner(); -#endif - - FrmGotoForm(MainForm); - return errNone; -} - - -/************************************************************ - * - * FUNCTION: AppStop - * - ************************************************************/ - -static void AppStop(void) -{ -#ifdef SCANLIB_BCS2 - CloseBCS2Scanner(); -#endif - -#ifdef SCANLIB_JANAM - //CloseBabboScanner(); -#endif - - FrmCloseAllForms(); -} - - -/************************************************************ - * - * FUNCTION: AppEventLoop - * - ************************************************************/ - -static void AppEventLoop(void) -{ - Err error; - EventType event; - - do { - EvtGetEvent(&event, evtWaitForever); - - if (SysHandleEvent(&event)) - continue; - - if (MenuHandleEvent(0, &event, &error)) - continue; - - if (AppHandleEvent(&event)) - continue; - - FrmDispatchEvent(&event); - - } while (event.eType != appStopEvent); -} - - -#ifdef SCANLIB_JANAM -/************************************************************ - * - * FUNCTION: CloseBabboScanner - * - ************************************************************/ - -/* -static void CloseBabboScanner() -{ - if (! gBabbo) - return; - - ScnCmdScanDisable(); - ScnCloseDecoder(); - gBabbo = false; -} -*/ -#endif - - -#ifdef SCANLIB_BCS2 -/************************************************************ - * - * FUNCTION: CloseBCS2Scanner - * - ************************************************************/ - -static void CloseBCS2Scanner() -{ - LocalID dbId; - - if (! gBcs2RefNum) - return; - - dbId = DmFindDatabase(cardNo, appDbRattail); - if (dbId) - SysNotifyUnregister(cardNo, dbId, BCS2BarCodeReadyNotification, - sysNotifyNormalPriority); - BCS2LibClose(gBcs2RefNum); - SysLibRemove(gBcs2RefNum); - gBcs2RefNum = 0; -} -#endif - - -/************************************************************ - * - * FUNCTION: CreateScanDatabase - * - ************************************************************/ - -static DmOpenRef CreateScanDatabase() -{ - Err err; - LocalID dbId; - UInt16 attrs, version; - DmOpenRef db; - - err = DmCreateDatabase(cardNo, appDbRattailScan, appFileCreator, 'SCAN', false); - if (err != errNone) - return NULL; - - dbId = DmFindDatabase(cardNo, appDbRattailScan); - if (dbId == 0) - return NULL; - - // Set backup flag, version for database. - attrs = dmHdrAttrBackup; - version = 1; - DmSetDatabaseInfo(cardNo, dbId, NULL, &attrs, &version, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL); - - db = DmOpenDatabase(cardNo, dbId, dmModeWrite); - if (db == 0) - return NULL; - - return db; -} - - -/************************************************************ - * - * FUNCTION: DrawCustomTableItem - * - ************************************************************/ - -static void DrawCustomTableItem(void* table, Int16 row, Int16 col, RectangleType* bounds) -{ - Char * text; - UInt16 size; - Coord x; - - text = (Char *) TblGetItemPtr(table, row, col); - if (*text != 0) { - size = StrLen(text); - - if (col == 0) { // barcode - WinDrawChars(text, size, bounds->topLeft.x, bounds->topLeft.y); - - } else { // cases or units - - // Only draw value if nonzero. - if (StrCompareAscii(text, "0") != 0) { - - // Draw value right-aligned. - x = bounds->topLeft.x + bounds->extent.x - FntCharsWidth(text, size); - WinDrawChars(text, size, x, bounds->topLeft.y); - } - } - - } -} - - -/************************************************************ - * - * FUNCTION: IsChecked - * - ************************************************************/ - -static Boolean IsChecked(FormPtr form, UInt16 controlID) -{ - return (Boolean) CtlGetValue(FrmGetObjectPtr(form, FrmGetObjectIndex(form, controlID))); -} - - -/************************************************************ - * - * FUNCTION: GetObjectPtr - * - ************************************************************/ - -static void *GetObjectPtr(UInt16 objectID) -{ - FormPtr form; - - form = FrmGetActiveForm(); - return FrmGetObjectPtr(form, FrmGetObjectIndex(form, objectID)); -} - - -/************************************************************ - * - * FUNCTION: MainFormDoCommand - * - ************************************************************/ - -static Boolean MainFormDoCommand(UInt16 command) -{ - Boolean handled = false; - FormPtr form; - - switch (command) { - case MainOptionsAboutStarterApp: - form = FrmInitForm(AboutForm); - FrmDoDialog(form); - FrmDeleteForm(form); - handled = true; - break; - - } - - return handled; -} - - /************************************************************ * * FUNCTION: MainFormHandleEvent @@ -380,10 +439,11 @@ static Boolean MainFormHandleEvent(EventPtr event) TablePtr table; //UInt16 objId; + form = FrmGetFormPtr(MainForm); + switch (event->eType) { case frmOpenEvent: - form = FrmGetActiveForm(); MainFormInit(form); FrmDrawForm(form); handled = true; @@ -392,6 +452,27 @@ static Boolean MainFormHandleEvent(EventPtr event) case menuEvent: handled = MainFormDoCommand(event->data.menu.itemID); break; + +#ifdef SCANLIB_SYMBOLY + /* case fldEnterEvent: */ + /* if (event->data.fldEnter.fieldID == MainFormBarcode) { */ + /* ScanCmdScanEnable(); */ + /* } */ + /* break; */ + + /* case tblEnterEvent: */ + /* if (event->data.tblEnter.tableID == MainFormScanRecords) { */ + /* FrmAlert(1000); */ + /* ScanCmdScanDisable(); */ + /* } */ + /* break; */ + + case scanDecodeEvent: + FetchScanData(); + /* ProcessScan(); */ + handled = true; + break; +#endif case sclRepeatEvent: if (event->data.sclRepeat.newValue > event->data.sclRepeat.value) @@ -399,7 +480,8 @@ static Boolean MainFormHandleEvent(EventPtr event) else { gTopVisibleRecord -= (event->data.sclRepeat.value - event->data.sclRepeat.newValue); } - table = GetObjectPtr(MainFormScanRecords); + + table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords)); MainFormLoadTable(table, false); TblRedrawTable(table); break; @@ -410,21 +492,24 @@ static Boolean MainFormHandleEvent(EventPtr event) // We only have one field (barcode) on the main form. If it's // active then we can operate on the ENTER key. - form = FrmGetFormPtr(MainForm); if (FrmGetActiveField(form)) { - - if (IsChecked(form, MainFormStopOnCases)) { - ShowQuantityForm(MainFormCases, false); - } else if (IsChecked(form, MainFormStopOnUnits)) { - ShowQuantityForm(MainFormUnits, false); - } else { - StoreScanData(); - } + ProcessScan(); handled = true; } + +#ifdef SCANLIB_BCS2 + } else if (event->data.keyDown.chr == MzVCentreKey) { + // On Meazura devices, the "center" hardware key is our scan + // trigger. But we'll only trigger the scanner if the barcode + // field has focus. + if (gBcs2RefNum && (FrmGetFocus(form) == FrmGetObjectIndex(form, MainFormBarcode))) { + BCS2LibTriggerOn(gBcs2RefNum); + } + handled = true; +#endif + } - /* form = FrmGetActiveForm(); */ /* // Pressing the ENTER (line feed) key will either advance focus */ /* // to the next quantity field marked to be stopped upon, or else */ /* // will validate and store the current data as a new scan record. */ @@ -486,10 +571,12 @@ static Boolean MainFormHandleEvent(EventPtr event) switch (event->data.ctlSelect.controlID) { case MainFormCases: + /* EnableScanner(false); */ ShowQuantityForm(MainFormCases, true); handled = true; break; case MainFormUnits: + /* EnableScanner(false); */ ShowQuantityForm(MainFormUnits, true); handled = true; break; @@ -518,8 +605,8 @@ static void MainFormInit(FormPtr form) TablePtr table; Int16 cols, col, rows, row; - table = GetObjectPtr(MainFormScanRecords); - //TblHasScrollBar(table, true); + table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords)); + TblHasScrollBar(table, true); cols = TblGetNumberOfColumns(table); rows = TblGetNumberOfRows(table); @@ -536,14 +623,72 @@ static void MainFormInit(FormPtr form) TblRedrawTable(table); // Set initial quantity values. - CtlSetLabel(GetObjectPtr(MainFormCases), "0"); - CtlSetLabel(GetObjectPtr(MainFormUnits), "1"); + CtlSetLabel(FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormCases)), "0"); + CtlSetLabel(FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormUnits)), "1"); // Only stop on unit quantity by default. - CtlSetValue(GetObjectPtr(MainFormStopOnUnits), 1); + CtlSetValue(FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormStopOnUnits)), 1); - // Set focus to the barcode field. - FrmSetFocus(form, FrmGetObjectIndex(form, MainFormBarcode)); + FocusBarcode(); +} + + +/************************************************************ + * + * FUNCTION: MainFormDoCommand + * + ************************************************************/ + +static Boolean MainFormDoCommand(UInt16 command) +{ + Boolean handled = false; + FormPtr form; + + switch (command) { + case MainOptionsAboutStarterApp: + form = FrmInitForm(AboutForm); + FrmDoDialog(form); + FrmDeleteForm(form); + handled = true; + break; + + } + + return handled; +} + + +/************************************************************ + * + * FUNCTION: DrawCustomTableItem + * + ************************************************************/ + +static void DrawCustomTableItem(void* table, Int16 row, Int16 col, RectangleType* bounds) +{ + Char * text; + UInt16 size; + Coord x; + + text = (Char *) TblGetItemPtr(table, row, col); + if (*text != 0) { + size = StrLen(text); + + if (col == 0) { // barcode + WinDrawChars(text, size, bounds->topLeft.x, bounds->topLeft.y); + + } else { // cases or units + + // Only draw value if nonzero. + if (StrCompareAscii(text, "0") != 0) { + + // Draw value right-aligned. + x = bounds->topLeft.x + bounds->extent.x - FntCharsWidth(text, size); + WinDrawChars(text, size, x, bounds->topLeft.y); + } + } + + } } @@ -560,6 +705,7 @@ static void MainFormLoadTable(TablePtr table, Boolean updateScroll) MemHandle recordH; RatScanDbRecord* recordP; UInt16 rows, row, rowId; + FormPtr form; ScrollBarPtr scroll; db = OpenScanDatabase(); @@ -576,14 +722,16 @@ static void MainFormLoadTable(TablePtr table, Boolean updateScroll) recordH = DmGetRecord(db, rowId); recordP = (MemPtr) MemHandleLock(recordH); TblSetItemPtr(table, row, 0, &recordP->barcode); - if (StrCompareAscii(recordP->cases, "0") != 0) + if (StrCompareAscii(recordP->cases, "0") != 0) { TblSetItemPtr(table, row, 1, &recordP->cases); - else + } else { TblSetItemPtr(table, row, 1, ""); - if (StrCompareAscii(recordP->units, "0") != 0) + } + if (StrCompareAscii(recordP->units, "0") != 0) { TblSetItemPtr(table, row, 2, &recordP->units); - else + } else { TblSetItemPtr(table, row, 2, ""); + } MemHandleUnlock(recordH); DmReleaseRecord(db, rowId, false); TblSetRowID(table, row, rowId); @@ -594,15 +742,379 @@ static void MainFormLoadTable(TablePtr table, Boolean updateScroll) TblMarkTableInvalid(table); if (updateScroll) { - scroll = GetObjectPtr(MainFormScroller); - if (numRecords <= 12) + form = FrmGetFormPtr(MainForm); + scroll = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScroller)); + if (numRecords <= 12) { SclSetScrollBar(scroll, 0, 0, 0, 0); - else + } else { SclSetScrollBar(scroll, gTopVisibleRecord, 0, numRecords - 12, 11); + } } } +/************************************************************ + * + * FUNCTION: OpenScanDatabase + * + ************************************************************/ + +static DmOpenRef OpenScanDatabase() +{ + LocalID dbID; + DmOpenRef db; + + dbID = DmFindDatabase(cardNumber, dbScanRecords); + if (dbID == 0) { + return CreateScanDatabase(); + } + + db = DmOpenDatabase(cardNumber, dbID, dmModeWrite); + if (db == 0) { + return NULL; + } + + return db; +} + + +#ifdef SCANLIB_BCS2 +/************************************************************ + * + * FUNCTION: CloseBCS2Scanner + * + ************************************************************/ + +static void CloseBCS2Scanner() +{ + LocalID dbId; + + if (! gBcs2RefNum) { + return; + } + + dbId = DmFindDatabase(cardNumber, appDbRattail); + if (dbId) { + SysNotifyUnregister(cardNumber, dbId, BCS2BarCodeReadyNotification, + sysNotifyNormalPriority); + } + BCS2LibClose(gBcs2RefNum); + SysLibRemove(gBcs2RefNum); + gBcs2RefNum = 0; +} +#endif + + +/************************************************************ + * + * FUNCTION: CreateScanDatabase + * + ************************************************************/ + +static DmOpenRef CreateScanDatabase() +{ + Err err; + LocalID dbID; + UInt16 attrs, version; + DmOpenRef db; + + err = DmCreateDatabase(cardNumber, dbScanRecords, appFileCreator, 'SCAN', false); + if (err != errNone) + return NULL; + + dbID = DmFindDatabase(cardNumber, dbScanRecords); + if (dbID == 0) + return NULL; + + // Set backup flag, version for database. + attrs = dmHdrAttrBackup; + version = 1; + DmSetDatabaseInfo(cardNumber, dbID, NULL, &attrs, &version, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + db = DmOpenDatabase(cardNumber, dbID, dmModeWrite); + if (db == 0) + return NULL; + + return db; +} + + +/************************************************************ + * + * FUNCTION: IsChecked + * + ************************************************************/ + +static Boolean IsChecked(FormPtr form, UInt16 controlID) +{ + return (Boolean) CtlGetValue(FrmGetObjectPtr(form, FrmGetObjectIndex(form, controlID))); +} + + +/************************************************************ + * + * FUNCTION: GetObjectPtr + * + ************************************************************/ + +/* static void *GetObjectPtr(UInt16 objectID) */ +/* { */ +/* FormPtr form; */ + +/* form = FrmGetActiveForm(); */ +/* return FrmGetObjectPtr(form, FrmGetObjectIndex(form, objectID)); */ +/* } */ + + +/************************************************************ + * + * FUNCTION: FocusBarcode + * + ************************************************************/ + +static void FocusBarcode() +{ + FormPtr form; + UInt16 index; + FieldPtr field; + + form = FrmGetFormPtr(MainForm); + index = FrmGetObjectIndex(form, MainFormBarcode); + field = FrmGetObjectPtr(form, index); + FldSetSelection(field, 0, FldGetTextLength(field)); + FrmSetFocus(form, index); + + EnableScanner(true); +} + + +/************************************************************ + * + * FUNCTION: EnableScanner + * + ************************************************************/ + +static void EnableScanner(Boolean enable) +{ +#ifdef SCANLIB_SYMBOL + if (bHaveScanner) { + if (enable) { + if (ScanGetScanEnabled() == 0) { + if (ScanCmdScanEnable() != STATUS_OK) { + FrmAlert(AlertScannerEnableError); + } + } + } else { + if (ScanGetScanEnabled() != 0) { + ScanCmdScanDisable(); + } + } + } +#endif +} + + +#ifdef SCANLIB_SYMBOL +/************************************************************ + * + * FUNCTION: FetchScanData + * + ************************************************************/ + +static void FetchScanData() +{ + FormPtr form; + FieldPtr field; + +#ifdef SCANLIB_SYMBOLY + /* MESSAGE msg; */ + /* MemPtr pBarcode; */ +#endif + + form = FrmGetFormPtr(MainForm); + field = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormBarcode)); + +#ifdef SCANLIB_SYMBOLY + /* ScanGetDecodedData(&msg); */ + + /* scanDataLength = decodeDataMsg.length; */ + /* hScanData = MemHandleNew(scanDataLength + 1); */ + /* pScanData = (UInt8*) MemHandleLock(hScanData); */ + /* MemMove(&pScanData[0], &decodeDataMsg.data[0], scanDataLength + 1); */ + + /* FldSetTextHandle(field, hScanData); */ + /* FldDrawField(field); */ + + /* MemHandleUnlock(hScanData); */ + /* MemHandleFree(hScanData); */ + + /* hBarcode = FldGetTextHandle(field); */ + /* if (! hBarcode) { */ + /* hBarcode = MemHandleNew(15); */ + /* } else { */ + /* FldSetTextHandle(field, NULL); */ + /* } */ + + /* pBarcode = (UInt8*) MemHandleLock(hBarcode); */ + /* MemMove(&pBarcode[0], &msg.data[0], msg.length + 1); */ + /* MemHandleUnlock(hBarcode); */ + + /* FldSetTextHandle(field, hBarcode); */ + /* FldDrawField(field); */ + + /* FldSetTextHandle(field, NULL); */ + /* StrCopy(gBarcodeText, msg.data); */ + /* FldSetTextHandle(field, gBarcodeText); */ + /* FldDrawField(field); */ + + /* FldSetTextHandle(field, NULL); */ + /* pBarcode = MemHandleLock(hBarcode); */ + /* MemMove(pBarcode, msg.data, msg.length + 1); */ + /* MemHandleUnlock(hBarcode); */ + /* FldSetTextHandle(field, hBarcode); */ + /* FldDrawField(field); */ +#endif +} +#endif + + +/************************************************************ + * + * FUNCTION: ProcessScan + * + ************************************************************/ + +static void ProcessScan() +{ + FormPtr form; + + form = FrmGetFormPtr(MainForm); + + if (IsChecked(form, MainFormStopOnCases)) { + ShowQuantityForm(MainFormCases, false); + + } else if (IsChecked(form, MainFormStopOnUnits)) { + ShowQuantityForm(MainFormUnits, false); + + } else { + StoreScanData(); + } +} + + +/************************************************************ + * + * FUNCTION: StoreScanData + * + ************************************************************/ + +static void StoreScanData() +{ + FormPtr form; + FieldPtr barcode; + ControlPtr cases, units; + /* Char* quantity; */ + UInt32 size, offset; + DmOpenRef db; + UInt16 recordIndex; + MemHandle recordH; + MemPtr recordP; + TablePtr table; + UInt16 row; + + // Get form and barcode field pointers. + form = FrmGetFormPtr(MainForm); + barcode = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormBarcode)); + + // Set focus to barcode field if empty. + if (FldGetTextLength(barcode) == 0) { + FocusBarcode(); + return; + } + + // Get case and unit field pointers. + cases = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormCases)); + units = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormUnits)); + + // Open the database and create a new record. + db = OpenScanDatabase(); + if (db == NULL) { + return; + } + recordIndex = dmMaxRecordIndex; + recordH = DmNewRecord(db, &recordIndex, sizeof(RatScanDbRecord)); + if (recordH == 0) { + DmCloseDatabase(db); + return; + } + + // Write scan data to the record... + recordP = MemHandleLock(recordH); + offset = 0; + + size = FldGetTextLength(barcode); + DmWrite(recordP, offset, FldGetTextPtr(barcode), size); + offset += size; + + size = 15 - size; + DmSet(recordP, offset, size, 0); + offset += size; + + /* + size = FldGetTextLength(cases); + DmWrite(recordP, offset, FldGetTextPtr(cases), size); + offset += size; + */ + + /* size = 2; */ + /* DmWrite(recordP, offset, "0", 2); */ + /* offset += size; */ + + /* quantity = CtlGetLabel(cases); */ + /* size = StrLen(quantity); */ + /* DmWrite(recordP, offset, quantity, size); */ + /* offset += size; */ + + size = StrLen(CtlGetLabel(cases)); + DmWrite(recordP, offset, CtlGetLabel(cases), size); + offset += size; + + size = 4 - size; + DmSet(recordP, offset, size, 0); + offset += size; + + /* size = FldGetTextLength(units); */ + /* DmWrite(recordP, offset, FldGetTextPtr(units), size); */ + /* offset += size; */ + + size = StrLen(CtlGetLabel(units)); + DmWrite(recordP, offset, CtlGetLabel(units), size); + offset += size; + + size = 4 - size; + DmSet(recordP, offset, size, 0); + + // ...then close the record and database. + MemHandleUnlock(recordH); + DmReleaseRecord(db, recordIndex, false); + DmCloseDatabase(db); + + // Redraw the table. + table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords)); + row = TblGetLastUsableRow(table); + if (row == tblUnusableRow) { + gTopVisibleRecord = 0; + } else if (row == 11) { + gTopVisibleRecord = TblGetRowID(table, 1); + } + MainFormLoadTable(table, true); + TblRedrawTable(table); + + FocusBarcode(); +} + + #ifdef SCANLIB_JANAM /************************************************************ * @@ -653,60 +1165,52 @@ static Boolean OpenBCS2Scanner() err = SysLibLoad('libr', 'BcAp', &gBcs2RefNum); if (err) { - /* - switch (err) { - case sysErrLibNotFound: - SysFatalAlert("BCS2 library not found!"); - break; - case sysErrNoFreeRAM: - SysFatalAlert("Out of memory (RAM)!"); - break; - case sysErrNoFreeLibSlots: - SysFatalAlert("No free library slots!"); - break; - default: - SysFatalAlert("BCS2 library load failed!"); - break; - } - */ + switch (err) { + case sysErrLibNotFound: + SysFatalAlert("BCS2 library not found!"); + break; + case sysErrNoFreeRAM: + SysFatalAlert("Out of memory (RAM)!"); + break; + case sysErrNoFreeLibSlots: + SysFatalAlert("No free library slots!"); + break; + default: + SysFatalAlert("BCS2 library load failed!"); + break; + } gBcs2RefNum = 0; return false; } err = BCS2LibOpen(gBcs2RefNum); if (err) { - /* - switch (err) { - case errBCS2MemoryError: - SysFatalAlert("BCS2 threw memory error"); - break; - default: - SysFatalAlert("BCS2 library open failed!"); - break; - } - */ + switch (err) { + case errBCS2MemoryError: + SysFatalAlert("BCS2 threw memory error"); + break; + default: + SysFatalAlert("BCS2 library open failed!"); + break; + } SysLibRemove(gBcs2RefNum); gBcs2RefNum = 0; return false; } - dbId = DmFindDatabase(cardNo, appDbRattail); + dbId = DmFindDatabase(cardNumber, appDbRattail); if (! dbId) { - /* - SysFatalAlert("Find database failed!"); - */ + SysFatalAlert("Find database failed!"); SysLibRemove(gBcs2RefNum); gBcs2RefNum = 0; return false; } - err = SysNotifyRegister(cardNo, dbId, + err = SysNotifyRegister(cardNumber, dbId, BCS2BarCodeReadyNotification, NULL, sysNotifyNormalPriority, NULL); if (err != errNone) { - /* - SysFatalAlert("Notify registration failed!"); - */ + SysFatalAlert("Notify registration failed!"); SysLibRemove(gBcs2RefNum); gBcs2RefNum = 0; return false; @@ -719,27 +1223,36 @@ static Boolean OpenBCS2Scanner() /************************************************************ * - * FUNCTION: OpenScanDatabase + * FUNCTION: ShowQuantityForm * ************************************************************/ - -static DmOpenRef OpenScanDatabase() + +static void ShowQuantityForm(UInt16 triggerID, Boolean manual) { - LocalID dbId; - DmOpenRef db; - - dbId = DmFindDatabase(cardNo, appDbRattailScan); - if (dbId == 0) - return CreateScanDatabase(); - - db = DmOpenDatabase(cardNo, dbId, dmModeWrite); - if (db == 0) - return NULL; - - return db; + FormPtr form; + ControlPtr ctrl; + + // Track whether the form is being shown due to manaul user initiation, + // versus automatically as part of the scanning workflow. + gQuantityManual = manual; + + // Store quantity trigger so we know which quantity we're editing. + gQuantityTriggerID = triggerID; + + // Update the current quantity buffer from the trigger label. + gQuantity = (triggerID == MainFormCases) ? gCaseQuantity : gUnitQuantity; + form = FrmGetFormPtr(MainForm); + ctrl = FrmGetObjectPtr(form, FrmGetObjectIndex(form, triggerID)); + StrCopy(gQuantity, CtlGetLabel(ctrl)); + + // The quantity has yet to be touched by the user. + gQuantityTouched = false; + + // Load and display the quantity form. + FrmPopupForm(QuantityForm); } - - + + /************************************************************ * * FUNCTION: QuantityFormHandleEvent @@ -798,6 +1311,7 @@ static Boolean QuantityFormHandleEvent(EventPtr event) break; case QuantityFormCancel: FrmReturnToForm(MainForm); + FocusBarcode(); break; case QuantityFormOK: QuantityFormAccept(); @@ -869,44 +1383,6 @@ static Boolean QuantityFormHandleEvent(EventPtr event) } -/************************************************************ - * - * FUNCTION: QuantityFormAccept - * - ************************************************************/ - -static void QuantityFormAccept() -{ - FormPtr form; - ControlPtr control; - - form = FrmGetFormPtr(MainForm); - - // Close the quantity form and return to the main form. - FrmReturnToForm(MainForm); - - // Update the selector trigger label with the new quantity. - control = FrmGetObjectPtr(form, FrmGetObjectIndex(form, gQuantityTriggerID)); - CtlSetLabel(control, gQuantity); - - // If the form was shown automatically as part of the scanning workflow, - // then we'll keep that workflow going... - if (! gQuantityManual) { - - if (gQuantityTriggerID == MainFormCases) { - if (IsChecked(form, MainFormStopOnUnits)) { - ShowQuantityForm(MainFormUnits, false); - } else { - StoreScanData(); - } - - } else if (gQuantityTriggerID == MainFormUnits) { - StoreScanData(); - } - } -} - - /************************************************************ * * FUNCTION: QuantityFormInit @@ -923,63 +1399,10 @@ static void QuantityFormInit(FormPtr form) } // Display current quantity value. - FldSetTextPtr(GetObjectPtr(QuantityFormQuantity), gQuantity); + FldSetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, QuantityFormQuantity)), gQuantity); } -/************************************************************ - * - * FUNCTION: SetFieldFocus - * - ************************************************************/ - -static void SetFieldFocus(UInt16 objId) -{ - FormPtr form; - FieldPtr field; - UInt16 index; - - form = FrmGetActiveForm(); - index = FrmGetObjectIndex(form, objId); - field = FrmGetObjectPtr(form, index); - FldSetSelection(field, 0, FldGetTextLength(field)); - FrmSetFocus(form, index); -} - - -/************************************************************ - * - * FUNCTION: ShowQuantityForm - * - ************************************************************/ - -static void ShowQuantityForm(UInt16 triggerID, Boolean manual) -{ - FormPtr form; - ControlPtr ctrl; - - form = FrmGetFormPtr(MainForm); - ctrl = FrmGetObjectPtr(form, FrmGetObjectIndex(form, triggerID)); - - // Track whether the form is being shown due to manaul user initiation, - // versus automatically as part of the scanning workflow. - gQuantityManual = manual; - - // Store quantity trigger so we know which quantity we're editing. - gQuantityTriggerID = triggerID; - - // Update the current quantity buffer from the trigger label. - gQuantity = (triggerID == MainFormCases) ? gCaseQuantity : gUnitQuantity; - StrCopy(gQuantity, CtlGetLabel(ctrl)); - - // The quantity has yet to be touched by the user. - gQuantityTouched = false; - - // Load and display the quantity form. - FrmPopupForm(QuantityForm); -} - - /************************************************************ * * FUNCTION: AppendQuantityDigit @@ -1037,171 +1460,42 @@ static void UpdateQuantityDisplay() /************************************************************ * - * FUNCTION: StoreScanData + * FUNCTION: QuantityFormAccept * ************************************************************/ - -static Boolean StoreScanData() + +static void QuantityFormAccept() { FormPtr form; - FieldPtr barcode; - ControlPtr cases, units; - /* Char* quantity; */ - UInt32 size, offset; - DmOpenRef db; - UInt16 recordIndex; - MemHandle recordH; - MemPtr recordP; - TablePtr table; - UInt16 row; - - // Get form and barcode field pointers. + ControlPtr control; + form = FrmGetFormPtr(MainForm); - barcode = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormBarcode)); - // Set focus to barcode field if empty. - if (! FldGetTextLength(barcode)) { - FrmSetFocus(form, FrmGetObjectIndex(form, MainFormBarcode)); - return false; - } - - // Get case and unit field pointers. - cases = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormCases)); - units = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormUnits)); - - // Open the database and create a new record. - db = OpenScanDatabase(); - if (db == NULL) - return false; - recordIndex = dmMaxRecordIndex; - recordH = DmNewRecord(db, &recordIndex, sizeof(RatScanDbRecord)); - if (recordH == 0) { - DmCloseDatabase(db); - return false; - } - - // Write scan data to the record... - recordP = MemHandleLock(recordH); - offset = 0; + // Close the quantity form and return to the main form. + FrmReturnToForm(MainForm); - size = FldGetTextLength(barcode); - DmWrite(recordP, offset, FldGetTextPtr(barcode), size); - offset += size; - - size = 15 - size; - DmSet(recordP, offset, size, 0); - offset += size; + // Update the selector trigger label with the new quantity. + control = FrmGetObjectPtr(form, FrmGetObjectIndex(form, gQuantityTriggerID)); + CtlSetLabel(control, gQuantity); - /* - size = FldGetTextLength(cases); - DmWrite(recordP, offset, FldGetTextPtr(cases), size); - offset += size; - */ + // If the form was shown automatically as part of the scanning workflow, + // then we'll keep that workflow going... + if (! gQuantityManual) { - /* size = 2; */ - /* DmWrite(recordP, offset, "0", 2); */ - /* offset += size; */ - - /* quantity = CtlGetLabel(cases); */ - /* size = StrLen(quantity); */ - /* DmWrite(recordP, offset, quantity, size); */ - /* offset += size; */ + if (gQuantityTriggerID == MainFormCases) { + if (IsChecked(form, MainFormStopOnUnits)) { + ShowQuantityForm(MainFormUnits, false); + } else { + StoreScanData(); + } - size = StrLen(CtlGetLabel(cases)); - DmWrite(recordP, offset, CtlGetLabel(cases), size); - offset += size; - - size = 4 - size; - DmSet(recordP, offset, size, 0); - offset += size; - - /* size = FldGetTextLength(units); */ - /* DmWrite(recordP, offset, FldGetTextPtr(units), size); */ - /* offset += size; */ - - size = StrLen(CtlGetLabel(units)); - DmWrite(recordP, offset, CtlGetLabel(units), size); - offset += size; - - size = 4 - size; - DmSet(recordP, offset, size, 0); - - // ...then close the record and database. - MemHandleUnlock(recordH); - DmReleaseRecord(db, recordIndex, false); - DmCloseDatabase(db); - - // Redraw the table. - table = GetObjectPtr(MainFormScanRecords); - row = TblGetLastUsableRow(table); - if (row == tblUnusableRow) - gTopVisibleRecord = 0; - else if (row == 11) - gTopVisibleRecord = TblGetRowID(table, 1); - MainFormLoadTable(table, true); - TblRedrawTable(table); - - SetFieldFocus(MainFormBarcode); - return true; -} - - -/************************************************************ - * - * FUNCTION: PilotMain - * - ************************************************************/ - -UInt32 PilotMain(UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags) -{ - Err error = errNone; - /* - SysNotifyParamType* notifyParams = (SysNotifyParamType *) cmdPBP; - FieldPtr barcode; - MemHandle barcodeH; - MemPtr barcodeP; - Char * scancode; - */ - - switch (cmd) { - case sysAppLaunchCmdNormalLaunch: - if ((error = AppStart()) == 0) { - AppEventLoop(); - AppStop(); + } else if (gQuantityTriggerID == MainFormUnits) { + StoreScanData(); } - break; - - /* - case sysAppLaunchCmdNotify: - if (notifyParams->notifyType == BCS2BarCodeReadyNotification) { - scancode = (Char *) notifyParams->notifyDetailsP + 1; - if (StrCompareAscii(scancode, "NO READ")) { // (means good read) - barcode = GetObjectPtr(MainFormBarcode); - barcodeH = FldGetTextHandle(barcode); - FldSetTextHandle(barcode, NULL); - if (! barcodeH) - barcodeH = MemHandleNew(15); // ugh - barcodeP = MemHandleLock(barcodeH); - StrCopy((Char *) barcodeP, scancode); - MemHandleUnlock(barcodeH); - FldSetTextHandle(barcode, barcodeH); - FldDrawField(barcode); - if (GetCheckedValue(MainFormStopOnCases)) { - //SetFieldFocus(MainFormCases); - } else if (GetCheckedValue(MainFormStopOnUnits)) { - SetFieldFocus(MainFormUnits); - } else { - if (StoreScanData()) - SndPlaySystemSound(sndInfo); - } - } - } - break; - */ - default: - break; - } - - return error; +#ifdef SCANLIB_SYMBOLY + /* } else { */ + /* EnableScanner(true); */ +#endif + } } diff --git a/src/AppMain.h b/src/AppMain.h index 967f507..22b138d 100644 --- a/src/AppMain.h +++ b/src/AppMain.h @@ -1,10 +1,10 @@ -typedef struct { - Char barcode[15]; - Char cases[4]; - Char units[4]; -} RatScanDbRecord; +/* typedef struct { */ +/* Char barcode[15]; */ +/* Char cases[4]; */ +/* Char units[4]; */ +/* } RatScanDbRecord; */ #ifdef SCANLIB_BCS2 @@ -14,25 +14,28 @@ static void CloseBCS2Scanner(); #ifdef SCANLIB_JANAM //static Boolean OpenBabboScanner(); -//static void CloseBabboScanner(); #endif -static DmOpenRef CreateScanDatabase(); -static void DrawCustomTableItem(void* table, Int16 row, Int16 col, RectangleType* bounds); +/* static DmOpenRef CreateScanDatabase(); */ +/* static void DrawCustomTableItem(void* table, Int16 row, Int16 col, RectangleType* bounds); */ /* static Int16 GetCheckedValue(UInt16 objId); */ -static Boolean IsChecked(FormPtr form, UInt16 controlID); -static void *GetObjectPtr(UInt16 objId); -static Boolean MainFormHandleEvent(EventPtr event); -static void MainFormInit(FormPtr form); -static void MainFormLoadTable(TablePtr table, Boolean updateScroll); -static DmOpenRef OpenScanDatabase(); -static void SetFieldFocus(UInt16 objId); -static void ShowQuantityForm(UInt16 triggerID, Boolean manual); -static Boolean StoreScanData(); +/* static Boolean IsChecked(FormPtr form, UInt16 controlID); */ +/* static void *GetObjectPtr(UInt16 objId); */ +/* static Boolean MainFormHandleEvent(EventPtr event); */ +/* static void MainFormInit(FormPtr form); */ +/* static void MainFormLoadTable(TablePtr table, Boolean updateScroll); */ +/* static DmOpenRef OpenScanDatabase(); */ +/* static void ShowQuantityForm(UInt16 triggerID, Boolean manual); */ +/* static Boolean StoreScanData(); */ + +/* static void EnableScanner(Boolean enable); */ +/* static void FocusBarcode(); */ +//static void FetchScanData(); +/* static void ProcessScan(); */ // Quantity Form -static Boolean QuantityFormHandleEvent(EventPtr event); -static void QuantityFormInit(FormPtr form); -static void AppendQuantityDigit(UInt16 digit); -static void UpdateQuantityDisplay(); -static void QuantityFormAccept(); +/* static Boolean QuantityFormHandleEvent(EventPtr event); */ +/* static void QuantityFormInit(FormPtr form); */ +/* static void AppendQuantityDigit(UInt16 digit); */ +/* static void UpdateQuantityDisplay(); */ +/* static void QuantityFormAccept(); */ diff --git a/src/AppResources.h b/src/AppResources.h index 8c70a65..f451fcd 100644 --- a/src/AppResources.h +++ b/src/AppResources.h @@ -16,6 +16,9 @@ #define MainFormUnits 1001 #define AlertTest 1000 +#define AlertScannerOpenError 1300 +#define AlertScannerEnableError 1200 +#define AlertScannerCloseError 1100 // Resource: tFRM 1100 #define AboutForm 1100 //(Left Origin = 2, Top Origin = 2, Width = 156, Height = 156, Usable = 1, Modal = 1, Save Behind = 1, Help ID = 0, Menu Bar ID = 0, Default Button ID = 0)