// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: winmain.cpp 
// C++ Compiler Used: MSVC40, HPUX CPP 10.24
// Produced By: Doug Gaer 
// File Creation Date: 12/16/1997 
// Date Last Modified: 11/04/1998 
// 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 index card file program designed to work with the
wxWindow GUI library version 1.68

- Modify "dbconfig.hpp" file to customize the user interface
- Modify "version.hpp" file to report changes and fixes
*/
// ----------------------------------------------------------- //
#include <stdlib.h> // Needed for getenv under HPUX CPP
#include "dbconfig.hpp"
#include "winmain.hpp"
#include "strutil.hpp"
#include "asprint.hpp"
#include "vbdstats.hpp"

// This struct was added to read Variable Block Headers because
// the Status member of VBHeader struct in the vbdfile.hpp 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
CCIndex *accindex = 0;       // Global data buffer used to add objects
CCIndex *cccindex = 0;       // Global data buffer used to change objects
CCIndex *dccindex = 0;       // Global data buffer used to display objects
const char *WildCard = "*";  // Used for wild card searches 

// 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 

wxButton *btn_ch_sh = 0;     // Object 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 

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;         // Cards per page slider
wxSlider *CellSlider = 0;        // Cell width slider
wxSlider *FsSlider = 0;          // Font size slider
wxSlider *LRMSlider = 0;         // Left and right margin slider
wxSlider *CsSlider = 0;          // Card spacing 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, "&Display", "Display all objects one a time");
  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");
  
  // Find menu
  wxMenu *find_menu = new wxMenu;
  find_menu->AppendSeparator();
  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_BYKEYNAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + M1Name; StringLen = strlen(M1Name);
  find_menu->Append(FIND_BYM1NAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + M2Name; StringLen = strlen(M2Name);
  find_menu->Append(FIND_BYM2NAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + M3Name; StringLen = strlen(M3Name);
  find_menu->Append(FIND_BYM3NAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + M4Name; StringLen = strlen(M4Name);
  find_menu->Append(FIND_BYM4NAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + M5Name; StringLen = strlen(M5Name);
  find_menu->Append(FIND_BYM5NAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + M6Name; StringLen = strlen(M6Name);
  find_menu->Append(FIND_BYM6NAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + M7Name; StringLen = strlen(M7Name);
  find_menu->Append(FIND_BYM7NAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + M8Name; StringLen = strlen(M8Name);
  find_menu->Append(FIND_BYM8NAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + M9Name; StringLen = strlen(M8Name);
  find_menu->Append(FIND_BYM9NAME, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  s = s + Comments; StringLen = strlen(Comments);
  find_menu->Append(FIND_BYCOMMENTS, s.c_str(), s.c_str());
  find_menu->AppendSeparator();
  s.DeleteAt(PromptLen, StringLen);

  // Print menu
  wxMenu *print_menu = new wxMenu;
#ifdef __USE_MSW_PRINTING__
  print_menu->Append(WXPRINT_PRINT, "Print...",
		     "Print to the selected printer");
  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->Append(WXPRINT_PREVIEW, "Print Preview",
		     "Preview before printing");
  print_menu->AppendSeparator();
#endif // __USE_MSW_PRINTING__
  print_menu->Append(PRINT_POSTSCRIPT, "Print to PostScript file",
		     "Print database to a PostScript file");
  print_menu->AppendSeparator();
  print_menu->Append(PRINT_ASCII, "Print to text file",
		     "Print database to text file");
  print_menu->AppendSeparator();
  print_menu->Append(PRINT_ASCIIOBJECT, "Print object to text file",
		     "Print a single object to a text file");
  
  // View menu
  wxMenu *view_menu = new wxMenu;
  view_menu->Append(VIEW_CLEAR, "&Clear Window", "Clear this text window");
  
  // 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, 450, 450);

  // 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->key = new wxText(frame->apanel, NULL,
				   (char *)KeyName, "",-1,-1,200,-1); 
  frame->addText->m1 = new wxText(frame->apanel, (wxFunction)&text_proc,
				  (char *)M1Name, "", -1, -1, 200, -1);
  frame->apanel->NewLine();
  frame->addText->m2 = new wxText(frame->apanel, (wxFunction)&text_proc,
				  (char *)M2Name, "", -1, -1, 200, -1);
  frame->addText->m3 = new wxText(frame->apanel, (wxFunction)&text_proc,
				  (char *)M3Name, "", -1, -1, 200, -1);
  frame->apanel->NewLine();
  frame->addText->m4 = new wxText(frame->apanel, (wxFunction)&text_proc,
				  (char *)M4Name, "", -1, -1, 200, -1);
  frame->addText->m5 = new wxText(frame->apanel, (wxFunction)&text_proc,
				  (char *)M5Name, "", -1, -1, 200, -1); 
  frame->apanel->NewLine();
  frame->addText->m6 = new wxText(frame->apanel, (wxFunction)&text_proc,
				  (char *)M6Name, "", -1, -1, 200, -1);
  frame->addText->m7 = new wxText(frame->apanel, (wxFunction)&text_proc,
				  (char *)M7Name, "", -1, -1, 200, -1);
  frame->apanel->NewLine();
  frame->addText->m8 = new wxMultiText(frame->apanel, (wxFunction)NULL,
				       (char *)M8Name, "",
				       -1, -1, 200, 75, wxHSCROLL);
  frame->addText->m9 = new wxMultiText(frame->apanel, (wxFunction)NULL,
				       (char *)M9Name, "",
				       -1, -1, 200, 75, wxHSCROLL);
  frame->apanel->NewLine();
  frame->addText->cmt = new wxMultiText(frame->apanel, (wxFunction)NULL,
					(char *)Comments, "",
					-1, -1, 200, 75, wxHSCROLL);
  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, 470, 470);

  // 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->key = new wxText(frame->cpanel, NULL,
				      (char *)KeyName, "",-1,-1,200,-1); 
  frame->changeText->m1 = new wxText(frame->cpanel, (wxFunction)&text_proc,
				     (char *)M1Name, "", -1, -1, 200, -1);
  frame->cpanel->NewLine();
  frame->changeText->m2 = new wxText(frame->cpanel, (wxFunction)&text_proc,
				     (char *)M2Name, "", -1, -1, 200, -1);
  frame->changeText->m3 = new wxText(frame->cpanel, (wxFunction)&text_proc,
				     (char *)M3Name, "", -1, -1, 200, -1);
  frame->cpanel->NewLine();
  frame->changeText->m4 = new wxText(frame->cpanel, (wxFunction)&text_proc,
				     (char *)M4Name, "", -1, -1, 200, -1);
  frame->changeText->m5 = new wxText(frame->cpanel, (wxFunction)&text_proc,
				     (char *)M5Name, "", -1, -1, 200, -1); 
  frame->cpanel->NewLine();
  frame->changeText->m6 = new wxText(frame->cpanel, (wxFunction)&text_proc,
				     (char *)M6Name, "", -1, -1, 200, -1);
  frame->changeText->m7 = new wxText(frame->cpanel, (wxFunction)&text_proc,
				     (char *)M7Name, "", -1, -1, 200, -1);
  frame->cpanel->NewLine();  
  frame->changeText->m8 = new wxMultiText(frame->cpanel, (wxFunction)NULL,
					  (char *)M8Name, "",
					  -1, -1, 200, 75, wxHSCROLL);
  frame->changeText->m9 = new wxMultiText(frame->cpanel, (wxFunction)NULL,
					  (char *)M9Name, "",
					  -1, -1, 200, 75, wxHSCROLL);
  frame->cpanel->NewLine();
  frame->changeText->cmt = new wxMultiText(frame->cpanel, (wxFunction)NULL,
					   (char *)Comments, "",
					   -1, -1, 200, 75, wxHSCROLL);
  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,
			    450, 450);

  // 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->key = new wxText(frame->dpanel, NULL,
				       (char *)KeyName, "",-1,-1,200,-1);
  frame->displayText->key->SetEditable(FALSE);
  frame->displayText->m1 = new wxText(frame->dpanel, (wxFunction)&text_proc,
				      (char *)M1Name, "", -1, -1, 200, -1);
  frame->displayText->m1->SetEditable(FALSE);
  frame->dpanel->NewLine();
  frame->displayText->m2 = new wxText(frame->dpanel, (wxFunction)&text_proc,
				      (char *)M2Name, "", -1, -1, 200, -1);
  frame->displayText->m2->SetEditable(FALSE);
  frame->displayText->m3 = new wxText(frame->dpanel, (wxFunction)&text_proc,
				      (char *)M3Name, "", -1, -1, 200, -1);
  frame->displayText->m3->SetEditable(FALSE);
  frame->dpanel->NewLine();
  frame->displayText->m4 = new wxText(frame->dpanel, (wxFunction)&text_proc,
				      (char *)M4Name, "", -1, -1, 200, -1);
  frame->displayText->m4->SetEditable(FALSE);
  frame->displayText->m5 = new wxText(frame->dpanel, (wxFunction)&text_proc,
				      (char *)M5Name, "", -1, -1, 200, -1);
  frame->displayText->m5->SetEditable(FALSE);
  frame->dpanel->NewLine();
  frame->displayText->m6 = new wxText(frame->dpanel, (wxFunction)&text_proc,
				      (char *)M6Name, "", -1, -1, 200, -1);
  frame->displayText->m6->SetEditable(FALSE);
  frame->displayText->m7 = new wxText(frame->dpanel, (wxFunction)&text_proc,
				      (char *)M7Name, "", -1, -1, 200, -1);
  frame->displayText->m7->SetEditable(FALSE);
  frame->dpanel->NewLine(); 
  frame->displayText->m8 = new wxMultiText(frame->dpanel, (wxFunction)NULL,
					   (char *)M8Name, "",
					   -1, -1, 200, 75, wxHSCROLL);
  frame->displayText->m8->SetEditable(FALSE);
  frame->displayText->m9 = new wxMultiText(frame->dpanel, (wxFunction)NULL,
					   (char *)M9Name, "",
					   -1, -1, 200, 75, wxHSCROLL);
  frame->displayText->m9->SetEditable(FALSE);
  frame->dpanel->NewLine();  
  frame->displayText->cmt = new wxMultiText(frame->dpanel, (wxFunction)NULL,
					    (char *)Comments, "",
					    -1, -1, 200, 75, wxHSCROLL);
  frame->displayText->cmt->SetEditable(FALSE);
  
  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;
  const char *DefaultWelcomeMesg = "welcome.txt";
  char *WelcomeMesgFile = 0;

#ifdef __USE_MSW_PRINTING__
  // Default values for wxWindows MSW Printing
  int mswp_cards_per_page = 0;
  int mswp_cell_length = 0;
  int mswp_font_size = 0;
  int mswp_left_margin = 0;
  char *mswp_orientation = 0;
  char *mswp_font = 0;
  char *mswp_doc_name = 0;
  int mswp_card_spacing = 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");
#ifdef __USE_MSW_PRINTING__
  // Default values for wxWindows MSW Printing
  mswp_cards_per_page = CfgData->GetIntValue("mswpCardsPerPage");
  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_card_spacing = CfgData->GetIntValue("mswpCardSpacing");
  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_cards_per_page > 0) {
    MSWPrintSetup->cards_per_page = mswp_cards_per_page;
    MSWPrintSetup->prev_cards_per_page = mswp_cards_per_page;
    MSWPrintSetup->default_cards_per_page = mswp_cards_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_card_spacing > 0) {
    MSWPrintSetup->card_spacing = mswp_card_spacing;
    MSWPrintSetup->prev_card_spacing = mswp_card_spacing;
    MSWPrintSetup->default_card_spacing = mswp_card_spacing;
  }

  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);

  accindex = new CCIndex(DB); // Initialize global buffer pointer
  cccindex = new CCIndex(DB); // Initialize global buffer pointer
  dccindex = new CCIndex(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(accindex) delete accindex;
  if(cccindex) delete cccindex;
  if(dccindex) delete dccindex;
  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:
      DisplayDB(*textWin);
      break;

    case FIND_BYKEYNAME:
      FindBy(*textWin, KeyName, CARD_NAME);
      break;

    case FIND_BYM1NAME:
      FindBy(*textWin, M1Name, COMPANY_NAME);
      break;
      
    case FIND_BYM2NAME:
      FindBy(*textWin, M2Name, DEPARTMENT_NAME);
      break;

    case FIND_BYM3NAME:
      FindBy(*textWin, M3Name, PHONE_NUMBER);
      break;

    case FIND_BYM4NAME:
      FindBy(*textWin, M4Name, FAX_NUMBER);
      break;

    case FIND_BYM5NAME:
      FindBy(*textWin, M5Name, CELL_NUMBER);
      break;

    case FIND_BYM6NAME:
      FindBy(*textWin, M6Name, BEEPER_NUMBER);
      break;

    case FIND_BYM7NAME:
      FindBy(*textWin, M7Name, EMAIL_ADDRESS);
      break;

    case FIND_BYM8NAME:
      FindBy(*textWin, M8Name, STREET_ADDRESS);
      break;

    case FIND_BYM9NAME:
      FindBy(*textWin, M9Name, INTERNET_URLS);
      break;

    case FIND_BYCOMMENTS:
      FindBy(*textWin, Comments, COMMENTS_BLOCK);
      break;

#ifdef __USE_MSW_PRINTING__
    case WXPRINT_PRINT:
      {
#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;
	
        wCCIndexPrint printout((char *)ProgramName);
	if (!printer.Print(this, &printout, TRUE))
	  wxMessageBox("The document was not printed.",
		       "Printing", wxOK);
	break;
      }

    case WXPRINT_PREVIEW:
      {
#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 wCCIndexPrint((char *)ProgramName),
                             new wCCIndexPrint((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=300;
#else 
      // Allow MSW style printing under UNIX
	int height=400;
#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, "Cards Per Page",
				 MSWPrintSetup->cards_per_page,
				 1, 5, 200);
	wxPageDialog->NewLine();
	CellSlider = new wxSlider(wxPageDialog, NULL, "Cell Length ",
				  MSWPrintSetup->cell_length,
				  1, 50, 150);
	wxPageDialog->NewLine();
	CsSlider = new wxSlider(wxPageDialog, NULL, "Card Spacing",
				MSWPrintSetup->card_spacing,
				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:
      PostScriptPrint(*textWin);
      break;

    case PRINT_ASCII:
      ASCIIPrint(*textWin);
      break;

    case PRINT_ASCIIOBJECT:
      ASCIIPrintObject(*textWin);  
      break;

    case VIEW_CLEAR:
      Clear(*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)
{
  key = m1 = m2 = m3 = m4 = m5 = m6 = m7 = 0;
  m8 = m9 = cmt = 0;
}

MyText::~MyText()
{
  if(key) delete key;
  if(m1) delete m1;
  if(m2) delete m2;
  if(m3) delete m3;
  if(m4) delete m4;
  if(m5) delete m5;
  if(m6) delete m6;
  if(m7) delete m7;
  if(m8) delete m8;
  if(m9) delete m9;
  if(cmt) delete cmt;
}

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

char *VerNumber()
// Version number for this windows program. Edit the version.hpp
// file to change this program's version number
{
 char buf[10];
 sprintf(buf, "%.3f", VersionNumber); 
 int len = strlen(buf);
 char *s = new char[len+1];
 s[len+1] = '\0';
 strcpy(s, buf);
 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() ;

  if(tag == ADD_BUTTON_ADD) {
    UString key_name;
    UString m1_name;
    UString m2_name;
    UString m3_name;
    UString m4_name;
    UString m5_name;
    UString m6_name;
    UString m7_name;
    UString m8_name;
    UString m9_name;
    UString cmts;

    key_name = frame->addText->key->GetValue();
    m1_name = frame->addText->m1->GetValue();
    m2_name = frame->addText->m2->GetValue();
    m3_name = frame->addText->m3->GetValue();
    m4_name = frame->addText->m4->GetValue();
    m5_name = frame->addText->m5->GetValue();
    m6_name = frame->addText->m6->GetValue();
    m7_name = frame->addText->m7->GetValue();
    m8_name = frame->addText->m8->GetValue();
    m9_name = frame->addText->m9->GetValue();
    cmts = frame->addText->cmt->GetValue();

    // Remove any leading space from the key name entry
    int offset = key_name.Find(" ");
    if(offset == 0) key_name.DeleteAt(offset, 1);
    
    if(key_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;
    }

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

    accindex->SetCardName(key_name.c_str());
    accindex->SetCompanyName(m1_name.c_str());
    accindex->SetDepartmentName(m2_name.c_str());
    accindex->SetPhoneNumber(m3_name.c_str());
    accindex->SetFaxNumber(m4_name.c_str());
    accindex->SetCellNumber(m5_name.c_str());
    accindex->SetBeeperNumber(m6_name.c_str());
    accindex->SetEmailAddress(m7_name.c_str());
    accindex->SetStreetAddress(m8_name.c_str());
    accindex->SetInternetURLS(m9_name.c_str());
    accindex->SetCommentsBlock(cmts.c_str());
    
    addr = accindex->AddObject(0); // Write the object to the file

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

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

    if(displaying_list) { // Update any list entries
      InMemCopy inmemcopy(accindex->GetCardName(), accindex->ObjectAddress(),
                          accindex->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(accindex->GetCardName())
      frame->addText->key->SetValue(accindex->GetCardName());
    if(accindex->GetCompanyName())
      frame->addText->m1->SetValue(accindex->GetCompanyName());
    if(accindex->GetDepartmentName())
      frame->addText->m2->SetValue(accindex->GetDepartmentName());
    if(accindex->GetPhoneNumber())
      frame->addText->m3->SetValue(accindex->GetPhoneNumber());
    if(accindex->GetFaxNumber())
      frame->addText->m4->SetValue(accindex->GetFaxNumber());
    if(accindex->GetCellNumber())
      frame->addText->m5->SetValue(accindex->GetCellNumber());
    if(accindex->GetBeeperNumber())
      frame->addText->m6->SetValue(accindex->GetBeeperNumber());
    if(accindex->GetEmailAddress())
      frame->addText->m7->SetValue(accindex->GetEmailAddress());
    if(accindex->GetStreetAddress())
      frame->addText->m8->SetValue(accindex->GetStreetAddress());
    if(accindex->GetInternetURLS())
      frame->addText->m9->SetValue(accindex->GetInternetURLS());
    if(accindex->GetCommentsBlock())
      frame->addText->cmt->SetValue(accindex->GetCommentsBlock());
    return;
  }
}

void ClearAddPanel()
{
  frame->addText->key->SetValue("");
  frame->addText->m1->SetValue("");
  frame->addText->m2->SetValue("");
  frame->addText->m3->SetValue("");
  frame->addText->m4->SetValue("");
  frame->addText->m5->SetValue("");
  frame->addText->m6->SetValue("");
  frame->addText->m7->SetValue("");
  frame->addText->m8->SetValue("");
  frame->addText->m9->SetValue("");
  frame->addText->cmt->SetValue("");
}

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";
  
  if(KeyName == 0) { // Alert user that key name is not set 
    textWin << "Primary Key Name value is not set in db config file!"
	    << "\n" << "Cannot add any objects." << "\n";
    
    return;
  }
  
  APanelFrame->Show(TRUE); // Show the Add Panel Frame
}

void ChBtnProc(wxButton& but, wxCommandEvent& event)
{
  UString sbuf, key_name; // Character input string buffer
  long tag = (long)but.GetClientData() ;
  int offset;
  
  if(tag == CHANGE_BUTTON_SH) {  
    if(KeyName != 0) key_name = frame->changeText->key->GetValue();

    // Remove any leading space from the key name entry
    offset = key_name.Find(" ");
    if(offset == 0) key_name.DeleteAt(offset, 1);
    
    if(key_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;
    }
    
    CCIndex ccindex(DB);
    ccindex.SetCardName(key_name);
    
    if(!(__LWORD__)ccindex.FindObject()) {
      sbuf = sbuf + "Could not find an entry for " + key_name;
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      btn_ch_cm->Enable(0);
      return;
    }

    cccindex->Copy(ccindex);
    
    if(cccindex->GetCompanyName())
      frame->changeText->m1->SetValue(cccindex->GetCompanyName());
    if(cccindex->GetDepartmentName())
      frame->changeText->m2->SetValue(cccindex->GetDepartmentName());
    if(cccindex->GetPhoneNumber())
      frame->changeText->m3->SetValue(cccindex->GetPhoneNumber());
    if(cccindex->GetFaxNumber())
      frame->changeText->m4->SetValue(cccindex->GetFaxNumber());
    if(cccindex->GetCellNumber())
      frame->changeText->m5->SetValue(cccindex->GetCellNumber());
    if(cccindex->GetBeeperNumber())
      frame->changeText->m6->SetValue(cccindex->GetBeeperNumber());
    if(cccindex->GetEmailAddress())
      frame->changeText->m7->SetValue(cccindex->GetEmailAddress());
    if(cccindex->GetStreetAddress())
      frame->changeText->m8->SetValue(cccindex->GetStreetAddress());
    if(cccindex->GetInternetURLS())
      frame->changeText->m9->SetValue(cccindex->GetInternetURLS());
    if(cccindex->GetCommentsBlock())
      frame->changeText->cmt->SetValue(cccindex->GetCommentsBlock());
    btn_ch_cm->Enable(1); // Enable the commit button
  }

  if(tag == CHANGE_BUTTON_CM) {
    if(KeyName != 0) key_name = frame->changeText->key->GetValue();

    // Remove any leading space from the key name entry
    offset = key_name.Find(" ");
    if(offset == 0) key_name.DeleteAt(offset, 1);
    
    if(key_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;
    }
    
    CCIndex changed(DB); // Buffer used to store changes
    changed.SetCardName(frame->changeText->key->GetValue());
    changed.SetCompanyName(frame->changeText->m1->GetValue());
    changed.SetDepartmentName(frame->changeText->m2->GetValue());
    changed.SetPhoneNumber(frame->changeText->m3->GetValue());
    changed.SetFaxNumber(frame->changeText->m4->GetValue());
    changed.SetCellNumber(frame->changeText->m5->GetValue());
    changed.SetBeeperNumber(frame->changeText->m6->GetValue());
    changed.SetEmailAddress(frame->changeText->m7->GetValue());
    changed.SetStreetAddress(frame->changeText->m8->GetValue());
    changed.SetInternetURLS(frame->changeText->m9->GetValue());
    changed.SetCommentsBlock(frame->changeText->cmt->GetValue());

    // Disable the commit button after changing the object
    btn_ch_cm->Enable(0);
    
    if(changed.FullCompare(*cccindex)) { // The object has not changed
      sbuf = sbuf + "No changes to commit for " + cccindex->GetCardName();
      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.GetCardName());
    UString old_key_name(cccindex->GetCardName());

    // 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 = cccindex->ObjectAddress(); // Record address before deleting
    cccindex->DeleteObject(); // Remove the object  
    changed.AddObject(0); // Add the changed object back to the database

    sbuf = sbuf + "Commited changes for " + changed.GetCardName();
    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(cccindex->GetCardName(), oa, cccindex->GetClassID());
      DNode<InMemCopy> *prevptr = dllistptr;
      dllistptr = (DNode<InMemCopy> *)dllist->Find(inmemcopy);
      if(dllistptr) {
	dllistptr->Data.key = changed.GetCardName();	
	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.GetCardName();	
       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(cccindex->GetCardName())
      frame->changeText->key->SetValue(cccindex->GetCardName());
    if(cccindex->GetCompanyName())
      frame->changeText->m1->SetValue(cccindex->GetCompanyName());
    if(cccindex->GetDepartmentName())
      frame->changeText->m2->SetValue(cccindex->GetDepartmentName());
    if(cccindex->GetPhoneNumber())
      frame->changeText->m3->SetValue(cccindex->GetPhoneNumber());
    if(cccindex->GetFaxNumber())
      frame->changeText->m4->SetValue(cccindex->GetFaxNumber());
    if(cccindex->GetCellNumber())
      frame->changeText->m5->SetValue(cccindex->GetCellNumber());
    if(cccindex->GetBeeperNumber())
      frame->changeText->m6->SetValue(cccindex->GetBeeperNumber());
    if(cccindex->GetEmailAddress())
      frame->changeText->m7->SetValue(cccindex->GetEmailAddress());
    if(cccindex->GetStreetAddress())
      frame->changeText->m8->SetValue(cccindex->GetStreetAddress());
    if(cccindex->GetInternetURLS())
      frame->changeText->m9->SetValue(cccindex->GetInternetURLS());
    if(cccindex->GetCommentsBlock())
      frame->changeText->cmt->SetValue(cccindex->GetCommentsBlock());
    btn_ch_cm->Enable(0);
    return;
  }
}

void ClearChangePanel()
{
  frame->changeText->key->SetValue("");
  frame->changeText->m1->SetValue("");
  frame->changeText->m2->SetValue("");
  frame->changeText->m3->SetValue("");
  frame->changeText->m4->SetValue("");
  frame->changeText->m5->SetValue("");
  frame->changeText->m6->SetValue("");
  frame->changeText->m7->SetValue("");
  frame->changeText->m8->SetValue("");
  frame->changeText->m9->SetValue("");
  frame->changeText->cmt->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(KeyName == 0) return;

  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 key_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;
    }

    key_name = buf;
    
    // Remove any leading space from the key name entry
    int offset = key_name.Find(" ");
    if(offset == 0) key_name.DeleteAt(offset, 1);
  
    if(key_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
    key_name = keyNM; // Key name memeber

  CCIndex ccindex(DB);
  ccindex.SetCardName(key_name);

  FAU addr = ccindex.FindObject();
  
  if(!addr) { 
    comp.DeleteAt(0, comp.length());
    comp = comp + "Could not find an entry for: " + key_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(ccindex.GetCardName(), ccindex.ObjectAddress(),
                     ccindex.GetClassID());

  ccindex.DeleteObject();

  textWin << "Removed entry for: " << key_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() ;
  CCIndex ccindex(DB);
  FAU addr;
  
  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)) {
      ccindex.ReadObject(dllistptr->Data.object_address);
      DisplayObject(ccindex);
    }
    else {
      wxMessageBox("Reached the end of the list\n",
		   "Program Message", wxOK|wxCENTRE);
      dllistptr = dllist->GetFront();
      ccindex.ReadObject(dllistptr->Data.object_address);
      DisplayObject(ccindex);
    }
  }
  
  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)) {
      ccindex.ReadObject(dllistptr->Data.object_address);
      DisplayObject(ccindex);
    }
    else {
      wxMessageBox("Reached the beginning of the list\n",
		   "Program Message", wxOK|wxCENTRE);
      dllistptr = dllist->GetBack();
      ccindex.ReadObject(dllistptr->Data.object_address);
      DisplayObject(ccindex);
    }
  }
  
  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()) {
      ccindex.ReadObject(dllistptr->Data.object_address);
      cccindex->Copy(ccindex);
    }
    else
      cccindex->Copy(*dccindex);
    
    frame->changeText->key->SetValue(cccindex->GetCardName());
    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);
    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, dccindex->GetCardName());
  }
}

void ClearDisplayPanel()
{
  frame->displayText->key->SetValue("");
  frame->displayText->m1->SetValue("");
  frame->displayText->m2->SetValue("");
  frame->displayText->m3->SetValue("");
  frame->displayText->m4->SetValue("");
  frame->displayText->m5->SetValue("");
  frame->displayText->m6->SetValue("");
  frame->displayText->m7->SetValue("");
  frame->displayText->m8->SetValue("");
  frame->displayText->m9->SetValue("");
  frame->displayText->cmt->SetValue("");
}

void DisplayObject(CCIndex &ccindex)
{
  dccindex->Copy(ccindex); // Initialize global display object

  if(ccindex.GetCardName())
    frame->displayText->key->SetValue(ccindex.GetCardName());
  if(ccindex.GetCompanyName())
    frame->displayText->m1->SetValue(ccindex.GetCompanyName());
  if(ccindex.GetDepartmentName())
    frame->displayText->m2->SetValue(ccindex.GetDepartmentName());
  if(ccindex.GetPhoneNumber())
    frame->displayText->m3->SetValue(ccindex.GetPhoneNumber());
  if(ccindex.GetFaxNumber())
    frame->displayText->m4->SetValue(ccindex.GetFaxNumber());
  if(ccindex.GetCellNumber())
    frame->displayText->m5->SetValue(ccindex.GetCellNumber());
  if(ccindex.GetBeeperNumber())
    frame->displayText->m6->SetValue(ccindex.GetBeeperNumber());
  if(ccindex.GetEmailAddress())
    frame->displayText->m7->SetValue(ccindex.GetEmailAddress());
  if(ccindex.GetStreetAddress())
    frame->displayText->m8->SetValue(ccindex.GetStreetAddress());
  if(ccindex.GetInternetURLS())
    frame->displayText->m9->SetValue(ccindex.GetInternetURLS());
  if(ccindex.GetCommentsBlock())
    frame->displayText->cmt->SetValue(ccindex.GetCommentsBlock());

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

int LoadIndexKeys(int build_new_tree, int clear_tree)
{
  int num = 0;
  
  // Clear the doubly linked list
  dllist->Clear();

  if(build_new_tree) { // Load the index keys into memory
    CachePointer n = DB->Index()->GetRoot();
    rbtree->Clear();
    BtreeWalk(n, LoadKeys, DB->Index());
  }
  
  // Using iterator object to walk throught the rbtree
  TreeWalkerb tw(rbtree->GetRoot(), INORDER);
  BNodeBase *nxt; // BSTree base node pointer
  
  // Walk through the rbtree using an iterator object
  while((nxt = tw.Next()) != 0) {
    RBNode<InMemCopy> *ptr = (RBNode<InMemCopy> *)nxt;
    dllist->StoreNode(ptr->Data);
    num++; 
  }

  if(clear_tree) rbtree->Clear(); // Clear the rbtree
  if(num > 0) print_list = 1;
  return num;
}

void DisplayDB(MyTextWindow &textWin)
{
  if(KeyName == 0) return;

  textWin.Clear();
  textWin << "Displaying the database objects." << "\n";
    
  if(DB->RebuildIndex()) {
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

  num_objects = LoadIndexKeys();
  
  if(num_objects == 0) {
    textWin << "The database is empty." << "\n";
    dllist->Clear();
    return;
  }
  else
    textWin << "Displaying " << num_objects << " objects..." << "\n";

  // Initialize the doubly list node pointer
  dllistptr = dllist->GetFront();
  
  // Display the first object
  CCIndex ccindex(DB);
  ccindex.ReadObject(dllistptr->Data.object_address);
  DisplayObject(ccindex);
}

void FindBy(MyTextWindow &textWin, const char *MemberName, CCIndexItem item)
{
  if(MemberName == 0) return;

  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 << "The index file needs to be rebuilt." << "\n";
    return;
  }
  
  CCIndex ccindex(DB);
  
  offset = name.Find(WildCard); // Look for wild card character
  if(offset == UString::NoMatch) { // No wild cards found
    ccindex.SetCardName(buf);
    CCIndexSearch(ccindex, item, name, textWin);
    return;
  }
  else 
    CCIndexSearch(ccindex, item, name, textWin, WildCard);
}

void CCIndexSearch(CCIndex &ccindex, CCIndexItem 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++;
    }
  }
  
  rbtree->Clear();
  CachePointer n = DB->Index()->GetRoot();

  // Ensure that the memory buffers and the file data
  // stay in sync during multiple file access.
  DB->Index()->TestTree();

  if(wildcard != 0)
    BtreeSearch(n, BtreeNodeSearch, item, ccindex, str, 1);
  else
    BtreeSearch(n, BtreeNodeSearch, item, ccindex, str);

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

  LoadIndexKeys(0);
  dllistptr = dllist->GetFront();

  UString comp;
  int yn;
  
  if(ObjectsFound > 1) { // Found multiple matches
    textWin << "Found " << ObjectsFound << " matching." << "\n";
    char intbuf[100];
    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();
    num_objects = 0;
    return;
  }
  
  // Display the first object
  ccindex.ReadObject(dllistptr->Data.object_address);
  DisplayObject(ccindex);
}

void ExportToASCII(MyTextWindow &textWin)
{
  const char dchar = '\t';  // Text delimiter
  const char filter = '\t'; // Filter out tabs
  const char *Fill = "Unknown"; // Fill character string
  int yn, exports = 0;
  
  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!\nOverwrite 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; 
  }

  CCIndex ccindex(DB);

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

  ASPrint(M1Name, stream, strlen(M1Name));
  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 << dchar;
  
  ASPrint(M8Name, stream, strlen(M8Name));
  stream << dchar;
  
  ASPrint(M9Name, stream, strlen(M9Name));
  stream << dchar;

  ASPrint(Comments, stream, strlen(Comments));
  stream << asLineFeed;

  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    ccindex.ReadObject(dllistptr->Data.object_address);
    ASPrint(ccindex.GetCardName(), filter, stream,
	    strlen(ccindex.GetCardName()));
    stream << dchar;
    
    if(!*ccindex.GetCompanyName())
      ASPrint(Fill, stream, strlen(Fill));
    else
      ASPrint(ccindex.GetCompanyName(), filter, stream,
              strlen(ccindex.GetCompanyName()));
    stream << dchar;

    if(!*ccindex.GetDepartmentName())
      ASPrint(Fill, stream, strlen(Fill));
    else
      ASPrint(ccindex.GetDepartmentName(), filter, stream,
              strlen(ccindex.GetDepartmentName()));
      stream << dchar;

      if(!*ccindex.GetPhoneNumber())
      ASPrint(Fill, stream, strlen(Fill));
    else
      ASPrint(ccindex.GetPhoneNumber(), filter, stream,
              strlen(ccindex.GetPhoneNumber()));
      stream << dchar;

      if(!*ccindex.GetFaxNumber())
	ASPrint(Fill, stream, strlen(Fill));
      else
        ASPrint(ccindex.GetFaxNumber(), filter, stream,
                strlen(ccindex.GetFaxNumber()));
      stream << dchar;

      if(!*ccindex.GetCellNumber())
	ASPrint(Fill, stream, strlen(Fill));
      else
        ASPrint(ccindex.GetCellNumber(), filter, stream,
                strlen(ccindex.GetCellNumber()));
      stream << dchar;

      if(!*ccindex.GetBeeperNumber())
	ASPrint(Fill, stream, strlen(Fill));
      else
        ASPrint(ccindex.GetBeeperNumber(), filter, stream,
                strlen(ccindex.GetBeeperNumber()));
      stream << dchar;

      if(!*ccindex.GetEmailAddress())
	ASPrint(Fill, stream, strlen(Fill));
      else
        ASPrint(ccindex.GetEmailAddress(), filter, stream,
                strlen(ccindex.GetEmailAddress()));
      stream << dchar;

      if(!*ccindex.GetStreetAddress())
	ASPrint(Fill, stream, strlen(Fill));
      else 
        ExportPrintwxMultiText(ccindex.GetStreetAddress(), stream);
      stream << dchar;
    
      if(!*ccindex.GetInternetURLS())
	ASPrint(Fill, stream, strlen(Fill));
      else 
        ExportPrintwxMultiText(ccindex.GetInternetURLS(), stream);
      stream << dchar;

      if(!*ccindex.GetCommentsBlock())
	ASPrint("None", stream, strlen("None"));
      else 
        ExportPrintwxMultiText(ccindex.GetCommentsBlock(), stream);

    stream << asLineFeed;
    exports++;
    dllistptr = dllistptr->GetNext();
  }
  
  textWin << "Exported " << exports << " objects." << "\n";
  stream.close();
}

void ExportPrintwxMultiText(char *str, ofstream &stream)
// Prints four lines of a wxMultiText box to a ASCII export file
{
  char words[MAXWORDS][MAXWORDLENGTH];
  int num;
  int offset;
  const char dchar = '\n';  // Text delimiter
  const char filter = '\t'; // Filter out tabs
  UString line_buf;
  int i, j;

  for(i = 0; i < MAXWORDS; i++) {
    for(j = 0; j < MAXWORDLENGTH; j++)
      words[i][j] = '\0';
  }
  
  parse(str, words, &num, dchar);

  if(words[0][0] != '\0') {
    line_buf = words[0];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), filter, stream, line_buf.length());
  }

  if(words[1][0] != '\0') {
    line_buf = words[1];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    stream << "\\n"; 
    ASPrint(line_buf.c_str(), filter, stream, line_buf.length());
  }

  if(words[2][0] != '\0') {
    line_buf = words[2];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    stream << "\\n"; 
    ASPrint(line_buf.c_str(), filter, stream, line_buf.length());
  }
  
  if(words[3][0] != '\0') {
    line_buf = words[3];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    stream << "\\n"; 
    ASPrint(line_buf.c_str(), filter, stream, line_buf.length());
  }
}

void ImportFromASCII(MyTextWindow &textWin)
{
  if(KeyName == 0) return;

  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();
  
  CCIndex ccindex(DB);
  CCIndex ccindexa(DB);
  CCIndex ob(DB);

  int linecount = 0;
  int imports = 0;
  int updates = 0;
  int updateall = 0;
  int ObjectItems = 11; // 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) {
      ccindex.SetCardName(words[0]);
      ccindex.SetCompanyName(words[1]);
      ccindex.SetDepartmentName(words[2]);
      ccindex.SetPhoneNumber(words[3]);
      ccindex.SetFaxNumber(words[4]);
      ccindex.SetCellNumber(words[5]);
      ccindex.SetBeeperNumber(words[6]);
      ccindex.SetEmailAddress(words[7]);
      ccindex.SetStreetAddress(ImportFormatwxMultiText(words[8]));
      ccindex.SetInternetURLS(ImportFormatwxMultiText(words[9]));
      ccindex.SetCommentsBlock(ImportFormatwxMultiText(words[10]));
    
      ccindexa.SetCardName(ccindex.GetCardName());
      FAU addr = ccindexa.FindObject();
      
      if(addr) {
	if(updateall == 0) {
	  sbuf.DeleteAt(0, sbuf.length());
          sbuf = sbuf + ccindex.GetCardName() + " 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(ccindexa);
            if(ccindex.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject(); 
            ccindex.AddObject(0);
	    sbuf.DeleteAt(0, sbuf.length());
            sbuf = sbuf + "Updating entry for: " + ccindex.GetCardName();
	    textWin << sbuf.c_str() << "\n";
	    frame->SetStatusText(sbuf.c_str());
	    break;
	  case UPDATE_SKIP:
	    break;
	  case UPDATE_ENTRY:
	    updates++;
            ob.Copy(ccindexa);
            if(ccindex.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject(); 
            ccindex.AddObject(0);
	    sbuf.DeleteAt(0, sbuf.length());
            sbuf = sbuf + "Updating entry for: " + ccindex.GetCardName();
	    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: " + ccindex.GetCardName();
	  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 = ccindex.AddObject(0); // Write the object to the file
	  imports++;
	  if(!addr) {
            textWin << "Could not add:" << ccindex.GetCardName()
		    << " to database" << "\n";
	    imports--;
	  }
	}
      }
    }
    sllistptr = sllistptr->GetNext();
  }

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

  sllist.Clear();
}

char *ImportFormatwxMultiText(char *str)
{
  UString line_buf(str);
  int Offset = 0;
  while(1) {
    Offset = line_buf.Find("\\n", Offset);
    if (Offset == UString::NoMatch) break;
    line_buf.DeleteAt(Offset, 2); // Remove forward slash and new line char 
    line_buf.InsertAt(Offset, "\n");
    Offset++;
  }
  return line_buf.c_str();
}

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;
  CCIndex ccindex(&newdb);
  CCIndex ccindex_old(DB);
  
  if(DB->RebuildIndex()) {
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

  // Load the index keys into memory
  CachePointer n = DB->Index()->GetRoot();
  rbtree->Clear();
  BtreeWalk(n, LoadKeys, DB->Index());

  BNodeBase *nxt; // BSTree base node pointer

  // Using iterator object to walk throught the rbtree
  TreeWalkerb tw(rbtree->GetRoot(), INORDER);

  // Walk through the rbtree using an iterator object
  while((nxt = tw.Next()) != 0) {
    RBNode<InMemCopy> *ptr = (RBNode<InMemCopy> *)nxt;
    ccindex_old.ReadObject(ptr->Data.object_address);
    ccindex.Copy(ccindex_old);
    ccindex.AddObject(0);
    items++;
  }

  rbtree->Clear(); // Clear the rbtree

  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);
  CCIndex ccindex(&newdb);
  CCIndex ccindexa(DB);
  CCIndex 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 == ccindex.GetClassID()) { 
          ccindex.ReadObject(oa);
          ccindexa.SetCardName(ccindex.GetCardName());
          existing = ccindexa.FindObject();
	  if(existing) {
	    if(updateall == 0) {
	      sbuf.DeleteAt(0, sbuf.length());
              sbuf = sbuf +  ccindexa.GetCardName() + \
		" 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(ccindex);
                if(ccindex.FullCompare(ob)) break; // Object has not changed
		ob.DeleteObject(); 
                ob.Copy(ccindex);
		ob.AddObject(0);
		sbuf.DeleteAt(0, sbuf.length());
                sbuf = sbuf  + "Updating entry for: " + ccindex.GetCardName();
		textWin << sbuf.c_str() << "\n";
		frame->SetStatusText(sbuf.c_str());
		break;
	      case UPDATE_SKIP:
		break;
	      case UPDATE_ENTRY:
		updates++;
                ob.Copy(ccindex);
                if(ccindex.FullCompare(ob)) break; // Object has not changed
		ob.DeleteObject(); 
                ob.Copy(ccindex);
		ob.AddObject(0);
		sbuf.DeleteAt(0, sbuf.length());
                sbuf = sbuf + "Updating entry for: " + ccindex.GetCardName();
		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: " + ccindex.GetCardName();
	      textWin << sbuf.c_str() << "\n";
	      frame->SetStatusText(sbuf.c_str());
	    }
	    else {
	      // Nothing to do
	    }
	  }
	  else {
            ccindexa.Copy(ccindex);
            FAU rvaddr = ccindexa.AddObject(0); // Write the object to the file
	    imports++;
	    if(!rvaddr) {
              textWin << "Could not add:" << ccindex.GetCardName()
		      << " 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(M1Name, stream, strlen(M1Name));
  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 << dchar;
  ASPrint(M8Name, stream, strlen(M8Name));
  stream << dchar;
  ASPrint(M9Name, stream, strlen(M9Name));
  stream << dchar;
  ASPrint(Comments, stream, strlen(Comments));
  stream << asLineFeed;
  
  textWin << "Finished." << "\n";
  stream.close();
}

void ASCIIPrint(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();
  if(dllist->IsEmpty()) LoadIndexKeys();
    
  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; 
  }

  CCIndex ccindex(DB);

  dllistptr = dllist->GetFront();
  textWin << "Printing..." << "\n";
  
  while(!dllist->IsHeader(dllistptr)) {
    ccindex.ReadObject(dllistptr->Data.object_address);
    asPrintwxText((char *)KeyName, ccindex.GetCardName(), stream);
    asPrintwxText((char *)M1Name, ccindex.GetCompanyName(), stream);
    asPrintwxText((char *)M2Name, ccindex.GetDepartmentName(), stream);
    asPrintwxText((char *)M3Name, ccindex.GetPhoneNumber(), stream);
    asPrintwxText((char *)M4Name, ccindex.GetFaxNumber(), stream);
    asPrintwxText((char *)M5Name, ccindex.GetCellNumber(), stream);
    asPrintwxText((char *)M6Name, ccindex.GetBeeperNumber(), stream);
    asPrintwxText((char *)M7Name, ccindex.GetEmailAddress(), stream);
    asPrintwxMultiText((char *)M8Name, ccindex.GetStreetAddress(), stream);
    asPrintwxMultiText((char *)M9Name, ccindex.GetInternetURLS(), stream);
    asPrintwxMultiText((char *)Comments, ccindex.GetCommentsBlock(), stream);
    stream << asLineFeed;
    dllistptr = dllistptr->GetNext();
  }
  textWin << "Finished." << "\n";
  stream.close();
}

void ASCIIPrintObject(MyTextWindow &textWin, char *keyNM)
{
  textWin.Clear();
  textWin << "Printing object to ASCII file..." << "\n";

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

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

    key_name = buf;
    
    // Remove any leading space from the key name entry
    int offset = key_name.Find(" ");
    if(offset == 0) key_name.DeleteAt(offset, 1);
  
    if(key_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
    key_name = keyNM; // Key name memeber

  CCIndex ccindex(DB);
  ccindex.SetCardName(key_name);
  FAU addr = ccindex.FindObject();

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

  char *FileName = wxFileSelector("Printing object 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?";
    int 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; 
  }

  asPrintwxText((char *)KeyName, ccindex.GetCardName(), stream);
  asPrintwxText((char *)M1Name, ccindex.GetCompanyName(), stream);
  asPrintwxText((char *)M2Name, ccindex.GetDepartmentName(), stream);
  asPrintwxText((char *)M3Name, ccindex.GetPhoneNumber(), stream);
  asPrintwxText((char *)M4Name, ccindex.GetFaxNumber(), stream);
  asPrintwxText((char *)M5Name, ccindex.GetCellNumber(), stream);
  asPrintwxText((char *)M6Name, ccindex.GetBeeperNumber(), stream);
  asPrintwxText((char *)M7Name, ccindex.GetEmailAddress(), stream);
  asPrintwxMultiText((char *)M8Name, ccindex.GetStreetAddress(), stream);
  asPrintwxMultiText((char *)M9Name, ccindex.GetInternetURLS(), stream);
  asPrintwxMultiText((char *)Comments, ccindex.GetCommentsBlock(), stream);

  textWin << "Finished." << "\n";
  stream.close();
}

void asPrintwxText(char *title, char *str, ofstream &stream)
// Print a single line of a wxText item to a ASCII File  
{
  ASPrint(title, stream, StringOffset+1);
  ASPrint(str, stream, strlen(str));
  stream << asLineFeed;
}

void asPrintwxMultiText(char *title, char *str, ofstream &stream)
// Prints four lines of a wxMultiText box to a ACSII file
{
  char words[MAXWORDS][MAXWORDLENGTH];
  int num;
  int offset;
  const char dchar = '\n';  // Text delimiter
  UString line_buf;
  int i, j;

  ASPrint(title, stream, StringOffset+1);
  
  for(i = 0; i < MAXWORDS; i++) {
    for(j = 0; j < MAXWORDLENGTH; j++)
      words[i][j] = '\0';
  }
  
  parse(str, words, &num, dchar);

  if(words[0][0] != '\0') {
    line_buf = words[0];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), stream, line_buf.length());
  }

  stream << asLineFeed;

  if(words[1][0] != '\0') {
    line_buf = words[1];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), stream, line_buf.length(), StringOffset+1);
  }

  stream << asLineFeed;
    
  if(words[2][0] != '\0') {
    line_buf = words[2];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), stream, line_buf.length(), StringOffset+1);
  }
  
  stream << asLineFeed;
  
  if(words[3][0] != '\0') {
    line_buf = words[3];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), stream, line_buf.length(), StringOffset+1);
  }
  stream << asLineFeed;
}

void CompareIndexFile(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Comparing the index file to the data file..." << "\n";
  
  CCIndex ccindex(DB);
  int rv = ccindex.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; 
    }
  }
  
  CCIndex ccindex(DB);
  int rv = ccindex.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 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 = 25;    
  double font_size = 10; 
  int orientation = 0;
  int cards_per_page = 2;
  double card_spacing = 1;
  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;

  // 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 int_val = 0;
    double dfp_val = 0;
    char *str_val = 0;

    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;
    }

    int_val = CfgData->GetIntValue("CardsPerPage");
    if(int_val > 0) cards_per_page = int_val;

    dfp_val = CfgData->GetDFPValue("CardSpacing");
    if(dfp_val > 0) card_spacing = dfp_val;

    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();
  if(dllist->IsEmpty()) LoadIndexKeys();
    
  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?";
    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; 
  }

  PostScriptDrv psdrv;
  psdrv.Copies(1);

  // Setup the PostScript driver
  psdrv.SetPaperSize(paper_code);
  psdrv.SetFont(cell_font, font_size);
  psdrv.UseHeader();
  psdrv.DrawHeaderLine();
  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);
  CCIndex ccindex(DB);
  int cards = 1;
  int char_offset = int((cell_len + 1) * psdrv.CharWidth());
  psdrv.row = psdrv.StartY();
  
  int x_offset;
  if(orientation == 0)
    x_offset = int(HEADER_OFFSET * PIXELS_PER_INCH);
  else
    x_offset = PRINTABLE_OFFSET_X;

  dllistptr = dllist->GetFront();
  textWin << "Printing..." << "\n";
  psdrv.StartPage(psdrv.page_count + 1, stream);

  while(!dllist->IsHeader(dllistptr)) {
    ccindex.ReadObject(dllistptr->Data.object_address);
    PSPrintwxText(psdrv, stream, (char *)KeyName,  ccindex.GetCardName(),
		  char_offset, cell_len, font_size, item_font, cell_font);
    PSPrintwxText(psdrv, stream, (char *)M1Name,  ccindex.GetCompanyName(),
		  char_offset, cell_len, font_size, item_font, cell_font);
    PSPrintwxText(psdrv, stream, (char *)M2Name,  ccindex.GetDepartmentName(),
		  char_offset, cell_len, font_size, item_font, cell_font);
    PSPrintwxText(psdrv, stream, (char *)M3Name,  ccindex.GetPhoneNumber(),
		  char_offset, cell_len, font_size, item_font, cell_font);
    PSPrintwxText(psdrv, stream, (char *)M4Name,  ccindex.GetFaxNumber(),
		  char_offset, cell_len, font_size, item_font, cell_font);
    PSPrintwxText(psdrv, stream, (char *)M5Name,  ccindex.GetCellNumber(),
		  char_offset, cell_len, font_size, item_font, cell_font);
    PSPrintwxText(psdrv, stream, (char *)M6Name,  ccindex.GetBeeperNumber(),
		  char_offset, cell_len, font_size, item_font, cell_font);
    PSPrintwxText(psdrv, stream, (char *)M7Name,  ccindex.GetEmailAddress(),
		  char_offset, cell_len, font_size, item_font, cell_font);
    PSPrintwxMultiText(psdrv, stream, (char *)M8Name,
                       ccindex.GetStreetAddress(),  char_offset, cell_len,
		       font_size, item_font, cell_font);
    PSPrintwxMultiText(psdrv, stream, (char *)M9Name,
                       ccindex.GetInternetURLS(),  char_offset, cell_len,
		       font_size, item_font, cell_font);
    PSPrintwxMultiText(psdrv, stream, (char *)Comments,
                       ccindex.GetCommentsBlock(),  char_offset, cell_len,
		       font_size, item_font, cell_font);
    psdrv.row -= int(card_spacing * PIXELS_PER_INCH);
    
    cards++;

    if(cards > cards_per_page) {
      psdrv.EndPage(stream);
      psdrv.StartPage(psdrv.page_count + 1, stream);
      psdrv.row = psdrv.StartY();
      cards = 1;
    }
    dllistptr = dllistptr->GetNext();
  } 

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

void PSPrintwxText(PostScriptDrv &psdrv, ofstream &stream, char *title,
		   char *str, int char_offset, int cell_len, double font_size,
		   PostScriptDrv::PSFonts item_font,
		   PostScriptDrv::PSFonts cell_font)
// Print a single line of a wxText item to a PostScript File  
{
  psdrv.ChangeFont(stream, item_font, font_size);
  psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);
  psdrv.PrintLine(title, cell_len, stream);
  psdrv.ChangeFont(stream, cell_font, font_size);
  psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
  psdrv.PrintLine(str, stream);
  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);
}

void PSPrintwxMultiText(PostScriptDrv &psdrv, ofstream &stream, char *title,
			char *str, int char_offset, int cell_len,
			double font_size, PostScriptDrv::PSFonts item_font,
			PostScriptDrv::PSFonts cell_font)
// Prints four lines of a wxMultiText box to a PostScript file
{
  char words[MAXWORDS][MAXWORDLENGTH];
  int num;
  int offset;
  const char dchar = '\n';  // Text delimiter
  UString line_buf;
  int i, j;

  psdrv.ChangeFont(stream, item_font, font_size);
  psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);
  psdrv.PrintLine(title, cell_len, stream);
  psdrv.ChangeFont(stream, cell_font, font_size);

  for(i = 0; i < MAXWORDS; i++) {
    for(j = 0; j < MAXWORDLENGTH; j++)
      words[i][j] = '\0';
  }
    
  parse(str, words, &num, dchar);
  if(words[0][0] != '\0') {
    line_buf = words[0];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
    psdrv.PrintLine(line_buf.c_str(), stream);
  }

  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);

  if(words[1][0] != '\0') {
    line_buf = words[1];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
    psdrv.PrintLine(line_buf.c_str(), stream);
  }

  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);
    
  if(words[2][0] != '\0') {
    line_buf = words[2];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
    psdrv.PrintLine(line_buf.c_str(), stream);
  }
  
  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);

  if(words[3][0] != '\0') {
    line_buf = words[3];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
    psdrv.PrintLine(line_buf.c_str(), stream);
  }

  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);
}

#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->cards_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->card_spacing = CsSlider->GetValue();
    
    MSWPrintSetup->prev_cards_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();
    MSWPrintSetup->prev_card_spacing = CsSlider->GetValue();
  }

  if(tag == PAGE_DIALOG_BUTTON_CANCEL) {
    LppSlider->SetValue(MSWPrintSetup->prev_cards_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);
    CsSlider->SetValue(MSWPrintSetup->prev_card_spacing);
  }

  if(tag == PAGE_DIALOG_BUTTON_DEFAULT) {
    LppSlider->SetValue(MSWPrintSetup->default_cards_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);
    CsSlider->SetValue(MSWPrintSetup->default_card_spacing);
  }
}

void wCCIndexPrint::SetPrinterFont(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 wCCIndexPrint::SetItemBarFont(int style, int weight)
{
  switch(MSWPrintSetup->font) {
    case 0:
      itemFont = wxTheFontList->FindOrCreateFont(MSWPrintSetup->font_size,
						    wxSWISS, style, weight);
      break;

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

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

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

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

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

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

Bool wCCIndexPrint::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 wCCIndexPrint::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 wCCIndexPrint::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 wCCIndexPrint::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;
  }


  CCIndex ccindex(DB);
  if(print_list == 0) num_objects = LoadIndexKeys();
  if(dllist->IsEmpty()) num_objects = LoadIndexKeys();
  dllistptr = dllist->GetFront();

  int cards = num_objects; 
  int first_page = 1; // Always starting on page one
  int 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(cards <= MSWPrintSetup->cards_per_page) break;
    cards = cards - MSWPrintSetup->cards_per_page;
    last_page++; // Increment the page count
  }
  
  *selPageTo = last_page;
  *maxPage = last_page;
}

void wCCIndexPrint::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 wCCIndexPrint::DrawTextPage(wxDC *dc, int page)
// Write page by page with margins, header, and page numbers
{
  dc->SetFont(itemFont); // Set the text font for the item bar
  dc->SetBackgroundMode(wxTRANSPARENT);
  ScaleDC(dc);
  
  start_x = (float)(logUnitsFactor * MSWPrintSetup->lr_margin);
  start_y = (float)(logUnitsFactor * MSWPrintSetup->tb_margin);

  int end = page * MSWPrintSetup->cards_per_page;  // Last line to write
  int start = end - MSWPrintSetup->cards_per_page; // First line to write
  float xpos = start_x; 
  float ypos = start_y;
  int char_offset = int((MSWPrintSetup->cell_length + 1) * char_width);
  float x_offset = xpos;
  int cards = 0;
  CCIndex ccindex(DB);
  
  for(int i = 0; i < start; i++) { // Find the next set of objects
    if(!dllist->IsHeader(dllistptr)) 
      dllistptr = dllistptr->GetNext();
  }

  while(!dllist->IsHeader(dllistptr)) {
    ccindex.ReadObject(dllistptr->Data.object_address);
    PrintwxText(dc, (char *)KeyName, ccindex.GetCardName(), char_offset,
		&xpos, &ypos);

    PrintwxText(dc, (char *)M1Name, ccindex.GetCompanyName(), char_offset,
		&xpos, &ypos);

    PrintwxText(dc, (char *)M2Name, ccindex.GetDepartmentName(), char_offset,
		&xpos, &ypos);

    PrintwxText(dc, (char *)M3Name, ccindex.GetPhoneNumber(), char_offset,
		&xpos, &ypos);

    PrintwxText(dc, (char *)M4Name, ccindex.GetFaxNumber(), char_offset,
		&xpos, &ypos);

    PrintwxText(dc, (char *)M5Name, ccindex.GetCellNumber(), char_offset,
		&xpos, &ypos);

    PrintwxText(dc, (char *)M6Name, ccindex.GetBeeperNumber(), char_offset,
		&xpos, &ypos);

    PrintwxText(dc, (char *)M7Name, ccindex.GetEmailAddress(), char_offset,
		&xpos, &ypos);
    
    PrintMultiText(dc, (char *)M8Name, ccindex.GetStreetAddress(), char_offset,
		   &xpos, &ypos);
    
    PrintMultiText(dc, (char *)M9Name, ccindex.GetInternetURLS(), char_offset,
		   &xpos, &ypos);
    
    PrintMultiText(dc, (char *)Comments, ccindex.GetCommentsBlock(),
		   char_offset,  &xpos, &ypos);

    ypos += (float)(logUnitsFactor * MSWPrintSetup->card_spacing);

    cards++;
    if(cards >= MSWPrintSetup->cards_per_page) break;
    dllistptr = dllistptr->GetNext();
  }
  
  char time_buf[255];      // Buffer used to hold time/date string
  GetSystemTime(time_buf); // Part of the PostScript Driver class
  WritePageHeader(dc, mswpDocumentName, time_buf);
  WritePageNumber(dc, page);
}

void wCCIndexPrint::PrintwxText(wxDC *dc, char *title, char *str,
				int char_offset, float *xpos, float *ypos)
// Print a single line of a wxText item
{
  float textW, textH;
  float x_offset = *xpos;
  dc->SetFont(itemFont); 
  PrintText(dc, (char *)title, MSWPrintSetup->cell_length, x_offset, *ypos); 
  dc->GetTextExtent((char *)title, &textW, &textH);
  x_offset += (char_offset + 1);
  dc->SetFont(printerFont); 
  PrintText(dc, str, x_offset, *ypos); 
  *ypos += (textH + mswpSEPARATOR);
}

void wCCIndexPrint::PrintMultiText(wxDC *dc, char *title, char *str,
				   int char_offset, float *xpos, float *ypos)
// Prints four lines of a wxMultiText box
{
  char words[MAXWORDS][MAXWORDLENGTH];
  int num;
  int offset;
  const char dchar = '\n';  // Text delimiter
  UString line_buf;
  int i, j;
  float textW, textH;
  float x_offset = *xpos;
  
  dc->SetFont(itemFont); 
  PrintText(dc, title, MSWPrintSetup->cell_length,
	    x_offset, *ypos); 
  x_offset += (char_offset +1);
  dc->GetTextExtent(title, &textW, &textH);
  dc->SetFont(printerFont); 
    
  for(i = 0; i < MAXWORDS; i++) {
    for(j = 0; j < MAXWORDLENGTH; j++)
      words[i][j] = '\0';
  }
    
  parse(str, words, &num, dchar);
  if(words[0][0] != '\0') {
    line_buf = words[0];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    PrintText(dc, line_buf.c_str(), x_offset, *ypos); 
  }

  *ypos += (textH + mswpSEPARATOR);

  if(words[1][0] != '\0') {
    line_buf = words[1];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    PrintText(dc, line_buf.c_str(), x_offset, *ypos); 
  }
  
  *ypos += (textH + mswpSEPARATOR);
    
  if(words[2][0] != '\0') {
    line_buf = words[2];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    PrintText(dc, line_buf.c_str(), x_offset, *ypos); 
  }
  
  *ypos += (textH + mswpSEPARATOR);

  if(words[3][0] != '\0') {
    line_buf = words[3];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    PrintText(dc, line_buf.c_str(), x_offset, *ypos); 
  }

  *ypos += (textH + mswpSEPARATOR);
}

void wCCIndexPrint::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 wCCIndexPrint::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);
      
    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);
  }

  dc->SetPen(wxBLACK_PEN);
  yExtentName += mswpLINE_WIDTH;
  dc->DrawLine(leftMarginLogical, topMarginLogical+ yExtentName,
	       rightMarginLogical, topMarginLogical+ yExtentName);
}

void wCCIndexPrint::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 buf[255];
  sprintf(buf, "PAGE %d", pagenum);
  dc->GetTextExtent(buf, &xExtent, &yExtent);
  float xPos = (float)(((((page_width - leftMargin - rightMargin)/2.0)
			 +leftMargin)*logUnitsFactor) - (xExtent/2.0));
  PrintText(dc, buf, xPos, bottomMarginLogical);

  dc->DrawLine(leftMarginLogical, bottomMarginLogical- yExtent,
	       rightMarginLogical, bottomMarginLogical- yExtent);
}

void wCCIndexPrint::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 wCCIndexPrint::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 wCCIndexPrint::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 wCCIndexPrint::PrintText(wxDC *dc, int i, float xpos, float ypos)
{
  char buf[255];
  sprintf(buf, "%d", i);
  PrintText(dc, buf, xpos, ypos);
}

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

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

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

#endif // __USE_MSW_PRINTING__

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

