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