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"
44 #include <stdio.h> // Included strictly for reading the text file with the database parameters
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
49 extern DbList
*PtrBegDbList
; // from db.cpp, used in getting back error results from db connections
51 #include "dbtest.h" // Header file for this demonstration program
52 #include "listdb.h" // Code to support the "Lookup" button on the editor dialog
54 IMPLEMENT_APP(DatabaseDemoApp
)
56 extern char ListDB_Selection
[]; // Used to return the first column value for the selected line from the listDB routines
57 extern char ListDB_Selection2
[]; // Used to return the second column value for the selected line from the listDB routines
59 DatabaseDemoFrame
*DemoFrame
; // Pointer to the main frame
62 // This statement initializes the whole application and calls OnInit
63 DatabaseDemoApp DatabaseDemoApp
;
66 /* Pointer to the main database connection used in the program. This
67 * pointer would normally be used for doing things as database lookups
68 * for user login names and passwords, getting workstation settings, etc.
71 * For each database object created which uses this wxDB pointer
72 * connection to the database, when a CommitTrans() or RollBackTrans()
73 * will commit or rollback EVERY object which uses this wxDB pointer.
75 * To allow each table object (those derived from wxTable) to be
76 * individually committed or rolled back, you MUST use a different
77 * instance of wxDB in the constructor of the table. Doing so creates
78 * more overhead, and will use more database connections (some DBs have
79 * connection limits...), so use connections sparringly.
81 * It is recommended that one "main" database connection be created for
82 * the entire program to use for READ-ONLY database accesses, but for each
83 * table object which will do a CommitTrans() or RollbackTrans() that a
84 * new wxDB object be created and used for it.
90 * This function will return the exact string(s) from the database engine
91 * indicating all error conditions which have just occured during the
92 * last call to the database engine.
94 * This demo uses the returned string by displaying it in a wxMessageBox. The
95 * formatting therefore is not the greatest, but this is just a demo, not a
96 * finished product. :-) gt
98 * NOTE: The value returned by this function is for temporary use only and
99 * should be copied for long term use
101 char *GetExtendedDBErrorMsg(char *ErrFile
, int ErrLine
)
107 if (ErrFile
|| ErrLine
)
114 tStr
.Printf("%d",ErrLine
);
115 msg
+= tStr
.GetData();
120 msg
.Append ("\nODBC ERRORS\n");
123 // Scan through each database connection displaying
124 // any ODBC errors that have occured.
125 for (DbList
*pDbList
= PtrBegDbList
; pDbList
; pDbList
= pDbList
->PtrNext
)
127 // Skip over any free connections
130 // Display errors for this connection
131 for (int i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
133 if (pDbList
->PtrDb
->errorList
[i
])
135 msg
.Append(pDbList
->PtrDb
->errorList
[i
]);
136 if (strcmp(pDbList
->PtrDb
->errorList
[i
],"") != 0)
143 return (char*) (const char*) msg
;
144 } // GetExtendedDBErrorMsg
147 bool DatabaseDemoApp::OnInit()
149 // Create the main frame window
150 DemoFrame
= new DatabaseDemoFrame(NULL
, "wxWindows Database Demo", wxPoint(50, 50), wxSize(537, 480));
153 DemoFrame
->SetIcon(wxICON(db
));
156 wxMenu
*file_menu
= new wxMenu
;
157 file_menu
->Append(FILE_CREATE
, "&Create contact table");
158 file_menu
->Append(FILE_EXIT
, "E&xit");
160 wxMenu
*edit_menu
= new wxMenu
;
161 edit_menu
->Append(EDIT_PARAMETERS
, "&Parameters...");
163 wxMenu
*about_menu
= new wxMenu
;
164 about_menu
->Append(ABOUT_DEMO
, "&About");
166 wxMenuBar
*menu_bar
= new wxMenuBar
;
167 menu_bar
->Append(file_menu
, "&File");
168 menu_bar
->Append(edit_menu
, "&Edit");
169 menu_bar
->Append(about_menu
, "&About");
170 DemoFrame
->SetMenuBar(menu_bar
);
172 // Initialize the ODBC Environment for Database Operations
173 if (SQLAllocEnv(&DbConnectInf
.Henv
) != SQL_SUCCESS
)
175 wxMessageBox("A problem occured while trying to get a connection to the data source","DB CONNECTION ERROR",wxOK
| wxICON_EXCLAMATION
);
180 if ((paramFile
= fopen(paramFilename
, "r")) == NULL
)
183 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
);
184 wxMessageBox(tStr
,"File I/O Error...",wxOK
| wxICON_EXCLAMATION
);
185 DemoFrame
->BuildParameterDialog(NULL
);
186 if ((paramFile
= fopen(paramFilename
, "r")) == NULL
)
191 fgets(buffer
, sizeof(params
.ODBCSource
), paramFile
);
192 buffer
[strlen(buffer
)-1] = '\0';
193 strcpy(params
.ODBCSource
,buffer
);
195 fgets(buffer
, sizeof(params
.UserName
), paramFile
);
196 buffer
[strlen(buffer
)-1] = '\0';
197 strcpy(params
.UserName
,buffer
);
199 fgets(buffer
, sizeof(params
.Password
), paramFile
);
200 buffer
[strlen(buffer
)-1] = '\0';
201 strcpy(params
.Password
,buffer
);
205 // Connect to datasource
206 strcpy(DbConnectInf
.Dsn
, params
.ODBCSource
); // ODBC data source name (created with ODBC Administrator under Win95/NT)
207 strcpy(DbConnectInf
.Uid
, params
.UserName
); // database username - must already exist in the data source
208 strcpy(DbConnectInf
.AuthStr
, params
.Password
); // password database username
209 READONLY_DB
= GetDbConnection(&DbConnectInf
);
210 if (READONLY_DB
== 0)
212 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
);
213 DemoFrame
->BuildParameterDialog(NULL
);
214 strcpy(DbConnectInf
.Dsn
, "");
215 strcpy(DbConnectInf
.Uid
, "");
216 strcpy(DbConnectInf
.AuthStr
, "");
217 wxMessageBox("Now exiting program.\n\nRestart program to try any new settings.","Notice...",wxOK
| wxICON_INFORMATION
);
221 DemoFrame
->BuildEditorDialog();
224 DemoFrame
->Show(TRUE
);
227 } // DatabaseDemoApp::OnInit()
229 BEGIN_EVENT_TABLE(DatabaseDemoFrame
, wxFrame
)
230 EVT_MENU(FILE_CREATE
, DatabaseDemoFrame::OnCreate
)
231 EVT_MENU(FILE_EXIT
, DatabaseDemoFrame::OnExit
)
232 EVT_MENU(EDIT_PARAMETERS
, DatabaseDemoFrame::OnEditParameters
)
233 EVT_MENU(ABOUT_DEMO
, DatabaseDemoFrame::OnAbout
)
234 EVT_CLOSE(DatabaseDemoFrame::OnCloseWindow
)
237 // DatabaseDemoFrame constructor
238 DatabaseDemoFrame::DatabaseDemoFrame(wxFrame
*frame
, const wxString
& title
,
239 const wxPoint
& pos
, const wxSize
& size
):
240 wxFrame(frame
, -1, title
, pos
, size
)
242 // Put any code in necessary for initializing the main frame here
245 void DatabaseDemoFrame::OnCreate(wxCommandEvent
& event
)
250 void DatabaseDemoFrame::OnExit(wxCommandEvent
& event
)
255 void DatabaseDemoFrame::OnEditParameters(wxCommandEvent
& event
)
257 if ((pEditorDlg
->mode
!= mCreate
) && (pEditorDlg
->mode
!= mEdit
))
258 BuildParameterDialog(this);
260 wxMessageBox("Cannot change database parameters while creating or editing a record","Notice...",wxOK
| wxICON_INFORMATION
);
263 void DatabaseDemoFrame::OnAbout(wxCommandEvent
& event
)
265 wxMessageBox("wxWindows sample program for database classes\n\nContributed on 27 July 1998","About...",wxOK
| wxICON_INFORMATION
);
268 void DatabaseDemoFrame::OnCloseWindow(wxCloseEvent
& event
)
270 // Put any additional checking necessary to make certain it is alright
271 // to close the program here that is not done elsewhere
274 } // DatabaseDemoFrame::OnClose()
277 void DatabaseDemoFrame::CreateDataTable()
279 bool Ok
= (wxMessageBox("Any data currently residing in the table will be erased.\n\nAre you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
288 Ccontact
*Contact
= new Ccontact();
292 wxMessageBox("Error allocating memory for 'Ccontact'object.\n\nTable was not created.","Error...",wxOK
| wxICON_EXCLAMATION
);
296 if (!Contact
->CreateTable())
300 tStr
= "Error creating CONTACTS table.\nTable was not created.\n\n";
301 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
302 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
307 if (!Contact
->CreateIndexes())
311 tStr
= "Error creating CONTACTS indexes.\nIndexes will be unavailable.\n\n";
312 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
313 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
322 wxMessageBox("Table and index(es) were successfully created.","Notice...",wxOK
| wxICON_INFORMATION
);
323 } // DatabaseDemoFrame::CreateDataTable()
326 void DatabaseDemoFrame::BuildEditorDialog()
328 pEditorDlg
= new CeditorDlg(this);
330 wxMessageBox("Unable to create the editor dialog for some reason","Error...",wxOK
| wxICON_EXCLAMATION
);
331 } // DatabaseDemoFrame::BuildEditorDialog()
334 void DatabaseDemoFrame::BuildParameterDialog(wxWindow
*parent
)
336 pParamDlg
= new CparameterDlg(parent
);
339 wxMessageBox("Unable to create the parameter dialog for some reason","Error...",wxOK
| wxICON_EXCLAMATION
);
340 } // DatabaseDemoFrame::BuildParameterDialog()
344 * Constructor note: If no wxDB object is passed in, a new connection to the database
345 * is created for this instance of Ccontact. This can be a slow process depending
346 * on the database engine being used, and some database engines have a limit on the
347 * number of connections (either hard limits, or license restricted) so care should
348 * be used to use as few connections as is necessary.
349 * IMPORTANT: Objects which share a wxDB pointer are ALL acted upon whenever a member
350 * function of pDb is called (i.e. CommitTrans() or RollbackTrans(), so if modifying
351 * or creating a table objects which use the same pDb, know that all the objects
352 * will be committed or rolled back when any of the objects has this function call made.
354 Ccontact::Ccontact (wxDB
*pwxDB
) : wxTable(pwxDB
? pwxDB
: GetDbConnection(&DbConnectInf
),CONTACT_TABLE_NAME
,CONTACT_NO_COLS
)
356 // This is used to represent whether the database connection should be released
357 // when this instance of the object is deleted. If using the same connection
358 // for multiple instance of database objects, then the connection should only be
359 // released when the last database instance using the connection is deleted
364 } // Ccontact Constructor
367 void Ccontact::Initialize()
376 JoinDate
.year
= 1980;
382 JoinDate
.fraction
= 0;
383 NativeLanguage
= langENGLISH
;
387 } // Ccontact::Initialize
390 Ccontact::~Ccontact()
394 if (!FreeDbConnection(pDb
))
397 tStr
= "Unable to Free the Ccontact data table handle\n\n";
398 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
399 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
402 } // Ccontract destructor
406 * Handles setting up all the connections for the interface from the wxTable
407 * functions to interface to the data structure used to store records in
408 * memory, and for all the column definitions that define the table structure
410 void Ccontact::SetupColumns()
412 SetColDefs ( 0,"NAME", DB_DATA_TYPE_VARCHAR
, Name
, SQL_C_CHAR
, sizeof(Name
), TRUE
, TRUE
); // Primary index
413 SetColDefs ( 1,"ADDRESS1", DB_DATA_TYPE_VARCHAR
, Addr1
, SQL_C_CHAR
, sizeof(Addr1
), FALSE
,TRUE
);
414 SetColDefs ( 2,"ADDRESS2", DB_DATA_TYPE_VARCHAR
, Addr2
, SQL_C_CHAR
, sizeof(Addr2
), FALSE
,TRUE
);
415 SetColDefs ( 3,"CITY", DB_DATA_TYPE_VARCHAR
, City
, SQL_C_CHAR
, sizeof(City
), FALSE
,TRUE
);
416 SetColDefs ( 4,"STATE", DB_DATA_TYPE_VARCHAR
, State
, SQL_C_CHAR
, sizeof(State
), FALSE
,TRUE
);
417 SetColDefs ( 5,"POSTAL_CODE", DB_DATA_TYPE_VARCHAR
, PostalCode
, SQL_C_CHAR
, sizeof(PostalCode
), FALSE
,TRUE
);
418 SetColDefs ( 6,"COUNTRY", DB_DATA_TYPE_VARCHAR
, Country
, SQL_C_CHAR
, sizeof(Country
), FALSE
,TRUE
);
419 SetColDefs ( 7,"JOIN_DATE", DB_DATA_TYPE_DATE
, &JoinDate
, SQL_C_TIMESTAMP
, sizeof(JoinDate
), FALSE
,TRUE
);
420 SetColDefs ( 8,"NATIVE_LANGUAGE", DB_DATA_TYPE_INTEGER
, &NativeLanguage
, SQL_C_ENUM
, sizeof(NativeLanguage
), FALSE
,TRUE
);
421 SetColDefs ( 9,"IS_DEVELOPER", DB_DATA_TYPE_INTEGER
, &IsDeveloper
, SQL_C_BOOLEAN
, sizeof(bool), FALSE
,TRUE
);
422 SetColDefs (10,"CONTRIBUTIONS", DB_DATA_TYPE_INTEGER
, &Contributions
, SQL_C_USHORT
, sizeof(Contributions
), FALSE
,TRUE
);
423 SetColDefs (11,"LINES_OF_CODE", DB_DATA_TYPE_INTEGER
, &LinesOfCode
, SQL_C_ULONG
, sizeof(LinesOfCode
), FALSE
,TRUE
);
424 } // Ccontact::SetupColumns
427 bool Ccontact::CreateIndexes(void)
429 // This index could easily be accomplished with an "orderBy" clause,
430 // but is done to show how to construct a non-primary index.
436 strcpy(idxDef
[0].ColName
, "IS_DEVELOPER");
437 idxDef
[0].Ascending
= TRUE
;
439 strcpy(idxDef
[1].ColName
, "NAME");
440 idxDef
[1].Ascending
= TRUE
;
442 indexName
= CONTACT_TABLE_NAME
;
443 indexName
+= "_IDX1";
444 Ok
= CreateIndex((char*) (const char*) indexName
, TRUE
, 2, idxDef
);
447 } // Ccontact::CreateIndexes()
451 * Having a function to do a query on the primary key (and possibly others) is
452 * very efficient and tighter coding so that it is available where ever the object
453 * is. Great for use with multiple tables when not using views or outer joins
455 bool Ccontact::FetchByName(char *name
)
457 whereStr
.Printf("NAME = '%s'",name
);
458 where
= (char*) (const char*) this->whereStr
;
467 } // Ccontact::FetchByName()
472 * ************* DIALOGS ***************
477 /* CeditorDlg constructor
479 * Creates the dialog used for creating/editing/deleting/copying a Ccontact object.
480 * This dialog actually is drawn in the main frame of the program
482 * An instance of Ccontact is created - "Contact" - which is used to hold the Ccontact
483 * object that is currently being worked with.
485 CeditorDlg::CeditorDlg(wxWindow
*parent
) : wxPanel (parent
, 1, 1, 460, 455)
487 // Since the ::OnCommand() function is overridden, this prevents the widget
488 // detection in ::OnCommand() until all widgets have been initialized to prevent
489 // uninitialized pointers from crashing the program
490 widgetPtrsSet
= FALSE
;
492 // Create the data structure and a new database connection.
493 // (As there is not a pDb being passed in the constructor, a new database
494 // connection is created)
495 Contact
= new Ccontact();
499 wxMessageBox("Unable to instantiate an instance of Ccontact","Error...",wxOK
| wxICON_EXCLAMATION
);
503 // Check if the table exists or not. If it doesn't, ask the user if they want to
504 // create the table. Continue trying to create the table until it exists, or user aborts
505 while (!Contact
->pDb
->TableExists((char *)CONTACT_TABLE_NAME
))
508 tStr
.Printf("Unable to open the table '%s'.\n\nTable may need to be created...?\n\n",CONTACT_TABLE_NAME
);
509 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
510 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
512 bool createTable
= (wxMessageBox("Do you wish to try to create/clear the CONTACTS table?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
522 DemoFrame
->CreateDataTable();
525 // Tables must be "opened" before anything other than creating/deleting table can be done
526 if (!Contact
->Open())
528 // Table does exist, there was some problem opening it. Currently this should
529 // never fail, except in the case of the table not exisiting. Open() basically
530 // only sets up variable/pointer values, other than checking for table existence.
531 if (Contact
->pDb
->TableExists((char *)CONTACT_TABLE_NAME
))
534 tStr
.Printf("Unable to open the table '%s'.\n\n",CONTACT_TABLE_NAME
);
535 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
536 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
546 wxStaticBox
*FunctionGrp
= new wxStaticBox(this, EDITOR_DIALOG_FN_GROUP
, "", wxPoint(15, 1), wxSize(497, 69), 0, "FunctionGrp");
547 wxStaticBox
*SearchGrp
= new wxStaticBox(this, EDITOR_DIALOG_SEARCH_GROUP
, "", wxPoint(417, 1), wxSize(95, 242), 0, "SearchGrp");
549 pCreateBtn
= new wxButton(this, EDITOR_DIALOG_CREATE
, "&Create", wxPoint(25, 21), wxSize(70, 35), 0, wxDefaultValidator
, "CreateBtn");
550 pEditBtn
= new wxButton(this, EDITOR_DIALOG_EDIT
, "&Edit", wxPoint(102, 21), wxSize(70, 35), 0, wxDefaultValidator
, "EditBtn");
551 pDeleteBtn
= new wxButton(this, EDITOR_DIALOG_DELETE
, "&Delete", wxPoint(179, 21), wxSize(70, 35), 0, wxDefaultValidator
, "DeleteBtn");
552 pCopyBtn
= new wxButton(this, EDITOR_DIALOG_COPY
, "Cop&y", wxPoint(256, 21), wxSize(70, 35), 0, wxDefaultValidator
, "CopyBtn");
553 pSaveBtn
= new wxButton(this, EDITOR_DIALOG_SAVE
, "&Save", wxPoint(333, 21), wxSize(70, 35), 0, wxDefaultValidator
, "SaveBtn");
554 pCancelBtn
= new wxButton(this, EDITOR_DIALOG_CANCEL
, "C&ancel", wxPoint(430, 21), wxSize(70, 35), 0, wxDefaultValidator
, "CancelBtn");
556 pPrevBtn
= new wxButton(this, EDITOR_DIALOG_PREV
, "<< &Prev", wxPoint(430, 81), wxSize(70, 35), 0, wxDefaultValidator
, "PrevBtn");
557 pNextBtn
= new wxButton(this, EDITOR_DIALOG_NEXT
, "&Next >>", wxPoint(430, 121), wxSize(70, 35), 0, wxDefaultValidator
, "NextBtn");
558 pQueryBtn
= new wxButton(this, EDITOR_DIALOG_QUERY
, "&Query", wxPoint(430, 161), wxSize(70, 35), 0, wxDefaultValidator
, "QueryBtn");
559 pResetBtn
= new wxButton(this, EDITOR_DIALOG_RESET
, "&Reset", wxPoint(430, 200), wxSize(70, 35), 0, wxDefaultValidator
, "ResetBtn");
561 pNameMsg
= new wxStaticText(this, EDITOR_DIALOG_NAME_MSG
, "Name:", wxPoint(17, 80), wxSize(-1, -1), 0, "NameMsg");
562 pNameTxt
= new wxTextCtrl(this, EDITOR_DIALOG_NAME_TEXT
, "", wxPoint(17, 97), wxSize(308, 25), 0, wxDefaultValidator
, "NameTxt");
563 pNameListBtn
= new wxButton(this, EDITOR_DIALOG_LOOKUP
, "&Lookup", wxPoint(333, 99), wxSize(70, 24), 0, wxDefaultValidator
, "LookupBtn");
565 pAddress1Msg
= new wxStaticText(this, EDITOR_DIALOG_ADDRESS1_MSG
, "Address:", wxPoint(17, 130), wxSize(-1, -1), 0, "Address1Msg");
566 pAddress1Txt
= new wxTextCtrl(this, EDITOR_DIALOG_ADDRESS2_TEXT
, "", wxPoint(17, 147), wxSize(308, 25), 0, wxDefaultValidator
, "Address1Txt");
568 pAddress2Msg
= new wxStaticText(this, EDITOR_DIALOG_ADDRESS2_MSG
, "Address:", wxPoint(17, 180), wxSize(-1, -1), 0, "Address2Msg");
569 pAddress2Txt
= new wxTextCtrl(this, EDITOR_DIALOG_ADDRESS2_TEXT
, "", wxPoint(17, 197), wxSize(308, 25), 0, wxDefaultValidator
, "Address2Txt");
571 pCityMsg
= new wxStaticText(this, EDITOR_DIALOG_CITY_MSG
, "City:", wxPoint(17, 230), wxSize(-1, -1), 0, "CityMsg");
572 pCityTxt
= new wxTextCtrl(this, EDITOR_DIALOG_CITY_TEXT
, "", wxPoint(17, 247), wxSize(225, 25), 0, wxDefaultValidator
, "CityTxt");
574 pStateMsg
= new wxStaticText(this, EDITOR_DIALOG_STATE_MSG
, "State:", wxPoint(250, 230), wxSize(-1, -1), 0, "StateMsg");
575 pStateTxt
= new wxTextCtrl(this, EDITOR_DIALOG_STATE_TEXT
, "", wxPoint(250, 247), wxSize(153, 25), 0, wxDefaultValidator
, "StateTxt");
577 pCountryMsg
= new wxStaticText(this, EDITOR_DIALOG_COUNTRY_MSG
, "Country:", wxPoint(17, 280), wxSize(-1, -1), 0, "CountryMsg");
578 pCountryTxt
= new wxTextCtrl(this, EDITOR_DIALOG_COUNTRY_TEXT
, "", wxPoint(17, 297), wxSize(225, 25), 0, wxDefaultValidator
, "CountryTxt");
580 pPostalCodeMsg
= new wxStaticText(this, EDITOR_DIALOG_POSTAL_MSG
, "Postal Code:", wxPoint(250, 280), wxSize(-1, -1), 0, "PostalCodeMsg");
581 pPostalCodeTxt
= new wxTextCtrl(this, EDITOR_DIALOG_POSTAL_TEXT
, "", wxPoint(250, 297), wxSize(153, 25), 0, wxDefaultValidator
, "PostalCodeTxt");
583 wxString choice_strings
[5];
584 choice_strings
[0] = "English";
585 choice_strings
[1] = "French";
586 choice_strings
[2] = "German";
587 choice_strings
[3] = "Spanish";
588 choice_strings
[4] = "Other";
589 pNativeLangChoice
= new wxChoice(this, EDITOR_DIALOG_LANG_CHOICE
, wxPoint(17, 346), wxSize(277, -1), 5, choice_strings
);
590 pNativeLangMsg
= new wxStaticText(this, EDITOR_DIALOG_LANG_MSG
, "Native language:", wxPoint(17, 330), wxSize(-1, -1), 0, "NativeLangMsg");
592 wxString radio_strings
[2];
593 radio_strings
[0] = "No";
594 radio_strings
[1] = "Yes";
595 pDeveloperRadio
= new wxRadioBox(this,EDITOR_DIALOG_DEVELOPER
,"Developer:",wxPoint(303,330),wxSize(-1,-1),2,radio_strings
,2,wxHORIZONTAL
);
597 pJoinDateMsg
= new wxStaticText(this, EDITOR_DIALOG_JOIN_MSG
, "Date joined:", wxPoint(17, 380), wxSize(-1, -1), 0, "JoinDateMsg");
598 pJoinDateTxt
= new wxTextCtrl(this, EDITOR_DIALOG_JOIN_TEXT
, "", wxPoint(17, 397), wxSize(150, 25), 0, wxDefaultValidator
, "JoinDateTxt");
600 pContribMsg
= new wxStaticText(this, EDITOR_DIALOG_CONTRIB_MSG
, "Contributions:", wxPoint(175, 380), wxSize(-1, -1), 0, "ContribMsg");
601 pContribTxt
= new wxTextCtrl(this, EDITOR_DIALOG_CONTRIB_TEXT
, "", wxPoint(175, 397), wxSize(120, 25), 0, wxDefaultValidator
, "ContribTxt");
603 pLinesMsg
= new wxStaticText(this, EDITOR_DIALOG_LINES_MSG
, "Lines of code:", wxPoint(303, 380), wxSize(-1, -1), 0, "LinesMsg");
604 pLinesTxt
= new wxTextCtrl(this, EDITOR_DIALOG_LINES_TEXT
, "", wxPoint(303, 397), wxSize(100, 25), 0, wxDefaultValidator
, "LinesTxt");
606 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
607 // handle all widget processing
608 widgetPtrsSet
= TRUE
;
610 // Setup the orderBy and where clauses to return back a single record as the result set,
611 // as there will only be one record being shown on the dialog at a time, this optimizes
612 // network traffic by only returning a one row result
614 Contact
->orderBy
= "NAME"; // field name to sort by
616 // The wxString "whereStr" is not a member of the wxTable object, it is a member variable
617 // specifically in the Ccontact class. It is used here for simpler construction of a varying
618 // length string, and then after the string is built, the wxTable member variable "where" is
619 // assigned the pointer to the constructed string.
621 // The constructed where clause below has a sub-query within it "SELECT MIN(NAME) FROM %s"
622 // to achieve a single row (in this case the first name in alphabetical order).
623 Contact
->whereStr
.Printf("NAME = (SELECT MIN(NAME) FROM %s)",Contact
->tableName
);
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
;
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())
634 tStr
= "ODBC error during Query()\n\n";
635 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
636 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
637 GetParent()->Close();
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();
652 } // CeditorDlg constructor
655 bool CeditorDlg::OnClose()
658 if ((mode
!= mCreate
) && (mode
!= mEdit
))
666 wxMessageBox("Must finish processing the current record being created/modified before exiting","Notice...",wxOK
| wxICON_INFORMATION
);
669 } // CeditorDlg::OnClose()
672 void CeditorDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
676 widgetName
= win
.GetName();
681 if (widgetName
== pCreateBtn
->GetName())
683 Contact
->Initialize();
686 pNameTxt
->SetValue("");
687 pNameTxt
->SetFocus();
691 if (widgetName
== pEditBtn
->GetName())
693 saveName
= Contact
->Name
;
695 pNameTxt
->SetFocus();
699 if (widgetName
== pCopyBtn
->GetName())
702 pNameTxt
->SetValue("");
703 pNameTxt
->SetFocus();
707 if (widgetName
== pDeleteBtn
->GetName())
709 bool Ok
= (wxMessageBox("Are you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
714 if (Ok
&& Contact
->Delete())
716 // NOTE: Deletions are not finalized until a CommitTrans() is performed.
717 // If the commit were not performed, the program will continue to
718 // show the table contents as if they were deleted until this instance
719 // of Ccontact is deleted. If the Commit wasn't performed, the
720 // database will automatically Rollback the changes when the database
721 // connection is terminated
722 Contact
->pDb
->CommitTrans();
724 // Try to get the row that followed the just deleted row in the orderBy sequence
727 // There was now row (in sequence) after the just deleted row, so get the
728 // row which preceded the just deleted row
731 // There are now no rows remaining, so clear the dialog widgets
732 Contact
->Initialize();
736 SetMode(mode
); // force reset of button enable/disable
740 Contact
->pDb
->RollbackTrans();
746 if (widgetName
== pSaveBtn
->GetName())
752 if (widgetName
== pCancelBtn
->GetName())
754 bool Ok
= (wxMessageBox("Are you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
759 if (!strcmp((const char*) saveName
,""))
761 Contact
->Initialize();
768 // Requery previous record
769 if (Contact
->FetchByName((char*) (const char*) saveName
))
777 // Previous record not available, retrieve first record in table
778 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
779 Contact
->whereStr
+= Contact
->tableName
;
780 Contact
->whereStr
+= ")";
782 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
783 if (!Contact
->Query())
786 tStr
= "ODBC error during Query()\n\n";
787 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
788 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
792 if (Contact
->GetNext()) // Successfully read first record
798 // No contacts are available, clear dialog
799 Contact
->Initialize();
805 if (widgetName
== pPrevBtn
->GetName())
812 if (widgetName
== pNextBtn
->GetName())
819 if (widgetName
== pQueryBtn
->GetName())
821 // Display the query dialog box
822 char qryWhere
[DB_MAX_WHERE_CLAUSE_LEN
+1];
823 strcpy(qryWhere
, (const char*) Contact
->qryWhereStr
);
824 char *tblName
[] = {(char *)CONTACT_TABLE_NAME
, 0};
825 new CqueryDlg(GetParent(), Contact
->pDb
, tblName
, qryWhere
);
827 // Query the first record in the new record set and
828 // display it, if the query string has changed.
829 if (strcmp(qryWhere
, (const char*) Contact
->qryWhereStr
))
831 Contact
->orderBy
= "NAME";
832 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
833 Contact
->whereStr
+= CONTACT_TABLE_NAME
;
834 // Append the query where string (if there is one)
835 Contact
->qryWhereStr
= qryWhere
;
836 if (strlen(qryWhere
))
838 Contact
->whereStr
+= " WHERE ";
839 Contact
->whereStr
+= Contact
->qryWhereStr
;
841 // Close the expression with a right paren
842 Contact
->whereStr
+= ")";
844 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
845 if (!Contact
->Query())
848 tStr
= "ODBC error during Query()\n\n";
849 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
850 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
853 // Display the first record from the query set
854 if (!Contact
->GetNext())
855 Contact
->Initialize();
859 // Enable/Disable the reset button
860 pResetBtn
->Enable(!Contact
->qryWhereStr
.IsEmpty());
866 if (widgetName
== pResetBtn
->GetName())
868 // Clear the additional where criteria established by the query feature
869 Contact
->qryWhereStr
= "";
871 // Query the first record in the table
872 Contact
->orderBy
= "NAME";
873 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
874 Contact
->whereStr
+= CONTACT_TABLE_NAME
;
875 Contact
->whereStr
+= ")";
876 Contact
->where
= (char*) (const char*) Contact
->whereStr
;
877 if (!Contact
->Query())
880 tStr
= "ODBC error during Query()\n\n";
881 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
882 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
885 if (!Contact
->GetNext())
886 Contact
->Initialize();
888 pResetBtn
->Enable(FALSE
);
894 if (widgetName
== pNameListBtn
->GetName())
896 new ClookUpDlg(/* wxWindow *parent */ this,
897 /* char *windowTitle */ "Select contact name",
898 /* char *tableName */ (char *) CONTACT_TABLE_NAME
,
899 /* char *dispCol1 */ "NAME",
900 /* char *dispCol2 */ "JOIN_DATE",
901 /* char *where */ "",
902 /* char *orderBy */ "NAME",
903 /* bool distinctValues */ TRUE
);
905 if (ListDB_Selection
&& strlen(ListDB_Selection
))
907 wxString w
= "NAME = '";
908 w
+= ListDB_Selection
;
910 GetRec((char*) (const char*) w
);
916 } // CeditorDlg::OnCommand()
919 void CeditorDlg::FieldsEditable()
921 pNameTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
922 pAddress1Txt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
923 pAddress2Txt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
924 pCityTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
925 pStateTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
926 pPostalCodeTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
927 pCountryTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
929 pJoinDateTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
930 pContribTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
931 pLinesTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
932 pNativeLangChoice
->Enable((mode
== mCreate
) || (mode
== mEdit
));
933 pDeveloperRadio
->Enable((mode
== mCreate
) || (mode
== mEdit
));
935 } // CeditorDlg::FieldsEditable()
938 void CeditorDlg::SetMode(enum DialogModes m
)
959 pCreateBtn
->Enable( !edit
);
960 pEditBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
961 pDeleteBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
962 pCopyBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
963 pSaveBtn
->Enable( edit
);
964 pCancelBtn
->Enable( edit
);
965 pPrevBtn
->Enable( !edit
);
966 pNextBtn
->Enable( !edit
);
967 pQueryBtn
->Enable( !edit
);
968 pResetBtn
->Enable( !edit
&& !Contact
->qryWhereStr
.IsEmpty() );
969 pNameListBtn
->Enable( !edit
);
973 } // CeditorDlg::SetMode()
976 bool CeditorDlg::PutData()
980 pNameTxt
->SetValue(Contact
->Name
);
981 pAddress1Txt
->SetValue(Contact
->Addr1
);
982 pAddress2Txt
->SetValue(Contact
->Addr2
);
983 pCityTxt
->SetValue(Contact
->City
);
984 pStateTxt
->SetValue(Contact
->State
);
985 pCountryTxt
->SetValue(Contact
->Country
);
986 pPostalCodeTxt
->SetValue(Contact
->PostalCode
);
988 tStr
.Printf("%d/%d/%d",Contact
->JoinDate
.month
,Contact
->JoinDate
.day
,Contact
->JoinDate
.year
);
989 pJoinDateTxt
->SetValue(tStr
);
991 tStr
.Printf("%d",Contact
->Contributions
);
992 pContribTxt
->SetValue(tStr
);
994 tStr
.Printf("%lu",Contact
->LinesOfCode
);
995 pLinesTxt
->SetValue(tStr
);
997 pNativeLangChoice
->SetSelection(Contact
->NativeLanguage
);
999 pDeveloperRadio
->SetSelection(Contact
->IsDeveloper
);
1002 } // Ceditor::PutData()
1006 * Reads the data out of all the widgets on the dialog. Some data evaluation is done
1007 * to ensure that there is a name entered and that the date field is valid.
1009 * A return value of TRUE means that valid data was retrieved from the dialog, otherwise
1010 * invalid data was found (and a message was displayed telling the user what to fix), and
1011 * the data was not placed into the appropraite fields of Ccontact
1013 bool CeditorDlg::GetData()
1015 // Validate that the data currently entered into the widgets is valid data
1018 tStr
= pNameTxt
->GetValue();
1019 if (!strcmp((const char*) tStr
,""))
1021 wxMessageBox("A name is required for entry into the contact table","Notice...",wxOK
| wxICON_INFORMATION
);
1025 bool invalid
= FALSE
;
1029 tStr
= pJoinDateTxt
->GetValue();
1030 if (tStr
.Freq('/') != 2)
1033 // Find the month, day, and year tokens
1036 first
= tStr
.First('/');
1037 second
= tStr
.Last('/');
1039 mm
= atoi(tStr
.SubString(0,first
));
1040 dd
= atoi(tStr
.SubString(first
+1,second
));
1041 yyyy
= atoi(tStr
.SubString(second
+1,tStr
.Length()-1));
1043 invalid
= !(mm
&& dd
&& yyyy
);
1046 // Force Year 2000 compliance
1047 if (!invalid
&& (yyyy
< 1000))
1050 // Check the token ranges for validity
1055 else if ((mm
< 1) || (mm
> 12))
1063 int days
[12] = {31,28,31,30,31,30,
1065 if (dd
> days
[mm
-1])
1068 if ((dd
== 29) && (mm
== 2))
1070 if (((yyyy
% 4) == 0) && (((yyyy
% 100) != 0) || ((yyyy
% 400) == 0)))
1080 Contact
->JoinDate
.month
= mm
;
1081 Contact
->JoinDate
.day
= dd
;
1082 Contact
->JoinDate
.year
= yyyy
;
1086 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
);
1090 tStr
= pNameTxt
->GetValue();
1091 strcpy(Contact
->Name
,(const char*) tStr
);
1092 strcpy(Contact
->Addr1
,pAddress1Txt
->GetValue());
1093 strcpy(Contact
->Addr2
,pAddress2Txt
->GetValue());
1094 strcpy(Contact
->City
,pCityTxt
->GetValue());
1095 strcpy(Contact
->State
,pStateTxt
->GetValue());
1096 strcpy(Contact
->Country
,pCountryTxt
->GetValue());
1097 strcpy(Contact
->PostalCode
,pPostalCodeTxt
->GetValue());
1099 Contact
->Contributions
= atoi(pContribTxt
->GetValue());
1100 Contact
->LinesOfCode
= atol(pLinesTxt
->GetValue());
1102 Contact
->NativeLanguage
= (enum Language
) pNativeLangChoice
->GetSelection();
1103 Contact
->IsDeveloper
= (bool) pDeveloperRadio
->GetSelection();
1106 } // CeditorDlg::GetData()
1110 * Retrieve data from the dialog, verify the validity of the data, and if it is valid,
1111 * try to insert/update the data to the table based on the current 'mode' the dialog
1114 * A return value of TRUE means the insert/update was completed successfully, a return
1115 * value of FALSE means that Save() failed. If returning FALSE, then this function
1116 * has displayed a detailed error message for the user.
1118 bool CeditorDlg::Save()
1120 bool failed
= FALSE
;
1122 // Read the data in the widgets of the dialog to get the user's data
1126 // Perform any other required validations necessary before saving
1131 wxBeginBusyCursor();
1133 if (mode
== mCreate
)
1135 RETCODE result
= Contact
->Insert();
1137 failed
= (result
!= DB_SUCCESS
);
1140 // Some errors may be expected, like a duplicate key, so handle those instances with
1141 // specific error messages.
1142 if (result
== DB_ERR_INTEGRITY_CONSTRAINT_VIOL
)
1145 tStr
= "A duplicate key value already exists in the table.\nUnable to save record\n\n";
1146 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1147 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1151 // Some other unexpexted error occurred
1153 tStr
= "Database insert failed\n\n";
1154 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1155 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1159 else // mode == mEdit
1161 if (!Contact
->Update())
1164 tStr
= "Database update failed\n\n";
1165 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1166 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1173 Contact
->pDb
->CommitTrans();
1174 SetMode(mView
); // Sets the dialog mode back to viewing after save is successful
1177 Contact
->pDb
->RollbackTrans();
1183 } // CeditorDlg::Save()
1187 * Where this program is only showing a single row at a time in the dialog,
1188 * a special where clause must be built to find just the single row which,
1189 * in sequence, would follow the currently displayed row.
1191 bool CeditorDlg::GetNextRec()
1195 w
= "NAME = (SELECT MIN(NAME) FROM ";
1196 w
+= Contact
->tableName
;
1197 w
+= " WHERE NAME > '";
1201 // If a query where string is currently set, append that criteria
1202 if (!Contact
->qryWhereStr
.IsEmpty())
1205 w
+= Contact
->qryWhereStr
;
1211 return(GetRec((char*) (const char*) w
));
1213 } // CeditorDlg::GetNextRec()
1217 * Where this program is only showing a single row at a time in the dialog,
1218 * a special where clause must be built to find just the single row which,
1219 * in sequence, would precede the currently displayed row.
1221 bool CeditorDlg::GetPrevRec()
1225 w
= "NAME = (SELECT MAX(NAME) FROM ";
1226 w
+= Contact
->tableName
;
1227 w
+= " WHERE NAME < '";
1231 // If a query where string is currently set, append that criteria
1232 if (!Contact
->qryWhereStr
.IsEmpty())
1235 w
+= Contact
->qryWhereStr
;
1241 return(GetRec((char*) (const char*)w
));
1243 } // CeditorDlg::GetPrevRec()
1247 * This function is here to avoid duplicating this same code in both the
1248 * GetPrevRec() and GetNextRec() functions
1250 bool CeditorDlg::GetRec(char *whereStr
)
1252 Contact
->where
= whereStr
;
1253 Contact
->orderBy
= "NAME";
1255 if (!Contact
->Query())
1258 tStr
= "ODBC error during Query()\n\n";
1259 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1260 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1265 if (Contact
->GetNext())
1272 } // CeditorDlg::GetRec()
1277 * CparameterDlg constructor
1279 CparameterDlg::CparameterDlg(wxWindow
*parent
) : wxDialog (parent
, PARAMETER_DIALOG
, "ODBC parameter settings", wxPoint(-1, -1), wxSize(400, 275))
1281 // Since the ::OnCommand() function is overridden, this prevents the widget
1282 // detection in ::OnCommand() until all widgets have been initialized to prevent
1283 // uninitialized pointers from crashing the program
1284 widgetPtrsSet
= FALSE
;
1286 pParamODBCSourceMsg
= new wxStaticText(this, PARAMETER_DIALOG_SOURCE_MSG
, "ODBC data sources:", wxPoint(10, 10), wxSize(-1, -1), 0, "ParamODBCSourceMsg");
1287 pParamODBCSourceList
= new wxListBox(this, PARAMETER_DIALOG_SOURCE_LISTBOX
, wxPoint(10, 29), wxSize(285, 150), 0, 0, wxLB_SINGLE
|wxLB_ALWAYS_SB
, wxDefaultValidator
, "ParamODBCSourceList");
1289 pParamUserNameMsg
= new wxStaticText(this, PARAMETER_DIALOG_NAME_MSG
, "Database user name:", wxPoint(10, 193), wxSize(-1, -1), 0, "ParamUserNameMsg");
1290 pParamUserNameTxt
= new wxTextCtrl(this, PARAMETER_DIALOG_NAME_TEXT
, "", wxPoint(10, 209), wxSize(140, 25), 0, wxDefaultValidator
, "ParamUserNameTxt");
1292 pParamPasswordMsg
= new wxStaticText(this, PARAMETER_DIALOG_PASSWORD_MSG
, "Password:", wxPoint(156, 193), wxSize(-1, -1), 0, "ParamPasswordMsg");
1293 pParamPasswordTxt
= new wxTextCtrl(this, PARAMETER_DIALOG_PASSWORD_TEXT
, "", wxPoint(156, 209), wxSize(140, 25), 0, wxDefaultValidator
, "ParamPasswordTxt");
1295 pParamSaveBtn
= new wxButton(this, PARAMETER_DIALOG_SAVE
, "&Save", wxPoint(310, 21), wxSize(70, 35), 0, wxDefaultValidator
, "ParamSaveBtn");
1296 pParamCancelBtn
= new wxButton(this, PARAMETER_DIALOG_CANCEL
, "C&ancel", wxPoint(310, 66), wxSize(70, 35), 0, wxDefaultValidator
, "ParamCancelBtn");
1298 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
1299 // handle all widget processing
1300 widgetPtrsSet
= TRUE
;
1303 savedParamSettings
= DatabaseDemoApp
.params
;
1308 } // CparameterDlg constructor
1311 bool CparameterDlg::OnClose()
1313 // Put any additional checking necessary to make certain it is alright
1314 // to close the program here that is not done elsewhere
1317 bool Ok
= (wxMessageBox("No changes have been saved.\n\nAre you sure you wish exit the parameter screen?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
1322 DatabaseDemoApp
.params
= savedParamSettings
;
1325 if (GetParent() != NULL
)
1326 GetParent()->SetFocus();
1328 } // Cparameter::OnClose()
1331 void CparameterDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
1333 wxString widgetName
;
1335 widgetName
= win
.GetName();
1340 if (widgetName
== pParamSaveBtn
->GetName())
1345 tStr
= "Database parameters have been saved.";
1346 if (GetParent() != NULL
) // The parameter dialog was not called during startup due to a missing cfg file
1347 tStr
+= "\nNew parameters will take effect the next time the program is started.";
1348 wxMessageBox(tStr
,"Notice...",wxOK
| wxICON_INFORMATION
);
1355 if (widgetName
== pParamCancelBtn
->GetName())
1360 } // CparameterDlg::OnCommand()
1363 bool CparameterDlg::PutData()
1365 // Fill the data source list box
1366 FillDataSourceList();
1368 // Fill in the fields from the params object
1369 pParamODBCSourceList
->SetStringSelection(DatabaseDemoApp
.params
.ODBCSource
);
1370 pParamUserNameTxt
->SetValue(DatabaseDemoApp
.params
.UserName
);
1371 pParamPasswordTxt
->SetValue(DatabaseDemoApp
.params
.Password
);
1373 } // CparameterDlg::PutData()
1376 bool CparameterDlg::GetData()
1379 if (pParamODBCSourceList
->GetStringSelection())
1381 tStr
= pParamODBCSourceList
->GetStringSelection();
1382 if (tStr
.Length() > (sizeof(DatabaseDemoApp
.params
.ODBCSource
)-1))
1385 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());
1386 wxMessageBox(errmsg
,"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1389 strcpy(DatabaseDemoApp
.params
.ODBCSource
, tStr
);
1394 tStr
= pParamUserNameTxt
->GetValue();
1395 if (tStr
.Length() > (sizeof(DatabaseDemoApp
.params
.UserName
)-1))
1398 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());
1399 wxMessageBox(errmsg
,"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1402 strcpy(DatabaseDemoApp
.params
.UserName
, tStr
);
1404 tStr
= pParamPasswordTxt
->GetValue();
1405 if (tStr
.Length() > (sizeof(DatabaseDemoApp
.params
.Password
)-1))
1408 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());
1409 wxMessageBox(errmsg
,"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1412 strcpy(DatabaseDemoApp
.params
.Password
,tStr
);
1414 } // CparameterDlg::GetData()
1417 bool CparameterDlg::Save()
1419 Cparameters saveParams
= DatabaseDemoApp
.params
;
1422 DatabaseDemoApp
.params
= saveParams
;
1427 if ((paramFile
= fopen(paramFilename
, "wt")) == NULL
)
1430 tStr
.Printf("Unable to write/overwrite '%s'.",paramFilename
);
1431 wxMessageBox(tStr
,"File I/O Error...",wxOK
| wxICON_EXCLAMATION
);
1435 fputs(DatabaseDemoApp
.params
.ODBCSource
, paramFile
);
1436 fputc('\n', paramFile
);
1437 fputs(DatabaseDemoApp
.params
.UserName
, paramFile
);
1438 fputc('\n', paramFile
);
1439 fputs(DatabaseDemoApp
.params
.Password
, paramFile
);
1440 fputc('\n', paramFile
);
1444 } // CparameterDlg::Save()
1447 void CparameterDlg::FillDataSourceList()
1449 char Dsn
[SQL_MAX_DSN_LENGTH
+ 1];
1451 wxStringList strList
;
1453 while(GetDataSource(DbConnectInf
.Henv
, Dsn
, SQL_MAX_DSN_LENGTH
+1, DsDesc
, 255))
1458 char **p
= strList
.ListToArray();
1460 for (int i
= 0; strlen(p
[i
]); i
++)
1461 pParamODBCSourceList
->Append(p
[i
]);
1462 } // CparameterDlg::CparameterDlg::FillDataSourceList()
1465 // CqueryDlg() constructor
1466 CqueryDlg::CqueryDlg(wxWindow
*parent
, wxDB
*pDb
, char *tblName
[], char *pWhereArg
) : wxDialog (parent
, QUERY_DIALOG
, "Query", wxPoint(-1, -1), wxSize(480, 360))
1468 wxBeginBusyCursor();
1472 masterTableName
= tblName
[0];
1473 widgetPtrsSet
= FALSE
;
1476 // Initialize the WHERE clause from the string passed in
1477 pWhere
= pWhereArg
; // Save a pointer to the output buffer
1478 if (strlen(pWhere
) > DB_MAX_WHERE_CLAUSE_LEN
) // Check the length of the buffer passed in
1481 s
.Printf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN
+1);
1482 wxMessageBox(s
,"Error...",wxOK
| wxICON_EXCLAMATION
);
1487 pQueryCol1Msg
= new wxStaticText(this, QUERY_DIALOG_COL_MSG
, "Column 1:", wxPoint(10, 10), wxSize(69, 16), 0, "QueryCol1Msg");
1488 pQueryCol1Choice
= new wxChoice(this, QUERY_DIALOG_COL_CHOICE
, wxPoint(10, 27), wxSize(250, 27), 0, 0, 0, wxDefaultValidator
, "QueryCol1Choice");
1490 pQueryNotMsg
= new wxStaticText(this, QUERY_DIALOG_NOT_MSG
, "NOT", wxPoint(268, 10), wxSize(-1, -1), 0, "QueryNotMsg");
1491 pQueryNotCheck
= new wxCheckBox(this, QUERY_DIALOG_NOT_CHECKBOX
, "", wxPoint(275, 37), wxSize(20, 20), 0, wxDefaultValidator
, "QueryNotCheck");
1493 wxString choice_strings
[9];
1494 choice_strings
[0] = "=";
1495 choice_strings
[1] = "<";
1496 choice_strings
[2] = ">";
1497 choice_strings
[3] = "<=";
1498 choice_strings
[4] = ">=";
1499 choice_strings
[5] = "Begins";
1500 choice_strings
[6] = "Contains";
1501 choice_strings
[7] = "Like";
1502 choice_strings
[8] = "Between";
1503 pQueryOperatorMsg
= new wxStaticText(this, QUERY_DIALOG_OP_MSG
, "Operator:", wxPoint(305, 10), wxSize(-1, -1), 0, "QueryOperatorMsg");
1504 pQueryOperatorChoice
= new wxChoice(this, QUERY_DIALOG_OP_CHOICE
, wxPoint(305, 27), wxSize(80, 27), 9, choice_strings
, 0, wxDefaultValidator
, "QueryOperatorChoice");
1506 pQueryCol2Msg
= new wxStaticText(this, QUERY_DIALOG_COL2_MSG
, "Column 2:", wxPoint(10, 65), wxSize(69, 16), 0, "QueryCol2Msg");
1507 pQueryCol2Choice
= new wxChoice(this, QUERY_DIALOG_COL2_CHOICE
, wxPoint(10, 82), wxSize(250, 27), 0, 0, 0, wxDefaultValidator
, "QueryCol2Choice");
1509 pQuerySqlWhereMsg
= new wxStaticText(this, QUERY_DIALOG_WHERE_MSG
, "SQL where clause:", wxPoint(10, 141), wxSize(-1, -1), 0, "QuerySqlWhereMsg");
1510 pQuerySqlWhereMtxt
= new wxTextCtrl(this, QUERY_DIALOG_WHERE_TEXT
, "", wxPoint(10, 159), wxSize(377, 134), wxTE_MULTILINE
, wxDefaultValidator
, "QuerySqlWhereMtxt");
1512 pQueryAddBtn
= new wxButton(this, QUERY_DIALOG_ADD
, "&Add", wxPoint(406, 24), wxSize(56, 26), 0, wxDefaultValidator
, "QueryAddBtn");
1513 pQueryAndBtn
= new wxButton(this, QUERY_DIALOG_AND
, "A&nd", wxPoint(406, 58), wxSize(56, 26), 0, wxDefaultValidator
, "QueryAndBtn");
1514 pQueryOrBtn
= new wxButton(this, QUERY_DIALOG_OR
, "&Or", wxPoint(406, 92), wxSize(56, 26), 0, wxDefaultValidator
, "QueryOrBtn");
1516 pQueryLParenBtn
= new wxButton(this, QUERY_DIALOG_LPAREN
, "(", wxPoint(406, 126), wxSize(26, 26), 0, wxDefaultValidator
, "QueryLParenBtn");
1517 pQueryRParenBtn
= new wxButton(this, QUERY_DIALOG_RPAREN
, ")", wxPoint(436, 126), wxSize(26, 26), 0, wxDefaultValidator
, "QueryRParenBtn");
1519 pQueryDoneBtn
= new wxButton(this, QUERY_DIALOG_DONE
, "&Done", wxPoint(406, 185), wxSize(56, 26), 0, wxDefaultValidator
, "QueryDoneBtn");
1520 pQueryClearBtn
= new wxButton(this, QUERY_DIALOG_CLEAR
, "C&lear", wxPoint(406, 218), wxSize(56, 26), 0, wxDefaultValidator
, "QueryClearBtn");
1521 pQueryCountBtn
= new wxButton(this, QUERY_DIALOG_COUNT
, "&Count", wxPoint(406, 252), wxSize(56, 26), 0, wxDefaultValidator
, "QueryCountBtn");
1523 pQueryValue1Msg
= new wxStaticText(this, QUERY_DIALOG_VALUE1_MSG
, "Value:", wxPoint(277, 66), wxSize(-1, -1), 0, "QueryValue1Msg");
1524 pQueryValue1Txt
= new wxTextCtrl(this, QUERY_DIALOG_VALUE1_TEXT
, "", wxPoint(277, 83), wxSize(108, 25), 0, wxDefaultValidator
, "QueryValue1Txt");
1526 pQueryValue2Msg
= new wxStaticText(this, QUERY_DIALOG_VALUE2_MSG
, "AND", wxPoint(238, 126), wxSize(-1, -1), 0, "QueryValue2Msg");
1527 pQueryValue2Txt
= new wxTextCtrl(this, QUERY_DIALOG_VALUE2_TEXT
, "", wxPoint(277, 120), wxSize(108, 25), 0, wxDefaultValidator
, "QueryValue2Txt");
1529 pQueryHintGrp
= new wxStaticBox(this, QUERY_DIALOG_HINT_GROUP
, "", wxPoint(10, 291), wxSize(377, 40), 0, "QueryHintGrp");
1530 pQueryHintMsg
= new wxStaticText(this, QUERY_DIALOG_HINT_MSG
, "", wxPoint(16, 306), wxSize(-1, -1), 0, "QueryHintMsg");
1532 widgetPtrsSet
= TRUE
;
1533 // Initialize the dialog
1535 pQueryCol2Choice
->Append("VALUE -->");
1536 colInf
= pDB
->GetColumns(tblName
);
1537 for (int i
= 0; colInf
[i
].colName
&& strlen(colInf
[i
].colName
); i
++)
1539 // If there is more than one table being queried, qualify
1540 // the column names with the table name prefix.
1541 if (tblName
[1] && strlen(tblName
[1]))
1543 qualName
.Printf("%s.%s", colInf
[i
].tableName
, colInf
[i
].colName
);
1544 pQueryCol1Choice
->Append(qualName
);
1545 pQueryCol2Choice
->Append(qualName
);
1547 else // Single table query, append just the column names
1549 pQueryCol1Choice
->Append(colInf
[i
].colName
);
1550 pQueryCol2Choice
->Append(colInf
[i
].colName
);
1554 pQueryCol1Choice
->SetSelection(0);
1555 pQueryCol2Choice
->SetSelection(0);
1556 pQueryOperatorChoice
->SetSelection(0);
1558 pQueryValue2Msg
->Show(FALSE
);
1559 pQueryValue2Txt
->Show(FALSE
);
1561 pQueryHintMsg
->SetLabel(langQRY_EQ
);
1563 pQuerySqlWhereMtxt
->SetValue(pWhere
);
1567 // Display the dialog window
1571 } // CqueryDlg() constructor
1574 void CqueryDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
1576 // Widget pointers won't be set when the dialog is constructed.
1577 // Control is passed through this function once for each widget on
1578 // a dialog as the dialog is constructed.
1582 wxString widgetName
= win
.GetName();
1584 // Operator choice box
1585 if (widgetName
== pQueryOperatorChoice
->GetName())
1587 // Set the help text
1588 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1591 pQueryHintMsg
->SetLabel(langQRY_EQ
);
1594 pQueryHintMsg
->SetLabel(langQRY_LT
);
1597 pQueryHintMsg
->SetLabel(langQRY_GT
);
1600 pQueryHintMsg
->SetLabel(langQRY_LE
);
1603 pQueryHintMsg
->SetLabel(langQRY_GE
);
1606 pQueryHintMsg
->SetLabel(langQRY_BEGINS
);
1609 pQueryHintMsg
->SetLabel(langQRY_CONTAINS
);
1612 pQueryHintMsg
->SetLabel(langQRY_LIKE
);
1615 pQueryHintMsg
->SetLabel(langQRY_BETWEEN
);
1619 // Hide the value2 widget
1620 pQueryValue2Msg
->Show(FALSE
); // BETWEEN will show this widget
1621 pQueryValue2Txt
->Show(FALSE
); // BETWEEN will show this widget
1623 // Disable the NOT operator for <, <=, >, >=
1624 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1630 pQueryNotCheck
->SetValue(0);
1631 pQueryNotCheck
->Enable(FALSE
);
1634 pQueryNotCheck
->Enable(TRUE
);
1638 // Manipulate the dialog to handle the selected operator
1639 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1646 pQueryCol2Choice
->Enable(TRUE
);
1647 if (pQueryCol2Choice
->GetSelection()) // Column name is highlighted
1649 pQueryValue1Msg
->Show(FALSE
);
1650 pQueryValue1Txt
->Show(FALSE
);
1652 else // "Value" is highlighted
1654 pQueryValue1Msg
->Show(TRUE
);
1655 pQueryValue1Txt
->Show(TRUE
);
1656 pQueryValue1Txt
->SetFocus();
1662 pQueryCol2Choice
->SetSelection(0);
1663 pQueryCol2Choice
->Enable(FALSE
);
1664 pQueryValue1Msg
->Show(TRUE
);
1665 pQueryValue1Txt
->Show(TRUE
);
1666 pQueryValue1Txt
->SetFocus();
1669 pQueryCol2Choice
->SetSelection(0);
1670 pQueryCol2Choice
->Enable(FALSE
);
1671 pQueryValue2Msg
->Show(TRUE
);
1672 pQueryValue2Txt
->Show(TRUE
);
1673 pQueryValue1Msg
->Show(TRUE
);
1674 pQueryValue1Txt
->Show(TRUE
);
1675 pQueryValue1Txt
->SetFocus();
1681 } // Operator choice box
1684 if (widgetName
== pQueryCol2Choice
->GetName())
1686 if (pQueryCol2Choice
->GetSelection()) // Column name is highlighted
1688 pQueryValue1Msg
->Show(FALSE
);
1689 pQueryValue1Txt
->Show(FALSE
);
1691 else // "Value" is highlighted
1693 pQueryValue1Msg
->Show(TRUE
);
1694 pQueryValue1Txt
->Show(TRUE
);
1695 pQueryValue1Txt
->SetFocus();
1699 } // Column 2 choice
1702 if (widgetName
== pQueryAddBtn
->GetName())
1710 if (widgetName
== pQueryAndBtn
->GetName())
1712 AppendToWhere(" AND\n");
1718 if (widgetName
== pQueryOrBtn
->GetName())
1720 AppendToWhere(" OR\n");
1725 // Left Paren button
1726 if (widgetName
== pQueryLParenBtn
->GetName())
1731 } // Left Paren button
1733 // Right paren button
1734 if (widgetName
== pQueryRParenBtn
->GetName())
1739 } // Right Paren button
1742 if (widgetName
== pQueryDoneBtn
->GetName())
1744 // Be sure the where clause will not overflow the output buffer
1745 if (strlen(pQuerySqlWhereMtxt
->GetValue()) > DB_MAX_WHERE_CLAUSE_LEN
)
1748 s
.Printf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN
+1);
1749 wxMessageBox(s
,"Error...",wxOK
| wxICON_EXCLAMATION
);
1752 // Validate the where clause for things such as matching parens
1753 if (!ValidateWhereClause())
1755 // Copy the where clause to the output buffer and exit
1756 strcpy(pWhere
, pQuerySqlWhereMtxt
->GetValue());
1763 if (widgetName
== pQueryClearBtn
->GetName())
1765 bool Ok
= (wxMessageBox("Are you sure you wish to clear the Query?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
1768 pQuerySqlWhereMtxt
->SetValue("");
1774 if (widgetName
== pQueryCountBtn
->GetName())
1776 wxBeginBusyCursor();
1783 } // CqueryDlg::OnCommand
1786 bool CqueryDlg::OnClose()
1801 GetParent()->SetFocus();
1805 } // CqueryDlg::OnClose()
1808 bool CqueryDlg::SetWidgetPtrs()
1812 abort = abort || !(pQueryCol1Choice = (wxChoice *)GetWidgetPtr("QueryCol1Choice",this));
1813 abort = abort || !(pQueryNotCheck = (wxCheckBox *)GetWidgetPtr("QueryNotCheck",this));
1814 abort = abort || !(pQueryOperatorChoice = (wxChoice *)GetWidgetPtr("QueryOperatorChoice",this));
1815 abort = abort || !(pQueryCol2Choice = (wxChoice *)GetWidgetPtr("QueryCol2Choice",this));
1816 abort = abort || !(pQueryValue1Txt = (wxTextCtrl *)GetWidgetPtr("QueryValue1Txt",this));
1817 abort = abort || !(pQueryValue2Txt = (wxTextCtrl *)GetWidgetPtr("QueryValue2Txt",this));
1818 abort = abort || !(pQuerySqlWhereMtxt = (wxMultiText *)GetWidgetPtr("QuerySqlWhereMtxt",this));
1819 abort = abort || !(pQueryAddBtn = (wxButton *)GetWidgetPtr("QueryAddBtn",this));
1820 abort = abort || !(pQueryAndBtn = (wxButton *)GetWidgetPtr("QueryAndBtn",this));
1821 abort = abort || !(pQueryOrBtn = (wxButton *)GetWidgetPtr("QueryOrBtn",this));
1822 abort = abort || !(pQueryLParenBtn = (wxButton *)GetWidgetPtr("QueryLParenBtn",this));
1823 abort = abort || !(pQueryRParenBtn = (wxButton *)GetWidgetPtr("QueryRParenBtn",this));
1824 abort = abort || !(pQueryDoneBtn = (wxButton *)GetWidgetPtr("QueryDoneBtn",this));
1825 abort = abort || !(pQueryClearBtn = (wxButton *)GetWidgetPtr("QueryClearBtn",this));
1826 abort = abort || !(pQueryCountBtn = (wxButton *)GetWidgetPtr("QueryCountBtn",this));
1827 abort = abort || !(pQueryHelpBtn = (wxButton *)GetWidgetPtr("QueryHelpBtn",this));
1828 abort = abort || !(pQueryHintMsg = (wxStaticText *)GetWidgetPtr("QueryHintMsg",this));
1832 return(widgetPtrsSet = !abort);
1834 } // CqueryDlg::SetWidgetPtrs
1837 void CqueryDlg::AppendToWhere(char *s
)
1839 wxString whereStr
= pQuerySqlWhereMtxt
->GetValue();
1841 pQuerySqlWhereMtxt
->SetValue(whereStr
);
1843 } // CqueryDlg::AppendToWhere()
1846 void CqueryDlg::ProcessAddBtn()
1848 qryOp oper
= (qryOp
) pQueryOperatorChoice
->GetSelection();
1850 // Verify that eveything is filled in correctly
1851 if (pQueryCol2Choice
->GetSelection() == 0) // "Value" is selected
1853 // Verify that value 1 is filled in
1854 if (strlen(pQueryValue1Txt
->GetValue()) == 0)
1857 pQueryValue1Txt
->SetFocus();
1860 // For the BETWEEN operator, value 2 must be filled in as well
1861 if (oper
== qryOpBETWEEN
&&
1862 strlen(pQueryValue2Txt
->GetValue()) == 0)
1865 pQueryValue2Txt
->SetFocus();
1870 // Build the expression and append it to the where clause window
1871 wxString s
= pQueryCol1Choice
->GetStringSelection();
1873 if (pQueryNotCheck
->GetValue() && (oper
!= qryOpEQ
))
1879 if (pQueryNotCheck
->GetValue()) // NOT box is checked
1908 int col1Idx
= pQueryCol1Choice
->GetSelection();
1911 if (colInf
[col1Idx
].sqlDataType
== SQL_VARCHAR
||
1912 oper
== qryOpBEGINS
||
1913 oper
== qryOpCONTAINS
||
1917 if (pQueryCol2Choice
->GetSelection()) // Column name
1918 s
+= pQueryCol2Choice
->GetStringSelection();
1919 else // Column 2 is a "value"
1923 if (oper
== qryOpCONTAINS
)
1925 s
+= pQueryValue1Txt
->GetValue();
1926 if (oper
== qryOpCONTAINS
|| oper
== qryOpBEGINS
)
1932 if (oper
== qryOpBETWEEN
)
1937 s
+= pQueryValue2Txt
->GetValue();
1942 AppendToWhere((char*) (const char*) s
);
1944 } // CqueryDlg::ProcessAddBtn()
1947 void CqueryDlg::ProcessCountBtn()
1949 if (!ValidateWhereClause())
1952 if (dbTable
== 0) // wxTable object needs to be created and opened
1954 if (!(dbTable
= new wxTable(pDB
, masterTableName
, 0)))
1956 wxMessageBox("Memory allocation failed creating a wxTable object.","Error...",wxOK
| wxICON_EXCLAMATION
);
1959 if (!dbTable
->Open())
1962 tStr
= "ODBC error during Open()\n\n";
1963 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1964 wxMessageBox(tStr
,"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1969 // Count() with WHERE clause
1970 dbTable
->where
= (char*) (const char*) pQuerySqlWhereMtxt
->GetValue();
1971 ULONG whereCnt
= dbTable
->Count();
1973 // Count() of all records in the table
1975 ULONG totalCnt
= dbTable
->Count();
1977 if (whereCnt
> 0 || totalCnt
== 0)
1980 tStr
.Printf("%lu of %lu records match the query criteria.",whereCnt
,totalCnt
);
1981 wxMessageBox(tStr
,"Notice...",wxOK
| wxICON_INFORMATION
);
1986 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
);
1987 wxMessageBox(tStr
,"Notice...",wxOK
| wxICON_INFORMATION
);
1990 // After a wxMessageBox, the focus does not necessarily return to the
1991 // window which was the focus when the message box popped up, so return
1992 // focus to the Query dialog for certain
1995 } // CqueryDlg::ProcessCountBtn()
1998 bool CqueryDlg::ValidateWhereClause()
2000 wxString where
= pQuerySqlWhereMtxt
->GetValue();
2002 if (where
.Freq('(') != where
.Freq(')'))
2004 wxMessageBox("There are mismatched parenthesis in the constructed where clause","Error...",wxOK
| wxICON_EXCLAMATION
);
2007 // After a wxMessageBox, the focus does not necessarily return to the
2008 // window which was the focus when the message box popped up, so return
2009 // focus to the Query dialog for certain
2014 } // CqueryDlg::ValidateWhereClause()