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 IMPLEMENT_APP(DatabaseDemoApp
)
42 #include <stdio.h> // Included strictly for reading the text file with the database parameters
44 #include <wx/db.h> // Required in the file which will get the data source connection
45 #include <wx/dbtable.h> // Has the wxTable object from which all data objects will inherit their data table functionality
47 extern DbList
*PtrBegDbList
; // from wx_db.cpp, used in getting back error results from db connections
49 #include "dbtest.h" // Header file for this demonstration program
50 #include "listdb.h" // Code to support the "Lookup" button on the editor dialog
51 extern char ListDB_Selection
[]; // Used to return the first column value for the selected line from the listDB routines
52 extern char ListDB_Selection2
[]; // Used to return the second column value for the selected line from the listDB routines
54 DatabaseDemoFrame
*DemoFrame
; // Pointer to the main frame
57 // This statement initializes the whole application and calls OnInit
58 DatabaseDemoApp DatabaseDemoApp
;
61 /* Pointer to the main database connection used in the program. This
62 * pointer would normally be used for doing things as database lookups
63 * for user login names and passwords, getting workstation settings, etc.
66 * For each database object created which uses this wxDB pointer
67 * connection to the database, when a CommitTrans() or RollBackTrans()
68 * will commit or rollback EVERY object which uses this wxDB pointer.
70 * To allow each table object (those derived from wxTable) to be
71 * individually committed or rolled back, you MUST use a different
72 * instance of wxDB in the constructor of the table. Doing so creates
73 * more overhead, and will use more database connections (some DBs have
74 * connection limits...), so use connections sparringly.
76 * It is recommended that one "main" database connection be created for
77 * the entire program to use for READ-ONLY database accesses, but for each
78 * table object which will do a CommitTrans() or RollbackTrans() that a
79 * new wxDB object be created and used for it.
85 * This function will return the exact string(s) from the database engine
86 * indicating all error conditions which have just occured during the
87 * last call to the database engine.
89 * This demo uses the returned string by displaying it in a wxMessageBox. The
90 * formatting therefore is not the greatest, but this is just a demo, not a
91 * finished product. :-) gt
93 * NOTE: The value returned by this function is for temporary use only and
94 * should be copied for long term use
96 char *GetExtendedDBErrorMsg(char *ErrFile
, int ErrLine
)
102 if (ErrFile
|| ErrLine
)
109 tStr
.sprintf("%d",ErrLine
);
110 msg
+= tStr
.GetData();
115 msg
.Append ("\nODBC ERRORS\n");
118 // Scan through each database connection displaying
119 // any ODBC errors that have occured.
120 for (DbList
*pDbList
= PtrBegDbList
; pDbList
; pDbList
= pDbList
->PtrNext
)
122 // Skip over any free connections
125 // Display errors for this connection
126 for (int i
= 0; i
< DB_MAX_ERROR_HISTORY
; i
++)
128 if (pDbList
->PtrDb
->errorList
[i
])
130 msg
.Append(pDbList
->PtrDb
->errorList
[i
]);
131 if (strcmp(pDbList
->PtrDb
->errorList
[i
],"") != 0)
138 return msg
.GetData();
139 } // GetExtendedDBErrorMsg
143 // `Main program' equivalent, creating windows and returning main app frame
144 wxFrame
*DatabaseDemoApp::OnInit(void)
146 // Create the main frame window
147 DemoFrame
= new DatabaseDemoFrame(NULL
, "wxWindows Database Demo", 50, 50, 537, 480);
151 DemoFrame
->SetIcon(wxIcon("db_icon"));
154 DemoFrame
->SetIcon(wxIcon("db.xbm"));
158 wxMenu
*file_menu
= new wxMenu
;
159 file_menu
->Append(FILE_CREATE
, "&Create contact table");
160 file_menu
->Append(FILE_EXIT
, "E&xit");
162 wxMenu
*edit_menu
= new wxMenu
;
163 edit_menu
->Append(EDIT_PARAMETERS
, "&Parameters...");
165 wxMenu
*about_menu
= new wxMenu
;
166 about_menu
->Append(ABOUT_DEMO
, "&About");
168 wxMenuBar
*menu_bar
= new wxMenuBar
;
169 menu_bar
->Append(file_menu
, "&File");
170 menu_bar
->Append(edit_menu
, "&Edit");
171 menu_bar
->Append(about_menu
, "&About");
172 DemoFrame
->SetMenuBar(menu_bar
);
174 // Initialize the ODBC Environment for Database Operations
175 if (SQLAllocEnv(&DbConnectInf
.Henv
) != SQL_SUCCESS
)
177 wxMessageBox("A problem occured while trying to get a connection to the data source","DB CONNECTION ERROR",wxOK
| wxICON_EXCLAMATION
);
182 if ((paramFile
= fopen(paramFilename
, "r")) == NULL
)
185 tStr
.sprintf("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
);
186 wxMessageBox(tStr
.GetData(),"File I/O Error...",wxOK
| wxICON_EXCLAMATION
);
187 DemoFrame
->BuildParameterDialog(NULL
);
188 if ((paramFile
= fopen(paramFilename
, "r")) == NULL
)
193 fgets(buffer
, sizeof(params
.ODBCSource
), paramFile
);
194 buffer
[strlen(buffer
)-1] = '\0';
195 strcpy(params
.ODBCSource
,buffer
);
197 fgets(buffer
, sizeof(params
.UserName
), paramFile
);
198 buffer
[strlen(buffer
)-1] = '\0';
199 strcpy(params
.UserName
,buffer
);
201 fgets(buffer
, sizeof(params
.Password
), paramFile
);
202 buffer
[strlen(buffer
)-1] = '\0';
203 strcpy(params
.Password
,buffer
);
207 // Connect to datasource
208 strcpy(DbConnectInf
.Dsn
, params
.ODBCSource
); // ODBC data source name (created with ODBC Administrator under Win95/NT)
209 strcpy(DbConnectInf
.Uid
, params
.UserName
); // database username - must already exist in the data source
210 strcpy(DbConnectInf
.AuthStr
, params
.Password
); // password database username
211 READONLY_DB
= GetDbConnection(&DbConnectInf
);
212 if (READONLY_DB
== 0)
214 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
);
215 DemoFrame
->BuildParameterDialog(NULL
);
216 strcpy(DbConnectInf
.Dsn
, "");
217 strcpy(DbConnectInf
.Uid
, "");
218 strcpy(DbConnectInf
.AuthStr
, "");
219 wxMessageBox("Now exiting program.\n\nRestart program to try any new settings.","Notice...",wxOK
| wxICON_INFORMATION
);
223 DemoFrame
->BuildEditorDialog();
226 DemoFrame
->Show(TRUE
);
228 // Return the main frame window
230 } // DatabaseDemoApp::OnInit()
234 // DatabaseDemoFrame constructor
235 DatabaseDemoFrame::DatabaseDemoFrame(wxFrame
*frame
, char *title
, int x
, int y
, int w
, int h
):
236 wxFrame(frame
, title
, x
, y
, w
, h
)
238 // Put any code in necessary for initializing the main frame here
242 // Intercept menu commands
243 void DatabaseDemoFrame::OnMenuCommand(int id
)
253 case EDIT_PARAMETERS
:
254 if ((pEditorDlg
->mode
!= mCreate
) && (pEditorDlg
->mode
!= mEdit
))
255 BuildParameterDialog(this);
257 wxMessageBox("Cannot change database parameters while creating or editing a record","Notice...",wxOK
| wxICON_INFORMATION
);
260 wxMessageBox("wxWindows sample program for database classes\n\nContributed on 27 July 1998","About...",wxOK
| wxICON_INFORMATION
);
263 } // DatabaseDemoFrame::OnMenuCommand()
266 Bool
DatabaseDemoFrame::OnClose(void)
268 // Put any additional checking necessary to make certain it is alright
269 // to close the program here that is not done elsewhere
272 } // DatabaseDemoFrame::OnClose()
275 void DatabaseDemoFrame::CreateDataTable()
277 Bool Ok
= (wxMessageBox("Any data currently residing in the table will be erased.\n\nAre you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
286 Ccontact
*Contact
= new Ccontact();
290 wxMessageBox("Error allocating memory for 'Ccontact'object.\n\nTable was not created.","Error...",wxOK
| wxICON_EXCLAMATION
);
294 if (!Contact
->CreateTable())
298 tStr
= "Error creating CONTACTS table.\nTable was not created.\n\n";
299 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
300 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
305 if (!Contact
->CreateIndexes())
309 tStr
= "Error creating CONTACTS indexes.\nIndexes will be unavailable.\n\n";
310 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
311 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
320 wxMessageBox("Table and index(es) were successfully created.","Notice...",wxOK
| wxICON_INFORMATION
);
321 } // DatabaseDemoFrame::CreateDataTable()
324 void DatabaseDemoFrame::BuildEditorDialog()
326 pEditorDlg
= new CeditorDlg(this);
328 wxMessageBox("Unable to create the editor dialog for some reason","Error...",wxOK
| wxICON_EXCLAMATION
);
329 } // DatabaseDemoFrame::BuildEditorDialog()
332 void DatabaseDemoFrame::BuildParameterDialog(wxWindow
*parent
)
334 pParamDlg
= new CparameterDlg(parent
);
337 wxMessageBox("Unable to create the parameter dialog for some reason","Error...",wxOK
| wxICON_EXCLAMATION
);
338 } // DatabaseDemoFrame::BuildParameterDialog()
342 * Constructor note: If no wxDB object is passed in, a new connection to the database
343 * is created for this instance of Ccontact. This can be a slow process depending
344 * on the database engine being used, and some database engines have a limit on the
345 * number of connections (either hard limits, or license restricted) so care should
346 * be used to use as few connections as is necessary.
347 * IMPORTANT: Objects which share a wxDB pointer are ALL acted upon whenever a member
348 * function of pDb is called (i.e. CommitTrans() or RollbackTrans(), so if modifying
349 * or creating a table objects which use the same pDb, know that all the objects
350 * will be committed or rolled back when any of the objects has this function call made.
352 Ccontact::Ccontact (wxDB
*pwxDB
) : wxTable(pwxDB
? pwxDB
: GetDbConnection(&DbConnectInf
),CONTACT_TABLE_NAME
,CONTACT_NO_COLS
)
354 // This is used to represent whether the database connection should be released
355 // when this instance of the object is deleted. If using the same connection
356 // for multiple instance of database objects, then the connection should only be
357 // released when the last database instance using the connection is deleted
362 } // Ccontact Constructor
365 void Ccontact::Initialize()
374 JoinDate
.year
= 1980;
380 JoinDate
.fraction
= 0;
381 NativeLanguage
= langENGLISH
;
385 } // Ccontact::Initialize
388 Ccontact::~Ccontact()
392 if (!FreeDbConnection(pDb
))
395 tStr
= "Unable to Free the Ccontact data table handle\n\n";
396 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
397 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
400 } // Ccontract destructor
404 * Handles setting up all the connections for the interface from the wxTable
405 * functions to interface to the data structure used to store records in
406 * memory, and for all the column definitions that define the table structure
408 void Ccontact::SetupColumns()
410 SetColDefs ( 0,"NAME", DB_DATA_TYPE_VARCHAR
, Name
, SQL_C_CHAR
, sizeof(Name
), TRUE
, TRUE
); // Primary index
411 SetColDefs ( 1,"ADDRESS1", DB_DATA_TYPE_VARCHAR
, Addr1
, SQL_C_CHAR
, sizeof(Addr1
), FALSE
,TRUE
);
412 SetColDefs ( 2,"ADDRESS2", DB_DATA_TYPE_VARCHAR
, Addr2
, SQL_C_CHAR
, sizeof(Addr2
), FALSE
,TRUE
);
413 SetColDefs ( 3,"CITY", DB_DATA_TYPE_VARCHAR
, City
, SQL_C_CHAR
, sizeof(City
), FALSE
,TRUE
);
414 SetColDefs ( 4,"STATE", DB_DATA_TYPE_VARCHAR
, State
, SQL_C_CHAR
, sizeof(State
), FALSE
,TRUE
);
415 SetColDefs ( 5,"POSTAL_CODE", DB_DATA_TYPE_VARCHAR
, PostalCode
, SQL_C_CHAR
, sizeof(PostalCode
), FALSE
,TRUE
);
416 SetColDefs ( 6,"COUNTRY", DB_DATA_TYPE_VARCHAR
, Country
, SQL_C_CHAR
, sizeof(Country
), FALSE
,TRUE
);
417 SetColDefs ( 7,"JOIN_DATE", DB_DATA_TYPE_DATE
, &JoinDate
, SQL_C_TIMESTAMP
, sizeof(JoinDate
), FALSE
,TRUE
);
418 SetColDefs ( 8,"NATIVE_LANGUAGE", DB_DATA_TYPE_INTEGER
, &NativeLanguage
, SQL_C_ENUM
, sizeof(NativeLanguage
), FALSE
,TRUE
);
419 SetColDefs ( 9,"IS_DEVELOPER", DB_DATA_TYPE_INTEGER
, &IsDeveloper
, SQL_C_BOOLEAN
, sizeof(Bool
), FALSE
,TRUE
);
420 SetColDefs (10,"CONTRIBUTIONS", DB_DATA_TYPE_INTEGER
, &Contributions
, SQL_C_USHORT
, sizeof(Contributions
), FALSE
,TRUE
);
421 SetColDefs (11,"LINES_OF_CODE", DB_DATA_TYPE_INTEGER
, &LinesOfCode
, SQL_C_ULONG
, sizeof(LinesOfCode
), FALSE
,TRUE
);
422 } // Ccontact::SetupColumns
425 Bool
Ccontact::CreateIndexes(void)
427 // This index could easily be accomplished with an "orderBy" clause,
428 // but is done to show how to construct a non-primary index.
434 strcpy(idxDef
[0].ColName
, "IS_DEVELOPER");
435 idxDef
[0].Ascending
= TRUE
;
437 strcpy(idxDef
[1].ColName
, "NAME");
438 idxDef
[1].Ascending
= TRUE
;
440 indexName
= CONTACT_TABLE_NAME
;
441 indexName
+= "_IDX1";
442 Ok
= CreateIndex(indexName
.GetData(), TRUE
, 2, idxDef
);
445 } // Ccontact::CreateIndexes()
449 * Having a function to do a query on the primary key (and possibly others) is
450 * very efficient and tighter coding so that it is available where ever the object
451 * is. Great for use with multiple tables when not using views or outer joins
453 Bool
Ccontact::FetchByName(char *name
)
455 whereStr
.sprintf("NAME = '%s'",name
);
456 where
= this->whereStr
.GetData();
465 } // Ccontact::FetchByName()
470 * ************* DIALOGS ***************
475 /* CeditorDlg constructor
477 * Creates the dialog used for creating/editing/deleting/copying a Ccontact object.
478 * This dialog actually is drawn in the main frame of the program
480 * An instance of Ccontact is created - "Contact" - which is used to hold the Ccontact
481 * object that is currently being worked with.
483 CeditorDlg::CeditorDlg(wxWindow
*parent
) : wxPanel (parent
, 1, 1, 460, 455)
485 // Since the ::OnCommand() function is overridden, this prevents the widget
486 // detection in ::OnCommand() until all widgets have been initialized to prevent
487 // uninitialized pointers from crashing the program
488 widgetPtrsSet
= FALSE
;
490 // Create the data structure and a new database connection.
491 // (As there is not a pDb being passed in the constructor, a new database
492 // connection is created)
493 Contact
= new Ccontact();
497 wxMessageBox("Unable to instantiate an instance of Ccontact","Error...",wxOK
| wxICON_EXCLAMATION
);
501 // Check if the table exists or not. If it doesn't, ask the user if they want to
502 // create the table. Continue trying to create the table until it exists, or user aborts
503 while (!Contact
->pDb
->TableExists((char *)CONTACT_TABLE_NAME
))
506 tStr
.sprintf("Unable to open the table '%s'.\n\nTable may need to be created...?\n\n",CONTACT_TABLE_NAME
);
507 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
508 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
510 Bool createTable
= (wxMessageBox("Do you wish to try to create/clear the CONTACTS table?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
520 DemoFrame
->CreateDataTable();
523 // Tables must be "opened" before anything other than creating/deleting table can be done
524 if (!Contact
->Open())
526 // Table does exist, there was some problem opening it. Currently this should
527 // never fail, except in the case of the table not exisiting. Open() basically
528 // only sets up variable/pointer values, other than checking for table existence.
529 if (Contact
->pDb
->TableExists((char *)CONTACT_TABLE_NAME
))
532 tStr
.sprintf("Unable to open the table '%s'.\n\n",CONTACT_TABLE_NAME
);
533 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
534 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
543 SetLabelPosition(wxHORIZONTAL
);
545 wxFont
*ButtonFont
= new wxFont(12,wxSWISS
,wxNORMAL
,wxBOLD
);
546 wxFont
*TextFont
= new wxFont(12,wxSWISS
,wxNORMAL
,wxNORMAL
);
548 SetButtonFont(ButtonFont
);
549 SetLabelFont(TextFont
);
550 SetLabelPosition(wxVERTICAL
);
552 wxGroupBox
*FunctionGrp
= new wxGroupBox(this, "", 15, 1, 497, 69, 0, "FunctionGrp");
553 wxGroupBox
*SearchGrp
= new wxGroupBox(this, "", 417, 1, 95, 242, 0, "SearchGrp");
555 pCreateBtn
= new wxButton(this, NULL
, "&Create", 25, 21, 70, 35, 0, "CreateBtn");
556 pEditBtn
= new wxButton(this, NULL
, "&Edit", 102, 21, 70, 35, 0, "EditBtn");
557 pDeleteBtn
= new wxButton(this, NULL
, "&Delete", 179, 21, 70, 35, 0, "DeleteBtn");
558 pCopyBtn
= new wxButton(this, NULL
, "Cop&y", 256, 21, 70, 35, 0, "CopyBtn");
559 pSaveBtn
= new wxButton(this, NULL
, "&Save", 333, 21, 70, 35, 0, "SaveBtn");
560 pCancelBtn
= new wxButton(this, NULL
, "C&ancel", 430, 21, 70, 35, 0, "CancelBtn");
562 pPrevBtn
= new wxButton(this, NULL
, "<< &Prev",430, 81, 70, 35, 0, "PrevBtn");
563 pNextBtn
= new wxButton(this, NULL
, "&Next >>",430, 121, 70, 35, 0, "NextBtn");
564 pQueryBtn
= new wxButton(this, NULL
, "&Query", 430, 161, 70, 35, 0, "QueryBtn");
565 pResetBtn
= new wxButton(this, NULL
, "&Reset", 430, 200, 70, 35, 0, "ResetBtn");
567 pNameMsg
= new wxMessage(this, "Name:", 17, 80, -1, -1, 0, "NameMsg");
568 pNameTxt
= new wxText(this, NULL
, "", "", 17, 97, 308, 25, 0, "NameTxt");
569 pNameListBtn
= new wxButton(this, NULL
, "&Lookup", 333, 99, 70, 24, 0, "LookupBtn");
571 pAddress1Msg
= new wxMessage(this, "Address:", 17, 130, -1, -1, 0, "Address1Msg");
572 pAddress1Txt
= new wxText(this, NULL
, "", "", 17, 147, 308, 25, 0, "Address1Txt");
574 pAddress2Msg
= new wxMessage(this, "Address:", 17, 180, -1, -1, 0, "Address2Msg");
575 pAddress2Txt
= new wxText(this, NULL
, "", "", 17, 197, 308, 25, 0, "Address2Txt");
577 pCityMsg
= new wxMessage(this, "City:", 17, 230, -1, -1, 0, "CityMsg");
578 pCityTxt
= new wxText(this, NULL
, "", "", 17, 247, 225, 25, 0, "CityTxt");
580 pStateMsg
= new wxMessage(this, "State:", 250, 230, -1, -1, 0, "StateMsg");
581 pStateTxt
= new wxText(this, NULL
, "", "", 250, 247, 153, 25, 0, "StateTxt");
583 pCountryMsg
= new wxMessage(this, "Country:", 17, 280, -1, -1, 0, "CountryMsg");
584 pCountryTxt
= new wxText(this, NULL
, "", "", 17, 297, 225, 25, 0, "CountryTxt");
586 pPostalCodeMsg
= new wxMessage(this, "Postal Code:", 250, 280, -1, -1, 0, "PostalCodeMsg");
587 pPostalCodeTxt
= new wxText(this, NULL
, "", "", 250, 297, 153, 25, 0, "PostalCodeTxt");
589 char *choice_strings
[5];
590 choice_strings
[0] = "English";
591 choice_strings
[1] = "French";
592 choice_strings
[2] = "German";
593 choice_strings
[3] = "Spanish";
594 choice_strings
[4] = "Other";
595 pNativeLangChoice
= new wxChoice(this, NULL
, "",17, 346, 277, -1, 5, choice_strings
);
596 pNativeLangMsg
= new wxMessage(this, "Native language:", 17, 330, -1, -1, 0, "NativeLangMsg");
598 char *radio_strings
[2];
599 radio_strings
[0] = "No";
600 radio_strings
[1] = "Yes";
601 pDeveloperRadio
= new wxRadioBox(this,NULL
,"Developer:",303,330,-1,-1,2,radio_strings
,2,wxHORIZONTAL
|wxFLAT
);
603 pJoinDateMsg
= new wxMessage(this, "Date joined:", 17, 380, -1, -1, 0, "JoinDateMsg");
604 pJoinDateTxt
= new wxText(this, NULL
, "", "", 17, 397, 150, 25, 0, "JoinDateTxt");
606 pContribMsg
= new wxMessage(this, "Contributions:", 175, 380, -1, -1, 0, "ContribMsg");
607 pContribTxt
= new wxText(this, NULL
, "", "", 175, 397, 120, 25, 0, "ContribTxt");
609 pLinesMsg
= new wxMessage(this, "Lines of code:", 303, 380, -1, -1, 0, "LinesMsg");
610 pLinesTxt
= new wxText(this, NULL
, "", "", 303, 397, 100, 25, 0, "LinesTxt");
612 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
613 // handle all widget processing
614 widgetPtrsSet
= TRUE
;
616 // Setup the orderBy and where clauses to return back a single record as the result set,
617 // as there will only be one record being shown on the dialog at a time, this optimizes
618 // network traffic by only returning a one row result
620 Contact
->orderBy
= "NAME"; // field name to sort by
622 // The wxString "whereStr" is not a member of the wxTable object, it is a member variable
623 // specifically in the Ccontact class. It is used here for simpler construction of a varying
624 // length string, and then after the string is built, the wxTable member variable "where" is
625 // assigned the pointer to the constructed string.
627 // The constructed where clause below has a sub-query within it "SELECT MIN(NAME) FROM %s"
628 // to achieve a single row (in this case the first name in alphabetical order).
629 Contact
->whereStr
.sprintf("NAME = (SELECT MIN(NAME) FROM %s)",Contact
->tableName
);
631 // NOTE: GetData() returns a pointer which may not be valid later, so this is short term use only
632 Contact
->where
= Contact
->whereStr
.GetData();
634 // Perform the Query to get the result set.
635 // NOTE: If there are no rows returned, that is a valid result, so Query() would return TRUE.
636 // Only if there is a database error will Query() come back as FALSE
637 if (!Contact
->Query())
640 tStr
= "ODBC error during Query()\n\n";
641 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
642 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
643 GetParent()->Close();
647 // Since Query succeeded, now get the row that was returned
648 if (!Contact
->GetNext())
649 // If the GetNext() failed at this point, then there are no rows to retrieve,
650 // so clear the values in the members of "Contact" so that PutData() blanks the
651 // widgets on the dialog
652 Contact
->Initialize();
658 } // CeditorDlg constructor
661 Bool
CeditorDlg::OnClose()
664 if ((mode
!= mCreate
) && (mode
!= mEdit
))
672 wxMessageBox("Must finish processing the current record being created/modified before exiting","Notice...",wxOK
| wxICON_INFORMATION
);
675 } // CeditorDlg::OnClose()
678 void CeditorDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
682 widgetName
= win
.GetName();
687 if (widgetName
== pCreateBtn
->GetName())
689 Contact
->Initialize();
692 pNameTxt
->SetValue("");
693 pNameTxt
->SetFocus();
697 if (widgetName
== pEditBtn
->GetName())
699 saveName
= Contact
->Name
;
701 pNameTxt
->SetFocus();
705 if (widgetName
== pCopyBtn
->GetName())
708 pNameTxt
->SetValue("");
709 pNameTxt
->SetFocus();
713 if (widgetName
== pDeleteBtn
->GetName())
715 Bool Ok
= (wxMessageBox("Are you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
720 if (Ok
&& Contact
->Delete())
722 // NOTE: Deletions are not finalized until a CommitTrans() is performed.
723 // If the commit were not performed, the program will continue to
724 // show the table contents as if they were deleted until this instance
725 // of Ccontact is deleted. If the Commit wasn't performed, the
726 // database will automatically Rollback the changes when the database
727 // connection is terminated
728 Contact
->pDb
->CommitTrans();
730 // Try to get the row that followed the just deleted row in the orderBy sequence
733 // There was now row (in sequence) after the just deleted row, so get the
734 // row which preceded the just deleted row
737 // There are now no rows remaining, so clear the dialog widgets
738 Contact
->Initialize();
742 SetMode(mode
); // force reset of button enable/disable
746 Contact
->pDb
->RollbackTrans();
752 if (widgetName
== pSaveBtn
->GetName())
758 if (widgetName
== pCancelBtn
->GetName())
760 Bool Ok
= (wxMessageBox("Are you sure?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
765 if (!strcmp(saveName
.GetData(),""))
767 Contact
->Initialize();
774 // Requery previous record
775 if (Contact
->FetchByName(saveName
.GetData()))
783 // Previous record not available, retrieve first record in table
784 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
785 Contact
->whereStr
+= Contact
->tableName
;
786 Contact
->whereStr
+= ")";
788 Contact
->where
= Contact
->whereStr
.GetData();
789 if (!Contact
->Query())
792 tStr
= "ODBC error during Query()\n\n";
793 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
794 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
798 if (Contact
->GetNext()) // Successfully read first record
804 // No contacts are available, clear dialog
805 Contact
->Initialize();
811 if (widgetName
== pPrevBtn
->GetName())
818 if (widgetName
== pNextBtn
->GetName())
825 if (widgetName
== pQueryBtn
->GetName())
827 // Display the query dialog box
828 char qryWhere
[DB_MAX_WHERE_CLAUSE_LEN
+1];
829 strcpy(qryWhere
, Contact
->qryWhereStr
.GetData());
830 char *tblName
[] = {(char *)CONTACT_TABLE_NAME
, 0};
831 new CqueryDlg(GetParent(), Contact
->pDb
, tblName
, qryWhere
);
833 // Query the first record in the new record set and
834 // display it, if the query string has changed.
835 if (strcmp(qryWhere
, Contact
->qryWhereStr
.GetData()))
837 Contact
->orderBy
= "NAME";
838 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
839 Contact
->whereStr
+= CONTACT_TABLE_NAME
;
840 // Append the query where string (if there is one)
841 Contact
->qryWhereStr
= qryWhere
;
842 if (strlen(qryWhere
))
844 Contact
->whereStr
+= " WHERE ";
845 Contact
->whereStr
+= Contact
->qryWhereStr
;
847 // Close the expression with a right paren
848 Contact
->whereStr
+= ")";
850 Contact
->where
= Contact
->whereStr
.GetData();
851 if (!Contact
->Query())
854 tStr
= "ODBC error during Query()\n\n";
855 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
856 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
859 // Display the first record from the query set
860 if (!Contact
->GetNext())
861 Contact
->Initialize();
865 // Enable/Disable the reset button
866 pResetBtn
->Enable(!Contact
->qryWhereStr
.Empty());
872 if (widgetName
== pResetBtn
->GetName())
874 // Clear the additional where criteria established by the query feature
875 Contact
->qryWhereStr
= "";
877 // Query the first record in the table
878 Contact
->orderBy
= "NAME";
879 Contact
->whereStr
= "NAME = (SELECT MIN(NAME) FROM ";
880 Contact
->whereStr
+= CONTACT_TABLE_NAME
;
881 Contact
->whereStr
+= ")";
882 Contact
->where
= Contact
->whereStr
.GetData();
883 if (!Contact
->Query())
886 tStr
= "ODBC error during Query()\n\n";
887 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
888 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
891 if (!Contact
->GetNext())
892 Contact
->Initialize();
894 pResetBtn
->Enable(FALSE
);
900 if (widgetName
== pNameListBtn
->GetName())
902 new ClookUpDlg(/* wxWindow *parent */ this,
903 /* char *windowTitle */ "Select contact name",
904 /* char *tableName */ (char *) CONTACT_TABLE_NAME
,
905 /* char *dispCol1 */ "NAME",
906 /* char *dispCol2 */ "JOIN_DATE",
907 /* char *where */ "",
908 /* char *orderBy */ "NAME",
909 /* Bool distinctValues */ TRUE
);
911 if (ListDB_Selection
&& strlen(ListDB_Selection
))
913 wxString w
= "NAME = '";
914 w
+= ListDB_Selection
;
922 } // CeditorDlg::OnCommand()
925 void CeditorDlg::FieldsEditable()
927 pNameTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
928 pAddress1Txt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
929 pAddress2Txt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
930 pCityTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
931 pStateTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
932 pPostalCodeTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
933 pCountryTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
935 pJoinDateTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
936 pContribTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
937 pLinesTxt
->Enable((mode
== mCreate
) || (mode
== mEdit
));
938 pNativeLangChoice
->Enable((mode
== mCreate
) || (mode
== mEdit
));
939 pDeveloperRadio
->Enable((mode
== mCreate
) || (mode
== mEdit
));
941 } // CeditorDlg::FieldsEditable()
944 void CeditorDlg::SetMode(enum DialogModes m
)
965 pCreateBtn
->Enable( !edit
);
966 pEditBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
967 pDeleteBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
968 pCopyBtn
->Enable( !edit
&& (strcmp(Contact
->Name
,"")!=0) );
969 pSaveBtn
->Enable( edit
);
970 pCancelBtn
->Enable( edit
);
971 pPrevBtn
->Enable( !edit
);
972 pNextBtn
->Enable( !edit
);
973 pQueryBtn
->Enable( !edit
);
974 pResetBtn
->Enable( !edit
&& !Contact
->qryWhereStr
.Empty() );
975 pNameListBtn
->Enable( !edit
);
979 } // CeditorDlg::SetMode()
982 Bool
CeditorDlg::PutData()
986 pNameTxt
->SetValue(Contact
->Name
);
987 pAddress1Txt
->SetValue(Contact
->Addr1
);
988 pAddress2Txt
->SetValue(Contact
->Addr2
);
989 pCityTxt
->SetValue(Contact
->City
);
990 pStateTxt
->SetValue(Contact
->State
);
991 pCountryTxt
->SetValue(Contact
->Country
);
992 pPostalCodeTxt
->SetValue(Contact
->PostalCode
);
994 tStr
.sprintf("%d/%d/%d",Contact
->JoinDate
.month
,Contact
->JoinDate
.day
,Contact
->JoinDate
.year
);
995 pJoinDateTxt
->SetValue(tStr
.GetData());
997 tStr
.sprintf("%d",Contact
->Contributions
);
998 pContribTxt
->SetValue(tStr
.GetData());
1000 tStr
.sprintf("%lu",Contact
->LinesOfCode
);
1001 pLinesTxt
->SetValue(tStr
.GetData());
1003 pNativeLangChoice
->SetSelection(Contact
->NativeLanguage
);
1005 pDeveloperRadio
->SetSelection(Contact
->IsDeveloper
);
1008 } // Ceditor::PutData()
1012 * Reads the data out of all the widgets on the dialog. Some data evaluation is done
1013 * to ensure that there is a name entered and that the date field is valid.
1015 * A return value of TRUE means that valid data was retrieved from the dialog, otherwise
1016 * invalid data was found (and a message was displayed telling the user what to fix), and
1017 * the data was not placed into the appropraite fields of Ccontact
1019 Bool
CeditorDlg::GetData()
1021 // Validate that the data currently entered into the widgets is valid data
1024 tStr
= pNameTxt
->GetValue();
1025 if (!strcmp(tStr
.GetData(),""))
1027 wxMessageBox("A name is required for entry into the contact table","Notice...",wxOK
| wxICON_INFORMATION
);
1031 Bool invalid
= FALSE
;
1035 tStr
= pJoinDateTxt
->GetValue();
1036 if (tStr
.Freq('/') != 2)
1039 // Find the month, day, and year tokens
1042 first
= tStr
.First('/');
1043 second
= tStr
.Last('/');
1045 mm
= atoi(tStr
.SubString(0,first
));
1046 dd
= atoi(tStr
.SubString(first
+1,second
));
1047 yyyy
= atoi(tStr
.SubString(second
+1,tStr
.Length()-1));
1049 invalid
= !(mm
&& dd
&& yyyy
);
1052 // Force Year 2000 compliance
1053 if (!invalid
&& (yyyy
< 1000))
1056 // Check the token ranges for validity
1061 else if ((mm
< 1) || (mm
> 12))
1069 int days
[12] = {31,28,31,30,31,30,
1071 if (dd
> days
[mm
-1])
1074 if ((dd
== 29) && (mm
== 2))
1076 if (((yyyy
% 4) == 0) && (((yyyy
% 100) != 0) || ((yyyy
% 400) == 0)))
1086 Contact
->JoinDate
.month
= mm
;
1087 Contact
->JoinDate
.day
= dd
;
1088 Contact
->JoinDate
.year
= yyyy
;
1092 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
);
1096 tStr
= pNameTxt
->GetValue();
1097 strcpy(Contact
->Name
,tStr
.GetData());
1098 strcpy(Contact
->Addr1
,pAddress1Txt
->GetValue());
1099 strcpy(Contact
->Addr2
,pAddress2Txt
->GetValue());
1100 strcpy(Contact
->City
,pCityTxt
->GetValue());
1101 strcpy(Contact
->State
,pStateTxt
->GetValue());
1102 strcpy(Contact
->Country
,pCountryTxt
->GetValue());
1103 strcpy(Contact
->PostalCode
,pPostalCodeTxt
->GetValue());
1105 Contact
->Contributions
= atoi(pContribTxt
->GetValue());
1106 Contact
->LinesOfCode
= atol(pLinesTxt
->GetValue());
1108 Contact
->NativeLanguage
= (enum Language
) pNativeLangChoice
->GetSelection();
1109 Contact
->IsDeveloper
= pDeveloperRadio
->GetSelection();
1112 } // CeditorDlg::GetData()
1116 * Retrieve data from the dialog, verify the validity of the data, and if it is valid,
1117 * try to insert/update the data to the table based on the current 'mode' the dialog
1120 * A return value of TRUE means the insert/update was completed successfully, a return
1121 * value of FALSE means that Save() failed. If returning FALSE, then this function
1122 * has displayed a detailed error message for the user.
1124 Bool
CeditorDlg::Save()
1126 Bool failed
= FALSE
;
1128 // Read the data in the widgets of the dialog to get the user's data
1132 // Perform any other required validations necessary before saving
1137 wxBeginBusyCursor();
1139 if (mode
== mCreate
)
1141 RETCODE result
= Contact
->Insert();
1143 failed
= (result
!= DB_SUCCESS
);
1146 // Some errors may be expected, like a duplicate key, so handle those instances with
1147 // specific error messages.
1148 if (result
== DB_ERR_INTEGRITY_CONSTRAINT_VIOL
)
1151 tStr
= "A duplicate key value already exists in the table.\nUnable to save record\n\n";
1152 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1153 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1157 // Some other unexpexted error occurred
1159 tStr
= "Database insert failed\n\n";
1160 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1161 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1165 else // mode == mEdit
1167 if (!Contact
->Update())
1170 tStr
= "Database update failed\n\n";
1171 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1172 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1179 Contact
->pDb
->CommitTrans();
1180 SetMode(mView
); // Sets the dialog mode back to viewing after save is successful
1183 Contact
->pDb
->RollbackTrans();
1189 } // CeditorDlg::Save()
1193 * Where this program is only showing a single row at a time in the dialog,
1194 * a special where clause must be built to find just the single row which,
1195 * in sequence, would follow the currently displayed row.
1197 Bool
CeditorDlg::GetNextRec()
1201 w
= "NAME = (SELECT MIN(NAME) FROM ";
1202 w
+= Contact
->tableName
;
1203 w
+= " WHERE NAME > '";
1207 // If a query where string is currently set, append that criteria
1208 if (!Contact
->qryWhereStr
.Empty())
1211 w
+= Contact
->qryWhereStr
;
1217 return(GetRec(w
.GetData()));
1219 } // CeditorDlg::GetNextRec()
1223 * Where this program is only showing a single row at a time in the dialog,
1224 * a special where clause must be built to find just the single row which,
1225 * in sequence, would precede the currently displayed row.
1227 Bool
CeditorDlg::GetPrevRec()
1231 w
= "NAME = (SELECT MAX(NAME) FROM ";
1232 w
+= Contact
->tableName
;
1233 w
+= " WHERE NAME < '";
1237 // If a query where string is currently set, append that criteria
1238 if (!Contact
->qryWhereStr
.Empty())
1241 w
+= Contact
->qryWhereStr
;
1247 return(GetRec(w
.GetData()));
1249 } // CeditorDlg::GetPrevRec()
1253 * This function is here to avoid duplicating this same code in both the
1254 * GetPrevRec() and GetNextRec() functions
1256 Bool
CeditorDlg::GetRec(char *whereStr
)
1258 Contact
->where
= whereStr
;
1259 Contact
->orderBy
= "NAME";
1261 if (!Contact
->Query())
1264 tStr
= "ODBC error during Query()\n\n";
1265 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1266 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1271 if (Contact
->GetNext())
1278 } // CeditorDlg::GetRec()
1283 * CparameterDlg constructor
1285 CparameterDlg::CparameterDlg(wxWindow
*parent
) : wxDialogBox (parent
, "ODBC parameter settings", 1, -1, -1, 400, 275)
1287 // Since the ::OnCommand() function is overridden, this prevents the widget
1288 // detection in ::OnCommand() until all widgets have been initialized to prevent
1289 // uninitialized pointers from crashing the program
1290 widgetPtrsSet
= FALSE
;
1293 SetLabelPosition(wxVERTICAL
);
1295 wxFont
*ButtonFont
= new wxFont(12,wxSWISS
,wxNORMAL
,wxBOLD
);
1296 wxFont
*TextFont
= new wxFont(12,wxSWISS
,wxNORMAL
,wxNORMAL
);
1298 SetButtonFont(ButtonFont
);
1299 SetLabelFont(TextFont
);
1300 SetLabelPosition(wxVERTICAL
);
1302 pParamODBCSourceMsg
= new wxMessage(this, "ODBC data sources:", 10, 10, -1, -1, 0, "ParamODBCSourceMsg");
1303 pParamODBCSourceList
= new wxListBox(this, NULL
, "", wxSINGLE
|wxALWAYS_SB
, 10, 29, 285, 150, 0, 0, 0, "ParamODBCSourceList");
1305 pParamUserNameMsg
= new wxMessage(this, "Database user name:", 10, 193, -1, -1, 0, "ParamUserNameMsg");
1306 pParamUserNameTxt
= new wxText(this, NULL
, "", "", 10, 209, 140, 25, 0, "ParamUserNameTxt");
1308 pParamPasswordMsg
= new wxMessage(this, "Password:", 156, 193, -1, -1, 0, "ParamPasswordMsg");
1309 pParamPasswordTxt
= new wxText(this, NULL
, "", "", 156, 209, 140, 25, 0, "ParamPasswordTxt");
1311 pParamSaveBtn
= new wxButton(this, NULL
, "&Save", 310, 21, 70, 35, 0, "ParamSaveBtn");
1312 pParamCancelBtn
= new wxButton(this, NULL
, "C&ancel", 310, 66, 70, 35, 0, "ParamCancelBtn");
1314 // Now that all the widgets on the panel are created, its safe to allow ::OnCommand() to
1315 // handle all widget processing
1316 widgetPtrsSet
= TRUE
;
1319 savedParamSettings
= DatabaseDemoApp
.params
;
1324 } // CparameterDlg constructor
1327 Bool
CparameterDlg::OnClose()
1329 // Put any additional checking necessary to make certain it is alright
1330 // to close the program here that is not done elsewhere
1333 Bool Ok
= (wxMessageBox("No changes have been saved.\n\nAre you sure you wish exit the parameter screen?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
1338 DatabaseDemoApp
.params
= savedParamSettings
;
1341 if (GetParent() != NULL
)
1342 GetParent()->SetFocus();
1344 } // Cparameter::OnClose()
1347 void CparameterDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
1349 wxString widgetName
;
1351 widgetName
= win
.GetName();
1356 if (widgetName
== pParamSaveBtn
->GetName())
1361 tStr
= "Database parameters have been saved.";
1362 if (GetParent() != NULL
) // The parameter dialog was not called during startup due to a missing cfg file
1363 tStr
+= "\nNew parameters will take effect the next time the program is started.";
1364 wxMessageBox(tStr
.GetData(),"Notice...",wxOK
| wxICON_INFORMATION
);
1371 if (widgetName
== pParamCancelBtn
->GetName())
1376 } // CparameterDlg::OnCommand()
1379 Bool
CparameterDlg::PutData()
1381 // Fill the data source list box
1382 FillDataSourceList();
1384 // Fill in the fields from the params object
1385 pParamODBCSourceList
->SetStringSelection(DatabaseDemoApp
.params
.ODBCSource
);
1386 pParamUserNameTxt
->SetValue(DatabaseDemoApp
.params
.UserName
);
1387 pParamPasswordTxt
->SetValue(DatabaseDemoApp
.params
.Password
);
1389 } // CparameterDlg::PutData()
1392 Bool
CparameterDlg::GetData()
1395 if (pParamODBCSourceList
->GetStringSelection())
1397 tStr
= pParamODBCSourceList
->GetStringSelection();
1398 if (tStr
.Length() > (sizeof(DatabaseDemoApp
.params
.ODBCSource
)-1))
1401 errmsg
.sprintf("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());
1402 wxMessageBox(errmsg
.GetData(),"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1405 strcpy(DatabaseDemoApp
.params
.ODBCSource
, tStr
.GetData());
1410 tStr
= pParamUserNameTxt
->GetValue();
1411 if (tStr
.Length() > (sizeof(DatabaseDemoApp
.params
.UserName
)-1))
1414 errmsg
.sprintf("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());
1415 wxMessageBox(errmsg
.GetData(),"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1418 strcpy(DatabaseDemoApp
.params
.UserName
, tStr
.GetData());
1420 tStr
= pParamPasswordTxt
->GetValue();
1421 if (tStr
.Length() > (sizeof(DatabaseDemoApp
.params
.Password
)-1))
1424 errmsg
.sprintf("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());
1425 wxMessageBox(errmsg
.GetData(),"Internal program error...",wxOK
| wxICON_EXCLAMATION
);
1428 strcpy(DatabaseDemoApp
.params
.Password
,tStr
.GetData());
1430 } // CparameterDlg::GetData()
1433 Bool
CparameterDlg::Save()
1435 Cparameters saveParams
= DatabaseDemoApp
.params
;
1438 DatabaseDemoApp
.params
= saveParams
;
1443 if ((paramFile
= fopen(paramFilename
, "wt")) == NULL
)
1446 tStr
.sprintf("Unable to write/overwrite '%s'.",paramFilename
);
1447 wxMessageBox(tStr
.GetData(),"File I/O Error...",wxOK
| wxICON_EXCLAMATION
);
1451 fputs(DatabaseDemoApp
.params
.ODBCSource
, paramFile
);
1452 fputc('\n', paramFile
);
1453 fputs(DatabaseDemoApp
.params
.UserName
, paramFile
);
1454 fputc('\n', paramFile
);
1455 fputs(DatabaseDemoApp
.params
.Password
, paramFile
);
1456 fputc('\n', paramFile
);
1460 } // CparameterDlg::Save()
1463 void CparameterDlg::FillDataSourceList()
1465 char Dsn
[SQL_MAX_DSN_LENGTH
+ 1];
1467 wxStringList strList
;
1469 while(GetDataSource(DbConnectInf
.Henv
, Dsn
, SQL_MAX_DSN_LENGTH
+1, DsDesc
, 255))
1474 char **p
= strList
.ListToArray();
1476 for (int i
= 0; strlen(p
[i
]); i
++)
1477 pParamODBCSourceList
->Append(p
[i
]);
1478 } // CparameterDlg::CparameterDlg::FillDataSourceList()
1481 // CqueryDlg() constructor
1482 CqueryDlg::CqueryDlg(wxWindow
*parent
, wxDB
*pDb
, char *tblName
[], char *pWhereArg
) : wxDialogBox (parent
, "Query", 1, -1, -1, 480, 360)
1484 wxBeginBusyCursor();
1488 masterTableName
= tblName
[0];
1489 widgetPtrsSet
= FALSE
;
1492 // Initialize the WHERE clause from the string passed in
1493 pWhere
= pWhereArg
; // Save a pointer to the output buffer
1494 if (strlen(pWhere
) > DB_MAX_WHERE_CLAUSE_LEN
) // Check the length of the buffer passed in
1497 s
.sprintf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN
+1);
1498 wxMessageBox(s
.GetData(),"Error...",wxOK
| wxICON_EXCLAMATION
);
1504 SetLabelPosition(wxVERTICAL
);
1506 wxFont
*ButtonFont
= new wxFont(12,wxSWISS
,wxNORMAL
,wxBOLD
);
1507 wxFont
*TextFont
= new wxFont(12,wxSWISS
,wxNORMAL
,wxNORMAL
);
1509 SetButtonFont(ButtonFont
);
1510 SetLabelFont(TextFont
);
1511 SetLabelPosition(wxVERTICAL
);
1513 pQueryCol1Msg
= new wxMessage(this, "Column 1:", 10, 10, 69, 16, 0, "QueryCol1Msg");
1514 pQueryCol1Choice
= new wxChoice(this, NULL
, "", 10, 27, 250, 27, 0, 0, 0, "QueryCol1Choice");
1516 pQueryNotMsg
= new wxMessage(this, "NOT", 268, 10, -1, -1, 0, "QueryNotMsg");
1517 pQueryNotCheck
= new wxCheckBox(this, NULL
, "", 275, 37, 20, 20, 0, "QueryNotCheck");
1519 char *choice_strings
[9];
1520 choice_strings
[0] = "=";
1521 choice_strings
[1] = "<";
1522 choice_strings
[2] = ">";
1523 choice_strings
[3] = "<=";
1524 choice_strings
[4] = ">=";
1525 choice_strings
[5] = "Begins";
1526 choice_strings
[6] = "Contains";
1527 choice_strings
[7] = "Like";
1528 choice_strings
[8] = "Between";
1529 pQueryOperatorMsg
= new wxMessage(this, "Operator:", 305, 10, -1, -1, 0, "QueryOperatorMsg");
1530 pQueryOperatorChoice
= new wxChoice(this, NULL
, "", 305, 27, 80, 27, 9, choice_strings
, 0, "QueryOperatorChoice");
1532 pQueryCol2Msg
= new wxMessage(this, "Column 2:", 10, 65, 69, 16, 0, "QueryCol2Msg");
1533 pQueryCol2Choice
= new wxChoice(this, NULL
, "", 10, 82, 250, 27, 0, 0, 0, "QueryCol2Choice");
1535 pQuerySqlWhereMsg
= new wxMessage(this, "SQL where clause:", 10, 141, -1, -1, 0, "QuerySqlWhereMsg");
1536 pQuerySqlWhereMtxt
= new wxMultiText(this, NULL
, "", "", 10, 159, 377, 134, 0, "QuerySqlWhereMtxt");
1538 pQueryAddBtn
= new wxButton(this, NULL
, "&Add", 406, 24, 56, 26, 0, "QueryAddBtn");
1539 pQueryAndBtn
= new wxButton(this, NULL
, "A&nd", 406, 58, 56, 26, 0, "QueryAndBtn");
1540 pQueryOrBtn
= new wxButton(this, NULL
, "&Or", 406, 92, 56, 26, 0, "QueryOrBtn");
1542 pQueryLParenBtn
= new wxButton(this, NULL
, "(", 406, 126, 26, 26, 0, "QueryLParenBtn");
1543 pQueryRParenBtn
= new wxButton(this, NULL
, ")", 436, 126, 26, 26, 0, "QueryRParenBtn");
1545 pQueryDoneBtn
= new wxButton(this, NULL
, "&Done", 406, 185, 56, 26, 0, "QueryDoneBtn");
1546 pQueryClearBtn
= new wxButton(this, NULL
, "C&lear", 406, 218, 56, 26, 0, "QueryClearBtn");
1547 pQueryCountBtn
= new wxButton(this, NULL
, "&Count", 406, 252, 56, 26, 0, "QueryCountBtn");
1549 pQueryValue1Msg
= new wxMessage(this, "Value:", 277, 66, -1, -1, 0, "QueryValue1Msg");
1550 pQueryValue1Txt
= new wxText(this, NULL
, "", "", 277, 83, 108, 25, 0, "QueryValue1Txt");
1552 pQueryValue2Msg
= new wxMessage(this, "AND", 238, 126, -1, -1, 0, "QueryValue2Msg");
1553 pQueryValue2Txt
= new wxText(this, NULL
, "", "", 277, 120, 108, 25, 0, "QueryValue2Txt");
1555 pQueryHintGrp
= new wxGroupBox(this, "", 10, 291, 377, 40, 0, "QueryHintGrp");
1556 pQueryHintMsg
= new wxMessage(this, "", 16, 306, -1, -1, 0, "QueryHintMsg");
1558 widgetPtrsSet
= TRUE
;
1559 // Initialize the dialog
1561 pQueryCol2Choice
->Append("VALUE -->");
1562 colInf
= pDB
->GetColumns(tblName
);
1563 for (int i
= 0; colInf
[i
].colName
&& strlen(colInf
[i
].colName
); i
++)
1565 // If there is more than one table being queried, qualify
1566 // the column names with the table name prefix.
1567 if (tblName
[1] && strlen(tblName
[1]))
1569 qualName
.sprintf("%s.%s", colInf
[i
].tableName
, colInf
[i
].colName
);
1570 pQueryCol1Choice
->Append(qualName
.GetData());
1571 pQueryCol2Choice
->Append(qualName
.GetData());
1573 else // Single table query, append just the column names
1575 pQueryCol1Choice
->Append(colInf
[i
].colName
);
1576 pQueryCol2Choice
->Append(colInf
[i
].colName
);
1580 pQueryCol1Choice
->SetSelection(0);
1581 pQueryCol2Choice
->SetSelection(0);
1582 pQueryOperatorChoice
->SetSelection(0);
1584 pQueryValue2Msg
->Show(FALSE
);
1585 pQueryValue2Txt
->Show(FALSE
);
1587 pQueryHintMsg
->SetLabel(langQRY_EQ
);
1589 pQuerySqlWhereMtxt
->SetValue(pWhere
);
1593 // Display the dialog window
1598 } // CqueryDlg() constructor
1601 void CqueryDlg::OnCommand(wxWindow
& win
, wxCommandEvent
& event
)
1603 // Widget pointers won't be set when the dialog is constructed.
1604 // Control is passed through this function once for each widget on
1605 // a dialog as the dialog is constructed.
1609 wxString widgetName
= win
.GetName();
1611 // Operator choice box
1612 if (widgetName
== pQueryOperatorChoice
->GetName())
1614 // Set the help text
1615 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1618 pQueryHintMsg
->SetLabel(langQRY_EQ
);
1621 pQueryHintMsg
->SetLabel(langQRY_LT
);
1624 pQueryHintMsg
->SetLabel(langQRY_GT
);
1627 pQueryHintMsg
->SetLabel(langQRY_LE
);
1630 pQueryHintMsg
->SetLabel(langQRY_GE
);
1633 pQueryHintMsg
->SetLabel(langQRY_BEGINS
);
1636 pQueryHintMsg
->SetLabel(langQRY_CONTAINS
);
1639 pQueryHintMsg
->SetLabel(langQRY_LIKE
);
1642 pQueryHintMsg
->SetLabel(langQRY_BETWEEN
);
1646 // Hide the value2 widget
1647 pQueryValue2Msg
->Show(FALSE
); // BETWEEN will show this widget
1648 pQueryValue2Txt
->Show(FALSE
); // BETWEEN will show this widget
1650 // Disable the NOT operator for <, <=, >, >=
1651 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1657 pQueryNotCheck
->SetValue(0);
1658 pQueryNotCheck
->Enable(FALSE
);
1661 pQueryNotCheck
->Enable(TRUE
);
1665 // Manipulate the dialog to handle the selected operator
1666 switch((qryOp
) pQueryOperatorChoice
->GetSelection())
1673 pQueryCol2Choice
->Enable(TRUE
);
1674 if (pQueryCol2Choice
->GetSelection()) // Column name is highlighted
1676 pQueryValue1Msg
->Show(FALSE
);
1677 pQueryValue1Txt
->Show(FALSE
);
1679 else // "Value" is highlighted
1681 pQueryValue1Msg
->Show(TRUE
);
1682 pQueryValue1Txt
->Show(TRUE
);
1683 pQueryValue1Txt
->SetFocus();
1689 pQueryCol2Choice
->SetSelection(0);
1690 pQueryCol2Choice
->Enable(FALSE
);
1691 pQueryValue1Msg
->Show(TRUE
);
1692 pQueryValue1Txt
->Show(TRUE
);
1693 pQueryValue1Txt
->SetFocus();
1696 pQueryCol2Choice
->SetSelection(0);
1697 pQueryCol2Choice
->Enable(FALSE
);
1698 pQueryValue2Msg
->Show(TRUE
);
1699 pQueryValue2Txt
->Show(TRUE
);
1700 pQueryValue1Msg
->Show(TRUE
);
1701 pQueryValue1Txt
->Show(TRUE
);
1702 pQueryValue1Txt
->SetFocus();
1708 } // Operator choice box
1711 if (widgetName
== pQueryCol2Choice
->GetName())
1713 if (pQueryCol2Choice
->GetSelection()) // Column name is highlighted
1715 pQueryValue1Msg
->Show(FALSE
);
1716 pQueryValue1Txt
->Show(FALSE
);
1718 else // "Value" is highlighted
1720 pQueryValue1Msg
->Show(TRUE
);
1721 pQueryValue1Txt
->Show(TRUE
);
1722 pQueryValue1Txt
->SetFocus();
1726 } // Column 2 choice
1729 if (widgetName
== pQueryAddBtn
->GetName())
1737 if (widgetName
== pQueryAndBtn
->GetName())
1739 AppendToWhere(" AND\n");
1745 if (widgetName
== pQueryOrBtn
->GetName())
1747 AppendToWhere(" OR\n");
1752 // Left Paren button
1753 if (widgetName
== pQueryLParenBtn
->GetName())
1758 } // Left Paren button
1760 // Right paren button
1761 if (widgetName
== pQueryRParenBtn
->GetName())
1766 } // Right Paren button
1769 if (widgetName
== pQueryDoneBtn
->GetName())
1771 // Be sure the where clause will not overflow the output buffer
1772 if (strlen(pQuerySqlWhereMtxt
->GetValue()) > DB_MAX_WHERE_CLAUSE_LEN
)
1775 s
.sprintf("Maximum where clause length exceeded.\nLength must be less than %d", DB_MAX_WHERE_CLAUSE_LEN
+1);
1776 wxMessageBox(s
.GetData(),"Error...",wxOK
| wxICON_EXCLAMATION
);
1779 // Validate the where clause for things such as matching parens
1780 if (!ValidateWhereClause())
1782 // Copy the where clause to the output buffer and exit
1783 strcpy(pWhere
, pQuerySqlWhereMtxt
->GetValue());
1790 if (widgetName
== pQueryClearBtn
->GetName())
1792 Bool Ok
= (wxMessageBox("Are you sure you wish to clear the Query?","Confirm",wxYES_NO
|wxICON_QUESTION
) == wxYES
);
1795 pQuerySqlWhereMtxt
->SetValue("");
1801 if (widgetName
== pQueryCountBtn
->GetName())
1803 wxBeginBusyCursor();
1810 } // CqueryDlg::OnCommand
1813 Bool
CqueryDlg::OnClose()
1828 GetParent()->SetFocus();
1832 } // CqueryDlg::OnClose()
1835 Bool CqueryDlg::SetWidgetPtrs()
1839 abort = abort || !(pQueryCol1Choice = (wxChoice *)GetWidgetPtr("QueryCol1Choice",this));
1840 abort = abort || !(pQueryNotCheck = (wxCheckBox *)GetWidgetPtr("QueryNotCheck",this));
1841 abort = abort || !(pQueryOperatorChoice = (wxChoice *)GetWidgetPtr("QueryOperatorChoice",this));
1842 abort = abort || !(pQueryCol2Choice = (wxChoice *)GetWidgetPtr("QueryCol2Choice",this));
1843 abort = abort || !(pQueryValue1Txt = (wxText *)GetWidgetPtr("QueryValue1Txt",this));
1844 abort = abort || !(pQueryValue2Txt = (wxText *)GetWidgetPtr("QueryValue2Txt",this));
1845 abort = abort || !(pQuerySqlWhereMtxt = (wxMultiText *)GetWidgetPtr("QuerySqlWhereMtxt",this));
1846 abort = abort || !(pQueryAddBtn = (wxButton *)GetWidgetPtr("QueryAddBtn",this));
1847 abort = abort || !(pQueryAndBtn = (wxButton *)GetWidgetPtr("QueryAndBtn",this));
1848 abort = abort || !(pQueryOrBtn = (wxButton *)GetWidgetPtr("QueryOrBtn",this));
1849 abort = abort || !(pQueryLParenBtn = (wxButton *)GetWidgetPtr("QueryLParenBtn",this));
1850 abort = abort || !(pQueryRParenBtn = (wxButton *)GetWidgetPtr("QueryRParenBtn",this));
1851 abort = abort || !(pQueryDoneBtn = (wxButton *)GetWidgetPtr("QueryDoneBtn",this));
1852 abort = abort || !(pQueryClearBtn = (wxButton *)GetWidgetPtr("QueryClearBtn",this));
1853 abort = abort || !(pQueryCountBtn = (wxButton *)GetWidgetPtr("QueryCountBtn",this));
1854 abort = abort || !(pQueryHelpBtn = (wxButton *)GetWidgetPtr("QueryHelpBtn",this));
1855 abort = abort || !(pQueryHintMsg = (wxMessage *)GetWidgetPtr("QueryHintMsg",this));
1859 return(widgetPtrsSet = !abort);
1861 } // CqueryDlg::SetWidgetPtrs
1864 void CqueryDlg::AppendToWhere(char *s
)
1866 wxString whereStr
= pQuerySqlWhereMtxt
->GetValue();
1868 pQuerySqlWhereMtxt
->SetValue(whereStr
.GetData());
1870 } // CqueryDlg::AppendToWhere()
1873 void CqueryDlg::ProcessAddBtn()
1875 qryOp oper
= (qryOp
) pQueryOperatorChoice
->GetSelection();
1877 // Verify that eveything is filled in correctly
1878 if (pQueryCol2Choice
->GetSelection() == 0) // "Value" is selected
1880 // Verify that value 1 is filled in
1881 if (strlen(pQueryValue1Txt
->GetValue()) == 0)
1884 pQueryValue1Txt
->SetFocus();
1887 // For the BETWEEN operator, value 2 must be filled in as well
1888 if (oper
== qryOpBETWEEN
&&
1889 strlen(pQueryValue2Txt
->GetValue()) == 0)
1892 pQueryValue2Txt
->SetFocus();
1897 // Build the expression and append it to the where clause window
1898 wxString s
= pQueryCol1Choice
->GetStringSelection();
1900 if (pQueryNotCheck
->GetValue() && (oper
!= qryOpEQ
))
1906 if (pQueryNotCheck
->GetValue()) // NOT box is checked
1935 int col1Idx
= pQueryCol1Choice
->GetSelection();
1938 if (colInf
[col1Idx
].sqlDataType
== SQL_VARCHAR
||
1939 oper
== qryOpBEGINS
||
1940 oper
== qryOpCONTAINS
||
1944 if (pQueryCol2Choice
->GetSelection()) // Column name
1945 s
+= pQueryCol2Choice
->GetStringSelection();
1946 else // Column 2 is a "value"
1950 if (oper
== qryOpCONTAINS
)
1952 s
+= pQueryValue1Txt
->GetValue();
1953 if (oper
== qryOpCONTAINS
|| oper
== qryOpBEGINS
)
1959 if (oper
== qryOpBETWEEN
)
1964 s
+= pQueryValue2Txt
->GetValue();
1969 AppendToWhere(s
.GetData());
1971 } // CqueryDlg::ProcessAddBtn()
1974 void CqueryDlg::ProcessCountBtn()
1976 if (!ValidateWhereClause())
1979 if (dbTable
== 0) // wxTable object needs to be created and opened
1981 if (!(dbTable
= new wxTable(pDB
, masterTableName
, 0)))
1983 wxMessageBox("Memory allocation failed creating a wxTable object.","Error...",wxOK
| wxICON_EXCLAMATION
);
1986 if (!dbTable
->Open())
1989 tStr
= "ODBC error during Open()\n\n";
1990 tStr
+= GetExtendedDBErrorMsg(__FILE__
,__LINE__
);
1991 wxMessageBox(tStr
.GetData(),"ODBC Error...",wxOK
| wxICON_EXCLAMATION
);
1996 // Count() with WHERE clause
1997 dbTable
->where
= pQuerySqlWhereMtxt
->GetValue();
1998 ULONG whereCnt
= dbTable
->Count();
2000 // Count() of all records in the table
2002 ULONG totalCnt
= dbTable
->Count();
2004 if (whereCnt
> 0 || totalCnt
== 0)
2007 tStr
.sprintf("%lu of %lu records match the query criteria.",whereCnt
,totalCnt
);
2008 wxMessageBox(tStr
.GetData(),"Notice...",wxOK
| wxICON_INFORMATION
);
2013 tStr
.sprintf("%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
);
2014 wxMessageBox(tStr
.GetData(),"Notice...",wxOK
| wxICON_INFORMATION
);
2017 // After a wxMessageBox, the focus does not necessarily return to the
2018 // window which was the focus when the message box popped up, so return
2019 // focus to the Query dialog for certain
2022 } // CqueryDlg::ProcessCountBtn()
2025 Bool
CqueryDlg::ValidateWhereClause()
2027 wxString where
= pQuerySqlWhereMtxt
->GetValue();
2029 if (where
.Freq('(') != where
.Freq(')'))
2031 wxMessageBox("There are mismatched parenthesis in the constructed where clause","Error...",wxOK
| wxICON_EXCLAMATION
);
2034 // After a wxMessageBox, the focus does not necessarily return to the
2035 // window which was the focus when the message box popped up, so return
2036 // focus to the Query dialog for certain
2041 } // CqueryDlg::ValidateWhereClause()