_WINDOWS 3.0 APPLICATION DEVELOPMENT_ by Walter Knowles [LISTING ONE] /******************************************************** * FILE: chekmate.ddl * DESCRIPTION: db_Vista database definition language schema *********************************************************/ database chekmate { /* File definition section */ data file checks1 = "chekmate.d01" contains system, payor, payee, tranPayee, account; data file checks2 = "chekmate.d02" contains transaction, actual; data file checks3 = "chekmate.d03" contains budget, distribution; key file checkKey = "chekmate.k01" contains strPayorName, strAccountName, strAccountCode, strPayeeCode, strPayeeName; /* Ewcord definition section */ record payor { unique key char strPayorName [35]; char strPayorAddr1 [35];/* single element fields */ char strPayorAddr2 [35];/* are easier in toolbook */ char strPayorCity [20]; char strPayorState [3]; char strPayorZip [10]; char strPayorCtry [10]; char strPayorTel [15]; char cPayorRegAdd; /* values are (a)D(d) and */ char cPayorAccAdd; /*(a)S(k) on exception processing */ char cPayorPayeeAdd; char cPayorAccess; /* values are C(ode) and N(ame) */ int nPayorYear; db_addr dbaPayorCurrBank; int nPayorNextRecur; } /* Account covers both balance sheet and income statement accounts. */ record account { key char strAccountName [35]; key char strAccountCode [15]; char strAccountNumber [15]; char cAccountType; /* A L E I E */ int bAccountTax; /* T= tax related */ int bAccountIsRegister; } /* Payee */ record payee { key char strPayeeName [35]; key char strPayeeCode [15]; char strPayeeAddr1 [35]; char strPayeeAddr2 [35]; char strPayeeCity [20]; char strPayeeState [3]; char strPayeeZip [10]; char strPayeeCtry [10]; char strPayeeTel [15]; /* default account is a set */ char strPayeeMemo [35]; char strPayeeType [10]; } /* Budget records are owned by the account. */ record budget { int nBudgetMonth; /* number from 1 to 12 */ long lBudgetAmount; } /* Actuals are maintained in order; speeds process of updating register.*/ record actual { int nActualMonth; long lActualAmount; } /* Transactions are headers for distributions. */ record transaction { char cTranType; /* check, deposit, etc */ char strTranMemo [35]; int bTranClear; /* T = cleared */ int nTranNumber; /* Check number */ long lTranDate; /* Transaction date as a "COBOL" date: ccyymmdd */ long lTranAmount; } /* Distributions are the balancing entries for transactions. */ record distribution { long lDistrAmount; } /* Set definition section -- Sets with system as parent */ set system_payor { order ascending; owner system; member payor by strPayorName; } set system_payeecode { order ascending; owner system; member payee by strPayeeCode; } set system_payeename { order ascending; owner system; member payee by strPayeeName; } set system_lastData { order next; owner system; member lastData; } /* Sets with payor as parent */ set payor_accountcode { order ascending; owner payor; member account by strAccountCode; } set payor_accounttypecode { order ascending; owner payor; member account by cAccountType, strAccountCode; } set payor_accountname { order ascending; owner payor; member account by strAccountName; } /* Sets with account as parent */ set account_budget { order ascending; owner account; member budget by nBudgetMonth; } set account_actual { order ascending; owner account; member actual by nActualMonth; } set account_distribution { order next; owner account; member distribution; } /* account_payee implements default distribution account for payees. */ set account_payee { order next; owner account; member payee; } /* sets with payee as owner */ set payee_transaction { order next; owner payee; member transaction; } /* sets with actual as owner. */ set actual_transaction_date { order ascending; owner actual; member transaction by lTranDate; } set actual_transaction_number { order ascending; owner actual; member transaction by nTranNumber; } set actual_transaction_type { order ascending; owner actual; member transaction by cTranType, lTranDate; } set actual_distribution { order next; owner actual; member distribution; } /* sets with transaction as owner */ set transaction_distribution { order next; owner transaction; member distribution; } set transaction_tranPayee { order next; owner transaction; member tranPayee; } } [LISTING TWO] to handle LINKDLLS -- use the Windows kernel for memory management linkdll "kernel.exe" word globalAlloc (word,dword) word globalFree (word) pointer globalLock (word) word globalReAlloc(word,dword,word) dword globalSize(word) word globalUnlock(word) end --use ToolBook's file dll for file system access linkdll "tbkfile.dll" string getCurrentDirectory(string) string getCurrentDrive() end -- use Raima's db_VISTA dll for database functionality linkdll "vista.dll" --except for close,closetask, and opentask, functions are as --in the db_vista docs (dt_ = d_). The last args are *currenttask,dbn int dt_close (pointer) int dt_closetask(pointer) int dt_connect (int,pointer,int) int dt_crget (pointer,pointer,int) --other calls ommitted here int dt_setro (int,pointer,int) end --chekmate.dll is the helper dll for the checking account system linkdll "chekmate.dll" --other calls ommitted here word dbv_EditRegister (word,int,long,int,word,int) word dbv_GetRegister (word,int,word) end end [LISTING THREE] to handle InitGlobals system svhControl, svPtrControl system svhDBBuffer, svPtrDBBuffer system svhCurrTask, svPtrCurrTask set svhControl to GlobalAlloc (66,64) --GHND is 66, size is 64 bytes set svhDBBuffer to GlobalAlloc (66,256) set svhCurrTask to GlobalAlloc (66,64) if svhControl <= 0 or svhDBBuffer <= 0 or svhCurrTask <= 0 --clean up, since allocation failed send FreeGlobals send Exit -- shutdown the system else --get pointers set svPtrControl to GlobalLock (svhControl) set svPtrDBBuffer to GlobalLock (svhDBBuffer) set svPtrCurrTask to GlobalLock (svhCurrTask) end end to handle FreeGlobals system svhControl, svPtrControl system svhDBBuffer, svPtrDBBuffer system svhCurrTask, svPtrCurrTask if svhControl is not null and svhControl > 0 get GlobalUnlock (svhControl) get GlobalFree (svhControl) end if svhDBBuffer is not null and svhDBBuffer > 0 get GlobalUnlock (svhDBBuffer) get GlobalFree (svhDBBuffer) end if svhCurrTask is not null and svhCurrTask > 0 get GlobalUnlock (svhCurrTask) get GlobalFree (svhCurrTask) end --clean up globals to make ToolBook suspend rather than GP Fault set svhControl to null set svhDBBuffer to null set svhCurrTask to null set svPtrControl to null set svPtrDBBuffer to null set svPtrCurrTAsk to null end [LISTING FOUR] typedef struct Control { HANDLE hCurrentTask; //current task structure (DB_TASK) HANDLE hDataBaseBuffer; //db_Vista's control buffer // Offscreen image / database address pairs HANDLE hRegisterImage; //offscreen image of the register field HANDLE hRegisterDBAArray; //array of database addresses, 1 for each line //in the register image // Selector fields storage HANDLE hPayeeCodeArray; //offscreen image of the payee selector field HANDLE hPayeeCodeDBAArray; //array of database addresses - payee selector HANDLE hPayeeNameArray; //offscreen image of the payee selector field HANDLE hPayeeNameDBAArray; //array of database addresses - payee selector HANDLE hAccountCodeArray; //offscreen image of account selector field HANDLE hAccountCodeDBAArray;//array of database addresses - accts. selector HANDLE hAccountNameArray; //offscreen image of account selector field HANDLE hAccountNameDBAArray;//array of database addresses - accts selector HANDLE hRegisterNumbers; //array of deposit, payments, balances // Database addresses for active records DB_ADDR dbaCurrPayor; //address of the current payor record DB_ADDR dbaCurrRegister; //address of the current active register record DB_ADDR dbaCurrRegisterActual; //address of current month's register // Database number for concurrency operations int nDatabaseNumber; //normally 0 } CONTROL; [LISTING FIVE] -------------------------- --CONTROL BLOCK ACCESSOR-- -------------------------- to get hControl fField system svPtrControl set vOffset to ControlOffset(fField) set retval to -1 conditions when char 1 of fField is "h" --handles set retval to pointerWord(vOffset,svPtrControl) when chars 1 to 3 of fField is "dba" --db_addrs set retval to pointerlong(vOffset,svPtrControl) when char 1 of fField is "n" set retval to pointerint(vOffset,svPtrControl) end return retval end to set hControl fField to fVal system svPtrControl set vOffset to ControlOffset(fField) conditions when char 1 of fField is "h" --handles get pointerWord(vOffset,svPtrControl,fVal) when chars 1 to 3 of fField is "dba" --db_addrs get pointerlong(vOffset,svPtrControl,fVal) when char 1 of fField is "n" get pointerint(vOffset,svPtrControl,fVal) end end to get ControlOffset fField conditions when char 1 of fField is "h" --handles conditions when fField is hCurrentTask set vOffset to 0 when fField is hDatabaseBuffer set vOffset to 1 when fField is hRegisterImage set vOffset to 2 when fField is hRegisterDBAArray set vOffset to 3 when fField is hRegisterCodeArray set vOffset to 4 when fField is hRegisterCodeDBAArray set vOffset to 5 when fField is hRegisterNameArray set vOffset to 6 when fField is hRegisterNameDBAArray set vOffset to 7 when fField is hPayeeCodeArray set vOffset to 8 when fField is hPayeeCodeDBAArray set vOffset to 9 when fField is hPayeeNameArray set vOffset to 10 when fField is hPayeeNameDBAArray set vOffset to 11 when fField is hAccountCodeArray set vOffset to 12 when fField is hAccountCodeDBAArray set vOffset to 13 when fField is hAccountNameArray set vOffset to 14 when fField is hAccountNameDBAArray set vOffset to 15 when fField is hRegisterNumbers set vOffset to 16 end return vOffset*2 when chars 1 to 3 of fField is "dba" --db_addrs set vbase to 34 -- max(vOffset*2)+2 conditions when fField is dbaCurrPayor set vOffset to 0 when fField is dbaCurrRegister set vOffset to 1 when fField is dbaCurrRegisterActual set vOffset to 2 end return vbase+vOffset*4 when char 1 of fField is "n" set vbase to 46 --vbase + dbaentries * 4 conditions when fField is "nDatabaseNumber" set vOffset to 0 end return vbase+vOffset*2 end end [LISTING SIX] -- Fill selector loads selector text from global memory into selector field to handle FILLSELECTOR fComboName,fObjectName set vCurdba to dbv_currentrecord() set vHText to hControl("h" & fComboName & "Array") if vhText = 0 get createSelectorList (fComboName) set vHText to hControl("h" & fComboName & "Array") end get globalLock (vHText) if fObjectName is null set fObjectName to fComboName end set text of (pListID of group fObjectName) to\ pointerString(0, it) get globalUnLock (vHText) set dbv_currentrecord() to vCurdba end -- createSelectorList loads global memory block with values from appropriate -- set member fields to get createSelectorList fArray system svPtrDbBuffer, svPtrCurrTask -- first determine what all the database constants are conditions when fArray is "RegisterName" get dt_findfm(20000,svPtrCurrTask,0) set vSet to 20006 get dt_setom(vSet,20000,svPtrCurrTask,0) set vField to 1000 -- cases for "RegisterCode", "PayeeCode", "PayeeName", "AccountCode", -- and "AccountName" are similar else return false end set vArray to "h" & fArray --check if the memory is already allocated if hControl(vArray & "Array") = 0 set vhTextBuffer to globalAlloc(66,1000) set vhDBABuffer to globalAlloc(66,500) set hControl(vArray & "Array") to vhTextBuffer set hControl(vArray & "DBAArray") to vhDBABuffer else set vhTextBuffer to hControl(vArray & "Array") set vhDBABuffer to hControl(vArray & "DBAArray") end set vPtrTextBuffer to globalLock(vhTextBuffer) set vPtrDBABuffer to globalLock(vhDBABuffer) set vDBAOffset to 0 set vcharCount to 0 get dt_findfm(vSet,svPtrCurrTask,0) while it = 0 --build the text buffer get dt_crread(vField,svPtrDbBuffer,svPtrCurrTask,0) set vline to pointerstring(0,svPtrDbBuffer) put CRLF after vLine get pointerstring(vCharCount,vPtrTextBuffer,vLine) increment vCharCount by charcount(vLine) --build the db_addr buffer get dt_crget(svPtrDbBuffer,svPtrCurrTask,0) get pointerlong(0,svPtrDbBuffer) get pointerlong(vDBAOffset,vPtrDBABuffer,it) increment vDBAOffset by 4 -- because DBAs are longs --get next record get dt_findnm(vSet,svPtrCurrTask,0) end return true get globalunLock(vhTextBuffer) get globalunLock(vhDBABuffer) end [LISTING SEVEN] /***************************************************************************** * dbv_CreateSelectorList--Purpose: Obtains list of available selections for * a particular chekmate field and save them along with database addresses * in the chekmate control block. * Parameters: hControl, HANDLE to the database control block; nSetID, numeric * identifier for set containing selection list; lField, LONG database field * number; nHandleOffset, integer offset into the Control block of handle for * memory where data should be stored. * Return Value: 0, if no errors; -n, if errors reading database */ extern WORD FAR PASCAL dbv_CreateSelectorList( HANDLE hControl, // Control Block int nSetID, // database set identifier LONG lField, // database field number int hHandleOffset) // offset in Control Block for memory handle { int i; int iError=-1; // error return code LPCONTROL lpControl=NULL; // control block DB_TASK DB_FAR *lpTask=NULL; // task pointer LPHANDLE lpHandle; // handle pointer if (NULL==hControl) { return -1; // control block not initialized } // Lock control block so task block can be locked for database call. lpControl = (LPCONTROL) GlobalLock (hControl); lpTask = (DB_TASK DB_FAR *) GlobalLock (lpControl->hCurrentTask); // point to handle in control block for list text. lpHandle = ((LPHANDLE) lpControl) + hHandleOffset; iError = LoadSelectorList (lpHandle,lpHandle+1,nSetID,lField,lpTask, lpControl->nDatabaseNumber); CleanUp: GlobalUnlock (lpControl->hCurrentTask); GlobalUnlock (hControl); return (iError); } /****************************************************************************** * LoadSelectorList--Purpose: Reads database for specified set and transfer * data from lField into text buffer. Each record a separate text line in * buffer and database addresses for each record will also be saved. * Parameters: lphListText, handle to memory for list text; lphListDBA, handle * to memory for list database addresses; nSetID, numeric identifier for * database set containing the selection list; lField, LONG database field * number; lpTask, pointer to database task; nDatabase, database number * Return Value: 0, if no errors; -n, if errors reading database */ int PASCAL LoadSelectorList ( LPHANDLE lphListText, // handle to memory for list text LPHANDLE lphListDBA, // memory handle for list database adr. int nSetID, // database set ID LONG lField, // database field number for list text DB_TASK DB_FAR *lpTask, // database task int nDatabase) // database number { int iError=-1; // error return code int nDBA=0; // number of DBA's int nMaxDBA=0; // maximum number of DBA's used int nMaxBytes=0; // maximum bytes allowed LPSTR lpText=NULL; // current text line DB_ADDR FAR *lpDBA=NULL; // database address value HANDLE hMem; // handle to reallocated memory block DB_ADDR lCurDBA; // current database record // save the current database record dt_crget ((DB_ADDR FAR *)&lCurDBA,lpTask,nDatabase); // initialize the text and DBA memory blocks. if (*lphListText == NULL) { *lphListText = GlobalAlloc (DLL_ALLOC,SELECTOR_TEXT_SIZE); } if (*lphListDBA == NULL) { *lphListDBA = GlobalAlloc (DLL_ALLOC,(LONG)(SELECTOR_DBA_COUNT*sizeof (DB_ADDR))); } if (*lphListText == NULL || *lphListDBA == NULL) { goto CleanUp; } // initial allocations to set the maximum values and lock the memory blocks. nMaxDBA = GlobalSize (*lphListDBA) / sizeof(DB_ADDR); nMaxBytes = GlobalSize (*lphListText); lpText = GlobalLock (*lphListText); lpDBA = (DB_ADDR FAR *) GlobalLock (*lphListDBA); // read the database and fill in the text values for (iError = dt_findfm (nSetID,lpTask,nDatabase); iError==S_OKAY; iError = dt_findnm (nSetID,lpTask,nDatabase)) { if (nMaxBytes<=MIN_SELECTOR_TEXT_SIZE) { // need to allocate more text memory. lpText = NULL; GlobalUnlock (*lphListText); hMem = GlobalReAlloc (*lphListText, GlobalSize(*lphListText)+SELECTOR_TEXT_SIZE, GMEM_ZEROINIT); if (hMem==NULL) { iError = -2; goto CleanUp; // not enough memory } *lphListText = hMem; // new handle lpText = GlobalLock (*lphListText); nMaxBytes = GlobalSize(*lphListText) - lstrlen (lpText); lpText += lstrlen(lpText); } // read the field contents into the text buffer dt_crread(lField,lpText,lpTask,nDatabase); lstrcat (lpText,"\r\n"); lpText += lstrlen(lpText); // save the DBA of the record if (nDBA >= nMaxDBA) { // need to allocate more DBA memory. lpDBA = NULL; GlobalUnlock (*lphListDBA); hMem = GlobalReAlloc (*lphListDBA, GlobalSize(*lphListDBA)+SELECTOR_DBA_COUNT*sizeof(DB_ADDR), GMEM_ZEROINIT); if (hMem==NULL) { iError = -2; goto CleanUp; // not enough memory } *lphListDBA = hMem; // new handle lpDBA = (DB_ADDR DB_FAR *)GlobalLock (*lphListDBA); nMaxDBA = GlobalSize(*lphListDBA) - nDBA; lpDBA += nDBA; } dt_crget (lpDBA,lpTask,nDatabase); lpDBA++; nDBA++; } CleanUp: // restore the address of the current record dt_crset ((DB_ADDR far *)&lCurDBA,lpTask,nDatabase); if (lpText!=NULL) { GlobalUnlock (*lphListText); } if (lpDBA!=NULL) { GlobalUnlock (*lphListDBA); } if (iError==S_EOS) { iError = S_OKAY; } return (iError); } [BALANCE MAINTENACE CODE] typedef struct RegisterNumbers { long lDeposit; //value of the deposit (may be 0) long lPayment; //value of the payment (may be 0) long lBalance; //value of the balance } REGISTERNUMBERS; /******************************************************************************* * UpdateRegisterNumbers * * Purpose: * This routine will update the register payments, deposits and balance * arrays. Additonally it will copy the information into the register * text arrays and add the final zero byte to the register text * * Parameters: * lpControl pointer to the control block * * Return Value: * 0 if no errors * */ int PASCAL UpdateRegisterNumbers( LPCONTROL lpControl) // pointer to the Control block { int i; char cPayment[20], cDeposit[20], cBalance[20]; LONG lBegBal; // begining balance LPREGISTERNUMBERS lpNumbers; // register numbers LPREGISTERIMAGE lpImage; // register image block LPSTR lpLine; // register line image LONG lPrevBal; // previous balance lpImage = (LPREGISTERIMAGE) GlobalLock (lpControl->hRegisterImage); lpNumbers = (LPREGISTERNUMBERS) GlobalLock (lpControl->hRegisterNumbers); lpLine = (LPSTR) &(lpImage->Text[0]); // we first go thru the list of numbers and set the balances and then // we convert the values to strings and place them into the register // line. Note the register lines are assumed to be blank filled and // terminated with a CRLF. for (i=0,lPrevBal=lpNumbers->lBalance;inLines;i++,lpNumbers++) { lPrevBal = lpNumbers->lBalance = lPrevBal + lpNumbers->lDeposit - lpNumbers->lPayment; // convert the values to zero terminated strings Long2Money (lpNumbers->lPayment,(LPSTR) &(cPayment[0])); Long2Money (lpNumbers->lDeposit,(LPSTR) &(cDeposit[0])); Long2Money (lpNumbers->lBalance,(LPSTR) &(cBalance[0])); wsprintf (lpLine+50," %10s %10s %10s", (LPSTR) &(cPayment[0]), (LPSTR) &(cDeposit[0]), (LPSTR) &(cBalance[0])); // now add the CRLF to the lines the current zero byte placed in // the lpLine will be overwritten and no zero byte will be used. lpLine += lstrlen(lpLine); // positioned at the zero byte *lpLine++ = '\r'; *lpLine++ = '\n'; // lpLine now positioned correctly for begining of next line } // add the final empyt line and a zero byte to terminate the register text field lstrcat (lpLine,"\r\n"); GlobalUnlock (lpControl->hRegisterImage); GlobalUnlock (lpControl->hRegisterNumbers); return (0); } /******************************************************************************* * Long2Money * * Purpose: * convert a long number into a money text string. The numeric value * is in terms of cents. * * Parameters: * lValue LONG value to be converted * lpText LPSTR to the text string * * Return Value: * LPSTR pointer to the text string * */ LPSTR PASCAL Long2Money( LONG lValue, // long value to be converted LPSTR lpDecimalText) // { LPSTR lpStr = lpDecimalText; // Take care of the sign of the number so that the conversion // will only have to deal with positive values. if (lValue <0) { lValue = -lValue; *lpStr++ = '-'; } // convert the number to characters - note if the value is less // than 100 then we will want the number to be be converted as // 0.nn Therefore 100 will be added to the value and then later // the '1' will be replaced with a '0'. wsprintf (lpStr,"%li",(LONG) ((lValue<100)?lValue+100:lValue)); // now make room for the decimal point lpStr += lstrlen(lpStr); // lpStr points to the end of the string *(lpStr+1) = *lpStr; lpStr--; // terminating character *(lpStr+1) = *lpStr; lpStr--; // units digit *(lpStr+1) = *lpStr; // tens digit *lpStr-- = '.'; // add in the decimal point if (lValue < 100) { *lpStr = '0'; // replace the '1' with a '0' forces leading zero } return (lpDecimalText); } [REGISTER MAINTENANCE CODE] /******************************************************************************* * dbv_GetRegister * * Purpose: * This routine will read the transactions associated with the current * account for a given month and generates a checkbook register image. * The image is saved in memory and also set into the toolbook fields * specified in hFldTable. * * Parameters: * hControl HANDLE to the database control block * nSetID numeric identifier for database set * hFldTable HANDLE to the ToolBook field table list * * Return Value: * 0 if no errors * 1 if error in setting field * -1 control block not initialized * */ extern WORD FAR PASCAL dbv_GetRegister( HANDLE hControl, // Control Block int nSetID) // database set identifier { int i; int iError=0; // error return LPCONTROL lpControl=NULL; // control block int nDatabase; // database number LPREGISTERIMAGE lpImage=NULL; // pointer to the register image block // Lock the control block so that the task block and the database // buffer can be obtained. The database buffer will be used to contain // the individual field values for the current record. if (NULL==hControl) { return -1; // control block not initialized } // Lock the control block so that the task block can be locked for the // database call. Then lock the field table so that we can access // which ToolBook fields are to be set. lpControl = (LPCONTROL) GlobalLock (hControl); lpFldTable = (LPFLDTABLE) GlobalLock(hFldTable); // Initialize the memory blocks that will be used for the register // information. if (0!=(iError=InitRegisterImage(lpControl))) { goto CleanUp; } // Now we load the register image if (0!=(iError=LoadRegisterImage (lpControl,nSetID))) { goto CleanUp; } GlobalUnlock (lpControl->hRegisterImage); CleanUp: // all done - now unlock all the allocations GlobalUnlock (lpFldTable->hBookName); GlobalUnlock (hFldTable); GlobalUnlock (hControl); return 0; } /******************************************************************************* * InitRegisterImage * * Purpose: * This routine will initialize the memory blocks that will contain the * chekmate register image, the database addresses and the array of the * deposits, payments and balances for the register. * * Parameters: * lpControl pointer to the control block * * Return Value: * 0 if no errors * -1 if error allocating the memory * */ int PASCAL InitRegisterImage( LPCONTROL lpControl) // pointer to the Control block { LPREGISTERIMAGE lpImage; // pointer to the register image // if the handles are currently pointing to memory blocks then we free // them and re-allocate. This is done just to make life a little easier if (NULL!=lpControl->hRegisterImage) { GlobalFree (lpControl->hRegisterImage); lpControl->hRegisterImage = NULL; } if (NULL!=lpControl->hRegisterDBAArray) { GlobalFree (lpControl->hRegisterDBAArray); lpControl->hRegisterDBAArray = NULL; } if (NULL!=lpControl->hRegisterNumbers) { GlobalFree (lpControl->hRegisterNumbers); lpControl->hRegisterNumbers = NULL; } // now allocate the space for the register image array lpControl->hRegisterImage = GlobalAlloc (DLL_ALLOC, (LONG) sizeof(REGISTERIMAGE)+ (INITIALREGISTERLINES*REGISTERLINESIZE)); if (NULL==lpControl->hRegisterImage) { return -1; } // now allocate the database address lists lpControl->hRegisterDBAArray = GlobalAlloc (DLL_ALLOC, ((long) sizeof(DB_ADDR))*((long) INITIALREGISTERLINES)); if (NULL==lpControl->hRegisterDBAArray) { return -1; } // now allocate the register payments, deposits and balance columns lpControl->hRegisterNumbers = GlobalAlloc (DLL_ALLOC, ((long) sizeof(REGISTERNUMBERS))*((long) INITIALREGISTERLINES)); if (NULL==lpControl->hRegisterNumbers) { return -1; } // initialize the image structure to contain the header information lpImage = (LPREGISTERIMAGE) GlobalLock (lpControl->hRegisterImage); lpImage->nMaxLines = INITIALREGISTERLINES; lpImage->nLines = 0; GlobalUnlock (lpControl->hRegisterImage); return (0); } /******************************************************************************* * LoadRegisterImage * * Purpose: * This routine will load the register data from the database. * * Parameters: * lpControl pointer to the control block * nSetID transaction set id * * Return Value: * 0 if no errors * -1 if error reading the database * */ int PASCAL LoadRegisterImage( LPCONTROL lpControl, // pointer to the Control block int nSetID) // set containing the transaction members { int i; int iError=0; // error return DB_ADDR lCurDBA; // current database address actual Actual; // current months value payee Payee; // current transactions payee transaction Transaction; // current transaction int nDatabase; // database number DB_ADDR lTranDBA; // transaction dba DB_TASK DB_FAR *lpTask=NULL; // task pointer DB_ADDR FAR *lpDBA; // database address array LPREGISTERNUMBERS lpNumbers; // register numbers LPREGISTERIMAGE lpImage; // register image block LPSTR lpLine; // line for the register image // lock the task, the register image, the database addresses array // and the register number array. lpTask = (DB_TASK DB_FAR *) GlobalLock (lpControl->hCurrentTask); nDatabase = lpControl->nDatabaseNumber; // save the current database record dt_crget ((DB_ADDR FAR *)&lCurDBA,lpTask,nDatabase); // now we load up the register data. The fist line in the register is // the begining balance. We "Fake out" the first line by creating // a psudeo transaction record for the actual month record. dt_crset ((DB_ADDR far *)&(lpControl->dbaCurrRegisterActual),lpTask,nDatabase); dt_setor (nSetID,lpTask,nDatabase); if (S_OKAY!=(iError=dt_recread((DB_ADDR FAR *)&Actual,lpTask,nDatabase))) { goto CleanUp; } lpImage = (LPREGISTERIMAGE) GlobalLock (lpControl->hRegisterImage); lpDBA = (DB_ADDR FAR *) GlobalLock (lpControl->hRegisterDBAArray); lpNumbers = (LPREGISTERNUMBERS) GlobalLock (lpControl->hRegisterNumbers); Transaction.cTranType = BBAL; // begining balance Transaction.lTranDate = 19000001 + (Actual.nActualMonth)*100; // date Transaction.nTranNumber = 0; // transaction number Transaction.bTranClear = 0; Transaction.lTranAmount = Actual.lActualAmount; lpNumbers->lBalance = Actual.lActualAmount; dt_crget (lpDBA++,lpTask,nDatabase); GenerateRegisterLine ((LPTRANSACTION) &Transaction,(LPPAYEE) &Payee,&(lpImage->Text[0])); lpImage->nLines++; lpNumbers++; GlobalUnlock (lpControl->hRegisterImage); GlobalUnlock (lpControl->hRegisterDBAArray); GlobalUnlock (lpControl->hRegisterNumbers); // now step thru the database reading the members of the set and // generating register lines for them. for (iError=dt_findfm (nSetID,lpTask,nDatabase),i=1; iError==S_OKAY; iError=dt_findnm (nSetID,lpTask,nDatabase),i++) { dt_crget ((DB_ADDR FAR *) &lTranDBA,lpTask,nDatabase); EditRegisterImage (lpControl,i,lTranDBA,nSetID,ADD_TRAN); } // now fill in the payment, deposit and balance fields UpdateRegisterNumbers (lpControl); CleanUp: // restore the address of the current record dt_crset ((DB_ADDR far *)&lCurDBA,lpTask,nDatabase); GlobalUnlock (lpControl->hCurrentTask); return (((iError==S_EOS)?S_OKAY:iError)); } [DB_VISTA SHELL CODE] -- composite functions for adding and editing records to get dbv_AddRecord fHDbControl, ftbFields, fRecordID system svPtrCurrTask,svPtrDbBuffer get dt_fillnew(fRecordID,svPtrDbBuffer,svPtrCurrTask,0) get last char of fRecordID set vStartingField to it *1000 --starting fields of VISTA records are on 1000 boundries return dbv_EditRecord(fHDbControl, fTbFields, vStartingField) end to get dbv_EditRecord fHDbControl, ftbFields, fStartingField system svPtrCurrTask,svPtrDbBuffer set vFldCount to itemcount( fTbFields ) step i from 1 to vFldCount get item i of fTbFields get pointerstring(0,svPtrDbBuffer,text of field id it) get dt_crwrite(fStartingField+i,svPtrDbBuffer,svPtrCurrTask,0) end return true end to get dbv_GetTBFieldArray fhDBControl, fSet, fFieldID, fTbFields system svPtrDbBuffer,svPtrCurrTask, svCurrAccountDBA set vCurDBA to dbv_currentrecord() get dt_findfm(fSet,svPtrCurrTask,0) if it = 0 set vFldCount to itemcount( fTbFields) step i from 1 to vFldCount get dt_crread(fFieldID,svPtrDbBuffer,svPtrCurrTask,0) set vID to item i of fTbFields set text of field id vID\ to pointerLong(0,svPtrDbBuffer)/10 format text of field id vID as "0.00" if dt_findnm(fSet,svPtrCurrTask,0) <> 0 break step end end end set dbv_currentrecord() to vCurDBA return true end to get dbv_EditTBFieldArray fhDBControl, fhFldTable, fSet, fTbFields system ,svPtrDbBuffer,svPtrCurrTask, svCurrAccountDBA set vCurDBA to dbv_currentrecord() get dt_findfm(fSet,svPtrCurrTask,0) if it = 0 set vFldCount to itemcount( fTbFields ) step i from 1 to vFldCount get text of field id (item i of fTbFields) format it as "0" get pointerLong(0,svPtrDbBuffer,it) get dt_crwrite(fFieldID,svPtrDbBuffer,svPtrCurrTask,0) if dt_findnm(fSet,svPtrCurrTask,0) <> 0 break step end end end set dbv_currentrecord() to vCurDBA return true end to get dbv_field fType,fFieldNumber system svhControl,svPtrCurrTask,svPtrDbBuffer get dt_crread(fFieldNumber,svPtrDbBuffer,svPtrCurrTask,0) if fType is "CHAR" set vVal to pointerByte(0,svPtrDbBuffer) set vVal to ansitochar(vVal) else execute "set vVal to pointer" & fType & "(0,svPtrDbBuffer)" end return vVal end to set dbv_field fType,fFieldNumber to fVal system svhControl,svPtrCurrTask,svPtrDbBuffer if ftype is "CHAR" get chartoansi(fVal) get pointerbyte (0,svPtrDbBuffer,it) else execute "get pointer" & fType & "(0,svPtrDbBuffer,fVal)" end get dt_crwrite(fFieldNumber,svPtrDbBuffer,svPtrCurrTask,0) end -- db_vISTA shell functions to get dbv_connectSet fSet system svPtrCurrTask get dt_connect(fSet,svPtrCurrTask,0) end to get dbv_currentOwnerField fSet, fType, fFieldNumber system svhControl,svPtrCurrTask,svPtrDbBuffer get dt_csoread(fSet,fFieldNumber,svPtrDbBuffer,svPtrCurrTask,0) if fType is "CHAR" set vVal to pointerByte(0,svPtrDbBuffer) set vVal to ansitochar(vVal) else execute "set vVal to pointer" & fType & "(0,svPtrDbBuffer)" end return vVal end to set dbv_currentOwnerField fSet, fType, fFieldNumber to fVal system svhControl,svPtrCurrTask,svPtrDbBuffer if ftype is "CHAR" get chartoansi(fVal) get pointerbyte (0,svPtrDbBuffer,it) else execute "get pointer" & fType & "(0,svPtrDbBuffer,fVal)" end get dt_csowrite(fSet,fFieldNumber,svPtrDbBuffer,svPtrCurrTask,0) end to get dbv_memberCount fSet system svPtrCurrTask,svPtrDbBuffer get dt_members(fSet,svPtrDbBuffer,svPtrCurrTask,0) return (pointerLong(0,svPtrDbBuffer)) end to get dbv_findFirstMember fSet system svPtrCurrTask return dt_findfm(fSet,svPtrCurrTask,0) end to get dbv_setOwnerToCurrRec fSet system svPtrCurrTask return dt_setor(fSet,svPtrCurrTask,0) end to get dbv_currentOwner fSet system svPtrCurrTask,svPtrDbBuffer set verror to dt_csoget(fSet,svPtrDbBuffer,svPtrCurrTask,0) set vdba to pointerlong(0,svPtrDbBuffer) set sysError to vError return vdba end to set dbv_currentOwner fSet to fDBA system svPtrCurrTask,svPtrDbBuffer if fDBA is null or fDBA is 0 get dbv_currentOwner(fSet) if sysError = 0 get dt_discon(fSet,svPtrCurrTask,0) end else get pointerlong(0,svPtrDbBuffer,fDBA) get dt_csoset(fSet,svPtrDbBuffer,svPtrCurrTask,0) end end to set dbv_connectOwner fSet to fDBA system svPtrCurrTask,svPtrDbBuffer set vCurrDBA to dbv_currentRecord() if dt_ismember(fSet,svPtrCurrTask,0) = 0 get dt_discon(fSet,svPtrCurrTask,0) end if not(fDBA is null or fDBA is 0 ) get pointerlong(0,svPtrDbBuffer,fDBA) get dt_csoset(fSet,svPtrDbBuffer,svPtrCurrTask,0) get dbv_currentmember(fSet) get dbv_currentrecord() get dbv_currentType() get dbv_currentOwner(fset) --get dt_csmset(fSet,) get dt_connect(fSet,svPtrCurrTask,0) end end to get dbv_currentMember fSet system svPtrCurrTask,svPtrDbBuffer set verror to dt_csmget(fSet,svPtrDbBuffer,svPtrCurrTask,0) set vdba to pointerlong(0,svPtrDbBuffer) set sysError to vError return vdba end to get dbv_currentType system svPtrCurrTask,svPtrDbBuffer get dt_crtype(svPtrDbBuffer,svPtrCurrTask,0) return pointerint(0,svPtrDbBuffer) end to get dbv_currentRecord system svPtrCurrTask,svPtrDbBuffer set verror to dt_crget(svPtrDbBuffer,svPtrCurrTask,0) set vdba to pointerlong(0,svPtrDbBuffer) set sysError to vError return vdba end to set dbv_currentRecord to fDBA system svPtrCurrTask,svPtrDbBuffer get pointerlong(0,svPtrDbBuffer,fDBA) get dt_crset(svPtrDbBuffer,svPtrCurrTask,0) end --dbv_findKey finds the key that matches fKeyVal, or the next higher key that --contains fKeyVal to get dbv_findkey fField, fKeyVal, fType system svPtrDbBuffer,svPtrCurrTask execute ("get pointer" & fType & "(0,svPtrDbBuffer,fKeyVal)") get dt_keyfind(fField, svPtrDbBuffer, svPtrCurrTask,0) if it <> 0 get dt_keynext(fField,svPtrCurrTask,0) if it <> 0 return -2 end execute ("get pointer" & fType & "(0,svPtrDbBuffer)") if fKeyVal is in it return 0 else return -1 end end end -- dbv_findrecord navigates through a set to find the nearest member of a set -- that matches or exceeds fVal to get dbv_findrecord fSet,fField,fVal,fType system svPtrDbBuffer,svPtrCurrTask set vCurrDBA to dbv_currentrecord() set sysError to -1 set retval to 0 set dbv_currentowner (fSet) to vCurrDBA get dt_findfm(fSet,svPtrCurrTask,0) if it = 0 get dt_crread(fField, svPtrDbBuffer, svPtrCurrTask, 0) execute ("set vdbVal to pointer" & fType &"(0,svPtrDbBuffer)") while vdbval <= fVal as text if vdbval is fval set retval to dbv_currentrecord() break while end get dt_findnm(fSet,svPtrCurrTask,0) if it <> 0 break while end get dt_crread(fField, svPtrDbBuffer, svPtrCurrTask, 0) set it to "set vdbVal to pointer" & fType &"(0,svPtrDbBuffer)" execute it end end set dbv_currentrecord() to vCurrDBA return retval end