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