+///////////////////////////////////////////////////////////////////////////////
+// Name: dbtest.cpp
+// Purpose: wxWindows database demo app
+// Author: George Tasker
+// Modified by:
+// Created: 1998
+// RCS-ID: $Id$
+// Copyright: (c) 1998 Remstar International, Inc.
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * SYNOPSIS START
+
+ This sample program demonstrates the cross-platform ODBC database classes
+ donated by the development team at Remstar International.
+
+ The table this sample is based on is developer contact table, and shows
+ some of the simple uses of the database classes wxDB and wxTable.
+
+
+
+ * SYNOPSIS END
+ */
+
+#ifdef __GNUG__
+#pragma implementation "dbtest.h"
+#endif
+
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+#pragma hdrstop
+#endif //__BORLANDC__
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif //WX_PRECOMP
+
+IMPLEMENT_APP(DatabaseDemoApp)
+
+#include <stdio.h> // Included strictly for reading the text file with the database parameters
+
+#include <wx/db.h> // Required in the file which will get the data source connection
+#include <wx/dbtable.h> // Has the wxTable object from which all data objects will inherit their data table functionality
+
+extern DbList *PtrBegDbList; // from wx_db.cpp, used in getting back error results from db connections
+
+#include "dbtest.h" // Header file for this demonstration program
+#include "listdb.h" // Code to support the "Lookup" button on the editor dialog
+extern char ListDB_Selection[]; // Used to return the first column value for the selected line from the listDB routines
+extern char ListDB_Selection2[]; // Used to return the second column value for the selected line from the listDB routines
+
+DatabaseDemoFrame *DemoFrame; // Pointer to the main frame
+
+
+// This statement initializes the whole application and calls OnInit
+DatabaseDemoApp DatabaseDemoApp;
+
+
+/* Pointer to the main database connection used in the program. This
+ * pointer would normally be used for doing things as database lookups
+ * for user login names and passwords, getting workstation settings, etc.
+ * ---> IMPORTANT <---
+ *
+ * For each database object created which uses this wxDB pointer
+ * connection to the database, when a CommitTrans() or RollBackTrans()
+ * will commit or rollback EVERY object which uses this wxDB pointer.
+ *
+ * To allow each table object (those derived from wxTable) to be
+ * individually committed or rolled back, you MUST use a different
+ * instance of wxDB in the constructor of the table. Doing so creates
+ * more overhead, and will use more database connections (some DBs have
+ * connection limits...), so use connections sparringly.
+ *
+ * It is recommended that one "main" database connection be created for
+ * the entire program to use for READ-ONLY database accesses, but for each
+ * table object which will do a CommitTrans() or RollbackTrans() that a
+ * new wxDB object be created and used for it.
+ */
+wxDB *READONLY_DB;
+
+
+/*
+ * This function will return the exact string(s) from the database engine
+ * indicating all error conditions which have just occured during the
+ * last call to the database engine.
+ *
+ * This demo uses the returned string by displaying it in a wxMessageBox. The
+ * formatting therefore is not the greatest, but this is just a demo, not a
+ * finished product. :-) gt
+ *
+ * NOTE: The value returned by this function is for temporary use only and
+ * should be copied for long term use
+ */
+char *GetExtendedDBErrorMsg(char *ErrFile, int ErrLine)
+{
+ static wxString msg;
+
+ wxString tStr;
+
+ if (ErrFile || ErrLine)
+ {
+ msg += "\n";
+ msg.Append ('-',80);
+ msg += "\nFile: ";
+ msg += ErrFile;
+ msg += " Line: ";
+ tStr.sprintf("%d",ErrLine);
+ msg += tStr.GetData();
+ msg += "\n";
+ }
+
+ msg.Append ('-',80);
+ msg.Append ("\nODBC ERRORS\n");
+ msg.Append ('-',80);
+ msg += "\n";
+ // Scan through each database connection displaying
+ // any ODBC errors that have occured.
+ for (DbList *pDbList = PtrBegDbList; pDbList; pDbList = pDbList->PtrNext)
+ {
+ // Skip over any free connections
+ if (pDbList->Free)
+ continue;
+ // Display errors for this connection
+ for (int i = 0; i < DB_MAX_ERROR_HISTORY; i++)
+ {
+ if (pDbList->PtrDb->errorList[i])
+ {
+ msg.Append(pDbList->PtrDb->errorList[i]);
+ if (strcmp(pDbList->PtrDb->errorList[i],"") != 0)
+ msg.Append("\n");
+ }
+ }
+ }
+ msg += "\n";
+
+ return msg.GetData();
+} // GetExtendedDBErrorMsg
+
+
+
+// `Main program' equivalent, creating windows and returning main app frame
+wxFrame *DatabaseDemoApp::OnInit(void)
+{
+ // Create the main frame window
+ DemoFrame = new DatabaseDemoFrame(NULL, "wxWindows Database Demo", 50, 50, 537, 480);
+
+ // Give it an icon
+#ifdef __WXMSW__
+ DemoFrame->SetIcon(wxIcon("db_icon"));
+#endif
+#ifdef __X__
+ DemoFrame->SetIcon(wxIcon("db.xbm"));
+#endif
+
+ // Make a menubar
+ wxMenu *file_menu = new wxMenu;
+ file_menu->Append(FILE_CREATE, "&Create contact table");
+ file_menu->Append(FILE_EXIT, "E&xit");
+
+ wxMenu *edit_menu = new wxMenu;
+ edit_menu->Append(EDIT_PARAMETERS, "&Parameters...");
+
+ wxMenu *about_menu = new wxMenu;
+ about_menu->Append(ABOUT_DEMO, "&About");
+
+ wxMenuBar *menu_bar = new wxMenuBar;
+ menu_bar->Append(file_menu, "&File");
+ menu_bar->Append(edit_menu, "&Edit");
+ menu_bar->Append(about_menu, "&About");
+ DemoFrame->SetMenuBar(menu_bar);
+
+ // Initialize the ODBC Environment for Database Operations
+ if (SQLAllocEnv(&DbConnectInf.Henv) != SQL_SUCCESS)
+ {
+ wxMessageBox("A problem occured while trying to get a connection to the data source","DB CONNECTION ERROR",wxOK | wxICON_EXCLAMATION);
+ return NULL;
+ }
+
+ FILE *paramFile;
+ if ((paramFile = fopen(paramFilename, "r")) == NULL)
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to open the parameter file '%s' for reading.\n\nYou must specify the data source, user name, and\npassword that will be used and save those settings.",paramFilename);
+ wxMessageBox(tStr.GetData(),"File I/O Error...",wxOK | wxICON_EXCLAMATION);
+ DemoFrame->BuildParameterDialog(NULL);
+ if ((paramFile = fopen(paramFilename, "r")) == NULL)
+ return FALSE;
+ }
+
+ char buffer[1000+1];
+ fgets(buffer, sizeof(params.ODBCSource), paramFile);
+ buffer[strlen(buffer)-1] = '\0';
+ strcpy(params.ODBCSource,buffer);
+
+ fgets(buffer, sizeof(params.UserName), paramFile);
+ buffer[strlen(buffer)-1] = '\0';
+ strcpy(params.UserName,buffer);
+
+ fgets(buffer, sizeof(params.Password), paramFile);
+ buffer[strlen(buffer)-1] = '\0';
+ strcpy(params.Password,buffer);
+
+ fclose(paramFile);
+
+ // Connect to datasource
+ strcpy(DbConnectInf.Dsn, params.ODBCSource); // ODBC data source name (created with ODBC Administrator under Win95/NT)
+ strcpy(DbConnectInf.Uid, params.UserName); // database username - must already exist in the data source
+ strcpy(DbConnectInf.AuthStr, params.Password); // password database username
+ READONLY_DB = GetDbConnection(&DbConnectInf);
+ if (READONLY_DB == 0)
+ {
+ wxMessageBox("Unable to connect to the data source.\n\nCheck the name of your data source to verify it has been correctly entered/spelled.\n\nWith some databases, the user name and password must\nbe created with full rights to the CONTACT table prior to making a connection\n(using tools provided by the database manufacturer)", "DB CONNECTION ERROR...",wxOK | wxICON_EXCLAMATION);
+ DemoFrame->BuildParameterDialog(NULL);
+ strcpy(DbConnectInf.Dsn, "");
+ strcpy(DbConnectInf.Uid, "");
+ strcpy(DbConnectInf.AuthStr, "");
+ wxMessageBox("Now exiting program.\n\nRestart program to try any new settings.","Notice...",wxOK | wxICON_INFORMATION);
+ return(FALSE);
+ }
+
+ DemoFrame->BuildEditorDialog();
+
+ // Show the frame
+ DemoFrame->Show(TRUE);
+
+ // Return the main frame window
+ return DemoFrame;
+} // DatabaseDemoApp::OnInit()
+
+
+
+// DatabaseDemoFrame constructor
+DatabaseDemoFrame::DatabaseDemoFrame(wxFrame *frame, char *title, int x, int y, int w, int h):
+ wxFrame(frame, title, x, y, w, h)
+{
+// Put any code in necessary for initializing the main frame here
+}
+
+
+// Intercept menu commands
+void DatabaseDemoFrame::OnMenuCommand(int id)
+{
+ switch (id)
+ {
+ case FILE_CREATE:
+ CreateDataTable();
+ break;
+ case FILE_EXIT:
+ Close();
+ break;
+ case EDIT_PARAMETERS:
+ if ((pEditorDlg->mode != mCreate) && (pEditorDlg->mode != mEdit))
+ BuildParameterDialog(this);
+ else
+ wxMessageBox("Cannot change database parameters while creating or editing a record","Notice...",wxOK | wxICON_INFORMATION);
+ break;
+ case ABOUT_DEMO:
+ wxMessageBox("wxWindows sample program for database classes\n\nContributed on 27 July 1998","About...",wxOK | wxICON_INFORMATION);
+ break;
+ }
+} // DatabaseDemoFrame::OnMenuCommand()
+
+
+Bool DatabaseDemoFrame::OnClose(void)
+{
+ // Put any additional checking necessary to make certain it is alright
+ // to close the program here that is not done elsewhere
+
+ return TRUE;
+} // DatabaseDemoFrame::OnClose()
+
+
+void DatabaseDemoFrame::CreateDataTable()
+{
+ Bool Ok = (wxMessageBox("Any data currently residing in the table will be erased.\n\nAre you sure?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!Ok)
+ return;
+
+ wxBeginBusyCursor();
+
+ Bool success = TRUE;
+
+ Ccontact *Contact = new Ccontact();
+ if (!Contact)
+ {
+ wxEndBusyCursor();
+ wxMessageBox("Error allocating memory for 'Ccontact'object.\n\nTable was not created.","Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+
+ if (!Contact->CreateTable())
+ {
+ wxEndBusyCursor();
+ wxString tStr;
+ tStr = "Error creating CONTACTS table.\nTable was not created.\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ success = FALSE;
+ }
+ else
+ {
+ if (!Contact->CreateIndexes())
+ {
+ wxEndBusyCursor();
+ wxString tStr;
+ tStr = "Error creating CONTACTS indexes.\nIndexes will be unavailable.\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ success = FALSE;
+ }
+ }
+ wxEndBusyCursor();
+
+ delete Contact;
+
+ if (success)
+ wxMessageBox("Table and index(es) were successfully created.","Notice...",wxOK | wxICON_INFORMATION);
+} // DatabaseDemoFrame::CreateDataTable()
+
+
+void DatabaseDemoFrame::BuildEditorDialog()
+{
+ pEditorDlg = new CeditorDlg(this);
+ if (!pEditorDlg)
+ wxMessageBox("Unable to create the editor dialog for some reason","Error...",wxOK | wxICON_EXCLAMATION);
+} // DatabaseDemoFrame::BuildEditorDialog()
+
+
+void DatabaseDemoFrame::BuildParameterDialog(wxWindow *parent)
+{
+ pParamDlg = new CparameterDlg(parent);
+
+ if (!pParamDlg)
+ wxMessageBox("Unable to create the parameter dialog for some reason","Error...",wxOK | wxICON_EXCLAMATION);
+} // DatabaseDemoFrame::BuildParameterDialog()
+
+
+/*
+ * Constructor note: If no wxDB object is passed in, a new connection to the database
+ * is created for this instance of Ccontact. This can be a slow process depending
+ * on the database engine being used, and some database engines have a limit on the
+ * number of connections (either hard limits, or license restricted) so care should
+ * be used to use as few connections as is necessary.
+ * IMPORTANT: Objects which share a wxDB pointer are ALL acted upon whenever a member
+ * function of pDb is called (i.e. CommitTrans() or RollbackTrans(), so if modifying
+ * or creating a table objects which use the same pDb, know that all the objects
+ * will be committed or rolled back when any of the objects has this function call made.
+ */
+Ccontact::Ccontact (wxDB *pwxDB) : wxTable(pwxDB ? pwxDB : GetDbConnection(&DbConnectInf),CONTACT_TABLE_NAME,CONTACT_NO_COLS)
+{
+ // This is used to represent whether the database connection should be released
+ // when this instance of the object is deleted. If using the same connection
+ // for multiple instance of database objects, then the connection should only be
+ // released when the last database instance using the connection is deleted
+ freeDbConn = !pwxDB;
+
+ SetupColumns();
+
+} // Ccontact Constructor
+
+
+void Ccontact::Initialize()
+{
+ Name[0] = 0;
+ Addr1[0] = 0;
+ Addr2[0] = 0;
+ City[0] = 0;
+ State[0] = 0;
+ PostalCode[0] = 0;
+ Country[0] = 0;
+ JoinDate.year = 1980;
+ JoinDate.month = 1;
+ JoinDate.day = 1;
+ JoinDate.hour = 0;
+ JoinDate.minute = 0;
+ JoinDate.second = 0;
+ JoinDate.fraction = 0;
+ NativeLanguage = langENGLISH;
+ IsDeveloper = FALSE;
+ Contributions = 0;
+ LinesOfCode = 0L;
+} // Ccontact::Initialize
+
+
+Ccontact::~Ccontact()
+{
+ if (freeDbConn)
+ {
+ if (!FreeDbConnection(pDb))
+ {
+ wxString tStr;
+ tStr = "Unable to Free the Ccontact data table handle\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ }
+ }
+} // Ccontract destructor
+
+
+/*
+ * Handles setting up all the connections for the interface from the wxTable
+ * functions to interface to the data structure used to store records in
+ * memory, and for all the column definitions that define the table structure
+ */
+void Ccontact::SetupColumns()
+{
+ SetColDefs ( 0,"NAME", DB_DATA_TYPE_VARCHAR, Name, SQL_C_CHAR, sizeof(Name), TRUE, TRUE); // Primary index
+ SetColDefs ( 1,"ADDRESS1", DB_DATA_TYPE_VARCHAR, Addr1, SQL_C_CHAR, sizeof(Addr1), FALSE,TRUE);
+ SetColDefs ( 2,"ADDRESS2", DB_DATA_TYPE_VARCHAR, Addr2, SQL_C_CHAR, sizeof(Addr2), FALSE,TRUE);
+ SetColDefs ( 3,"CITY", DB_DATA_TYPE_VARCHAR, City, SQL_C_CHAR, sizeof(City), FALSE,TRUE);
+ SetColDefs ( 4,"STATE", DB_DATA_TYPE_VARCHAR, State, SQL_C_CHAR, sizeof(State), FALSE,TRUE);
+ SetColDefs ( 5,"POSTAL_CODE", DB_DATA_TYPE_VARCHAR, PostalCode, SQL_C_CHAR, sizeof(PostalCode), FALSE,TRUE);
+ SetColDefs ( 6,"COUNTRY", DB_DATA_TYPE_VARCHAR, Country, SQL_C_CHAR, sizeof(Country), FALSE,TRUE);
+ SetColDefs ( 7,"JOIN_DATE", DB_DATA_TYPE_DATE, &JoinDate, SQL_C_TIMESTAMP, sizeof(JoinDate), FALSE,TRUE);
+ SetColDefs ( 8,"NATIVE_LANGUAGE", DB_DATA_TYPE_INTEGER, &NativeLanguage, SQL_C_ENUM, sizeof(NativeLanguage), FALSE,TRUE);
+ SetColDefs ( 9,"IS_DEVELOPER", DB_DATA_TYPE_INTEGER, &IsDeveloper, SQL_C_BOOLEAN, sizeof(Bool), FALSE,TRUE);
+ SetColDefs (10,"CONTRIBUTIONS", DB_DATA_TYPE_INTEGER, &Contributions, SQL_C_USHORT, sizeof(Contributions), FALSE,TRUE);
+ SetColDefs (11,"LINES_OF_CODE", DB_DATA_TYPE_INTEGER, &LinesOfCode, SQL_C_ULONG, sizeof(LinesOfCode), FALSE,TRUE);
+} // Ccontact::SetupColumns
+
+
+Bool Ccontact::CreateIndexes(void)
+{
+ // This index could easily be accomplished with an "orderBy" clause,
+ // but is done to show how to construct a non-primary index.
+ wxString indexName;
+ CidxDef idxDef[2];
+
+ Bool Ok = TRUE;
+
+ strcpy(idxDef[0].ColName, "IS_DEVELOPER");
+ idxDef[0].Ascending = TRUE;
+
+ strcpy(idxDef[1].ColName, "NAME");
+ idxDef[1].Ascending = TRUE;
+
+ indexName = CONTACT_TABLE_NAME;
+ indexName += "_IDX1";
+ Ok = CreateIndex(indexName.GetData(), TRUE, 2, idxDef);
+
+ return Ok;
+} // Ccontact::CreateIndexes()
+
+
+/*
+ * Having a function to do a query on the primary key (and possibly others) is
+ * very efficient and tighter coding so that it is available where ever the object
+ * is. Great for use with multiple tables when not using views or outer joins
+ */
+Bool Ccontact::FetchByName(char *name)
+{
+ whereStr.sprintf("NAME = '%s'",name);
+ where = this->whereStr.GetData();
+ orderBy = 0;
+
+ if (!Query())
+ return(FALSE);
+
+ // Fetch the record
+ return(GetNext());
+
+} // Ccontact::FetchByName()
+
+
+/*
+ *
+ * ************* DIALOGS ***************
+ *
+ */
+
+
+/* CeditorDlg constructor
+ *
+ * Creates the dialog used for creating/editing/deleting/copying a Ccontact object.
+ * This dialog actually is drawn in the main frame of the program
+ *
+ * An instance of Ccontact is created - "Contact" - which is used to hold the Ccontact
+ * object that is currently being worked with.
+ */
+CeditorDlg::CeditorDlg(wxWindow *parent) : wxPanel (parent, 1, 1, 460, 455)
+{
+ // Since the ::OnCommand() function is overridden, this prevents the widget
+ // detection in ::OnCommand() until all widgets have been initialized to prevent
+ // uninitialized pointers from crashing the program
+ widgetPtrsSet = FALSE;
+
+ // Create the data structure and a new database connection.
+ // (As there is not a pDb being passed in the constructor, a new database
+ // connection is created)
+ Contact = new Ccontact();
+
+ if (!Contact)
+ {
+ wxMessageBox("Unable to instantiate an instance of Ccontact","Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+
+ // Check if the table exists or not. If it doesn't, ask the user if they want to
+ // create the table. Continue trying to create the table until it exists, or user aborts
+ while (!Contact->pDb->TableExists((char *)CONTACT_TABLE_NAME))
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to open the table '%s'.\n\nTable may need to be created...?\n\n",CONTACT_TABLE_NAME);
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+
+ Bool createTable = (wxMessageBox("Do you wish to try to create/clear the CONTACTS table?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!createTable)
+ {
+ delete Contact;
+ Close();
+ DemoFrame->Close();
+ return;
+ }
+ else
+ DemoFrame->CreateDataTable();
+ }
+
+ // Tables must be "opened" before anything other than creating/deleting table can be done
+ if (!Contact->Open())
+ {
+ // Table does exist, there was some problem opening it. Currently this should
+ // never fail, except in the case of the table not exisiting. Open() basically
+ // only sets up variable/pointer values, other than checking for table existence.
+ if (Contact->pDb->TableExists((char *)CONTACT_TABLE_NAME))
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to open the table '%s'.\n\n",CONTACT_TABLE_NAME);
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ delete Contact;
+ Close();
+ DemoFrame->Close();
+ return;
+ }
+ }
+
+ // Build the dialog
+ SetLabelPosition(wxHORIZONTAL);
+
+ wxFont *ButtonFont = new wxFont(12,wxSWISS,wxNORMAL,wxBOLD);
+ wxFont *TextFont = new wxFont(12,wxSWISS,wxNORMAL,wxNORMAL);
+
+ SetButtonFont(ButtonFont);
+ SetLabelFont(TextFont);
+ SetLabelPosition(wxVERTICAL);
+
+ wxGroupBox *FunctionGrp = new wxGroupBox(this, "", 15, 1, 497, 69, 0, "FunctionGrp");
+ wxGroupBox *SearchGrp = new wxGroupBox(this, "", 417, 1, 95, 242, 0, "SearchGrp");
+
+ pCreateBtn = new wxButton(this, NULL, "&Create", 25, 21, 70, 35, 0, "CreateBtn");
+ pEditBtn = new wxButton(this, NULL, "&Edit", 102, 21, 70, 35, 0, "EditBtn");
+ pDeleteBtn = new wxButton(this, NULL, "&Delete", 179, 21, 70, 35, 0, "DeleteBtn");
+ pCopyBtn = new wxButton(this, NULL, "Cop&y", 256, 21, 70, 35, 0, "CopyBtn");
+ pSaveBtn = new wxButton(this, NULL, "&Save", 333, 21, 70, 35, 0, "SaveBtn");
+ pCancelBtn = new wxButton(this, NULL, "C&ancel", 430, 21, 70, 35, 0, "CancelBtn");
+
+ pPrevBtn = new wxButton(this, NULL, "<< &Prev",430, 81, 70, 35, 0, "PrevBtn");
+ pNextBtn = new wxButton(this, NULL, "&Next >>",430, 121, 70, 35, 0, "NextBtn");
+ pQueryBtn = new wxButton(this, NULL, "&Query", 430, 161, 70, 35, 0, "QueryBtn");
+ pResetBtn = new wxButton(this, NULL, "&Reset", 430, 200, 70, 35, 0, "ResetBtn");
+
+ pNameMsg = new wxMessage(this, "Name:", 17, 80, -1, -1, 0, "NameMsg");
+ pNameTxt = new wxText(this, NULL, "", "", 17, 97, 308, 25, 0, "NameTxt");
+ pNameListBtn = new wxButton(this, NULL, "&Lookup", 333, 99, 70, 24, 0, "LookupBtn");
+
+ pAddress1Msg = new wxMessage(this, "Address:", 17, 130, -1, -1, 0, "Address1Msg");
+ pAddress1Txt = new wxText(this, NULL, "", "", 17, 147, 308, 25, 0, "Address1Txt");
+
+ pAddress2Msg = new wxMessage(this, "Address:", 17, 180, -1, -1, 0, "Address2Msg");
+ pAddress2Txt = new wxText(this, NULL, "", "", 17, 197, 308, 25, 0, "Address2Txt");
+
+ pCityMsg = new wxMessage(this, "City:", 17, 230, -1, -1, 0, "CityMsg");
+ pCityTxt = new wxText(this, NULL, "", "", 17, 247, 225, 25, 0, "CityTxt");
+
+ pStateMsg = new wxMessage(this, "State:", 250, 230, -1, -1, 0, "StateMsg");
+ pStateTxt = new wxText(this, NULL, "", "", 250, 247, 153, 25, 0, "StateTxt");
+
+ pCountryMsg = new wxMessage(this, "Country:", 17, 280, -1, -1, 0, "CountryMsg");
+ pCountryTxt = new wxText(this, NULL, "", "", 17, 297, 225, 25, 0, "CountryTxt");
+
+ pPostalCodeMsg = new wxMessage(this, "Postal Code:", 250, 280, -1, -1, 0, "PostalCodeMsg");
+ pPostalCodeTxt = new wxText(this, NULL, "", "", 250, 297, 153, 25, 0, "PostalCodeTxt");
+
+ char *choice_strings[5];
+ choice_strings[0] = "English";
+ choice_strings[1] = "French";
+ choice_strings[2] = "German";
+ choice_strings[3] = "Spanish";
+ choice_strings[4] = "Other";
+ pNativeLangChoice = new wxChoice(this, NULL, "",17, 346, 277, -1, 5, choice_strings);
+ pNativeLangMsg = new wxMessage(this, "Native language:", 17, 330, -1, -1, 0, "NativeLangMsg");
+
+ char *radio_strings[2];
+ radio_strings[0] = "No";
+ radio_strings[1] = "Yes";
+ pDeveloperRadio = new wxRadioBox(this,NULL,"Developer:",303,330,-1,-1,2,radio_strings,2,wxHORIZONTAL|wxFLAT);
+
+ pJoinDateMsg = new wxMessage(this, "Date joined:", 17, 380, -1, -1, 0, "JoinDateMsg");
+ pJoinDateTxt = new wxText(this, NULL, "", "", 17, 397, 150, 25, 0, "JoinDateTxt");
+
+ pContribMsg = new wxMessage(this, "Contributions:", 175, 380, -1, -1, 0, "ContribMsg");
+ pContribTxt = new wxText(this, NULL, "", "", 175, 397, 120, 25, 0, "ContribTxt");
+
+ pLinesMsg = new wxMessage(this, "Lines of code:", 303, 380, -1, -1, 0, "LinesMsg");
+ pLinesTxt = new wxText(this, NULL, "", "", 303, 397, 100, 25, 0, "LinesTxt");
+
+ // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
+ // handle all widget processing
+ widgetPtrsSet = TRUE;
+
+ // Setup the orderBy and where clauses to return back a single record as the result set,
+ // as there will only be one record being shown on the dialog at a time, this optimizes
+ // network traffic by only returning a one row result
+
+ Contact->orderBy = "NAME"; // field name to sort by
+
+ // The wxString "whereStr" is not a member of the wxTable object, it is a member variable
+ // specifically in the Ccontact class. It is used here for simpler construction of a varying
+ // length string, and then after the string is built, the wxTable member variable "where" is
+ // assigned the pointer to the constructed string.
+ //
+ // The constructed where clause below has a sub-query within it "SELECT MIN(NAME) FROM %s"
+ // to achieve a single row (in this case the first name in alphabetical order).
+ Contact->whereStr.sprintf("NAME = (SELECT MIN(NAME) FROM %s)",Contact->tableName);
+
+ // NOTE: GetData() returns a pointer which may not be valid later, so this is short term use only
+ Contact->where = Contact->whereStr.GetData();
+
+ // Perform the Query to get the result set.
+ // NOTE: If there are no rows returned, that is a valid result, so Query() would return TRUE.
+ // Only if there is a database error will Query() come back as FALSE
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ GetParent()->Close();
+ return;
+ }
+
+ // Since Query succeeded, now get the row that was returned
+ if (!Contact->GetNext())
+ // If the GetNext() failed at this point, then there are no rows to retrieve,
+ // so clear the values in the members of "Contact" so that PutData() blanks the
+ // widgets on the dialog
+ Contact->Initialize();
+
+ SetMode(mView);
+ PutData();
+
+ Show(TRUE);
+} // CeditorDlg constructor
+
+
+Bool CeditorDlg::OnClose()
+{
+ // Clean up time
+ if ((mode != mCreate) && (mode != mEdit))
+ {
+ if (Contact)
+ delete Contact;
+ return TRUE;
+ }
+ else
+ {
+ wxMessageBox("Must finish processing the current record being created/modified before exiting","Notice...",wxOK | wxICON_INFORMATION);
+ return FALSE;
+ }
+} // CeditorDlg::OnClose()
+
+
+void CeditorDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
+{
+ wxString widgetName;
+
+ widgetName = win.GetName();
+
+ if (!widgetPtrsSet)
+ return;
+
+ if (widgetName == pCreateBtn->GetName())
+ {
+ Contact->Initialize();
+ PutData();
+ SetMode( mCreate );
+ pNameTxt->SetValue("");
+ pNameTxt->SetFocus();
+ return;
+ }
+
+ if (widgetName == pEditBtn->GetName())
+ {
+ saveName = Contact->Name;
+ SetMode( mEdit );
+ pNameTxt->SetFocus();
+ return;
+ }
+
+ if (widgetName == pCopyBtn->GetName())
+ {
+ SetMode(mCreate);
+ pNameTxt->SetValue("");
+ pNameTxt->SetFocus();
+ return;
+ }
+
+ if (widgetName == pDeleteBtn->GetName())
+ {
+ Bool Ok = (wxMessageBox("Are you sure?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!Ok)
+ return;
+
+ if (Ok && Contact->Delete())
+ {
+ // NOTE: Deletions are not finalized until a CommitTrans() is performed.
+ // If the commit were not performed, the program will continue to
+ // show the table contents as if they were deleted until this instance
+ // of Ccontact is deleted. If the Commit wasn't performed, the
+ // database will automatically Rollback the changes when the database
+ // connection is terminated
+ Contact->pDb->CommitTrans();
+
+ // Try to get the row that followed the just deleted row in the orderBy sequence
+ if (!GetNextRec())
+ {
+ // There was now row (in sequence) after the just deleted row, so get the
+ // row which preceded the just deleted row
+ if (!GetPrevRec())
+ {
+ // There are now no rows remaining, so clear the dialog widgets
+ Contact->Initialize();
+ PutData();
+ }
+ }
+ SetMode(mode); // force reset of button enable/disable
+ }
+ else
+ // Delete failed
+ Contact->pDb->RollbackTrans();
+
+ SetMode(mView);
+ return;
+ }
+
+ if (widgetName == pSaveBtn->GetName())
+ {
+ Save();
+ return;
+ }
+
+ if (widgetName == pCancelBtn->GetName())
+ {
+ Bool Ok = (wxMessageBox("Are you sure?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!Ok)
+ return;
+
+ if (!strcmp(saveName.GetData(),""))
+ {
+ Contact->Initialize();
+ PutData();
+ SetMode(mView);
+ return;
+ }
+ else
+ {
+ // Requery previous record
+ if (Contact->FetchByName(saveName.GetData()))
+ {
+ PutData();
+ SetMode(mView);
+ return;
+ }
+ }
+
+ // Previous record not available, retrieve first record in table
+ Contact->whereStr = "NAME = (SELECT MIN(NAME) FROM ";
+ Contact->whereStr += Contact->tableName;
+ Contact->whereStr += ")";
+
+ Contact->where = Contact->whereStr.GetData();
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ SetMode(mView);
+ return;
+ }
+ if (Contact->GetNext()) // Successfully read first record
+ {
+ PutData();
+ SetMode(mView);
+ return;
+ }
+ // No contacts are available, clear dialog
+ Contact->Initialize();
+ PutData();
+ SetMode(mView);
+ return;
+ } // Cancel Button
+
+ if (widgetName == pPrevBtn->GetName())
+ {
+ if (!GetPrevRec())
+ wxBell();
+ return;
+ } // Prev Button
+
+ if (widgetName == pNextBtn->GetName())
+ {
+ if (!GetNextRec())
+ wxBell();
+ return;
+ } // Next Button
+
+ if (widgetName == pQueryBtn->GetName())
+ {
+ // Display the query dialog box
+ char qryWhere[DB_MAX_WHERE_CLAUSE_LEN+1];
+ strcpy(qryWhere, Contact->qryWhereStr.GetData());
+ char *tblName[] = {(char *)CONTACT_TABLE_NAME, 0};
+ new CqueryDlg(GetParent(), Contact->pDb, tblName, qryWhere);
+
+ // Query the first record in the new record set and
+ // display it, if the query string has changed.
+ if (strcmp(qryWhere, Contact->qryWhereStr.GetData()))
+ {
+ Contact->orderBy = "NAME";
+ Contact->whereStr = "NAME = (SELECT MIN(NAME) FROM ";
+ Contact->whereStr += CONTACT_TABLE_NAME;
+ // Append the query where string (if there is one)
+ Contact->qryWhereStr = qryWhere;
+ if (strlen(qryWhere))
+ {
+ Contact->whereStr += " WHERE ";
+ Contact->whereStr += Contact->qryWhereStr;
+ }
+ // Close the expression with a right paren
+ Contact->whereStr += ")";
+ // Requery the table
+ Contact->where = Contact->whereStr.GetData();
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ // Display the first record from the query set
+ if (!Contact->GetNext())
+ Contact->Initialize();
+ PutData();
+ }
+
+ // Enable/Disable the reset button
+ pResetBtn->Enable(!Contact->qryWhereStr.Empty());
+
+ return;
+ } // Query button
+
+
+ if (widgetName == pResetBtn->GetName())
+ {
+ // Clear the additional where criteria established by the query feature
+ Contact->qryWhereStr = "";
+
+ // Query the first record in the table
+ Contact->orderBy = "NAME";
+ Contact->whereStr = "NAME = (SELECT MIN(NAME) FROM ";
+ Contact->whereStr += CONTACT_TABLE_NAME;
+ Contact->whereStr += ")";
+ Contact->where = Contact->whereStr.GetData();
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ if (!Contact->GetNext())
+ Contact->Initialize();
+ PutData();
+ pResetBtn->Enable(FALSE);
+
+ return;
+ } // Reset button
+
+
+ if (widgetName == pNameListBtn->GetName())
+ {
+ new ClookUpDlg(/* wxWindow *parent */ this,
+ /* char *windowTitle */ "Select contact name",
+ /* char *tableName */ (char *) CONTACT_TABLE_NAME,
+ /* char *dispCol1 */ "NAME",
+ /* char *dispCol2 */ "JOIN_DATE",
+ /* char *where */ "",
+ /* char *orderBy */ "NAME",
+ /* Bool distinctValues */ TRUE);
+
+ if (ListDB_Selection && strlen(ListDB_Selection))
+ {
+ wxString w = "NAME = '";
+ w += ListDB_Selection;
+ w += "'";
+ GetRec(w.GetData());
+ }
+
+ return;
+ }
+
+} // CeditorDlg::OnCommand()
+
+
+void CeditorDlg::FieldsEditable()
+{
+ pNameTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pAddress1Txt->Enable((mode == mCreate) || (mode == mEdit));
+ pAddress2Txt->Enable((mode == mCreate) || (mode == mEdit));
+ pCityTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pStateTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pPostalCodeTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pCountryTxt->Enable((mode == mCreate) || (mode == mEdit));
+
+ pJoinDateTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pContribTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pLinesTxt->Enable((mode == mCreate) || (mode == mEdit));
+ pNativeLangChoice->Enable((mode == mCreate) || (mode == mEdit));
+ pDeveloperRadio->Enable((mode == mCreate) || (mode == mEdit));
+
+} // CeditorDlg::FieldsEditable()
+
+
+void CeditorDlg::SetMode(enum DialogModes m)
+{
+ Bool edit = FALSE;
+
+ mode = m;
+ switch (mode)
+ {
+ case mCreate:
+ case mEdit:
+ edit = TRUE;
+ break;
+ case mView:
+ case mSearch:
+ edit = FALSE;
+ break;
+ default:
+ break;
+ };
+
+ if (widgetPtrsSet)
+ {
+ pCreateBtn->Enable( !edit );
+ pEditBtn->Enable( !edit && (strcmp(Contact->Name,"")!=0) );
+ pDeleteBtn->Enable( !edit && (strcmp(Contact->Name,"")!=0) );
+ pCopyBtn->Enable( !edit && (strcmp(Contact->Name,"")!=0) );
+ pSaveBtn->Enable( edit );
+ pCancelBtn->Enable( edit );
+ pPrevBtn->Enable( !edit );
+ pNextBtn->Enable( !edit );
+ pQueryBtn->Enable( !edit );
+ pResetBtn->Enable( !edit && !Contact->qryWhereStr.Empty() );
+ pNameListBtn->Enable( !edit );
+ }
+
+ FieldsEditable();
+} // CeditorDlg::SetMode()
+
+
+Bool CeditorDlg::PutData()
+{
+ wxString tStr;
+
+ pNameTxt->SetValue(Contact->Name);
+ pAddress1Txt->SetValue(Contact->Addr1);
+ pAddress2Txt->SetValue(Contact->Addr2);
+ pCityTxt->SetValue(Contact->City);
+ pStateTxt->SetValue(Contact->State);
+ pCountryTxt->SetValue(Contact->Country);
+ pPostalCodeTxt->SetValue(Contact->PostalCode);
+
+ tStr.sprintf("%d/%d/%d",Contact->JoinDate.month,Contact->JoinDate.day,Contact->JoinDate.year);
+ pJoinDateTxt->SetValue(tStr.GetData());
+
+ tStr.sprintf("%d",Contact->Contributions);
+ pContribTxt->SetValue(tStr.GetData());
+
+ tStr.sprintf("%lu",Contact->LinesOfCode);
+ pLinesTxt->SetValue(tStr.GetData());
+
+ pNativeLangChoice->SetSelection(Contact->NativeLanguage);
+
+ pDeveloperRadio->SetSelection(Contact->IsDeveloper);
+
+ return TRUE;
+} // Ceditor::PutData()
+
+
+/*
+ * Reads the data out of all the widgets on the dialog. Some data evaluation is done
+ * to ensure that there is a name entered and that the date field is valid.
+ *
+ * A return value of TRUE means that valid data was retrieved from the dialog, otherwise
+ * invalid data was found (and a message was displayed telling the user what to fix), and
+ * the data was not placed into the appropraite fields of Ccontact
+ */
+Bool CeditorDlg::GetData()
+{
+ // Validate that the data currently entered into the widgets is valid data
+
+ wxString tStr;
+ tStr = pNameTxt->GetValue();
+ if (!strcmp(tStr.GetData(),""))
+ {
+ wxMessageBox("A name is required for entry into the contact table","Notice...",wxOK | wxICON_INFORMATION);
+ return FALSE;
+ }
+
+ Bool invalid = FALSE;
+ int mm,dd,yyyy;
+ int first, second;
+
+ tStr = pJoinDateTxt->GetValue();
+ if (tStr.Freq('/') != 2)
+ invalid = TRUE;
+
+ // Find the month, day, and year tokens
+ if (!invalid)
+ {
+ first = tStr.First('/');
+ second = tStr.Last('/');
+
+ mm = atoi(tStr.SubString(0,first));
+ dd = atoi(tStr.SubString(first+1,second));
+ yyyy = atoi(tStr.SubString(second+1,tStr.Length()-1));
+
+ invalid = !(mm && dd && yyyy);
+ }
+
+ // Force Year 2000 compliance
+ if (!invalid && (yyyy < 1000))
+ invalid = TRUE;
+
+ // Check the token ranges for validity
+ if (!invalid)
+ {
+ if (yyyy > 9999)
+ invalid = TRUE;
+ else if ((mm < 1) || (mm > 12))
+ invalid = TRUE;
+ else
+ {
+ if (dd < 1)
+ invalid = TRUE;
+ else
+ {
+ int days[12] = {31,28,31,30,31,30,
+ 31,31,30,31,30,31};
+ if (dd > days[mm-1])
+ {
+ invalid = TRUE;
+ if ((dd == 29) && (mm == 2))
+ {
+ if (((yyyy % 4) == 0) && (((yyyy % 100) != 0) || ((yyyy % 400) == 0)))
+ invalid = FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ if (!invalid)
+ {
+ Contact->JoinDate.month = mm;
+ Contact->JoinDate.day = dd;
+ Contact->JoinDate.year = yyyy;
+ }
+ else
+ {
+ wxMessageBox("Improper date format. Please check the date\nspecified and try again.\n\nNOTE: Dates are in american format (MM/DD/YYYY)","Notice...",wxOK | wxICON_INFORMATION);
+ return FALSE;
+ }
+
+ tStr = pNameTxt->GetValue();
+ strcpy(Contact->Name,tStr.GetData());
+ strcpy(Contact->Addr1,pAddress1Txt->GetValue());
+ strcpy(Contact->Addr2,pAddress2Txt->GetValue());
+ strcpy(Contact->City,pCityTxt->GetValue());
+ strcpy(Contact->State,pStateTxt->GetValue());
+ strcpy(Contact->Country,pCountryTxt->GetValue());
+ strcpy(Contact->PostalCode,pPostalCodeTxt->GetValue());
+
+ Contact->Contributions = atoi(pContribTxt->GetValue());
+ Contact->LinesOfCode = atol(pLinesTxt->GetValue());
+
+ Contact->NativeLanguage = (enum Language) pNativeLangChoice->GetSelection();
+ Contact->IsDeveloper = pDeveloperRadio->GetSelection();
+
+ return TRUE;
+} // CeditorDlg::GetData()
+
+
+/*
+ * Retrieve data from the dialog, verify the validity of the data, and if it is valid,
+ * try to insert/update the data to the table based on the current 'mode' the dialog
+ * is set to.
+ *
+ * A return value of TRUE means the insert/update was completed successfully, a return
+ * value of FALSE means that Save() failed. If returning FALSE, then this function
+ * has displayed a detailed error message for the user.
+ */
+Bool CeditorDlg::Save()
+{
+ Bool failed = FALSE;
+
+ // Read the data in the widgets of the dialog to get the user's data
+ if (!GetData())
+ failed = TRUE;
+
+ // Perform any other required validations necessary before saving
+
+
+ if (!failed)
+ {
+ wxBeginBusyCursor();
+
+ if (mode == mCreate)
+ {
+ RETCODE result = Contact->Insert();
+
+ failed = (result != DB_SUCCESS);
+ if (failed)
+ {
+ // Some errors may be expected, like a duplicate key, so handle those instances with
+ // specific error messages.
+ if (result == DB_ERR_INTEGRITY_CONSTRAINT_VIOL)
+ {
+ wxString tStr;
+ tStr = "A duplicate key value already exists in the table.\nUnable to save record\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ }
+ else
+ {
+ // Some other unexpexted error occurred
+ wxString tStr;
+ tStr = "Database insert failed\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ }
+ }
+ }
+ else // mode == mEdit
+ {
+ if (!Contact->Update())
+ {
+ wxString tStr;
+ tStr = "Database update failed\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ failed = TRUE;
+ }
+ }
+
+ if (!failed)
+ {
+ Contact->pDb->CommitTrans();
+ SetMode(mView); // Sets the dialog mode back to viewing after save is successful
+ }
+ else
+ Contact->pDb->RollbackTrans();
+
+ wxEndBusyCursor();
+ }
+
+ return !failed;
+} // CeditorDlg::Save()
+
+
+/*
+ * Where this program is only showing a single row at a time in the dialog,
+ * a special where clause must be built to find just the single row which,
+ * in sequence, would follow the currently displayed row.
+ */
+Bool CeditorDlg::GetNextRec()
+{
+ wxString w;
+
+ w = "NAME = (SELECT MIN(NAME) FROM ";
+ w += Contact->tableName;
+ w += " WHERE NAME > '";
+ w += Contact->Name;
+ w += "'";
+
+ // If a query where string is currently set, append that criteria
+ if (!Contact->qryWhereStr.Empty())
+ {
+ w += " AND (";
+ w += Contact->qryWhereStr;
+ w += ")";
+ }
+
+ w += ")";
+
+ return(GetRec(w.GetData()));
+
+} // CeditorDlg::GetNextRec()
+
+
+/*
+ * Where this program is only showing a single row at a time in the dialog,
+ * a special where clause must be built to find just the single row which,
+ * in sequence, would precede the currently displayed row.
+ */
+Bool CeditorDlg::GetPrevRec()
+{
+ wxString w;
+
+ w = "NAME = (SELECT MAX(NAME) FROM ";
+ w += Contact->tableName;
+ w += " WHERE NAME < '";
+ w += Contact->Name;
+ w += "'";
+
+ // If a query where string is currently set, append that criteria
+ if (!Contact->qryWhereStr.Empty())
+ {
+ w += " AND (";
+ w += Contact->qryWhereStr;
+ w += ")";
+ }
+
+ w += ")";
+
+ return(GetRec(w.GetData()));
+
+} // CeditorDlg::GetPrevRec()
+
+
+/*
+ * This function is here to avoid duplicating this same code in both the
+ * GetPrevRec() and GetNextRec() functions
+ */
+Bool CeditorDlg::GetRec(char *whereStr)
+{
+ Contact->where = whereStr;
+ Contact->orderBy = "NAME";
+
+ if (!Contact->Query())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Query()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+
+ return(FALSE);
+ }
+
+ if (Contact->GetNext())
+ {
+ PutData();
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+} // CeditorDlg::GetRec()
+
+
+
+/*
+ * CparameterDlg constructor
+ */
+CparameterDlg::CparameterDlg(wxWindow *parent) : wxDialogBox (parent, "ODBC parameter settings", 1, -1, -1, 400, 275)
+{
+ // Since the ::OnCommand() function is overridden, this prevents the widget
+ // detection in ::OnCommand() until all widgets have been initialized to prevent
+ // uninitialized pointers from crashing the program
+ widgetPtrsSet = FALSE;
+
+ // Build the dialog
+ SetLabelPosition(wxVERTICAL);
+
+ wxFont *ButtonFont = new wxFont(12,wxSWISS,wxNORMAL,wxBOLD);
+ wxFont *TextFont = new wxFont(12,wxSWISS,wxNORMAL,wxNORMAL);
+
+ SetButtonFont(ButtonFont);
+ SetLabelFont(TextFont);
+ SetLabelPosition(wxVERTICAL);
+
+ pParamODBCSourceMsg = new wxMessage(this, "ODBC data sources:", 10, 10, -1, -1, 0, "ParamODBCSourceMsg");
+ pParamODBCSourceList = new wxListBox(this, NULL, "", wxSINGLE|wxALWAYS_SB, 10, 29, 285, 150, 0, 0, 0, "ParamODBCSourceList");
+
+ pParamUserNameMsg = new wxMessage(this, "Database user name:", 10, 193, -1, -1, 0, "ParamUserNameMsg");
+ pParamUserNameTxt = new wxText(this, NULL, "", "", 10, 209, 140, 25, 0, "ParamUserNameTxt");
+
+ pParamPasswordMsg = new wxMessage(this, "Password:", 156, 193, -1, -1, 0, "ParamPasswordMsg");
+ pParamPasswordTxt = new wxText(this, NULL, "", "", 156, 209, 140, 25, 0, "ParamPasswordTxt");
+
+ pParamSaveBtn = new wxButton(this, NULL, "&Save", 310, 21, 70, 35, 0, "ParamSaveBtn");
+ pParamCancelBtn = new wxButton(this, NULL, "C&ancel", 310, 66, 70, 35, 0, "ParamCancelBtn");
+
+ // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
+ // handle all widget processing
+ widgetPtrsSet = TRUE;
+
+ saved = FALSE;
+ savedParamSettings = DatabaseDemoApp.params;
+
+ Centre(wxBOTH);
+ PutData();
+ Show(TRUE);
+} // CparameterDlg constructor
+
+
+Bool CparameterDlg::OnClose()
+{
+ // Put any additional checking necessary to make certain it is alright
+ // to close the program here that is not done elsewhere
+ if (!saved)
+ {
+ Bool Ok = (wxMessageBox("No changes have been saved.\n\nAre you sure you wish exit the parameter screen?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (!Ok)
+ return FALSE;
+
+ DatabaseDemoApp.params = savedParamSettings;
+ }
+
+ if (GetParent() != NULL)
+ GetParent()->SetFocus();
+ return TRUE;
+} // Cparameter::OnClose()
+
+
+void CparameterDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
+{
+ wxString widgetName;
+
+ widgetName = win.GetName();
+
+ if (!widgetPtrsSet)
+ return;
+
+ if (widgetName == pParamSaveBtn->GetName())
+ {
+ if (Save())
+ {
+ wxString tStr;
+ tStr = "Database parameters have been saved.";
+ if (GetParent() != NULL) // The parameter dialog was not called during startup due to a missing cfg file
+ tStr += "\nNew parameters will take effect the next time the program is started.";
+ wxMessageBox(tStr.GetData(),"Notice...",wxOK | wxICON_INFORMATION);
+ saved = TRUE;
+ Close();
+ }
+ return;
+ }
+
+ if (widgetName == pParamCancelBtn->GetName())
+ {
+ Close();
+ return;
+ }
+} // CparameterDlg::OnCommand()
+
+
+Bool CparameterDlg::PutData()
+{
+ // Fill the data source list box
+ FillDataSourceList();
+
+ // Fill in the fields from the params object
+ pParamODBCSourceList->SetStringSelection(DatabaseDemoApp.params.ODBCSource);
+ pParamUserNameTxt->SetValue(DatabaseDemoApp.params.UserName);
+ pParamPasswordTxt->SetValue(DatabaseDemoApp.params.Password);
+ return TRUE;
+} // CparameterDlg::PutData()
+
+
+Bool CparameterDlg::GetData()
+{
+ wxString tStr;
+ if (pParamODBCSourceList->GetStringSelection())
+ {
+ tStr = pParamODBCSourceList->GetStringSelection();
+ if (tStr.Length() > (sizeof(DatabaseDemoApp.params.ODBCSource)-1))
+ {
+ wxString errmsg;
+ errmsg.sprintf("ODBC Data source name is longer than the data structure to hold it.\n'Cparameter.ODBCSource' must have a larger character array\nto handle a data source with this long of a name\n\nThe data source currently selected is %d characters long.",tStr.Length());
+ wxMessageBox(errmsg.GetData(),"Internal program error...",wxOK | wxICON_EXCLAMATION);
+ return FALSE;
+ }
+ strcpy(DatabaseDemoApp.params.ODBCSource, tStr.GetData());
+ }
+ else
+ return FALSE;
+
+ tStr = pParamUserNameTxt->GetValue();
+ if (tStr.Length() > (sizeof(DatabaseDemoApp.params.UserName)-1))
+ {
+ wxString errmsg;
+ errmsg.sprintf("User name is longer than the data structure to hold it.\n'Cparameter.UserName' must have a larger character array\nto handle a data source with this long of a name\n\nThe user name currently specified is %d characters long.",tStr.Length());
+ wxMessageBox(errmsg.GetData(),"Internal program error...",wxOK | wxICON_EXCLAMATION);
+ return FALSE;
+ }
+ strcpy(DatabaseDemoApp.params.UserName, tStr.GetData());
+
+ tStr = pParamPasswordTxt->GetValue();
+ if (tStr.Length() > (sizeof(DatabaseDemoApp.params.Password)-1))
+ {
+ wxString errmsg;
+ errmsg.sprintf("Password is longer than the data structure to hold it.\n'Cparameter.Password' must have a larger character array\nto handle a data source with this long of a name\n\nThe password currently specified is %d characters long.",tStr.Length());
+ wxMessageBox(errmsg.GetData(),"Internal program error...",wxOK | wxICON_EXCLAMATION);
+ return FALSE;
+ }
+ strcpy(DatabaseDemoApp.params.Password,tStr.GetData());
+ return TRUE;
+} // CparameterDlg::GetData()
+
+
+Bool CparameterDlg::Save()
+{
+ Cparameters saveParams = DatabaseDemoApp.params;
+ if (!GetData())
+ {
+ DatabaseDemoApp.params = saveParams;
+ return FALSE;
+ }
+
+ FILE *paramFile;
+ if ((paramFile = fopen(paramFilename, "wt")) == NULL)
+ {
+ wxString tStr;
+ tStr.sprintf("Unable to write/overwrite '%s'.",paramFilename);
+ wxMessageBox(tStr.GetData(),"File I/O Error...",wxOK | wxICON_EXCLAMATION);
+ return FALSE;
+ }
+
+ fputs(DatabaseDemoApp.params.ODBCSource, paramFile);
+ fputc('\n', paramFile);
+ fputs(DatabaseDemoApp.params.UserName, paramFile);
+ fputc('\n', paramFile);
+ fputs(DatabaseDemoApp.params.Password, paramFile);
+ fputc('\n', paramFile);
+ fclose(paramFile);
+
+ return TRUE;
+} // CparameterDlg::Save()
+
+
+void CparameterDlg::FillDataSourceList()
+{
+ char Dsn[SQL_MAX_DSN_LENGTH + 1];
+ char DsDesc[255];
+ wxStringList strList;
+
+ while(GetDataSource(DbConnectInf.Henv, Dsn, SQL_MAX_DSN_LENGTH+1, DsDesc, 255))
+ strList.Add(Dsn);
+
+ strList.Sort();
+ strList.Add("");
+ char **p = strList.ListToArray();
+
+ for (int i = 0; strlen(p[i]); i++)
+ pParamODBCSourceList->Append(p[i]);
+} // CparameterDlg::CparameterDlg::FillDataSourceList()
+
+
+// CqueryDlg() constructor
+CqueryDlg::CqueryDlg(wxWindow *parent, wxDB *pDb, char *tblName[], char *pWhereArg) : wxDialogBox (parent, "Query", 1, -1, -1, 480, 360)
+{
+ wxBeginBusyCursor();
+
+ colInf = 0;
+ dbTable = 0;
+ masterTableName = tblName[0];
+ widgetPtrsSet = FALSE;
+ pDB = pDb;
+
+ // Initialize the WHERE clause from the string passed in
+ pWhere = pWhereArg; // Save a pointer to the output buffer
+ if (strlen(pWhere) > DB_MAX_WHERE_CLAUSE_LEN) // Check the length of the buffer passed in
+ {
+ wxString s;
+ s.sprintf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN+1);
+ wxMessageBox(s.GetData(),"Error...",wxOK | wxICON_EXCLAMATION);
+ Close();
+ return;
+ }
+
+ // Build the dialog
+ SetLabelPosition(wxVERTICAL);
+
+ wxFont *ButtonFont = new wxFont(12,wxSWISS,wxNORMAL,wxBOLD);
+ wxFont *TextFont = new wxFont(12,wxSWISS,wxNORMAL,wxNORMAL);
+
+ SetButtonFont(ButtonFont);
+ SetLabelFont(TextFont);
+ SetLabelPosition(wxVERTICAL);
+
+ pQueryCol1Msg = new wxMessage(this, "Column 1:", 10, 10, 69, 16, 0, "QueryCol1Msg");
+ pQueryCol1Choice = new wxChoice(this, NULL, "", 10, 27, 250, 27, 0, 0, 0, "QueryCol1Choice");
+
+ pQueryNotMsg = new wxMessage(this, "NOT", 268, 10, -1, -1, 0, "QueryNotMsg");
+ pQueryNotCheck = new wxCheckBox(this, NULL, "", 275, 37, 20, 20, 0, "QueryNotCheck");
+
+ char *choice_strings[9];
+ choice_strings[0] = "=";
+ choice_strings[1] = "<";
+ choice_strings[2] = ">";
+ choice_strings[3] = "<=";
+ choice_strings[4] = ">=";
+ choice_strings[5] = "Begins";
+ choice_strings[6] = "Contains";
+ choice_strings[7] = "Like";
+ choice_strings[8] = "Between";
+ pQueryOperatorMsg = new wxMessage(this, "Operator:", 305, 10, -1, -1, 0, "QueryOperatorMsg");
+ pQueryOperatorChoice = new wxChoice(this, NULL, "", 305, 27, 80, 27, 9, choice_strings, 0, "QueryOperatorChoice");
+
+ pQueryCol2Msg = new wxMessage(this, "Column 2:", 10, 65, 69, 16, 0, "QueryCol2Msg");
+ pQueryCol2Choice = new wxChoice(this, NULL, "", 10, 82, 250, 27, 0, 0, 0, "QueryCol2Choice");
+
+ pQuerySqlWhereMsg = new wxMessage(this, "SQL where clause:", 10, 141, -1, -1, 0, "QuerySqlWhereMsg");
+ pQuerySqlWhereMtxt = new wxMultiText(this, NULL, "", "", 10, 159, 377, 134, 0, "QuerySqlWhereMtxt");
+
+ pQueryAddBtn = new wxButton(this, NULL, "&Add", 406, 24, 56, 26, 0, "QueryAddBtn");
+ pQueryAndBtn = new wxButton(this, NULL, "A&nd", 406, 58, 56, 26, 0, "QueryAndBtn");
+ pQueryOrBtn = new wxButton(this, NULL, "&Or", 406, 92, 56, 26, 0, "QueryOrBtn");
+
+ pQueryLParenBtn = new wxButton(this, NULL, "(", 406, 126, 26, 26, 0, "QueryLParenBtn");
+ pQueryRParenBtn = new wxButton(this, NULL, ")", 436, 126, 26, 26, 0, "QueryRParenBtn");
+
+ pQueryDoneBtn = new wxButton(this, NULL, "&Done", 406, 185, 56, 26, 0, "QueryDoneBtn");
+ pQueryClearBtn = new wxButton(this, NULL, "C&lear", 406, 218, 56, 26, 0, "QueryClearBtn");
+ pQueryCountBtn = new wxButton(this, NULL, "&Count", 406, 252, 56, 26, 0, "QueryCountBtn");
+
+ pQueryValue1Msg = new wxMessage(this, "Value:", 277, 66, -1, -1, 0, "QueryValue1Msg");
+ pQueryValue1Txt = new wxText(this, NULL, "", "", 277, 83, 108, 25, 0, "QueryValue1Txt");
+
+ pQueryValue2Msg = new wxMessage(this, "AND", 238, 126, -1, -1, 0, "QueryValue2Msg");
+ pQueryValue2Txt = new wxText(this, NULL, "", "", 277, 120, 108, 25, 0, "QueryValue2Txt");
+
+ pQueryHintGrp = new wxGroupBox(this, "", 10, 291, 377, 40, 0, "QueryHintGrp");
+ pQueryHintMsg = new wxMessage(this, "", 16, 306, -1, -1, 0, "QueryHintMsg");
+
+ widgetPtrsSet = TRUE;
+ // Initialize the dialog
+ wxString qualName;
+ pQueryCol2Choice->Append("VALUE -->");
+ colInf = pDB->GetColumns(tblName);
+ for (int i = 0; colInf[i].colName && strlen(colInf[i].colName); i++)
+ {
+ // If there is more than one table being queried, qualify
+ // the column names with the table name prefix.
+ if (tblName[1] && strlen(tblName[1]))
+ {
+ qualName.sprintf("%s.%s", colInf[i].tableName, colInf[i].colName);
+ pQueryCol1Choice->Append(qualName.GetData());
+ pQueryCol2Choice->Append(qualName.GetData());
+ }
+ else // Single table query, append just the column names
+ {
+ pQueryCol1Choice->Append(colInf[i].colName);
+ pQueryCol2Choice->Append(colInf[i].colName);
+ }
+ }
+
+ pQueryCol1Choice->SetSelection(0);
+ pQueryCol2Choice->SetSelection(0);
+ pQueryOperatorChoice->SetSelection(0);
+
+ pQueryValue2Msg->Show(FALSE);
+ pQueryValue2Txt->Show(FALSE);
+
+ pQueryHintMsg->SetLabel(langQRY_EQ);
+
+ pQuerySqlWhereMtxt->SetValue(pWhere);
+
+ wxEndBusyCursor();
+
+ // Display the dialog window
+ SetModal(TRUE);
+ Centre(wxBOTH);
+ Show(TRUE);
+
+} // CqueryDlg() constructor
+
+
+void CqueryDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
+{
+ // Widget pointers won't be set when the dialog is constructed.
+ // Control is passed through this function once for each widget on
+ // a dialog as the dialog is constructed.
+ if (!widgetPtrsSet)
+ return;
+
+ wxString widgetName = win.GetName();
+
+ // Operator choice box
+ if (widgetName == pQueryOperatorChoice->GetName())
+ {
+ // Set the help text
+ switch((qryOp) pQueryOperatorChoice->GetSelection())
+ {
+ case qryOpEQ:
+ pQueryHintMsg->SetLabel(langQRY_EQ);
+ break;
+ case qryOpLT:
+ pQueryHintMsg->SetLabel(langQRY_LT);
+ break;
+ case qryOpGT:
+ pQueryHintMsg->SetLabel(langQRY_GT);
+ break;
+ case qryOpLE:
+ pQueryHintMsg->SetLabel(langQRY_LE);
+ break;
+ case qryOpGE:
+ pQueryHintMsg->SetLabel(langQRY_GE);
+ break;
+ case qryOpBEGINS:
+ pQueryHintMsg->SetLabel(langQRY_BEGINS);
+ break;
+ case qryOpCONTAINS:
+ pQueryHintMsg->SetLabel(langQRY_CONTAINS);
+ break;
+ case qryOpLIKE:
+ pQueryHintMsg->SetLabel(langQRY_LIKE);
+ break;
+ case qryOpBETWEEN:
+ pQueryHintMsg->SetLabel(langQRY_BETWEEN);
+ break;
+ }
+
+ // Hide the value2 widget
+ pQueryValue2Msg->Show(FALSE); // BETWEEN will show this widget
+ pQueryValue2Txt->Show(FALSE); // BETWEEN will show this widget
+
+ // Disable the NOT operator for <, <=, >, >=
+ switch((qryOp) pQueryOperatorChoice->GetSelection())
+ {
+ case qryOpLT:
+ case qryOpGT:
+ case qryOpLE:
+ case qryOpGE:
+ pQueryNotCheck->SetValue(0);
+ pQueryNotCheck->Enable(FALSE);
+ break;
+ default:
+ pQueryNotCheck->Enable(TRUE);
+ break;
+ }
+
+ // Manipulate the dialog to handle the selected operator
+ switch((qryOp) pQueryOperatorChoice->GetSelection())
+ {
+ case qryOpEQ:
+ case qryOpLT:
+ case qryOpGT:
+ case qryOpLE:
+ case qryOpGE:
+ pQueryCol2Choice->Enable(TRUE);
+ if (pQueryCol2Choice->GetSelection()) // Column name is highlighted
+ {
+ pQueryValue1Msg->Show(FALSE);
+ pQueryValue1Txt->Show(FALSE);
+ }
+ else // "Value" is highlighted
+ {
+ pQueryValue1Msg->Show(TRUE);
+ pQueryValue1Txt->Show(TRUE);
+ pQueryValue1Txt->SetFocus();
+ }
+ break;
+ case qryOpBEGINS:
+ case qryOpCONTAINS:
+ case qryOpLIKE:
+ pQueryCol2Choice->SetSelection(0);
+ pQueryCol2Choice->Enable(FALSE);
+ pQueryValue1Msg->Show(TRUE);
+ pQueryValue1Txt->Show(TRUE);
+ pQueryValue1Txt->SetFocus();
+ break;
+ case qryOpBETWEEN:
+ pQueryCol2Choice->SetSelection(0);
+ pQueryCol2Choice->Enable(FALSE);
+ pQueryValue2Msg->Show(TRUE);
+ pQueryValue2Txt->Show(TRUE);
+ pQueryValue1Msg->Show(TRUE);
+ pQueryValue1Txt->Show(TRUE);
+ pQueryValue1Txt->SetFocus();
+ break;
+ }
+
+ return;
+
+ } // Operator choice box
+
+ // Column 2 choice
+ if (widgetName == pQueryCol2Choice->GetName())
+ {
+ if (pQueryCol2Choice->GetSelection()) // Column name is highlighted
+ {
+ pQueryValue1Msg->Show(FALSE);
+ pQueryValue1Txt->Show(FALSE);
+ }
+ else // "Value" is highlighted
+ {
+ pQueryValue1Msg->Show(TRUE);
+ pQueryValue1Txt->Show(TRUE);
+ pQueryValue1Txt->SetFocus();
+ }
+ return;
+
+ } // Column 2 choice
+
+ // Add button
+ if (widgetName == pQueryAddBtn->GetName())
+ {
+ ProcessAddBtn();
+ return;
+
+ } // Add button
+
+ // And button
+ if (widgetName == pQueryAndBtn->GetName())
+ {
+ AppendToWhere(" AND\n");
+ return;
+
+ } // And button
+
+ // Or button
+ if (widgetName == pQueryOrBtn->GetName())
+ {
+ AppendToWhere(" OR\n");
+ return;
+
+ } // Or button
+
+ // Left Paren button
+ if (widgetName == pQueryLParenBtn->GetName())
+ {
+ AppendToWhere("(");
+ return;
+
+ } // Left Paren button
+
+ // Right paren button
+ if (widgetName == pQueryRParenBtn->GetName())
+ {
+ AppendToWhere(")");
+ return;
+
+ } // Right Paren button
+
+ // Done button
+ if (widgetName == pQueryDoneBtn->GetName())
+ {
+ // Be sure the where clause will not overflow the output buffer
+ if (strlen(pQuerySqlWhereMtxt->GetValue()) > DB_MAX_WHERE_CLAUSE_LEN)
+ {
+ wxString s;
+ s.sprintf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN+1);
+ wxMessageBox(s.GetData(),"Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ // Validate the where clause for things such as matching parens
+ if (!ValidateWhereClause())
+ return;
+ // Copy the where clause to the output buffer and exit
+ strcpy(pWhere, pQuerySqlWhereMtxt->GetValue());
+ Close();
+ return;
+
+ } // Done button
+
+ // Clear button
+ if (widgetName == pQueryClearBtn->GetName())
+ {
+ Bool Ok = (wxMessageBox("Are you sure you wish to clear the Query?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
+
+ if (Ok)
+ pQuerySqlWhereMtxt->SetValue("");
+ return;
+
+ } // Clear button
+
+ // Count button
+ if (widgetName == pQueryCountBtn->GetName())
+ {
+ wxBeginBusyCursor();
+ ProcessCountBtn();
+ wxEndBusyCursor();
+ return;
+
+ } // Count button
+
+} // CqueryDlg::OnCommand
+
+
+Bool CqueryDlg::OnClose()
+{
+ // Clean up
+ if (colInf)
+ {
+ delete [] colInf;
+ colInf = 0;
+ }
+
+ if (dbTable)
+ {
+ delete dbTable;
+ dbTable = 0;
+ }
+
+ GetParent()->SetFocus();
+ wxEndBusyCursor();
+ return TRUE;
+
+} // CqueryDlg::OnClose()
+
+/*
+Bool CqueryDlg::SetWidgetPtrs()
+{
+ Bool abort = FALSE;
+
+ abort = abort || !(pQueryCol1Choice = (wxChoice *)GetWidgetPtr("QueryCol1Choice",this));
+ abort = abort || !(pQueryNotCheck = (wxCheckBox *)GetWidgetPtr("QueryNotCheck",this));
+ abort = abort || !(pQueryOperatorChoice = (wxChoice *)GetWidgetPtr("QueryOperatorChoice",this));
+ abort = abort || !(pQueryCol2Choice = (wxChoice *)GetWidgetPtr("QueryCol2Choice",this));
+ abort = abort || !(pQueryValue1Txt = (wxText *)GetWidgetPtr("QueryValue1Txt",this));
+ abort = abort || !(pQueryValue2Txt = (wxText *)GetWidgetPtr("QueryValue2Txt",this));
+ abort = abort || !(pQuerySqlWhereMtxt = (wxMultiText *)GetWidgetPtr("QuerySqlWhereMtxt",this));
+ abort = abort || !(pQueryAddBtn = (wxButton *)GetWidgetPtr("QueryAddBtn",this));
+ abort = abort || !(pQueryAndBtn = (wxButton *)GetWidgetPtr("QueryAndBtn",this));
+ abort = abort || !(pQueryOrBtn = (wxButton *)GetWidgetPtr("QueryOrBtn",this));
+ abort = abort || !(pQueryLParenBtn = (wxButton *)GetWidgetPtr("QueryLParenBtn",this));
+ abort = abort || !(pQueryRParenBtn = (wxButton *)GetWidgetPtr("QueryRParenBtn",this));
+ abort = abort || !(pQueryDoneBtn = (wxButton *)GetWidgetPtr("QueryDoneBtn",this));
+ abort = abort || !(pQueryClearBtn = (wxButton *)GetWidgetPtr("QueryClearBtn",this));
+ abort = abort || !(pQueryCountBtn = (wxButton *)GetWidgetPtr("QueryCountBtn",this));
+ abort = abort || !(pQueryHelpBtn = (wxButton *)GetWidgetPtr("QueryHelpBtn",this));
+ abort = abort || !(pQueryHintMsg = (wxMessage *)GetWidgetPtr("QueryHintMsg",this));
+
+ pFocusTxt = NULL;
+
+ return(widgetPtrsSet = !abort);
+
+} // CqueryDlg::SetWidgetPtrs
+*/
+
+void CqueryDlg::AppendToWhere(char *s)
+{
+ wxString whereStr = pQuerySqlWhereMtxt->GetValue();
+ whereStr += s;
+ pQuerySqlWhereMtxt->SetValue(whereStr.GetData());
+
+} // CqueryDlg::AppendToWhere()
+
+
+void CqueryDlg::ProcessAddBtn()
+{
+ qryOp oper = (qryOp) pQueryOperatorChoice->GetSelection();
+
+ // Verify that eveything is filled in correctly
+ if (pQueryCol2Choice->GetSelection() == 0) // "Value" is selected
+ {
+ // Verify that value 1 is filled in
+ if (strlen(pQueryValue1Txt->GetValue()) == 0)
+ {
+ wxBell();
+ pQueryValue1Txt->SetFocus();
+ return;
+ }
+ // For the BETWEEN operator, value 2 must be filled in as well
+ if (oper == qryOpBETWEEN &&
+ strlen(pQueryValue2Txt->GetValue()) == 0)
+ {
+ wxBell();
+ pQueryValue2Txt->SetFocus();
+ return;
+ }
+ }
+
+ // Build the expression and append it to the where clause window
+ wxString s = pQueryCol1Choice->GetStringSelection();
+
+ if (pQueryNotCheck->GetValue() && (oper != qryOpEQ))
+ s += " NOT";
+
+ switch(oper)
+ {
+ case qryOpEQ:
+ if (pQueryNotCheck->GetValue()) // NOT box is checked
+ s += " <>";
+ else
+ s += " =";
+ break;
+ case qryOpLT:
+ s += " <";
+ break;
+ case qryOpGT:
+ s += " >";
+ break;
+ case qryOpLE:
+ s += " <=";
+ break;
+ case qryOpGE:
+ s += " >=";
+ break;
+ case qryOpBEGINS:
+ case qryOpCONTAINS:
+ case qryOpLIKE:
+ s += " LIKE";
+ break;
+ case qryOpBETWEEN:
+ s += " BETWEEN";
+ break;
+ }
+
+ s += " ";
+
+ int col1Idx = pQueryCol1Choice->GetSelection();
+
+ Bool quote = FALSE;
+ if (colInf[col1Idx].sqlDataType == SQL_VARCHAR ||
+ oper == qryOpBEGINS ||
+ oper == qryOpCONTAINS ||
+ oper == qryOpLIKE)
+ quote = TRUE;
+
+ if (pQueryCol2Choice->GetSelection()) // Column name
+ s += pQueryCol2Choice->GetStringSelection();
+ else // Column 2 is a "value"
+ {
+ if (quote)
+ s += "'";
+ if (oper == qryOpCONTAINS)
+ s += "%";
+ s += pQueryValue1Txt->GetValue();
+ if (oper == qryOpCONTAINS || oper == qryOpBEGINS)
+ s += "%";
+ if (quote)
+ s += "'";
+ }
+
+ if (oper == qryOpBETWEEN)
+ {
+ s += " AND ";
+ if (quote)
+ s += "'";
+ s += pQueryValue2Txt->GetValue();
+ if (quote)
+ s += "'";
+ }
+
+ AppendToWhere(s.GetData());
+
+} // CqueryDlg::ProcessAddBtn()
+
+
+void CqueryDlg::ProcessCountBtn()
+{
+ if (!ValidateWhereClause())
+ return;
+
+ if (dbTable == 0) // wxTable object needs to be created and opened
+ {
+ if (!(dbTable = new wxTable(pDB, masterTableName, 0)))
+ {
+ wxMessageBox("Memory allocation failed creating a wxTable object.","Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ if (!dbTable->Open())
+ {
+ wxString tStr;
+ tStr = "ODBC error during Open()\n\n";
+ tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
+ wxMessageBox(tStr.GetData(),"ODBC Error...",wxOK | wxICON_EXCLAMATION);
+ return;
+ }
+ }
+
+ // Count() with WHERE clause
+ dbTable->where = pQuerySqlWhereMtxt->GetValue();
+ ULONG whereCnt = dbTable->Count();
+
+ // Count() of all records in the table
+ dbTable->where = 0;
+ ULONG totalCnt = dbTable->Count();
+
+ if (whereCnt > 0 || totalCnt == 0)
+ {
+ wxString tStr;
+ tStr.sprintf("%lu of %lu records match the query criteria.",whereCnt,totalCnt);
+ wxMessageBox(tStr.GetData(),"Notice...",wxOK | wxICON_INFORMATION);
+ }
+ else
+ {
+ wxString tStr;
+ tStr.sprintf("%lu of %lu records match the query criteria.\n\nEither the criteria entered produced a result set\nwith no records, or there was a syntactical error\nin the clause you entered.\n\nPress the details button to see if any database errors were reported.",whereCnt,totalCnt);
+ wxMessageBox(tStr.GetData(),"Notice...",wxOK | wxICON_INFORMATION);
+ }
+
+ // After a wxMessageBox, the focus does not necessarily return to the
+ // window which was the focus when the message box popped up, so return
+ // focus to the Query dialog for certain
+ SetFocus();
+
+} // CqueryDlg::ProcessCountBtn()
+
+
+Bool CqueryDlg::ValidateWhereClause()
+{
+ wxString where = pQuerySqlWhereMtxt->GetValue();
+
+ if (where.Freq('(') != where.Freq(')'))
+ {
+ wxMessageBox("There are mismatched parenthesis in the constructed where clause","Error...",wxOK | wxICON_EXCLAMATION);
+ return(FALSE);
+ }
+ // After a wxMessageBox, the focus does not necessarily return to the
+ // window which was the focus when the message box popped up, so return
+ // focus to the Query dialog for certain
+ SetFocus();
+
+ return(TRUE);
+
+} // CqueryDlg::ValidateWhereClause()