/* -*- coding: utf-8 -*- */
/*****************************************************************************
*
* Rattail -- Retail Software Framework
* Copyright © 2010-2013 Lance Edgar
*
* 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
#ifdef SCANLIB_JANAM
#include "JanamScnCompatible.h"
#include "ScanMgr.h"
#endif
#ifdef SCANLIB_SYMBOL
#include "Symbol/ScanMgrDef.h"
#include "Symbol/ScanMgrStruct.h"
#include "Symbol/ScanMgr.h"
#endif
/************************************************************
* Rattail Includes
************************************************************/
#include "AppResources.h"
/* #include "AppMain.h" */
/************************************************************
* Palm App Constants
************************************************************/
#define appFileCreator 'RTTL'
#define appVersionNum 0x01
#define appPrefID 0x00
#define appPrefVersionNum 0x01
/************************************************************
* Rattail App Constants
************************************************************/
#define appDbRattail "Rattail"
#define dbScanRecords "Rattail_Scan"
/* UInt16 cardNo = 0; */
#define cardNumber 0
#define COLUMN_BARCODE 0
#define COLUMN_CASES 1
#define COLUMN_UNITS 2
/************************************************************
* Scanner App Variables
************************************************************/
#ifdef SCANLIB_BCS2
UInt16 gBcs2RefNum = 0;
#endif
#ifdef SCANLIB_JANAM
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;
Boolean scanRecordsSelected[12];
Int16 eventEnteredRow = -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.
UInt16 gQuantityTriggerID;
// This tracks whether the quantity form was initiated by the user manually
// selecting a trigger, i.e. versus being auto-initiated by the scanning
// workflow.
Boolean gQuantityManual;
// These buffers hold the current quantities as text.
Char gCaseQuantity[4];
Char gUnitQuantity[4];
// This pointer refers to the quantity currently being edited.
Char* gQuantity;
// This tracks whether the quantity shown is the initial/original value, or
// whether it has already been touched in some way. This is used to control
// the behavior of the first number button pressed.
Boolean gQuantityTouched;
/************************************************************
* Type Definitions
************************************************************/
/* typedef struct { */
/* Char barcode[15]; */
/* Char cases[4]; */
/* Char units[4]; */
/* } RatScanDbRecord; */
typedef struct {
Char barcode[15];
UInt16 cases;
UInt16 units;
} RattailScanRecord;
/************************************************************
* 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 BarcodeSelectItem(Int16 row); */
static Boolean TableHitTest(TablePtr table, Int16 screenX, Int16 screenY);
static Int16 TableRowHitTest(TablePtr table, Int16 screenX, Int16 screenY);
static void ClearScanRecordSelection();
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 {
FrmCustomAlert(AlertScannerError, "There was an error opening the scanner.", "", "");
}
#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) {
FrmCustomAlert(AlertScannerError, "There was an error closing the scanner.", "", "");
}
}
#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
*
************************************************************/
static Boolean AppHandleEvent(EventPtr event)
{
UInt16 formID;
FormPtr form;
Boolean handled = false;
if (event->eType == frmLoadEvent) {
// Load the form resource.
formID = event->data.frmLoad.formID;
form = FrmInitForm(formID);
FrmSetActiveForm(form);
// Set the event handler for the form.
switch (formID) {
case MainForm:
FrmSetEventHandler(form, MainFormHandleEvent);
break;
case QuantityForm:
FrmSetEventHandler(form, QuantityFormHandleEvent);
break;
default:
break;
}
handled = true;
}
return handled;
}
/************************************************************
*
* FUNCTION: MainFormHandleEvent
*
************************************************************/
static Boolean MainFormHandleEvent(EventPtr event)
{
Boolean handled = false;
FormPtr form;
TablePtr table;
//UInt16 objId;
/* Int16 row, r; */
Int16 row;
form = FrmGetFormPtr(MainForm);
switch (event->eType) {
case frmOpenEvent:
MainFormInit(form);
FrmDrawForm(form);
handled = true;
break;
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 penDownEvent:
eventEnteredRow = -1;
table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords));
if (TableHitTest(table, event->screenX, event->screenY)) {
ClearScanRecordSelection();
row = TableRowHitTest(table, event->screenX, event->screenY);
if (row >= 0) {
eventEnteredRow = row;
}
handled = true;
}
break;
case penMoveEvent:
table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords));
if (TableHitTest(table, event->screenX, event->screenY)) {
row = TableRowHitTest(table, event->screenX, event->screenY);
if (row >= 0) {
if (! scanRecordsSelected[row]) {
scanRecordsSelected[row] = true;
TblMarkRowInvalid(table, row);
TblRedrawTable(table);
}
}
handled = true;
}
break;
case penUpEvent:
if (eventEnteredRow >= 0) {
table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords));
if (TableHitTest(table, event->screenX, event->screenY)) {
row = TableRowHitTest(table, event->screenX, event->screenY);
if (row >= 0) {
if (! scanRecordsSelected[row]) {
scanRecordsSelected[row] = true;
TblMarkRowInvalid(table, row);
TblRedrawTable(table);
}
}
handled = true;
}
}
break;
case sclEnterEvent:
ClearScanRecordSelection();
break;
case sclRepeatEvent:
if (event->data.sclRepeat.newValue > event->data.sclRepeat.value)
gTopVisibleRecord += (event->data.sclRepeat.newValue - event->data.sclRepeat.value);
else {
gTopVisibleRecord -= (event->data.sclRepeat.value - event->data.sclRepeat.newValue);
}
table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords));
MainFormLoadTable(table, false);
TblRedrawTable(table);
break;
case keyDownEvent:
if (event->data.keyDown.chr == chrLineFeed) {
// We only have one field (barcode) on the main form. If it's
// active then we can operate on the ENTER key.
if (FrmGetActiveField(form)) {
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
}
/* // 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. */
/* if (event->data.keyDown.chr == chrLineFeed) { */
/* objId = GetActiveFieldId(form); */
/* if (objId == MainFormBarcode) { */
/* ProcessScan(); */
/* /\* */
/* } else if (objId == MainFormCases) { */
/* if (GetCheckedValue(MainFormStopOnUnits)) { */
/* SetFieldFocus(MainFormUnits); */
/* } else { */
/* StoreScanData(); */
/* } */
/* *\/ */
/* } else if (objId == MainFormUnits) { */
/* StoreScanData(); */
/* } */
/* // Until I have more to go on, I'm assuming that if a handheld has */
/* // a BCS2-capable scanner, then it is an Aceeca Meazura, and that its */
/* // "center" navigational (hardware) key should be used as the scan */
/* // trigger. */
/* } else if (event->data.keyDown.chr == MzVCentreKey) { */
/* if (gBcs2RefNum) { */
/* if (FrmGetFocus(form) == FrmGetObjectIndex(form, MainFormBarcode)) */
/* BCS2LibTriggerOn(gBcs2RefNum); */
/* } */
/* /\* */
/* // Handle Babbo key */
/* } else if ((event->data.keyDown.chr & vchrScanKey && */
/* (event->data.keyDown.keyCode == scanBitHard1)) || */
/* ((event->data.keyDown.chr & vchrScanKey) && */
/* (event->data.keyDown.keyCode == scanBitHard2))) { */
/* if (gBabbo) { */
/* if (ScnCmdStartDecode() != STATUS_OK) { */
/* SysFatalAlert("Start decode failed!"); */
/* } */
/* handled = true; */
/* } */
/* *\/ */
/* } */
break;
/*
// This event is passed by Babbo scanners (Janam handhelds).
case scanDecodeEvent:
if (gBabbo) { // just to be sure?
if (ScnCmdBeep(One_Short_Low) != STATUS_OK) {
SysFatalAlert("Beep failed!");
}
}
break;
*/
case ctlSelectEvent:
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;
default:
break;
}
break;
default:
break;
}
return handled;
}
/************************************************************
*
* FUNCTION: MainFormInit
*
************************************************************/
static void MainFormInit(FormPtr form)
{
TablePtr table;
Int16 cols, col, rows, row;
// Clear row selection state.
for (row = 0; row < 12; row++) {
scanRecordsSelected[row] = false;
}
table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords));
TblHasScrollBar(table, true);
cols = TblGetNumberOfColumns(table);
rows = TblGetNumberOfRows(table);
for (col = 0; col < cols; col++) {
TblSetCustomDrawProcedure(table, col, DrawCustomTableItem);
TblSetColumnUsable(table, col, true);
for (row = 0; row < rows; row++) {
TblSetItemStyle(table, row, col, customTableItem);
TblSetRowUsable(table, row, false);
}
}
MainFormLoadTable(table, true);
TblRedrawTable(table);
// Set initial quantity values.
CtlSetLabel(FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormCases)), "0");
CtlSetLabel(FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormUnits)), "1");
// Only stop on unit quantity by default.
CtlSetValue(FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormStopOnUnits)), 1);
FocusBarcode();
}
/************************************************************
*
* FUNCTION: MainFormDoCommand
*
************************************************************/
static Boolean MainFormDoCommand(UInt16 command)
{
Boolean handled;
FormPtr form;
TablePtr table;
DmOpenRef db;
UInt16 row, offset, records;
Int16 rowIndex;
Boolean selected;
handled = false;
switch (command) {
case MainEditDeleteSelected:
selected = false;
for (row = 0; row < 12; row++) {
if (scanRecordsSelected[row]) {
selected = true;
break;
}
}
if (selected) {
if (FrmCustomAlert(AlertConfirmation, "Do you really wish to delete the selected record(s)?", "", "") == 0) {
form = FrmGetFormPtr(MainForm);
table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords));
db = OpenScanDatabase();
offset = 0;
for (row = 0; row < 12; row++) {
if (scanRecordsSelected[row]) {
rowIndex = TblGetRowID(table, row);
DmRemoveRecord(db, rowIndex - offset++);
}
}
DmCloseDatabase(db);
MainFormLoadTable(table, true);
ClearScanRecordSelection();
TblRedrawTable(table);
}
break;
} else {
FrmCustomAlert(AlertInformation, "You don't have any records selected for deletion.", "", "");
}
handled = true;
break;
case MainEditDeleteAll:
db = OpenScanDatabase();
records = DmNumRecords(db);
DmCloseDatabase(db);
if (records) {
if (FrmCustomAlert(AlertConfirmation,
"This will delete ALL records in the batch.\n\nDo you really wish to do this?", "", "") == 0) {
db = OpenScanDatabase();
for (rowIndex = records - 1; rowIndex >= 0; rowIndex--) {
DmRemoveRecord(db, rowIndex);
}
DmCloseDatabase(db);
form = FrmGetFormPtr(MainForm);
table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords));
MainFormLoadTable(table, true);
ClearScanRecordSelection();
TblRedrawTable(table);
}
} else {
FrmCustomAlert(AlertInformation, "The batch is already empty.", "", "");
}
handled = true;
break;
case MainOptionsAboutRattail:
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* barcode;
Char quantity[4];
Coord x;
if (col == 0) {
barcode = (Char*) TblGetItemPtr(table, row, col);
WinDrawChars(barcode, StrLen(barcode), bounds->topLeft.x, bounds->topLeft.y);
} else {
StrIToA(quantity, TblGetItemInt(table, row, col));
x = bounds->topLeft.x + bounds->extent.x - FntCharsWidth(quantity, StrLen(quantity));
WinDrawChars(quantity, StrLen(quantity), x, bounds->topLeft.y);
}
if (scanRecordsSelected[row]) {
WinInvertRectangle(bounds, 0);
}
}
/* 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); *\/ */
/* /\* } *\/ */
/* /\* if (StrCompareAscii(text, "0") == 0) { *\/ */
/* /\* WinDrawChars(" ", 1, bounds->topLeft.x, bounds->topLeft.y); *\/ */
/* /\* } else { *\/ */
/* x = bounds->topLeft.x + bounds->extent.x - FntCharsWidth(text, size); */
/* WinDrawChars(text, size, x, bounds->topLeft.y); */
/* /\* } *\/ */
/* } */
/* // Invert item if row is selected. */
/* if (scanRecordsSelected[row]) { */
/* WinInvertRectangle(bounds, 0); */
/* } */
/* } */
/* } */
/************************************************************
*
* FUNCTION: ClearScanRecordSelection
*
************************************************************/
static void ClearScanRecordSelection()
{
FormPtr form;
TablePtr table;
Int16 row;
Boolean dirty = false;
form = FrmGetFormPtr(MainForm);
table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords));
for (row = 0; row < 12; row++) {
if (scanRecordsSelected[row]) {
scanRecordsSelected[row] = false;
TblMarkRowInvalid(table, row);
dirty = true;
}
}
if (dirty) {
TblRedrawTable(table);
}
}
/************************************************************
*
* FUNCTION: TableHitTest
*
************************************************************/
static Boolean TableHitTest(TablePtr table, Int16 screenX, Int16 screenY)
{
RectangleType bounds;
TblGetBounds(table, &bounds);
if ((screenX >= bounds.topLeft.x) && (screenX <= (bounds.topLeft.x + bounds.extent.x))
&& (screenY >= bounds.topLeft.y) && (screenY <= (bounds.topLeft.y + bounds.extent.y))) {
return true;
}
return false;
}
/************************************************************
*
* FUNCTION: TableRowHitTest
*
************************************************************/
static Int16 TableRowHitTest(TablePtr table, Int16 screenX, Int16 screenY)
{
RectangleType bounds;
Int16 row, col;
for (row = 0; row < 12; row++) {
if (! TblRowUsable(table, row))
break;
for (col = 0; col < 3; col++) {
TblGetItemBounds(table, row, col, &bounds);
if ((screenX >= bounds.topLeft.x) && (screenX <= (bounds.topLeft.x + bounds.extent.x))
&& (screenY >= bounds.topLeft.y) && (screenY <= (bounds.topLeft.y + bounds.extent.y))) {
return row;
}
}
}
return -1;
}
/************************************************************
*
* FUNCTION: MainFormLoadTable
*
************************************************************/
static void MainFormLoadTable(TablePtr table, Boolean updateScroll)
{
DmOpenRef db;
UInt16 records, recordIndex;
Int16 rows, row;
RattailScanRecord* recordP;
MemHandle recordH;
FormPtr form;
ScrollBarPtr scroll;
db = OpenScanDatabase();
records = DmNumRecords(db);
if (gTopVisibleRecord < 0) {
gTopVisibleRecord = (records > 12) ? (records - 12) : 0;
}
rows = (records < 12) ? records : 12;
for (row = 0; row < rows; row++) {
recordIndex = gTopVisibleRecord + row;
recordH = DmGetRecord(db, recordIndex);
recordP = MemHandleLock(recordH);
TblSetItemPtr(table, row, 0, recordP->barcode);
TblSetItemInt(table, row, 1, recordP->cases);
TblSetItemInt(table, row, 2, recordP->units);
MemHandleUnlock(recordH);
DmReleaseRecord(db, recordIndex, false);
TblSetRowID(table, row, recordIndex);
TblSetRowUsable(table, row, true);
}
DmCloseDatabase(db);
for (row = 11; row >= rows; row--) {
if (TblRowUsable(table, row)) {
TblSetRowUsable(table, row, false);
}
}
TblMarkTableInvalid(table);
if (updateScroll) {
form = FrmGetFormPtr(MainForm);
scroll = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScroller));
if (records <= 12) {
SclSetScrollBar(scroll, 0, 0, 0, 0);
} else {
SclSetScrollBar(scroll, gTopVisibleRecord, 0, records - 12, 11);
}
}
}
/* static void MainFormLoadTable(TablePtr table, Boolean updateScroll) */
/* { */
/* DmOpenRef db; */
/* UInt16 numRecords; */
/* MemHandle recordH; */
/* /\* RatScanDbRecord* recordP; *\/ */
/* RattailScanRecord* recordP; */
/* UInt16 rows, row, rowId; */
/* FormPtr form; */
/* ScrollBarPtr scroll; */
/* db = OpenScanDatabase(); */
/* if (db == NULL) */
/* return; */
/* numRecords = DmNumRecords(db); */
/* rows = (numRecords < 12) ? numRecords : 12; */
/* if (gTopVisibleRecord < 0) */
/* gTopVisibleRecord = (numRecords > 12) ? (numRecords - 12) : 0; */
/* for (row = 0; row < rows; row++) { */
/* rowId = gTopVisibleRecord + row; */
/* recordH = DmGetRecord(db, rowId); */
/* recordP = (MemPtr) MemHandleLock(recordH); */
/* TblSetItemPtr(table, row, 0, &recordP->barcode); */
/* /\* if (StrCompareAscii(recordP->cases, "0") != 0) { *\/ */
/* /\* TblSetItemPtr(table, row, 1, &recordP->cases); *\/ */
/* /\* } else { *\/ */
/* /\* TblSetItemPtr(table, row, 1, ""); *\/ */
/* /\* } *\/ */
/* /\* if (StrCompareAscii(recordP->units, "0") != 0) { *\/ */
/* /\* TblSetItemPtr(table, row, 2, &recordP->units); *\/ */
/* /\* } else { *\/ */
/* /\* TblSetItemPtr(table, row, 2, ""); *\/ */
/* /\* } *\/ */
/* MemHandleUnlock(recordH); */
/* DmReleaseRecord(db, rowId, false); */
/* TblSetRowID(table, row, rowId); */
/* TblSetRowUsable(table, row, true); */
/* } */
/* DmCloseDatabase(db); */
/* TblMarkTableInvalid(table); */
/* if (updateScroll) { */
/* form = FrmGetFormPtr(MainForm); */
/* scroll = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScroller)); */
/* if (numRecords <= 12) { */
/* SclSetScrollBar(scroll, 0, 0, 0, 0); */
/* } else { */
/* SclSetScrollBar(scroll, gTopVisibleRecord, 0, numRecords - 12, 11); */
/* } */
/* } */
/* } */
/************************************************************
*
* FUNCTION: BarcodeSelectItem
*
************************************************************/
/* static void BarcodeSelectItem(Int16 row) */
/* { */
/* FormPtr form; */
/* TablePtr table; */
/* form = FrmGetFormPtr(MainForm); */
/* table = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormScanRecords)); */
/* scanRecordsSelected[row] = true; */
/* TblRedrawTable(table); */
/* } */
/************************************************************
*
* 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) {
FrmCustomAlert(AlertScannerError, "There was an error enabling the scanner.", "", "");
}
}
} 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;
ClearScanRecordSelection();
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;
ControlPtr ctrl;
DmOpenRef db;
RattailScanRecord record;
UInt16 recordIndex;
MemHandle recordH;
MemPtr recordP;
TablePtr table;
UInt16 row;
form = FrmGetFormPtr(MainForm);
ctrl = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormBarcode));
StrCopy(record.barcode, FldGetTextPtr((FieldPtr) ctrl));
ctrl = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormCases));
record.cases = StrAToI(CtlGetLabel(ctrl));
ctrl = FrmGetObjectPtr(form, FrmGetObjectIndex(form, MainFormUnits));
record.units = StrAToI(CtlGetLabel(ctrl));
db = OpenScanDatabase();
recordIndex = dmMaxRecordIndex;
recordH = DmNewRecord(db, &recordIndex, sizeof(RattailScanRecord));
recordP = MemHandleLock(recordH);
DmWrite(recordP, 0, &record, sizeof(RattailScanRecord));
MemHandleUnlock(recordH);
DmReleaseRecord(db, recordIndex, false);
DmCloseDatabase(db);
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();
}
/* static void StoreScanData() */
/* { */
/* FormPtr form; */
/* FieldPtr barcode; */
/* /\* ControlPtr cases, units; *\/ */
/* /\* Char* quantity; *\/ */
/* /\* UInt8 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)); *\/ */
/* recordH = DmNewRecord(db, &recordIndex, sizeof(RattailScanRecord)); */
/* 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
/************************************************************
*
* FUNCTION: OpenBabboScanner
*
************************************************************/
/*
static Boolean OpenBabboScanner()
{
Err err;
if (! ScnIsJanamUnit()) {
SysFatalAlert("Not a Janam unit!");
return false;
}
err = ScnOpenDecoder();
if (err) {
SysFatalAlert("Open decoder failed!");
return false;
}
if (ScnCmdScanEnable() != STATUS_OK) {
SysFatalAlert("Enable scanner failed!");
ScnCloseDecoder();
return false;
}
gBabbo = true;
return true;
}
*/
#endif
#ifdef SCANLIB_BCS2
/************************************************************
*
* FUNCTION: OpenBCS2Scanner
*
************************************************************/
static Boolean OpenBCS2Scanner()
{
Err err;
LocalID dbId;
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;
}
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;
}
SysLibRemove(gBcs2RefNum);
gBcs2RefNum = 0;
return false;
}
dbId = DmFindDatabase(cardNumber, appDbRattail);
if (! dbId) {
SysFatalAlert("Find database failed!");
SysLibRemove(gBcs2RefNum);
gBcs2RefNum = 0;
return false;
}
err = SysNotifyRegister(cardNumber, dbId,
BCS2BarCodeReadyNotification, NULL,
sysNotifyNormalPriority, NULL);
if (err != errNone) {
SysFatalAlert("Notify registration failed!");
SysLibRemove(gBcs2RefNum);
gBcs2RefNum = 0;
return false;
}
return true;
}
#endif
/************************************************************
*
* FUNCTION: ShowQuantityForm
*
************************************************************/
static void ShowQuantityForm(UInt16 triggerID, Boolean manual)
{
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
*
************************************************************/
static Boolean QuantityFormHandleEvent(EventPtr event)
{
Boolean handled = false;
FormPtr form;
switch (event->eType) {
case frmOpenEvent:
form = FrmGetActiveForm();
QuantityFormInit(form);
FrmDrawForm(form);
handled = true;
break;
case ctlSelectEvent:
switch (event->data.ctlSelect.controlID) {
case QuantityForm1:
AppendQuantityDigit(1);
break;
case QuantityForm2:
AppendQuantityDigit(2);
break;
case QuantityForm3:
AppendQuantityDigit(3);
break;
case QuantityForm4:
AppendQuantityDigit(4);
break;
case QuantityForm5:
AppendQuantityDigit(5);
break;
case QuantityForm6:
AppendQuantityDigit(6);
break;
case QuantityForm7:
AppendQuantityDigit(7);
break;
case QuantityForm8:
AppendQuantityDigit(8);
break;
case QuantityForm9:
AppendQuantityDigit(9);
break;
case QuantityForm0:
AppendQuantityDigit(0);
break;
case QuantityFormClear:
StrCopy(gQuantity, "0");
UpdateQuantityDisplay();
break;
case QuantityFormCancel:
FrmReturnToForm(MainForm);
FocusBarcode();
break;
case QuantityFormOK:
QuantityFormAccept();
break;
default:
break;
}
handled = true;
break;
case keyDownEvent:
switch (event->data.keyDown.chr) {
case chrLineFeed:
QuantityFormAccept();
handled = true;
break;
case chrDigitZero:
AppendQuantityDigit(0);
handled = true;
break;
case chrDigitOne:
AppendQuantityDigit(1);
handled = true;
break;
case chrDigitTwo:
AppendQuantityDigit(2);
handled = true;
break;
case chrDigitThree:
AppendQuantityDigit(3);
handled = true;
break;
case chrDigitFour:
AppendQuantityDigit(4);
handled = true;
break;
case chrDigitFive:
AppendQuantityDigit(5);
handled = true;
break;
case chrDigitSix:
AppendQuantityDigit(6);
handled = true;
break;
case chrDigitSeven:
AppendQuantityDigit(7);
handled = true;
break;
case chrDigitEight:
AppendQuantityDigit(8);
handled = true;
break;
case chrDigitNine:
AppendQuantityDigit(9);
handled = true;
break;
default:
break;
}
break;
default:
break;
}
return handled;
}
/************************************************************
*
* FUNCTION: QuantityFormInit
*
************************************************************/
static void QuantityFormInit(FormPtr form)
{
// Set form title according to what is being edited.
if (gQuantityTriggerID == MainFormCases) {
FrmSetTitle(form, "Case Quantity");
} else if (gQuantityTriggerID == MainFormUnits) {
FrmSetTitle(form, "Unit Quantity");
}
// Display current quantity value.
FldSetTextPtr(FrmGetObjectPtr(form, FrmGetObjectIndex(form, QuantityFormQuantity)), gQuantity);
}
/************************************************************
*
* FUNCTION: AppendQuantityDigit
*
************************************************************/
static void AppendQuantityDigit(UInt16 digit)
{
Char digitText[2];
if (! gQuantityTouched) {
// This is the first time the quantity is being touched; replace it
// outright instead of appending the digit.
StrIToA(gQuantity, digit);
UpdateQuantityDisplay();
gQuantityTouched = true;
} else if (StrLen(gQuantity) > 2) {
// The current quantity already exceeds 2 digits; issue a warning beep
// instead of appending the digit.
SndPlaySystemSound(sndWarning);
} else if (StrCompare(gQuantity, "0") == 0) {
// The current quantity is zero; replace it outright instead of
// appending the digit.
StrIToA(gQuantity, digit);
UpdateQuantityDisplay();
} else {
// Okay, we can append the digit.
StrIToA(digitText, digit);
StrCat(gQuantity, digitText);
UpdateQuantityDisplay();
}
}
/************************************************************
*
* FUNCTION: UpdateQuantityDisplay
*
************************************************************/
static void UpdateQuantityDisplay()
{
FormPtr form;
FieldPtr field;
form = FrmGetFormPtr(QuantityForm);
field = FrmGetObjectPtr(form, FrmGetObjectIndex(form, QuantityFormQuantity));
FldSetTextPtr(field, gQuantity);
FldDrawField(field);
}
/************************************************************
*
* 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();
}
#ifdef SCANLIB_SYMBOLY
/* } else { */
/* EnableScanner(true); */
#endif
}
}