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

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

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

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

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

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

// Output limit per string for ASCII printing
const int StringOffset = 16;   // String limits

// 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;     // Objedct Change Panel Search button
wxButton *btn_ch_cm = 0;     // Object Change Panel Commit button
wxButton *btn_ch_can = 0;    // Object Change Panel Cancel button
wxButton *btn_ch_close = 0;  // Object Change Panel Close button
wxButton *btn_ch_revert = 0; // Object Change Panel Revert button 

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_al = 0;     // Object Display Panel Anaylze button
wxButton *btn_ds_close = 0;  // Object Display Panel Close button

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

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

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

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


MyApp::MyApp()
// Testing of resources
{

}

wxFrame *MyApp::OnInit(void)
// Main program equivalent, creating windows and returning main app frame
{
  // 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 test results");
  db_menu->AppendSeparator();
  db_menu->Append(DB_ADD, "&Add", "Add a test entry to the database");
  db_menu->AppendSeparator();
  db_menu->Append(DB_CHANGE, "&Change", "Change a test in the database");
  
  // Find menu
  wxMenu *find_menu = new wxMenu;
  const char *Prompt = "Find by ";
  UString s(Prompt); int PromptLen = strlen(Prompt); int StringLen = 0;

  s = s + KeyName; StringLen = strlen(KeyName);
  find_menu->Append(FIND_BY_YEAR, s.c_str(), s.c_str());
  s.DeleteAt(PromptLen, StringLen);
  find_menu->AppendSeparator();
  
  s = s + M7Name; StringLen = strlen(M2Name);
  find_menu->Append(FIND_BY_COMMENTS, s.c_str(), s.c_str());
  s.DeleteAt(PromptLen, StringLen);

  // Print menu
  wxMenu *print_menu = new wxMenu;
#ifdef __USE_MSW_PRINTING__
  print_menu->Append(WXPRINT_PRINT, "Print",
		     "Print all test results to the selected printer");
  print_menu->AppendSeparator();
  print_menu->Append(WXPRINT_PRINTER_SETUP, "Printer Setup",
		     "Configure the seleted printer");
  print_menu->Append(WXPRINT_PAGE_SETUP, "Page Setup",
		     "Set the page parameters before printing");
  print_menu->AppendSeparator();
  print_menu->Append(WXPRINT_PREVIEW, "Print Preview ",
		     "Preview before printing all test results");
  print_menu->AppendSeparator();
#endif // __USE_MSW_PRINTING__

  print_menu->Append(PRINT_POSTSCRIPT,
		     "Print to PostScript File",
		     "Print all test results to a PostScript file");
  print_menu->AppendSeparator();
  print_menu->Append(PRINT_ASCII, "Print to Text File",
		     "Print all test results 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, 390, 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->Month = new wxText(frame->apanel, NULL,
				     "MONTH", "",-1,-1,50,-1); 

  frame->addText->Day = new wxText(frame->apanel, NULL,
				   "DAY", "",-1,-1,50,-1); 

  frame->addText->Year = new wxText(frame->apanel, NULL,
				    "YEAR", "",-1,-1,50,-1); 
  wxMessage *ap_mdy = new wxMessage(frame->apanel, "(MM/DD/YYYY Format)"); 

  frame->apanel->NewLine();
  frame->addText->SG = new wxText(frame->apanel, (wxFunction)&text_proc,
				  (char *)M2Name, "", -1, -1, 75, -1);
  wxMessage *ap_sg = new wxMessage(frame->apanel,
				   "(Hydrometer reading for salintiy)"); 

  frame->apanel->NewLine();
  frame->addText->PH = new wxText(frame->apanel, (wxFunction)&text_proc,
				  (char *)M3Name, "", -1, -1, 75, -1);
  wxMessage *ap_ph = new wxMessage(frame->apanel, "(Percentage of hydrogen)"); 
  frame->apanel->NewLine();
  frame->addText->Ammonia = new wxText(frame->apanel, (wxFunction)&text_proc,
				     (char *)M4Name, "", -1, -1, 75, -1);
  wxMessage *ap_am = new wxMessage(frame->apanel,
				"(NH3-Nitrogen in milligrams/liter or ppm)"); 
  frame->apanel->NewLine();
  frame->addText->Nitrate = new wxText(frame->apanel, (wxFunction)&text_proc,
				       (char *)M5Name, "", -1, -1, 75, -1); 
  wxMessage *ap_na = new wxMessage(frame->apanel,
			   "(NO3-Nitrogen in milligrams/liter or ppm)"); 
  frame->apanel->NewLine();

  frame->addText->Nitrite = new wxText(frame->apanel, (wxFunction)&text_proc,
				       (char *)M6Name, "", -1, -1, 75, -1); 
  wxMessage *ap_ni = new wxMessage(frame->apanel,
			   "(NO2-Nitrogen in milligrams/liter or ppm)"); 
  frame->apanel->NewLine();

  frame->addText->Comments = new wxMultiText(frame->apanel, (wxFunction)NULL,
					     (char *)M7Name, "",
					     -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, 400, 490);

  // 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->Month = new wxText(frame->cpanel, NULL,
				     "MONTH", "",-1,-1,50,-1); 

  frame->changeText->Day = new wxText(frame->cpanel, NULL,
				   "DAY", "",-1,-1,50,-1); 

  frame->changeText->Year = new wxText(frame->cpanel, NULL,
				    "YEAR", "",-1,-1,50,-1); 
  wxMessage *cp_mdy = new wxMessage(frame->cpanel, "(MM/DD/YYYY Format)"); 

  frame->cpanel->NewLine();
  frame->changeText->SG = new wxText(frame->cpanel, (wxFunction)&text_proc,
				  (char *)M2Name, "", -1, -1, 75, -1);
  wxMessage *cp_sg = new wxMessage(frame->cpanel,
				"(Hydrometer reading for salintiy)"); 

  frame->cpanel->NewLine();
  frame->changeText->PH = new wxText(frame->cpanel, (wxFunction)&text_proc,
				  (char *)M3Name, "", -1, -1, 75, -1);
  wxMessage *cp_ph = new wxMessage(frame->cpanel, "(Percentage of hydrogen)"); 
  frame->cpanel->NewLine();
  frame->changeText->Ammonia = new wxText(frame->cpanel,
					  (wxFunction)&text_proc,
					  (char *)M4Name, "", -1, -1, 75, -1);
  wxMessage *cp_am = new wxMessage(frame->cpanel,
				"(NH3-Nitrogen in milligrams/liter or ppm)"); 
  frame->cpanel->NewLine();
  frame->changeText->Nitrate = new wxText(frame->cpanel,
					  (wxFunction)&text_proc,
					  (char *)M5Name, "", -1, -1, 75, -1); 
  wxMessage *cp_na = new wxMessage(frame->cpanel,
				"(NO3-Nitrogen in milligrams/liter or ppm)"); 
  frame->cpanel->NewLine();

  frame->changeText->Nitrite = new wxText(frame->cpanel,
					  (wxFunction)&text_proc,
					  (char *)M6Name, "", -1, -1, 75, -1); 
  wxMessage *cp_ni = new wxMessage(frame->cpanel,
				"(NO2-Nitrogen in milligrams/liter or ppm)"); 
  frame->cpanel->NewLine();

  frame->changeText->Comments = new wxMultiText(frame->cpanel,
						(wxFunction)NULL,
						(char *)M7Name, "",
						-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,
			    400, 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->Month = new wxText(frame->dpanel, NULL,
				     "MONTH", "",-1,-1,50,-1); 
  frame->displayText->Month->SetEditable(FALSE);
  frame->displayText->Day = new wxText(frame->dpanel, NULL,
				   "DAY", "",-1,-1,50,-1); 
  frame->displayText->Day->SetEditable(FALSE);
  frame->displayText->Year = new wxText(frame->dpanel, NULL,
				    "YEAR", "",-1,-1,50,-1); 
  frame->displayText->Year->SetEditable(FALSE);
  wxMessage *dp_mdy = new wxMessage(frame->dpanel, "(MM/DD/YYYY Format)"); 

  frame->dpanel->NewLine();
  frame->displayText->SG = new wxText(frame->dpanel, (wxFunction)&text_proc,
				  (char *)M2Name, "", -1, -1, 75, -1);
  wxMessage *dp_sg = new wxMessage(frame->dpanel,
				   "(Hydrometer reading for salintiy)"); 
  frame->displayText->SG->SetEditable(FALSE);
  frame->dpanel->NewLine();
  frame->displayText->PH = new wxText(frame->dpanel, (wxFunction)&text_proc,
				  (char *)M3Name, "", -1, -1, 75, -1);
  wxMessage *dp_ph = new wxMessage(frame->dpanel, "(Percentage of hydrogen)"); 
  frame->displayText->PH->SetEditable(FALSE);
  frame->dpanel->NewLine();
  frame->displayText->Ammonia = new wxText(frame->dpanel,
					   (wxFunction)&text_proc,
					   (char *)M4Name, "", -1, -1, 75, -1);
  wxMessage *dp_am = new wxMessage(frame->dpanel,
				"(NH3-Nitrogen in milligrams/liter or ppm)"); 
  frame->displayText->Ammonia->SetEditable(FALSE);
  frame->dpanel->NewLine();
  frame->displayText->Nitrate = new wxText(frame->dpanel,
					   (wxFunction)&text_proc,
					   (char *)M5Name, "",
					   -1, -1, 75, -1); 
  wxMessage *dp_na = new wxMessage(frame->dpanel,
			   "(NO3-Nitrogen in milligrams/liter or ppm)"); 
  frame->displayText->Nitrate->SetEditable(FALSE);
  frame->dpanel->NewLine();
  frame->displayText->Nitrite = new wxText(frame->dpanel,
					   (wxFunction)&text_proc,
					   (char *)M6Name, "",
					   -1, -1, 75, -1); 
  wxMessage *dp_ni = new wxMessage(frame->dpanel,
			   "(NO2-Nitrogen in milligrams/liter or ppm)"); 
  frame->displayText->Nitrite->SetEditable(FALSE);
  frame->dpanel->NewLine();
  frame->displayText->Comments = new wxMultiText(frame->dpanel,
						 (wxFunction)NULL,
						 (char *)M7Name, "",
						 -1, -1, 200, 75, wxHSCROLL);

  frame->displayText->Comments->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

  btn_ds_al = new wxButton(frame->dpanel, (wxFunction)&DsBtnProc, "Analyze");
  btn_ds_al->SetClientData((char*)DISPLAY_BUTTON_ANALYZE); // 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_lines_per_page = 0;
  int mswp_cell_length = 0;
  int mswp_font_size = 0;
  int mswp_left_margin = 0;
  char *mswp_orientation = 0;
  char *mswp_doc_name = 0;
  char *mswp_font = 0;
#endif // __USE_MSW_PRINTING__
  
  // Look for CfgFile name in environment
  if((CurrentCfgFile = getenv(EnvSetting)) == 0)
     CfgFile = (char *)DefaultCfgFile;
  else
    CfgFile = CurrentCfgFile;

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

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

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

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

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

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

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

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

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

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

#endif // __USE_MSW_PRINTING__

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

  // Initialize global database pointer
  // NOTE: Index file and data file will share the same file name
  // with different file extensions. The data file will have a 
  // .pod extension and the index file will have a .btx extension.
  if(AdminRights == 1)
    DB = new POD(FileName, RWMode, 1, CacheSize);
  else
    DB = new POD(FileName, ROMode, 1, CacheSize);
  
  amtank = new MarineTank(DB); // Initialize global buffer pointer
  cmtank = new MarineTank(DB); // Initialize global buffer pointer
  dmtank = new MarineTank(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(amtank) delete amtank;
  if(cmtank) delete cmtank;
  if(dmtank) delete dmtank;
  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_DISPLAY:
      DisplayDB(*textWin);
      break;

    case FIND_BY_YEAR:
      FindBy(*textWin, KeyName, YEAR);
      break;
      
    case FIND_BY_COMMENTS:
      FindBy(*textWin, M7Name, COMMENTS);
      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);
	
	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;

	wxPrinter printer(&printData);
        wMTankPrint 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 wMTankPrint((char *)ProgramName),
                             new wMTankPrint((char *)ProgramName),
			     &printData);

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

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

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

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

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

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

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

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

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

	wxPageDialog->Show(TRUE);
	break;
      }
#endif //  __USE_MSW_PRINTING__
    
    case PRINT_POSTSCRIPT :
      PostScriptPrint(*textWin);
      break;

    case PRINT_ASCII :
      ASCIIPrintAll(*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)
{
  Month = Day = Year = SG = PH = Ammonia = Nitrate = Nitrite = 0;
  Comments = 0;
}

MyText::~MyText()
{
  if(Month) delete Month;
  if(Day) delete Day;
  if(Year) delete Year;
  if(SG) delete SG;
  if(PH) delete PH;
  if(Ammonia) delete Ammonia;
  if(Nitrate) delete Nitrate;
  if(Nitrite) delete Nitrite;
  if(Comments) delete Comments;
}

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

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

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

void AddBtnProc(wxButton& but, wxCommandEvent& event)
{
  UString sbuf; // Character string input buffer
  long tag = (long)but.GetClientData() ;
  __UBYTE__ month, day;
  UINT16 year;
  double sg;
  double ph;
  double ammonia;
  int nitrate, i;
  double nitrite;
  UString comments;
  char sbuffer[sbuffer_len];
  
  if(tag == ADD_BUTTON_ADD) {
    month = (__UBYTE__)atoi(frame->addText->Month->GetValue());
    day = (__UBYTE__)atoi(frame->addText->Day->GetValue());
    year = atoi(frame->addText->Year->GetValue());

    if(month <= 0 || day <= 0 || year <=0) {
      sbuf = sbuf + "Invalid " + KeyName + "  input.";
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      return;
    }

    sg = atof(frame->addText->SG->GetValue());
    ph = atof(frame->addText->PH->GetValue());
    ammonia = atof(frame->addText->Ammonia->GetValue());
    nitrate = atoi(frame->addText->Nitrate->GetValue());
    nitrite = atof(frame->addText->Nitrite->GetValue());
    comments = frame->addText->Comments->GetValue();

    MarineTank mtank(DB);
    mtank.SetDate(month, day, year);
    FAU addr = mtank.FindObject();
    
    if(addr) {
      sbuf = sbuf + "An Entry for " + mtank.GetDateStr() +  " already exists!";
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      return;
    }

    amtank->SetDate(month, day, year);
    amtank->SetSG(sg);
    amtank->SetPH(ph);
    amtank->SetAmmonia(ammonia);
    amtank->SetNitrate(nitrate);
    amtank->SetNitrite(nitrite);
    amtank->SetComments(comments);
    addr = amtank->AddObject(0); // Write the object to the file

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

    AnalyzeWaterTest(*frame->textWin, *amtank);
    ClearAddPanel();
    
    if(displaying_list) { // Update any list entries
      MTankInMemCopy inmemcopy(amtank->GetDateStr(), amtank->ObjectAddress(),
			       amtank->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(amtank->GetMonthStr())
      frame->addText->Month->SetValue(amtank->GetMonthStr());
    if(amtank->GetDayStr())
      frame->addText->Day->SetValue(amtank->GetDayStr());
    if(amtank->GetYearStr())
      frame->addText->Year->SetValue(amtank->GetYearStr());
    sg = amtank->GetSG();
    sprintf(sbuffer, "%.3f", sg);
    frame->addText->SG->SetValue(sbuffer);

    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    ph = amtank->GetPH();
    sprintf(sbuffer, "%.2f", ph);
    frame->addText->PH->SetValue(sbuffer);
    
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    ammonia = amtank->GetAmmonia();
    sprintf(sbuffer, "%.2f", ammonia);
    frame->addText->Ammonia->SetValue(sbuffer);

    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    nitrate = amtank->GetNitrate();
    sprintf(sbuffer, "%d", nitrate);
    frame->addText->Nitrate->SetValue(sbuffer);

    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    nitrite = amtank->GetNitrite();
    sprintf(sbuffer, "%.2f", nitrite);
    frame->addText->Nitrite->SetValue(sbuffer);

    if(amtank->GetComments())
      frame->addText->Comments->SetValue(amtank->GetComments());
  }
}

void ClearAddPanel()
{
  frame->addText->Month->SetValue("");
  frame->addText->Day->SetValue("");
  frame->addText->Year->SetValue("");
  frame->addText->SG->SetValue("");
  frame->addText->PH->SetValue("");
  frame->addText->Ammonia->SetValue("");
  frame->addText->Nitrate->SetValue("");
  frame->addText->Nitrite->SetValue("");
  frame->addText->Comments->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";
  
  
  APanelFrame->Show(TRUE); // Show the Add Panel Frame
}

void ChBtnProc(wxButton& but, wxCommandEvent& event)
{
  UString sbuf; // Character input string buffer
  long tag = (long)but.GetClientData() ;
  __UBYTE__ month, day;
  UINT16 year;
  double sg;
  double ph;
  double ammonia;
  int nitrate, i;
  double nitrite;
  UString comments;
  char sbuffer[sbuffer_len];
  
  if(tag == CHANGE_BUTTON_SH) {  
    month = (__UBYTE__)atoi(frame->changeText->Month->GetValue());
    day = (__UBYTE__)atoi(frame->changeText->Day->GetValue());
    year = atoi(frame->changeText->Year->GetValue());

    if(month <= 0 || day <= 0 || year <=0) {
      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;
    }
    
    MarineTank mtank(DB);
    mtank.SetDate(month, day, year);
    FAU addr = mtank.FindObject();

    if(!addr) {
      sbuf = sbuf + "Could not find an entry for " + mtank.GetDateStr();
      frame->textWin->WriteText(sbuf.c_str());
      frame->textWin->WriteText("\n");
      Error->Message(sbuf.c_str());
      btn_ch_cm->Enable(0); 
      return;
    }
    
    cmtank->Copy(mtank);
    sg = cmtank->GetSG();
    sprintf(sbuffer, "%.3f", sg);
    frame->changeText->SG->SetValue(sbuffer);

    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    ph = cmtank->GetPH();
    sprintf(sbuffer, "%.2f", ph);
    frame->changeText->PH->SetValue(sbuffer);
    
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    ammonia = cmtank->GetAmmonia();
    sprintf(sbuffer, "%.2f", ammonia);
    frame->changeText->Ammonia->SetValue(sbuffer);

    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    nitrate = cmtank->GetNitrate();
    sprintf(sbuffer, "%d", nitrate);
    frame->changeText->Nitrate->SetValue(sbuffer);

    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    nitrite = cmtank->GetNitrite();
    sprintf(sbuffer, "%.2f", nitrate);
    frame->changeText->Nitrite->SetValue(sbuffer);

    if(cmtank->GetComments())
      frame->changeText->Comments->SetValue(cmtank->GetComments());
    btn_ch_cm->Enable(1); // Enable the commit button
  }

  if(tag == CHANGE_BUTTON_CM) {
    month = (__UBYTE__)atoi(frame->changeText->Month->GetValue());
    day = (__UBYTE__)atoi(frame->changeText->Day->GetValue());
    year = atoi(frame->changeText->Year->GetValue());

    if(month <= 0 || day <= 0 || year <=0) {
      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;
    }

    MarineTank changed(DB); // Buffer used to store changes
    month = (__UBYTE__)atoi(frame->changeText->Month->GetValue());
    day = (__UBYTE__)atoi(frame->changeText->Day->GetValue());
    year = atoi(frame->changeText->Year->GetValue());
    changed.SetDate(month, day, year);
    sg = atof(frame->changeText->SG->GetValue());
    changed.SetSG(sg);
    ph = atof(frame->changeText->PH->GetValue());
    changed.SetPH(ph);
    ammonia = atof(frame->changeText->Ammonia->GetValue());
    changed.SetAmmonia(ammonia);
    nitrate = atoi(frame->changeText->Nitrate->GetValue());
    changed.SetNitrate(nitrate);
    nitrite = atof(frame->changeText->Nitrite->GetValue());
    changed.SetNitrite(nitrite);
    changed.SetComments(frame->changeText->Comments->GetValue());

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

    if(changed.FullCompare(*cmtank)) { // The object has not changed
      sbuf = sbuf + "No changes to commit for " + cmtank->GetDateStr();
      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;
    }

    if(changed.GetDate() != cmtank->GetDate()) {
      if((__LWORD__)changed.FindObject()) {
	sbuf = sbuf + "An Entry for " + changed.GetDateStr() + \
	  " 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 = cmtank->ObjectAddress(); // Record address before deleting
    cmtank->DeleteObject(); // Remove the object  
    changed.AddObject(0); // Add the changed object back to the database

    sbuf = sbuf + "Commited changes for " + changed.GetDateStr();
    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;
      }
      MTankInMemCopy inmemcopy(cmtank->GetDateStr(), oa, cmtank->GetClassID());
      MTankInMemCopy ob(changed.GetDateStr(), changed.ObjectAddress(),
			changed.GetClassID());
      DNode<MTankInMemCopy> *prevptr = dllistptr;
      dllistptr = (DNode<MTankInMemCopy> *)dllist->Find(inmemcopy);
      if(dllistptr) {
	dllistptr->Data = ob;
	if(displaying_list) {
	  DPanelFrame->Show(TRUE);
	  DisplayObject(changed);
	}
      }
      else {
       dllistptr = prevptr;
       dllistptr->Data = ob;
       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(cmtank->GetMonthStr())
      frame->changeText->Month->SetValue(cmtank->GetMonthStr());
    if(cmtank->GetDayStr())
      frame->changeText->Day->SetValue(cmtank->GetDayStr());
    if(cmtank->GetYearStr())
      frame->changeText->Year->SetValue(cmtank->GetYearStr());
    sg = cmtank->GetSG();
    sprintf(sbuffer, "%.3f", sg);
    frame->changeText->SG->SetValue(sbuffer);

    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    ph = cmtank->GetPH();
    sprintf(sbuffer, "%.2f", ph);
    frame->changeText->PH->SetValue(sbuffer);
    
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    ammonia = cmtank->GetAmmonia();
    sprintf(sbuffer, "%.2f", ammonia);
    frame->changeText->Ammonia->SetValue(sbuffer);

    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    nitrate = cmtank->GetNitrate();
    sprintf(sbuffer, "%d", nitrate);
    frame->changeText->Nitrate->SetValue(sbuffer);

    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    nitrite = cmtank->GetNitrite();
    sprintf(sbuffer, "%.2f", nitrate);
    frame->changeText->Nitrite->SetValue(sbuffer);

    if(cmtank->GetComments())
      frame->changeText->Comments->SetValue(cmtank->GetComments());
    btn_ch_cm->Enable(0);
  }
}

void ClearChangePanel()
{
  frame->changeText->Month->SetValue("");
  frame->changeText->Day->SetValue("");
  frame->changeText->Year->SetValue("");
  frame->changeText->SG->SetValue("");
  frame->changeText->PH->SetValue("");
  frame->changeText->Ammonia->SetValue("");
  frame->changeText->Nitrate->SetValue("");
  frame->changeText->Nitrite->SetValue("");
  frame->changeText->Comments->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 DsBtnProc(wxButton& but, wxCommandEvent& event)
{
  char *sbuf = 0; // Character string buffer
  long tag = (long)but.GetClientData() ;
  MarineTank mtank(DB);
  
  if(tag == DISPLAY_BUTTON_NX) {
    if(!dllistptr) return;
    if(!dllist) return;
    if(dllist->IsEmpty()) {
      ClearDisplayPanel();
      wxMessageBox("The display list is empty\n",
		   "Program Message", wxOK|wxCENTRE);
      return;
    }
    else
      dllistptr = dllistptr->GetNext();

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

  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)) {
      mtank.ReadObject(dllistptr->Data.object_address);
      DisplayObject(mtank);
    }
    else {
      wxMessageBox("Reached the beginning of the list\n",
		   "Program Message", wxOK|wxCENTRE);
      dllistptr = dllist->GetBack();
      mtank.ReadObject(dllistptr->Data.object_address);
      DisplayObject(mtank);
    }
  }

  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()) {
      mtank.ReadObject(dllistptr->Data.object_address);
      cmtank->Copy(mtank);
    }
    else
      cmtank->Copy(*dmtank);
    frame->changeText->Month->SetValue(cmtank->GetMonthStr());
    frame->changeText->Day->SetValue(cmtank->GetDayStr());
    frame->changeText->Year->SetValue(cmtank->GetYearStr());
    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);
    dllist->Clear();
    return;
  }

  if(tag == DISPLAY_BUTTON_REMOVE) {
    if(dllist->IsEmpty()) {
      wxMessageBox("The display list is empty\n",
		   "Program Message", wxOK|wxCENTRE);
      return;
    }
    else {
      if(!AdminRights) {
	wxMessageBox("You do not have Admin User Privileges\n",
		     "Program Message", wxOK|wxCENTRE);
	return;
      }

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

      if(yn == wxNO) return;

      MTankInMemCopy listdata(dmtank->GetDateStr(), dmtank->ObjectAddress(),
			      dmtank->GetClassID());

      dmtank->DeleteObject();

      *(frame->textWin) << "Removed entry for: "
			<< dmtank->GetDateStr() << "\n";

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

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

    if(!dllist->IsHeader(dllistptr)) 
      mtank.ReadObject(dllistptr->Data.object_address);
    else
      return;
    
    AnalyzeWaterTest(*frame->textWin, mtank);
  }
}

void ClearDisplayPanel()
{
  frame->displayText->Month->SetValue("");
  frame->displayText->Day->SetValue("");
  frame->displayText->Year->SetValue("");
  frame->displayText->SG->SetValue("");
  frame->displayText->PH->SetValue("");
  frame->displayText->Ammonia->SetValue("");
  frame->displayText->Nitrate->SetValue("");
  frame->displayText->Nitrite->SetValue("");
  frame->displayText->Comments->SetValue("");
}

void AnalyzeWaterTest(MyTextWindow &textWin, MarineTank &mtank)
{
  textWin.Clear();
    
  textWin << "Analyzing Salt Water Test: " << mtank.GetDateStr() << "\n";
  textWin << "\n";
  char sbuffer[sbuffer_len];
  int i;

  sprintf(sbuffer, "%.3f", (double)mtank.GetSG());
  textWin << "Specific Gravity reads: " << sbuffer << "\n";
  if(mtank.GetSG() < sg_lower_limit) {
    textWin << "The salinity is to low." << "\n";
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.3f", sg_lower_limit);
    textWin << "Acceptable range = " << sbuffer;
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.3f", sg_upper_limit);
    textWin << " to " << sbuffer << "\n";
  }
  else if(mtank.GetSG() > sg_upper_limit) {
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.3f", sg_lower_limit);
    textWin << "The salinity is to high." << "\n";
    textWin << "Acceptable range = " << sbuffer;
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.3f", sg_upper_limit);
    textWin << " to " << sbuffer << "\n";
  }
  else {
    textWin << "The salinity tests good." << "\n";
  }

  textWin << "\n";
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "%.2f", (double)mtank.GetPH());
  textWin << "The pH reads: " << sbuffer << "\n";
  if(mtank.GetPH() < ph_lower_limit) {
    textWin << "The pH is to low." << "\n";
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", ph_lower_limit);
    textWin << "Acceptable range = " << sbuffer;
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", ph_upper_limit);
    textWin << " to " << sbuffer << "\n";
  }
  else if(mtank.GetPH() > ph_upper_limit) {
    textWin << "The pH is to high." << "\n";
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", ph_lower_limit);
    textWin << "Acceptable range = " << sbuffer;
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", ph_upper_limit);
    textWin << " to " << sbuffer << "\n";
  }
  else {
    textWin << "The pH tests good." << "\n";
  }

  textWin << "\n";
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "%.2f", (double)mtank.GetAmmonia());
  textWin << "Ammonia level reads: " << sbuffer
	  <<  " mg/liter" << "\n";
  if(mtank.GetAmmonia() > ammonia_limit) {
    textWin << "Ammonia level is to high." << "\n";
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", ammonia_limit);
    textWin << "Acceptable range is below " << sbuffer << " mg/liter" << "\n"; 
  }
  else {
    textWin << "Ammonia level tests good." << "\n";
  }

  textWin << "\n";
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "%d", (int)mtank.GetNitrate());
  textWin << "Nitrate level reads: " << sbuffer << " mg/liter" << "\n";
  if(mtank.GetNitrate() >= nitrate_limit_IV &&
     mtank.GetNitrate() < nitrate_limit_fish) {
    textWin << "Nitrate level is deadly to invertebrates." << "\n";
    textWin << "Acceptable range is below " << nitrate_limit_IV
	    <<  " for invertebrates." << "\n";
    textWin << "Acceptable range for fish is below " << nitrate_limit_fish
	    <<  " mg/liter" << "\n"; 
  }
  else if(mtank.GetNitrate() >= nitrate_limit_fish) {
    textWin << "Nitrate level is to high." << "\n";
    textWin << "Acceptable range is below " << nitrate_limit_fish
	    <<  " mg/liter" << "\n"; 
    textWin << "This indicates poor water quality. " << "\n"; 
  }
  else {
    textWin << "Nitrate level tests good." << "\n";
  }

  textWin << "\n";
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  sprintf(sbuffer, "%.2f", (double)mtank.GetNitrite());
  textWin << "Nitrite level reads: " << sbuffer
	  <<  " mg/liter" << "\n";

  if(mtank.GetNitrite() > nitrite_limit) {
    textWin << "Nitrite level is to high." << "\n";
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", nitrite_limit);
    textWin << "Acceptable range is below " << sbuffer << " mg/liter" << "\n"; 
  }
  else {
    textWin << "Nitrite level tests good." << "\n";
  }
}

void DisplayObject(MarineTank &mtank)
{
  dmtank->Copy(mtank); // Initialize global display object

  double sg;
  double ph;
  double ammonia;
  int nitrate, i;
  double nitrite;
  char sbuffer[sbuffer_len];

  if(mtank.GetMonthStr())
    frame->displayText->Month->SetValue(mtank.GetMonthStr());
  if(mtank.GetDayStr())
    frame->displayText->Day->SetValue(mtank.GetDayStr());
  if(mtank.GetYearStr())
    frame->displayText->Year->SetValue(mtank.GetYearStr());
  sg = mtank.GetSG();
  sprintf(sbuffer, "%.3f", sg);
  frame->displayText->SG->SetValue(sbuffer);
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  ph = mtank.GetPH();
  sprintf(sbuffer, "%.2f", ph);
  frame->displayText->PH->SetValue(sbuffer);
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  ammonia = mtank.GetAmmonia();
  sprintf(sbuffer, "%.2f", ammonia);
  frame->displayText->Ammonia->SetValue(sbuffer);
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  nitrate = mtank.GetNitrate();
  sprintf(sbuffer, "%d", nitrate);
  frame->displayText->Nitrate->SetValue(sbuffer);
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  nitrite = mtank.GetNitrite();
  sprintf(sbuffer, "%.2f", nitrate);
  frame->displayText->Nitrite->SetValue(sbuffer);
  
  if(mtank.GetComments())
    frame->displayText->Comments->SetValue(mtank.GetComments());

  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<MTankInMemCopy> *ptr = (RBNode<MTankInMemCopy> *)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)
{
  MarineTank mtank(DB);
  int num = 0;
  
  textWin.Clear();
  textWin << "Displaying the database objects." << "\n";
    
  if(DB->RebuildIndex()) {
    textWin << "\n";
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

  num_objects = LoadIndexKeys();
  
  if(num_objects == 0) {
    textWin << "Nothing to display." << "\n";
    dllist->Clear();
    return;
  }

  textWin << "Displaying all objects in the database." << "\n";
  textWin << "Displaying " << num_objects << " objects..." << "\n";

  dllistptr = dllist->GetFront();
  
  // Display the first object
  mtank.ReadObject(dllistptr->Data.object_address); 
  DisplayObject(mtank);
}

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

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

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

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

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

void MTankDBSearch(MarineTank &mtank, MTankDBItem 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, mtank, str, 1);
  else
    BtreeSearch(n, BtreeNodeSearch, item, mtank, 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[sbuffer_len];
    sprintf(intbuf, "%d", ObjectsFound); 
    comp = comp + "Found " + intbuf + " matching.\nDisplay the objects?";
    yn = wxMessageBox(comp.c_str(), "Program Message", wxYES_NO|wxCENTRE);
  }
  else {
    textWin << "Found matching entry for: " << str.c_str() << "\n";
    comp = comp + "Found matching entry for: " + str +  \
      "\nDisplay the object?";
    yn = wxMessageBox(comp.c_str(), "Program Message", wxYES_NO|wxCENTRE);
  }

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

void ExportToASCII(MyTextWindow &textWin)
{
  const char dchar = '\t';   // Text delimiter
  const char *Fill = "None"; // Fill character string
  double sg;
  double ph;
  double ammonia;
  int nitrate, i, yn;
  double nitrite;

  char sbuffer[sbuffer_len];
  MarineTank mtank(DB);
  
  textWin.Clear();
  textWin << "Exporting database to ASCII file delimited by tabs..." << "\n";

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

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

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

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

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

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

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

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

  ASPrint(M7Name, stream, strlen(M7Name));
  stream << asLineFeed;
  
  int exports = 0;

  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    mtank.ReadObject(dllistptr->Data.object_address);

    ASPrint(mtank.GetDateStr(), stream, strlen(mtank.GetDateStr()));
    stream << dchar;

    sg = mtank.GetSG();
    sprintf(sbuffer, "%.3f", sg);
    ASPrint(sbuffer, stream, strlen(sbuffer));
    stream << dchar;
  
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    ph = mtank.GetPH();
    sprintf(sbuffer, "%.2f", ph);
    ASPrint(sbuffer, stream, strlen(sbuffer));
    stream << dchar;
  
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    ammonia = mtank.GetAmmonia();
    sprintf(sbuffer, "%.2f", ammonia);
    ASPrint(sbuffer, stream, strlen(sbuffer));
    stream << dchar;
  
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    nitrate = mtank.GetNitrate();
    sprintf(sbuffer, "%d", nitrate);
    ASPrint(sbuffer, stream, strlen(sbuffer));
    stream << dchar;
  
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    nitrite = mtank.GetNitrite();
    sprintf(sbuffer, "%.2f", nitrate);
    ASPrint(sbuffer, stream, strlen(sbuffer));
    stream << dchar;
  
    if(!*mtank.GetComments())
      ASPrint(Fill, stream, strlen(Fill));
    else
      ExportPrintwxMultiText(mtank.GetComments(), 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(!AdminRights) {
    wxMessageBox("You do not have Admin User Privileges\n",
		 "Program Message", wxOK|wxCENTRE);
    return;
  }
  
  char words[MAXWORDS][MAXWORDLENGTH];
  char date_words[MAXWORDS][MAXWORDLENGTH];
  int num, i;
  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();
  
  MarineTank mtank(DB);
  MarineTank mtank_a(DB);
  MarineTank ob(DB);

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

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

    linecount++;
    if(num != ObjectItems && (num != 0 && num != 1)) {
      textWin << "Error in " << FileName << " import file!" << "\n";
      if(num > ObjectItems) 
	textWin << "To many items on line: ";
      if(num < ObjectItems) 
	textWin << "Not enough items on line: ";
      textWin << linecount << "\n";
      int yn = wxMessageBox("Error in import file! Continue?",
			    "Program Message", wxYES_NO|wxCENTRE);
      if(yn == wxNO) {
	  sllist.Clear();
	  return;
      }
    }
    
    if(num == ObjectItems && *words[0] != 0) {
      parse(words[0], date_words, &i, '/');
      mtank.SetDate(atoi(date_words[0]), atoi(date_words[1]),
		    atoi(date_words[2])); 
      mtank.SetSG(atof(words[1]));
      mtank.SetPH(atof(words[2]));
      mtank.SetAmmonia(atof(words[3]));
      mtank.SetNitrate(atoi(words[4]));
      mtank.SetNitrite(atof(words[5]));
      mtank.SetComments(ImportFormatwxMultiText(words[6]));

      mtank_a.SetCDate(mtank.GetDate());
      FAU addr = mtank_a.FindObject();
      
      if(addr) {
	if(updateall == 0) {
	  sbuf.DeleteAt(0, sbuf.length());
          sbuf = sbuf + mtank.GetDateStr() + " 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(mtank_a);
            if(mtank.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject(); 
            mtank.AddObject(0);
	    sbuf.DeleteAt(0, sbuf.length());
            sbuf = sbuf + "Updating entry for: " + mtank.GetDateStr();
	    textWin << sbuf.c_str() << "\n";
	    frame->SetStatusText(sbuf.c_str());
	    break;
	  case UPDATE_SKIP:
	    break;
	  case UPDATE_ENTRY:
	    updates++;
            ob.Copy(mtank_a);
            if(mtank.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject(); 
            mtank.AddObject(0);
	    sbuf.DeleteAt(0, sbuf.length());
            sbuf = sbuf + "Updating entry for: " + mtank.GetDateStr();
	    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: " + mtank.GetDateStr();
	  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 = mtank.AddObject(0); // Write the object to the file
	  imports++;
	  if(!addr) {
            textWin << "Could not add:" << mtank.GetDateStr()
		    << " 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;
  MarineTank mtank(&newdb);
  MarineTank mtank_old(DB);
  
  if(DB->RebuildIndex()) {
    textWin << "\n";
    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<MTankInMemCopy> *ptr = (RBNode<MTankInMemCopy> *)nxt;
    mtank_old.ReadObject(ptr->Data.object_address);
    mtank.Copy(mtank_old);
    mtank.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);
  MarineTank mtank(&newdb);
  MarineTank mtank_a(DB);
  MarineTank 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 == mtank.GetClassID()) { 
          mtank.ReadObject(oa);
	  mtank_a.SetCDate(mtank.GetDate());
          existing = mtank_a.FindObject();
	  if(existing) {
	    if(updateall == 0) {
	      sbuf.DeleteAt(0, sbuf.length());
              sbuf = sbuf +  mtank_a.GetDateStr() + \
		" 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(mtank);
                if(mtank.FullCompare(ob)) break; // Object has not changed
		ob.DeleteObject(); 
                ob.Copy(mtank);
		ob.AddObject(0);
		sbuf.DeleteAt(0, sbuf.length());
                sbuf = sbuf  + "Updating entry for: " + mtank.GetDateStr();
		textWin << sbuf.c_str() << "\n";
		frame->SetStatusText(sbuf.c_str());
		break;
	      case UPDATE_SKIP:
		break;
	      case UPDATE_ENTRY:
		updates++;
                ob.Copy(mtank);
                if(mtank.FullCompare(ob)) break; // Object has not changed
		ob.DeleteObject(); 
                ob.Copy(mtank);
		ob.AddObject(0);
		sbuf.DeleteAt(0, sbuf.length());
                sbuf = sbuf + "Updating entry for: " + mtank.GetDateStr();
		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: " + mtank.GetDateStr();
	      textWin << sbuf.c_str() << "\n";
	      frame->SetStatusText(sbuf.c_str());
	    }
	    else {
	      // Nothing to do
	    }
	  }
	  else {
            mtank_a.Copy(mtank);
            FAU rvaddr = mtank_a.AddObject(0); // Write object to the file
	    imports++;
	    if(!rvaddr) {
              textWin << "Could not add:" << mtank.GetDateStr()
		      << " to database" << "\n";
	      imports--;
	    }
	    
	  }
	}
	
      }
      addr = addr + vb.Length; // Goto the next variable block
    }
    else {
      addr = newdb.OpenDatabase()->VBSearch(addr); 
      if(!addr) break;
    }
  }
  
  textWin << "Imported " << imports << " objects." << "\n";
  if(updates) {
    textWin << "Updated " << updates << " objects." << "\n";
  }
}

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

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


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

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

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

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

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

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

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

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

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

void ASCIIPrintAll(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Printing database to ASCII file..." << "\n";

  int yn;
  
  if(DB->RebuildIndex()) {
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

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

  if(yn == wxNO) LoadIndexKeys();
  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; 
  }

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

  while(!dllist->IsHeader(dllistptr)) {
    mtank.ReadObject(dllistptr->Data.object_address);
    PrintLineByLine(mtank, stream);
    dllistptr = dllistptr->GetNext();
  }

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

void PrintItemBar(ofstream &stream)
{
  ASPrint(KeyName, stream, StringOffset+1);
  ASPrint(M2Name, stream, StringOffset+1);
  ASPrint(M3Name, stream, StringOffset+1);
  ASPrint(M4Name, stream, StringOffset+1);
  ASPrint(M5Name, stream, StringOffset+1);
  ASPrint(M6Name, stream, StringOffset+1);
  stream << asLineFeed;
  unsigned i = 0;
  unsigned line_len = (StringOffset+1) * 5;
  line_len += strlen(M6Name); 
  while (i++ < line_len) stream << '=';
  stream << asLineFeed;
}

void PrintLineByLine(MarineTank &mtank, ofstream &stream)
{
  double sg;
  double ph;
  double ammonia;
  int nitrate, i;
  double nitrite;
  char sbuffer[sbuffer_len];
  
  ASPrint(mtank.GetDateStr(), stream, StringOffset+1);

  sg = mtank.GetSG();
  sprintf(sbuffer, "%.3f", sg);
  ASPrint(sbuffer, stream, StringOffset+1);
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  ph = mtank.GetPH();
  sprintf(sbuffer, "%.2f", ph);
  ASPrint(sbuffer, stream, StringOffset+1);
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  ammonia = mtank.GetAmmonia();
  sprintf(sbuffer, "%.2f", ammonia);
  ASPrint(sbuffer, stream, StringOffset+1);
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  nitrate = mtank.GetNitrate();
  sprintf(sbuffer, "%d", nitrate);
  ASPrint(sbuffer, stream, StringOffset+1);
  
  for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
  nitrite = mtank.GetNitrite();
  sprintf(sbuffer, "%.2f", nitrate);
  ASPrint(sbuffer, stream, StringOffset+1);

  stream << asLineFeed;
  unsigned j = 0;
  unsigned line_len = (StringOffset+1) * 5;
  line_len += strlen(M6Name); 
  while (j++ < line_len) stream << '-';
  stream << asLineFeed;
}

void CompareIndexFile(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Comparing the index file to the data file..." << "\n";
  
  MarineTank mtank(DB);
  int rv = mtank.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; 
    }
  }
  
  MarineTank mtank(DB);
  int rv = mtank.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";
}

int PrintPSItemBar(ofstream &stream, PostScriptDrv &psdrv,
		    int x_offset, int char_offset, int cell_len)
{
  int last_entry_char_offset = int(strlen(M6Name) * psdrv.CharWidth());
  psdrv.PrintLine((char *)KeyName, cell_len, stream);
  x_offset += char_offset;
  
  psdrv.MoveTo(stream, x_offset, psdrv.row);
  psdrv.PrintLine((char *)M2Name, cell_len, stream);
  x_offset += char_offset;
  
  psdrv.MoveTo(stream, x_offset, psdrv.row);
  psdrv.PrintLine((char *)M3Name, cell_len, stream);
  x_offset += char_offset;
  
  psdrv.MoveTo(stream, x_offset, psdrv.row);
  psdrv.PrintLine((char *)M4Name, cell_len, stream);
  x_offset += char_offset;
  
  psdrv.MoveTo(stream, x_offset, psdrv.row);
  psdrv.PrintLine((char *)M5Name, cell_len, stream);
  x_offset += char_offset;
  
  psdrv.MoveTo(stream, x_offset, psdrv.row);
  psdrv.PrintLine((char *)M6Name, cell_len, stream);
  x_offset += last_entry_char_offset;
  
  return x_offset;
}

void PostScriptPrint(MyTextWindow &textWin)
{
  textWin.Clear();
  textWin << "Printing database to PostScript file..." << "\n";

  int yn, i;
  
  if(DB->RebuildIndex()) {
    textWin << "The index file needs to be rebuilt." << "\n";
    return;
  }

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

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

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

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

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

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

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

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

    str_val = CfgData->GetStrValue("psDocumentName");
    if(str_val != 0) {
      for(i = 0; i < DocNameLen; i++) psDocName[i] = 0;
      strcpy(psDocName, str_val);
    }
// *********************************************************** //    
  }
  CfgData->UnLoad(); // Unload the Config file from memory

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

  if(yn == wxNO) LoadIndexKeys();
  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();
  if(orientation == 1) psdrv.LandScapeMode();
  psdrv.SetDocumentName(psDocName);
  psdrv.DocumentSetup();
  psdrv.Prologue(stream);
  psdrv.MediaSetup(stream);
  MarineTank mtank(DB);
  double sg;
  double ph;
  double ammonia;
  int nitrate;
  double nitrite;
  char sbuffer[sbuffer_len];

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

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

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

  psdrv.row -= (int)psdrv.FontSize();
  lines++;
  psdrv.drawThickLine(stream, line_points, psdrv.StartX(), psdrv.row);
  psdrv.row -= THICK_LINE_WIDTH + psdrv.FontSize();
  lines ++;

  dllistptr = dllist->GetFront();
  textWin << "Printing..." << "\n";

  while(!dllist->IsHeader(dllistptr)) {
    mtank.ReadObject(dllistptr->Data.object_address);

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

    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(mtank.GetDateStr(), cell_len, stream);
    x_offset += char_offset;

    sg = mtank.GetSG();
    sprintf(sbuffer, "%.4f", sg);
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(sbuffer, cell_len, stream);
    x_offset += char_offset;
     
    ph = mtank.GetPH();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", ph);
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(sbuffer, cell_len, stream);
    x_offset += char_offset;
    
    ammonia = mtank.GetAmmonia();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", ammonia);
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(sbuffer, cell_len, stream);
    x_offset += char_offset;

    nitrate = mtank.GetNitrate();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%d", nitrate);
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(sbuffer, cell_len, stream);
    x_offset += char_offset;

    nitrite = mtank.GetNitrite();
    for(i = 0; i < sbuffer_len; i++) sbuffer[i] = '\0';
    sprintf(sbuffer, "%.2f", nitrite);
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine(sbuffer, cell_len, stream);
    x_offset += last_entry_char_offset;

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

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

#ifdef __USE_MSW_PRINTING__

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

  int lines = num_objects;
  int first_page = 1; // Always starting on page one
  last_page = 1;      // Default is the first page
  
  *minPage = first_page;     // Always starting on page one
  *selPageFrom = first_page; // Always starting on page one

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

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

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

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

  int char_offset = int((MSWPrintSetup->cell_length + 1) * char_width);
  int last_entry_char_offset = int(strlen(M6Name) * char_width);
  float x_offset = xpos;
  char sbuffer[sbuffer_len];
  double SG;
  UString line_buf;
  int Offset = 0;
    
  PrintText(dc, (char *)KeyName, MSWPrintSetup->cell_length,
	    x_offset, ypos); 
  x_offset += char_offset;
  dc->GetTextExtent((char *)KeyName, &textW, &textH);
  
  PrintText(dc, (char *)M2Name, MSWPrintSetup->cell_length,
	    x_offset, ypos); 
  x_offset += char_offset;
  
  PrintText(dc, (char *)M3Name, MSWPrintSetup->cell_length,
	    x_offset, ypos); 
  x_offset += char_offset;
    
  PrintText(dc, (char *)M4Name, MSWPrintSetup->cell_length,
	    x_offset, ypos); 
  x_offset += char_offset;
    
  PrintText(dc, (char *)M5Name, MSWPrintSetup->cell_length,
	    x_offset, ypos); 
  x_offset += char_offset;

  PrintText(dc, (char *)M6Name, MSWPrintSetup->cell_length,
	    x_offset, ypos); 
  x_offset += last_entry_char_offset;
  
  ypos += textH;
  dc->DrawLine(xpos , ypos, x_offset, ypos);
  ypos += mswpLINE_WIDTH;

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

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

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

    sbuffer[sbuffer_len];
    SG = mtank.GetSG();
    sprintf(sbuffer, "%.4f", SG);
    PrintText(dc, sbuffer, MSWPrintSetup->cell_length, x_offset, ypos); 
    x_offset += char_offset;

    PrintText(dc, mtank.GetPH(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    
    PrintText(dc, (double)mtank.GetAmmonia(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    
    PrintText(dc, (long)mtank.GetNitrate(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += char_offset;
    
    PrintText(dc, (double)mtank.GetNitrite(), MSWPrintSetup->cell_length,
	      x_offset, ypos); 
    x_offset += last_entry_char_offset;
    
    ypos += textH;
    ypos += mswpLINE_WIDTH;
    dc->DrawLine(xpos , ypos, x_offset, ypos);
    ypos += mswpLINE_WIDTH;
    lines++;
    if(lines >= MSWPrintSetup->lines_per_page) break;
    dllistptr = dllistptr->GetNext();
  }
  
  WritePageHeader(dc, mswpDocumentName);
  WritePageNumber(dc, page);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#endif // __USE_MSW_PRINTING__

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

