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