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