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