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