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