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