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