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