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