// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: winmain.cpp 
// Compiler Used: MSVC40, HPUX CPP 10.24
// Produced By: Doug Gaer  
// File Creation Date: 09/18/1997  
// Date Last Modified: 03/18/1999
// Copyright (c) 1997 Douglas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
The VBD C++ classes are copyright (c) 1997, by Douglas M. Gaer.
All those who put this code or its derivatives in a commercial
product MUST mention this copyright in their documentation for
users of the products in which this code or its derivative
classes are used. Otherwise, you have the freedom to redistribute
verbatim copies of this source code, adapt it to your specific
needs, or improve the code and release your improvements to the
public provided that the modified files carry prominent notices
stating that you changed the files and the date of any change.

THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.

This is a test program used to test the (P)ersistent base class
in a practical database application using the wxWindow GUI library
version 1.68
*/
// ----------------------------------------------------------- // 
#include <stdlib.h> // Needed for getenv under HPUX CPP
#include "timer.h"
#include "dbconfig.h"
#include "winmain.h"
#include "strutil.h"
#include "asprint.h"
#include "vbdstats.h"
#include "btwalker.h"

// This struct was added to read Variable Block Headers because
// the Status member of VBHeader struct in the vbdfile.h file
// caused a syntax error when compiling this program under HPUX
// CPP version 1024. 
struct VBlockHeader // (V)ariable (B)lock (H)eader 
{
  UINT32 CkWord;      // (C)heck (W)ord for file integrity checks (4 bytes)
  UINT32 Length;      // Length of this block including VB header (4 bytes) 
  UINT32 VBStatus;    // Status of dynamic data stored in VB (4 byte)
  FAU NextDeletedVB;  // Pointer to next deleted VB (4 bytes) 
};

// (V)ariable (B)lock (D)atabse components
static POD *DB;             // Global database pointer
Grocery *agrocery = 0;      // Global data buffer used to add objects
Grocery *cgrocery = 0;      // Global data buffer used to change objects
Grocery *dgrocery = 0;      // Global data buffer used to display objects
const char *WildCard = "*"; // Used for wild card searches 

// Define global field names for controls, displays, printing
const char *KeyName = "ITEM NAME";
const char *M2Name =  "BRAND NAME";
const char *M3Name =  "STORE NAME";
const char *M4Name =  "PRICE";
const char *M5Name =  "QUANTITY";
const char *M6Name =  "PURCHASING";
const char *M7Name =  "LINE TOTAL";
const char *M8Name =  "GRAND TOTAL";
double grand_total = 0;
double sales_tax = 0;

// Set buffer length for formatting strings
const int sbuffer_len = 255;

// Output limit per string for ASCII printing
const int StringOffset = 15;   // String limits
const int PrintColsSpec = 111; // Columns limits

// Global mode indicators used to select items to be purchased
int display_all = 0;  // Display items for purchase only
int print_all = 0;    // Print items for purchase only
int ps_print_all = 0; // PostScript print items for purchase only
int as_print_all = 0; // ASCII print items for purchase only
int view_all = 0;     // View total cost of items for purchase only
int purchase_all = 1; // Used to set purchasing status

// Declare frame, subframe and menu objects
MyFrame *frame = 0;
wxMenuBar *menu_bar = 0;
wxFrame *APanelFrame = 0;
wxFrame *CPanelFrame = 0;
wxFrame *DPanelFrame = 0;

#ifdef __USE_MSW_PRINTING__
wxDialogBox *wxPageDialog = 0;  // MSW page setup dialog box
MSWPrintingParameters MSWPrnPars;
MSWPrintingParameters *MSWPrintSetup = &MSWPrnPars;
const int mswpDocNameLen = 255;
char mswpDocumentName[mswpDocNameLen];
#endif // __USE_MSW_PRINTING__

// Variable used to print display list objects
int print_list = 0;
int num_objects = 0;
int displaying_list = 0;

// Font, Pen, and Cursor objects
wxFont *labelFont = 0;
wxFont *itemFont = 0;
wxFont *textWindowFont = 0;

// wxWindows control objects
wxButton *btn_add_add = 0;          // Object Add Panel Add button
wxButton *btn_add_can = 0;          // Object Add Panel Cancel button
wxButton *btn_add_close = 0;        // Object Add Panel Close button
wxButton *btn_add_revert = 0;       // Object Add Panel Revert button 
wxChoice *add_PurchasingChoice = 0; // Add panel purchasing choice box
char *PurchasingChoiceStrings[2] = {
  "No",
  "Yes"
};

wxButton *btn_ch_sh = 0;           // Objedct Change Panel Search button
wxButton *btn_ch_cm = 0;           // Object Change Panel Commit button
wxButton *btn_ch_can = 0;          // Object Change Panel Cancel button
wxButton *btn_ch_close = 0;        // Object Change Panel Close button
wxButton *btn_ch_revert = 0;       // Object Change Panel Revert button 
wxChoice *ch_PurchasingChoice = 0; // Change panel purchasing choice box

wxButton *btn_ds_nx = 0;     // Object Display Panel Next button
wxButton *btn_ds_prev = 0;   // Object Display Panel Previous button
wxButton *btn_ds_ch = 0;     // Object Display Panel Change button
wxButton *btn_ds_rm = 0;     // Object Display Panel Remove button
wxButton *btn_ds_close = 0;  // Object Display Panel Close button

#ifdef __USE_MSW_PRINTING__
wxButton *btn_pdb_close = 0;     // MSW page setup dialog Close button
wxButton *btn_pdb_cancel = 0;    // MSW page setup dialog Accept button
wxButton *btn_pdb_accept = 0;    // MSW page setup dialog Accept button
wxButton *btn_pdb_default = 0;   // MSW page setup dialog Default button
wxSlider *LppSlider = 0;         // Lines per page slider
wxSlider *CellSlider = 0;        // Cell width slider
wxSlider *FsSlider = 0;          // Font size slider
wxSlider *LRMSlider = 0;         // Left and right margin slider
wxChoice *OrientationChoice = 0; // Orientation choice box
char *OrChoiceStrings[2] = {
  "Landscape",
  "Portrait"
};

wxChoice *FontChoice = 0;        // Font family choice box
char *FontChoiceStrings[6] = {
  "Swiss",      // wxSWISS
  "Roman",      // wxROMAN
  "Decorative", // wxDECORATIVE
  "Modern",     // wxMODERN 
  "Script",     // wxSCRIPT
  "System"      // wxDEFAULT
};
#endif // __USE_MSW_PRINTING__

// This statement initializes the whole application and calls OnInit
MyApp myApp;

// A macro needed for some compilers (AIX) that need 'main' to be defined
// in the application itself.
IMPLEMENT_WXWIN_MAIN

// `Main program' equivalent, creating windows and returning main app frame
wxFrame *MyApp::OnInit(void)
{
  // Create the main frame window
  frame = new MyFrame(NULL, (char *)ProgramName, 50, 50, 400, 300);

  // Give this frame a status line
  frame->CreateStatusLine(1);
  
  // Give it an icon
#ifdef wx_msw
  frame->SetIcon(new wxIcon("vbdbase"));
#endif
#ifdef wx_x
  frame->SetIcon(new wxIcon("vbdbase.xbm"));
#endif

  // File menu 
  wxMenu *file_menu = new wxMenu;
  file_menu->Append(FILE_VBDSTATS, "VBD file &Stats", 
		    "Display VBD file statistics");
  file_menu->AppendSeparator();
  file_menu->Append(FILE_EXPORT, "Export","Export database to ASCII file");
  file_menu->Append(FILE_IMPORT, "Import", "Import ASCII file into database");
  file_menu->Append(FILE_TEMPLATE, "Create Template",
		    "Create an ASCII template for import file");
  file_menu->AppendSeparator();
  file_menu->Append(FILE_BACKUP, "Backup",
		    "Backup the database to another file");
  file_menu->Append(FILE_MERGE, "Merge",
		    "Merge the contents of another database file");
  file_menu->AppendSeparator();
  file_menu->Append(FILE_COMPARE_INDEX, "Compare Index File",
		    "Compare the index file to the database file");
  file_menu->Append(FILE_REBUILD_INDEX, "Rebuild Index File",
		    "Rebuild the index file for this database");
  file_menu->AppendSeparator();
  file_menu->Append(FILE_QUIT, "E&xit","Exit this program");

  // Edit menu
  wxMenu *edit_menu = new wxMenu;
  edit_menu->Append(EDIT_CUT, "Cut\tCtrl+X", "Cut text and copy to clipboard");
  edit_menu->AppendSeparator();
  edit_menu->Append(EDIT_COPY, "Copy\tCtrl+C", "Copy text to clipboard");
  edit_menu->AppendSeparator();
  edit_menu->Append(EDIT_PASTE, "Paste\tCtrl+V", "Paste text from clipboard");
  
  // Database menu
  wxMenu *db_menu = new wxMenu;
  db_menu->Append(DB_DISPLAY_PURCHASING,
		  "&Display Objects to be Purchased",
		  "Display only objects selected for purchase ");
  db_menu->AppendSeparator();
  db_menu->Append(DB_DISPLAY_ALL, "Display All Objects",
		  "Display all objects in the database");
  db_menu->AppendSeparator();
  db_menu->Append(DB_ADD, "&Add", "Add objects to the database");
  db_menu->AppendSeparator();
  db_menu->Append(DB_CHANGE, "&Change", "Change an object in the database");
  db_menu->AppendSeparator();
  db_menu->Append(DB_REMOVE, "&Remove", "Remove an object from the database");
  db_menu->AppendSeparator();
  db_menu->Append(DB_RESET_PURCHASING, "Reset Purchasing List",
		  "Setting all objects for purchase to no");
  db_menu->AppendSeparator();
  db_menu->Append(DB_SET_ALL_PURCHASING, "Set All For Purchase",
		  "Purchase all the objects in the database");
  
  // Find menu
  wxMenu *find_menu = new wxMenu;
  const char *Prompt = "Find by ";
  UString s(Prompt); int PromptLen = strlen(Prompt); int StringLen = 0;

  s = s + KeyName; StringLen = strlen(KeyName);
  find_menu->Append(FIND_BYNAME, s.c_str(), s.c_str());
  s.DeleteAt(PromptLen, StringLen);
  find_menu->AppendSeparator();
  
  s = s + M2Name; StringLen = strlen(M2Name);
  find_menu->Append(FIND_BYBRAND, s.c_str(), s.c_str());
  s.DeleteAt(PromptLen, StringLen);
  find_menu->AppendSeparator();
  
  s = s + M3Name; StringLen = strlen(M3Name);
  find_menu->Append(FIND_BYSTORE, s.c_str(), s.c_str());
  s.DeleteAt(PromptLen, StringLen);

  // Print menu
  wxMenu *print_menu = new wxMenu;
#ifdef __USE_MSW_PRINTING__

  print_menu->Append(WXPRINT_PRINT_PURCHASING, "Print Objects to be Purchased",
		     "Print only the objects selected for purchase");
  print_menu->Append(WXPRINT_PRINT_ALL, "Print All Objects",
		     "Print all objects to the selected printer");
  print_menu->AppendSeparator();
  print_menu->Append(WXPRINT_PRINTER_SETUP, "Printer Setup",
		     "Configure the seleted printer");
  print_menu->Append(WXPRINT_PAGE_SETUP, "Page Setup",
		     "Set the page parameters before printing");
  print_menu->AppendSeparator();
  print_menu->Append(WXPRINT_PREVIEW_PURCHASING,
		     "Print Preview Objects for Purchase",
		     "Preview before printing objects for purchase");
  print_menu->Append(WXPRINT_PREVIEW_ALL, "Print Preview for All Objects",
		     "Preview before printing all objects");
  print_menu->AppendSeparator();
#endif // __USE_MSW_PRINTING__

  print_menu->Append(PRINT_POSTSCRIPT_PURCHASING, "Print to PostScript file",
		     "Print objects for purchase to PostScript file");

  print_menu->Append(PRINT_POSTSCRIPT_ALL,
		     "Print all Objects to PostScript file",
		     "Print all objects to a PostScript file");
  print_menu->AppendSeparator();
  print_menu->Append(PRINT_ASCII_PURCHASING, "Print to Text File",
		     "Print objects for purchase to text file");
  print_menu->Append(PRINT_ASCII_ALL, "Print all Objects to Text File",
		     "Print all objects to a text file");
  
  // View menu
  wxMenu *view_menu = new wxMenu;
  view_menu->Append(VIEW_CLEAR, "Clear Window", "Clear this text window");
  view_menu->AppendSeparator();
  view_menu->Append(VIEW_TOTAL_PURCHASING, "View Cost of Purchase",
		    "View total cost of objects selected for purchase");
  view_menu->AppendSeparator();
  view_menu->Append(VIEW_TOTAL_ALL, "View Cost All Objects",
		    "View total cost of all the objects");
  // Help menu
  wxMenu *help_menu = new wxMenu;
  help_menu->Append(HELP_ABOUT, "&About", "Info about this program");
  help_menu->AppendSeparator();
  help_menu->Append(HELP_USERLEVEL, "Access Rights",
		    "Display your user access rights");
  
  // Make all the menu bars  
  menu_bar = new wxMenuBar;
  menu_bar->Append(file_menu, "&File");
  menu_bar->Append(edit_menu, "&Edit");
  menu_bar->Append(db_menu, "Data&base");
  menu_bar->Append(find_menu, "F&ind");
  menu_bar->Append(print_menu, "&Print");
  menu_bar->Append(view_menu, "&View");
  menu_bar->Append(help_menu, "&Help");
  frame->SetMenuBar(menu_bar);

  // Associate the menu bar with the frame
  frame->SetMenuBar(menu_bar);

  // Create a text window in the main frame
  frame->textWin = new MyTextWindow(frame, 0, 250, 400, 250,
				    wxNATIVE_IMPL);
  frame->textWin->DragAcceptFiles(TRUE);
  
// *********************************************************** //
// Object Add Panel Code Starts Here 
// *********************************************************** //
  frame->addText = new MyText; // Init MyText Class pointer
  APanelFrame = new wxFrame(NULL, "Adding Object", 0, 0, 390, 390);

  // Panel fonts
  labelFont = new wxFont(12, wxSWISS, wxNORMAL, wxBOLD);
  itemFont = new wxFont(11, wxSWISS, wxNORMAL, wxBOLD);

  // Give it an icon
#ifdef wx_msw
  APanelFrame->SetIcon(new wxIcon("vbdbase"));
#endif
#ifdef wx_x
  APanelFrame->SetIcon(new wxIcon("vbdbase.xbm"));
#endif

  int apfwidth, apfheight;
  APanelFrame->GetClientSize(&apfwidth, &apfheight);

  // Make a panel in the subframe
  frame->apanel = new wxPanel(APanelFrame, 0, 0,
			      apfwidth, apfheight); 
  frame->apanel->SetLabelPosition(wxVERTICAL);
  frame->apanel->SetLabelFont(labelFont);
  frame->apanel->SetButtonFont(itemFont);
  frame->apanel->SetFont(itemFont);

  // Needed to line up first addText box under motif 
  frame->apanel->NewLine(); 
 
  frame->addText->item_name = new wxText(frame->apanel, NULL,
					 (char *)KeyName, "",-1,-1,150,-1); 
  frame->apanel->NewLine();
  frame->addText->brand = new wxText(frame->apanel, (wxFunction)&text_proc,
				     (char *)M2Name, "", -1, -1, 150, -1);
  frame->apanel->NewLine();
  frame->addText->store = new wxText(frame->apanel, (wxFunction)&text_proc,
				     (char *)M3Name, "", -1, -1, 150, -1);
  frame->apanel->NewLine();
  frame->addText->price = new wxText(frame->apanel, (wxFunction)&text_proc,
				     (char *)M4Name, "", -1, -1, 75, -1);
  frame->apanel->NewLine();
  frame->addText->quantity = new wxText(frame->apanel, (wxFunction)&text_proc,
					(char *)M5Name, "", -1, -1, 75, -1); 
  frame->apanel->NewLine();
  add_PurchasingChoice = new wxChoice(frame->apanel, NULL, (char *)M6Name,
				      -1, -1, -1, -1, 2,
				      PurchasingChoiceStrings);
  add_PurchasingChoice->SetSelection(0);

  frame->apanel->NewLine();
  frame->apanel->NewLine();
  
  btn_add_add = new wxButton(frame->apanel, (wxFunction)&AddBtnProc, "Add");
  btn_add_add->SetClientData((char*)ADD_BUTTON_ADD); // ID the button  

  btn_add_can = new wxButton(frame->apanel,(wxFunction)&AddBtnProc, "Cancel");
  btn_add_can->SetClientData((char*)ADD_BUTTON_CANCEL); // ID the button  

  btn_add_close = new wxButton(frame->apanel, (wxFunction)&AddBtnProc,
			       "Close");
  btn_add_close->SetClientData((char*)ADD_BUTTON_CLOSE); // ID the button  

  btn_add_revert = new wxButton(frame->apanel, (wxFunction)&AddBtnProc,
				"Revert");
  btn_add_revert->SetClientData((char*)ADD_BUTTON_REVERT); // ID the button  
// *********************************************************** //
// Object Add Panel Code Ends Here 
// *********************************************************** //

// *********************************************************** //
// Object Change Panel Code Starts Here 
// *********************************************************** //
  frame->changeText = new MyText; // Init MyText Class pointer
  CPanelFrame = new wxFrame(NULL, "Changing Object", 0, 0, 400, 400);

  // Give it an icon
#ifdef wx_msw
  CPanelFrame->SetIcon(new wxIcon("vbdbase"));
#endif
#ifdef wx_x
  CPanelFrame->SetIcon(new wxIcon("vbdbase.xbm"));
#endif

  int cpfwidth, cpfheight;
  CPanelFrame->GetClientSize(&cpfwidth, &cpfheight);

  // Make a panel in the subframe
  frame->cpanel = new wxPanel(CPanelFrame, 0, 0,
			      cpfwidth, cpfheight);
  frame->cpanel->SetLabelPosition(wxVERTICAL);
  frame->cpanel->SetLabelFont(labelFont);
  frame->cpanel->SetButtonFont(itemFont);
  frame->cpanel->SetFont(itemFont);

  // Needed to line up first panel with the others under motif 
  frame->cpanel->NewLine(); 

  UString cmesg("Enter the ");
  cmesg = cmesg + KeyName + " and click on Search.";
  wxMessage *message = new wxMessage(frame->cpanel, cmesg.c_str());
  frame->cpanel->NewLine();
  
  frame->changeText->item_name = new wxText(frame->cpanel, NULL,
					    (char *)KeyName, "",-1,-1,150,-1); 
  frame->cpanel->NewLine();
  frame->changeText->brand = new wxText(frame->cpanel, (wxFunction)&text_proc,
					(char *)M2Name, "", -1, -1, 150, -1);
  frame->cpanel->NewLine();
  frame->changeText->store = new wxText(frame->cpanel, (wxFunction)&text_proc,
					(char *)M3Name, "", -1, -1, 150, -1);
  frame->cpanel->NewLine();
  frame->changeText->price = new wxText(frame->cpanel, (wxFunction)&text_proc,
					(char *)M4Name, "", -1, -1, 75, -1);
  frame->changeText->quantity = new wxText(frame->cpanel,
					   (wxFunction)&text_proc,
					   (char *)M5Name, "",
					   -1, -1, 75, -1); 
  frame->cpanel->NewLine();
  ch_PurchasingChoice = new wxChoice(frame->cpanel, NULL, (char *)M6Name,
				     -1, -1, -1, -1, 2,
				     PurchasingChoiceStrings);
  ch_PurchasingChoice->SetSelection(0);

  frame->cpanel->NewLine();
  frame->changeText->line_total = new wxText(frame->cpanel,
					     (wxFunction)&text_proc,
					     (char *)M7Name, "",
					     -1, -1, 150, -1);
  frame->changeText->line_total->SetEditable(FALSE);
  frame->cpanel->NewLine();
  frame->cpanel->NewLine();
  btn_ch_sh = new wxButton(frame->cpanel, (wxFunction)&ChBtnProc, "Search");
  btn_ch_sh->SetClientData((char*)CHANGE_BUTTON_SH); // ID the button  

  btn_ch_cm = new wxButton(frame->cpanel, (wxFunction)&ChBtnProc, "Commit");
  btn_ch_cm->SetClientData((char*)CHANGE_BUTTON_CM); // ID the button  
  btn_ch_cm->Enable(0);
  
  btn_ch_can = new wxButton(frame->cpanel,(wxFunction)&ChBtnProc, "Cancel");
  btn_ch_can->SetClientData((char*)CHANGE_BUTTON_CANCEL); // ID the button  

  btn_ch_close = new wxButton(frame->cpanel, (wxFunction)&ChBtnProc, "Close");
  btn_ch_close->SetClientData((char*)CHANGE_BUTTON_CLOSE); // ID the button  

  btn_ch_revert = new wxButton(frame->cpanel, (wxFunction)&ChBtnProc,
				"Revert");
  btn_ch_revert->SetClientData((char*)CHANGE_BUTTON_REVERT); // ID the button  
// *********************************************************** //
// Object Change Panel Code Ends Here 
// *********************************************************** //

// *********************************************************** //
// Object Display Panel Code Starts Here 
// *********************************************************** //
  frame->displayText = new MyText; // Init MyText Class pointer
  DPanelFrame = new wxFrame(NULL, "Displaying Objects", 0, 0,
			    400, 400);

  // Give it an icon
#ifdef wx_msw
  DPanelFrame->SetIcon(new wxIcon("vbdbase"));
#endif
#ifdef wx_x
  DPanelFrame->SetIcon(new wxIcon("vbdbase.xbm"));
#endif

  int dpfwidth, dpfheight;
  DPanelFrame->GetClientSize(&dpfwidth, &dpfheight);

  // Make a panel in the subframe
  frame->dpanel = new wxPanel(DPanelFrame, 0, 0,
			      dpfwidth, dpfheight);
  frame->dpanel->SetLabelPosition(wxVERTICAL);
  frame->dpanel->SetLabelFont(labelFont);
  frame->dpanel->SetButtonFont(itemFont);
  frame->dpanel->SetFont(itemFont);

  // Needed to line up first addText box under motif 
  frame->dpanel->NewLine(); 

  frame->displayText->item_name = new wxText(frame->dpanel, NULL,
					     (char *)KeyName, "",-1,-1,150,-1);
  frame->displayText->item_name->SetEditable(FALSE);

  frame->dpanel->NewLine();
  frame->displayText->brand = new wxText(frame->dpanel, (wxFunction)&text_proc,
					 (char *)M2Name, "", -1, -1, 150, -1);
  frame->displayText->brand->SetEditable(FALSE);

  frame->dpanel->NewLine();
  frame->displayText->store = new wxText(frame->dpanel, (wxFunction)&text_proc,
					 (char *)M3Name, "", -1, -1, 150, -1);
  frame->displayText->store->SetEditable(FALSE);

  frame->dpanel->NewLine();
  frame->displayText->price = new wxText(frame->dpanel, (wxFunction)&text_proc,
					 (char *)M4Name, "", -1, -1, 75, -1);
  frame->displayText->price->SetEditable(FALSE);

  frame->displayText->quantity = new wxText(frame->dpanel,
					    (wxFunction)&text_proc,
					    (char *)M5Name, "",
					    -1, -1, 75, -1);
  frame->displayText->quantity->SetEditable(FALSE);
  
  frame->dpanel->NewLine();
  frame->displayText->purchasing = new wxText(frame->dpanel,
					     (wxFunction)&text_proc,
					     (char *)M6Name, "",
					     -1, -1, 75, -1);
  frame->displayText->purchasing->SetEditable(FALSE);

  frame->dpanel->NewLine();
  frame->displayText->line_total = new wxText(frame->dpanel,
					      (wxFunction)&text_proc,
					      (char *)M7Name, "",
					      -1, -1, 75, -1);
  frame->displayText->line_total->SetEditable(FALSE);

  frame->dpanel->NewLine();
  frame->dpanel->NewLine();
  btn_ds_nx = new wxButton(frame->dpanel, (wxFunction)&DsBtnProc, "Next");
  btn_ds_nx->SetClientData((char*)DISPLAY_BUTTON_NX); // ID the button  
  
  btn_ds_prev = new wxButton(frame->dpanel, (wxFunction)&DsBtnProc, "Prior");
  btn_ds_prev->SetClientData((char*)DISPLAY_BUTTON_PREV); // ID the button  

  btn_ds_ch = new wxButton(frame->dpanel,(wxFunction)&DsBtnProc, "Change");
  btn_ds_ch->SetClientData((char*)DISPLAY_BUTTON_CHANGE); // ID the button  

  btn_ds_close = new wxButton(frame->dpanel, (wxFunction)&DsBtnProc, "Close");
  btn_ds_close->SetClientData((char*)DISPLAY_BUTTON_CLOSE); // ID the button  

  btn_ds_rm = new wxButton(frame->dpanel, (wxFunction)&DsBtnProc, "Remove");
  btn_ds_rm->SetClientData((char*)DISPLAY_BUTTON_REMOVE); // ID the button
// *********************************************************** //
// Object Display Panel Code Ends Here 
// *********************************************************** //

  // Show the frames
  frame->Show(TRUE);
  APanelFrame->Show(FALSE);
  CPanelFrame->Show(FALSE);
  displaying_list = 0;
  DPanelFrame->Show(FALSE);
  
  // Vaiable Block Database components
  char *FileName = 0;
  char *CurrentCfgFile = 0;
  char *CfgFile = 0;
  Config *CfgData = new Config;
  char *AdminUser = 0;
  double dfp_val = 0;

  const char *DefaultWelcomeMesg = "welcome.txt";
  char *WelcomeMesgFile = 0;

#ifdef __USE_MSW_PRINTING__
  // Default values for wxWindows MSW Printing
  int mswp_lines_per_page = 0;
  int mswp_cell_length = 0;
  int mswp_font_size = 0;
  int mswp_left_margin = 0;
  char *mswp_orientation = 0;
  char *mswp_doc_name = 0;
  char *mswp_font = 0;
#endif // __USE_MSW_PRINTING__
  
  // Look for CfgFile name in environment
  if((CurrentCfgFile = getenv(EnvSetting)) == 0)
     CfgFile = (char *)DefaultCfgFile;
  else
    CfgFile = CurrentCfgFile;

  // Use default file name if no file is specifed on command line
  if(argc < 2) {
    int FileStatus = CfgData->Load((char *)CfgFile);
    if(!FileStatus) {
      Error->Message("Cannot locate any database files!\n",
		     "Defaulting to: ", DefaultDBFileName);
      FileName = (char *)DefaultDBFileName;
      WelcomeMesgFile = (char *)DefaultWelcomeMesg;
    }
    else { 
// *********************************************************** //
// Add all startup config file entries here
// *********************************************************** //
      FileName = CfgData->GetStrValue("DBFileName");
      AdminUser = CfgData->GetStrValue("AdminUser");
      WelcomeMesgFile = CfgData->GetStrValue("WelcomeMessageFile");
      sales_tax = CfgData->GetDFPValue("SalesTax");
#ifdef __USE_MSW_PRINTING__
  // Default values for wxWindows MSW Printing
  mswp_lines_per_page = CfgData->GetIntValue("mswpLinesPerPage");
  mswp_cell_length = CfgData->GetIntValue("mswpCellLength");
  mswp_font_size = CfgData->GetIntValue("mswpFontSize");
  mswp_left_margin = CfgData->GetIntValue("mswpLeftMargin");
  mswp_orientation = CfgData->GetStrValue("mswpOrientation");
  mswp_font = CfgData->GetStrValue("mswpFont");
  mswp_doc_name = CfgData->GetStrValue("mswpDocumentName");
#endif // __USE_MSW_PRINTING__
// *********************************************************** //

      if(!FileName) {
        UString comp("\nDefaulting to: ");
	comp = comp + DefaultDBFileName;
	Error->Message("DBFileName section missing in config file:\n",
		       CfgFile, comp.c_str());
	FileName = (char *)DefaultDBFileName;
      }
    }
  }
  else {
    FileName = argv[1];
  }

  if(!AdminUser) {
    AdminRights = 0;
    frame->SetStatusText((char *)ProgramName);
  }
  else {
    int result = strcmp(AdminUser, "TRUE");
    if(result == 0) {
      AdminRights = 1;
      frame->SetStatusText("ADMIN USER");
    }
    else {
      AdminRights = 0;
      frame->SetStatusText((char *)ProgramName);
    }
  }

  if(!WelcomeMesgFile) 
      WelcomeMesgFile = (char *)DefaultWelcomeMesg;

#ifdef __USE_MSW_PRINTING__
  // Default values for wxWindows MSW Printing
  if(mswp_lines_per_page > 0) {
    MSWPrintSetup->lines_per_page = mswp_lines_per_page;
    MSWPrintSetup->prev_lines_per_page = mswp_lines_per_page;
    MSWPrintSetup->default_lines_per_page = mswp_lines_per_page;
  }

  if(mswp_cell_length > 0) {
    MSWPrintSetup->cell_length = mswp_cell_length;
    MSWPrintSetup->prev_cell_length = mswp_cell_length;
    MSWPrintSetup->default_cell_length = mswp_cell_length;
  }

  if(mswp_font_size > 0) {
    MSWPrintSetup->font_size = mswp_font_size;
    MSWPrintSetup->prev_font_size = mswp_font_size;
    MSWPrintSetup->default_font_size = mswp_font_size;
  }

  if(mswp_left_margin > 0) {
    MSWPrintSetup->lr_margin = mswp_left_margin;
    MSWPrintSetup->prev_lr_margin = mswp_left_margin;
    MSWPrintSetup->default_lr_margin = mswp_left_margin;
  }

  if(mswp_orientation != 0) {
    if(strcmp(mswp_orientation, "PORTRAIT") == 0) {
      MSWPrintSetup->orientation = 1;
      MSWPrintSetup->prev_orientation = 1;
      MSWPrintSetup->default_orientation = 1;
    }
    if(strcmp(mswp_orientation, "LANDSCAPE") == 0) {
      MSWPrintSetup->orientation = 0;
      MSWPrintSetup->prev_orientation = 0;
      MSWPrintSetup->default_orientation = 0;
    }
  }
  
  if(mswp_font != 0) {
    if(strcmp(mswp_font, "SWISS") == 0) {
      MSWPrintSetup->font = 0;
      MSWPrintSetup->prev_font = 0;
      MSWPrintSetup->default_font = 0;
    }
    if(strcmp(mswp_font, "ROMAN") == 0) {
      MSWPrintSetup->font = 1;
      MSWPrintSetup->prev_font = 1;
      MSWPrintSetup->default_font = 1;
    }
    if(strcmp(mswp_font, "DECORATIVE") == 0) {
      MSWPrintSetup->font = 2;
      MSWPrintSetup->prev_font = 2;
      MSWPrintSetup->default_font = 2;
    }
    if(strcmp(mswp_font, "MODERN") == 0) {
      MSWPrintSetup->font = 3;
      MSWPrintSetup->prev_font = 3;
      MSWPrintSetup->default_font = 3;
    }
    if(strcmp(mswp_font, "SCRIPT") == 0) {
      MSWPrintSetup->font = 4;
      MSWPrintSetup->prev_font = 4;
      MSWPrintSetup->default_font = 4;
    }
    if(strcmp(mswp_font, "SYSTEM") == 0) {
      MSWPrintSetup->font = 5;
      MSWPrintSetup->prev_font = 5;
      MSWPrintSetup->default_font = 5;
    }
  }

  for(int i = 0; i < mswpDocNameLen; i++) mswpDocumentName[i] = 0;
  if(mswp_doc_name != 0) 
    strcpy(mswpDocumentName, mswp_doc_name);
  else
    strcpy(mswpDocumentName, ProgramName);
#endif // __USE_MSW_PRINTING__

  CfgData->UnLoad(); // Unload the Config file from memory

  // Initialize global database pointer
  // NOTE: Index file and data file will share the same file name
  // with different file extensions. The data file will have a 
  // .pod extension and the index file will have a .btx extension.
  if(AdminRights == 1)
    DB = new POD(FileName, RWMode, 1, CacheSize);
  else
    DB = new POD(FileName, ROMode, 1, CacheSize);
  
  agrocery = new Grocery(DB); // Initialize global buffer pointer
  cgrocery = new Grocery(DB); // Initialize global buffer pointer
  dgrocery = new Grocery(DB); // Initialize global buffer pointer
  
  // Display the welcome message if it exists
  frame->textWin->LoadFile(WelcomeMesgFile);

  // Return the main frame window
  return frame;
}

MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h):
  wxFrame(frame, title, x, y, w, h)
{
  textWin = 0;
  apanel = 0;
  cpanel = 0;
  dpanel = 0;
  addText = 0;
  changeText = 0;
}

Bool MyFrame::OnClose(void)
// Define the behaviour for the frame closing
{
  // Close sub-frames
  if(APanelFrame) {
    APanelFrame->Close(TRUE);
    delete APanelFrame;
  }
  
  if(CPanelFrame) {
    CPanelFrame->Close(TRUE);
    delete CPanelFrame;
  }
  
  if(DPanelFrame) {
    DPanelFrame->Close(TRUE);
    delete DPanelFrame;
  }
  
  // Close main frame
  Show(FALSE);

  // Clean up Variable Block Database objects
  if(DB) delete DB;
  if(agrocery) delete agrocery;
  if(cgrocery) delete cgrocery;
  if(dgrocery) delete dgrocery;
  return TRUE;
}

void MyFrame::OnMenuCommand(int id)
// Intercept menu commands
{
  switch (id) {
    case FILE_QUIT:
      if (OnClose()) delete this;
      break;

    case FILE_VBDSTATS:
      textWin->Clear();
      *(textWin) << "----- Database file statistics -----" << "\n";
      VBDStats(textWin, DB->OpenDatabase());
      *(textWin) << "----- Index file statistics -----" << "\n";
      VBDStats(textWin, DB->OpenIndexFile());
      break;

    case FILE_EXPORT:
      ExportToASCII(*textWin);
      break;

    case FILE_IMPORT:
      ImportFromASCII(*textWin);
      break;

    case FILE_BACKUP:
      BackUp(*textWin);
      break;

    case FILE_MERGE:
      Merge(*textWin);
      break;

    case FILE_TEMPLATE:
      CreateTemplate(*textWin);
      break;

    case FILE_COMPARE_INDEX:
      CompareIndexFile(*textWin);
      break;
      
    case FILE_REBUILD_INDEX:
      RebuildIndexFile(*textWin);
      break;

    case EDIT_CUT:
      textWin->Cut();
      break;

    case EDIT_COPY:
      textWin->Copy();
      break;
      
    case EDIT_PASTE:
      textWin->Paste();
      break;

    case DB_ADD: 
      Add(*textWin);
      break;
      
    case DB_CHANGE:
      Change(*textWin);
      break;
      
    case DB_REMOVE:
      Remove(*textWin);
      break;

    case DB_DISPLAY_PURCHASING:
      display_all = 0;
      DisplayDB(*textWin);
      break;

    case DB_DISPLAY_ALL:
      display_all = 1;
      DisplayDB(*textWin);
      break;

    case DB_RESET_PURCHASING:
      SetPurchasing(*textWin, 'N');
      break;

    case DB_SET_ALL_PURCHASING:
      SetPurchasing(*textWin, 'Y');
      break;
      
    case FIND_BYNAME:
      FindBy(*textWin, KeyName, NAME);
      break;
      
    case FIND_BYBRAND:
      FindBy(*textWin, M2Name, BRAND);
      break;

    case FIND_BYSTORE:
      FindBy(*textWin, M3Name, STORE);
      break;

#ifdef __USE_MSW_PRINTING__
    case WXPRINT_PRINT_PURCHASING:
      {
	print_all = 0;
#ifdef wx_msw
      myApp.SetPrintMode(wxPRINT_WINDOWS);
#else 
      // Allow MSW style printing under UNIX
      myApp.SetPrintMode(wxPRINT_POSTSCRIPT);
#endif
	wxPrintData printData;

	if(MSWPrintSetup->orientation == 0) 
	  printData.SetOrientation(wxLANDSCAPE);
	else
	  printData.SetOrientation(wxPORTRAIT);
	
	wxPrinter printer(&printData);
	if(displaying_list) { 
	  int yn = wxMessageBox("Print display list objects?",
				"Program Message", wxYES_NO|wxCENTRE);
	  if(yn == wxYES) print_list = 1; else print_list = 0;
	}
	else
	  print_list = 0;

	wGrocPrint printout((char *)ProgramName);
	if (!printer.Print(this, &printout, TRUE))
	  wxMessageBox("The document was not printed.",
		       "Printing", wxOK);
	break;
      }

    case WXPRINT_PRINT_ALL:
      {
	print_all = 1;
#ifdef wx_msw
      myApp.SetPrintMode(wxPRINT_WINDOWS);
#else 
      // Allow MSW style printing under UNIX
      myApp.SetPrintMode(wxPRINT_POSTSCRIPT);
#endif
	wxPrintData printData;

	if(MSWPrintSetup->orientation == 0) 
	  printData.SetOrientation(wxLANDSCAPE);
	else
	  printData.SetOrientation(wxPORTRAIT);
	
	wxPrinter printer(&printData);
	if(displaying_list) { 
	  int yn = wxMessageBox("Print display list objects?",
				"Program Message", wxYES_NO|wxCENTRE);
	  if(yn == wxYES) print_list = 1; else print_list = 0;
	}
	else
	  print_list = 0;

	wGrocPrint printout((char *)ProgramName);
	if (!printer.Print(this, &printout, TRUE))
	  wxMessageBox("The document was not printed.",
		       "Printing", wxOK);
	break;
      }

    case WXPRINT_PREVIEW_ALL:
      {
	print_all = 1;
#ifdef wx_msw
      myApp.SetPrintMode(wxPRINT_WINDOWS);
#else 
      // Allow MSW style printing under UNIX
      myApp.SetPrintMode(wxPRINT_POSTSCRIPT);
#endif
	wxPrintData printData;
	if(MSWPrintSetup->orientation == 0) 
	  printData.SetOrientation(wxLANDSCAPE);
	else
	  printData.SetOrientation(wxPORTRAIT);

	if(displaying_list) {
	  int yn = wxMessageBox("Print display list objects?",
				"Program Message", wxYES_NO|wxCENTRE);
	  if(yn == wxYES) print_list = 1; else print_list = 0;
	}
	else
	  print_list = 0;
	
	// Pass two printout objects: for preview, and possible printing.
	wxPrintPreview *preview = \
	  new wxPrintPreview(new wGrocPrint((char *)ProgramName),
			     new wGrocPrint((char *)ProgramName),
			     &printData);

	if (!preview->Ok()) {
	  delete preview;
	  wxMessageBox("There was a problem previewing.", 
		       "Previewing", wxOK);
	  return;
	}

	wxPreviewFrame *frame = new wxPreviewFrame(preview,
						   this,
						   (char *)ProgramName,
						   100, 100, 600, 650);
	frame->Centre(wxBOTH);
	frame->Initialize();
	frame->Show(TRUE);
	break;
      }

    case WXPRINT_PREVIEW_PURCHASING :
      {
	print_all = 0;
#ifdef wx_msw
      myApp.SetPrintMode(wxPRINT_WINDOWS);
#else 
      // Allow MSW style printing under UNIX
      myApp.SetPrintMode(wxPRINT_POSTSCRIPT);
#endif
	wxPrintData printData;
	if(MSWPrintSetup->orientation == 0) 
	  printData.SetOrientation(wxLANDSCAPE);
	else
	  printData.SetOrientation(wxPORTRAIT);

	if(displaying_list) {
	  int yn = wxMessageBox("Print display list objects?",
				"Program Message", wxYES_NO|wxCENTRE);
	  if(yn == wxYES) print_list = 1; else print_list = 0;
	}
	else
	  print_list = 0;

	// Pass two printout objects: for preview, and possible printing.
	wxPrintPreview *preview = \
	  new wxPrintPreview(new wGrocPrint((char *)ProgramName),
			     new wGrocPrint((char *)ProgramName),
			     &printData);

	if (!preview->Ok()) {
	  delete preview;
	  wxMessageBox("There was a problem previewing.", 
		       "Previewing", wxOK);
	  return;
	}

	wxPreviewFrame *frame = new wxPreviewFrame(preview,
						   this,
						   (char *)ProgramName,
						   100, 100, 600, 650);
	frame->Centre(wxBOTH);
	frame->Initialize();
	frame->Show(TRUE);
	break;
      }

    case WXPRINT_PRINTER_SETUP:
      {
#ifdef wx_msw
      myApp.SetPrintMode(wxPRINT_WINDOWS);
#else 
      // Allow MSW style printing under UNIX
      myApp.SetPrintMode(wxPRINT_POSTSCRIPT);
#endif
	wxPrintData printData;

	if(MSWPrintSetup->orientation == 0) 
	  printData.SetOrientation(wxLANDSCAPE);
	else
	  printData.SetOrientation(wxPORTRAIT);
      
	wxPrintDialog printerDialog(this, &printData);
	printerDialog.GetPrintData().SetSetupDialog(TRUE);
	printerDialog.Show(TRUE);
	break;
      }

    case WXPRINT_PAGE_SETUP:
      {
	int xpos=300; int ypos=300; int width=300; 
#ifdef wx_msw
	int height=250;
#else 
      // Allow MSW style printing under UNIX
	int height=350;
#endif

      wxPageDialog = new wxDialogBox(this, "Page Setup", TRUE,
				       xpos, ypos, width, height,
				       wxDEFAULT_DIALOG_STYLE,
				       "Page Setup");
	
	wxPageDialog->Centre();
	wxPageDialog->NewLine();
	LppSlider = new wxSlider(wxPageDialog, NULL, "Lines Per Page",
				 MSWPrintSetup->lines_per_page,
				 1, 200, 200);
	wxPageDialog->NewLine();
	CellSlider = new wxSlider(wxPageDialog, NULL, "Cell Length",
				  MSWPrintSetup->cell_length,
				  1, 50, 150);
	wxPageDialog->NewLine();
	FsSlider = new wxSlider(wxPageDialog, NULL, "Font Size   ",
				MSWPrintSetup->font_size,
				1, 50, 150);
	wxPageDialog->NewLine();
	LRMSlider = new wxSlider(wxPageDialog, NULL, "Left Margin",
				MSWPrintSetup->lr_margin,
				1, 20, 150);
	wxPageDialog->NewLine();
	wxMessage *om = new wxMessage(wxPageDialog,
	      "The Orientation must match the Printer Setup"); 
	wxPageDialog->NewLine();

	OrientationChoice = new wxChoice(wxPageDialog, NULL, "Orientation",
					 -1, -1, -1, -1, 2, OrChoiceStrings);

	OrientationChoice->SetSelection(MSWPrintSetup->orientation);
	wxPageDialog->NewLine();
	
	FontChoice = new wxChoice(wxPageDialog, NULL, "Font",
				  -1, -1, -1, -1, 6, FontChoiceStrings);
	FontChoice->SetSelection(MSWPrintSetup->font);
	wxPageDialog->NewLine();
	
        btn_pdb_close = new wxButton(wxPageDialog, (wxFunction)&page_btn_proc,
				     "Close", 10, -1, -1, -1);
        btn_pdb_close->SetClientData((char*)PAGE_DIALOG_BUTTON_CLOSE);

	btn_pdb_accept = new wxButton(wxPageDialog, (wxFunction)&page_btn_proc,
				     "Accept", 60, -1, -1, -1);
	btn_pdb_accept->SetClientData((char*)PAGE_DIALOG_BUTTON_ACCEPT);
	
	btn_pdb_cancel = new wxButton(wxPageDialog, (wxFunction)&page_btn_proc,
				     "Cancel", 118, -1, -1, -1);
	btn_pdb_cancel->SetClientData((char*)PAGE_DIALOG_BUTTON_CANCEL);
	
	btn_pdb_default = new wxButton(wxPageDialog,
				       (wxFunction)&page_btn_proc,
				       "Default", 175, -1, -1, -1);
	btn_pdb_default->SetClientData((char*)PAGE_DIALOG_BUTTON_DEFAULT);

	wxPageDialog->Show(TRUE);
	break;
      }
#endif //  __USE_MSW_PRINTING__
    
    case PRINT_POSTSCRIPT_PURCHASING:
      ps_print_all = 0;
      PostScriptPrint(*textWin);
      break;

    case PRINT_POSTSCRIPT_ALL:
      ps_print_all = 1;
      PostScriptPrint(*textWin);
      break;

    case PRINT_ASCII_PURCHASING:
      as_print_all = 0;
      ASCIIPrintAll(*textWin);
      break;

    case PRINT_ASCII_ALL:
      as_print_all = 1;
      ASCIIPrintAll(*textWin);
      break;

    case VIEW_CLEAR:
      Clear(*textWin);
      break;

    case VIEW_TOTAL_PURCHASING :
      view_all = 0;
      ViewTotals(*textWin);
      break;

    case VIEW_TOTAL_ALL :
      view_all = 1;
      ViewTotals(*textWin);
      break;

    case HELP_ABOUT: {
      char *s = VerNumber();
      UString mesg(ProgramName);
      mesg = mesg + "\nVersion Number " + s;
      wxMessageBox(mesg.c_str(), "About", wxOK|wxCENTRE);
      break;
    }
    
    case HELP_USERLEVEL: {
      UString mesg;
      if(AdminRights) {
	mesg = mesg + "You have Admin User Privileges\n" + \
	  "As defined in the " + DefaultCfgFile + " config file\n";
	wxMessageBox(mesg.c_str(), "Access Level", wxOK|wxCENTRE);
      }
      else {
	mesg = mesg + "You do not have Admin User Privileges\n" + \
	  "As defined in the " + DefaultCfgFile + " config file\n";
	wxMessageBox(mesg.c_str(), "Access Level", wxOK|wxCENTRE);
      }
      break;
    }
  }
}

void MyTextWindow::OnChar(wxKeyEvent& event)
{
  if(event.keyCode == CONTROL('x')) {
    Cut(); 
    frame->SetStatusText("Cut text and copied to clipboard");
  }

  if(event.keyCode == CONTROL('c')) {
    Copy(); 
    frame->SetStatusText("Copied text from clipboard");
  }

  if(event.keyCode == CONTROL('v')) {
    Paste(); 
    frame->SetStatusText("Pasted text from clipboard");
  }

  wxTextWindow::OnChar(event);  // Process the default behavior
}

void MyFrame::OnSize(int w, int h)
{
  wxFrame::OnSize(w, h);
}

MyText::MyText(wxPanel *parent, wxFunction func, char *label,
		     char *value, int x, int y, int width, int height ,
		     long style, char *name) :
  wxText(parent, func, label, value,x, y, width, height , style, name)
{
  item_name = brand = store = price = quantity = purchasing = line_total = 0; 
}

MyText::~MyText()
{
  if(item_name) delete item_name;
  if(brand) delete brand;
  if(store) delete store;
  if(price) delete price;
  if(quantity) delete quantity;
  if(purchasing) delete purchasing;
  if(line_total) delete line_total;
}

void Clear(MyTextWindow& textWin)
{
  textWin.Clear();
}

char *VerNumber()
// Version number for this windows program. Edit the version.h
// file to change this program's version number
{
 char sbuffer[sbuffer_len];
 sprintf(sbuffer, "%.3f", VersionNumber); 
 int len = strlen(sbuffer);
 char *s = new char[len+1];
 s[len+1] = '\0';
 strcpy(s, sbuffer);
 return s;
}

void text_proc(wxText &but, wxCommandEvent &event)
{
  // Nothing to do
}

void AddBtnProc(wxButton& but, wxCommandEvent& event)
{
  UString sbuf; // Character string input buffer
  long tag = (long)but.GetClientData() ;
  double price;
  long quantity;
  int i, rv;
  char purchasing;
  char sbuffer[sbuffer_len];
  FLOAT64 line_total;
  
  if(tag == ADD_BUTTON_ADD) {
    UString item_name;
    UString brand;
    UString store;

    item_name = frame->addText->item_name->GetValue();
    brand = frame->addText->brand->GetValue();
    store = frame->addText->store->GetValue();
    price = atof(frame->addText->price->GetValue());
    quantity = atoi(frame->addText->quantity->GetValue());
    rv = add_PurchasingChoice->GetSelection();
    
    // Remove any leading space from the key name entry
    int offset = item_name.Find(" ");
    if(offset == 0) item_name.DeleteAt(offset, 1);
    
    if(item_name == "") { // Check for a key name valid input
      sbuf = sbuf + "Invalid " + KeyName + " input!";
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      return;
    }

    Grocery grocery(DB);
    grocery.SetName(item_name);
    FAU addr = grocery.FindObject();
    
    if(addr) {
      sbuf = sbuf + "An Entry for " + item_name +  " already exists!";
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      return;
    }

    agrocery->SetName(item_name.c_str());
    agrocery->SetBrand(brand.c_str());
    agrocery->SetStore(store.c_str());
    agrocery->SetPrice(price);
    agrocery->SetQuantity(quantity);

    if(rv == 0)
      agrocery->SetPurchasing('N');
    else
      agrocery->SetPurchasing('Y');

    line_total = agrocery->GetPrice() * agrocery->GetQuantity();
    agrocery->SetLineTotal(line_total);
    
    addr = agrocery->AddObject(0); // Write the object to the file

    if(!addr) {
      sbuf = sbuf + "Could not add Entry for " + agrocery->GetName();
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      return;
    }

    sbuf = sbuf + "Added Entry for " + agrocery->GetName();
    frame->textWin->WriteText(sbuf.c_str());
    frame->textWin->WriteText("\n");
    ClearAddPanel();

    if(displaying_list) {
      InMemCopy inmemcopy(agrocery->GetName(), agrocery->ObjectAddress(),
			  agrocery->GetClassID());
      dllist->StoreNode(inmemcopy);
    }
  }
  
  if(tag == ADD_BUTTON_CANCEL) {
    ClearAddPanel();
    return;
  }

  if(tag == ADD_BUTTON_CLOSE) {
    APanelFrame->Show(FALSE);
    return;
  }

  if(tag == ADD_BUTTON_REVERT) {
    if(agrocery->GetName())
      frame->addText->item_name->SetValue(agrocery->GetName());
      if(agrocery->GetBrand())
	frame->addText->brand->SetValue(agrocery->GetBrand());
      if(agrocery->GetStore())
	frame->addText->store->SetValue(agrocery->GetStore());
      price = agrocery->GetPrice();
      sprintf(sbuffer, "%.2f", price);
      frame->addText->price->SetValue(sbuffer);
      quantity = agrocery->GetQuantity();
      for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
      sprintf(sbuffer, "%d", quantity);
      frame->addText->quantity->SetValue(sbuffer);
      purchasing = agrocery->GetPurchasing();
      if(purchasing == 'N' || purchasing == 'n')
	add_PurchasingChoice->SetSelection(0);
      else
	add_PurchasingChoice->SetSelection(1);
    return;
  }
}

void ClearAddPanel()
{
  frame->addText->item_name->SetValue("");
  frame->addText->brand->SetValue("");
  frame->addText->store->SetValue("");
  frame->addText->price->SetValue("");
  frame->addText->quantity->SetValue("");
  add_PurchasingChoice->SetSelection(0);
}

void Add(MyTextWindow &textWin)
{
  if(!AdminRights) {
      wxMessageBox("You do not have Admin User Privileges\n",
		   "Program Message", wxOK|wxCENTRE);
    return;
  }
    
  textWin.Clear();
  textWin << "Adding objects to database..." << "\n";
  textWin << "\n";
  
  
  APanelFrame->Show(TRUE); // Show the Add Panel Frame
}

void ChBtnProc(wxButton& but, wxCommandEvent& event)
{
  UString sbuf, item_name; // Character input string buffer
  long tag = (long)but.GetClientData() ;
  double price, line_total;
  long quantity;
  int i, rv, offset;
  char purchasing;
  char sbuffer[sbuffer_len];
  
  if(tag == CHANGE_BUTTON_SH) {  
    item_name = frame->changeText->item_name->GetValue();

    // Remove any leading space from the key name entry
    offset = item_name.Find(" ");
    if(offset == 0) item_name.DeleteAt(offset, 1);
    
    if(item_name == "") { // Check for a key name valid input
      sbuf = sbuf + "Invalid " + KeyName + " input!";
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      btn_ch_cm->Enable(0);
      return;
    }
    
    Grocery grocery(DB);
    grocery.SetName(item_name);
    FAU addr = grocery.FindObject();

    if(!addr) {
      sbuf = sbuf + "Could not find an entry for " + item_name;
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      btn_ch_cm->Enable(0);
      return;
    }

    cgrocery->Copy(grocery);
    
    if(cgrocery->GetBrand())
      frame->changeText->brand->SetValue(cgrocery->GetBrand());
    if(cgrocery->GetStore())
      frame->changeText->store->SetValue(cgrocery->GetStore());
    price = cgrocery->GetPrice();
    sprintf(sbuffer, "%.2f", price);
    frame->changeText->price->SetValue(sbuffer);
    quantity = cgrocery->GetQuantity();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%d", quantity);
    frame->changeText->quantity->SetValue(sbuffer);
    purchasing = cgrocery->GetPurchasing();
    if(purchasing == 'N' || purchasing == 'n')
      ch_PurchasingChoice->SetSelection(0);
    else
      ch_PurchasingChoice->SetSelection(1);
    line_total = cgrocery->GetLineTotal();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", line_total);
    frame->changeText->line_total->SetValue(sbuffer);
    frame->changeText->line_total->SetEditable(FALSE);
    btn_ch_cm->Enable(1); // Enable the commit button
  }

  if(tag == CHANGE_BUTTON_CM) {
    item_name = frame->changeText->item_name->GetValue();

    // Remove any leading space from the key name entry
    offset = item_name.Find(" ");
    if(offset == 0) item_name.DeleteAt(offset, 1);
    
    if(item_name == "") { // Check for a key name valid input
      sbuf = sbuf + "Invalid " + KeyName + " input!";
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      btn_ch_cm->Enable(0);
      return;
    }

    Grocery changed(DB); // Buffer used to store changes
    changed.SetName(frame->changeText->item_name->GetValue());
    changed.SetBrand(frame->changeText->brand->GetValue());
    changed.SetStore(frame->changeText->store->GetValue());
    price = atof(frame->changeText->price->GetValue());
    changed.SetPrice(price);
    quantity = atoi(frame->changeText->quantity->GetValue());
    changed.SetQuantity(quantity);
    rv = ch_PurchasingChoice->GetSelection();
    if(rv == 0)
      changed.SetPurchasing('N');
    else
      changed.SetPurchasing('Y');
    FLOAT64 ltotal = changed.GetPrice() * changed.GetQuantity();
    changed.SetLineTotal(ltotal);

    // Disable the commit button after changing the object
    btn_ch_cm->Enable(0);

    if(changed.FullCompare(*cgrocery)) { // The object has not changed
      sbuf = sbuf + "No changes to commit for " + cgrocery->GetName();
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      ClearChangePanel();

      if(displaying_list) { //Objects are being displayed
	if(!dllist->IsEmpty()) DPanelFrame->Show(TRUE);
      }
      btn_ch_cm->Enable(0);
      return;
    }

    UString key_name(changed.GetName());
    UString old_key_name(cgrocery->GetName());

    // If the key name is changed, ensure no duplicates are added
    if(key_name != old_key_name) {
      if((__LWORD__)changed.FindObject()) {
	sbuf = sbuf + "An Entry for " + key_name + " already exists!";
	frame->textWin->WriteText(sbuf.c_str());
	frame->textWin->WriteText("\n");
	Error->Message(sbuf.c_str());
	if(displaying_list) { // Objects are being displayed
	  if(!dllist->IsEmpty()) DPanelFrame->Show(TRUE);
	}
	btn_ch_cm->Enable(0);
	return;
      }
    }

    FAU oa = cgrocery->ObjectAddress(); // Record address before deleting
    cgrocery->DeleteObject(); // Remove the object  
    changed.AddObject(0); // Add the changed object back to the database

    sbuf = sbuf + "Commited changes for " + changed.GetName();
    frame->textWin->WriteText(sbuf.c_str());
    frame->textWin->WriteText("\n");
    ClearChangePanel();

    // Update any list entries for display panel
    if(displaying_list) { // Objects are being displayed
      if(dllist->IsEmpty()) {
	displaying_list = 0;
	DPanelFrame->Show(FALSE);
	return;
      }
      InMemCopy inmemcopy(cgrocery->GetName(), oa, cgrocery->GetClassID());
      DNode<InMemCopy> *prevptr = dllistptr;
      dllistptr = (DNode<InMemCopy> *)dllist->Find(inmemcopy);
      if(dllistptr) {
	dllistptr->Data.key = changed.GetName();	
	dllistptr->Data.object_address = changed.ObjectAddress();
	dllistptr->Data.class_id = changed.GetClassID();
	if(displaying_list) {
	  DPanelFrame->Show(TRUE);
	  DisplayObject(changed);
	}
      }
      else {
       dllistptr = prevptr;
       dllistptr->Data.key = changed.GetName();	
       dllistptr->Data.object_address = changed.ObjectAddress();
       dllistptr->Data.class_id = changed.GetClassID();
       if(displaying_list) {
	 DPanelFrame->Show(TRUE);
	 DisplayObject(changed);
       }
      }
    }
  }
    
  if(tag == CHANGE_BUTTON_CANCEL) {
    ClearChangePanel();
    if(displaying_list) { // Objects are being displayed
      if(!dllist->IsEmpty()) DPanelFrame->Show(TRUE);
    }
    btn_ch_cm->Enable(0);
    return;
  }

  if(tag == CHANGE_BUTTON_CLOSE) {
    ClearChangePanel();
    CPanelFrame->Show(FALSE);
    btn_ch_cm->Enable(0);
    return;
  }

  if(tag == CHANGE_BUTTON_REVERT) {
    if(cgrocery->GetName())
      frame->changeText->item_name->SetValue(cgrocery->GetName());
      if(cgrocery->GetBrand())
	frame->changeText->brand->SetValue(cgrocery->GetBrand());
      if(cgrocery->GetStore())
	frame->changeText->store->SetValue(cgrocery->GetStore());
      price = cgrocery->GetPrice();
      sprintf(sbuffer, "%.2f", price);
      frame->changeText->price->SetValue(sbuffer);
      quantity = cgrocery->GetQuantity();
      for(int i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
      sprintf(sbuffer, "%d", quantity);
      frame->changeText->quantity->SetValue(sbuffer);
      purchasing = cgrocery->GetPurchasing();
      if(purchasing == 'N' || purchasing == 'n')
	ch_PurchasingChoice->SetSelection(0);
      else
	ch_PurchasingChoice->SetSelection(1);
      line_total = cgrocery->GetPrice() * cgrocery->GetQuantity();
      for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
      sprintf(sbuffer, "%.2f", line_total);
      frame->changeText->line_total->SetValue(sbuffer);
      btn_ch_cm->Enable(0);
      return;
  }
}

void ClearChangePanel()
{
  frame->changeText->item_name->SetValue("");
  frame->changeText->brand->SetValue("");
  frame->changeText->store->SetValue("");
  frame->changeText->price->SetValue("");
  frame->changeText->quantity->SetValue("");
  ch_PurchasingChoice->SetSelection(0);
  frame->changeText->line_total->SetValue("");
}

void Change(MyTextWindow &textWin)
{
  if(!AdminRights) {
      wxMessageBox("You do not have Admin User Privileges\n",
		   "Program Message", wxOK|wxCENTRE);
    return;
  }

  textWin.Clear();
  textWin << "Changing objects in database..." << "\n";
  textWin << "\n";
  
  CPanelFrame->Show(TRUE); // Show the Add Panel Frame
}

void Remove(MyTextWindow &textWin, char *keyNM)
{
  if(!AdminRights) {
    wxMessageBox("You do not have Admin User Privileges\n",
		 "Program Message", wxOK|wxCENTRE);
    return;
  }

  textWin.Clear();
  textWin << "Removing object from database..." << "\n";

  UString item_name;
  UString comp;
  char *buf;
  
  if(!keyNM) {
    comp = comp + "Enter " + KeyName + " to Remove:";
    buf = wxGetTextFromUser(comp.c_str(), "String Input");

    if(!buf) {
      textWin << "Canceled." << "\n";
      return;
    }

    item_name = buf;
    
    // Remove any leading space from the key name entry
    int offset = item_name.Find(" ");
    if(offset == 0) item_name.DeleteAt(offset, 1);
  
    if(item_name == "") { // Check for a key name valid input
      comp.DeleteAt(0, comp.length());
      comp = comp + "Invalid " + KeyName + " input!";
      frame->textWin->WriteText(comp.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(comp.c_str());
      return;
    }
  }  
  else
    item_name = keyNM; // Key name memeber

  Grocery grocery(DB);
  grocery.SetName(item_name);

  FAU addr = grocery.FindObject();
  
  if(!addr) { 
    comp.DeleteAt(0, comp.length());
    comp = comp + "Could not find an entry for: " + item_name.c_str();
    Error->Message(comp.c_str());
    textWin << comp.c_str() << "\n";
    return;
  }

  int yn = wxMessageBox("Are you sure you want to remove",
		       "Program Message", wxYES_NO|wxCENTRE);

  if(yn == wxNO) return;
  

  InMemCopy listdata(grocery.GetName(), grocery.ObjectAddress(),
		     grocery.GetClassID());

  grocery.DeleteObject();
  textWin << "Removed entry for: " << item_name.c_str() << "\n";

  // Update display item panel list 
  if(displaying_list) {
    if(dllist->IsEmpty()) {
      displaying_list = 0;
      DPanelFrame->Show(TRUE);
      return;
    }
    DNode<InMemCopy> *dllptr = 0;
    dllptr = (DNode<InMemCopy> *)dllist->Find(listdata);
    if(dllptr) {
      dllist->Delete(dllptr);
      wxCommandEvent wxevent1(wxEVENT_TYPE_BUTTON_COMMAND);
      DsBtnProc(*btn_ds_nx, wxevent1); // Simulate pushing the Next Button
    }
  }
}

void DsBtnProc(wxButton& but, wxCommandEvent& event)
{
  char *sbuf = 0; // Character string buffer
  long tag = (long)but.GetClientData() ;
  Grocery grocery(DB);
  
  if(tag == DISPLAY_BUTTON_NX) {
    if(!dllistptr) return;
    if(!dllist) return;
    if(dllist->IsEmpty()) {
      ClearDisplayPanel();
      wxMessageBox("The display list is empty\n",
		   "Program Message", wxOK|wxCENTRE);
      return;
    }
    else
      dllistptr = dllistptr->GetNext();

    if(!dllist->IsHeader(dllistptr)) {
      grocery.ReadObject(dllistptr->Data.object_address);
      DisplayObject(grocery);
    }
    else {
      wxMessageBox("Reached the end of the list\n",
		   "Program Message", wxOK|wxCENTRE);
      dllistptr = dllist->GetFront();
      grocery.ReadObject(dllistptr->Data.object_address);
      DisplayObject(grocery);
    }
  }

  if(tag == DISPLAY_BUTTON_PREV) {
    if(!dllistptr) return;
    if(!dllist) return;
    if(dllist->IsEmpty()) {
      ClearDisplayPanel();
      wxMessageBox("The display list is empty\n",
		   "Program Message", wxOK|wxCENTRE);
      return;
    }
    else
      dllistptr = dllistptr->GetPrior();

    if(!dllist->IsHeader(dllistptr)) {
      grocery.ReadObject(dllistptr->Data.object_address);
      DisplayObject(grocery);
    }
    else {
      wxMessageBox("Reached the beginning of the list\n",
		   "Program Message", wxOK|wxCENTRE);
      dllistptr = dllist->GetBack();
      grocery.ReadObject(dllistptr->Data.object_address);
      DisplayObject(grocery);
    }
  }

  if(tag == DISPLAY_BUTTON_CHANGE) {
    if(!AdminRights) {
      wxMessageBox("You do not have Admin User Privileges\n",
		   "Program Message", wxOK|wxCENTRE);
      return;
    }

    if(dllist->IsEmpty()) {
      wxMessageBox("The display list is empty\n",
		   "Program Message", wxOK|wxCENTRE);
      return;
    }

    if(!dllist->IsEmpty()) {
      grocery.ReadObject(dllistptr->Data.object_address);
      cgrocery->Copy(grocery);
    }
    else
      cgrocery->Copy(*dgrocery);
    frame->changeText->item_name->SetValue(cgrocery->GetName());
    wxCommandEvent wxevent(wxEVENT_TYPE_BUTTON_COMMAND);
    ChBtnProc(*btn_ch_sh, wxevent); // Simulate pushing the Search Button
    CPanelFrame->Show(TRUE); // Show the Change Panel Frame
  }

  if(tag == DISPLAY_BUTTON_CLOSE) {
    ClearDisplayPanel();
    displaying_list = 0;
    DPanelFrame->Show(FALSE);
    CPanelFrame->Show(FALSE);
    if(!dllist->IsEmpty()) dllist->Clear();
    return;
  }

  if(tag == DISPLAY_BUTTON_REMOVE) {
    if(!AdminRights) {
      wxMessageBox("You do not have Admin User Privileges\n",
		   "Program Message", wxOK|wxCENTRE);
      return;
    }

    if(dllist->IsEmpty()) {
      wxMessageBox("The display list is empty\n",
		   "Program Message", wxOK|wxCENTRE);
      return;
    }
    else
      Remove(*frame->textWin, dgrocery->GetName());
  }
}

void ClearDisplayPanel()
{
  frame->displayText->item_name->SetValue("");
  frame->displayText->brand->SetValue("");
  frame->displayText->store->SetValue("");
  frame->displayText->price->SetValue("");
  frame->displayText->quantity->SetValue("");
  frame->displayText->purchasing->SetValue("");
  frame->displayText->line_total->SetValue("");
}

void DisplayObject(Grocery &grocery)
{
  dgrocery->Copy(grocery); // Initialize global display object
  double price, line_total;
  long quantity;
  int i;
  char purchasing;
  char sbuffer[sbuffer_len];

  if(grocery.GetName())
      frame->displayText->item_name->SetValue(grocery.GetName());
  if(grocery.GetBrand())
    frame->displayText->brand->SetValue(grocery.GetBrand());
  if(grocery.GetStore())
    frame->displayText->store->SetValue(grocery.GetStore());

  price = grocery.GetPrice();
  sprintf(sbuffer, "%.2f", price);
  frame->displayText->price->SetValue(sbuffer);
  
  quantity = grocery.GetQuantity();
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "%d", quantity);
  frame->displayText->quantity->SetValue(sbuffer);

  purchasing = grocery.GetPurchasing();
  if(purchasing == 'N' || purchasing == 'n')
    frame->displayText->purchasing->SetValue("No");
  else
    frame->displayText->purchasing->SetValue("Yes");

  line_total = grocery.GetLineTotal();
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "%.2f", line_total);
  frame->displayText->line_total->SetValue(sbuffer);

  displaying_list = 1;
  DPanelFrame->Show(TRUE);
}

void LoadGKeys(EntryKey &e)
// Visit function used to load the btree keys into memory
{
  Grocery grocery(DB);
  char purchasing;

  grocery.ReadObject(e.object_address);
  
  if(purchase_all) {
    InMemCopy inmemcopy(e.key, e.object_address, e.class_id);
    dllist->StoreNode(inmemcopy);
    grand_total += grocery.GetLineTotal();
    num_objects++;
  }
  else {
    purchasing = grocery.GetPurchasing();
    if(purchasing == 'Y' || purchasing == 'y') {
      InMemCopy inmemcopy(e.key, e.object_address, e.class_id);
      dllist->StoreNode(inmemcopy);
      grand_total += grocery.GetLineTotal();
      num_objects++;
    }
  }
}

int LoadIndexKeys(int load_all)
{
  grand_total = 0;
  num_objects = 0;
  purchase_all = load_all;
  dllist->Clear();
  BtreeWalker btw(DB->Index());
  btw.Sort(LoadGKeys);
  if(num_objects > 0) print_list = 1;
  return num_objects;
}

void ReOrderDisplayList()
{
  if(dllist->IsEmpty()) return;
  Grocery grocery(DB);
  char purchasing;
  dllistptr = dllist->GetFront();
  DLList<InMemCopy> tmp_list;
  grand_total = 0;
  num_objects = 0;
  
  while(!dllist->IsHeader(dllistptr)) { 
    grocery.ReadObject(dllistptr->Data.object_address);
    purchasing = grocery.GetPurchasing();
    if(purchasing == 'Y' || purchasing == 'y') {
      grand_total += grocery.GetLineTotal();
      num_objects++;
      tmp_list.StoreNode(dllistptr->Data);
    }
    dllistptr = dllistptr->GetNext();
  }

  dllist->Copy(tmp_list);
  
  // Always close the display panel after reordering the list
  if(displaying_list == 1) { 
    displaying_list = 0;
    DPanelFrame->Show(FALSE);
  }
}

void DisplayDB(MyTextWindow &textWin)
{
  Grocery grocery(DB);
  char purchasing;
  
  textWin.Clear();
    
  if(DB->RebuildIndex()) {
    textWin << "\n";
    textWin << "Displaying the database objects." << "\n";
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

  num_objects = LoadIndexKeys(display_all);
  
  if(display_all == 0) {
    textWin << "Displaying objects select for purchase only." << "\n";
    if(num_objects == 0) {
      textWin << "Nothing to display." << "\n";
      return;
    }
    textWin << "Displaying " << num_objects << " objects..." << "\n";
  }
  else {
    textWin << "Displaying all objects in the database." << "\n";
    if(num_objects == 0) {
      textWin << "Nothing to display." << "\n";
      return;
    }
    textWin << "Displaying " << num_objects << " objects..." << "\n";
  }

  dllistptr = dllist->GetFront();
  
  // Display the first object
  grocery.ReadObject(dllistptr->Data.object_address);	
  if(display_all == 0) {
    purchasing = grocery.GetPurchasing();
    if(purchasing == 'N' || purchasing == 'n') return;
  }
  DisplayObject(grocery);
}

void FindBy(MyTextWindow &textWin, const char *MemberName, GrocDBItem item)
{
  textWin.Clear();
  textWin << "Searching for object by " << (char *)MemberName << "\n";
  UString comp;

  comp = comp + "Enter complete string or use " + WildCard + \
    " for a wild card:";
  
  char *buf = wxGetTextFromUser(comp.c_str(), (char *)MemberName);

  if(!buf) {
    textWin << "Canceled." << "\n";
    return;
  }
  UString name(buf);
  
  // Remove any leading space from the key name entry
  unsigned offset = name.Find(" ");

  if(offset == 0) name.DeleteAt(offset, 1);
  
  if(name == "") { // Check for a key name valid input
    comp.DeleteAt(0, comp.length());
    comp = comp + "Invalid " + MemberName + " input!";
    frame->textWin->WriteText(comp.c_str());
    frame->textWin->WriteText("\n");
    Error->Message(comp.c_str());
    return;
  }

  if(DB->RebuildIndex()) {
    textWin << "\n";
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }
  
  Grocery grocery(DB);
  
  offset = name.Find(WildCard); // Look for wild card character
  if(offset == UString::NoMatch) { // No wild cards found
    grocery.SetName(buf);
    GrocDBSearch(grocery, item, name, textWin);
    return;
  }
  else 
    GrocDBSearch(grocery, item, name, textWin, WildCard);
}

void GrocDBSearch(Grocery &grocery, GrocDBItem item, UString &str,
		  MyTextWindow &textWin, const char *wildcard)
{
  textWin << "Searching for " << str.c_str() << "\n";
  int offset;
  offset = ObjectsFound = 0;

  if(wildcard != 0) {
    while(1) { // Remove the wild card characters from the string
      offset = str.Find(wildcard, offset);
      if (offset == UString::NoMatch) break;
      str.DeleteAt(offset, strlen(wildcard));
      offset++;
    }
  }
  
  // Ensure that the memory buffers and the file data
  // stay in sync during multiple file access.
  DB->Index()->TestTree();

  dllist->Clear();
  
  if(wildcard != 0)
    BtreeSearch(DB->Index(), item, grocery, str, 1);
  else
    BtreeSearch(DB->Index(), item, grocery, str);

  if(ObjectsFound == 0) {
    textWin << "No matches found." << "\n";
    return;
  }
  else
    num_objects = ObjectsFound;

  dllistptr = dllist->GetFront();

  grand_total = 0;
  while(!dllist->IsHeader(dllistptr)) {
    grocery.ReadObject(dllistptr->Data.object_address);
    grand_total += grocery.GetLineTotal();
    dllistptr = dllistptr->GetNext();
  }
  
  UString comp;
  int yn;
  
  if(ObjectsFound > 1) { // Found multiple matches
    textWin << "Found " << ObjectsFound << " matching." << "\n";
    char intbuf[sbuffer_len];
    sprintf(intbuf, "%d", ObjectsFound); 
    comp = comp + "Found " + intbuf + " matching.\nDisplay the objects?";
    yn = wxMessageBox(comp.c_str(), "Program Message", wxYES_NO|wxCENTRE);
  }
  else {
    textWin << "Found matching entry for: " << str.c_str() << "\n";
    comp = comp + "Found matching entry for: " + str +  \
      "\nDisplay the object?";
    yn = wxMessageBox(comp.c_str(), "Program Message", wxYES_NO|wxCENTRE);
  }

  if(yn == wxNO) {
    dllist->Clear();
    return;
  }
  
  // Display the first object
  dllistptr = dllist->GetFront();
  grocery.ReadObject(dllistptr->Data.object_address);
  DisplayObject(grocery);
}

void ExportToASCII(MyTextWindow &textWin)
{
  const char dchar = '\t';   // Text delimiter
  const char filter = '\t';  // Filter out tabs
  const char *Fill = "None"; // Fill character string
  double price, line_total;
  char purchasing;
  long quantity;
  int yn, i;
  char sbuffer[sbuffer_len];
  Grocery grocery(DB);
  
  textWin.Clear();
  textWin << "Exporting database to ASCII file delimited by tabs..." << "\n";
  
  if(DB->RebuildIndex()) {
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

  if(displaying_list) {
    yn = wxMessageBox("Export display list objects?",
			"Program Message", wxYES_NO|wxCENTRE);
  }
  else
    yn = wxNO;

  if(yn == wxNO) { LoadIndexKeys(); }
  if(dllist->IsEmpty()) { LoadIndexKeys(); }

  char *FileName = wxFileSelector("Export to file:", NULL, NULL, NULL, "*.*");

  if(FileName) {
#ifdef wx_msw
    wxUnix2DosFilename(FileName);
#endif
  }
  else {
    textWin << "Export canceled." << "\n";
    return; // No vaild file name
  }
  
  UString sbuf;
  
  if(VBDFile::Exists(FileName)) {
    sbuf = sbuf + "The " + FileName + " file already exists! Overwrite it?";
    yn = wxMessageBox(sbuf.c_str(), "File Exists", wxYES_NO|wxCENTRE);

    if(yn == wxNO) {
    textWin << "Export canceled." << "\n";
    return;
    }
  }
  
  ofstream stream(FileName, ios::out); // Open file and truncate it
  textWin << "Exporting database to: " << FileName << "\n";
  
  if(!stream) { // Could not open the stream
    sbuf.DeleteAt(0, sbuf.length());
    sbuf = sbuf + "Could not write to: " + FileName;
    textWin << sbuf.c_str() << "\n";
    Error->Message(sbuf.c_str());
    return; 
  }

  // Write the item bar to the file first
  ASPrint(KeyName, stream, strlen(KeyName));
  stream << dchar;

  ASPrint(M2Name, stream, strlen(M2Name));
  stream << dchar;
  
  ASPrint(M3Name, stream, strlen(M3Name));
  stream << dchar;
    
  ASPrint(M4Name, stream, strlen(M4Name));
  stream << dchar;
  
  ASPrint(M5Name, stream, strlen(M5Name));
  stream << dchar;

  ASPrint(M6Name, stream, strlen(M6Name));
  stream << dchar;

  ASPrint(M7Name, stream, strlen(M7Name));
  stream << asLineFeed;
  
  int exports = 0;
  dllistptr = dllist->GetFront();
  
  while(!dllist->IsHeader(dllistptr)) {
    grocery.ReadObject(dllistptr->Data.object_address);

    ASPrint(grocery.GetName(), filter, stream, strlen(grocery.GetName()));
    stream << dchar;

    if(!*grocery.GetBrand())
      ASPrint(Fill, stream, strlen(Fill));
    else
      ASPrint(grocery.GetBrand(), filter, stream, strlen(grocery.GetBrand()));
    stream << dchar;
    
    if(!*grocery.GetStore())
      ASPrint(Fill, stream, strlen(Fill));
    else
      ASPrint(grocery.GetStore(), filter, stream, strlen(grocery.GetStore()));
    stream << dchar;

    price = grocery.GetPrice();
    sprintf(sbuffer, "%.2f", price);
    ASPrint(sbuffer, stream, strlen(sbuffer));
    stream << dchar;
  
    quantity = grocery.GetQuantity();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%d", quantity);
    ASPrint(sbuffer, stream, strlen(sbuffer));
    stream << dchar;
    
    purchasing = grocery.GetPurchasing();
    if(purchasing == 'N' || purchasing == 'n')
      ASPrint("No", stream, strlen("No"));
    else
      ASPrint("Yes", stream, strlen("Yes"));
    stream << dchar;

    line_total = grocery.GetLineTotal();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", line_total);
    ASPrint(sbuffer, stream, strlen(sbuffer));
    
    stream << asLineFeed;
    exports++;
    dllistptr = dllistptr->GetNext();
  }

  textWin << "Exported " << exports << " objects." << "\n";
  stream.close();
}

void ImportFromASCII(MyTextWindow &textWin)
{
  if(!AdminRights) {
    wxMessageBox("You do not have Admin User Privileges\n",
		 "Program Message", wxOK|wxCENTRE);
    return;
  }
  
  char words[MAXWORDS][MAXWORDLENGTH];
  int num;
  const int MaxLine = 512;
  char LineBuffer[MaxLine];
  UString sbuf;
  const char dchar = '\t';  // Text delimiter

  // wxWindows single choice index box setup
  int ch = -1;
  const int CHArray = 4; 
  
  char *ch_array[CHArray] = {
    "Update this entry",
    "Skip this entry",
    "Update all entries without prompting",
    "Quit this import"
  };

  enum ICHOICES {
    UPDATE_ENTRY,
    UPDATE_SKIP,
    UPDATE_ALL,
    QUIT_THIS
  };
  
  textWin.Clear();
  textWin << "Importing database from ASCII file delimited by tabs..." << "\n";

  char *FileName = wxFileSelector("Import from file:",
				  NULL, NULL, NULL, "*.*");
  if(FileName) {
#ifdef wx_msw
    wxUnix2DosFilename(FileName);
#endif
  }
  else {
    textWin << "Import canceled." << "\n";
    return; // No vaild file name
  }

  ifstream stream(FileName, ios::in|ios::nocreate);
  textWin << "Importing database from: " << FileName << "\n";
  
  if(!stream) { // Could not open the stream
    textWin << "Could not open file: " << FileName << "\n";
    return; 
  }

  textWin << "Importing..." << "\n";

  SNode<UString> *sllistptr; // Linked list node pointer
  SLList<UString> sllist;    // Linked list

  while(!stream.eof())
  {
    // Read in file line by line
    stream.getline(LineBuffer, MaxLine);

    // Copy contents of the array to temporary holding buffer
    sbuf = LineBuffer;

    // Load file data into singly-linked list
    sllistptr = sllist.StoreNode(sbuf); 

    // Clear the string buffer
    sbuf.DeleteAt(0, sbuf.length());
    
    if(!sllistptr) { // Out of memory
      stream.close();
      sllist.Clear();
      return;
   }
  }
  stream.close();
  
  Grocery grocery(DB);
  Grocery grocery_a(DB);
  Grocery ob(DB);

  int linecount = 0;
  int imports = 0;
  int updates = 0;
  int updateall = 0;
  int ObjectItems = 7; // Number of class data members 

  sllistptr = sllist.GetFront();
  while(!sllist.IsHeader(sllistptr)) {
    if(parse(sllistptr->Data.c_str(), words, &num, dchar) == 1) {
      textWin << "Parse error!" << "\n";
      break;
    }

    linecount++;
    if(num != ObjectItems && (num != 0 && num != 1)) {
      textWin << "Error in " << FileName << " import file!" << "\n";
      if(num > ObjectItems) 
	textWin << "To many items on line: ";
      if(num < ObjectItems) 
	textWin << "Not enough items on line: ";
      textWin << linecount << "\n";
      int yn = wxMessageBox("Error in import file! Continue?",
			    "Program Message", wxYES_NO|wxCENTRE);
      if(yn == wxNO) {
	  sllist.Clear();
	  return;
      }
    }
    
    if(num == ObjectItems && *words[0] != 0) {
      grocery.SetName(words[0]);
      grocery.SetBrand(words[1]);
      grocery.SetStore(words[2]);
      grocery.SetPrice(atof(words[3]));
      grocery.SetQuantity(atoi(words[4]));
      char *pur_buf = words[5];
      if(strcmp(pur_buf, "No") == 0)
        grocery.SetPurchasing('N');
      else
        grocery.SetPurchasing('Y');
      grocery.SetLineTotal(atof(words[6]));
      
      grocery_a.SetName(grocery.GetName());
      FAU addr = grocery_a.FindObject();
      
      if(addr) {
	if(updateall == 0) {
	  sbuf.DeleteAt(0, sbuf.length());
          sbuf = sbuf + grocery.GetName() + " entry already exists.\n" + \
	    "Select your choice and click on OK";
	  ch = wxGetSingleChoiceIndex(sbuf.c_str(), "Importing Objects",
				      CHArray, ch_array, frame);
	}
	if(updateall == 1) ch = UPDATE_ALL;
	switch(ch) {
	  case UPDATE_ALL:
	    updateall = 1;
	    updates++;
            ob.Copy(grocery_a);
            if(grocery.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject(); 
            grocery.AddObject(0);
	    sbuf.DeleteAt(0, sbuf.length());
            sbuf = sbuf + "Updating entry for: " + grocery.GetName();
	    textWin << sbuf.c_str() << "\n";
	    frame->SetStatusText(sbuf.c_str());
	    break;
	  case UPDATE_SKIP:
	    break;
	  case UPDATE_ENTRY:
	    updates++;
            ob.Copy(grocery_a);
            if(grocery.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject(); 
            grocery.AddObject(0);
	    sbuf.DeleteAt(0, sbuf.length());
            sbuf = sbuf + "Updating entry for: " + grocery.GetName();
	    textWin << sbuf.c_str() << "\n";
	    frame->SetStatusText(sbuf.c_str());
	    break;
	  case QUIT_THIS:
	    textWin << "Import cancled." << "\n";
	    textWin << "Imported " << imports << " objects." << "\n";
	    if(updates) {
	      textWin << "Updated " << updates << " objects." << "\n";
	    }
	    sllist.Clear();
	    return;
	  default:
	    break;
	}
	if(updateall == 1) {
	  sbuf.DeleteAt(0, sbuf.length());
          sbuf = sbuf + "Updating entry for: " + grocery.GetName();
	  textWin << sbuf.c_str() << "\n";
	  frame->SetStatusText(sbuf.c_str());
	}
	else {
	  // Nothing to do
	}
      }
      else {
	// Ensure that the template does not get added to the file
	if(CaseICmp(KeyName, words[0]) != 0) {
          FAU addr = grocery.AddObject(0); // Write the object to the file
	  imports++;
	  if(!addr) {
            textWin << "Could not add:" << grocery.GetName()
		    << " to database" << "\n";
	    imports--;
	  }
	}
      }
    }
    sllistptr = sllistptr->GetNext();
  }

  textWin << "Imported " << imports << " objects." << "\n";
  if(updates) {
    textWin << "Updated " << updates << " objects." << "\n";
  }

  sllist.Clear();
}

void BackUp(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Backing up the database to another file..." << "\n";

  char *FileName = wxFileSelector("Backup to file:",
				  NULL, NULL, NULL, "*.*");
  if(FileName) {
#ifdef wx_msw
    wxUnix2DosFilename(FileName);
#endif
  }
  else {
    textWin << "Backup canceled." << "\n";
    return; // No vaild file name
  }
  
  UString sbuf;

  if(VBDFile::Exists(FileName)) {
    sbuf = sbuf + "The " + FileName + " file already exists!\nOverwrite it?";
    int yn = wxMessageBox(sbuf.c_str(), "File Exists", wxYES_NO|wxCENTRE);

    if(yn == wxNO) {
    textWin << "Export canceled." << "\n";
    return;
    }

    if(!wxRemoveFile(FileName)) {
      sbuf.DeleteAt(0, sbuf.length());
      sbuf = sbuf + "Could not write to: " + FileName;
      textWin << sbuf.c_str() << "\n";
      Error->Message(sbuf.c_str());
      return; 
    }
  }

  POD newdb(FileName, FileName);
  int items = 0;
  Grocery grocery(&newdb);
  Grocery grocery_old(DB);
  
  if(DB->RebuildIndex()) {
    textWin << "\n";
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

  CachePointer nxt(*DB->Index()->GetCache());
  BtreeWalkerb tw(DB->Index(), btINORDER);
  nxt = DB->Index()->GetRoot();

  while((__LWORD__)nxt != 0) {
    nxt = tw.Next();
    if((__LWORD__)nxt) {
      for(int i = 0; i < nxt->cnt; i++) {
	grocery_old.ReadObject(nxt->entry[i].object_address);
	grocery.Copy(grocery_old);
	grocery.AddObject(0);
	items++;
      }
    }
  }
  textWin << "Backed up " << items << " objects to " << FileName << "\n";
}

void Merge(MyTextWindow &textWin)
{
  if(!AdminRights) {
    wxMessageBox("You do not have Admin User Privileges\n",
		 "Program Message", wxOK|wxCENTRE);
    return;
  }
  
  // wxWindows single choice index box setup
  int ch = -1;
  const int CHArray = 4; 
  
  char *ch_array[CHArray] = {
    "Update this entry",
    "Skip this entry",
    "Update all entries without prompting",
    "Quit this merge"
  };

  enum ICHOICES {
    UPDATE_ENTRY,
    UPDATE_SKIP,
    UPDATE_ALL,
    QUIT_THIS
  };
  
  textWin.Clear();
  textWin << "Merging the contents of another database..." << "\n";

  char *FileName = wxFileSelector("Merge from file:", NULL, NULL, NULL, "*.*");
  if(FileName) {
#ifdef wx_msw
    wxUnix2DosFilename(FileName);
#endif
  }
  else {
    textWin << "Merge canceled." << "\n";
    return; // No vaild file name
  }
  
  POD newdb(FileName, FileName);
  Grocery grocery(&newdb);
  Grocery grocery_a(DB);
  Grocery ob(DB);
  FAU existing;
  FAU addr;
  UString sbuf;

  int imports = 0;
  int updates = 0;
  int updateall = 0;
  
  FAU oa;          // Object Address
  VBlockHeader vb;     // Variable Block Header
  ObjectHeader oh; // Object Header

  FAU vbdfileEOF = newdb.OpenDatabase()->GetEOF();
  
  addr = 0;
  addr = newdb.OpenDatabase()->FindFirstVB(addr); // Search the entire file

  if(addr == 0) {
    textWin << "No variable blocks found in file: " << FileName << "\n";
    return;
  }

  textWin << "Merging..." << "\n";

  while(1) { 
    if(addr >= vbdfileEOF) break;
    newdb.OpenDatabase()->Read(&vb, sizeof(VBlockHeader), addr);
    if(vb.CkWord == CheckWord) {
      if((__SBYTE__)vb.VBStatus == NormalVB) {
	oa = addr + sizeof(VBlockHeader);
	newdb.OpenDatabase()->Read(&oh, sizeof(ObjectHeader), oa);
	if(oh.ClassID == grocery.GetClassID()) { 
	  grocery.ReadObject(oa);
	  grocery_a.SetName(grocery.GetName());
	  existing = grocery_a.FindObject();
	  if(existing) {
	    if(updateall == 0) {
	      sbuf.DeleteAt(0, sbuf.length());
	      sbuf = sbuf +  grocery_a.GetName() + \
		" entry already exists.\n" + \
		"Select your choice and click on OK"; 
	      ch = wxGetSingleChoiceIndex(sbuf.c_str(), "Importing Objects",
					  CHArray, ch_array, frame);
	    }
	    if(updateall == 1) ch = UPDATE_ALL;
	    switch(ch) {
	      case UPDATE_ALL:
		updateall = 1;
		updates++;
		ob.Copy(grocery);
		if(grocery.FullCompare(ob)) break; // Object has not changed
		ob.DeleteObject(); 
		ob.Copy(grocery);
		ob.AddObject(0);
		sbuf.DeleteAt(0, sbuf.length());
		sbuf = sbuf  + "Updating entry for: " + grocery.GetName();
		textWin << sbuf.c_str() << "\n";
		frame->SetStatusText(sbuf.c_str());
		break;
	      case UPDATE_SKIP:
		break;
	      case UPDATE_ENTRY:
		updates++;
		ob.Copy(grocery);
		if(grocery.FullCompare(ob)) break; // Object has not changed
		ob.DeleteObject(); 
		ob.Copy(grocery);
		ob.AddObject(0);
		sbuf.DeleteAt(0, sbuf.length());
		sbuf = sbuf + "Updating entry for: " + grocery.GetName();
		textWin << sbuf.c_str() << "\n";
		frame->SetStatusText(sbuf.c_str());
		break;
	      case QUIT_THIS:
		textWin << "Merge cancled." << "\n";
		textWin << "Imported " << imports << " objects." << "\n";
		if(updates) {
		  textWin << "Updated " << updates << " objects." << "\n";
		}
		return;
	      default:
		break;
	    }
	    if(updateall == 1) {
	      sbuf.DeleteAt(0, sbuf.length());
	      sbuf = sbuf + "Updating entry for: " + grocery.GetName();
	      textWin << sbuf.c_str() << "\n";
	      frame->SetStatusText(sbuf.c_str());
	    }
	    else {
	      // Nothing to do
	    }
	  }
	  else {
	    grocery_a.Copy(grocery);
	    FAU rvaddr = grocery_a.AddObject(0); // Write object to the file
	    imports++;
	    if(!rvaddr) {
	      textWin << "Could not add:" << grocery.GetName()
		      << " to database" << "\n";
	      imports--;
	    }
	    
	  }
	}
	
      }
      addr = addr + vb.Length; // Goto the next variable block
    }
    else {
      addr = newdb.OpenDatabase()->VBSearch(addr); 
      if(!addr) break;
    }
  }
  
  textWin << "Imported " << imports << " objects." << "\n";
  if(updates) {
    textWin << "Updated " << updates << " objects." << "\n";
  }
}

void CreateTemplate(MyTextWindow &textWin)
{
  const char dchar = '\t';   // Text delimiter

  textWin.Clear();
  textWin << "Creating template file delimited by tabs..." << "\n";


  char *FileName = wxFileSelector("Template file to create:",
				  NULL, NULL, NULL, "*.*");
  if(FileName) {
#ifdef wx_msw
    wxUnix2DosFilename(FileName);
#endif
  }
  else {
    textWin << "Creating template canceled." << "\n";
    return; // No vaild file name
  }
  
  UString sbuf;
  
  if(VBDFile::Exists(FileName)) {
    sbuf = sbuf + "The " + FileName + " file already exists!\nOverwrite it?";
    int yn = wxMessageBox(sbuf.c_str(), "File Exists", wxYES_NO|wxCENTRE);

    if(yn == wxNO) {
    textWin << "Creating template canceled." << "\n";
    return;
    }
  }
  
  ofstream stream(FileName, ios::out); // Open file and truncate it
  textWin << "Creating template file: " << FileName << "\n";

  if(!stream) { // Could not open the stream
    sbuf.DeleteAt(0, sbuf.length());
    sbuf = sbuf  + "Could not write to: " + FileName;
    textWin << sbuf.c_str() << "\n";
    Error->Message(sbuf.c_str());
    return; 
  }

  ASPrint(KeyName, stream, strlen(KeyName));
  stream << dchar;
  
  ASPrint(M2Name, stream, strlen(M2Name));
  stream << dchar;

  ASPrint(M3Name, stream, strlen(M3Name));
  stream << dchar;

  ASPrint(M4Name, stream, strlen(M4Name));
  stream << dchar;

  ASPrint(M5Name, stream, strlen(M5Name));
  stream << dchar;

  ASPrint(M6Name, stream, strlen(M6Name));
  stream << dchar;

  ASPrint(M7Name, stream, strlen(M7Name));
  stream << asLineFeed;
  
  textWin << "Finished." << "\n";
  stream.close();
}

void ASCIIPrintAll(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Printing database to ASCII file..." << "\n";
  int yn;
  
  if(DB->RebuildIndex()) {
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

  if(displaying_list) {
    yn = wxMessageBox("Print display list objects?",
		      "Program Message", wxYES_NO|wxCENTRE);
  }
  else
    yn = wxNO;

  if(yn == wxNO) LoadIndexKeys(as_print_all);
  if(dllist->IsEmpty()) LoadIndexKeys(as_print_all);
  if(yn == wxYES && as_print_all == 0) ReOrderDisplayList();
  
  char *FileName = wxFileSelector("Print database to file:",
				  NULL, NULL, NULL, "*.*");
  if(FileName) {
#ifdef wx_msw
    wxUnix2DosFilename(FileName);
#endif
  }
  else {
    textWin << "ASCII print canceled." << "\n";
    return; // No vaild file name
  }
  
  UString sbuf;
  
  if(VBDFile::Exists(FileName)) {
    sbuf = sbuf + "The " + FileName + " file already exists!\nOverwrite it?";
    yn = wxMessageBox(sbuf.c_str(), "File Exists", wxYES_NO|wxCENTRE);

    if(yn == wxNO) {
      textWin << "ASCII print canceled." << "\n";
      return;
    }
  }
  
  ofstream stream(FileName, ios::out); // Open file and truncate it
  textWin << "Printing to: " << FileName << "\n";
  
  if(!stream) { // Could not open the stream
    sbuf.DeleteAt(0, sbuf.length());
    sbuf = sbuf + "Could not write to: " + FileName;
    textWin << sbuf.c_str() << "\n";
    Error->Message(sbuf.c_str());
    return; 
  }

  textWin << "Printing..." << "\n";
  Grocery grocery(DB);
  PrintItemBar(stream);
  dllistptr = dllist->GetFront();

  while(!dllist->IsHeader(dllistptr)) {
    grocery.ReadObject(dllistptr->Data.object_address);
    PrintLineByLine(grocery, stream);
    dllistptr = dllistptr->GetNext();
  }
  
  textWin << "Finished." << "\n";
  stream.close();
}

void PrintItemBar(ofstream &stream)
{
  int i = 0;
  ASPrint(KeyName, stream, StringOffset+1);
  ASPrint(M2Name, stream, StringOffset+1);
  ASPrint(M3Name, stream, StringOffset+1);
  ASPrint(M4Name, stream, StringOffset+1);
  ASPrint(M5Name, stream, StringOffset);
  ASPrint(M7Name, stream, StringOffset+1);
  stream << asLineFeed;
  i = 0;
  while (i++ < PrintColsSpec-1) stream << '=';
  stream << asLineFeed;
}

void PrintLineByLine(Grocery &grocery, ofstream &stream)
{
  int i;
  double price, line_total;
  long quantity;
  char sbuffer[sbuffer_len];
  
  ASPrint(grocery.GetName(), stream, StringOffset+1);
  ASPrint(grocery.GetBrand(), stream, StringOffset+1);
  ASPrint(grocery.GetStore(), stream, StringOffset+1);

  price = grocery.GetPrice();
  sprintf(sbuffer, "%.2f", price);
  ASPrint(sbuffer, stream, StringOffset+1);

  quantity = grocery.GetQuantity();
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "%d", quantity);
  ASPrint(sbuffer, stream, StringOffset+1);

  line_total = grocery.GetLineTotal();
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "%.2f", line_total);
  ASPrint(sbuffer, stream, StringOffset+1);
  stream << asLineFeed;
  i = 0;
  while (i++ < PrintColsSpec-1) stream << '-';
  stream << asLineFeed;
}

void CompareIndexFile(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Comparing the index file to the data file..." << "\n";
  
  Grocery grocery(DB);
  int rv = grocery.CompareIndex();
  if(!rv) {
    textWin << "The index file does not match the data file!" << "\n";
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

  textWin << "The index file checks good." << "\n";
}

void RebuildIndexFile(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Rebuilding the index file..." << "\n";
  UString sbuf;
  
  char *FileName = wxFileSelector("Enter name for new index file:",
				  NULL, NULL, NULL, "*.*");
  if(FileName) {
#ifdef wx_msw
    wxUnix2DosFilename(FileName);
#endif
  }
  else {
    textWin << "Rebuild canceled." << "\n";
    return; // No vaild file name
  }

  if(VBDFile::Exists(FileName)) {
    sbuf = sbuf + "The " + FileName + " file already exists!\nOverwrite it?";
    int yn = wxMessageBox(sbuf.c_str(), "File Exists", wxYES_NO|wxCENTRE);

    if(yn == wxNO) {
    textWin << "Rebuild canceled." << "\n";
    return;
    }

    if(!wxRemoveFile(FileName)) {
      sbuf.DeleteAt(0, sbuf.length());
      sbuf = sbuf + "Could not write to: " + FileName;
      textWin << sbuf.c_str() << "\n";
      Error->Message(sbuf.c_str());
      return; 
    }
  }
  
  Grocery grocery(DB);
  int rv = grocery.RebuildIndexFile(FileName);
  
  if(!rv) {
    textWin << "The index file was not rebuilt!" << "\n";
    return;
  }

  textWin << "The index file was rebuilt." << "\n";
  textWin << "A new index file named:\n"
	  << FileName << " was created." << "\n";
}

void ViewTotals(MyTextWindow &textWin)
{
  textWin.Clear();
  if(view_all == 0)
    textWin << "Viewing total cost of objects selected for purchase." << "\n";
  else
    textWin << "Viewing total cost of all objects." << "\n";

  if(view_all == 0) {
    LoadIndexKeys(0);
  }
  else {
    LoadIndexKeys(1);
  }

  int i;
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "TOTAL BEFORE TAX = $%.2f", grand_total);
  textWin << "\n";
  textWin << sbuffer << "\n";

  if(sales_tax <= 0) {
    textWin << "\n";
    textWin << "No sales tax value set in config file." << "\n";
    textWin << "\n";
    return; 
  }

  double tax_percent = sales_tax / 100;
  double tax = tax_percent * grand_total;
  double tax_total = grand_total;
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "SALES TAX = $%.2f", tax);
  textWin << "\n";
  textWin << sbuffer << "\n";
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  tax_total += tax;
  sprintf(sbuffer, "GRAND TOTAL = $%.2f", tax_total);
  textWin << "\n";
  textWin << sbuffer << "\n";
  textWin << "\n";
}

void SetPurchasing(MyTextWindow &textWin, char purchasing)
{
  if(!AdminRights) {
      wxMessageBox("You do not have Admin User Privileges\n",
		   "Program Message", wxOK|wxCENTRE);
    return;
  }

  textWin.Clear();
  if(purchasing == 'Y' || purchasing == 'y')
    textWin << "Purchasing all objects in the database." << "\n";
  else
    textWin << "Setting all objects for purchase to no." << "\n";

  Grocery grocery(DB);
  
  CachePointer nxt(*DB->Index()->GetCache());
  BtreeWalkerb tw(DB->Index(), btLVLORDER);
  nxt = DB->Index()->GetRoot();

  while((__LWORD__)nxt != 0) {
    nxt = tw.Next();
    if((__LWORD__)nxt) {
      for(int i = 0; i < nxt->cnt; i++) {
	grocery.ReadObject(nxt->entry[i].object_address);
	grocery.SetPurchasing(purchasing);
	grocery.SaveChanges();
      }
    }
  }
}

int PrintPSItemBar(ofstream &stream, PostScriptDrv &psdrv,
		    int x_offset, int char_offset, int cell_len)
{
    psdrv.PrintLine((char *)KeyName, cell_len, stream);
    x_offset += char_offset;

    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M2Name, cell_len, stream);
    x_offset += char_offset;

    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M3Name, cell_len, stream);
    x_offset += char_offset;

    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M4Name, cell_len, stream);
    x_offset += char_offset;

    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M5Name, cell_len, stream);
    x_offset += char_offset;

    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M7Name, cell_len, stream);
    x_offset += char_offset;

  return x_offset;
}

void PostScriptPrint(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Printing database to PostScript file..." << "\n";
  int yn, i;
  
  if(DB->RebuildIndex()) {
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

  // Setup the default values for user configurable PostScript parameters
  int cell_len = 19;    
  double font_size = 7; 
  int orientation = 0;
  const int DocNameLen = 255;
  char psDocName[DocNameLen];
  for(i = 0; i < DocNameLen; i++) psDocName[i] = 0;
  strcpy(psDocName, ProgramName);

  PostScriptDrv::PSPaperSizes paper_code = PostScriptDrv::LETTER_SIZE;
  PostScriptDrv::PSFonts item_font = PostScriptDrv::COURIER_BOLD_OBLIQUE;
  PostScriptDrv::PSFonts cell_font = PostScriptDrv::COURIER;

  // Load the PostScript configuration from the config file
  char *CurrentCfgFile;
  char *CfgFile;
  Config *CfgData = new Config;
  int int_val = 0;
  double dfp_val = 0;
  char *str_val = 0;

  // Look for CfgFile name in environment
  if((CurrentCfgFile = getenv(EnvSetting)) == 0)
    CfgFile = (char *)DefaultCfgFile;
  else
    CfgFile = CurrentCfgFile;

  int FileStatus = CfgData->Load((char *)CfgFile);
  if(FileStatus) { // Found a valid config file
// *********************************************************** //
// Add all PostScript config file entries here
// *********************************************************** //
    int_val = CfgData->GetIntValue("CellLength");
    if(int_val > 0) cell_len = int_val;

    dfp_val = CfgData->GetDFPValue("FontSize");
    if(dfp_val > 0) font_size = dfp_val;

    str_val = CfgData->GetStrValue("Orientation");
    if(str_val != 0) {
      if(strcmp(str_val, "PORTRAIT") == 0) orientation = 0;
      str_val = 0;
    }
    
    str_val = CfgData->GetStrValue("PaperSize");
    if(str_val != 0) {
      if(strcmp(str_val, "LETTER") == 0)
	paper_code = PostScriptDrv::LETTER_SIZE;
      if(strcmp(str_val, "LEGAL") == 0)
	paper_code = PostScriptDrv::LEGAL_SIZE;
      if(strcmp(str_val, "TABLOID") == 0)
	paper_code = PostScriptDrv::TABLOID_SIZE;
      if(strcmp(str_val, "A3") == 0) paper_code = PostScriptDrv::A3_SIZE;
      if(strcmp(str_val, "A4") == 0) paper_code = PostScriptDrv::A4_SIZE;
      str_val = 0;
    }

    str_val = CfgData->GetStrValue("ItemBarFont");
    if(str_val != 0) {
      if(strcmp(str_val, "COURIER") == 0)
	item_font = PostScriptDrv::COURIER;
      if(strcmp(str_val, "COURIER_BOLD") == 0)
	item_font = PostScriptDrv::COURIER_BOLD;
      if(strcmp(str_val, "COURIER_OBLIQUE") == 0)
	item_font = PostScriptDrv::COURIER_OBLIQUE;
      if(strcmp(str_val, "COURIER_BOLD_OBLIQUE") == 0)
	item_font = PostScriptDrv::COURIER_BOLD_OBLIQUE;
      str_val = 0;
    }
    
    str_val = CfgData->GetStrValue("CellFont");
    if(str_val != 0) {
      if(strcmp(str_val, "COURIER") == 0)
	cell_font = PostScriptDrv::COURIER;
      if(strcmp(str_val, "COURIER_BOLD") == 0)
	cell_font = PostScriptDrv::COURIER_BOLD;
      if(strcmp(str_val, "COURIER_OBLIQUE") == 0)
	cell_font = PostScriptDrv::COURIER_OBLIQUE;
      if(strcmp(str_val, "COURIER_BOLD_OBLIQUE") == 0)
	cell_font = PostScriptDrv::COURIER_BOLD_OBLIQUE;
    }

    str_val = CfgData->GetStrValue("psDocumentName");
    if(str_val != 0) {
      for(i = 0; i < DocNameLen; i++) psDocName[i] = 0;
      strcpy(psDocName, str_val);
    }

// *********************************************************** //    
  }
  CfgData->UnLoad(); // Unload the Config file from memory

  if(displaying_list) {
    yn = wxMessageBox("Print display list objects?",
		      "Program Message", wxYES_NO|wxCENTRE);
  }
  else
    yn = wxNO;

  if(yn == wxNO) LoadIndexKeys(ps_print_all);
  if(dllist->IsEmpty()) LoadIndexKeys(ps_print_all);
  if(yn == wxYES && ps_print_all == 0) ReOrderDisplayList();
  
  char *FileName = wxFileSelector("Print database to PostScript file:",
				  NULL, NULL, NULL, "*.*");
  if(FileName) {
#ifdef wx_msw
    wxUnix2DosFilename(FileName);
#endif
  }
  else {
    textWin << "PostScript print canceled." << "\n";
    return; // No vaild file name
  }
  
  UString sbuf;
  
  if(VBDFile::Exists(FileName)) {
    sbuf = sbuf + "The " + FileName + " file already exists!\nOverwrite it?";
    int yn = wxMessageBox(sbuf.c_str(), "File Exists", wxYES_NO|wxCENTRE);

    if(yn == wxNO) {
      textWin << "PostScript print canceled." << "\n";
      return;
    }
  }
  
  ofstream stream(FileName, ios::out); // Open file and truncate it
  textWin << "Printing to: " << FileName << "\n";
  
  if(!stream) { // Could not open the stream
    sbuf.DeleteAt(0, sbuf.length());
    sbuf = sbuf + "Could not write to: " + FileName;
    textWin << sbuf.c_str() << "\n";
    Error->Message(sbuf.c_str());
    return; 
  }

  textWin << "Printing..." << "\n";

  PostScriptDrv psdrv;
  psdrv.Copies(1);
  
  // Setup the PostScript driver
  psdrv.SetPaperSize(paper_code);
  psdrv.SetFont(cell_font, font_size);
  psdrv.UseHeader();
  if(orientation == 1) psdrv.LandScapeMode();
  psdrv.SetDocumentName(psDocName);
  char time_buf[255]; // Buffer used to hold time/date string
  GetSystemTime(time_buf);
  psdrv.SetDateString(time_buf);
  psdrv.DocumentSetup();
  psdrv.Prologue(stream);
  psdrv.MediaSetup(stream);
  Grocery grocery(DB);
  double price, line_total;
  long quantity;
  char sbuffer[sbuffer_len];

  int char_offset = int((cell_len + 1) * psdrv.CharWidth());
  int x_offset;
  if(orientation == 0)
    x_offset = int(HEADER_OFFSET * PIXELS_PER_INCH);
  else
    x_offset = PRINTABLE_OFFSET_X;

  int lines = 4;
  psdrv.StartPage(psdrv.page_count + 1, stream);
  psdrv.row = psdrv.StartY();
  psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);

  psdrv.ChangeFont(stream, item_font, font_size);
  x_offset = PrintPSItemBar(stream, psdrv, x_offset, char_offset, cell_len);
  psdrv.ChangeFont(stream, cell_font, font_size);
  
  int line_points;
  if(orientation == 0)
    line_points = int(x_offset - (HEADER_OFFSET * PIXELS_PER_INCH));
  else
    line_points = x_offset - PRINTABLE_OFFSET_X;

  psdrv.row -= (int)psdrv.FontSize();
  lines++;
  psdrv.drawThickLine(stream, line_points, psdrv.StartX(), psdrv.row);
  psdrv.row -= THICK_LINE_WIDTH + psdrv.FontSize();
  lines ++;
  
  dllistptr = dllist->GetFront();  
  while(!dllist->IsHeader(dllistptr)) {
    grocery.ReadObject(dllistptr->Data.object_address);

    if(orientation == 0)
      x_offset = int(HEADER_OFFSET * PIXELS_PER_INCH);
    else
      x_offset = PRINTABLE_OFFSET_X;

    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(grocery.GetName(), cell_len, stream);
    x_offset += char_offset;

    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(grocery.GetBrand(), cell_len, stream);
    x_offset += char_offset;
    
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(grocery.GetStore(), cell_len, stream);
    x_offset += char_offset;
    
    price = grocery.GetPrice();
    sprintf(sbuffer, "%.2f", price);
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(sbuffer, cell_len, stream);
    x_offset += char_offset;
     
    quantity = grocery.GetQuantity();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%d", quantity);
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(sbuffer, cell_len, stream);
    x_offset += char_offset;
    
    line_total = grocery.GetLineTotal();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", line_total);
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(sbuffer, cell_len, stream);
    x_offset += char_offset;

    psdrv.row -= psdrv.FontSize();
    lines++;
    if(orientation == 0)
      line_points = int(x_offset - (HEADER_OFFSET * PIXELS_PER_INCH));
    else
      line_points = x_offset - PRINTABLE_OFFSET_X;
    psdrv.drawLine(stream, line_points, psdrv.StartX(), psdrv.row);
    psdrv.row -= LINE_WIDTH + psdrv.FontSize();
    lines++; // Increment the line count

    if(lines >= psdrv.LinesPerPage()) {
      psdrv.EndPage(stream);
      psdrv.StartPage(psdrv.page_count + 1, stream);
      if(orientation == 0)
	x_offset = int(HEADER_OFFSET * PIXELS_PER_INCH);
      else
	x_offset = PRINTABLE_OFFSET_X;
      psdrv.row = psdrv.StartY();
      lines = 4;
      psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);
      psdrv.ChangeFont(stream, item_font, font_size);
      x_offset = PrintPSItemBar(stream, psdrv, x_offset,
				char_offset, cell_len);
      psdrv.ChangeFont(stream, cell_font, font_size);
      if(orientation == 0)
	line_points = int(x_offset - (HEADER_OFFSET * PIXELS_PER_INCH));
      else
	line_points = x_offset - PRINTABLE_OFFSET_X;
      psdrv.row -= (int)psdrv.FontSize();
      lines++;
      psdrv.drawThickLine(stream, line_points, psdrv.StartX(), psdrv.row);
      psdrv.row -= THICK_LINE_WIDTH + psdrv.FontSize();
      lines ++;
    }
    dllistptr = dllistptr->GetNext();
  }
  psdrv.EndPage(stream);
  PostScriptPrintTotals(textWin, stream, psdrv);
  stream.close();
}  

void PostScriptPrintTotals(MyTextWindow &textWin, ofstream &stream,
			   PostScriptDrv &psdrv)
{
  psdrv.StartPage(psdrv.page_count + 1, stream);
  double tax_font_size = 15;
  psdrv.ChangeFont(stream, PostScriptDrv::COURIER_BOLD, tax_font_size);
  psdrv.row = psdrv.StartY();

  char sbuffer[sbuffer_len];
  psdrv.row -= tax_font_size * 2; // Offset the tax from the header
  sprintf(sbuffer, "TOTAL BEFORE TAX = $%.2f", grand_total);
  psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);
  psdrv.PrintLine(sbuffer, stream);

  if(sales_tax <= 0) {
    textWin << "Finished." << "\n";
    psdrv.EndPage(stream);
    psdrv.Epilogue(stream, psdrv.page_count);
    stream.close();
    return; 
  }

  double tax_percent = sales_tax / 100;
  double tax = tax_percent * grand_total;
  double tax_total = grand_total;
  
  int i;
  psdrv.row -= tax_font_size * 2;
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "SALES TAX = $%.2f", tax);
  psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);
  psdrv.PrintLine(sbuffer, stream);
  
  psdrv.row -= tax_font_size * 2;
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  tax_total += tax;
  sprintf(sbuffer, "GRAND TOTAL= $%.2f", tax_total);
  psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);
  psdrv.PrintLine(sbuffer, stream);

  psdrv.EndPage(stream);
  psdrv.Epilogue(stream, psdrv.page_count);
  textWin << "Finished." << "\n";
  stream.close();
}

#ifdef __USE_MSW_PRINTING__

void page_btn_proc(wxButton& but, wxCommandEvent& event)
// Button procedures for the MSW Page Setup Dialog box
{
  long tag = (long)but.GetClientData() ;

  if(tag == PAGE_DIALOG_BUTTON_CLOSE) {
    wxPageDialog->Show(FALSE);
    delete wxPageDialog;
  }

  if(tag == PAGE_DIALOG_BUTTON_ACCEPT) {
    MSWPrintSetup->lines_per_page = LppSlider->GetValue();
    MSWPrintSetup->cell_length = CellSlider->GetValue();
    MSWPrintSetup->font_size = FsSlider->GetValue();
    MSWPrintSetup->orientation = OrientationChoice->GetSelection();
    MSWPrintSetup->font = FontChoice->GetSelection();
    MSWPrintSetup->lr_margin = LRMSlider->GetValue();
    
    MSWPrintSetup->prev_lines_per_page = LppSlider->GetValue();
    MSWPrintSetup->prev_cell_length = CellSlider->GetValue();
    MSWPrintSetup->prev_font_size = FsSlider->GetValue();
    MSWPrintSetup->prev_orientation = OrientationChoice->GetSelection();
    MSWPrintSetup->prev_font = FontChoice->GetSelection();
    MSWPrintSetup->prev_lr_margin = LRMSlider->GetValue();
  }

  if(tag == PAGE_DIALOG_BUTTON_CANCEL) {
    LppSlider->SetValue(MSWPrintSetup->prev_lines_per_page);
    CellSlider->SetValue(MSWPrintSetup->prev_cell_length);
    FsSlider->SetValue(MSWPrintSetup->prev_font_size);
    OrientationChoice->SetSelection(MSWPrintSetup->prev_orientation);
    FontChoice->SetSelection(MSWPrintSetup->prev_font);
    LRMSlider->SetValue(MSWPrintSetup->prev_lr_margin);
  }

  if(tag == PAGE_DIALOG_BUTTON_DEFAULT) {
    LppSlider->SetValue(MSWPrintSetup->default_lines_per_page);
    CellSlider->SetValue(MSWPrintSetup->default_cell_length);
    FsSlider->SetValue(MSWPrintSetup->default_font_size);
    OrientationChoice->SetSelection(MSWPrintSetup->default_orientation);
    FontChoice->SetSelection(MSWPrintSetup->default_font);
    LRMSlider->SetValue(MSWPrintSetup->default_lr_margin);
  }
}

void wGrocPrint::SetFont(int style, int weight)
{
  switch(MSWPrintSetup->font) {
    case 0:
      printerFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxSWISS, style, weight);
      char_width = .6 * MSWPrintSetup->font_size;
      break;

    case 1:
      printerFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxROMAN, style, weight);
      char_width = .6 * MSWPrintSetup->font_size;
      break;

    case 2:
      printerFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxDECORATIVE,
						    style, weight);
      char_width = .6 * MSWPrintSetup->font_size;
      break;

    case 3:
      printerFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxMODERN, style, weight);
      char_width = .6 * MSWPrintSetup->font_size;
      break;
    
    case 4:
      printerFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxSCRIPT, style, weight);
      char_width = .6 * MSWPrintSetup->font_size;
      break;

    case 5:
      printerFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxDEFAULT, style, weight);
      char_width = .6 * MSWPrintSetup->font_size;
      break;

    default:
      printerFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxSWISS, style, weight);
      char_width = .6 * MSWPrintSetup->font_size;
      break;
  };
}

void wGrocPrint::SetItemBarFont(int style, int weight)
{
  switch(MSWPrintSetup->font) {
    case 0:
      itembarFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxSWISS, style, weight);
      break;

    case 1:
      itembarFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxROMAN, style, weight);
      break;

    case 2:
      itembarFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxDECORATIVE,
						    style, weight);
      break;

    case 3:
      itembarFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxMODERN, style, weight);
      break;
    
    case 4:
      itembarFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxSCRIPT, style, weight);
      break;

    case 5:
      itembarFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxDEFAULT, style, weight);
      break;

    default:
      itembarFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxSWISS, style, weight);
      break;
  };
}

void wGrocPrint::OnPreparePrinting()
// Called once by the framework before any other demands are made
// of the wxPrintout object. 
{
  SetFont();
  SetItemBarFont();
  SetHeaderFont(15, wxSWISS, wxNORMAL, wxBOLD);
}

Bool wGrocPrint::OnPrintPage(int page)
// Called by the framework when a page should be printed.
// Returning FALSE cancels the print job. The application
// can use wxPrintout::GetDC to obtain a device context to
// draw on.
{
  wxDC *dc = GetDC();
  if(dc) {
      DrawTextPage(dc, page);
    return TRUE;
  }
  else
    return FALSE;
}

Bool wGrocPrint::OnBeginDocument(int startPage, int endPage)
// Called by the framework at the start of document printing.
// OnBeginDocument is called once for every copy printed. 
// The base wxPrintout::OnBeginDocument must be called
// (and the return value checked) from within the overriden
// function, since it calls wxDC::StartDoc.
{
  if (!wxPrintout::OnBeginDocument(startPage, endPage))
    return FALSE; // Returns FALSE if the print job is canceled

  return TRUE;
}

Bool wGrocPrint::HasPage(int pageNum)
// Should be overriden to return TRUE if the document has this page,
// or FALSE if not. Returning FALSE signifies the end of the document.
// By default, HasPage behaves as if the document has only one page.
{
  // Calculate the number of pages in this document
  int minPage, maxPage, selPageFrom, selPageTo;
  GetPageInfo(&minPage, &maxPage, &selPageFrom, &selPageTo);
  return (pageNum == minPage) || (pageNum <= maxPage);
}

void wGrocPrint::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom,
			      int *selPageTo)
// Called by the framework to obtain information from the application
// about minimum and maximum page values that the user can select, and
// the required page range to be printed.
{
  if(DB->RebuildIndex()) { // The index files needs to be rebuilt
    *minPage = 1;
    *selPageFrom = 1;
    *selPageTo = 1;
    *maxPage = 1;
    return;
  }  

  if(print_list == 0) num_objects = LoadIndexKeys(print_all);
  if(dllist->IsEmpty()) num_objects = LoadIndexKeys(print_all);
  if(displaying_list == 1) {
    if(print_all == 0) ReOrderDisplayList();
  }
  
  dllistptr = dllist->GetFront();
  int lines = num_objects;
  int first_page = 1; // Always starting on page one
  last_page = 1;      // Default is the first page
  
  *minPage = first_page;     // Always starting on page one
  *selPageFrom = first_page; // Always starting on page one

  for(;;) {
    if(lines <= MSWPrintSetup->lines_per_page) break;
    lines = lines - MSWPrintSetup->lines_per_page;
    last_page++; // Increment the page count
  }

  last_page++; // Reserve a page to print the grand total
  
  *selPageTo = last_page;
  *maxPage = last_page;
}

void wGrocPrint::ScaleDC(wxDC *dc)
// Scale the DC so that the printout roughly represents the
// the screen scaling.
{
  // Get the page size 
  GetPageSizeMM(&page_width, &page_height);
  
    // Get the logical pixels per inch of screen and printer
  GetPPIScreen(&ppiScreenX, &ppiScreenY);
  GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);

  // Scale the DC so that the printout roughly represents the
  // the screen scaling.
  scale = (float)((float)ppiPrinterX/(float)ppiScreenX);

  // Calculate conversion factor for converting millimetres into
  // logical units. There are approx. 25.1 mm to the inch. There
  // are ppi device units to the inch. Therefore 1 mm corresponds
  // to ppi/25.1 device units.
  logUnitsFactor = (float)(ppiPrinterX / (scale * 25.1));

  // Check real page size in case it is reduced by print preview 
  int pageWidth, pageHeight;
  float w, h;
  dc->GetSize(&w, &h);
  GetPageSizePixels(&pageWidth, &pageHeight);
  
  // Do not change if printer pageWidth == current DC width
  float overallScale = scale * (float)(w/(float)pageWidth);
  dc->SetUserScale(overallScale, overallScale);
}

void wGrocPrint::DrawTextPage(wxDC *dc, int page)
// Write page by page with margins, header, and page numbers
{
  dc->SetFont(itembarFont); // Set the text font for the item bar
  ScaleDC(dc);
  
  start_x = (float)(logUnitsFactor * MSWPrintSetup->lr_margin);
  start_y = (float)(logUnitsFactor * MSWPrintSetup->tb_margin);

  int end = page * MSWPrintSetup->lines_per_page;  // Last line to write
  int start = end - MSWPrintSetup->lines_per_page; // First line to write

  float textW, textH; // Text width and height 
  float xpos = start_x; 
  float ypos = start_y;

  int char_offset = int((MSWPrintSetup->cell_length + 1) * char_width);
  float x_offset = xpos;

  if(page != last_page) { // Do not print item bar on last page
    PrintText(dc, (char *)KeyName, MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    dc->GetTextExtent((char *)KeyName, &textW, &textH);

    PrintText(dc, (char *)M2Name, MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;

    PrintText(dc, (char *)M3Name, MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    
    PrintText(dc, (char *)M4Name, MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    
    PrintText(dc, (char *)M5Name, MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;

    PrintText(dc, (char *)M7Name, MSWPrintSetup->cell_length,
	      x_offset, ypos); 

    // Do not draw a line past the member
    x_offset += int((strlen(M7Name) + 1) * char_width);
  }
  
  ypos += textH;
  dc->DrawLine(xpos , ypos, x_offset, ypos);
  ypos += mswpLINE_WIDTH;

  int lines = 0;
  Grocery grocery(DB);
  dc->SetFont(printerFont); // Set the text font for the objects
  
  for(int i = 0; i < start; i++) {
    if(!dllist->IsHeader(dllistptr)) 
      dllistptr = dllistptr->GetNext();
  }

  while(!dllist->IsHeader(dllistptr)) {
    grocery.ReadObject(dllistptr->Data.object_address);
    x_offset = xpos;
    ypos += mswpLINE_WIDTH;

    PrintText(dc, grocery.GetName(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    dc->GetTextExtent(grocery.GetName(), &textW, &textH);

    PrintText(dc, grocery.GetBrand(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
      
    PrintText(dc, grocery.GetStore(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    
    PrintText(dc, (double)grocery.GetPrice(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    
    PrintText(dc, (long)grocery.GetQuantity(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    
    PrintText(dc, (double)grocery.GetLineTotal(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 

    // Do not draw a line past the member
    x_offset += int((strlen(M7Name) + 1) * char_width);
    
    ypos += textH;
    ypos += mswpLINE_WIDTH;
    dc->DrawLine(xpos , ypos, x_offset, ypos);
    ypos += mswpLINE_WIDTH;
    lines++;
    if(lines >= MSWPrintSetup->lines_per_page) break;
    dllistptr = dllistptr->GetNext();
  }
  
  char time_buf[sbuffer_len]; // Buffer used to hold time/date string
  GetSystemTime(time_buf);    // Part of the PostScript Driver class
  WritePageHeader(dc, mswpDocumentName, time_buf);
  WritePageNumber(dc, page);

  // Print the grand total
  if(page == last_page) {
    dc->SetFont(headerFont);
    char sbuffer[sbuffer_len];
    xpos = start_x; ypos = start_y;

    sprintf(sbuffer, "TOTAL BEFORE TAX = $%.2f", grand_total);
    dc->GetTextExtent(sbuffer, &textW, &textH);
    ypos += textH * 2; // Offset text from the page header
    PrintText(dc, sbuffer, xpos, ypos);

    if(sales_tax <= 0) 
      return; // No sales tax info in the config file

    double tax_percent = sales_tax / 100;
    double tax = tax_percent * grand_total;
    double tax_total = grand_total;
    
    ypos += textH * 2;
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "SALES TAX = $%.2f", tax);
    dc->GetTextExtent(sbuffer, &textW, &textH);
    PrintText(dc, sbuffer, xpos, ypos);

    ypos += textH * 2; // Offest from header
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    tax_total += tax;
    sprintf(sbuffer, "GRAND TOTAL = $%.2f", tax_total);
    PrintText(dc, sbuffer, xpos, ypos);
  }
}

void wGrocPrint::SetHeaderFont(int size, int font, int style, int weight)
// Set the header and page number font type, size, and style
{
  headerFont = wxTheFontList->FindOrCreateFont(size, font, style, weight);
}
 
void wGrocPrint::WritePageHeader(wxDC *dc, char *doc_name, char *doc_date)
// Writes header on top of page. Margin units are in millimetres.
{
  dc->SetFont(headerFont);

  // Offset the margins to move text above the first line
  int leftMargin  = MSWPrintSetup->lr_margin / 2;
  int topMargin   = MSWPrintSetup->tb_margin / 2;
  int rightMargin = MSWPrintSetup->lr_margin / 2;

  float leftMarginLogical = (float)(logUnitsFactor * leftMargin);
  float topMarginLogical = (float)(logUnitsFactor * topMargin);
  float rightMarginLogical = (float)(logUnitsFactor* \
				     (page_width - rightMargin));

  float xExtentName, yExtentName, xExtentDate, yExtentDate, xPos, offset;
  dc->GetTextExtent(doc_name, &xExtentName, &yExtentName);
  if(doc_date != 0) { // Printing document name and date strings
    dc->GetTextExtent(doc_date, &xExtentDate, &yExtentDate);

    // Draw the document's name left justified
    PrintText(dc, doc_name, start_x, topMarginLogical);

    // Draw the document's date right justified
    if(MSWPrintSetup->orientation == 0) // Landscape printing
      offset = (logUnitsFactor * page_width) - \
       (logUnitsFactor * (MSWPrintSetup->lr_margin + mswpPRINTABLE_OFFSET_X));
    else
      offset = (logUnitsFactor * page_width) - \
	(logUnitsFactor * (MSWPrintSetup->lr_margin + mswpPRINTABLE_OFFSET_X));
      
    xPos = (float)(offset - xExtentDate);
    PrintText(dc, doc_date, xPos, topMarginLogical);
  }
  else { // Center the document's name
    xPos = (float)(((((page_width - leftMargin - rightMargin)/2.0)
		     +leftMargin)*logUnitsFactor) - (xExtentName/2.0));
    PrintText(dc, doc_name, xPos, topMarginLogical);
  }
}

void wGrocPrint::WritePageNumber(wxDC *dc, int pagenum)
// Writes page number on bottom of page. Margin units are in millimetres.
{
  dc->SetFont(headerFont);

  // Offset the margins to move text below the last line
  int leftMargin = MSWPrintSetup->lr_margin;
  int bottomMargin = MSWPrintSetup->tb_margin;
  int rightMargin = MSWPrintSetup->lr_margin;

  float leftMarginLogical = (float)(logUnitsFactor * leftMargin);

  float bottomMarginLogical = (float)(logUnitsFactor * (page_height - \
							bottomMargin));

  float rightMarginLogical = (float)(logUnitsFactor*(page_width - \
						     rightMargin));
  float xExtent, yExtent;
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "PAGE %d", pagenum);
  dc->GetTextExtent(sbuffer, &xExtent, &yExtent);
  float xPos = (float)(((((page_width - leftMargin - rightMargin)/2.0)
			 +leftMargin)*logUnitsFactor) - (xExtent/2.0));
  PrintText(dc, sbuffer, xPos, bottomMarginLogical);
}

void wGrocPrint::PrintText(wxDC *dc, char *s, int max_len,
			   float xpos, float ypos)
// Draw text in the device context.
{
  if(s == 0 || max_len <= 0) return;
  int slen = strlen(s);
  if(slen > max_len) {
    char *buf = new char[max_len];
    buf[max_len] = '\0';
    memmove(buf, s, max_len);
    dc->DrawText(buf, xpos, ypos);
    delete [] buf;
  }
  else
    dc->DrawText(s, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, char *s, float xpos, float ypos)
// Draw text in the device context.
{
  if(s == 0) return;
  dc->DrawText(s, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, char c, float xpos, float ypos)
{
  char s[1];
  s[0] = c;    // Copy char into a string buffer
  s[1] = '\0'; // Null terminate the string
  PrintText(dc, s, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, int i, int max_len,
			   float xpos, float ypos)
{
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "%d", i);
  PrintText(dc, sbuffer, max_len, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, long i, int max_len,
			   float xpos, float ypos)
{
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "%d", i);
  PrintText(dc, sbuffer, max_len, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, double i, int max_len,
			   float xpos, float ypos)
{
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "%.2f", i);
  PrintText(dc, sbuffer, max_len, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, float i, int max_len,
			   float xpos, float ypos)
{
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "%.2f", i);
  PrintText(dc, sbuffer, max_len, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, int i, float xpos, float ypos)
{
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "%d", i);
  PrintText(dc, sbuffer, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, long i, float xpos, float ypos)
{
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "%d", i);
  PrintText(dc, sbuffer, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, double i, float xpos, float ypos)
{
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "%.2f", i);
  PrintText(dc, sbuffer, xpos, ypos);
}

void wGrocPrint::PrintText(wxDC *dc, float i, float xpos, float ypos)
{
  char sbuffer[sbuffer_len];
  sprintf(sbuffer, "%.2f", i);
  PrintText(dc, sbuffer, xpos, ypos);
}

#endif // __USE_MSW_PRINTING__

// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //

