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