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