1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxWindows database demo app
4 // Author: George Tasker
8 // Copyright: (c) 1998 Remstar International, Inc.
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
15 This sample program demonstrates the cross-platform ODBC database classes
16 donated by the development team at Remstar International.
18 The table this sample is based on is developer contact table, and shows
19 some of the simple uses of the database classes wxDB and wxTable.
27 #pragma implementation "dbtest.h"
30 #include "wx/wxprec.h"
44 #include <stdio.h> // Included strictly for reading the text file with the database parameters
46 #include <wx/db.h> // Required in the file which will get the data source connection
47 #include <wx/dbtable.h> // Has the wxTable object from which all data objects will inherit their data table functionality
49 extern DbList
* WXDLLEXPORT PtrBegDbList
; // from db.cpp, used in getting back error results from db connections
51 #include "dbtest.h" // Header file for this demonstration program
52 #include "listdb.h" // Code to support the "Lookup" button on the editor dialog
54 IMPLEMENT_APP(DatabaseDemoApp
)
56 extern char ListDB_Selection
[]; // Used to return the first column value for the selected line from the listDB routines
57 extern char ListDB_Selection2
[]; // Used to return the second column value for the selected line from the listDB routines
59 DatabaseDemoFrame
*DemoFrame
; // Pointer to the main frame
62 /* Pointer to the main database connection used in the program. This
63 * pointer would normally be used for doing things as database lookups
64 * for user login names and passwords, getting workstation settings, etc.
67 * For each database object created which uses this wxDB pointer
68 * connection to the database, when a CommitTrans() or RollBackTrans()
69 * will commit or rollback EVERY object which uses this wxDB pointer.
71 * To allow each table object (those derived from wxTable) to be
72 * individually committed or rolled back, you MUST use a different
73 * instance of wxDB in the constructor of the table. Doing so creates
74 * more overhead, and will use more database connections (some DBs have
75 * connection limits...), so use connections sparringly.
77 * It is recommended that one "main" database connection be created for
78 * the entire program to use for READ-ONLY database accesses, but for each
79 * table object which will do a CommitTrans() or RollbackTrans() that a
80 * new wxDB object be created and used for it.
86 * This function will return the exact string(s) from the database engine
87 * indicating all error conditions which have just occured during the
88 * last call to the database engine.
90 * This demo uses the returned string by displaying it in a wxMessageBox. The
91 * formatting therefore is not the greatest, but this is just a demo, not a
92 * finished product. :-) gt
94 * NOTE: The value returned by this function is for temporary use only and
95 * should be copied for long term use
97 char *GetExtendedDBErrorMsg(char *ErrFile
, int ErrLine
)
103 if (ErrFile
|| ErrLine
)
110 tStr
.Printf("%d",ErrLine
);
111 msg
+= tStr
.GetData();
116 msg
.Append ("\nODBC ERRORS\n");
119 // Scan through each database connection displaying
120 // any ODBC errors that have occured.
121 for (DbList
*pDbList
= PtrBegDbList
; pDbList
; pDbList
= pDbList
->PtrNext
)
123 // Skip over any free connections
126 // Display errors for this connection
127 for (int i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
129 if (pDbList
->PtrDb
->errorList
[i
])
131 msg
.Append(pDbList
->PtrDb
->errorList
[i
]);
132 if (strcmp(pDbList
->PtrDb
->errorList
[i
],"") != 0)
139 return (char*) (const char*) msg
;
140 } // GetExtendedDBErrorMsg
143 bool DatabaseDemoApp::OnInit()
145 // Create the main frame window
146 DemoFrame
= new DatabaseDemoFrame(NULL
, "wxWindows Database Demo", wxPoint(50, 50), wxSize(537, 480));
149 DemoFrame
->SetIcon(wxICON(db
));
152 wxMenu
*file_menu
= new wxMenu
;
153 file_menu
->Append(FILE_CREATE
, "&Create contact table");
154 file_menu
->Append(FILE_EXIT
, "E&xit");
156 wxMenu
*edit_menu
= new wxMenu
;
157 edit_menu
->Append(EDIT_PARAMETERS
, "&Parameters...");
159 wxMenu
*about_menu
= new wxMenu
;
160 about_menu
->Append(ABOUT_DEMO
, "&About");
162 wxMenuBar
*menu_bar
= new wxMenuBar
;
163 menu_bar
->Append(file_menu
, "&File");
164 menu_bar
->Append(edit_menu
, "&Edit");
165 menu_bar
->Append(about_menu
, "&About");
166 DemoFrame
->SetMenuBar(menu_bar
);
168 // Initialize the ODBC Environment for Database Operations
169 if (SQLAllocEnv(&DbConnectInf
.Henv
) != SQL_SUCCESS
)
171 wxMessageBox("A problem occured while trying to get a connection to the data source","DB CONNECTION ERROR",wxOK
| wxICON_EXCLAMATION
);
176 if ((paramFile
= fopen(paramFilename
, "r")) == NULL
)
179 tStr
.Printf("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
);
180 wxMessageBox(tStr
,"File I/O Error...",wxOK
| wxICON_EXCLAMATION
);
181 DemoFrame
->BuildParameterDialog(NULL
);
182 if ((paramFile
= fopen(paramFilename
, "r")) == NULL
)
187 fgets(buffer
, sizeof(params
.ODBCSource
), paramFile
);
188 buffer
[strlen(buffer
)-1] = '\0';
189 strcpy(params
.ODBCSource
,buffer
);
191 fgets(buffer
, sizeof(params
.UserName
), paramFile
);
192 buffer
[strlen(buffer
)-1] = '\0';
193 strcpy(params
.UserName
,buffer
);
195 fgets(buffer
, sizeof(params
.Password
), paramFile
);
196 buffer
[strlen(buffer
)-1] = '\0';
197 strcpy(params
.Password
,buffer
);
201 // Connect to datasource
202 strcpy(DbConnectInf
.Dsn
, params
.ODBCSource
); // ODBC data source name (created with ODBC Administrator under Win95/NT)
203 strcpy(DbConnectInf
.Uid
, params
.UserName
); // database username - must already exist in the data source
204 strcpy(DbConnectInf
.AuthStr
, params
.Password
); // password database username
205 READONLY_DB
= GetDbConnection(&DbConnectInf
);
206 if (READONLY_DB
== 0)
208 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
);
209 DemoFrame
->BuildParameterDialog(NULL
);
210 strcpy(DbConnectInf
.Dsn
, "");
211 strcpy(DbConnectInf
.Uid
, "");
212 strcpy(DbConnectInf
.AuthStr
, "");
213 wxMessageBox("Now exiting program.\n\nRestart program to try any new settings.","Notice...",wxOK
| wxICON_INFORMATION
);
217 DemoFrame
->BuildEditorDialog();
220 DemoFrame
->Show(TRUE
);
223 } // DatabaseDemoApp::OnInit()
225 BEGIN_EVENT_TABLE(DatabaseDemoFrame
, wxFrame
)
226 EVT_MENU(FILE_CREATE
, DatabaseDemoFrame::OnCreate
)
227 EVT_MENU(FILE_EXIT
, DatabaseDemoFrame::OnExit
)
228 EVT_MENU(EDIT_PARAMETERS
, DatabaseDemoFrame::OnEditParameters
)
229 EVT_MENU(ABOUT_DEMO
, DatabaseDemoFrame::OnAbout
)
230 EVT_CLOSE(DatabaseDemoFrame::OnCloseWindow
)
233 // DatabaseDemoFrame constructor
234 DatabaseDemoFrame::DatabaseDemoFrame(wxFrame
*frame
, const wxString
& title
,
235 const wxPoint
& pos
, const wxSize
& size
):
236 wxFrame(frame
, -1, title
, pos
, size
)
238 // Put any code in necessary for initializing the main frame here
241 void DatabaseDemoFrame::OnCreate(wxCommandEvent
& event
)
246 void DatabaseDemoFrame::OnExit(wxCommandEvent
& event
)
251 void DatabaseDemoFrame::OnEditParameters(wxCommandEvent
& event
)
253 if ((pEditorDlg
->mode
!= mCreate
) && (pEditorDlg
->mode
!= mEdit
))
254 BuildParameterDialog(this);
256 wxMessageBox("Cannot change database parameters while creating or editing a record","Notice...",wxOK
| wxICON_INFORMATION
);
259 void DatabaseDemoFrame::OnAbout(wxCommandEvent
& event
)
261 wxMessageBox("wxWindows sample program for database classes\n\nContributed on 27 July 1998","About...",wxOK
| wxICON_INFORMATION
);
264 void DatabaseDemoFrame::OnCloseWindow(wxCloseEvent
& event
)
266 // Put any additional checking necessary to make certain it is alright
267 // to close the program here that is not done elsewhere
270 } // DatabaseDemoFrame::OnClose()
273 void DatabaseDemoFrame::CreateDataTable()
275 bool Ok
= (wxMessageBox("Any data currently residing in the table will be erased.\n\nAre you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
284 Ccontact
*Contact
= new Ccontact();
288 wxMessageBox("Error allocating memory for 'Ccontact'object.\n\nTable was not created.","Error...",wxOK
| wxICON_EXCLAMATION
);
292 if (!Contact
->CreateTable())
296 tStr
= "Error creating CONTACTS table.\nTable was not created.\n\n";
297 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
298 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
303 if (!Contact
->CreateIndexes())
307 tStr
= "Error creating CONTACTS indexes.\nIndexes will be unavailable.\n\n";
308 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
309 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
318 wxMessageBox("Table and index(es) were successfully created.","Notice...",wxOK
| wxICON_INFORMATION
);
319 } // DatabaseDemoFrame::CreateDataTable()
322 void DatabaseDemoFrame::BuildEditorDialog()
324 pEditorDlg
= new CeditorDlg(this);
326 wxMessageBox("Unable to create the editor dialog for some reason","Error...",wxOK
| wxICON_EXCLAMATION
);
327 } // DatabaseDemoFrame::BuildEditorDialog()
330 void DatabaseDemoFrame::BuildParameterDialog(wxWindow
*parent
)
332 pParamDlg
= new CparameterDlg(parent
);
335 wxMessageBox("Unable to create the parameter dialog for some reason","Error...",wxOK
| wxICON_EXCLAMATION
);
336 } // DatabaseDemoFrame::BuildParameterDialog()
340 * Constructor note: If no wxDB object is passed in, a new connection to the database
341 * is created for this instance of Ccontact. This can be a slow process depending
342 * on the database engine being used, and some database engines have a limit on the
343 * number of connections (either hard limits, or license restricted) so care should
344 * be used to use as few connections as is necessary.
345 * IMPORTANT: Objects which share a wxDB pointer are ALL acted upon whenever a member
346 * function of pDb is called (i.e. CommitTrans() or RollbackTrans(), so if modifying
347 * or creating a table objects which use the same pDb, know that all the objects
348 * will be committed or rolled back when any of the objects has this function call made.
350 Ccontact::Ccontact (wxDB
*pwxDB
) : wxTable(pwxDB
? pwxDB
: GetDbConnection(&DbConnectInf
),CONTACT_TABLE_NAME
,CONTACT_NO_COLS
)
352 // This is used to represent whether the database connection should be released
353 // when this instance of the object is deleted. If using the same connection
354 // for multiple instance of database objects, then the connection should only be
355 // released when the last database instance using the connection is deleted
360 } // Ccontact Constructor
363 void Ccontact::Initialize()
372 JoinDate
.year
= 1980;
378 JoinDate
.fraction
= 0;
379 NativeLanguage
= langENGLISH
;
383 } // Ccontact::Initialize
386 Ccontact::~Ccontact()
390 if (!FreeDbConnection(pDb
))
393 tStr
= "Unable to Free the Ccontact data table handle\n\n";
394 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
395 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
398 } // Ccontract destructor
402 * Handles setting up all the connections for the interface from the wxTable
403 * functions to interface to the data structure used to store records in
404 * memory, and for all the column definitions that define the table structure
406 void Ccontact::SetupColumns()
408 SetColDefs ( 0,"NAME", DB_DATA_TYPE_VARCHAR
, Name
, SQL_C_CHAR
, sizeof(Name
), TRUE
, TRUE
); // Primary index
409 SetColDefs ( 1,"ADDRESS1", DB_DATA_TYPE_VARCHAR
, Addr1
, SQL_C_CHAR
, sizeof(Addr1
), FALSE
,TRUE
);
410 SetColDefs ( 2,"ADDRESS2", DB_DATA_TYPE_VARCHAR
, Addr2
, SQL_C_CHAR
, sizeof(Addr2
), FALSE
,TRUE
);
411 SetColDefs ( 3,"CITY", DB_DATA_TYPE_VARCHAR
, City
, SQL_C_CHAR
, sizeof(City
), FALSE
,TRUE
);
412 SetColDefs ( 4,"STATE", DB_DATA_TYPE_VARCHAR
, State
, SQL_C_CHAR
, sizeof(State
), FALSE
,TRUE
);
413 SetColDefs ( 5,"POSTAL_CODE", DB_DATA_TYPE_VARCHAR
, PostalCode
, SQL_C_CHAR
, sizeof(PostalCode
), FALSE
,TRUE
);
414 SetColDefs ( 6,"COUNTRY", DB_DATA_TYPE_VARCHAR
, Country
, SQL_C_CHAR
, sizeof(Country
), FALSE
,TRUE
);
415 SetColDefs ( 7,"JOIN_DATE", DB_DATA_TYPE_DATE
, &JoinDate
, SQL_C_TIMESTAMP
, sizeof(JoinDate
), FALSE
,TRUE
);
416 SetColDefs ( 8,"NATIVE_LANGUAGE", DB_DATA_TYPE_INTEGER
, &NativeLanguage
, SQL_C_ENUM
, sizeof(NativeLanguage
), FALSE
,TRUE
);
417 SetColDefs ( 9,"IS_DEVELOPER", DB_DATA_TYPE_INTEGER
, &IsDeveloper
, SQL_C_BOOLEAN
, sizeof(bool), FALSE
,TRUE
);
418 SetColDefs (10,"CONTRIBUTIONS", DB_DATA_TYPE_INTEGER
, &Contributions
, SQL_C_USHORT
, sizeof(Contributions
), FALSE
,TRUE
);
419 SetColDefs (11,"LINES_OF_CODE", DB_DATA_TYPE_INTEGER
, &LinesOfCode
, SQL_C_ULONG
, sizeof(LinesOfCode
), FALSE
,TRUE
);
420 } // Ccontact::SetupColumns
423 bool Ccontact::CreateIndexes(void)
425 // This index could easily be accomplished with an "orderBy" clause,
426 // but is done to show how to construct a non-primary index.
432 strcpy(idxDef
[0].ColName
, "IS_DEVELOPER");
433 idxDef
[0].Ascending
= TRUE
;
435 strcpy(idxDef
[1].ColName
, "NAME");
436 idxDef
[1].Ascending
= TRUE
;
438 indexName
= CONTACT_TABLE_NAME
;
439 indexName
+= "_IDX1";
440 Ok
= CreateIndex((char*) (const char*) indexName
, TRUE
, 2, idxDef
);
443 } // Ccontact::CreateIndexes()
447 * Having a function to do a query on the primary key (and possibly others) is
448 * very efficient and tighter coding so that it is available where ever the object
449 * is. Great for use with multiple tables when not using views or outer joins
451 bool Ccontact::FetchByName(char *name
)
453 whereStr
.Printf("NAME = '%s'",name
);
454 where
= (char*) (const char*) this->whereStr
;
463 } // Ccontact::FetchByName()
468 * ************* DIALOGS ***************
473 /* CeditorDlg constructor
475 * Creates the dialog used for creating/editing/deleting/copying a Ccontact object.
476 * This dialog actually is drawn in the main frame of the program
478 * An instance of Ccontact is created - "Contact" - which is used to hold the Ccontact
479 * object that is currently being worked with.
482 BEGIN_EVENT_TABLE(CeditorDlg
, wxPanel
)
483 EVT_BUTTON(-1, CeditorDlg::OnButton
)
486 CeditorDlg::CeditorDlg(wxWindow
*parent
) : wxPanel (parent
, 1, 1, 460, 455)
488 // Since the ::OnCommand() function is overridden, this prevents the widget
489 // detection in ::OnCommand() until all widgets have been initialized to prevent
490 // uninitialized pointers from crashing the program
491 widgetPtrsSet
= FALSE
;
493 // Create the data structure and a new database connection.
494 // (As there is not a pDb being passed in the constructor, a new database
495 // connection is created)
496 Contact
= new Ccontact();
500 wxMessageBox("Unable to instantiate an instance of Ccontact","Error...",wxOK
| wxICON_EXCLAMATION
);
504 // Check if the table exists or not. If it doesn't, ask the user if they want to
505 // create the table. Continue trying to create the table until it exists, or user aborts
506 while (!Contact
->pDb
->TableExists((char *)CONTACT_TABLE_NAME
))
509 tStr
.Printf("Unable to open the table '%s'.\n\nTable may need to be created...?\n\n",CONTACT_TABLE_NAME
);
510 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
511 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
513 bool createTable
= (wxMessageBox("Do you wish to try to create/clear the CONTACTS table?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
523 DemoFrame
->CreateDataTable();
526 // Tables must be "opened" before anything other than creating/deleting table can be done
527 if (!Contact
->Open())
529 // Table does exist, there was some problem opening it. Currently this should
530 // never fail, except in the case of the table not exisiting. Open() basically
531 // only sets up variable/pointer values, other than checking for table existence.
532 if (Contact
->pDb
->TableExists((char *)CONTACT_TABLE_NAME
))
535 tStr
.Printf("Unable to open the table '%s'.\n\n",CONTACT_TABLE_NAME
);
536 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
537 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
547 wxStaticBox
*FunctionGrp
= new wxStaticBox(this, EDITOR_DIALOG_FN_GROUP
, "", wxPoint(15, 1), wxSize(497, 69), 0, "FunctionGrp");
548 wxStaticBox
*SearchGrp
= new wxStaticBox(this, EDITOR_DIALOG_SEARCH_GROUP
, "", wxPoint(417, 1), wxSize(95, 242), 0, "SearchGrp");
550 pCreateBtn
= new wxButton(this, EDITOR_DIALOG_CREATE
, "&Create", wxPoint(25, 21), wxSize(70, 35), 0, wxDefaultValidator
, "CreateBtn");
551 pEditBtn
= new wxButton(this, EDITOR_DIALOG_EDIT
, "&Edit", wxPoint(102, 21), wxSize(70, 35), 0, wxDefaultValidator
, "EditBtn");
552 pDeleteBtn
= new wxButton(this, EDITOR_DIALOG_DELETE
, "&Delete", wxPoint(179, 21), wxSize(70, 35), 0, wxDefaultValidator
, "DeleteBtn");
553 pCopyBtn
= new wxButton(this, EDITOR_DIALOG_COPY
, "Cop&y", wxPoint(256, 21), wxSize(70, 35), 0, wxDefaultValidator
, "CopyBtn");
554 pSaveBtn
= new wxButton(this, EDITOR_DIALOG_SAVE
, "&Save", wxPoint(333, 21), wxSize(70, 35), 0, wxDefaultValidator
, "SaveBtn");
555 pCancelBtn
= new wxButton(this, EDITOR_DIALOG_CANCEL
, "C&ancel", wxPoint(430, 21), wxSize(70, 35), 0, wxDefaultValidator
, "CancelBtn");
557 pPrevBtn
= new wxButton(this, EDITOR_DIALOG_PREV
, "<< &Prev", wxPoint(430, 81), wxSize(70, 35), 0, wxDefaultValidator
, "PrevBtn");
558 pNextBtn
= new wxButton(this, EDITOR_DIALOG_NEXT
, "&Next >>", wxPoint(430, 121), wxSize(70, 35), 0, wxDefaultValidator
, "NextBtn");
559 pQueryBtn
= new wxButton(this, EDITOR_DIALOG_QUERY
, "&Query", wxPoint(430, 161), wxSize(70, 35), 0, wxDefaultValidator
, "QueryBtn");
560 pResetBtn
= new wxButton(this, EDITOR_DIALOG_RESET
, "&Reset", wxPoint(430, 200), wxSize(70, 35), 0, wxDefaultValidator
, "ResetBtn");
562 pNameMsg
= new wxStaticText(this, EDITOR_DIALOG_NAME_MSG
, "Name:", wxPoint(17, 80), wxSize(-1, -1), 0, "NameMsg");
563 pNameTxt
= new wxTextCtrl(this, EDITOR_DIALOG_NAME_TEXT
, "", wxPoint(17, 97), wxSize(308, 25), 0, wxDefaultValidator
, "NameTxt");
564 pNameListBtn
= new wxButton(this, EDITOR_DIALOG_LOOKUP
, "&Lookup", wxPoint(333, 99), wxSize(70, 24), 0, wxDefaultValidator
, "LookupBtn");
566 pAddress1Msg
= new wxStaticText(this, EDITOR_DIALOG_ADDRESS1_MSG
, "Address:", wxPoint(17, 130), wxSize(-1, -1), 0, "Address1Msg");
567 pAddress1Txt
= new wxTextCtrl(this, EDITOR_DIALOG_ADDRESS2_TEXT
, "", wxPoint(17, 147), wxSize(308, 25), 0, wxDefaultValidator
, "Address1Txt");
569 pAddress2Msg
= new wxStaticText(this, EDITOR_DIALOG_ADDRESS2_MSG
, "Address:", wxPoint(17, 180), wxSize(-1, -1), 0, "Address2Msg");
570 pAddress2Txt
= new wxTextCtrl(this, EDITOR_DIALOG_ADDRESS2_TEXT
, "", wxPoint(17, 197), wxSize(308, 25), 0, wxDefaultValidator
, "Address2Txt");
572 pCityMsg
= new wxStaticText(this, EDITOR_DIALOG_CITY_MSG
, "City:", wxPoint(17, 230), wxSize(-1, -1), 0, "CityMsg");
573 pCityTxt
= new wxTextCtrl(this, EDITOR_DIALOG_CITY_TEXT
, "", wxPoint(17, 247), wxSize(225, 25), 0, wxDefaultValidator
, "CityTxt");
575 pStateMsg
= new wxStaticText(this, EDITOR_DIALOG_STATE_MSG
, "State:", wxPoint(250, 230), wxSize(-1, -1), 0, "StateMsg");
576 pStateTxt
= new wxTextCtrl(this, EDITOR_DIALOG_STATE_TEXT
, "", wxPoint(250, 247), wxSize(153, 25), 0, wxDefaultValidator
, "StateTxt");
578 pCountryMsg
= new wxStaticText(this, EDITOR_DIALOG_COUNTRY_MSG
, "Country:", wxPoint(17, 280), wxSize(-1, -1), 0, "CountryMsg");
579 pCountryTxt
= new wxTextCtrl(this, EDITOR_DIALOG_COUNTRY_TEXT
, "", wxPoint(17, 297), wxSize(225, 25), 0, wxDefaultValidator
, "CountryTxt");
581 pPostalCodeMsg
= new wxStaticText(this, EDITOR_DIALOG_POSTAL_MSG
, "Postal Code:", wxPoint(250, 280), wxSize(-1, -1), 0, "PostalCodeMsg");
582 pPostalCodeTxt
= new wxTextCtrl(this, EDITOR_DIALOG_POSTAL_TEXT
, "", wxPoint(250, 297), wxSize(153, 25), 0, wxDefaultValidator
, "PostalCodeTxt");
584 wxString choice_strings
[5];
585 choice_strings
[0] = "English";
586 choice_strings
[1] = "French";
587 choice_strings
[2] = "German";
588 choice_strings
[3] = "Spanish";
589 choice_strings
[4] = "Other";
590 pNativeLangChoice
= new wxChoice(this, EDITOR_DIALOG_LANG_CHOICE
, wxPoint(17, 346), wxSize(277, -1), 5, choice_strings
);
591 pNativeLangMsg
= new wxStaticText(this, EDITOR_DIALOG_LANG_MSG
, "Native language:", wxPoint(17, 330), wxSize(-1, -1), 0, "NativeLangMsg");
593 wxString radio_strings
[2];
594 radio_strings
[0] = "No";
595 radio_strings
[1] = "Yes";
596 pDeveloperRadio
= new wxRadioBox(this,EDITOR_DIALOG_DEVELOPER
,"Developer:",wxPoint(303,330),wxSize(-1,-1),2,radio_strings
,2,wxHORIZONTAL
);
598 pJoinDateMsg
= new wxStaticText(this, EDITOR_DIALOG_JOIN_MSG
, "Date joined:", wxPoint(17, 380), wxSize(-1, -1), 0, "JoinDateMsg");
599 pJoinDateTxt
= new wxTextCtrl(this, EDITOR_DIALOG_JOIN_TEXT
, "", wxPoint(17, 397), wxSize(150, 25), 0, wxDefaultValidator
, "JoinDateTxt");
601 pContribMsg
= new wxStaticText(this, EDITOR_DIALOG_CONTRIB_MSG
, "Contributions:", wxPoint(175, 380), wxSize(-1, -1), 0, "ContribMsg");
602 pContribTxt
= new wxTextCtrl(this, EDITOR_DIALOG_CONTRIB_TEXT
, "", wxPoint(175, 397), wxSize(120, 25), 0, wxDefaultValidator
, "ContribTxt");
604 pLinesMsg
= new wxStaticText(this, EDITOR_DIALOG_LINES_MSG
, "Lines of code:", wxPoint(303, 380), wxSize(-1, -1), 0, "LinesMsg");
605 pLinesTxt
= new wxTextCtrl(this, EDITOR_DIALOG_LINES_TEXT
, "", wxPoint(303, 397), wxSize(100, 25), 0, wxDefaultValidator
, "LinesTxt");
607 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
608 // handle all widget processing
609 widgetPtrsSet
= TRUE
;
611 // Setup the orderBy and where clauses to return back a single record as the result set,
612 // as there will only be one record being shown on the dialog at a time, this optimizes
613 // network traffic by only returning a one row result
615 Contact
->orderBy
= "NAME"; // field name to sort by
617 // The wxString "whereStr" is not a member of the wxTable object, it is a member variable
618 // specifically in the Ccontact class. It is used here for simpler construction of a varying
619 // length string, and then after the string is built, the wxTable member variable "where" is
620 // assigned the pointer to the constructed string.
622 // The constructed where clause below has a sub-query within it "SELECT MIN(NAME) FROM %s"
623 // to achieve a single row (in this case the first name in alphabetical order).
625 Contact
->whereStr
.Printf("NAME = (SELECT MIN(NAME) FROM %s)",Contact
->tableName
);
627 // NOTE: (const char*) returns a pointer which may not be valid later, so this is short term use only
628 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
630 // Perform the Query to get the result set.
631 // NOTE: If there are no rows returned, that is a valid result, so Query() would return TRUE.
632 // Only if there is a database error will Query() come back as FALSE
633 if (!Contact
->Query())
636 tStr
= "ODBC error during Query()\n\n";
637 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
638 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
639 GetParent()->Close();
643 // Since Query succeeded, now get the row that was returned
644 if (!Contact
->GetNext())
645 // If the GetNext() failed at this point, then there are no rows to retrieve,
646 // so clear the values in the members of "Contact" so that PutData() blanks the
647 // widgets on the dialog
648 Contact
->Initialize();
654 } // CeditorDlg constructor
657 bool CeditorDlg::OnClose()
660 if ((mode
!= mCreate
) && (mode
!= mEdit
))
668 wxMessageBox("Must finish processing the current record being created/modified before exiting","Notice...",wxOK
| wxICON_INFORMATION
);
671 } // CeditorDlg::OnClose()
674 void CeditorDlg::OnButton( wxCommandEvent
&event
)
676 wxWindow
*win
= (wxWindow
*) event
.GetEventObject();
677 OnCommand( *win
, event
);
680 void CeditorDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
684 widgetName
= win
.GetName();
689 if (widgetName
== pCreateBtn
->GetName())
691 Contact
->Initialize();
694 pNameTxt
->SetValue("");
695 pNameTxt
->SetFocus();
699 if (widgetName
== pEditBtn
->GetName())
701 saveName
= Contact
->Name
;
703 pNameTxt
->SetFocus();
707 if (widgetName
== pCopyBtn
->GetName())
710 pNameTxt
->SetValue("");
711 pNameTxt
->SetFocus();
715 if (widgetName
== pDeleteBtn
->GetName())
717 bool Ok
= (wxMessageBox("Are you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
722 if (Ok
&& Contact
->Delete())
724 // NOTE: Deletions are not finalized until a CommitTrans() is performed.
725 // If the commit were not performed, the program will continue to
726 // show the table contents as if they were deleted until this instance
727 // of Ccontact is deleted. If the Commit wasn't performed, the
728 // database will automatically Rollback the changes when the database
729 // connection is terminated
730 Contact
->pDb
->CommitTrans();
732 // Try to get the row that followed the just deleted row in the orderBy sequence
735 // There was now row (in sequence) after the just deleted row, so get the
736 // row which preceded the just deleted row
739 // There are now no rows remaining, so clear the dialog widgets
740 Contact
->Initialize();
744 SetMode(mode
); // force reset of button enable/disable
748 Contact
->pDb
->RollbackTrans();
754 if (widgetName
== pSaveBtn
->GetName())
760 if (widgetName
== pCancelBtn
->GetName())
762 bool Ok
= (wxMessageBox("Are you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
767 if (!strcmp((const char*) saveName
,""))
769 Contact
->Initialize();
776 // Requery previous record
777 if (Contact
->FetchByName((char*) (const char*) saveName
))
785 // Previous record not available, retrieve first record in table
786 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
787 Contact
->whereStr
+= Contact
->tableName
;
788 Contact
->whereStr
+= ")";
789 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
790 if (!Contact
->Query())
793 tStr
= "ODBC error during Query()\n\n";
794 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
795 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
799 if (Contact
->GetNext()) // Successfully read first record
805 // No contacts are available, clear dialog
806 Contact
->Initialize();
812 if (widgetName
== pPrevBtn
->GetName())
819 if (widgetName
== pNextBtn
->GetName())
826 if (widgetName
== pQueryBtn
->GetName())
828 // Display the query dialog box
829 char qryWhere
[DB_MAX_WHERE_CLAUSE_LEN
+1];
830 strcpy(qryWhere
, (const char*) Contact
->qryWhereStr
);
831 char *tblName
[] = {(char *)CONTACT_TABLE_NAME
, 0};
832 new CqueryDlg(GetParent(), Contact
->pDb
, tblName
, qryWhere
);
834 // Query the first record in the new record set and
835 // display it, if the query string has changed.
836 if (strcmp(qryWhere
, (const char*) Contact
->qryWhereStr
))
838 Contact
->orderBy
= "NAME";
839 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
840 Contact
->whereStr
+= CONTACT_TABLE_NAME
;
842 // Append the query where string (if there is one)
843 Contact
->qryWhereStr
= qryWhere
;
844 if (strlen(qryWhere
))
846 Contact
->whereStr
+= " WHERE ";
847 Contact
->whereStr
+= Contact
->qryWhereStr
;
849 // Close the expression with a right paren
850 Contact
->whereStr
+= ")";
852 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
853 if (!Contact
->Query())
856 tStr
= "ODBC error during Query()\n\n";
857 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
858 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
861 // Display the first record from the query set
862 if (!Contact
->GetNext())
863 Contact
->Initialize();
867 // Enable/Disable the reset button
868 pResetBtn
->Enable(!Contact
->qryWhereStr
.IsEmpty());
874 if (widgetName
== pResetBtn
->GetName())
876 // Clear the additional where criteria established by the query feature
877 Contact
->qryWhereStr
= "";
879 // Query the first record in the table
880 Contact
->orderBy
= "NAME";
882 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
883 Contact
->whereStr
+= CONTACT_TABLE_NAME
;
884 Contact
->whereStr
+= ")";
886 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
887 if (!Contact
->Query())
890 tStr
= "ODBC error during Query()\n\n";
891 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
892 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
895 if (!Contact
->GetNext())
896 Contact
->Initialize();
898 pResetBtn
->Enable(FALSE
);
904 if (widgetName
== pNameListBtn
->GetName())
906 new ClookUpDlg(/* wxWindow *parent */ this,
907 /* char *windowTitle */ "Select contact name",
908 /* char *tableName */ (char *) CONTACT_TABLE_NAME
,
909 /* char *dispCol1 */ "NAME",
910 /* char *dispCol2 */ "JOIN_DATE",
911 /* char *where */ "",
912 /* char *orderBy */ "NAME",
913 /* bool distinctValues */ TRUE
);
915 if (ListDB_Selection
&& strlen(ListDB_Selection
))
917 wxString w
= "NAME = '";
918 w
+= ListDB_Selection
;
920 GetRec((char*) (const char*) w
);
926 } // CeditorDlg::OnCommand()
929 void CeditorDlg::FieldsEditable()
931 pNameTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
932 pAddress1Txt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
933 pAddress2Txt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
934 pCityTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
935 pStateTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
936 pPostalCodeTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
937 pCountryTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
939 pJoinDateTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
940 pContribTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
941 pLinesTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
942 pNativeLangChoice
->Enable((mode
== mCreate
) || (mode
== mEdit
));
943 pDeveloperRadio
->Enable((mode
== mCreate
) || (mode
== mEdit
));
945 } // CeditorDlg::FieldsEditable()
948 void CeditorDlg::SetMode(enum DialogModes m
)
969 pCreateBtn
->Enable( !edit
);
970 pEditBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
971 pDeleteBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
972 pCopyBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
973 pSaveBtn
->Enable( edit
);
974 pCancelBtn
->Enable( edit
);
975 pPrevBtn
->Enable( !edit
);
976 pNextBtn
->Enable( !edit
);
977 pQueryBtn
->Enable( !edit
);
978 pResetBtn
->Enable( !edit
&& !Contact
->qryWhereStr
.IsEmpty() );
979 pNameListBtn
->Enable( !edit
);
983 } // CeditorDlg::SetMode()
986 bool CeditorDlg::PutData()
990 pNameTxt
->SetValue(Contact
->Name
);
991 pAddress1Txt
->SetValue(Contact
->Addr1
);
992 pAddress2Txt
->SetValue(Contact
->Addr2
);
993 pCityTxt
->SetValue(Contact
->City
);
994 pStateTxt
->SetValue(Contact
->State
);
995 pCountryTxt
->SetValue(Contact
->Country
);
996 pPostalCodeTxt
->SetValue(Contact
->PostalCode
);
998 tStr
.Printf("%d/%d/%d",Contact
->JoinDate
.month
,Contact
->JoinDate
.day
,Contact
->JoinDate
.year
);
999 pJoinDateTxt
->SetValue(tStr
);
1001 tStr
.Printf("%d",Contact
->Contributions
);
1002 pContribTxt
->SetValue(tStr
);
1004 tStr
.Printf("%lu",Contact
->LinesOfCode
);
1005 pLinesTxt
->SetValue(tStr
);
1007 pNativeLangChoice
->SetSelection(Contact
->NativeLanguage
);
1009 pDeveloperRadio
->SetSelection(Contact
->IsDeveloper
);
1012 } // Ceditor::PutData()
1016 * Reads the data out of all the widgets on the dialog. Some data evaluation is done
1017 * to ensure that there is a name entered and that the date field is valid.
1019 * A return value of TRUE means that valid data was retrieved from the dialog, otherwise
1020 * invalid data was found (and a message was displayed telling the user what to fix), and
1021 * the data was not placed into the appropraite fields of Ccontact
1023 bool CeditorDlg::GetData()
1025 // Validate that the data currently entered into the widgets is valid data
1028 tStr
= pNameTxt
->GetValue();
1029 if (!strcmp((const char*) tStr
,""))
1031 wxMessageBox("A name is required for entry into the contact table","Notice...",wxOK
| wxICON_INFORMATION
);
1035 bool invalid
= FALSE
;
1039 tStr
= pJoinDateTxt
->GetValue();
1040 if (tStr
.Freq('/') != 2)
1043 // Find the month, day, and year tokens
1046 first
= tStr
.First('/');
1047 second
= tStr
.Last('/');
1049 mm
= atoi(tStr
.SubString(0,first
));
1050 dd
= atoi(tStr
.SubString(first
+1,second
));
1051 yyyy
= atoi(tStr
.SubString(second
+1,tStr
.Length()-1));
1053 invalid
= !(mm
&& dd
&& yyyy
);
1056 // Force Year 2000 compliance
1057 if (!invalid
&& (yyyy
< 1000))
1060 // Check the token ranges for validity
1065 else if ((mm
< 1) || (mm
> 12))
1073 int days
[12] = {31,28,31,30,31,30,
1075 if (dd
> days
[mm
-1])
1078 if ((dd
== 29) && (mm
== 2))
1080 if (((yyyy
% 4) == 0) && (((yyyy
% 100) != 0) || ((yyyy
% 400) == 0)))
1090 Contact
->JoinDate
.month
= mm
;
1091 Contact
->JoinDate
.day
= dd
;
1092 Contact
->JoinDate
.year
= yyyy
;
1096 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
);
1100 tStr
= pNameTxt
->GetValue();
1101 strcpy(Contact
->Name
,(const char*) tStr
);
1102 strcpy(Contact
->Addr1
,pAddress1Txt
->GetValue());
1103 strcpy(Contact
->Addr2
,pAddress2Txt
->GetValue());
1104 strcpy(Contact
->City
,pCityTxt
->GetValue());
1105 strcpy(Contact
->State
,pStateTxt
->GetValue());
1106 strcpy(Contact
->Country
,pCountryTxt
->GetValue());
1107 strcpy(Contact
->PostalCode
,pPostalCodeTxt
->GetValue());
1109 Contact
->Contributions
= atoi(pContribTxt
->GetValue());
1110 Contact
->LinesOfCode
= atol(pLinesTxt
->GetValue());
1112 Contact
->NativeLanguage
= (enum Language
) pNativeLangChoice
->GetSelection();
1113 Contact
->IsDeveloper
= (bool) pDeveloperRadio
->GetSelection();
1116 } // CeditorDlg::GetData()
1120 * Retrieve data from the dialog, verify the validity of the data, and if it is valid,
1121 * try to insert/update the data to the table based on the current 'mode' the dialog
1124 * A return value of TRUE means the insert/update was completed successfully, a return
1125 * value of FALSE means that Save() failed. If returning FALSE, then this function
1126 * has displayed a detailed error message for the user.
1128 bool CeditorDlg::Save()
1130 bool failed
= FALSE
;
1132 // Read the data in the widgets of the dialog to get the user's data
1136 // Perform any other required validations necessary before saving
1141 wxBeginBusyCursor();
1143 if (mode
== mCreate
)
1145 RETCODE result
= Contact
->Insert();
1147 failed
= (result
!= DB_SUCCESS
);
1150 // Some errors may be expected, like a duplicate key, so handle those instances with
1151 // specific error messages.
1152 if (result
== DB_ERR_INTEGRITY_CONSTRAINT_VIOL
)
1155 tStr
= "A duplicate key value already exists in the table.\nUnable to save record\n\n";
1156 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1157 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1161 // Some other unexpexted error occurred
1163 tStr
= "Database insert failed\n\n";
1164 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1165 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1169 else // mode == mEdit
1171 if (!Contact
->Update())
1174 tStr
= "Database update failed\n\n";
1175 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1176 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1183 Contact
->pDb
->CommitTrans();
1184 SetMode(mView
); // Sets the dialog mode back to viewing after save is successful
1187 Contact
->pDb
->RollbackTrans();
1193 } // CeditorDlg::Save()
1197 * Where this program is only showing a single row at a time in the dialog,
1198 * a special where clause must be built to find just the single row which,
1199 * in sequence, would follow the currently displayed row.
1201 bool CeditorDlg::GetNextRec()
1206 w
= "NAME = (SELECT MIN(NAME) FROM ";
1207 w
+= Contact
->tableName
;
1208 w
+= " WHERE NAME > '";
1212 // If a query where string is currently set, append that criteria
1213 if (!Contact
->qryWhereStr
.IsEmpty())
1216 w
+= Contact
->qryWhereStr
;
1221 return(GetRec((char*) (const char*) w
));
1223 } // CeditorDlg::GetNextRec()
1227 * Where this program is only showing a single row at a time in the dialog,
1228 * a special where clause must be built to find just the single row which,
1229 * in sequence, would precede the currently displayed row.
1231 bool CeditorDlg::GetPrevRec()
1235 w
= "NAME = (SELECT MAX(NAME) FROM ";
1236 w
+= Contact
->tableName
;
1237 w
+= " WHERE NAME < '";
1241 // If a query where string is currently set, append that criteria
1242 if (!Contact
->qryWhereStr
.IsEmpty())
1245 w
+= Contact
->qryWhereStr
;
1251 return(GetRec((char*) (const char*)w
));
1253 } // CeditorDlg::GetPrevRec()
1257 * This function is here to avoid duplicating this same code in both the
1258 * GetPrevRec() and GetNextRec() functions
1260 bool CeditorDlg::GetRec(char *whereStr
)
1262 Contact
->where
= whereStr
;
1263 Contact
->orderBy
= "NAME";
1265 if (!Contact
->Query())
1268 tStr
= "ODBC error during Query()\n\n";
1269 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1270 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1275 if (Contact
->GetNext())
1282 } // CeditorDlg::GetRec()
1287 * CparameterDlg constructor
1289 CparameterDlg::CparameterDlg(wxWindow
*parent
) : wxDialog (parent
, PARAMETER_DIALOG
, "ODBC parameter settings", wxPoint(-1, -1), wxSize(400, 275))
1291 // Since the ::OnCommand() function is overridden, this prevents the widget
1292 // detection in ::OnCommand() until all widgets have been initialized to prevent
1293 // uninitialized pointers from crashing the program
1294 widgetPtrsSet
= FALSE
;
1296 pParamODBCSourceMsg
= new wxStaticText(this, PARAMETER_DIALOG_SOURCE_MSG
, "ODBC data sources:", wxPoint(10, 10), wxSize(-1, -1), 0, "ParamODBCSourceMsg");
1297 pParamODBCSourceList
= new wxListBox(this, PARAMETER_DIALOG_SOURCE_LISTBOX
, wxPoint(10, 29), wxSize(285, 150), 0, 0, wxLB_SINGLE
|wxLB_ALWAYS_SB
, wxDefaultValidator
, "ParamODBCSourceList");
1299 pParamUserNameMsg
= new wxStaticText(this, PARAMETER_DIALOG_NAME_MSG
, "Database user name:", wxPoint(10, 193), wxSize(-1, -1), 0, "ParamUserNameMsg");
1300 pParamUserNameTxt
= new wxTextCtrl(this, PARAMETER_DIALOG_NAME_TEXT
, "", wxPoint(10, 209), wxSize(140, 25), 0, wxDefaultValidator
, "ParamUserNameTxt");
1302 pParamPasswordMsg
= new wxStaticText(this, PARAMETER_DIALOG_PASSWORD_MSG
, "Password:", wxPoint(156, 193), wxSize(-1, -1), 0, "ParamPasswordMsg");
1303 pParamPasswordTxt
= new wxTextCtrl(this, PARAMETER_DIALOG_PASSWORD_TEXT
, "", wxPoint(156, 209), wxSize(140, 25), 0, wxDefaultValidator
, "ParamPasswordTxt");
1305 pParamSaveBtn
= new wxButton(this, PARAMETER_DIALOG_SAVE
, "&Save", wxPoint(310, 21), wxSize(70, 35), 0, wxDefaultValidator
, "ParamSaveBtn");
1306 pParamCancelBtn
= new wxButton(this, PARAMETER_DIALOG_CANCEL
, "C&ancel", wxPoint(310, 66), wxSize(70, 35), 0, wxDefaultValidator
, "ParamCancelBtn");
1308 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
1309 // handle all widget processing
1310 widgetPtrsSet
= TRUE
;
1313 savedParamSettings
= wxGetApp().params
;
1318 } // CparameterDlg constructor
1321 bool CparameterDlg::OnClose()
1323 // Put any additional checking necessary to make certain it is alright
1324 // to close the program here that is not done elsewhere
1327 bool Ok
= (wxMessageBox("No changes have been saved.\n\nAre you sure you wish exit the parameter screen?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
1332 wxGetApp().params
= savedParamSettings
;
1335 if (GetParent() != NULL
)
1336 GetParent()->SetFocus();
1338 } // Cparameter::OnClose()
1341 void CparameterDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
1343 wxString widgetName
;
1345 widgetName
= win
.GetName();
1350 if (widgetName
== pParamSaveBtn
->GetName())
1355 tStr
= "Database parameters have been saved.";
1356 if (GetParent() != NULL
) // The parameter dialog was not called during startup due to a missing cfg file
1357 tStr
+= "\nNew parameters will take effect the next time the program is started.";
1358 wxMessageBox(tStr
,"Notice...",wxOK
| wxICON_INFORMATION
);
1365 if (widgetName
== pParamCancelBtn
->GetName())
1370 } // CparameterDlg::OnCommand()
1373 bool CparameterDlg::PutData()
1375 // Fill the data source list box
1376 FillDataSourceList();
1378 // Fill in the fields from the params object
1379 pParamODBCSourceList
->SetStringSelection(wxGetApp().params
.ODBCSource
);
1380 pParamUserNameTxt
->SetValue(wxGetApp().params
.UserName
);
1381 pParamPasswordTxt
->SetValue(wxGetApp().params
.Password
);
1383 } // CparameterDlg::PutData()
1386 bool CparameterDlg::GetData()
1389 if (pParamODBCSourceList
->GetStringSelection())
1391 tStr
= pParamODBCSourceList
->GetStringSelection();
1392 if (tStr
.Length() > (sizeof(wxGetApp().params
.ODBCSource
)-1))
1395 errmsg
.Printf("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());
1396 wxMessageBox(errmsg
,"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1399 strcpy(wxGetApp().params
.ODBCSource
, tStr
);
1404 tStr
= pParamUserNameTxt
->GetValue();
1405 if (tStr
.Length() > (sizeof(wxGetApp().params
.UserName
)-1))
1408 errmsg
.Printf("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());
1409 wxMessageBox(errmsg
,"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1412 strcpy(wxGetApp().params
.UserName
, tStr
);
1414 tStr
= pParamPasswordTxt
->GetValue();
1415 if (tStr
.Length() > (sizeof(wxGetApp().params
.Password
)-1))
1418 errmsg
.Printf("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());
1419 wxMessageBox(errmsg
,"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1422 strcpy(wxGetApp().params
.Password
,tStr
);
1424 } // CparameterDlg::GetData()
1427 bool CparameterDlg::Save()
1429 Cparameters saveParams
= wxGetApp().params
;
1432 wxGetApp().params
= saveParams
;
1437 if ((paramFile
= fopen(paramFilename
, "wt")) == NULL
)
1440 tStr
.Printf("Unable to write/overwrite '%s'.",paramFilename
);
1441 wxMessageBox(tStr
,"File I/O Error...",wxOK
| wxICON_EXCLAMATION
);
1445 fputs(wxGetApp().params
.ODBCSource
, paramFile
);
1446 fputc('\n', paramFile
);
1447 fputs(wxGetApp().params
.UserName
, paramFile
);
1448 fputc('\n', paramFile
);
1449 fputs(wxGetApp().params
.Password
, paramFile
);
1450 fputc('\n', paramFile
);
1454 } // CparameterDlg::Save()
1457 void CparameterDlg::FillDataSourceList()
1459 char Dsn
[SQL_MAX_DSN_LENGTH
+ 1];
1461 wxStringList strList
;
1463 while(GetDataSource(DbConnectInf
.Henv
, Dsn
, SQL_MAX_DSN_LENGTH
+1, DsDesc
, 255))
1468 char **p
= strList
.ListToArray();
1470 for (int i
= 0; strlen(p
[i
]); i
++)
1471 pParamODBCSourceList
->Append(p
[i
]);
1472 } // CparameterDlg::CparameterDlg::FillDataSourceList()
1475 BEGIN_EVENT_TABLE(CqueryDlg
, wxDialog
)
1476 EVT_BUTTON(-1, CqueryDlg::OnButton
)
1479 // CqueryDlg() constructor
1480 CqueryDlg::CqueryDlg(wxWindow
*parent
, wxDB
*pDb
, char *tblName
[], char *pWhereArg
) : wxDialog (parent
, QUERY_DIALOG
, "Query", wxPoint(-1, -1), wxSize(480, 360))
1482 wxBeginBusyCursor();
1486 masterTableName
= tblName
[0];
1487 widgetPtrsSet
= FALSE
;
1490 // Initialize the WHERE clause from the string passed in
1491 pWhere
= pWhereArg
; // Save a pointer to the output buffer
1492 if (strlen(pWhere
) > DB_MAX_WHERE_CLAUSE_LEN
) // Check the length of the buffer passed in
1495 s
.Printf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN
+1);
1496 wxMessageBox(s
,"Error...",wxOK
| wxICON_EXCLAMATION
);
1501 pQueryCol1Msg
= new wxStaticText(this, QUERY_DIALOG_COL_MSG
, "Column 1:", wxPoint(10, 10), wxSize(69, 16), 0, "QueryCol1Msg");
1502 pQueryCol1Choice
= new wxChoice(this, QUERY_DIALOG_COL_CHOICE
, wxPoint(10, 27), wxSize(250, 27), 0, 0, 0, wxDefaultValidator
, "QueryCol1Choice");
1504 pQueryNotMsg
= new wxStaticText(this, QUERY_DIALOG_NOT_MSG
, "NOT", wxPoint(268, 10), wxSize(-1, -1), 0, "QueryNotMsg");
1505 pQueryNotCheck
= new wxCheckBox(this, QUERY_DIALOG_NOT_CHECKBOX
, "", wxPoint(275, 37), wxSize(20, 20), 0, wxDefaultValidator
, "QueryNotCheck");
1507 wxString choice_strings
[9];
1508 choice_strings
[0] = "=";
1509 choice_strings
[1] = "<";
1510 choice_strings
[2] = ">";
1511 choice_strings
[3] = "<=";
1512 choice_strings
[4] = ">=";
1513 choice_strings
[5] = "Begins";
1514 choice_strings
[6] = "Contains";
1515 choice_strings
[7] = "Like";
1516 choice_strings
[8] = "Between";
1517 pQueryOperatorMsg
= new wxStaticText(this, QUERY_DIALOG_OP_MSG
, "Operator:", wxPoint(305, 10), wxSize(-1, -1), 0, "QueryOperatorMsg");
1518 pQueryOperatorChoice
= new wxChoice(this, QUERY_DIALOG_OP_CHOICE
, wxPoint(305, 27), wxSize(80, 27), 9, choice_strings
, 0, wxDefaultValidator
, "QueryOperatorChoice");
1520 pQueryCol2Msg
= new wxStaticText(this, QUERY_DIALOG_COL2_MSG
, "Column 2:", wxPoint(10, 65), wxSize(69, 16), 0, "QueryCol2Msg");
1521 pQueryCol2Choice
= new wxChoice(this, QUERY_DIALOG_COL2_CHOICE
, wxPoint(10, 82), wxSize(250, 27), 0, 0, 0, wxDefaultValidator
, "QueryCol2Choice");
1523 pQuerySqlWhereMsg
= new wxStaticText(this, QUERY_DIALOG_WHERE_MSG
, "SQL where clause:", wxPoint(10, 141), wxSize(-1, -1), 0, "QuerySqlWhereMsg");
1524 pQuerySqlWhereMtxt
= new wxTextCtrl(this, QUERY_DIALOG_WHERE_TEXT
, "", wxPoint(10, 159), wxSize(377, 134), wxTE_MULTILINE
, wxDefaultValidator
, "QuerySqlWhereMtxt");
1526 pQueryAddBtn
= new wxButton(this, QUERY_DIALOG_ADD
, "&Add", wxPoint(406, 24), wxSize(56, 26), 0, wxDefaultValidator
, "QueryAddBtn");
1527 pQueryAndBtn
= new wxButton(this, QUERY_DIALOG_AND
, "A&nd", wxPoint(406, 58), wxSize(56, 26), 0, wxDefaultValidator
, "QueryAndBtn");
1528 pQueryOrBtn
= new wxButton(this, QUERY_DIALOG_OR
, "&Or", wxPoint(406, 92), wxSize(56, 26), 0, wxDefaultValidator
, "QueryOrBtn");
1530 pQueryLParenBtn
= new wxButton(this, QUERY_DIALOG_LPAREN
, "(", wxPoint(406, 126), wxSize(26, 26), 0, wxDefaultValidator
, "QueryLParenBtn");
1531 pQueryRParenBtn
= new wxButton(this, QUERY_DIALOG_RPAREN
, ")", wxPoint(436, 126), wxSize(26, 26), 0, wxDefaultValidator
, "QueryRParenBtn");
1533 pQueryDoneBtn
= new wxButton(this, QUERY_DIALOG_DONE
, "&Done", wxPoint(406, 185), wxSize(56, 26), 0, wxDefaultValidator
, "QueryDoneBtn");
1534 pQueryClearBtn
= new wxButton(this, QUERY_DIALOG_CLEAR
, "C&lear", wxPoint(406, 218), wxSize(56, 26), 0, wxDefaultValidator
, "QueryClearBtn");
1535 pQueryCountBtn
= new wxButton(this, QUERY_DIALOG_COUNT
, "&Count", wxPoint(406, 252), wxSize(56, 26), 0, wxDefaultValidator
, "QueryCountBtn");
1537 pQueryValue1Msg
= new wxStaticText(this, QUERY_DIALOG_VALUE1_MSG
, "Value:", wxPoint(277, 66), wxSize(-1, -1), 0, "QueryValue1Msg");
1538 pQueryValue1Txt
= new wxTextCtrl(this, QUERY_DIALOG_VALUE1_TEXT
, "", wxPoint(277, 83), wxSize(108, 25), 0, wxDefaultValidator
, "QueryValue1Txt");
1540 pQueryValue2Msg
= new wxStaticText(this, QUERY_DIALOG_VALUE2_MSG
, "AND", wxPoint(238, 126), wxSize(-1, -1), 0, "QueryValue2Msg");
1541 pQueryValue2Txt
= new wxTextCtrl(this, QUERY_DIALOG_VALUE2_TEXT
, "", wxPoint(277, 120), wxSize(108, 25), 0, wxDefaultValidator
, "QueryValue2Txt");
1543 pQueryHintGrp
= new wxStaticBox(this, QUERY_DIALOG_HINT_GROUP
, "", wxPoint(10, 291), wxSize(377, 40), 0, "QueryHintGrp");
1544 pQueryHintMsg
= new wxStaticText(this, QUERY_DIALOG_HINT_MSG
, "", wxPoint(16, 306), wxSize(-1, -1), 0, "QueryHintMsg");
1546 widgetPtrsSet
= TRUE
;
1547 // Initialize the dialog
1549 pQueryCol2Choice
->Append("VALUE -->");
1550 colInf
= pDB
->GetColumns(tblName
);
1551 for (int i
= 0; colInf
[i
].colName
&& strlen(colInf
[i
].colName
); i
++)
1553 // If there is more than one table being queried, qualify
1554 // the column names with the table name prefix.
1555 if (tblName
[1] && strlen(tblName
[1]))
1557 qualName
.Printf("%s.%s", colInf
[i
].tableName
, colInf
[i
].colName
);
1558 pQueryCol1Choice
->Append(qualName
);
1559 pQueryCol2Choice
->Append(qualName
);
1561 else // Single table query, append just the column names
1563 pQueryCol1Choice
->Append(colInf
[i
].colName
);
1564 pQueryCol2Choice
->Append(colInf
[i
].colName
);
1568 pQueryCol1Choice
->SetSelection(0);
1569 pQueryCol2Choice
->SetSelection(0);
1570 pQueryOperatorChoice
->SetSelection(0);
1572 pQueryValue2Msg
->Show(FALSE
);
1573 pQueryValue2Txt
->Show(FALSE
);
1575 pQueryHintMsg
->SetLabel(langQRY_EQ
);
1577 pQuerySqlWhereMtxt
->SetValue(pWhere
);
1581 // Display the dialog window
1585 } // CqueryDlg() constructor
1588 void CqueryDlg::OnButton( wxCommandEvent
&event
)
1590 wxWindow
*win
= (wxWindow
*) event
.GetEventObject();
1591 OnCommand( *win
, event
);
1594 void CqueryDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
1596 // Widget pointers won't be set when the dialog is constructed.
1597 // Control is passed through this function once for each widget on
1598 // a dialog as the dialog is constructed.
1602 wxString widgetName
= win
.GetName();
1604 // Operator choice box
1605 if (widgetName
== pQueryOperatorChoice
->GetName())
1607 // Set the help text
1608 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1611 pQueryHintMsg
->SetLabel(langQRY_EQ
);
1614 pQueryHintMsg
->SetLabel(langQRY_LT
);
1617 pQueryHintMsg
->SetLabel(langQRY_GT
);
1620 pQueryHintMsg
->SetLabel(langQRY_LE
);
1623 pQueryHintMsg
->SetLabel(langQRY_GE
);
1626 pQueryHintMsg
->SetLabel(langQRY_BEGINS
);
1629 pQueryHintMsg
->SetLabel(langQRY_CONTAINS
);
1632 pQueryHintMsg
->SetLabel(langQRY_LIKE
);
1635 pQueryHintMsg
->SetLabel(langQRY_BETWEEN
);
1639 // Hide the value2 widget
1640 pQueryValue2Msg
->Show(FALSE
); // BETWEEN will show this widget
1641 pQueryValue2Txt
->Show(FALSE
); // BETWEEN will show this widget
1643 // Disable the NOT operator for <, <=, >, >=
1644 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1650 pQueryNotCheck
->SetValue(0);
1651 pQueryNotCheck
->Enable(FALSE
);
1654 pQueryNotCheck
->Enable(TRUE
);
1658 // Manipulate the dialog to handle the selected operator
1659 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1666 pQueryCol2Choice
->Enable(TRUE
);
1667 if (pQueryCol2Choice
->GetSelection()) // Column name is highlighted
1669 pQueryValue1Msg
->Show(FALSE
);
1670 pQueryValue1Txt
->Show(FALSE
);
1672 else // "Value" is highlighted
1674 pQueryValue1Msg
->Show(TRUE
);
1675 pQueryValue1Txt
->Show(TRUE
);
1676 pQueryValue1Txt
->SetFocus();
1682 pQueryCol2Choice
->SetSelection(0);
1683 pQueryCol2Choice
->Enable(FALSE
);
1684 pQueryValue1Msg
->Show(TRUE
);
1685 pQueryValue1Txt
->Show(TRUE
);
1686 pQueryValue1Txt
->SetFocus();
1689 pQueryCol2Choice
->SetSelection(0);
1690 pQueryCol2Choice
->Enable(FALSE
);
1691 pQueryValue2Msg
->Show(TRUE
);
1692 pQueryValue2Txt
->Show(TRUE
);
1693 pQueryValue1Msg
->Show(TRUE
);
1694 pQueryValue1Txt
->Show(TRUE
);
1695 pQueryValue1Txt
->SetFocus();
1701 } // Operator choice box
1704 if (widgetName
== pQueryCol2Choice
->GetName())
1706 if (pQueryCol2Choice
->GetSelection()) // Column name is highlighted
1708 pQueryValue1Msg
->Show(FALSE
);
1709 pQueryValue1Txt
->Show(FALSE
);
1711 else // "Value" is highlighted
1713 pQueryValue1Msg
->Show(TRUE
);
1714 pQueryValue1Txt
->Show(TRUE
);
1715 pQueryValue1Txt
->SetFocus();
1719 } // Column 2 choice
1722 if (widgetName
== pQueryAddBtn
->GetName())
1730 if (widgetName
== pQueryAndBtn
->GetName())
1732 AppendToWhere(" AND\n");
1738 if (widgetName
== pQueryOrBtn
->GetName())
1740 AppendToWhere(" OR\n");
1745 // Left Paren button
1746 if (widgetName
== pQueryLParenBtn
->GetName())
1751 } // Left Paren button
1753 // Right paren button
1754 if (widgetName
== pQueryRParenBtn
->GetName())
1759 } // Right Paren button
1762 if (widgetName
== pQueryDoneBtn
->GetName())
1764 // Be sure the where clause will not overflow the output buffer
1765 if (strlen(pQuerySqlWhereMtxt
->GetValue()) > DB_MAX_WHERE_CLAUSE_LEN
)
1768 s
.Printf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN
+1);
1769 wxMessageBox(s
,"Error...",wxOK
| wxICON_EXCLAMATION
);
1772 // Validate the where clause for things such as matching parens
1773 if (!ValidateWhereClause())
1775 // Copy the where clause to the output buffer and exit
1776 strcpy(pWhere
, pQuerySqlWhereMtxt
->GetValue());
1783 if (widgetName
== pQueryClearBtn
->GetName())
1785 bool Ok
= (wxMessageBox("Are you sure you wish to clear the Query?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
1788 pQuerySqlWhereMtxt
->SetValue("");
1794 if (widgetName
== pQueryCountBtn
->GetName())
1796 wxBeginBusyCursor();
1803 } // CqueryDlg::OnCommand
1806 bool CqueryDlg::OnClose()
1821 GetParent()->SetFocus();
1825 } // CqueryDlg::OnClose()
1828 bool CqueryDlg::SetWidgetPtrs()
1832 abort = abort || !(pQueryCol1Choice = (wxChoice *)GetWidgetPtr("QueryCol1Choice",this));
1833 abort = abort || !(pQueryNotCheck = (wxCheckBox *)GetWidgetPtr("QueryNotCheck",this));
1834 abort = abort || !(pQueryOperatorChoice = (wxChoice *)GetWidgetPtr("QueryOperatorChoice",this));
1835 abort = abort || !(pQueryCol2Choice = (wxChoice *)GetWidgetPtr("QueryCol2Choice",this));
1836 abort = abort || !(pQueryValue1Txt = (wxTextCtrl *)GetWidgetPtr("QueryValue1Txt",this));
1837 abort = abort || !(pQueryValue2Txt = (wxTextCtrl *)GetWidgetPtr("QueryValue2Txt",this));
1838 abort = abort || !(pQuerySqlWhereMtxt = (wxMultiText *)GetWidgetPtr("QuerySqlWhereMtxt",this));
1839 abort = abort || !(pQueryAddBtn = (wxButton *)GetWidgetPtr("QueryAddBtn",this));
1840 abort = abort || !(pQueryAndBtn = (wxButton *)GetWidgetPtr("QueryAndBtn",this));
1841 abort = abort || !(pQueryOrBtn = (wxButton *)GetWidgetPtr("QueryOrBtn",this));
1842 abort = abort || !(pQueryLParenBtn = (wxButton *)GetWidgetPtr("QueryLParenBtn",this));
1843 abort = abort || !(pQueryRParenBtn = (wxButton *)GetWidgetPtr("QueryRParenBtn",this));
1844 abort = abort || !(pQueryDoneBtn = (wxButton *)GetWidgetPtr("QueryDoneBtn",this));
1845 abort = abort || !(pQueryClearBtn = (wxButton *)GetWidgetPtr("QueryClearBtn",this));
1846 abort = abort || !(pQueryCountBtn = (wxButton *)GetWidgetPtr("QueryCountBtn",this));
1847 abort = abort || !(pQueryHelpBtn = (wxButton *)GetWidgetPtr("QueryHelpBtn",this));
1848 abort = abort || !(pQueryHintMsg = (wxStaticText *)GetWidgetPtr("QueryHintMsg",this));
1852 return(widgetPtrsSet = !abort);
1854 } // CqueryDlg::SetWidgetPtrs
1857 void CqueryDlg::AppendToWhere(char *s
)
1859 wxString whereStr
= pQuerySqlWhereMtxt
->GetValue();
1861 pQuerySqlWhereMtxt
->SetValue(whereStr
);
1863 } // CqueryDlg::AppendToWhere()
1866 void CqueryDlg::ProcessAddBtn()
1868 qryOp oper
= (qryOp
) pQueryOperatorChoice
->GetSelection();
1870 // Verify that eveything is filled in correctly
1871 if (pQueryCol2Choice
->GetSelection() == 0) // "Value" is selected
1873 // Verify that value 1 is filled in
1874 if (strlen(pQueryValue1Txt
->GetValue()) == 0)
1877 pQueryValue1Txt
->SetFocus();
1880 // For the BETWEEN operator, value 2 must be filled in as well
1881 if (oper
== qryOpBETWEEN
&&
1882 strlen(pQueryValue2Txt
->GetValue()) == 0)
1885 pQueryValue2Txt
->SetFocus();
1890 // Build the expression and append it to the where clause window
1891 wxString s
= pQueryCol1Choice
->GetStringSelection();
1893 if (pQueryNotCheck
->GetValue() && (oper
!= qryOpEQ
))
1899 if (pQueryNotCheck
->GetValue()) // NOT box is checked
1928 int col1Idx
= pQueryCol1Choice
->GetSelection();
1931 if (colInf
[col1Idx
].sqlDataType
== SQL_VARCHAR
||
1932 oper
== qryOpBEGINS
||
1933 oper
== qryOpCONTAINS
||
1937 if (pQueryCol2Choice
->GetSelection()) // Column name
1938 s
+= pQueryCol2Choice
->GetStringSelection();
1939 else // Column 2 is a "value"
1943 if (oper
== qryOpCONTAINS
)
1945 s
+= pQueryValue1Txt
->GetValue();
1946 if (oper
== qryOpCONTAINS
|| oper
== qryOpBEGINS
)
1952 if (oper
== qryOpBETWEEN
)
1957 s
+= pQueryValue2Txt
->GetValue();
1962 AppendToWhere((char*) (const char*) s
);
1964 } // CqueryDlg::ProcessAddBtn()
1967 void CqueryDlg::ProcessCountBtn()
1969 if (!ValidateWhereClause())
1972 if (dbTable
== 0) // wxTable object needs to be created and opened
1974 if (!(dbTable
= new wxTable(pDB
, masterTableName
, 0)))
1976 wxMessageBox("Memory allocation failed creating a wxTable object.","Error...",wxOK
| wxICON_EXCLAMATION
);
1979 if (!dbTable
->Open())
1982 tStr
= "ODBC error during Open()\n\n";
1983 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1984 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1989 // Count() with WHERE clause
1990 dbTable
->where
= (char*) (const char*) pQuerySqlWhereMtxt
->GetValue();
1991 ULONG whereCnt
= dbTable
->Count();
1993 // Count() of all records in the table
1995 ULONG totalCnt
= dbTable
->Count();
1997 if (whereCnt
> 0 || totalCnt
== 0)
2000 tStr
.Printf("%lu of %lu records match the query criteria.",whereCnt
,totalCnt
);
2001 wxMessageBox(tStr
,"Notice...",wxOK
| wxICON_INFORMATION
);
2006 tStr
.Printf("%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
);
2007 wxMessageBox(tStr
,"Notice...",wxOK
| wxICON_INFORMATION
);
2010 // After a wxMessageBox, the focus does not necessarily return to the
2011 // window which was the focus when the message box popped up, so return
2012 // focus to the Query dialog for certain
2015 } // CqueryDlg::ProcessCountBtn()
2018 bool CqueryDlg::ValidateWhereClause()
2020 wxString where
= pQuerySqlWhereMtxt
->GetValue();
2022 if (where
.Freq('(') != where
.Freq(')'))
2024 wxMessageBox("There are mismatched parenthesis in the constructed where clause","Error...",wxOK
| wxICON_EXCLAMATION
);
2027 // After a wxMessageBox, the focus does not necessarily return to the
2028 // window which was the focus when the message box popped up, so return
2029 // focus to the Query dialog for certain
2034 } // CqueryDlg::ValidateWhereClause()