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