]> git.saurik.com Git - wxWidgets.git/blame - samples/db/dbtest.cpp
More replacements of strcmp -> wxStr..
[wxWidgets.git] / samples / db / dbtest.cpp
CommitLineData
108106cf
JS
1///////////////////////////////////////////////////////////////////////////////
2// Name: dbtest.cpp
3// Purpose: wxWindows database demo app
4// Author: George Tasker
5// Modified by:
6// Created: 1998
7// RCS-ID: $Id$
8// Copyright: (c) 1998 Remstar International, Inc.
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12/*
13 * SYNOPSIS START
14
15 This sample program demonstrates the cross-platform ODBC database classes
16 donated by the development team at Remstar International.
17
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.
20
21
22
23 * SYNOPSIS END
24 */
25
26#ifdef __GNUG__
27#pragma implementation "dbtest.h"
28#endif
29
30#include "wx/wxprec.h"
31
32#ifdef __BORLANDC__
33#pragma hdrstop
34#endif //__BORLANDC__
35
36#ifndef WX_PRECOMP
37#include <wx/wx.h>
38#endif //WX_PRECOMP
39
7e616b10
RR
40#ifdef __WXGTK__
41#include "db.xpm"
42#endif
43
5b077d48 44#include <stdio.h> /* Included strictly for reading the text file with the database parameters */
108106cf 45
5b077d48
RR
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 */
108106cf 48
5b077d48 49extern DbList* WXDLLEXPORT PtrBegDbList; /* from db.cpp, used in getting back error results from db connections */
108106cf 50
5b077d48
RR
51#include "dbtest.h" /* Header file for this demonstration program */
52#include "listdb.h" /* Code to support the "Lookup" button on the editor dialog */
1fc5dd6f
JS
53
54IMPLEMENT_APP(DatabaseDemoApp)
55
5b077d48
RR
56extern char ListDB_Selection[]; /* Used to return the first column value for the selected line from the listDB routines */
57extern char ListDB_Selection2[]; /* Used to return the second column value for the selected line from the listDB routines */
108106cf 58
5b077d48 59DatabaseDemoFrame *DemoFrame; /* Pointer to the main frame */
108106cf 60
108106cf
JS
61/* Pointer to the main database connection used in the program. This
62 * pointer would normally be used for doing things as database lookups
63 * for user login names and passwords, getting workstation settings, etc.
64 * ---> IMPORTANT <---
65 *
66 * For each database object created which uses this wxDB pointer
67 * connection to the database, when a CommitTrans() or RollBackTrans()
68 * will commit or rollback EVERY object which uses this wxDB pointer.
69 *
70 * To allow each table object (those derived from wxTable) to be
71 * individually committed or rolled back, you MUST use a different
72 * instance of wxDB in the constructor of the table. Doing so creates
73 * more overhead, and will use more database connections (some DBs have
74 * connection limits...), so use connections sparringly.
75 *
76 * It is recommended that one "main" database connection be created for
77 * the entire program to use for READ-ONLY database accesses, but for each
78 * table object which will do a CommitTrans() or RollbackTrans() that a
79 * new wxDB object be created and used for it.
80 */
5b077d48 81
108106cf
JS
82wxDB *READONLY_DB;
83
108106cf
JS
84/*
85 * This function will return the exact string(s) from the database engine
86 * indicating all error conditions which have just occured during the
87 * last call to the database engine.
88 *
89 * This demo uses the returned string by displaying it in a wxMessageBox. The
1fc5dd6f 90 * formatting therefore is not the greatest, but this is just a demo, not a
108106cf
JS
91 * finished product. :-) gt
92 *
93 * NOTE: The value returned by this function is for temporary use only and
94 * should be copied for long term use
95 */
96char *GetExtendedDBErrorMsg(char *ErrFile, int ErrLine)
97{
98 static wxString msg;
99
100 wxString tStr;
101
102 if (ErrFile || ErrLine)
103 {
5b077d48 104 msg += "File: ";
108106cf
JS
105 msg += ErrFile;
106 msg += " Line: ";
1fc5dd6f 107 tStr.Printf("%d",ErrLine);
108106cf
JS
108 msg += tStr.GetData();
109 msg += "\n";
110 }
111
5b077d48 112 msg.Append ("\nODBC errors:\n");
108106cf 113 msg += "\n";
5b077d48
RR
114
115 /* Scan through each database connection displaying
116 * any ODBC errors that have occured. */
108106cf
JS
117 for (DbList *pDbList = PtrBegDbList; pDbList; pDbList = pDbList->PtrNext)
118 {
119 // Skip over any free connections
120 if (pDbList->Free)
121 continue;
122 // Display errors for this connection
123 for (int i = 0; i < DB_MAX_ERROR_HISTORY; i++)
124 {
125 if (pDbList->PtrDb->errorList[i])
126 {
127 msg.Append(pDbList->PtrDb->errorList[i]);
128 if (strcmp(pDbList->PtrDb->errorList[i],"") != 0)
129 msg.Append("\n");
130 }
131 }
132 }
133 msg += "\n";
134
1fc5dd6f 135 return (char*) (const char*) msg;
108106cf
JS
136} // GetExtendedDBErrorMsg
137
138
1fc5dd6f 139bool DatabaseDemoApp::OnInit()
108106cf
JS
140{
141 // Create the main frame window
1fc5dd6f 142 DemoFrame = new DatabaseDemoFrame(NULL, "wxWindows Database Demo", wxPoint(50, 50), wxSize(537, 480));
108106cf
JS
143
144 // Give it an icon
1fc5dd6f 145 DemoFrame->SetIcon(wxICON(db));
108106cf
JS
146
147 // Make a menubar
148 wxMenu *file_menu = new wxMenu;
149 file_menu->Append(FILE_CREATE, "&Create contact table");
150 file_menu->Append(FILE_EXIT, "E&xit");
151
152 wxMenu *edit_menu = new wxMenu;
153 edit_menu->Append(EDIT_PARAMETERS, "&Parameters...");
154
155 wxMenu *about_menu = new wxMenu;
156 about_menu->Append(ABOUT_DEMO, "&About");
157
158 wxMenuBar *menu_bar = new wxMenuBar;
159 menu_bar->Append(file_menu, "&File");
160 menu_bar->Append(edit_menu, "&Edit");
161 menu_bar->Append(about_menu, "&About");
162 DemoFrame->SetMenuBar(menu_bar);
163
164 // Initialize the ODBC Environment for Database Operations
165 if (SQLAllocEnv(&DbConnectInf.Henv) != SQL_SUCCESS)
166 {
167 wxMessageBox("A problem occured while trying to get a connection to the data source","DB CONNECTION ERROR",wxOK | wxICON_EXCLAMATION);
168 return NULL;
169 }
170
171 FILE *paramFile;
172 if ((paramFile = fopen(paramFilename, "r")) == NULL)
173 {
174 wxString tStr;
1fc5dd6f
JS
175 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);
176 wxMessageBox(tStr,"File I/O Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
177 DemoFrame->BuildParameterDialog(NULL);
178 if ((paramFile = fopen(paramFilename, "r")) == NULL)
179 return FALSE;
180 }
181
182 char buffer[1000+1];
183 fgets(buffer, sizeof(params.ODBCSource), paramFile);
184 buffer[strlen(buffer)-1] = '\0';
185 strcpy(params.ODBCSource,buffer);
186
187 fgets(buffer, sizeof(params.UserName), paramFile);
188 buffer[strlen(buffer)-1] = '\0';
189 strcpy(params.UserName,buffer);
190
191 fgets(buffer, sizeof(params.Password), paramFile);
192 buffer[strlen(buffer)-1] = '\0';
193 strcpy(params.Password,buffer);
194
65d7ddc4
GT
195 fgets(buffer, sizeof(params.DirPath), paramFile);
196 buffer[strlen(buffer)-1] = '\0';
197 strcpy(params.DirPath,buffer);
198
108106cf
JS
199 fclose(paramFile);
200
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
65d7ddc4
GT
205 strcpy(DbConnectInf.defaultDir,params.DirPath); // path where the table exists (needed for dBase)
206
108106cf
JS
207 READONLY_DB = GetDbConnection(&DbConnectInf);
208 if (READONLY_DB == 0)
209 {
210 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);
211 DemoFrame->BuildParameterDialog(NULL);
212 strcpy(DbConnectInf.Dsn, "");
213 strcpy(DbConnectInf.Uid, "");
214 strcpy(DbConnectInf.AuthStr, "");
215 wxMessageBox("Now exiting program.\n\nRestart program to try any new settings.","Notice...",wxOK | wxICON_INFORMATION);
216 return(FALSE);
217 }
218
219 DemoFrame->BuildEditorDialog();
220
221 // Show the frame
222 DemoFrame->Show(TRUE);
223
1fc5dd6f 224 return TRUE;
108106cf
JS
225} // DatabaseDemoApp::OnInit()
226
1fc5dd6f
JS
227BEGIN_EVENT_TABLE(DatabaseDemoFrame, wxFrame)
228 EVT_MENU(FILE_CREATE, DatabaseDemoFrame::OnCreate)
229 EVT_MENU(FILE_EXIT, DatabaseDemoFrame::OnExit)
230 EVT_MENU(EDIT_PARAMETERS, DatabaseDemoFrame::OnEditParameters)
231 EVT_MENU(ABOUT_DEMO, DatabaseDemoFrame::OnAbout)
232 EVT_CLOSE(DatabaseDemoFrame::OnCloseWindow)
233END_EVENT_TABLE()
108106cf
JS
234
235// DatabaseDemoFrame constructor
1fc5dd6f
JS
236DatabaseDemoFrame::DatabaseDemoFrame(wxFrame *frame, const wxString& title,
237 const wxPoint& pos, const wxSize& size):
238 wxFrame(frame, -1, title, pos, size)
108106cf
JS
239{
240// Put any code in necessary for initializing the main frame here
241}
242
1fc5dd6f
JS
243void DatabaseDemoFrame::OnCreate(wxCommandEvent& event)
244{
245 CreateDataTable();
246}
108106cf 247
1fc5dd6f 248void DatabaseDemoFrame::OnExit(wxCommandEvent& event)
108106cf 249{
1fc5dd6f
JS
250 this->Destroy();
251}
108106cf 252
1fc5dd6f
JS
253void DatabaseDemoFrame::OnEditParameters(wxCommandEvent& event)
254{
255 if ((pEditorDlg->mode != mCreate) && (pEditorDlg->mode != mEdit))
256 BuildParameterDialog(this);
257 else
258 wxMessageBox("Cannot change database parameters while creating or editing a record","Notice...",wxOK | wxICON_INFORMATION);
259}
260
261void DatabaseDemoFrame::OnAbout(wxCommandEvent& event)
262{
263 wxMessageBox("wxWindows sample program for database classes\n\nContributed on 27 July 1998","About...",wxOK | wxICON_INFORMATION);
264}
108106cf 265
1fc5dd6f 266void DatabaseDemoFrame::OnCloseWindow(wxCloseEvent& event)
108106cf
JS
267{
268 // Put any additional checking necessary to make certain it is alright
269 // to close the program here that is not done elsewhere
270
1fc5dd6f 271 this->Destroy();
e3065973 272} // DatabaseDemoFrame::OnCloseWindow()
108106cf
JS
273
274
275void DatabaseDemoFrame::CreateDataTable()
276{
1fc5dd6f 277 bool Ok = (wxMessageBox("Any data currently residing in the table will be erased.\n\nAre you sure?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
108106cf
JS
278
279 if (!Ok)
280 return;
281
282 wxBeginBusyCursor();
283
1fc5dd6f 284 bool success = TRUE;
108106cf
JS
285
286 Ccontact *Contact = new Ccontact();
287 if (!Contact)
288 {
289 wxEndBusyCursor();
290 wxMessageBox("Error allocating memory for 'Ccontact'object.\n\nTable was not created.","Error...",wxOK | wxICON_EXCLAMATION);
291 return;
292 }
293
65d7ddc4 294 if (!Contact->CreateTable(FALSE))
108106cf
JS
295 {
296 wxEndBusyCursor();
297 wxString tStr;
298 tStr = "Error creating CONTACTS table.\nTable was not created.\n\n";
299 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 300 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
301 success = FALSE;
302 }
303 else
304 {
305 if (!Contact->CreateIndexes())
306 {
307 wxEndBusyCursor();
308 wxString tStr;
309 tStr = "Error creating CONTACTS indexes.\nIndexes will be unavailable.\n\n";
310 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 311 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
312 success = FALSE;
313 }
314 }
65d7ddc4
GT
315 while (wxIsBusy())
316 wxEndBusyCursor();
108106cf
JS
317
318 delete Contact;
319
320 if (success)
321 wxMessageBox("Table and index(es) were successfully created.","Notice...",wxOK | wxICON_INFORMATION);
322} // DatabaseDemoFrame::CreateDataTable()
323
324
325void DatabaseDemoFrame::BuildEditorDialog()
326{
327 pEditorDlg = new CeditorDlg(this);
328 if (!pEditorDlg)
329 wxMessageBox("Unable to create the editor dialog for some reason","Error...",wxOK | wxICON_EXCLAMATION);
330} // DatabaseDemoFrame::BuildEditorDialog()
331
332
333void DatabaseDemoFrame::BuildParameterDialog(wxWindow *parent)
334{
335 pParamDlg = new CparameterDlg(parent);
336
337 if (!pParamDlg)
338 wxMessageBox("Unable to create the parameter dialog for some reason","Error...",wxOK | wxICON_EXCLAMATION);
339} // DatabaseDemoFrame::BuildParameterDialog()
340
341
342/*
343 * Constructor note: If no wxDB object is passed in, a new connection to the database
344 * is created for this instance of Ccontact. This can be a slow process depending
345 * on the database engine being used, and some database engines have a limit on the
346 * number of connections (either hard limits, or license restricted) so care should
347 * be used to use as few connections as is necessary.
348 * IMPORTANT: Objects which share a wxDB pointer are ALL acted upon whenever a member
349 * function of pDb is called (i.e. CommitTrans() or RollbackTrans(), so if modifying
350 * or creating a table objects which use the same pDb, know that all the objects
351 * will be committed or rolled back when any of the objects has this function call made.
352 */
65d7ddc4 353Ccontact::Ccontact (wxDB *pwxDB) : wxTable(pwxDB ? pwxDB : GetDbConnection(&DbConnectInf),CONTACT_TABLE_NAME,CONTACT_NO_COLS,NULL,!QUERY_ONLY,DbConnectInf.defaultDir)
108106cf
JS
354{
355 // This is used to represent whether the database connection should be released
356 // when this instance of the object is deleted. If using the same connection
357 // for multiple instance of database objects, then the connection should only be
358 // released when the last database instance using the connection is deleted
359 freeDbConn = !pwxDB;
360
361 SetupColumns();
362
363} // Ccontact Constructor
364
365
366void Ccontact::Initialize()
367{
368 Name[0] = 0;
369 Addr1[0] = 0;
370 Addr2[0] = 0;
371 City[0] = 0;
372 State[0] = 0;
373 PostalCode[0] = 0;
374 Country[0] = 0;
375 JoinDate.year = 1980;
376 JoinDate.month = 1;
377 JoinDate.day = 1;
378 JoinDate.hour = 0;
379 JoinDate.minute = 0;
380 JoinDate.second = 0;
381 JoinDate.fraction = 0;
382 NativeLanguage = langENGLISH;
383 IsDeveloper = FALSE;
384 Contributions = 0;
385 LinesOfCode = 0L;
386} // Ccontact::Initialize
387
388
389Ccontact::~Ccontact()
390{
391 if (freeDbConn)
392 {
393 if (!FreeDbConnection(pDb))
394 {
395 wxString tStr;
396 tStr = "Unable to Free the Ccontact data table handle\n\n";
397 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 398 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
399 }
400 }
401} // Ccontract destructor
402
403
404/*
405 * Handles setting up all the connections for the interface from the wxTable
406 * functions to interface to the data structure used to store records in
407 * memory, and for all the column definitions that define the table structure
408 */
409void Ccontact::SetupColumns()
410{
65d7ddc4
GT
411 // NOTE: Columns now are 8 character names, as that is all dBase can support. Longer
412 // names can be used for other database engines
413 SetColDefs ( 0,"NAME", DB_DATA_TYPE_VARCHAR, Name, SQL_C_CHAR, sizeof(Name), TRUE, TRUE); // Primary index
414 SetColDefs ( 1,"ADDRESS1", DB_DATA_TYPE_VARCHAR, Addr1, SQL_C_CHAR, sizeof(Addr1), FALSE,TRUE);
415 SetColDefs ( 2,"ADDRESS2", DB_DATA_TYPE_VARCHAR, Addr2, SQL_C_CHAR, sizeof(Addr2), FALSE,TRUE);
416 SetColDefs ( 3,"CITY", DB_DATA_TYPE_VARCHAR, City, SQL_C_CHAR, sizeof(City), FALSE,TRUE);
417 SetColDefs ( 4,"STATE", DB_DATA_TYPE_VARCHAR, State, SQL_C_CHAR, sizeof(State), FALSE,TRUE);
418 SetColDefs ( 5,"POSTCODE", DB_DATA_TYPE_VARCHAR, PostalCode, SQL_C_CHAR, sizeof(PostalCode), FALSE,TRUE);
419 SetColDefs ( 6,"COUNTRY", DB_DATA_TYPE_VARCHAR, Country, SQL_C_CHAR, sizeof(Country), FALSE,TRUE);
420 SetColDefs ( 7,"JOINDATE", DB_DATA_TYPE_DATE, &JoinDate, SQL_C_TIMESTAMP, sizeof(JoinDate), FALSE,TRUE);
421 SetColDefs ( 8,"IS_DEV", DB_DATA_TYPE_INTEGER, &IsDeveloper, SQL_C_BOOLEAN(IsDeveloper),sizeof(IsDeveloper), FALSE,TRUE);
422 SetColDefs ( 9,"CONTRIBS", DB_DATA_TYPE_INTEGER, &Contributions, SQL_C_USHORT, sizeof(Contributions), FALSE,TRUE);
423 SetColDefs (10,"LINE_CNT", DB_DATA_TYPE_INTEGER, &LinesOfCode, SQL_C_ULONG, sizeof(LinesOfCode), FALSE,TRUE);
424 SetColDefs (11,"LANGUAGE", DB_DATA_TYPE_INTEGER, &NativeLanguage, SQL_C_ENUM, sizeof(NativeLanguage), FALSE,TRUE);
108106cf
JS
425} // Ccontact::SetupColumns
426
427
1fc5dd6f 428bool Ccontact::CreateIndexes(void)
108106cf
JS
429{
430 // This index could easily be accomplished with an "orderBy" clause,
431 // but is done to show how to construct a non-primary index.
432 wxString indexName;
433 CidxDef idxDef[2];
434
1fc5dd6f 435 bool Ok = TRUE;
108106cf 436
8493118f 437 strcpy(idxDef[0].ColName, "IS_DEV");
108106cf
JS
438 idxDef[0].Ascending = TRUE;
439
440 strcpy(idxDef[1].ColName, "NAME");
441 idxDef[1].Ascending = TRUE;
442
443 indexName = CONTACT_TABLE_NAME;
444 indexName += "_IDX1";
1fc5dd6f 445 Ok = CreateIndex((char*) (const char*) indexName, TRUE, 2, idxDef);
108106cf
JS
446
447 return Ok;
448} // Ccontact::CreateIndexes()
449
450
451/*
452 * Having a function to do a query on the primary key (and possibly others) is
453 * very efficient and tighter coding so that it is available where ever the object
454 * is. Great for use with multiple tables when not using views or outer joins
455 */
1fc5dd6f 456bool Ccontact::FetchByName(char *name)
108106cf 457{
1fc5dd6f
JS
458 whereStr.Printf("NAME = '%s'",name);
459 where = (char*) (const char*) this->whereStr;
108106cf
JS
460 orderBy = 0;
461
462 if (!Query())
463 return(FALSE);
464
465 // Fetch the record
466 return(GetNext());
467
468} // Ccontact::FetchByName()
469
470
471/*
472 *
473 * ************* DIALOGS ***************
474 *
475 */
476
477
478/* CeditorDlg constructor
479 *
480 * Creates the dialog used for creating/editing/deleting/copying a Ccontact object.
481 * This dialog actually is drawn in the main frame of the program
482 *
483 * An instance of Ccontact is created - "Contact" - which is used to hold the Ccontact
484 * object that is currently being worked with.
485 */
f6fcbb63
RR
486
487BEGIN_EVENT_TABLE(CeditorDlg, wxPanel)
488 EVT_BUTTON(-1, CeditorDlg::OnButton)
489END_EVENT_TABLE()
490
108106cf
JS
491CeditorDlg::CeditorDlg(wxWindow *parent) : wxPanel (parent, 1, 1, 460, 455)
492{
493 // Since the ::OnCommand() function is overridden, this prevents the widget
494 // detection in ::OnCommand() until all widgets have been initialized to prevent
495 // uninitialized pointers from crashing the program
496 widgetPtrsSet = FALSE;
497
498 // Create the data structure and a new database connection.
65d7ddc4 499 // (As there is not a pDb being passed in the constructor, a new database
108106cf
JS
500 // connection is created)
501 Contact = new Ccontact();
502
503 if (!Contact)
504 {
505 wxMessageBox("Unable to instantiate an instance of Ccontact","Error...",wxOK | wxICON_EXCLAMATION);
506 return;
507 }
508
509 // Check if the table exists or not. If it doesn't, ask the user if they want to
510 // create the table. Continue trying to create the table until it exists, or user aborts
65d7ddc4 511 while (!Contact->pDb->TableExists((char *)CONTACT_TABLE_NAME,DbConnectInf.Uid,DbConnectInf.defaultDir))
108106cf
JS
512 {
513 wxString tStr;
1fc5dd6f 514 tStr.Printf("Unable to open the table '%s'.\n\nTable may need to be created...?\n\n",CONTACT_TABLE_NAME);
108106cf 515 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 516 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf 517
1fc5dd6f 518 bool createTable = (wxMessageBox("Do you wish to try to create/clear the CONTACTS table?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
108106cf
JS
519
520 if (!createTable)
521 {
522 delete Contact;
523 Close();
524 DemoFrame->Close();
525 return;
526 }
527 else
528 DemoFrame->CreateDataTable();
529 }
530
531 // Tables must be "opened" before anything other than creating/deleting table can be done
532 if (!Contact->Open())
533 {
534 // Table does exist, there was some problem opening it. Currently this should
535 // never fail, except in the case of the table not exisiting. Open() basically
536 // only sets up variable/pointer values, other than checking for table existence.
537 if (Contact->pDb->TableExists((char *)CONTACT_TABLE_NAME))
538 {
539 wxString tStr;
1fc5dd6f 540 tStr.Printf("Unable to open the table '%s'.\n\n",CONTACT_TABLE_NAME);
108106cf 541 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 542 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
543 delete Contact;
544 Close();
545 DemoFrame->Close();
546 return;
547 }
548 }
549
550 // Build the dialog
108106cf 551
5b077d48
RR
552 (void)new wxStaticBox(this, EDITOR_DIALOG_FN_GROUP, "", wxPoint(15, 1), wxSize(497, 69), 0, "FunctionGrp");
553 (void)new wxStaticBox(this, EDITOR_DIALOG_SEARCH_GROUP, "", wxPoint(417, 1), wxSize(95, 242), 0, "SearchGrp");
108106cf 554
1fc5dd6f
JS
555 pCreateBtn = new wxButton(this, EDITOR_DIALOG_CREATE, "&Create", wxPoint(25, 21), wxSize(70, 35), 0, wxDefaultValidator, "CreateBtn");
556 pEditBtn = new wxButton(this, EDITOR_DIALOG_EDIT, "&Edit", wxPoint(102, 21), wxSize(70, 35), 0, wxDefaultValidator, "EditBtn");
557 pDeleteBtn = new wxButton(this, EDITOR_DIALOG_DELETE, "&Delete", wxPoint(179, 21), wxSize(70, 35), 0, wxDefaultValidator, "DeleteBtn");
558 pCopyBtn = new wxButton(this, EDITOR_DIALOG_COPY, "Cop&y", wxPoint(256, 21), wxSize(70, 35), 0, wxDefaultValidator, "CopyBtn");
559 pSaveBtn = new wxButton(this, EDITOR_DIALOG_SAVE, "&Save", wxPoint(333, 21), wxSize(70, 35), 0, wxDefaultValidator, "SaveBtn");
560 pCancelBtn = new wxButton(this, EDITOR_DIALOG_CANCEL, "C&ancel", wxPoint(430, 21), wxSize(70, 35), 0, wxDefaultValidator, "CancelBtn");
108106cf 561
1fc5dd6f
JS
562 pPrevBtn = new wxButton(this, EDITOR_DIALOG_PREV, "<< &Prev", wxPoint(430, 81), wxSize(70, 35), 0, wxDefaultValidator, "PrevBtn");
563 pNextBtn = new wxButton(this, EDITOR_DIALOG_NEXT, "&Next >>", wxPoint(430, 121), wxSize(70, 35), 0, wxDefaultValidator, "NextBtn");
564 pQueryBtn = new wxButton(this, EDITOR_DIALOG_QUERY, "&Query", wxPoint(430, 161), wxSize(70, 35), 0, wxDefaultValidator, "QueryBtn");
565 pResetBtn = new wxButton(this, EDITOR_DIALOG_RESET, "&Reset", wxPoint(430, 200), wxSize(70, 35), 0, wxDefaultValidator, "ResetBtn");
108106cf 566
1fc5dd6f
JS
567 pNameMsg = new wxStaticText(this, EDITOR_DIALOG_NAME_MSG, "Name:", wxPoint(17, 80), wxSize(-1, -1), 0, "NameMsg");
568 pNameTxt = new wxTextCtrl(this, EDITOR_DIALOG_NAME_TEXT, "", wxPoint(17, 97), wxSize(308, 25), 0, wxDefaultValidator, "NameTxt");
569 pNameListBtn = new wxButton(this, EDITOR_DIALOG_LOOKUP, "&Lookup", wxPoint(333, 99), wxSize(70, 24), 0, wxDefaultValidator, "LookupBtn");
108106cf 570
1fc5dd6f
JS
571 pAddress1Msg = new wxStaticText(this, EDITOR_DIALOG_ADDRESS1_MSG, "Address:", wxPoint(17, 130), wxSize(-1, -1), 0, "Address1Msg");
572 pAddress1Txt = new wxTextCtrl(this, EDITOR_DIALOG_ADDRESS2_TEXT, "", wxPoint(17, 147), wxSize(308, 25), 0, wxDefaultValidator, "Address1Txt");
108106cf 573
1fc5dd6f
JS
574 pAddress2Msg = new wxStaticText(this, EDITOR_DIALOG_ADDRESS2_MSG, "Address:", wxPoint(17, 180), wxSize(-1, -1), 0, "Address2Msg");
575 pAddress2Txt = new wxTextCtrl(this, EDITOR_DIALOG_ADDRESS2_TEXT, "", wxPoint(17, 197), wxSize(308, 25), 0, wxDefaultValidator, "Address2Txt");
108106cf 576
1fc5dd6f
JS
577 pCityMsg = new wxStaticText(this, EDITOR_DIALOG_CITY_MSG, "City:", wxPoint(17, 230), wxSize(-1, -1), 0, "CityMsg");
578 pCityTxt = new wxTextCtrl(this, EDITOR_DIALOG_CITY_TEXT, "", wxPoint(17, 247), wxSize(225, 25), 0, wxDefaultValidator, "CityTxt");
108106cf 579
1fc5dd6f
JS
580 pStateMsg = new wxStaticText(this, EDITOR_DIALOG_STATE_MSG, "State:", wxPoint(250, 230), wxSize(-1, -1), 0, "StateMsg");
581 pStateTxt = new wxTextCtrl(this, EDITOR_DIALOG_STATE_TEXT, "", wxPoint(250, 247), wxSize(153, 25), 0, wxDefaultValidator, "StateTxt");
108106cf 582
1fc5dd6f
JS
583 pCountryMsg = new wxStaticText(this, EDITOR_DIALOG_COUNTRY_MSG, "Country:", wxPoint(17, 280), wxSize(-1, -1), 0, "CountryMsg");
584 pCountryTxt = new wxTextCtrl(this, EDITOR_DIALOG_COUNTRY_TEXT, "", wxPoint(17, 297), wxSize(225, 25), 0, wxDefaultValidator, "CountryTxt");
108106cf 585
1fc5dd6f
JS
586 pPostalCodeMsg = new wxStaticText(this, EDITOR_DIALOG_POSTAL_MSG, "Postal Code:", wxPoint(250, 280), wxSize(-1, -1), 0, "PostalCodeMsg");
587 pPostalCodeTxt = new wxTextCtrl(this, EDITOR_DIALOG_POSTAL_TEXT, "", wxPoint(250, 297), wxSize(153, 25), 0, wxDefaultValidator, "PostalCodeTxt");
108106cf 588
1fc5dd6f 589 wxString choice_strings[5];
108106cf
JS
590 choice_strings[0] = "English";
591 choice_strings[1] = "French";
592 choice_strings[2] = "German";
593 choice_strings[3] = "Spanish";
594 choice_strings[4] = "Other";
1fc5dd6f
JS
595 pNativeLangChoice = new wxChoice(this, EDITOR_DIALOG_LANG_CHOICE, wxPoint(17, 346), wxSize(277, -1), 5, choice_strings);
596 pNativeLangMsg = new wxStaticText(this, EDITOR_DIALOG_LANG_MSG, "Native language:", wxPoint(17, 330), wxSize(-1, -1), 0, "NativeLangMsg");
108106cf 597
1fc5dd6f 598 wxString radio_strings[2];
108106cf
JS
599 radio_strings[0] = "No";
600 radio_strings[1] = "Yes";
1fc5dd6f 601 pDeveloperRadio = new wxRadioBox(this,EDITOR_DIALOG_DEVELOPER,"Developer:",wxPoint(303,330),wxSize(-1,-1),2,radio_strings,2,wxHORIZONTAL);
108106cf 602
1fc5dd6f
JS
603 pJoinDateMsg = new wxStaticText(this, EDITOR_DIALOG_JOIN_MSG, "Date joined:", wxPoint(17, 380), wxSize(-1, -1), 0, "JoinDateMsg");
604 pJoinDateTxt = new wxTextCtrl(this, EDITOR_DIALOG_JOIN_TEXT, "", wxPoint(17, 397), wxSize(150, 25), 0, wxDefaultValidator, "JoinDateTxt");
108106cf 605
1fc5dd6f
JS
606 pContribMsg = new wxStaticText(this, EDITOR_DIALOG_CONTRIB_MSG, "Contributions:", wxPoint(175, 380), wxSize(-1, -1), 0, "ContribMsg");
607 pContribTxt = new wxTextCtrl(this, EDITOR_DIALOG_CONTRIB_TEXT, "", wxPoint(175, 397), wxSize(120, 25), 0, wxDefaultValidator, "ContribTxt");
108106cf 608
1fc5dd6f
JS
609 pLinesMsg = new wxStaticText(this, EDITOR_DIALOG_LINES_MSG, "Lines of code:", wxPoint(303, 380), wxSize(-1, -1), 0, "LinesMsg");
610 pLinesTxt = new wxTextCtrl(this, EDITOR_DIALOG_LINES_TEXT, "", wxPoint(303, 397), wxSize(100, 25), 0, wxDefaultValidator, "LinesTxt");
108106cf
JS
611
612 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
613 // handle all widget processing
614 widgetPtrsSet = TRUE;
615
616 // Setup the orderBy and where clauses to return back a single record as the result set,
617 // as there will only be one record being shown on the dialog at a time, this optimizes
618 // network traffic by only returning a one row result
619
620 Contact->orderBy = "NAME"; // field name to sort by
621
622 // The wxString "whereStr" is not a member of the wxTable object, it is a member variable
623 // specifically in the Ccontact class. It is used here for simpler construction of a varying
624 // length string, and then after the string is built, the wxTable member variable "where" is
625 // assigned the pointer to the constructed string.
626 //
627 // The constructed where clause below has a sub-query within it "SELECT MIN(NAME) FROM %s"
628 // to achieve a single row (in this case the first name in alphabetical order).
f6fcbb63 629
8fc613f1 630 // commented out because PostgreSQL can't do this
65d7ddc4
GT
631 if (Contact->pDb->Dbms() != dbmsPOSTGRES)
632 {
633 Contact->whereStr.sprintf("NAME = (SELECT MIN(NAME) FROM %s)",Contact->tableName);
634 // NOTE: (const char*) returns a pointer which may not be valid later, so this is short term use only
635 Contact->where = (char*) (const char*) Contact->whereStr;
636 }
637 else
638 Contact->where = 0;
108106cf
JS
639
640 // Perform the Query to get the result set.
641 // NOTE: If there are no rows returned, that is a valid result, so Query() would return TRUE.
642 // Only if there is a database error will Query() come back as FALSE
643 if (!Contact->Query())
644 {
645 wxString tStr;
646 tStr = "ODBC error during Query()\n\n";
647 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 648 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
649 GetParent()->Close();
650 return;
651 }
652
653 // Since Query succeeded, now get the row that was returned
654 if (!Contact->GetNext())
655 // If the GetNext() failed at this point, then there are no rows to retrieve,
656 // so clear the values in the members of "Contact" so that PutData() blanks the
657 // widgets on the dialog
658 Contact->Initialize();
659
660 SetMode(mView);
661 PutData();
662
663 Show(TRUE);
664} // CeditorDlg constructor
665
666
e3065973 667void CeditorDlg::OnCloseWindow(wxCloseEvent& event)
108106cf
JS
668{
669 // Clean up time
670 if ((mode != mCreate) && (mode != mEdit))
671 {
672 if (Contact)
673 delete Contact;
e3065973 674 this->Destroy();
108106cf
JS
675 }
676 else
677 {
678 wxMessageBox("Must finish processing the current record being created/modified before exiting","Notice...",wxOK | wxICON_INFORMATION);
e3065973 679 event.Veto();
108106cf 680 }
e3065973 681} // CeditorDlg::OnCloseWindow()
108106cf
JS
682
683
f6fcbb63
RR
684void CeditorDlg::OnButton( wxCommandEvent &event )
685{
686 wxWindow *win = (wxWindow*) event.GetEventObject();
687 OnCommand( *win, event );
688}
689
65d7ddc4 690
108106cf
JS
691void CeditorDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
692{
693 wxString widgetName;
694
695 widgetName = win.GetName();
696
697 if (!widgetPtrsSet)
698 return;
699
700 if (widgetName == pCreateBtn->GetName())
701 {
702 Contact->Initialize();
703 PutData();
704 SetMode( mCreate );
705 pNameTxt->SetValue("");
706 pNameTxt->SetFocus();
707 return;
708 }
709
710 if (widgetName == pEditBtn->GetName())
711 {
712 saveName = Contact->Name;
713 SetMode( mEdit );
714 pNameTxt->SetFocus();
715 return;
716 }
717
718 if (widgetName == pCopyBtn->GetName())
719 {
720 SetMode(mCreate);
721 pNameTxt->SetValue("");
722 pNameTxt->SetFocus();
723 return;
724 }
725
726 if (widgetName == pDeleteBtn->GetName())
727 {
1fc5dd6f 728 bool Ok = (wxMessageBox("Are you sure?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
108106cf
JS
729
730 if (!Ok)
731 return;
732
733 if (Ok && Contact->Delete())
734 {
735 // NOTE: Deletions are not finalized until a CommitTrans() is performed.
736 // If the commit were not performed, the program will continue to
737 // show the table contents as if they were deleted until this instance
738 // of Ccontact is deleted. If the Commit wasn't performed, the
739 // database will automatically Rollback the changes when the database
740 // connection is terminated
741 Contact->pDb->CommitTrans();
742
743 // Try to get the row that followed the just deleted row in the orderBy sequence
744 if (!GetNextRec())
745 {
746 // There was now row (in sequence) after the just deleted row, so get the
747 // row which preceded the just deleted row
748 if (!GetPrevRec())
749 {
750 // There are now no rows remaining, so clear the dialog widgets
751 Contact->Initialize();
752 PutData();
753 }
754 }
755 SetMode(mode); // force reset of button enable/disable
756 }
757 else
758 // Delete failed
759 Contact->pDb->RollbackTrans();
760
761 SetMode(mView);
762 return;
763 }
764
765 if (widgetName == pSaveBtn->GetName())
766 {
767 Save();
768 return;
769 }
770
771 if (widgetName == pCancelBtn->GetName())
772 {
1fc5dd6f 773 bool Ok = (wxMessageBox("Are you sure?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
108106cf
JS
774
775 if (!Ok)
776 return;
777
1fc5dd6f 778 if (!strcmp((const char*) saveName,""))
108106cf
JS
779 {
780 Contact->Initialize();
781 PutData();
782 SetMode(mView);
783 return;
784 }
785 else
786 {
787 // Requery previous record
1fc5dd6f 788 if (Contact->FetchByName((char*) (const char*) saveName))
108106cf
JS
789 {
790 PutData();
791 SetMode(mView);
792 return;
793 }
794 }
795
796 // Previous record not available, retrieve first record in table
65d7ddc4
GT
797 if (Contact->pDb->Dbms() != dbmsPOSTGRES)
798 {
799 // PostgreSQL can't do this
800 Contact->whereStr = "NAME = (SELECT MIN(NAME) FROM ";
801 Contact->whereStr += Contact->tableName;
802 Contact->whereStr += ")";
803 Contact->where = (char*) (const char*) Contact->whereStr;
804 }
805 else
806 Contact->where = 0;
807
108106cf
JS
808 if (!Contact->Query())
809 {
810 wxString tStr;
811 tStr = "ODBC error during Query()\n\n";
812 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 813 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
814 SetMode(mView);
815 return;
816 }
817 if (Contact->GetNext()) // Successfully read first record
818 {
819 PutData();
820 SetMode(mView);
821 return;
822 }
823 // No contacts are available, clear dialog
824 Contact->Initialize();
825 PutData();
826 SetMode(mView);
827 return;
828 } // Cancel Button
829
830 if (widgetName == pPrevBtn->GetName())
831 {
832 if (!GetPrevRec())
833 wxBell();
834 return;
835 } // Prev Button
836
837 if (widgetName == pNextBtn->GetName())
838 {
839 if (!GetNextRec())
840 wxBell();
841 return;
842 } // Next Button
843
844 if (widgetName == pQueryBtn->GetName())
845 {
846 // Display the query dialog box
847 char qryWhere[DB_MAX_WHERE_CLAUSE_LEN+1];
1fc5dd6f 848 strcpy(qryWhere, (const char*) Contact->qryWhereStr);
108106cf
JS
849 char *tblName[] = {(char *)CONTACT_TABLE_NAME, 0};
850 new CqueryDlg(GetParent(), Contact->pDb, tblName, qryWhere);
851
852 // Query the first record in the new record set and
853 // display it, if the query string has changed.
1fc5dd6f 854 if (strcmp(qryWhere, (const char*) Contact->qryWhereStr))
108106cf 855 {
8fc613f1
RR
856
857 // commented out because PostgreSQL can't do this
858 Contact->whereStr = "";
859 //Contact->orderBy = "NAME";
860 //Contact->whereStr = "NAME = (SELECT MIN(NAME) FROM ";
861 //Contact->whereStr += CONTACT_TABLE_NAME;
0d2a2b60 862
108106cf
JS
863 // Append the query where string (if there is one)
864 Contact->qryWhereStr = qryWhere;
865 if (strlen(qryWhere))
866 {
867 Contact->whereStr += " WHERE ";
868 Contact->whereStr += Contact->qryWhereStr;
869 }
870 // Close the expression with a right paren
8fc613f1 871 // Contact->whereStr += ")";
108106cf 872 // Requery the table
1fc5dd6f 873 Contact->where = (char*) (const char*) Contact->whereStr;
108106cf
JS
874 if (!Contact->Query())
875 {
876 wxString tStr;
877 tStr = "ODBC error during Query()\n\n";
878 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 879 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
880 return;
881 }
882 // Display the first record from the query set
883 if (!Contact->GetNext())
884 Contact->Initialize();
885 PutData();
886 }
887
888 // Enable/Disable the reset button
1fc5dd6f 889 pResetBtn->Enable(!Contact->qryWhereStr.IsEmpty());
108106cf
JS
890
891 return;
892 } // Query button
893
894
895 if (widgetName == pResetBtn->GetName())
896 {
897 // Clear the additional where criteria established by the query feature
898 Contact->qryWhereStr = "";
899
900 // Query the first record in the table
901 Contact->orderBy = "NAME";
0d2a2b60 902
8fc613f1
RR
903 // commented out because PostgreSQL can't do this
904 //Contact->whereStr = "NAME = (SELECT MIN(NAME) FROM ";
905 //Contact->whereStr += CONTACT_TABLE_NAME;
906 //Contact->whereStr += ")";
0d2a2b60 907
1fc5dd6f 908 Contact->where = (char*) (const char*) Contact->whereStr;
108106cf
JS
909 if (!Contact->Query())
910 {
911 wxString tStr;
912 tStr = "ODBC error during Query()\n\n";
913 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 914 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
915 return;
916 }
917 if (!Contact->GetNext())
918 Contact->Initialize();
919 PutData();
920 pResetBtn->Enable(FALSE);
921
922 return;
923 } // Reset button
924
925
926 if (widgetName == pNameListBtn->GetName())
927 {
928 new ClookUpDlg(/* wxWindow *parent */ this,
929 /* char *windowTitle */ "Select contact name",
930 /* char *tableName */ (char *) CONTACT_TABLE_NAME,
931 /* char *dispCol1 */ "NAME",
65d7ddc4 932 /* char *dispCol2 */ "JOINDATE",
108106cf
JS
933 /* char *where */ "",
934 /* char *orderBy */ "NAME",
1fc5dd6f 935 /* bool distinctValues */ TRUE);
108106cf
JS
936
937 if (ListDB_Selection && strlen(ListDB_Selection))
938 {
939 wxString w = "NAME = '";
940 w += ListDB_Selection;
941 w += "'";
1fc5dd6f 942 GetRec((char*) (const char*) w);
108106cf
JS
943 }
944
945 return;
946 }
947
948} // CeditorDlg::OnCommand()
949
950
951void CeditorDlg::FieldsEditable()
952{
953 pNameTxt->Enable((mode == mCreate) || (mode == mEdit));
954 pAddress1Txt->Enable((mode == mCreate) || (mode == mEdit));
955 pAddress2Txt->Enable((mode == mCreate) || (mode == mEdit));
956 pCityTxt->Enable((mode == mCreate) || (mode == mEdit));
957 pStateTxt->Enable((mode == mCreate) || (mode == mEdit));
958 pPostalCodeTxt->Enable((mode == mCreate) || (mode == mEdit));
959 pCountryTxt->Enable((mode == mCreate) || (mode == mEdit));
960
961 pJoinDateTxt->Enable((mode == mCreate) || (mode == mEdit));
962 pContribTxt->Enable((mode == mCreate) || (mode == mEdit));
963 pLinesTxt->Enable((mode == mCreate) || (mode == mEdit));
964 pNativeLangChoice->Enable((mode == mCreate) || (mode == mEdit));
965 pDeveloperRadio->Enable((mode == mCreate) || (mode == mEdit));
966
967} // CeditorDlg::FieldsEditable()
968
969
970void CeditorDlg::SetMode(enum DialogModes m)
971{
1fc5dd6f 972 bool edit = FALSE;
108106cf
JS
973
974 mode = m;
975 switch (mode)
976 {
977 case mCreate:
978 case mEdit:
979 edit = TRUE;
980 break;
981 case mView:
982 case mSearch:
983 edit = FALSE;
984 break;
985 default:
986 break;
987 };
988
989 if (widgetPtrsSet)
990 {
991 pCreateBtn->Enable( !edit );
992 pEditBtn->Enable( !edit && (strcmp(Contact->Name,"")!=0) );
993 pDeleteBtn->Enable( !edit && (strcmp(Contact->Name,"")!=0) );
994 pCopyBtn->Enable( !edit && (strcmp(Contact->Name,"")!=0) );
995 pSaveBtn->Enable( edit );
996 pCancelBtn->Enable( edit );
997 pPrevBtn->Enable( !edit );
998 pNextBtn->Enable( !edit );
999 pQueryBtn->Enable( !edit );
1fc5dd6f 1000 pResetBtn->Enable( !edit && !Contact->qryWhereStr.IsEmpty() );
108106cf
JS
1001 pNameListBtn->Enable( !edit );
1002 }
1003
1004 FieldsEditable();
1005} // CeditorDlg::SetMode()
1006
1007
1fc5dd6f 1008bool CeditorDlg::PutData()
108106cf
JS
1009{
1010 wxString tStr;
1011
1012 pNameTxt->SetValue(Contact->Name);
1013 pAddress1Txt->SetValue(Contact->Addr1);
1014 pAddress2Txt->SetValue(Contact->Addr2);
1015 pCityTxt->SetValue(Contact->City);
1016 pStateTxt->SetValue(Contact->State);
1017 pCountryTxt->SetValue(Contact->Country);
1018 pPostalCodeTxt->SetValue(Contact->PostalCode);
1019
1fc5dd6f
JS
1020 tStr.Printf("%d/%d/%d",Contact->JoinDate.month,Contact->JoinDate.day,Contact->JoinDate.year);
1021 pJoinDateTxt->SetValue(tStr);
108106cf 1022
1fc5dd6f
JS
1023 tStr.Printf("%d",Contact->Contributions);
1024 pContribTxt->SetValue(tStr);
108106cf 1025
1fc5dd6f
JS
1026 tStr.Printf("%lu",Contact->LinesOfCode);
1027 pLinesTxt->SetValue(tStr);
108106cf
JS
1028
1029 pNativeLangChoice->SetSelection(Contact->NativeLanguage);
1030
1031 pDeveloperRadio->SetSelection(Contact->IsDeveloper);
1032
1033 return TRUE;
1034} // Ceditor::PutData()
1035
1036
1037/*
1038 * Reads the data out of all the widgets on the dialog. Some data evaluation is done
1039 * to ensure that there is a name entered and that the date field is valid.
1040 *
1041 * A return value of TRUE means that valid data was retrieved from the dialog, otherwise
1042 * invalid data was found (and a message was displayed telling the user what to fix), and
1043 * the data was not placed into the appropraite fields of Ccontact
1044 */
1fc5dd6f 1045bool CeditorDlg::GetData()
108106cf
JS
1046{
1047 // Validate that the data currently entered into the widgets is valid data
1048
1049 wxString tStr;
1050 tStr = pNameTxt->GetValue();
1fc5dd6f 1051 if (!strcmp((const char*) tStr,""))
108106cf
JS
1052 {
1053 wxMessageBox("A name is required for entry into the contact table","Notice...",wxOK | wxICON_INFORMATION);
1054 return FALSE;
1055 }
1056
1fc5dd6f 1057 bool invalid = FALSE;
108106cf
JS
1058 int mm,dd,yyyy;
1059 int first, second;
1060
1061 tStr = pJoinDateTxt->GetValue();
1062 if (tStr.Freq('/') != 2)
1063 invalid = TRUE;
1064
1065 // Find the month, day, and year tokens
1066 if (!invalid)
1067 {
1068 first = tStr.First('/');
1069 second = tStr.Last('/');
1070
1071 mm = atoi(tStr.SubString(0,first));
1072 dd = atoi(tStr.SubString(first+1,second));
1073 yyyy = atoi(tStr.SubString(second+1,tStr.Length()-1));
1074
1075 invalid = !(mm && dd && yyyy);
1076 }
1077
1078 // Force Year 2000 compliance
1079 if (!invalid && (yyyy < 1000))
1080 invalid = TRUE;
1081
1082 // Check the token ranges for validity
1083 if (!invalid)
1084 {
1085 if (yyyy > 9999)
1086 invalid = TRUE;
1087 else if ((mm < 1) || (mm > 12))
1088 invalid = TRUE;
1089 else
1090 {
1091 if (dd < 1)
1092 invalid = TRUE;
1093 else
1094 {
1095 int days[12] = {31,28,31,30,31,30,
1096 31,31,30,31,30,31};
1097 if (dd > days[mm-1])
1098 {
1099 invalid = TRUE;
1100 if ((dd == 29) && (mm == 2))
1101 {
1102 if (((yyyy % 4) == 0) && (((yyyy % 100) != 0) || ((yyyy % 400) == 0)))
1103 invalid = FALSE;
1104 }
1105 }
1106 }
1107 }
1108 }
1109
1110 if (!invalid)
1111 {
1112 Contact->JoinDate.month = mm;
1113 Contact->JoinDate.day = dd;
1114 Contact->JoinDate.year = yyyy;
1115 }
1116 else
1117 {
1118 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);
1119 return FALSE;
1120 }
1121
1122 tStr = pNameTxt->GetValue();
1fc5dd6f 1123 strcpy(Contact->Name,(const char*) tStr);
108106cf
JS
1124 strcpy(Contact->Addr1,pAddress1Txt->GetValue());
1125 strcpy(Contact->Addr2,pAddress2Txt->GetValue());
1126 strcpy(Contact->City,pCityTxt->GetValue());
1127 strcpy(Contact->State,pStateTxt->GetValue());
1128 strcpy(Contact->Country,pCountryTxt->GetValue());
1129 strcpy(Contact->PostalCode,pPostalCodeTxt->GetValue());
1130
1131 Contact->Contributions = atoi(pContribTxt->GetValue());
1132 Contact->LinesOfCode = atol(pLinesTxt->GetValue());
1133
1134 Contact->NativeLanguage = (enum Language) pNativeLangChoice->GetSelection();
65d7ddc4 1135 Contact->IsDeveloper = pDeveloperRadio->GetSelection() > 0;
108106cf
JS
1136
1137 return TRUE;
1138} // CeditorDlg::GetData()
1139
1140
1141/*
1142 * Retrieve data from the dialog, verify the validity of the data, and if it is valid,
1143 * try to insert/update the data to the table based on the current 'mode' the dialog
1144 * is set to.
1145 *
1146 * A return value of TRUE means the insert/update was completed successfully, a return
1147 * value of FALSE means that Save() failed. If returning FALSE, then this function
1148 * has displayed a detailed error message for the user.
1149 */
1fc5dd6f 1150bool CeditorDlg::Save()
108106cf 1151{
1fc5dd6f 1152 bool failed = FALSE;
108106cf
JS
1153
1154 // Read the data in the widgets of the dialog to get the user's data
1155 if (!GetData())
1156 failed = TRUE;
1157
1158 // Perform any other required validations necessary before saving
108106cf
JS
1159 if (!failed)
1160 {
1161 wxBeginBusyCursor();
1162
1163 if (mode == mCreate)
1164 {
1165 RETCODE result = Contact->Insert();
1166
1167 failed = (result != DB_SUCCESS);
1168 if (failed)
1169 {
1170 // Some errors may be expected, like a duplicate key, so handle those instances with
1171 // specific error messages.
1172 if (result == DB_ERR_INTEGRITY_CONSTRAINT_VIOL)
1173 {
1174 wxString tStr;
1175 tStr = "A duplicate key value already exists in the table.\nUnable to save record\n\n";
1176 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 1177 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1178 }
1179 else
1180 {
1181 // Some other unexpexted error occurred
1182 wxString tStr;
1183 tStr = "Database insert failed\n\n";
1184 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 1185 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1186 }
1187 }
1188 }
1189 else // mode == mEdit
1190 {
1191 if (!Contact->Update())
1192 {
1193 wxString tStr;
1194 tStr = "Database update failed\n\n";
1195 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 1196 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1197 failed = TRUE;
1198 }
1199 }
1200
1201 if (!failed)
1202 {
1203 Contact->pDb->CommitTrans();
1204 SetMode(mView); // Sets the dialog mode back to viewing after save is successful
1205 }
1206 else
1207 Contact->pDb->RollbackTrans();
1208
1209 wxEndBusyCursor();
1210 }
1211
1212 return !failed;
1213} // CeditorDlg::Save()
1214
1215
1216/*
1217 * Where this program is only showing a single row at a time in the dialog,
1218 * a special where clause must be built to find just the single row which,
1219 * in sequence, would follow the currently displayed row.
1220 */
1fc5dd6f 1221bool CeditorDlg::GetNextRec()
108106cf
JS
1222{
1223 wxString w;
1224
65d7ddc4
GT
1225 if (Contact->pDb->Dbms() != dbmsPOSTGRES)
1226 {
1227 // PostgreSQL can't do this
1228 w = "NAME = (SELECT MIN(NAME) FROM ";
1229 w += Contact->tableName;
1230 w += " WHERE NAME > '";
1231 }
1232 else
1233 w = "(NAME > '";
1234
108106cf
JS
1235 w += Contact->Name;
1236 w += "'";
1237
1238 // If a query where string is currently set, append that criteria
1fc5dd6f 1239 if (!Contact->qryWhereStr.IsEmpty())
108106cf
JS
1240 {
1241 w += " AND (";
1242 w += Contact->qryWhereStr;
1243 w += ")";
1244 }
1245
1246 w += ")";
1fc5dd6f 1247 return(GetRec((char*) (const char*) w));
108106cf
JS
1248
1249} // CeditorDlg::GetNextRec()
1250
1251
1252/*
1253 * Where this program is only showing a single row at a time in the dialog,
1254 * a special where clause must be built to find just the single row which,
1255 * in sequence, would precede the currently displayed row.
1256 */
1fc5dd6f 1257bool CeditorDlg::GetPrevRec()
108106cf
JS
1258{
1259 wxString w;
1260
65d7ddc4
GT
1261 if (Contact->pDb->Dbms() != dbmsPOSTGRES)
1262 {
1263 // PostgreSQL can't do this
1264 w = "NAME = (SELECT MAX(NAME) FROM ";
1265 w += Contact->tableName;
1266 w += " WHERE NAME < '";
1267 }
1268 else
1269 w = "(NAME < '";
1270
108106cf
JS
1271 w += Contact->Name;
1272 w += "'";
1273
1274 // If a query where string is currently set, append that criteria
1fc5dd6f 1275 if (!Contact->qryWhereStr.IsEmpty())
108106cf
JS
1276 {
1277 w += " AND (";
1278 w += Contact->qryWhereStr;
1279 w += ")";
1280 }
1281
1282 w += ")";
1283
1fc5dd6f 1284 return(GetRec((char*) (const char*)w));
108106cf
JS
1285
1286} // CeditorDlg::GetPrevRec()
1287
1288
1289/*
1290 * This function is here to avoid duplicating this same code in both the
1291 * GetPrevRec() and GetNextRec() functions
1292 */
1fc5dd6f 1293bool CeditorDlg::GetRec(char *whereStr)
108106cf
JS
1294{
1295 Contact->where = whereStr;
1296 Contact->orderBy = "NAME";
65d7ddc4 1297
108106cf
JS
1298 if (!Contact->Query())
1299 {
1300 wxString tStr;
1301 tStr = "ODBC error during Query()\n\n";
1302 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 1303 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1304
1305 return(FALSE);
1306 }
1307
1308 if (Contact->GetNext())
1309 {
1310 PutData();
1311 return(TRUE);
1312 }
1313 else
1314 return(FALSE);
1315} // CeditorDlg::GetRec()
1316
1317
1318
1319/*
1320 * CparameterDlg constructor
1321 */
e3065973
JS
1322
1323BEGIN_EVENT_TABLE(CparameterDlg, wxDialog)
65d7ddc4
GT
1324 EVT_BUTTON(PARAMETER_DIALOG_SAVE, CparameterDlg::OnButton)
1325 EVT_BUTTON(PARAMETER_DIALOG_CANCEL, CparameterDlg::OnButton)
e3065973
JS
1326 EVT_CLOSE(CparameterDlg::OnCloseWindow)
1327END_EVENT_TABLE()
1328
65d7ddc4 1329CparameterDlg::CparameterDlg(wxWindow *parent) : wxDialog (parent, PARAMETER_DIALOG, "ODBC parameter settings", wxPoint(-1, -1), wxSize(400, 325))
108106cf
JS
1330{
1331 // Since the ::OnCommand() function is overridden, this prevents the widget
1332 // detection in ::OnCommand() until all widgets have been initialized to prevent
1333 // uninitialized pointers from crashing the program
1334 widgetPtrsSet = FALSE;
1335
1fc5dd6f
JS
1336 pParamODBCSourceMsg = new wxStaticText(this, PARAMETER_DIALOG_SOURCE_MSG, "ODBC data sources:", wxPoint(10, 10), wxSize(-1, -1), 0, "ParamODBCSourceMsg");
1337 pParamODBCSourceList = new wxListBox(this, PARAMETER_DIALOG_SOURCE_LISTBOX, wxPoint(10, 29), wxSize(285, 150), 0, 0, wxLB_SINGLE|wxLB_ALWAYS_SB, wxDefaultValidator, "ParamODBCSourceList");
108106cf 1338
1fc5dd6f
JS
1339 pParamUserNameMsg = new wxStaticText(this, PARAMETER_DIALOG_NAME_MSG, "Database user name:", wxPoint(10, 193), wxSize(-1, -1), 0, "ParamUserNameMsg");
1340 pParamUserNameTxt = new wxTextCtrl(this, PARAMETER_DIALOG_NAME_TEXT, "", wxPoint(10, 209), wxSize(140, 25), 0, wxDefaultValidator, "ParamUserNameTxt");
108106cf 1341
1fc5dd6f
JS
1342 pParamPasswordMsg = new wxStaticText(this, PARAMETER_DIALOG_PASSWORD_MSG, "Password:", wxPoint(156, 193), wxSize(-1, -1), 0, "ParamPasswordMsg");
1343 pParamPasswordTxt = new wxTextCtrl(this, PARAMETER_DIALOG_PASSWORD_TEXT, "", wxPoint(156, 209), wxSize(140, 25), 0, wxDefaultValidator, "ParamPasswordTxt");
108106cf 1344
65d7ddc4
GT
1345 pParamDirPathMsg = new wxStaticText(this, PARAMETER_DIALOG_DIRPATH_MSG, "Directory:", wxPoint(10, 243), wxSize(-1, -1), 0, "ParamDirPathMsg");
1346 pParamDirPathTxt = new wxTextCtrl(this, PARAMETER_DIALOG_DIRPATH_TEXT, "", wxPoint(10, 259), wxSize(140, 25), 0, wxDefaultValidator, "ParamDirPathTxt");
1347
1fc5dd6f
JS
1348 pParamSaveBtn = new wxButton(this, PARAMETER_DIALOG_SAVE, "&Save", wxPoint(310, 21), wxSize(70, 35), 0, wxDefaultValidator, "ParamSaveBtn");
1349 pParamCancelBtn = new wxButton(this, PARAMETER_DIALOG_CANCEL, "C&ancel", wxPoint(310, 66), wxSize(70, 35), 0, wxDefaultValidator, "ParamCancelBtn");
108106cf
JS
1350
1351 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
1352 // handle all widget processing
1353 widgetPtrsSet = TRUE;
1354
1355 saved = FALSE;
025e88c5 1356 savedParamSettings = wxGetApp().params;
108106cf
JS
1357
1358 Centre(wxBOTH);
1359 PutData();
1fc5dd6f 1360 ShowModal();
108106cf
JS
1361} // CparameterDlg constructor
1362
1363
e3065973 1364void CparameterDlg::OnCloseWindow(wxCloseEvent& event)
108106cf
JS
1365{
1366 // Put any additional checking necessary to make certain it is alright
1367 // to close the program here that is not done elsewhere
1368 if (!saved)
1369 {
1fc5dd6f 1370 bool Ok = (wxMessageBox("No changes have been saved.\n\nAre you sure you wish exit the parameter screen?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
108106cf
JS
1371
1372 if (!Ok)
e3065973
JS
1373 {
1374 event.Veto();
1375 return;
1376 }
108106cf 1377
025e88c5 1378 wxGetApp().params = savedParamSettings;
108106cf
JS
1379 }
1380
1381 if (GetParent() != NULL)
1382 GetParent()->SetFocus();
e3065973
JS
1383 this->Destroy();
1384
1385} // Cparameter::OnCloseWindow()
108106cf
JS
1386
1387
65d7ddc4
GT
1388void CparameterDlg::OnButton( wxCommandEvent &event )
1389{
1390 wxWindow *win = (wxWindow*) event.GetEventObject();
1391 OnCommand( *win, event );
1392}
1393
108106cf
JS
1394void CparameterDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
1395{
1396 wxString widgetName;
1397
1398 widgetName = win.GetName();
1399
1400 if (!widgetPtrsSet)
1401 return;
1402
1403 if (widgetName == pParamSaveBtn->GetName())
1404 {
1405 if (Save())
1406 {
1407 wxString tStr;
1408 tStr = "Database parameters have been saved.";
1409 if (GetParent() != NULL) // The parameter dialog was not called during startup due to a missing cfg file
1410 tStr += "\nNew parameters will take effect the next time the program is started.";
1fc5dd6f 1411 wxMessageBox(tStr,"Notice...",wxOK | wxICON_INFORMATION);
108106cf
JS
1412 saved = TRUE;
1413 Close();
1414 }
1415 return;
1416 }
1417
1418 if (widgetName == pParamCancelBtn->GetName())
1419 {
1420 Close();
1421 return;
1422 }
1423} // CparameterDlg::OnCommand()
1424
1425
1fc5dd6f 1426bool CparameterDlg::PutData()
108106cf
JS
1427{
1428 // Fill the data source list box
1429 FillDataSourceList();
1430
1431 // Fill in the fields from the params object
025e88c5
JS
1432 pParamODBCSourceList->SetStringSelection(wxGetApp().params.ODBCSource);
1433 pParamUserNameTxt->SetValue(wxGetApp().params.UserName);
1434 pParamPasswordTxt->SetValue(wxGetApp().params.Password);
65d7ddc4 1435 pParamDirPathTxt->SetValue(wxGetApp().params.DirPath);
108106cf
JS
1436 return TRUE;
1437} // CparameterDlg::PutData()
1438
1439
1fc5dd6f 1440bool CparameterDlg::GetData()
108106cf
JS
1441{
1442 wxString tStr;
1443 if (pParamODBCSourceList->GetStringSelection())
1444 {
1445 tStr = pParamODBCSourceList->GetStringSelection();
025e88c5 1446 if (tStr.Length() > (sizeof(wxGetApp().params.ODBCSource)-1))
108106cf
JS
1447 {
1448 wxString errmsg;
1fc5dd6f
JS
1449 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());
1450 wxMessageBox(errmsg,"Internal program error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1451 return FALSE;
1452 }
025e88c5 1453 strcpy(wxGetApp().params.ODBCSource, tStr);
108106cf
JS
1454 }
1455 else
1456 return FALSE;
1457
1458 tStr = pParamUserNameTxt->GetValue();
025e88c5 1459 if (tStr.Length() > (sizeof(wxGetApp().params.UserName)-1))
108106cf
JS
1460 {
1461 wxString errmsg;
1fc5dd6f
JS
1462 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());
1463 wxMessageBox(errmsg,"Internal program error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1464 return FALSE;
1465 }
025e88c5 1466 strcpy(wxGetApp().params.UserName, tStr);
108106cf
JS
1467
1468 tStr = pParamPasswordTxt->GetValue();
025e88c5 1469 if (tStr.Length() > (sizeof(wxGetApp().params.Password)-1))
108106cf
JS
1470 {
1471 wxString errmsg;
1fc5dd6f
JS
1472 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());
1473 wxMessageBox(errmsg,"Internal program error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1474 return FALSE;
1475 }
025e88c5 1476 strcpy(wxGetApp().params.Password,tStr);
65d7ddc4
GT
1477
1478 tStr = pParamDirPathTxt->GetValue();
1479 tStr.Replace("\\","/");
1480 if (tStr.Length() > (sizeof(wxGetApp().params.DirPath)-1))
1481 {
1482 wxString errmsg;
1483 errmsg.Printf("DirPath is longer than the data structure to hold it.\n'Cparameter.DirPath' 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());
1484 wxMessageBox(errmsg,"Internal program error...",wxOK | wxICON_EXCLAMATION);
1485 return FALSE;
1486 }
1487 strcpy(wxGetApp().params.DirPath,tStr);
108106cf
JS
1488 return TRUE;
1489} // CparameterDlg::GetData()
1490
1491
1fc5dd6f 1492bool CparameterDlg::Save()
108106cf 1493{
025e88c5 1494 Cparameters saveParams = wxGetApp().params;
108106cf
JS
1495 if (!GetData())
1496 {
025e88c5 1497 wxGetApp().params = saveParams;
108106cf
JS
1498 return FALSE;
1499 }
1500
1501 FILE *paramFile;
1502 if ((paramFile = fopen(paramFilename, "wt")) == NULL)
1503 {
1504 wxString tStr;
1fc5dd6f
JS
1505 tStr.Printf("Unable to write/overwrite '%s'.",paramFilename);
1506 wxMessageBox(tStr,"File I/O Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1507 return FALSE;
1508 }
1509
025e88c5 1510 fputs(wxGetApp().params.ODBCSource, paramFile);
108106cf 1511 fputc('\n', paramFile);
025e88c5 1512 fputs(wxGetApp().params.UserName, paramFile);
108106cf 1513 fputc('\n', paramFile);
025e88c5 1514 fputs(wxGetApp().params.Password, paramFile);
108106cf 1515 fputc('\n', paramFile);
65d7ddc4
GT
1516 fputs(wxGetApp().params.DirPath, paramFile);
1517 fputc('\n', paramFile);
108106cf
JS
1518 fclose(paramFile);
1519
1520 return TRUE;
1521} // CparameterDlg::Save()
1522
1523
1524void CparameterDlg::FillDataSourceList()
1525{
1526 char Dsn[SQL_MAX_DSN_LENGTH + 1];
1527 char DsDesc[255];
1528 wxStringList strList;
1529
1530 while(GetDataSource(DbConnectInf.Henv, Dsn, SQL_MAX_DSN_LENGTH+1, DsDesc, 255))
1531 strList.Add(Dsn);
1532
1533 strList.Sort();
1534 strList.Add("");
1535 char **p = strList.ListToArray();
1536
1537 for (int i = 0; strlen(p[i]); i++)
1538 pParamODBCSourceList->Append(p[i]);
1539} // CparameterDlg::CparameterDlg::FillDataSourceList()
1540
1541
f6fcbb63
RR
1542BEGIN_EVENT_TABLE(CqueryDlg, wxDialog)
1543 EVT_BUTTON(-1, CqueryDlg::OnButton)
e3065973 1544 EVT_CLOSE(CqueryDlg::OnCloseWindow)
f6fcbb63
RR
1545END_EVENT_TABLE()
1546
108106cf 1547// CqueryDlg() constructor
1fc5dd6f 1548CqueryDlg::CqueryDlg(wxWindow *parent, wxDB *pDb, char *tblName[], char *pWhereArg) : wxDialog (parent, QUERY_DIALOG, "Query", wxPoint(-1, -1), wxSize(480, 360))
108106cf
JS
1549{
1550 wxBeginBusyCursor();
1551
1552 colInf = 0;
1553 dbTable = 0;
1554 masterTableName = tblName[0];
1555 widgetPtrsSet = FALSE;
1556 pDB = pDb;
1557
1558 // Initialize the WHERE clause from the string passed in
1559 pWhere = pWhereArg; // Save a pointer to the output buffer
1560 if (strlen(pWhere) > DB_MAX_WHERE_CLAUSE_LEN) // Check the length of the buffer passed in
1561 {
1562 wxString s;
1fc5dd6f
JS
1563 s.Printf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN+1);
1564 wxMessageBox(s,"Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1565 Close();
1566 return;
1567 }
1568
1fc5dd6f
JS
1569 pQueryCol1Msg = new wxStaticText(this, QUERY_DIALOG_COL_MSG, "Column 1:", wxPoint(10, 10), wxSize(69, 16), 0, "QueryCol1Msg");
1570 pQueryCol1Choice = new wxChoice(this, QUERY_DIALOG_COL_CHOICE, wxPoint(10, 27), wxSize(250, 27), 0, 0, 0, wxDefaultValidator, "QueryCol1Choice");
108106cf 1571
1fc5dd6f
JS
1572 pQueryNotMsg = new wxStaticText(this, QUERY_DIALOG_NOT_MSG, "NOT", wxPoint(268, 10), wxSize(-1, -1), 0, "QueryNotMsg");
1573 pQueryNotCheck = new wxCheckBox(this, QUERY_DIALOG_NOT_CHECKBOX, "", wxPoint(275, 37), wxSize(20, 20), 0, wxDefaultValidator, "QueryNotCheck");
108106cf 1574
1fc5dd6f 1575 wxString choice_strings[9];
108106cf
JS
1576 choice_strings[0] = "=";
1577 choice_strings[1] = "<";
1578 choice_strings[2] = ">";
1579 choice_strings[3] = "<=";
1580 choice_strings[4] = ">=";
1581 choice_strings[5] = "Begins";
1582 choice_strings[6] = "Contains";
1583 choice_strings[7] = "Like";
1584 choice_strings[8] = "Between";
1fc5dd6f
JS
1585 pQueryOperatorMsg = new wxStaticText(this, QUERY_DIALOG_OP_MSG, "Operator:", wxPoint(305, 10), wxSize(-1, -1), 0, "QueryOperatorMsg");
1586 pQueryOperatorChoice = new wxChoice(this, QUERY_DIALOG_OP_CHOICE, wxPoint(305, 27), wxSize(80, 27), 9, choice_strings, 0, wxDefaultValidator, "QueryOperatorChoice");
108106cf 1587
1fc5dd6f
JS
1588 pQueryCol2Msg = new wxStaticText(this, QUERY_DIALOG_COL2_MSG, "Column 2:", wxPoint(10, 65), wxSize(69, 16), 0, "QueryCol2Msg");
1589 pQueryCol2Choice = new wxChoice(this, QUERY_DIALOG_COL2_CHOICE, wxPoint(10, 82), wxSize(250, 27), 0, 0, 0, wxDefaultValidator, "QueryCol2Choice");
108106cf 1590
1fc5dd6f
JS
1591 pQuerySqlWhereMsg = new wxStaticText(this, QUERY_DIALOG_WHERE_MSG, "SQL where clause:", wxPoint(10, 141), wxSize(-1, -1), 0, "QuerySqlWhereMsg");
1592 pQuerySqlWhereMtxt = new wxTextCtrl(this, QUERY_DIALOG_WHERE_TEXT, "", wxPoint(10, 159), wxSize(377, 134), wxTE_MULTILINE, wxDefaultValidator, "QuerySqlWhereMtxt");
108106cf 1593
1fc5dd6f
JS
1594 pQueryAddBtn = new wxButton(this, QUERY_DIALOG_ADD, "&Add", wxPoint(406, 24), wxSize(56, 26), 0, wxDefaultValidator, "QueryAddBtn");
1595 pQueryAndBtn = new wxButton(this, QUERY_DIALOG_AND, "A&nd", wxPoint(406, 58), wxSize(56, 26), 0, wxDefaultValidator, "QueryAndBtn");
1596 pQueryOrBtn = new wxButton(this, QUERY_DIALOG_OR, "&Or", wxPoint(406, 92), wxSize(56, 26), 0, wxDefaultValidator, "QueryOrBtn");
108106cf 1597
1fc5dd6f
JS
1598 pQueryLParenBtn = new wxButton(this, QUERY_DIALOG_LPAREN, "(", wxPoint(406, 126), wxSize(26, 26), 0, wxDefaultValidator, "QueryLParenBtn");
1599 pQueryRParenBtn = new wxButton(this, QUERY_DIALOG_RPAREN, ")", wxPoint(436, 126), wxSize(26, 26), 0, wxDefaultValidator, "QueryRParenBtn");
108106cf 1600
1fc5dd6f
JS
1601 pQueryDoneBtn = new wxButton(this, QUERY_DIALOG_DONE, "&Done", wxPoint(406, 185), wxSize(56, 26), 0, wxDefaultValidator, "QueryDoneBtn");
1602 pQueryClearBtn = new wxButton(this, QUERY_DIALOG_CLEAR, "C&lear", wxPoint(406, 218), wxSize(56, 26), 0, wxDefaultValidator, "QueryClearBtn");
1603 pQueryCountBtn = new wxButton(this, QUERY_DIALOG_COUNT, "&Count", wxPoint(406, 252), wxSize(56, 26), 0, wxDefaultValidator, "QueryCountBtn");
108106cf 1604
1fc5dd6f
JS
1605 pQueryValue1Msg = new wxStaticText(this, QUERY_DIALOG_VALUE1_MSG, "Value:", wxPoint(277, 66), wxSize(-1, -1), 0, "QueryValue1Msg");
1606 pQueryValue1Txt = new wxTextCtrl(this, QUERY_DIALOG_VALUE1_TEXT, "", wxPoint(277, 83), wxSize(108, 25), 0, wxDefaultValidator, "QueryValue1Txt");
108106cf 1607
1fc5dd6f
JS
1608 pQueryValue2Msg = new wxStaticText(this, QUERY_DIALOG_VALUE2_MSG, "AND", wxPoint(238, 126), wxSize(-1, -1), 0, "QueryValue2Msg");
1609 pQueryValue2Txt = new wxTextCtrl(this, QUERY_DIALOG_VALUE2_TEXT, "", wxPoint(277, 120), wxSize(108, 25), 0, wxDefaultValidator, "QueryValue2Txt");
1610
1611 pQueryHintGrp = new wxStaticBox(this, QUERY_DIALOG_HINT_GROUP, "", wxPoint(10, 291), wxSize(377, 40), 0, "QueryHintGrp");
1612 pQueryHintMsg = new wxStaticText(this, QUERY_DIALOG_HINT_MSG, "", wxPoint(16, 306), wxSize(-1, -1), 0, "QueryHintMsg");
108106cf
JS
1613
1614 widgetPtrsSet = TRUE;
1615 // Initialize the dialog
1616 wxString qualName;
1617 pQueryCol2Choice->Append("VALUE -->");
1618 colInf = pDB->GetColumns(tblName);
65d7ddc4
GT
1619
1620 if (!colInf)
1621 {
1622 wxEndBusyCursor();
1623 wxString tStr;
1624 tStr = "ODBC error during GetColumns()\n\n";
1625 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1626 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
1627 return;
1628 }
1629
108106cf
JS
1630 for (int i = 0; colInf[i].colName && strlen(colInf[i].colName); i++)
1631 {
1632 // If there is more than one table being queried, qualify
1633 // the column names with the table name prefix.
1634 if (tblName[1] && strlen(tblName[1]))
1635 {
1fc5dd6f
JS
1636 qualName.Printf("%s.%s", colInf[i].tableName, colInf[i].colName);
1637 pQueryCol1Choice->Append(qualName);
1638 pQueryCol2Choice->Append(qualName);
108106cf
JS
1639 }
1640 else // Single table query, append just the column names
1641 {
1642 pQueryCol1Choice->Append(colInf[i].colName);
1643 pQueryCol2Choice->Append(colInf[i].colName);
1644 }
1645 }
1646
1647 pQueryCol1Choice->SetSelection(0);
1648 pQueryCol2Choice->SetSelection(0);
1649 pQueryOperatorChoice->SetSelection(0);
1650
1651 pQueryValue2Msg->Show(FALSE);
1652 pQueryValue2Txt->Show(FALSE);
1653
1654 pQueryHintMsg->SetLabel(langQRY_EQ);
1655
1656 pQuerySqlWhereMtxt->SetValue(pWhere);
1657
1658 wxEndBusyCursor();
1659
1660 // Display the dialog window
108106cf 1661 Centre(wxBOTH);
1fc5dd6f 1662 ShowModal();
108106cf
JS
1663
1664} // CqueryDlg() constructor
1665
1666
f6fcbb63
RR
1667void CqueryDlg::OnButton( wxCommandEvent &event )
1668{
1669 wxWindow *win = (wxWindow*) event.GetEventObject();
1670 OnCommand( *win, event );
1671}
1672
108106cf
JS
1673void CqueryDlg::OnCommand(wxWindow& win, wxCommandEvent& event)
1674{
1675 // Widget pointers won't be set when the dialog is constructed.
1676 // Control is passed through this function once for each widget on
1677 // a dialog as the dialog is constructed.
1678 if (!widgetPtrsSet)
1679 return;
1680
1681 wxString widgetName = win.GetName();
1682
1683 // Operator choice box
1684 if (widgetName == pQueryOperatorChoice->GetName())
1685 {
1686 // Set the help text
1687 switch((qryOp) pQueryOperatorChoice->GetSelection())
1688 {
1689 case qryOpEQ:
1690 pQueryHintMsg->SetLabel(langQRY_EQ);
1691 break;
1692 case qryOpLT:
1693 pQueryHintMsg->SetLabel(langQRY_LT);
1694 break;
1695 case qryOpGT:
1696 pQueryHintMsg->SetLabel(langQRY_GT);
1697 break;
1698 case qryOpLE:
1699 pQueryHintMsg->SetLabel(langQRY_LE);
1700 break;
1701 case qryOpGE:
1702 pQueryHintMsg->SetLabel(langQRY_GE);
1703 break;
1704 case qryOpBEGINS:
1705 pQueryHintMsg->SetLabel(langQRY_BEGINS);
1706 break;
1707 case qryOpCONTAINS:
1708 pQueryHintMsg->SetLabel(langQRY_CONTAINS);
1709 break;
1710 case qryOpLIKE:
1711 pQueryHintMsg->SetLabel(langQRY_LIKE);
1712 break;
1713 case qryOpBETWEEN:
1714 pQueryHintMsg->SetLabel(langQRY_BETWEEN);
1715 break;
1716 }
1717
1718 // Hide the value2 widget
1719 pQueryValue2Msg->Show(FALSE); // BETWEEN will show this widget
1720 pQueryValue2Txt->Show(FALSE); // BETWEEN will show this widget
1721
1722 // Disable the NOT operator for <, <=, >, >=
1723 switch((qryOp) pQueryOperatorChoice->GetSelection())
1724 {
1725 case qryOpLT:
1726 case qryOpGT:
1727 case qryOpLE:
1728 case qryOpGE:
1729 pQueryNotCheck->SetValue(0);
1730 pQueryNotCheck->Enable(FALSE);
1731 break;
1732 default:
1733 pQueryNotCheck->Enable(TRUE);
1734 break;
1735 }
1736
1737 // Manipulate the dialog to handle the selected operator
1738 switch((qryOp) pQueryOperatorChoice->GetSelection())
1739 {
1740 case qryOpEQ:
1741 case qryOpLT:
1742 case qryOpGT:
1743 case qryOpLE:
1744 case qryOpGE:
1745 pQueryCol2Choice->Enable(TRUE);
1746 if (pQueryCol2Choice->GetSelection()) // Column name is highlighted
1747 {
1748 pQueryValue1Msg->Show(FALSE);
1749 pQueryValue1Txt->Show(FALSE);
1750 }
1751 else // "Value" is highlighted
1752 {
1753 pQueryValue1Msg->Show(TRUE);
1754 pQueryValue1Txt->Show(TRUE);
1755 pQueryValue1Txt->SetFocus();
1756 }
1757 break;
1758 case qryOpBEGINS:
1759 case qryOpCONTAINS:
1760 case qryOpLIKE:
1761 pQueryCol2Choice->SetSelection(0);
1762 pQueryCol2Choice->Enable(FALSE);
1763 pQueryValue1Msg->Show(TRUE);
1764 pQueryValue1Txt->Show(TRUE);
1765 pQueryValue1Txt->SetFocus();
1766 break;
1767 case qryOpBETWEEN:
1768 pQueryCol2Choice->SetSelection(0);
1769 pQueryCol2Choice->Enable(FALSE);
1770 pQueryValue2Msg->Show(TRUE);
1771 pQueryValue2Txt->Show(TRUE);
1772 pQueryValue1Msg->Show(TRUE);
1773 pQueryValue1Txt->Show(TRUE);
1774 pQueryValue1Txt->SetFocus();
1775 break;
1776 }
1777
1778 return;
1779
1780 } // Operator choice box
1781
1782 // Column 2 choice
1783 if (widgetName == pQueryCol2Choice->GetName())
1784 {
1785 if (pQueryCol2Choice->GetSelection()) // Column name is highlighted
1786 {
1787 pQueryValue1Msg->Show(FALSE);
1788 pQueryValue1Txt->Show(FALSE);
1789 }
1790 else // "Value" is highlighted
1791 {
1792 pQueryValue1Msg->Show(TRUE);
1793 pQueryValue1Txt->Show(TRUE);
1794 pQueryValue1Txt->SetFocus();
1795 }
1796 return;
1797
1798 } // Column 2 choice
1799
1800 // Add button
1801 if (widgetName == pQueryAddBtn->GetName())
1802 {
1803 ProcessAddBtn();
1804 return;
1805
1806 } // Add button
1807
1808 // And button
1809 if (widgetName == pQueryAndBtn->GetName())
1810 {
1811 AppendToWhere(" AND\n");
1812 return;
1813
1814 } // And button
1815
1816 // Or button
1817 if (widgetName == pQueryOrBtn->GetName())
1818 {
1819 AppendToWhere(" OR\n");
1820 return;
1821
1822 } // Or button
1823
1824 // Left Paren button
1825 if (widgetName == pQueryLParenBtn->GetName())
1826 {
1827 AppendToWhere("(");
1828 return;
1829
1830 } // Left Paren button
1831
1832 // Right paren button
1833 if (widgetName == pQueryRParenBtn->GetName())
1834 {
1835 AppendToWhere(")");
1836 return;
1837
1838 } // Right Paren button
1839
1840 // Done button
1841 if (widgetName == pQueryDoneBtn->GetName())
1842 {
1843 // Be sure the where clause will not overflow the output buffer
1844 if (strlen(pQuerySqlWhereMtxt->GetValue()) > DB_MAX_WHERE_CLAUSE_LEN)
1845 {
1846 wxString s;
1fc5dd6f
JS
1847 s.Printf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN+1);
1848 wxMessageBox(s,"Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
1849 return;
1850 }
1851 // Validate the where clause for things such as matching parens
1852 if (!ValidateWhereClause())
1853 return;
1854 // Copy the where clause to the output buffer and exit
1855 strcpy(pWhere, pQuerySqlWhereMtxt->GetValue());
1856 Close();
1857 return;
1858
1859 } // Done button
1860
1861 // Clear button
1862 if (widgetName == pQueryClearBtn->GetName())
1863 {
1fc5dd6f 1864 bool Ok = (wxMessageBox("Are you sure you wish to clear the Query?","Confirm",wxYES_NO|wxICON_QUESTION) == wxYES);
108106cf
JS
1865
1866 if (Ok)
1867 pQuerySqlWhereMtxt->SetValue("");
1868 return;
1869
1870 } // Clear button
1871
1872 // Count button
1873 if (widgetName == pQueryCountBtn->GetName())
1874 {
1875 wxBeginBusyCursor();
1876 ProcessCountBtn();
1877 wxEndBusyCursor();
1878 return;
1879
1880 } // Count button
1881
1882} // CqueryDlg::OnCommand
1883
1884
e3065973 1885void CqueryDlg::OnCloseWindow(wxCloseEvent& event)
108106cf
JS
1886{
1887 // Clean up
1888 if (colInf)
1889 {
1890 delete [] colInf;
1891 colInf = 0;
1892 }
1893
1894 if (dbTable)
1895 {
1896 delete dbTable;
1897 dbTable = 0;
1898 }
1899
1900 GetParent()->SetFocus();
65d7ddc4
GT
1901 while (wxIsBusy())
1902 wxEndBusyCursor();
108106cf 1903
e3065973
JS
1904 this->Destroy();
1905
1906} // CqueryDlg::OnCloseWindow()
108106cf
JS
1907
1908/*
1fc5dd6f 1909bool CqueryDlg::SetWidgetPtrs()
108106cf 1910{
1fc5dd6f 1911 bool abort = FALSE;
108106cf
JS
1912
1913 abort = abort || !(pQueryCol1Choice = (wxChoice *)GetWidgetPtr("QueryCol1Choice",this));
1914 abort = abort || !(pQueryNotCheck = (wxCheckBox *)GetWidgetPtr("QueryNotCheck",this));
1915 abort = abort || !(pQueryOperatorChoice = (wxChoice *)GetWidgetPtr("QueryOperatorChoice",this));
1916 abort = abort || !(pQueryCol2Choice = (wxChoice *)GetWidgetPtr("QueryCol2Choice",this));
1fc5dd6f
JS
1917 abort = abort || !(pQueryValue1Txt = (wxTextCtrl *)GetWidgetPtr("QueryValue1Txt",this));
1918 abort = abort || !(pQueryValue2Txt = (wxTextCtrl *)GetWidgetPtr("QueryValue2Txt",this));
108106cf
JS
1919 abort = abort || !(pQuerySqlWhereMtxt = (wxMultiText *)GetWidgetPtr("QuerySqlWhereMtxt",this));
1920 abort = abort || !(pQueryAddBtn = (wxButton *)GetWidgetPtr("QueryAddBtn",this));
1921 abort = abort || !(pQueryAndBtn = (wxButton *)GetWidgetPtr("QueryAndBtn",this));
1922 abort = abort || !(pQueryOrBtn = (wxButton *)GetWidgetPtr("QueryOrBtn",this));
1923 abort = abort || !(pQueryLParenBtn = (wxButton *)GetWidgetPtr("QueryLParenBtn",this));
1924 abort = abort || !(pQueryRParenBtn = (wxButton *)GetWidgetPtr("QueryRParenBtn",this));
1925 abort = abort || !(pQueryDoneBtn = (wxButton *)GetWidgetPtr("QueryDoneBtn",this));
1926 abort = abort || !(pQueryClearBtn = (wxButton *)GetWidgetPtr("QueryClearBtn",this));
1927 abort = abort || !(pQueryCountBtn = (wxButton *)GetWidgetPtr("QueryCountBtn",this));
1928 abort = abort || !(pQueryHelpBtn = (wxButton *)GetWidgetPtr("QueryHelpBtn",this));
1fc5dd6f 1929 abort = abort || !(pQueryHintMsg = (wxStaticText *)GetWidgetPtr("QueryHintMsg",this));
108106cf
JS
1930
1931 pFocusTxt = NULL;
1932
1933 return(widgetPtrsSet = !abort);
1934
1935} // CqueryDlg::SetWidgetPtrs
1936*/
1937
1938void CqueryDlg::AppendToWhere(char *s)
1939{
1940 wxString whereStr = pQuerySqlWhereMtxt->GetValue();
1941 whereStr += s;
1fc5dd6f 1942 pQuerySqlWhereMtxt->SetValue(whereStr);
108106cf
JS
1943
1944} // CqueryDlg::AppendToWhere()
1945
1946
1947void CqueryDlg::ProcessAddBtn()
1948{
1949 qryOp oper = (qryOp) pQueryOperatorChoice->GetSelection();
1950
1951 // Verify that eveything is filled in correctly
1952 if (pQueryCol2Choice->GetSelection() == 0) // "Value" is selected
1953 {
1954 // Verify that value 1 is filled in
1955 if (strlen(pQueryValue1Txt->GetValue()) == 0)
1956 {
1957 wxBell();
1958 pQueryValue1Txt->SetFocus();
1959 return;
1960 }
1961 // For the BETWEEN operator, value 2 must be filled in as well
1962 if (oper == qryOpBETWEEN &&
1963 strlen(pQueryValue2Txt->GetValue()) == 0)
1964 {
1965 wxBell();
1966 pQueryValue2Txt->SetFocus();
1967 return;
1968 }
1969 }
1970
1971 // Build the expression and append it to the where clause window
1972 wxString s = pQueryCol1Choice->GetStringSelection();
1973
1974 if (pQueryNotCheck->GetValue() && (oper != qryOpEQ))
1975 s += " NOT";
1976
1977 switch(oper)
1978 {
1979 case qryOpEQ:
1980 if (pQueryNotCheck->GetValue()) // NOT box is checked
1981 s += " <>";
1982 else
1983 s += " =";
1984 break;
1985 case qryOpLT:
1986 s += " <";
1987 break;
1988 case qryOpGT:
1989 s += " >";
1990 break;
1991 case qryOpLE:
1992 s += " <=";
1993 break;
1994 case qryOpGE:
1995 s += " >=";
1996 break;
1997 case qryOpBEGINS:
1998 case qryOpCONTAINS:
1999 case qryOpLIKE:
2000 s += " LIKE";
2001 break;
2002 case qryOpBETWEEN:
2003 s += " BETWEEN";
2004 break;
2005 }
2006
2007 s += " ";
2008
2009 int col1Idx = pQueryCol1Choice->GetSelection();
2010
1fc5dd6f
JS
2011 bool quote = FALSE;
2012 if (colInf[col1Idx].sqlDataType == SQL_VARCHAR ||
108106cf
JS
2013 oper == qryOpBEGINS ||
2014 oper == qryOpCONTAINS ||
2015 oper == qryOpLIKE)
2016 quote = TRUE;
2017
2018 if (pQueryCol2Choice->GetSelection()) // Column name
2019 s += pQueryCol2Choice->GetStringSelection();
2020 else // Column 2 is a "value"
2021 {
2022 if (quote)
2023 s += "'";
2024 if (oper == qryOpCONTAINS)
2025 s += "%";
2026 s += pQueryValue1Txt->GetValue();
2027 if (oper == qryOpCONTAINS || oper == qryOpBEGINS)
2028 s += "%";
2029 if (quote)
2030 s += "'";
2031 }
2032
2033 if (oper == qryOpBETWEEN)
2034 {
2035 s += " AND ";
2036 if (quote)
2037 s += "'";
2038 s += pQueryValue2Txt->GetValue();
2039 if (quote)
2040 s += "'";
2041 }
2042
1fc5dd6f 2043 AppendToWhere((char*) (const char*) s);
108106cf
JS
2044
2045} // CqueryDlg::ProcessAddBtn()
2046
2047
2048void CqueryDlg::ProcessCountBtn()
2049{
2050 if (!ValidateWhereClause())
2051 return;
2052
2053 if (dbTable == 0) // wxTable object needs to be created and opened
2054 {
65d7ddc4 2055 if (!(dbTable = new wxTable(pDB, masterTableName, 0, NULL, !QUERY_ONLY, DbConnectInf.defaultDir)))
108106cf
JS
2056 {
2057 wxMessageBox("Memory allocation failed creating a wxTable object.","Error...",wxOK | wxICON_EXCLAMATION);
2058 return;
2059 }
2060 if (!dbTable->Open())
2061 {
2062 wxString tStr;
2063 tStr = "ODBC error during Open()\n\n";
2064 tStr += GetExtendedDBErrorMsg(__FILE__,__LINE__);
1fc5dd6f 2065 wxMessageBox(tStr,"ODBC Error...",wxOK | wxICON_EXCLAMATION);
108106cf
JS
2066 return;
2067 }
2068 }
2069
2070 // Count() with WHERE clause
1fc5dd6f 2071 dbTable->where = (char*) (const char*) pQuerySqlWhereMtxt->GetValue();
108106cf
JS
2072 ULONG whereCnt = dbTable->Count();
2073
2074 // Count() of all records in the table
2075 dbTable->where = 0;
2076 ULONG totalCnt = dbTable->Count();
2077
2078 if (whereCnt > 0 || totalCnt == 0)
2079 {
2080 wxString tStr;
1fc5dd6f
JS
2081 tStr.Printf("%lu of %lu records match the query criteria.",whereCnt,totalCnt);
2082 wxMessageBox(tStr,"Notice...",wxOK | wxICON_INFORMATION);
108106cf
JS
2083 }
2084 else
2085 {
2086 wxString tStr;
1fc5dd6f
JS
2087 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);
2088 wxMessageBox(tStr,"Notice...",wxOK | wxICON_INFORMATION);
108106cf
JS
2089 }
2090
2091 // After a wxMessageBox, the focus does not necessarily return to the
2092 // window which was the focus when the message box popped up, so return
2093 // focus to the Query dialog for certain
2094 SetFocus();
2095
2096} // CqueryDlg::ProcessCountBtn()
2097
2098
1fc5dd6f 2099bool CqueryDlg::ValidateWhereClause()
108106cf
JS
2100{
2101 wxString where = pQuerySqlWhereMtxt->GetValue();
2102
2103 if (where.Freq('(') != where.Freq(')'))
2104 {
2105 wxMessageBox("There are mismatched parenthesis in the constructed where clause","Error...",wxOK | wxICON_EXCLAMATION);
2106 return(FALSE);
2107 }
2108 // After a wxMessageBox, the focus does not necessarily return to the
2109 // window which was the focus when the message box popped up, so return
2110 // focus to the Query dialog for certain
2111 SetFocus();
2112
2113 return(TRUE);
2114
2115} // CqueryDlg::ValidateWhereClause()