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