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