1 ///////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxWindows database demo app
4 // Author: George Tasker
8 // Copyright: (c) 1998 Remstar International, Inc.
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
15 This sample program demonstrates the cross-platform ODBC database classes
16 donated by the development team at Remstar International.
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.
27 #pragma implementation "dbtest.h"
30 #include "wx/wxprec.h"
40 #include <stdio.h> // Included strictly for reading the text file with the database parameters
42 #include <wx/db.h> // Required in the file which will get the data source connection
43 #include <wx/dbtable.h> // Has the wxTable object from which all data objects will inherit their data table functionality
45 extern DbList
*PtrBegDbList
; // from db.cpp, used in getting back error results from db connections
47 #include "dbtest.h" // Header file for this demonstration program
48 #include "listdb.h" // Code to support the "Lookup" button on the editor dialog
50 IMPLEMENT_APP(DatabaseDemoApp
)
52 extern char ListDB_Selection
[]; // Used to return the first column value for the selected line from the listDB routines
53 extern char ListDB_Selection2
[]; // Used to return the second column value for the selected line from the listDB routines
55 DatabaseDemoFrame
*DemoFrame
; // Pointer to the main frame
58 // This statement initializes the whole application and calls OnInit
59 DatabaseDemoApp DatabaseDemoApp
;
62 /* Pointer to the main database connection used in the program. This
63 * pointer would normally be used for doing things as database lookups
64 * for user login names and passwords, getting workstation settings, etc.
67 * For each database object created which uses this wxDB pointer
68 * connection to the database, when a CommitTrans() or RollBackTrans()
69 * will commit or rollback EVERY object which uses this wxDB pointer.
71 * To allow each table object (those derived from wxTable) to be
72 * individually committed or rolled back, you MUST use a different
73 * instance of wxDB in the constructor of the table. Doing so creates
74 * more overhead, and will use more database connections (some DBs have
75 * connection limits...), so use connections sparringly.
77 * It is recommended that one "main" database connection be created for
78 * the entire program to use for READ-ONLY database accesses, but for each
79 * table object which will do a CommitTrans() or RollbackTrans() that a
80 * new wxDB object be created and used for it.
86 * This function will return the exact string(s) from the database engine
87 * indicating all error conditions which have just occured during the
88 * last call to the database engine.
90 * This demo uses the returned string by displaying it in a wxMessageBox. The
91 * formatting therefore is not the greatest, but this is just a demo, not a
92 * finished product. :-) gt
94 * NOTE: The value returned by this function is for temporary use only and
95 * should be copied for long term use
97 char *GetExtendedDBErrorMsg(char *ErrFile
, int ErrLine
)
103 if (ErrFile
|| ErrLine
)
110 tStr
.Printf("%d",ErrLine
);
111 msg
+= tStr
.GetData();
116 msg
.Append ("\nODBC ERRORS\n");
119 // Scan through each database connection displaying
120 // any ODBC errors that have occured.
121 for (DbList
*pDbList
= PtrBegDbList
; pDbList
; pDbList
= pDbList
->PtrNext
)
123 // Skip over any free connections
126 // Display errors for this connection
127 for (int i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
129 if (pDbList
->PtrDb
->errorList
[i
])
131 msg
.Append(pDbList
->PtrDb
->errorList
[i
]);
132 if (strcmp(pDbList
->PtrDb
->errorList
[i
],"") != 0)
139 return (char*) (const char*) msg
;
140 } // GetExtendedDBErrorMsg
143 bool DatabaseDemoApp::OnInit()
145 // Create the main frame window
146 DemoFrame
= new DatabaseDemoFrame(NULL
, "wxWindows Database Demo", wxPoint(50, 50), wxSize(537, 480));
149 DemoFrame
->SetIcon(wxICON(db
));
152 wxMenu
*file_menu
= new wxMenu
;
153 file_menu
->Append(FILE_CREATE
, "&Create contact table");
154 file_menu
->Append(FILE_EXIT
, "E&xit");
156 wxMenu
*edit_menu
= new wxMenu
;
157 edit_menu
->Append(EDIT_PARAMETERS
, "&Parameters...");
159 wxMenu
*about_menu
= new wxMenu
;
160 about_menu
->Append(ABOUT_DEMO
, "&About");
162 wxMenuBar
*menu_bar
= new wxMenuBar
;
163 menu_bar
->Append(file_menu
, "&File");
164 menu_bar
->Append(edit_menu
, "&Edit");
165 menu_bar
->Append(about_menu
, "&About");
166 DemoFrame
->SetMenuBar(menu_bar
);
168 // Initialize the ODBC Environment for Database Operations
169 if (SQLAllocEnv(&DbConnectInf
.Henv
) != SQL_SUCCESS
)
171 wxMessageBox("A problem occured while trying to get a connection to the data source","DB CONNECTION ERROR",wxOK
| wxICON_EXCLAMATION
);
176 if ((paramFile
= fopen(paramFilename
, "r")) == NULL
)
179 tStr
.Printf("Unable to open the parameter file '%s' for reading.\n\nYou must specify the data source, user name, and\npassword that will be used and save those settings.",paramFilename
);
180 wxMessageBox(tStr
,"File I/O Error...",wxOK
| wxICON_EXCLAMATION
);
181 DemoFrame
->BuildParameterDialog(NULL
);
182 if ((paramFile
= fopen(paramFilename
, "r")) == NULL
)
187 fgets(buffer
, sizeof(params
.ODBCSource
), paramFile
);
188 buffer
[strlen(buffer
)-1] = '\0';
189 strcpy(params
.ODBCSource
,buffer
);
191 fgets(buffer
, sizeof(params
.UserName
), paramFile
);
192 buffer
[strlen(buffer
)-1] = '\0';
193 strcpy(params
.UserName
,buffer
);
195 fgets(buffer
, sizeof(params
.Password
), paramFile
);
196 buffer
[strlen(buffer
)-1] = '\0';
197 strcpy(params
.Password
,buffer
);
201 // Connect to datasource
202 strcpy(DbConnectInf
.Dsn
, params
.ODBCSource
); // ODBC data source name (created with ODBC Administrator under Win95/NT)
203 strcpy(DbConnectInf
.Uid
, params
.UserName
); // database username - must already exist in the data source
204 strcpy(DbConnectInf
.AuthStr
, params
.Password
); // password database username
205 READONLY_DB
= GetDbConnection(&DbConnectInf
);
206 if (READONLY_DB
== 0)
208 wxMessageBox("Unable to connect to the data source.\n\nCheck the name of your data source to verify it has been correctly entered/spelled.\n\nWith some databases, the user name and password must\nbe created with full rights to the CONTACT table prior to making a connection\n(using tools provided by the database manufacturer)", "DB CONNECTION ERROR...",wxOK
| wxICON_EXCLAMATION
);
209 DemoFrame
->BuildParameterDialog(NULL
);
210 strcpy(DbConnectInf
.Dsn
, "");
211 strcpy(DbConnectInf
.Uid
, "");
212 strcpy(DbConnectInf
.AuthStr
, "");
213 wxMessageBox("Now exiting program.\n\nRestart program to try any new settings.","Notice...",wxOK
| wxICON_INFORMATION
);
217 DemoFrame
->BuildEditorDialog();
220 DemoFrame
->Show(TRUE
);
223 } // DatabaseDemoApp::OnInit()
225 BEGIN_EVENT_TABLE(DatabaseDemoFrame
, wxFrame
)
226 EVT_MENU(FILE_CREATE
, DatabaseDemoFrame::OnCreate
)
227 EVT_MENU(FILE_EXIT
, DatabaseDemoFrame::OnExit
)
228 EVT_MENU(EDIT_PARAMETERS
, DatabaseDemoFrame::OnEditParameters
)
229 EVT_MENU(ABOUT_DEMO
, DatabaseDemoFrame::OnAbout
)
230 EVT_CLOSE(DatabaseDemoFrame::OnCloseWindow
)
233 // DatabaseDemoFrame constructor
234 DatabaseDemoFrame::DatabaseDemoFrame(wxFrame
*frame
, const wxString
& title
,
235 const wxPoint
& pos
, const wxSize
& size
):
236 wxFrame(frame
, -1, title
, pos
, size
)
238 // Put any code in necessary for initializing the main frame here
241 void DatabaseDemoFrame::OnCreate(wxCommandEvent
& event
)
246 void DatabaseDemoFrame::OnExit(wxCommandEvent
& event
)
251 void DatabaseDemoFrame::OnEditParameters(wxCommandEvent
& event
)
253 if ((pEditorDlg
->mode
!= mCreate
) && (pEditorDlg
->mode
!= mEdit
))
254 BuildParameterDialog(this);
256 wxMessageBox("Cannot change database parameters while creating or editing a record","Notice...",wxOK
| wxICON_INFORMATION
);
259 void DatabaseDemoFrame::OnAbout(wxCommandEvent
& event
)
261 wxMessageBox("wxWindows sample program for database classes\n\nContributed on 27 July 1998","About...",wxOK
| wxICON_INFORMATION
);
264 void DatabaseDemoFrame::OnCloseWindow(wxCloseEvent
& event
)
266 // Put any additional checking necessary to make certain it is alright
267 // to close the program here that is not done elsewhere
270 } // DatabaseDemoFrame::OnClose()
273 void DatabaseDemoFrame::CreateDataTable()
275 bool Ok
= (wxMessageBox("Any data currently residing in the table will be erased.\n\nAre you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
284 Ccontact
*Contact
= new Ccontact();
288 wxMessageBox("Error allocating memory for 'Ccontact'object.\n\nTable was not created.","Error...",wxOK
| wxICON_EXCLAMATION
);
292 if (!Contact
->CreateTable())
296 tStr
= "Error creating CONTACTS table.\nTable was not created.\n\n";
297 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
298 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
303 if (!Contact
->CreateIndexes())
307 tStr
= "Error creating CONTACTS indexes.\nIndexes will be unavailable.\n\n";
308 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
309 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
318 wxMessageBox("Table and index(es) were successfully created.","Notice...",wxOK
| wxICON_INFORMATION
);
319 } // DatabaseDemoFrame::CreateDataTable()
322 void DatabaseDemoFrame::BuildEditorDialog()
324 pEditorDlg
= new CeditorDlg(this);
326 wxMessageBox("Unable to create the editor dialog for some reason","Error...",wxOK
| wxICON_EXCLAMATION
);
327 } // DatabaseDemoFrame::BuildEditorDialog()
330 void DatabaseDemoFrame::BuildParameterDialog(wxWindow
*parent
)
332 pParamDlg
= new CparameterDlg(parent
);
335 wxMessageBox("Unable to create the parameter dialog for some reason","Error...",wxOK
| wxICON_EXCLAMATION
);
336 } // DatabaseDemoFrame::BuildParameterDialog()
340 * Constructor note: If no wxDB object is passed in, a new connection to the database
341 * is created for this instance of Ccontact. This can be a slow process depending
342 * on the database engine being used, and some database engines have a limit on the
343 * number of connections (either hard limits, or license restricted) so care should
344 * be used to use as few connections as is necessary.
345 * IMPORTANT: Objects which share a wxDB pointer are ALL acted upon whenever a member
346 * function of pDb is called (i.e. CommitTrans() or RollbackTrans(), so if modifying
347 * or creating a table objects which use the same pDb, know that all the objects
348 * will be committed or rolled back when any of the objects has this function call made.
350 Ccontact::Ccontact (wxDB
*pwxDB
) : wxTable(pwxDB
? pwxDB
: GetDbConnection(&DbConnectInf
),CONTACT_TABLE_NAME
,CONTACT_NO_COLS
)
352 // This is used to represent whether the database connection should be released
353 // when this instance of the object is deleted. If using the same connection
354 // for multiple instance of database objects, then the connection should only be
355 // released when the last database instance using the connection is deleted
360 } // Ccontact Constructor
363 void Ccontact::Initialize()
372 JoinDate
.year
= 1980;
378 JoinDate
.fraction
= 0;
379 NativeLanguage
= langENGLISH
;
383 } // Ccontact::Initialize
386 Ccontact::~Ccontact()
390 if (!FreeDbConnection(pDb
))
393 tStr
= "Unable to Free the Ccontact data table handle\n\n";
394 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
395 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
398 } // Ccontract destructor
402 * Handles setting up all the connections for the interface from the wxTable
403 * functions to interface to the data structure used to store records in
404 * memory, and for all the column definitions that define the table structure
406 void Ccontact::SetupColumns()
408 SetColDefs ( 0,"NAME", DB_DATA_TYPE_VARCHAR
, Name
, SQL_C_CHAR
, sizeof(Name
), TRUE
, TRUE
); // Primary index
409 SetColDefs ( 1,"ADDRESS1", DB_DATA_TYPE_VARCHAR
, Addr1
, SQL_C_CHAR
, sizeof(Addr1
), FALSE
,TRUE
);
410 SetColDefs ( 2,"ADDRESS2", DB_DATA_TYPE_VARCHAR
, Addr2
, SQL_C_CHAR
, sizeof(Addr2
), FALSE
,TRUE
);
411 SetColDefs ( 3,"CITY", DB_DATA_TYPE_VARCHAR
, City
, SQL_C_CHAR
, sizeof(City
), FALSE
,TRUE
);
412 SetColDefs ( 4,"STATE", DB_DATA_TYPE_VARCHAR
, State
, SQL_C_CHAR
, sizeof(State
), FALSE
,TRUE
);
413 SetColDefs ( 5,"POSTAL_CODE", DB_DATA_TYPE_VARCHAR
, PostalCode
, SQL_C_CHAR
, sizeof(PostalCode
), FALSE
,TRUE
);
414 SetColDefs ( 6,"COUNTRY", DB_DATA_TYPE_VARCHAR
, Country
, SQL_C_CHAR
, sizeof(Country
), FALSE
,TRUE
);
415 SetColDefs ( 7,"JOIN_DATE", DB_DATA_TYPE_DATE
, &JoinDate
, SQL_C_TIMESTAMP
, sizeof(JoinDate
), FALSE
,TRUE
);
416 SetColDefs ( 8,"NATIVE_LANGUAGE", DB_DATA_TYPE_INTEGER
, &NativeLanguage
, SQL_C_ENUM
, sizeof(NativeLanguage
), FALSE
,TRUE
);
417 SetColDefs ( 9,"IS_DEVELOPER", DB_DATA_TYPE_INTEGER
, &IsDeveloper
, SQL_C_BOOLEAN
, sizeof(bool), FALSE
,TRUE
);
418 SetColDefs (10,"CONTRIBUTIONS", DB_DATA_TYPE_INTEGER
, &Contributions
, SQL_C_USHORT
, sizeof(Contributions
), FALSE
,TRUE
);
419 SetColDefs (11,"LINES_OF_CODE", DB_DATA_TYPE_INTEGER
, &LinesOfCode
, SQL_C_ULONG
, sizeof(LinesOfCode
), FALSE
,TRUE
);
420 } // Ccontact::SetupColumns
423 bool Ccontact::CreateIndexes(void)
425 // This index could easily be accomplished with an "orderBy" clause,
426 // but is done to show how to construct a non-primary index.
432 strcpy(idxDef
[0].ColName
, "IS_DEVELOPER");
433 idxDef
[0].Ascending
= TRUE
;
435 strcpy(idxDef
[1].ColName
, "NAME");
436 idxDef
[1].Ascending
= TRUE
;
438 indexName
= CONTACT_TABLE_NAME
;
439 indexName
+= "_IDX1";
440 Ok
= CreateIndex((char*) (const char*) indexName
, TRUE
, 2, idxDef
);
443 } // Ccontact::CreateIndexes()
447 * Having a function to do a query on the primary key (and possibly others) is
448 * very efficient and tighter coding so that it is available where ever the object
449 * is. Great for use with multiple tables when not using views or outer joins
451 bool Ccontact::FetchByName(char *name
)
453 whereStr
.Printf("NAME = '%s'",name
);
454 where
= (char*) (const char*) this->whereStr
;
463 } // Ccontact::FetchByName()
468 * ************* DIALOGS ***************
473 /* CeditorDlg constructor
475 * Creates the dialog used for creating/editing/deleting/copying a Ccontact object.
476 * This dialog actually is drawn in the main frame of the program
478 * An instance of Ccontact is created - "Contact" - which is used to hold the Ccontact
479 * object that is currently being worked with.
481 CeditorDlg::CeditorDlg(wxWindow
*parent
) : wxPanel (parent
, 1, 1, 460, 455)
483 // Since the ::OnCommand() function is overridden, this prevents the widget
484 // detection in ::OnCommand() until all widgets have been initialized to prevent
485 // uninitialized pointers from crashing the program
486 widgetPtrsSet
= FALSE
;
488 // Create the data structure and a new database connection.
489 // (As there is not a pDb being passed in the constructor, a new database
490 // connection is created)
491 Contact
= new Ccontact();
495 wxMessageBox("Unable to instantiate an instance of Ccontact","Error...",wxOK
| wxICON_EXCLAMATION
);
499 // Check if the table exists or not. If it doesn't, ask the user if they want to
500 // create the table. Continue trying to create the table until it exists, or user aborts
501 while (!Contact
->pDb
->TableExists((char *)CONTACT_TABLE_NAME
))
504 tStr
.Printf("Unable to open the table '%s'.\n\nTable may need to be created...?\n\n",CONTACT_TABLE_NAME
);
505 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
506 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
508 bool createTable
= (wxMessageBox("Do you wish to try to create/clear the CONTACTS table?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
518 DemoFrame
->CreateDataTable();
521 // Tables must be "opened" before anything other than creating/deleting table can be done
522 if (!Contact
->Open())
524 // Table does exist, there was some problem opening it. Currently this should
525 // never fail, except in the case of the table not exisiting. Open() basically
526 // only sets up variable/pointer values, other than checking for table existence.
527 if (Contact
->pDb
->TableExists((char *)CONTACT_TABLE_NAME
))
530 tStr
.Printf("Unable to open the table '%s'.\n\n",CONTACT_TABLE_NAME
);
531 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
532 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
542 wxStaticBox
*FunctionGrp
= new wxStaticBox(this, EDITOR_DIALOG_FN_GROUP
, "", wxPoint(15, 1), wxSize(497, 69), 0, "FunctionGrp");
543 wxStaticBox
*SearchGrp
= new wxStaticBox(this, EDITOR_DIALOG_SEARCH_GROUP
, "", wxPoint(417, 1), wxSize(95, 242), 0, "SearchGrp");
545 pCreateBtn
= new wxButton(this, EDITOR_DIALOG_CREATE
, "&Create", wxPoint(25, 21), wxSize(70, 35), 0, wxDefaultValidator
, "CreateBtn");
546 pEditBtn
= new wxButton(this, EDITOR_DIALOG_EDIT
, "&Edit", wxPoint(102, 21), wxSize(70, 35), 0, wxDefaultValidator
, "EditBtn");
547 pDeleteBtn
= new wxButton(this, EDITOR_DIALOG_DELETE
, "&Delete", wxPoint(179, 21), wxSize(70, 35), 0, wxDefaultValidator
, "DeleteBtn");
548 pCopyBtn
= new wxButton(this, EDITOR_DIALOG_COPY
, "Cop&y", wxPoint(256, 21), wxSize(70, 35), 0, wxDefaultValidator
, "CopyBtn");
549 pSaveBtn
= new wxButton(this, EDITOR_DIALOG_SAVE
, "&Save", wxPoint(333, 21), wxSize(70, 35), 0, wxDefaultValidator
, "SaveBtn");
550 pCancelBtn
= new wxButton(this, EDITOR_DIALOG_CANCEL
, "C&ancel", wxPoint(430, 21), wxSize(70, 35), 0, wxDefaultValidator
, "CancelBtn");
552 pPrevBtn
= new wxButton(this, EDITOR_DIALOG_PREV
, "<< &Prev", wxPoint(430, 81), wxSize(70, 35), 0, wxDefaultValidator
, "PrevBtn");
553 pNextBtn
= new wxButton(this, EDITOR_DIALOG_NEXT
, "&Next >>", wxPoint(430, 121), wxSize(70, 35), 0, wxDefaultValidator
, "NextBtn");
554 pQueryBtn
= new wxButton(this, EDITOR_DIALOG_QUERY
, "&Query", wxPoint(430, 161), wxSize(70, 35), 0, wxDefaultValidator
, "QueryBtn");
555 pResetBtn
= new wxButton(this, EDITOR_DIALOG_RESET
, "&Reset", wxPoint(430, 200), wxSize(70, 35), 0, wxDefaultValidator
, "ResetBtn");
557 pNameMsg
= new wxStaticText(this, EDITOR_DIALOG_NAME_MSG
, "Name:", wxPoint(17, 80), wxSize(-1, -1), 0, "NameMsg");
558 pNameTxt
= new wxTextCtrl(this, EDITOR_DIALOG_NAME_TEXT
, "", wxPoint(17, 97), wxSize(308, 25), 0, wxDefaultValidator
, "NameTxt");
559 pNameListBtn
= new wxButton(this, EDITOR_DIALOG_LOOKUP
, "&Lookup", wxPoint(333, 99), wxSize(70, 24), 0, wxDefaultValidator
, "LookupBtn");
561 pAddress1Msg
= new wxStaticText(this, EDITOR_DIALOG_ADDRESS1_MSG
, "Address:", wxPoint(17, 130), wxSize(-1, -1), 0, "Address1Msg");
562 pAddress1Txt
= new wxTextCtrl(this, EDITOR_DIALOG_ADDRESS2_TEXT
, "", wxPoint(17, 147), wxSize(308, 25), 0, wxDefaultValidator
, "Address1Txt");
564 pAddress2Msg
= new wxStaticText(this, EDITOR_DIALOG_ADDRESS2_MSG
, "Address:", wxPoint(17, 180), wxSize(-1, -1), 0, "Address2Msg");
565 pAddress2Txt
= new wxTextCtrl(this, EDITOR_DIALOG_ADDRESS2_TEXT
, "", wxPoint(17, 197), wxSize(308, 25), 0, wxDefaultValidator
, "Address2Txt");
567 pCityMsg
= new wxStaticText(this, EDITOR_DIALOG_CITY_MSG
, "City:", wxPoint(17, 230), wxSize(-1, -1), 0, "CityMsg");
568 pCityTxt
= new wxTextCtrl(this, EDITOR_DIALOG_CITY_TEXT
, "", wxPoint(17, 247), wxSize(225, 25), 0, wxDefaultValidator
, "CityTxt");
570 pStateMsg
= new wxStaticText(this, EDITOR_DIALOG_STATE_MSG
, "State:", wxPoint(250, 230), wxSize(-1, -1), 0, "StateMsg");
571 pStateTxt
= new wxTextCtrl(this, EDITOR_DIALOG_STATE_TEXT
, "", wxPoint(250, 247), wxSize(153, 25), 0, wxDefaultValidator
, "StateTxt");
573 pCountryMsg
= new wxStaticText(this, EDITOR_DIALOG_COUNTRY_MSG
, "Country:", wxPoint(17, 280), wxSize(-1, -1), 0, "CountryMsg");
574 pCountryTxt
= new wxTextCtrl(this, EDITOR_DIALOG_COUNTRY_TEXT
, "", wxPoint(17, 297), wxSize(225, 25), 0, wxDefaultValidator
, "CountryTxt");
576 pPostalCodeMsg
= new wxStaticText(this, EDITOR_DIALOG_POSTAL_MSG
, "Postal Code:", wxPoint(250, 280), wxSize(-1, -1), 0, "PostalCodeMsg");
577 pPostalCodeTxt
= new wxTextCtrl(this, EDITOR_DIALOG_POSTAL_TEXT
, "", wxPoint(250, 297), wxSize(153, 25), 0, wxDefaultValidator
, "PostalCodeTxt");
579 wxString choice_strings
[5];
580 choice_strings
[0] = "English";
581 choice_strings
[1] = "French";
582 choice_strings
[2] = "German";
583 choice_strings
[3] = "Spanish";
584 choice_strings
[4] = "Other";
585 pNativeLangChoice
= new wxChoice(this, EDITOR_DIALOG_LANG_CHOICE
, wxPoint(17, 346), wxSize(277, -1), 5, choice_strings
);
586 pNativeLangMsg
= new wxStaticText(this, EDITOR_DIALOG_LANG_MSG
, "Native language:", wxPoint(17, 330), wxSize(-1, -1), 0, "NativeLangMsg");
588 wxString radio_strings
[2];
589 radio_strings
[0] = "No";
590 radio_strings
[1] = "Yes";
591 pDeveloperRadio
= new wxRadioBox(this,EDITOR_DIALOG_DEVELOPER
,"Developer:",wxPoint(303,330),wxSize(-1,-1),2,radio_strings
,2,wxHORIZONTAL
);
593 pJoinDateMsg
= new wxStaticText(this, EDITOR_DIALOG_JOIN_MSG
, "Date joined:", wxPoint(17, 380), wxSize(-1, -1), 0, "JoinDateMsg");
594 pJoinDateTxt
= new wxTextCtrl(this, EDITOR_DIALOG_JOIN_TEXT
, "", wxPoint(17, 397), wxSize(150, 25), 0, wxDefaultValidator
, "JoinDateTxt");
596 pContribMsg
= new wxStaticText(this, EDITOR_DIALOG_CONTRIB_MSG
, "Contributions:", wxPoint(175, 380), wxSize(-1, -1), 0, "ContribMsg");
597 pContribTxt
= new wxTextCtrl(this, EDITOR_DIALOG_CONTRIB_TEXT
, "", wxPoint(175, 397), wxSize(120, 25), 0, wxDefaultValidator
, "ContribTxt");
599 pLinesMsg
= new wxStaticText(this, EDITOR_DIALOG_LINES_MSG
, "Lines of code:", wxPoint(303, 380), wxSize(-1, -1), 0, "LinesMsg");
600 pLinesTxt
= new wxTextCtrl(this, EDITOR_DIALOG_LINES_TEXT
, "", wxPoint(303, 397), wxSize(100, 25), 0, wxDefaultValidator
, "LinesTxt");
602 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
603 // handle all widget processing
604 widgetPtrsSet
= TRUE
;
606 // Setup the orderBy and where clauses to return back a single record as the result set,
607 // as there will only be one record being shown on the dialog at a time, this optimizes
608 // network traffic by only returning a one row result
610 Contact
->orderBy
= "NAME"; // field name to sort by
612 // The wxString "whereStr" is not a member of the wxTable object, it is a member variable
613 // specifically in the Ccontact class. It is used here for simpler construction of a varying
614 // length string, and then after the string is built, the wxTable member variable "where" is
615 // assigned the pointer to the constructed string.
617 // The constructed where clause below has a sub-query within it "SELECT MIN(NAME) FROM %s"
618 // to achieve a single row (in this case the first name in alphabetical order).
619 Contact
->whereStr
.Printf("NAME = (SELECT MIN(NAME) FROM %s)",Contact
->tableName
);
621 // NOTE: (const char*) returns a pointer which may not be valid later, so this is short term use only
622 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
624 // Perform the Query to get the result set.
625 // NOTE: If there are no rows returned, that is a valid result, so Query() would return TRUE.
626 // Only if there is a database error will Query() come back as FALSE
627 if (!Contact
->Query())
630 tStr
= "ODBC error during Query()\n\n";
631 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
632 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
633 GetParent()->Close();
637 // Since Query succeeded, now get the row that was returned
638 if (!Contact
->GetNext())
639 // If the GetNext() failed at this point, then there are no rows to retrieve,
640 // so clear the values in the members of "Contact" so that PutData() blanks the
641 // widgets on the dialog
642 Contact
->Initialize();
648 } // CeditorDlg constructor
651 bool CeditorDlg::OnClose()
654 if ((mode
!= mCreate
) && (mode
!= mEdit
))
662 wxMessageBox("Must finish processing the current record being created/modified before exiting","Notice...",wxOK
| wxICON_INFORMATION
);
665 } // CeditorDlg::OnClose()
668 void CeditorDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
672 widgetName
= win
.GetName();
677 if (widgetName
== pCreateBtn
->GetName())
679 Contact
->Initialize();
682 pNameTxt
->SetValue("");
683 pNameTxt
->SetFocus();
687 if (widgetName
== pEditBtn
->GetName())
689 saveName
= Contact
->Name
;
691 pNameTxt
->SetFocus();
695 if (widgetName
== pCopyBtn
->GetName())
698 pNameTxt
->SetValue("");
699 pNameTxt
->SetFocus();
703 if (widgetName
== pDeleteBtn
->GetName())
705 bool Ok
= (wxMessageBox("Are you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
710 if (Ok
&& Contact
->Delete())
712 // NOTE: Deletions are not finalized until a CommitTrans() is performed.
713 // If the commit were not performed, the program will continue to
714 // show the table contents as if they were deleted until this instance
715 // of Ccontact is deleted. If the Commit wasn't performed, the
716 // database will automatically Rollback the changes when the database
717 // connection is terminated
718 Contact
->pDb
->CommitTrans();
720 // Try to get the row that followed the just deleted row in the orderBy sequence
723 // There was now row (in sequence) after the just deleted row, so get the
724 // row which preceded the just deleted row
727 // There are now no rows remaining, so clear the dialog widgets
728 Contact
->Initialize();
732 SetMode(mode
); // force reset of button enable/disable
736 Contact
->pDb
->RollbackTrans();
742 if (widgetName
== pSaveBtn
->GetName())
748 if (widgetName
== pCancelBtn
->GetName())
750 bool Ok
= (wxMessageBox("Are you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
755 if (!strcmp((const char*) saveName
,""))
757 Contact
->Initialize();
764 // Requery previous record
765 if (Contact
->FetchByName((char*) (const char*) saveName
))
773 // Previous record not available, retrieve first record in table
774 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
775 Contact
->whereStr
+= Contact
->tableName
;
776 Contact
->whereStr
+= ")";
778 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
779 if (!Contact
->Query())
782 tStr
= "ODBC error during Query()\n\n";
783 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
784 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
788 if (Contact
->GetNext()) // Successfully read first record
794 // No contacts are available, clear dialog
795 Contact
->Initialize();
801 if (widgetName
== pPrevBtn
->GetName())
808 if (widgetName
== pNextBtn
->GetName())
815 if (widgetName
== pQueryBtn
->GetName())
817 // Display the query dialog box
818 char qryWhere
[DB_MAX_WHERE_CLAUSE_LEN
+1];
819 strcpy(qryWhere
, (const char*) Contact
->qryWhereStr
);
820 char *tblName
[] = {(char *)CONTACT_TABLE_NAME
, 0};
821 new CqueryDlg(GetParent(), Contact
->pDb
, tblName
, qryWhere
);
823 // Query the first record in the new record set and
824 // display it, if the query string has changed.
825 if (strcmp(qryWhere
, (const char*) Contact
->qryWhereStr
))
827 Contact
->orderBy
= "NAME";
828 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
829 Contact
->whereStr
+= CONTACT_TABLE_NAME
;
830 // Append the query where string (if there is one)
831 Contact
->qryWhereStr
= qryWhere
;
832 if (strlen(qryWhere
))
834 Contact
->whereStr
+= " WHERE ";
835 Contact
->whereStr
+= Contact
->qryWhereStr
;
837 // Close the expression with a right paren
838 Contact
->whereStr
+= ")";
840 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
841 if (!Contact
->Query())
844 tStr
= "ODBC error during Query()\n\n";
845 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
846 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
849 // Display the first record from the query set
850 if (!Contact
->GetNext())
851 Contact
->Initialize();
855 // Enable/Disable the reset button
856 pResetBtn
->Enable(!Contact
->qryWhereStr
.IsEmpty());
862 if (widgetName
== pResetBtn
->GetName())
864 // Clear the additional where criteria established by the query feature
865 Contact
->qryWhereStr
= "";
867 // Query the first record in the table
868 Contact
->orderBy
= "NAME";
869 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
870 Contact
->whereStr
+= CONTACT_TABLE_NAME
;
871 Contact
->whereStr
+= ")";
872 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
873 if (!Contact
->Query())
876 tStr
= "ODBC error during Query()\n\n";
877 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
878 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
881 if (!Contact
->GetNext())
882 Contact
->Initialize();
884 pResetBtn
->Enable(FALSE
);
890 if (widgetName
== pNameListBtn
->GetName())
892 new ClookUpDlg(/* wxWindow *parent */ this,
893 /* char *windowTitle */ "Select contact name",
894 /* char *tableName */ (char *) CONTACT_TABLE_NAME
,
895 /* char *dispCol1 */ "NAME",
896 /* char *dispCol2 */ "JOIN_DATE",
897 /* char *where */ "",
898 /* char *orderBy */ "NAME",
899 /* bool distinctValues */ TRUE
);
901 if (ListDB_Selection
&& strlen(ListDB_Selection
))
903 wxString w
= "NAME = '";
904 w
+= ListDB_Selection
;
906 GetRec((char*) (const char*) w
);
912 } // CeditorDlg::OnCommand()
915 void CeditorDlg::FieldsEditable()
917 pNameTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
918 pAddress1Txt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
919 pAddress2Txt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
920 pCityTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
921 pStateTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
922 pPostalCodeTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
923 pCountryTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
925 pJoinDateTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
926 pContribTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
927 pLinesTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
928 pNativeLangChoice
->Enable((mode
== mCreate
) || (mode
== mEdit
));
929 pDeveloperRadio
->Enable((mode
== mCreate
) || (mode
== mEdit
));
931 } // CeditorDlg::FieldsEditable()
934 void CeditorDlg::SetMode(enum DialogModes m
)
955 pCreateBtn
->Enable( !edit
);
956 pEditBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
957 pDeleteBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
958 pCopyBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
959 pSaveBtn
->Enable( edit
);
960 pCancelBtn
->Enable( edit
);
961 pPrevBtn
->Enable( !edit
);
962 pNextBtn
->Enable( !edit
);
963 pQueryBtn
->Enable( !edit
);
964 pResetBtn
->Enable( !edit
&& !Contact
->qryWhereStr
.IsEmpty() );
965 pNameListBtn
->Enable( !edit
);
969 } // CeditorDlg::SetMode()
972 bool CeditorDlg::PutData()
976 pNameTxt
->SetValue(Contact
->Name
);
977 pAddress1Txt
->SetValue(Contact
->Addr1
);
978 pAddress2Txt
->SetValue(Contact
->Addr2
);
979 pCityTxt
->SetValue(Contact
->City
);
980 pStateTxt
->SetValue(Contact
->State
);
981 pCountryTxt
->SetValue(Contact
->Country
);
982 pPostalCodeTxt
->SetValue(Contact
->PostalCode
);
984 tStr
.Printf("%d/%d/%d",Contact
->JoinDate
.month
,Contact
->JoinDate
.day
,Contact
->JoinDate
.year
);
985 pJoinDateTxt
->SetValue(tStr
);
987 tStr
.Printf("%d",Contact
->Contributions
);
988 pContribTxt
->SetValue(tStr
);
990 tStr
.Printf("%lu",Contact
->LinesOfCode
);
991 pLinesTxt
->SetValue(tStr
);
993 pNativeLangChoice
->SetSelection(Contact
->NativeLanguage
);
995 pDeveloperRadio
->SetSelection(Contact
->IsDeveloper
);
998 } // Ceditor::PutData()
1002 * Reads the data out of all the widgets on the dialog. Some data evaluation is done
1003 * to ensure that there is a name entered and that the date field is valid.
1005 * A return value of TRUE means that valid data was retrieved from the dialog, otherwise
1006 * invalid data was found (and a message was displayed telling the user what to fix), and
1007 * the data was not placed into the appropraite fields of Ccontact
1009 bool CeditorDlg::GetData()
1011 // Validate that the data currently entered into the widgets is valid data
1014 tStr
= pNameTxt
->GetValue();
1015 if (!strcmp((const char*) tStr
,""))
1017 wxMessageBox("A name is required for entry into the contact table","Notice...",wxOK
| wxICON_INFORMATION
);
1021 bool invalid
= FALSE
;
1025 tStr
= pJoinDateTxt
->GetValue();
1026 if (tStr
.Freq('/') != 2)
1029 // Find the month, day, and year tokens
1032 first
= tStr
.First('/');
1033 second
= tStr
.Last('/');
1035 mm
= atoi(tStr
.SubString(0,first
));
1036 dd
= atoi(tStr
.SubString(first
+1,second
));
1037 yyyy
= atoi(tStr
.SubString(second
+1,tStr
.Length()-1));
1039 invalid
= !(mm
&& dd
&& yyyy
);
1042 // Force Year 2000 compliance
1043 if (!invalid
&& (yyyy
< 1000))
1046 // Check the token ranges for validity
1051 else if ((mm
< 1) || (mm
> 12))
1059 int days
[12] = {31,28,31,30,31,30,
1061 if (dd
> days
[mm
-1])
1064 if ((dd
== 29) && (mm
== 2))
1066 if (((yyyy
% 4) == 0) && (((yyyy
% 100) != 0) || ((yyyy
% 400) == 0)))
1076 Contact
->JoinDate
.month
= mm
;
1077 Contact
->JoinDate
.day
= dd
;
1078 Contact
->JoinDate
.year
= yyyy
;
1082 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
);
1086 tStr
= pNameTxt
->GetValue();
1087 strcpy(Contact
->Name
,(const char*) tStr
);
1088 strcpy(Contact
->Addr1
,pAddress1Txt
->GetValue());
1089 strcpy(Contact
->Addr2
,pAddress2Txt
->GetValue());
1090 strcpy(Contact
->City
,pCityTxt
->GetValue());
1091 strcpy(Contact
->State
,pStateTxt
->GetValue());
1092 strcpy(Contact
->Country
,pCountryTxt
->GetValue());
1093 strcpy(Contact
->PostalCode
,pPostalCodeTxt
->GetValue());
1095 Contact
->Contributions
= atoi(pContribTxt
->GetValue());
1096 Contact
->LinesOfCode
= atol(pLinesTxt
->GetValue());
1098 Contact
->NativeLanguage
= (enum Language
) pNativeLangChoice
->GetSelection();
1099 Contact
->IsDeveloper
= (bool) pDeveloperRadio
->GetSelection();
1102 } // CeditorDlg::GetData()
1106 * Retrieve data from the dialog, verify the validity of the data, and if it is valid,
1107 * try to insert/update the data to the table based on the current 'mode' the dialog
1110 * A return value of TRUE means the insert/update was completed successfully, a return
1111 * value of FALSE means that Save() failed. If returning FALSE, then this function
1112 * has displayed a detailed error message for the user.
1114 bool CeditorDlg::Save()
1116 bool failed
= FALSE
;
1118 // Read the data in the widgets of the dialog to get the user's data
1122 // Perform any other required validations necessary before saving
1127 wxBeginBusyCursor();
1129 if (mode
== mCreate
)
1131 RETCODE result
= Contact
->Insert();
1133 failed
= (result
!= DB_SUCCESS
);
1136 // Some errors may be expected, like a duplicate key, so handle those instances with
1137 // specific error messages.
1138 if (result
== DB_ERR_INTEGRITY_CONSTRAINT_VIOL
)
1141 tStr
= "A duplicate key value already exists in the table.\nUnable to save record\n\n";
1142 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1143 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1147 // Some other unexpexted error occurred
1149 tStr
= "Database insert failed\n\n";
1150 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1151 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1155 else // mode == mEdit
1157 if (!Contact
->Update())
1160 tStr
= "Database update failed\n\n";
1161 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1162 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1169 Contact
->pDb
->CommitTrans();
1170 SetMode(mView
); // Sets the dialog mode back to viewing after save is successful
1173 Contact
->pDb
->RollbackTrans();
1179 } // CeditorDlg::Save()
1183 * Where this program is only showing a single row at a time in the dialog,
1184 * a special where clause must be built to find just the single row which,
1185 * in sequence, would follow the currently displayed row.
1187 bool CeditorDlg::GetNextRec()
1191 w
= "NAME = (SELECT MIN(NAME) FROM ";
1192 w
+= Contact
->tableName
;
1193 w
+= " WHERE NAME > '";
1197 // If a query where string is currently set, append that criteria
1198 if (!Contact
->qryWhereStr
.IsEmpty())
1201 w
+= Contact
->qryWhereStr
;
1207 return(GetRec((char*) (const char*) w
));
1209 } // CeditorDlg::GetNextRec()
1213 * Where this program is only showing a single row at a time in the dialog,
1214 * a special where clause must be built to find just the single row which,
1215 * in sequence, would precede the currently displayed row.
1217 bool CeditorDlg::GetPrevRec()
1221 w
= "NAME = (SELECT MAX(NAME) FROM ";
1222 w
+= Contact
->tableName
;
1223 w
+= " WHERE NAME < '";
1227 // If a query where string is currently set, append that criteria
1228 if (!Contact
->qryWhereStr
.IsEmpty())
1231 w
+= Contact
->qryWhereStr
;
1237 return(GetRec((char*) (const char*)w
));
1239 } // CeditorDlg::GetPrevRec()
1243 * This function is here to avoid duplicating this same code in both the
1244 * GetPrevRec() and GetNextRec() functions
1246 bool CeditorDlg::GetRec(char *whereStr
)
1248 Contact
->where
= whereStr
;
1249 Contact
->orderBy
= "NAME";
1251 if (!Contact
->Query())
1254 tStr
= "ODBC error during Query()\n\n";
1255 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1256 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1261 if (Contact
->GetNext())
1268 } // CeditorDlg::GetRec()
1273 * CparameterDlg constructor
1275 CparameterDlg::CparameterDlg(wxWindow
*parent
) : wxDialog (parent
, PARAMETER_DIALOG
, "ODBC parameter settings", wxPoint(-1, -1), wxSize(400, 275))
1277 // Since the ::OnCommand() function is overridden, this prevents the widget
1278 // detection in ::OnCommand() until all widgets have been initialized to prevent
1279 // uninitialized pointers from crashing the program
1280 widgetPtrsSet
= FALSE
;
1282 pParamODBCSourceMsg
= new wxStaticText(this, PARAMETER_DIALOG_SOURCE_MSG
, "ODBC data sources:", wxPoint(10, 10), wxSize(-1, -1), 0, "ParamODBCSourceMsg");
1283 pParamODBCSourceList
= new wxListBox(this, PARAMETER_DIALOG_SOURCE_LISTBOX
, wxPoint(10, 29), wxSize(285, 150), 0, 0, wxLB_SINGLE
|wxLB_ALWAYS_SB
, wxDefaultValidator
, "ParamODBCSourceList");
1285 pParamUserNameMsg
= new wxStaticText(this, PARAMETER_DIALOG_NAME_MSG
, "Database user name:", wxPoint(10, 193), wxSize(-1, -1), 0, "ParamUserNameMsg");
1286 pParamUserNameTxt
= new wxTextCtrl(this, PARAMETER_DIALOG_NAME_TEXT
, "", wxPoint(10, 209), wxSize(140, 25), 0, wxDefaultValidator
, "ParamUserNameTxt");
1288 pParamPasswordMsg
= new wxStaticText(this, PARAMETER_DIALOG_PASSWORD_MSG
, "Password:", wxPoint(156, 193), wxSize(-1, -1), 0, "ParamPasswordMsg");
1289 pParamPasswordTxt
= new wxTextCtrl(this, PARAMETER_DIALOG_PASSWORD_TEXT
, "", wxPoint(156, 209), wxSize(140, 25), 0, wxDefaultValidator
, "ParamPasswordTxt");
1291 pParamSaveBtn
= new wxButton(this, PARAMETER_DIALOG_SAVE
, "&Save", wxPoint(310, 21), wxSize(70, 35), 0, wxDefaultValidator
, "ParamSaveBtn");
1292 pParamCancelBtn
= new wxButton(this, PARAMETER_DIALOG_CANCEL
, "C&ancel", wxPoint(310, 66), wxSize(70, 35), 0, wxDefaultValidator
, "ParamCancelBtn");
1294 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
1295 // handle all widget processing
1296 widgetPtrsSet
= TRUE
;
1299 savedParamSettings
= DatabaseDemoApp
.params
;
1304 } // CparameterDlg constructor
1307 bool CparameterDlg::OnClose()
1309 // Put any additional checking necessary to make certain it is alright
1310 // to close the program here that is not done elsewhere
1313 bool Ok
= (wxMessageBox("No changes have been saved.\n\nAre you sure you wish exit the parameter screen?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
1318 DatabaseDemoApp
.params
= savedParamSettings
;
1321 if (GetParent() != NULL
)
1322 GetParent()->SetFocus();
1324 } // Cparameter::OnClose()
1327 void CparameterDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
1329 wxString widgetName
;
1331 widgetName
= win
.GetName();
1336 if (widgetName
== pParamSaveBtn
->GetName())
1341 tStr
= "Database parameters have been saved.";
1342 if (GetParent() != NULL
) // The parameter dialog was not called during startup due to a missing cfg file
1343 tStr
+= "\nNew parameters will take effect the next time the program is started.";
1344 wxMessageBox(tStr
,"Notice...",wxOK
| wxICON_INFORMATION
);
1351 if (widgetName
== pParamCancelBtn
->GetName())
1356 } // CparameterDlg::OnCommand()
1359 bool CparameterDlg::PutData()
1361 // Fill the data source list box
1362 FillDataSourceList();
1364 // Fill in the fields from the params object
1365 pParamODBCSourceList
->SetStringSelection(DatabaseDemoApp
.params
.ODBCSource
);
1366 pParamUserNameTxt
->SetValue(DatabaseDemoApp
.params
.UserName
);
1367 pParamPasswordTxt
->SetValue(DatabaseDemoApp
.params
.Password
);
1369 } // CparameterDlg::PutData()
1372 bool CparameterDlg::GetData()
1375 if (pParamODBCSourceList
->GetStringSelection())
1377 tStr
= pParamODBCSourceList
->GetStringSelection();
1378 if (tStr
.Length() > (sizeof(DatabaseDemoApp
.params
.ODBCSource
)-1))
1381 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());
1382 wxMessageBox(errmsg
,"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1385 strcpy(DatabaseDemoApp
.params
.ODBCSource
, tStr
);
1390 tStr
= pParamUserNameTxt
->GetValue();
1391 if (tStr
.Length() > (sizeof(DatabaseDemoApp
.params
.UserName
)-1))
1394 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());
1395 wxMessageBox(errmsg
,"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1398 strcpy(DatabaseDemoApp
.params
.UserName
, tStr
);
1400 tStr
= pParamPasswordTxt
->GetValue();
1401 if (tStr
.Length() > (sizeof(DatabaseDemoApp
.params
.Password
)-1))
1404 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());
1405 wxMessageBox(errmsg
,"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1408 strcpy(DatabaseDemoApp
.params
.Password
,tStr
);
1410 } // CparameterDlg::GetData()
1413 bool CparameterDlg::Save()
1415 Cparameters saveParams
= DatabaseDemoApp
.params
;
1418 DatabaseDemoApp
.params
= saveParams
;
1423 if ((paramFile
= fopen(paramFilename
, "wt")) == NULL
)
1426 tStr
.Printf("Unable to write/overwrite '%s'.",paramFilename
);
1427 wxMessageBox(tStr
,"File I/O Error...",wxOK
| wxICON_EXCLAMATION
);
1431 fputs(DatabaseDemoApp
.params
.ODBCSource
, paramFile
);
1432 fputc('\n', paramFile
);
1433 fputs(DatabaseDemoApp
.params
.UserName
, paramFile
);
1434 fputc('\n', paramFile
);
1435 fputs(DatabaseDemoApp
.params
.Password
, paramFile
);
1436 fputc('\n', paramFile
);
1440 } // CparameterDlg::Save()
1443 void CparameterDlg::FillDataSourceList()
1445 char Dsn
[SQL_MAX_DSN_LENGTH
+ 1];
1447 wxStringList strList
;
1449 while(GetDataSource(DbConnectInf
.Henv
, Dsn
, SQL_MAX_DSN_LENGTH
+1, DsDesc
, 255))
1454 char **p
= strList
.ListToArray();
1456 for (int i
= 0; strlen(p
[i
]); i
++)
1457 pParamODBCSourceList
->Append(p
[i
]);
1458 } // CparameterDlg::CparameterDlg::FillDataSourceList()
1461 // CqueryDlg() constructor
1462 CqueryDlg::CqueryDlg(wxWindow
*parent
, wxDB
*pDb
, char *tblName
[], char *pWhereArg
) : wxDialog (parent
, QUERY_DIALOG
, "Query", wxPoint(-1, -1), wxSize(480, 360))
1464 wxBeginBusyCursor();
1468 masterTableName
= tblName
[0];
1469 widgetPtrsSet
= FALSE
;
1472 // Initialize the WHERE clause from the string passed in
1473 pWhere
= pWhereArg
; // Save a pointer to the output buffer
1474 if (strlen(pWhere
) > DB_MAX_WHERE_CLAUSE_LEN
) // Check the length of the buffer passed in
1477 s
.Printf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN
+1);
1478 wxMessageBox(s
,"Error...",wxOK
| wxICON_EXCLAMATION
);
1483 pQueryCol1Msg
= new wxStaticText(this, QUERY_DIALOG_COL_MSG
, "Column 1:", wxPoint(10, 10), wxSize(69, 16), 0, "QueryCol1Msg");
1484 pQueryCol1Choice
= new wxChoice(this, QUERY_DIALOG_COL_CHOICE
, wxPoint(10, 27), wxSize(250, 27), 0, 0, 0, wxDefaultValidator
, "QueryCol1Choice");
1486 pQueryNotMsg
= new wxStaticText(this, QUERY_DIALOG_NOT_MSG
, "NOT", wxPoint(268, 10), wxSize(-1, -1), 0, "QueryNotMsg");
1487 pQueryNotCheck
= new wxCheckBox(this, QUERY_DIALOG_NOT_CHECKBOX
, "", wxPoint(275, 37), wxSize(20, 20), 0, wxDefaultValidator
, "QueryNotCheck");
1489 wxString choice_strings
[9];
1490 choice_strings
[0] = "=";
1491 choice_strings
[1] = "<";
1492 choice_strings
[2] = ">";
1493 choice_strings
[3] = "<=";
1494 choice_strings
[4] = ">=";
1495 choice_strings
[5] = "Begins";
1496 choice_strings
[6] = "Contains";
1497 choice_strings
[7] = "Like";
1498 choice_strings
[8] = "Between";
1499 pQueryOperatorMsg
= new wxStaticText(this, QUERY_DIALOG_OP_MSG
, "Operator:", wxPoint(305, 10), wxSize(-1, -1), 0, "QueryOperatorMsg");
1500 pQueryOperatorChoice
= new wxChoice(this, QUERY_DIALOG_OP_CHOICE
, wxPoint(305, 27), wxSize(80, 27), 9, choice_strings
, 0, wxDefaultValidator
, "QueryOperatorChoice");
1502 pQueryCol2Msg
= new wxStaticText(this, QUERY_DIALOG_COL2_MSG
, "Column 2:", wxPoint(10, 65), wxSize(69, 16), 0, "QueryCol2Msg");
1503 pQueryCol2Choice
= new wxChoice(this, QUERY_DIALOG_COL2_CHOICE
, wxPoint(10, 82), wxSize(250, 27), 0, 0, 0, wxDefaultValidator
, "QueryCol2Choice");
1505 pQuerySqlWhereMsg
= new wxStaticText(this, QUERY_DIALOG_WHERE_MSG
, "SQL where clause:", wxPoint(10, 141), wxSize(-1, -1), 0, "QuerySqlWhereMsg");
1506 pQuerySqlWhereMtxt
= new wxTextCtrl(this, QUERY_DIALOG_WHERE_TEXT
, "", wxPoint(10, 159), wxSize(377, 134), wxTE_MULTILINE
, wxDefaultValidator
, "QuerySqlWhereMtxt");
1508 pQueryAddBtn
= new wxButton(this, QUERY_DIALOG_ADD
, "&Add", wxPoint(406, 24), wxSize(56, 26), 0, wxDefaultValidator
, "QueryAddBtn");
1509 pQueryAndBtn
= new wxButton(this, QUERY_DIALOG_AND
, "A&nd", wxPoint(406, 58), wxSize(56, 26), 0, wxDefaultValidator
, "QueryAndBtn");
1510 pQueryOrBtn
= new wxButton(this, QUERY_DIALOG_OR
, "&Or", wxPoint(406, 92), wxSize(56, 26), 0, wxDefaultValidator
, "QueryOrBtn");
1512 pQueryLParenBtn
= new wxButton(this, QUERY_DIALOG_LPAREN
, "(", wxPoint(406, 126), wxSize(26, 26), 0, wxDefaultValidator
, "QueryLParenBtn");
1513 pQueryRParenBtn
= new wxButton(this, QUERY_DIALOG_RPAREN
, ")", wxPoint(436, 126), wxSize(26, 26), 0, wxDefaultValidator
, "QueryRParenBtn");
1515 pQueryDoneBtn
= new wxButton(this, QUERY_DIALOG_DONE
, "&Done", wxPoint(406, 185), wxSize(56, 26), 0, wxDefaultValidator
, "QueryDoneBtn");
1516 pQueryClearBtn
= new wxButton(this, QUERY_DIALOG_CLEAR
, "C&lear", wxPoint(406, 218), wxSize(56, 26), 0, wxDefaultValidator
, "QueryClearBtn");
1517 pQueryCountBtn
= new wxButton(this, QUERY_DIALOG_COUNT
, "&Count", wxPoint(406, 252), wxSize(56, 26), 0, wxDefaultValidator
, "QueryCountBtn");
1519 pQueryValue1Msg
= new wxStaticText(this, QUERY_DIALOG_VALUE1_MSG
, "Value:", wxPoint(277, 66), wxSize(-1, -1), 0, "QueryValue1Msg");
1520 pQueryValue1Txt
= new wxTextCtrl(this, QUERY_DIALOG_VALUE1_TEXT
, "", wxPoint(277, 83), wxSize(108, 25), 0, wxDefaultValidator
, "QueryValue1Txt");
1522 pQueryValue2Msg
= new wxStaticText(this, QUERY_DIALOG_VALUE2_MSG
, "AND", wxPoint(238, 126), wxSize(-1, -1), 0, "QueryValue2Msg");
1523 pQueryValue2Txt
= new wxTextCtrl(this, QUERY_DIALOG_VALUE2_TEXT
, "", wxPoint(277, 120), wxSize(108, 25), 0, wxDefaultValidator
, "QueryValue2Txt");
1525 pQueryHintGrp
= new wxStaticBox(this, QUERY_DIALOG_HINT_GROUP
, "", wxPoint(10, 291), wxSize(377, 40), 0, "QueryHintGrp");
1526 pQueryHintMsg
= new wxStaticText(this, QUERY_DIALOG_HINT_MSG
, "", wxPoint(16, 306), wxSize(-1, -1), 0, "QueryHintMsg");
1528 widgetPtrsSet
= TRUE
;
1529 // Initialize the dialog
1531 pQueryCol2Choice
->Append("VALUE -->");
1532 colInf
= pDB
->GetColumns(tblName
);
1533 for (int i
= 0; colInf
[i
].colName
&& strlen(colInf
[i
].colName
); i
++)
1535 // If there is more than one table being queried, qualify
1536 // the column names with the table name prefix.
1537 if (tblName
[1] && strlen(tblName
[1]))
1539 qualName
.Printf("%s.%s", colInf
[i
].tableName
, colInf
[i
].colName
);
1540 pQueryCol1Choice
->Append(qualName
);
1541 pQueryCol2Choice
->Append(qualName
);
1543 else // Single table query, append just the column names
1545 pQueryCol1Choice
->Append(colInf
[i
].colName
);
1546 pQueryCol2Choice
->Append(colInf
[i
].colName
);
1550 pQueryCol1Choice
->SetSelection(0);
1551 pQueryCol2Choice
->SetSelection(0);
1552 pQueryOperatorChoice
->SetSelection(0);
1554 pQueryValue2Msg
->Show(FALSE
);
1555 pQueryValue2Txt
->Show(FALSE
);
1557 pQueryHintMsg
->SetLabel(langQRY_EQ
);
1559 pQuerySqlWhereMtxt
->SetValue(pWhere
);
1563 // Display the dialog window
1567 } // CqueryDlg() constructor
1570 void CqueryDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
1572 // Widget pointers won't be set when the dialog is constructed.
1573 // Control is passed through this function once for each widget on
1574 // a dialog as the dialog is constructed.
1578 wxString widgetName
= win
.GetName();
1580 // Operator choice box
1581 if (widgetName
== pQueryOperatorChoice
->GetName())
1583 // Set the help text
1584 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1587 pQueryHintMsg
->SetLabel(langQRY_EQ
);
1590 pQueryHintMsg
->SetLabel(langQRY_LT
);
1593 pQueryHintMsg
->SetLabel(langQRY_GT
);
1596 pQueryHintMsg
->SetLabel(langQRY_LE
);
1599 pQueryHintMsg
->SetLabel(langQRY_GE
);
1602 pQueryHintMsg
->SetLabel(langQRY_BEGINS
);
1605 pQueryHintMsg
->SetLabel(langQRY_CONTAINS
);
1608 pQueryHintMsg
->SetLabel(langQRY_LIKE
);
1611 pQueryHintMsg
->SetLabel(langQRY_BETWEEN
);
1615 // Hide the value2 widget
1616 pQueryValue2Msg
->Show(FALSE
); // BETWEEN will show this widget
1617 pQueryValue2Txt
->Show(FALSE
); // BETWEEN will show this widget
1619 // Disable the NOT operator for <, <=, >, >=
1620 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1626 pQueryNotCheck
->SetValue(0);
1627 pQueryNotCheck
->Enable(FALSE
);
1630 pQueryNotCheck
->Enable(TRUE
);
1634 // Manipulate the dialog to handle the selected operator
1635 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1642 pQueryCol2Choice
->Enable(TRUE
);
1643 if (pQueryCol2Choice
->GetSelection()) // Column name is highlighted
1645 pQueryValue1Msg
->Show(FALSE
);
1646 pQueryValue1Txt
->Show(FALSE
);
1648 else // "Value" is highlighted
1650 pQueryValue1Msg
->Show(TRUE
);
1651 pQueryValue1Txt
->Show(TRUE
);
1652 pQueryValue1Txt
->SetFocus();
1658 pQueryCol2Choice
->SetSelection(0);
1659 pQueryCol2Choice
->Enable(FALSE
);
1660 pQueryValue1Msg
->Show(TRUE
);
1661 pQueryValue1Txt
->Show(TRUE
);
1662 pQueryValue1Txt
->SetFocus();
1665 pQueryCol2Choice
->SetSelection(0);
1666 pQueryCol2Choice
->Enable(FALSE
);
1667 pQueryValue2Msg
->Show(TRUE
);
1668 pQueryValue2Txt
->Show(TRUE
);
1669 pQueryValue1Msg
->Show(TRUE
);
1670 pQueryValue1Txt
->Show(TRUE
);
1671 pQueryValue1Txt
->SetFocus();
1677 } // Operator choice box
1680 if (widgetName
== pQueryCol2Choice
->GetName())
1682 if (pQueryCol2Choice
->GetSelection()) // Column name is highlighted
1684 pQueryValue1Msg
->Show(FALSE
);
1685 pQueryValue1Txt
->Show(FALSE
);
1687 else // "Value" is highlighted
1689 pQueryValue1Msg
->Show(TRUE
);
1690 pQueryValue1Txt
->Show(TRUE
);
1691 pQueryValue1Txt
->SetFocus();
1695 } // Column 2 choice
1698 if (widgetName
== pQueryAddBtn
->GetName())
1706 if (widgetName
== pQueryAndBtn
->GetName())
1708 AppendToWhere(" AND\n");
1714 if (widgetName
== pQueryOrBtn
->GetName())
1716 AppendToWhere(" OR\n");
1721 // Left Paren button
1722 if (widgetName
== pQueryLParenBtn
->GetName())
1727 } // Left Paren button
1729 // Right paren button
1730 if (widgetName
== pQueryRParenBtn
->GetName())
1735 } // Right Paren button
1738 if (widgetName
== pQueryDoneBtn
->GetName())
1740 // Be sure the where clause will not overflow the output buffer
1741 if (strlen(pQuerySqlWhereMtxt
->GetValue()) > DB_MAX_WHERE_CLAUSE_LEN
)
1744 s
.Printf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN
+1);
1745 wxMessageBox(s
,"Error...",wxOK
| wxICON_EXCLAMATION
);
1748 // Validate the where clause for things such as matching parens
1749 if (!ValidateWhereClause())
1751 // Copy the where clause to the output buffer and exit
1752 strcpy(pWhere
, pQuerySqlWhereMtxt
->GetValue());
1759 if (widgetName
== pQueryClearBtn
->GetName())
1761 bool Ok
= (wxMessageBox("Are you sure you wish to clear the Query?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
1764 pQuerySqlWhereMtxt
->SetValue("");
1770 if (widgetName
== pQueryCountBtn
->GetName())
1772 wxBeginBusyCursor();
1779 } // CqueryDlg::OnCommand
1782 bool CqueryDlg::OnClose()
1797 GetParent()->SetFocus();
1801 } // CqueryDlg::OnClose()
1804 bool CqueryDlg::SetWidgetPtrs()
1808 abort = abort || !(pQueryCol1Choice = (wxChoice *)GetWidgetPtr("QueryCol1Choice",this));
1809 abort = abort || !(pQueryNotCheck = (wxCheckBox *)GetWidgetPtr("QueryNotCheck",this));
1810 abort = abort || !(pQueryOperatorChoice = (wxChoice *)GetWidgetPtr("QueryOperatorChoice",this));
1811 abort = abort || !(pQueryCol2Choice = (wxChoice *)GetWidgetPtr("QueryCol2Choice",this));
1812 abort = abort || !(pQueryValue1Txt = (wxTextCtrl *)GetWidgetPtr("QueryValue1Txt",this));
1813 abort = abort || !(pQueryValue2Txt = (wxTextCtrl *)GetWidgetPtr("QueryValue2Txt",this));
1814 abort = abort || !(pQuerySqlWhereMtxt = (wxMultiText *)GetWidgetPtr("QuerySqlWhereMtxt",this));
1815 abort = abort || !(pQueryAddBtn = (wxButton *)GetWidgetPtr("QueryAddBtn",this));
1816 abort = abort || !(pQueryAndBtn = (wxButton *)GetWidgetPtr("QueryAndBtn",this));
1817 abort = abort || !(pQueryOrBtn = (wxButton *)GetWidgetPtr("QueryOrBtn",this));
1818 abort = abort || !(pQueryLParenBtn = (wxButton *)GetWidgetPtr("QueryLParenBtn",this));
1819 abort = abort || !(pQueryRParenBtn = (wxButton *)GetWidgetPtr("QueryRParenBtn",this));
1820 abort = abort || !(pQueryDoneBtn = (wxButton *)GetWidgetPtr("QueryDoneBtn",this));
1821 abort = abort || !(pQueryClearBtn = (wxButton *)GetWidgetPtr("QueryClearBtn",this));
1822 abort = abort || !(pQueryCountBtn = (wxButton *)GetWidgetPtr("QueryCountBtn",this));
1823 abort = abort || !(pQueryHelpBtn = (wxButton *)GetWidgetPtr("QueryHelpBtn",this));
1824 abort = abort || !(pQueryHintMsg = (wxStaticText *)GetWidgetPtr("QueryHintMsg",this));
1828 return(widgetPtrsSet = !abort);
1830 } // CqueryDlg::SetWidgetPtrs
1833 void CqueryDlg::AppendToWhere(char *s
)
1835 wxString whereStr
= pQuerySqlWhereMtxt
->GetValue();
1837 pQuerySqlWhereMtxt
->SetValue(whereStr
);
1839 } // CqueryDlg::AppendToWhere()
1842 void CqueryDlg::ProcessAddBtn()
1844 qryOp oper
= (qryOp
) pQueryOperatorChoice
->GetSelection();
1846 // Verify that eveything is filled in correctly
1847 if (pQueryCol2Choice
->GetSelection() == 0) // "Value" is selected
1849 // Verify that value 1 is filled in
1850 if (strlen(pQueryValue1Txt
->GetValue()) == 0)
1853 pQueryValue1Txt
->SetFocus();
1856 // For the BETWEEN operator, value 2 must be filled in as well
1857 if (oper
== qryOpBETWEEN
&&
1858 strlen(pQueryValue2Txt
->GetValue()) == 0)
1861 pQueryValue2Txt
->SetFocus();
1866 // Build the expression and append it to the where clause window
1867 wxString s
= pQueryCol1Choice
->GetStringSelection();
1869 if (pQueryNotCheck
->GetValue() && (oper
!= qryOpEQ
))
1875 if (pQueryNotCheck
->GetValue()) // NOT box is checked
1904 int col1Idx
= pQueryCol1Choice
->GetSelection();
1907 if (colInf
[col1Idx
].sqlDataType
== SQL_VARCHAR
||
1908 oper
== qryOpBEGINS
||
1909 oper
== qryOpCONTAINS
||
1913 if (pQueryCol2Choice
->GetSelection()) // Column name
1914 s
+= pQueryCol2Choice
->GetStringSelection();
1915 else // Column 2 is a "value"
1919 if (oper
== qryOpCONTAINS
)
1921 s
+= pQueryValue1Txt
->GetValue();
1922 if (oper
== qryOpCONTAINS
|| oper
== qryOpBEGINS
)
1928 if (oper
== qryOpBETWEEN
)
1933 s
+= pQueryValue2Txt
->GetValue();
1938 AppendToWhere((char*) (const char*) s
);
1940 } // CqueryDlg::ProcessAddBtn()
1943 void CqueryDlg::ProcessCountBtn()
1945 if (!ValidateWhereClause())
1948 if (dbTable
== 0) // wxTable object needs to be created and opened
1950 if (!(dbTable
= new wxTable(pDB
, masterTableName
, 0)))
1952 wxMessageBox("Memory allocation failed creating a wxTable object.","Error...",wxOK
| wxICON_EXCLAMATION
);
1955 if (!dbTable
->Open())
1958 tStr
= "ODBC error during Open()\n\n";
1959 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1960 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1965 // Count() with WHERE clause
1966 dbTable
->where
= (char*) (const char*) pQuerySqlWhereMtxt
->GetValue();
1967 ULONG whereCnt
= dbTable
->Count();
1969 // Count() of all records in the table
1971 ULONG totalCnt
= dbTable
->Count();
1973 if (whereCnt
> 0 || totalCnt
== 0)
1976 tStr
.Printf("%lu of %lu records match the query criteria.",whereCnt
,totalCnt
);
1977 wxMessageBox(tStr
,"Notice...",wxOK
| wxICON_INFORMATION
);
1982 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
);
1983 wxMessageBox(tStr
,"Notice...",wxOK
| wxICON_INFORMATION
);
1986 // After a wxMessageBox, the focus does not necessarily return to the
1987 // window which was the focus when the message box popped up, so return
1988 // focus to the Query dialog for certain
1991 } // CqueryDlg::ProcessCountBtn()
1994 bool CqueryDlg::ValidateWhereClause()
1996 wxString where
= pQuerySqlWhereMtxt
->GetValue();
1998 if (where
.Freq('(') != where
.Freq(')'))
2000 wxMessageBox("There are mismatched parenthesis in the constructed where clause","Error...",wxOK
| wxICON_EXCLAMATION
);
2003 // After a wxMessageBox, the focus does not necessarily return to the
2004 // window which was the focus when the message box popped up, so return
2005 // focus to the Query dialog for certain
2010 } // CqueryDlg::ValidateWhereClause()