//---------------------------------------------------------------------------- // ObjectWindows - (C) Copyright 1991, 1993 by Borland International //---------------------------------------------------------------------------- #include #include #include #include #include "diagxprt.rh" #include "setup.h" #include "diagxprt.h" #include // // The diagnostic setup dialog box is divided in 2 main groups: // * the system group (TSysGroup->TGroup); // * the user group (TUsrGroup->TGroup). // // Each group is a list of items (TSetupItem). // Items belonging to the system group (TSysItem->TSetupItem) are // different than items belonging to the user group (TUsrItem->TSetupItem) // in that the setup infos are not maintained the same way in OWL.INI. // // Each item TSysItem and TUsrItem is attached to a set of child windows // in the dialog box. Each such set of windows is abstracted thru a class: // TUsrWindow and TSysWindow respectively. Each of those classes is // generated by a class template, which parametrizes the exact contents // of the set of windows: TRadioButton/TRadioButton and TStatic/TEdit resp. // // The number of windows set is limited (by the area in the dialog box); // the number of items is NOT limited. Scroll bars are displayed when // necessary. Those scrollbars are abstracted thru the class TGroupScroll. // Therefore each group maintains two arrays: an array of items TItemsArray // and an array of window sets TWindowArray. // // // Utility function to copy diagnostic code to clipboard so that user // can paste into appropriate source file // void CopyToClipboard(TWindow *w, char *descr, char *className) { // Generate a C++ example on the clipboard, which is a very trivial // string subsitution operation. // string pattern( "// Diagnostic: %s\n" "DIAG_DEFINE_GROUP_INIT(OWL_INI, %s, 1, 0);\n" "//TRACEX(%s, errLevel, \042A comment \042 << aFunction());\n" ); pattern.substring("%s") = descr; pattern.substring("%s") = className; pattern.substring("%s") = className; TClipboard& c = w->OpenClipboard(); c.EmptyClipboard(); HANDLE hData = GlobalAlloc(GMEM_SHARE, pattern.length()+1); char far* pData = (char far*)GlobalLock(hData); pattern.copy(pData, pattern.length()+1); GlobalUnlock(hData); c.SetClipboardData(CF_TEXT, hData); c.CloseClipboard(); // Clipboard is responsible for freeing hData } // // class TSetupItem // TSetupItem::TSetupItem(char *C, char *D) : Enable(0), level(0) { Class = C; Descr = D; } void TSetupItem::operator =(TBaseSetupWindow& w) { // Unmap from a physical set of windows: set the enability and // warning level accordingly to the setup window: // SetEnable(w.GetEnable()); SetLevel(w.GetLevel()); } void TSetupItem::Load() { // Loads itself from .ini file - loads the diagnostic classname and the // diagnostic description: // char b[80]; GetPrivateProfileString(GetSection(), Class.c_str(), "0 0", b, sizeof(b), GetIniFile()); sscanf(b, "%d %d", &bEnable, &level); if (Descr == "") { GetPrivateProfileString(GetSectionDesc(), Class.c_str(), Class.c_str(), b, sizeof(b), GetIniFile()); Descr = b; } } void TSetupItem::Save() { // Save itself to the .ini file - save the diagnostic classname and the // diagnostic description: // char b[20]; sprintf(b, "%d %d", bEnable, level); WritePrivateProfileString(GetSection(), Class.c_str(), b, GetIniFile()); WritePrivateProfileString(GetSectionDesc(), Class.c_str(), Descr.c_str(), GetIniFile()); } // // class TDiagEnable // DEFINE_RESPONSE_TABLE1(TDiagEnable, TCheckBox) EV_NOTIFY_AT_CHILD(BN_CLICKED, BNClicked), END_RESPONSE_TABLE; // TDiagEnable provides an abstraction for the small check box associated // to each setup item. This checkbox enables of disables the associated // setup item. But the checkbox can itself be disabled (by a upper level // checkbox). // void TDiagEnable::BNClicked() { TCheckBox::BNClicked(); EnableWindow(TRUE); } BOOL TDiagEnable::EnableWindow(BOOL enable) { BOOL ret = TCheckBox::EnableWindow(enable); if (enable) enable = GetCheck(); if (W0) W0->EnableWindow(enable); if (W1) W1->EnableWindow(enable); if (S0) S0->EnableWindow(enable); if (S1) S1->EnableWindow(enable); return ret; } // // TMainEnable provides the abstraction for the main checkbox which // enables of disables ALL the diagnostic groups. It knows how to save/load // it state from the .ini file: // char* TMainEnable::Class = "Enabled"; TMainEnable::TMainEnable(TWindow* p, int n, TGroup* g1, TGroup* g2) : TDiagEnable(p, n, g1, g2) { Create(); Load(); } void TMainEnable::Load() { char b[80]; GetPrivateProfileString(SYS_CLS, Class, "0", b, sizeof(b), SYS_INI); SetCheck(atoi(b)); TDiagEnable::EnableWindow(TRUE); } void TMainEnable::Save() { char b[80]; wsprintf(b, "%d", GetCheck() != 0); WritePrivateProfileString(SYS_CLS, Class, b, SYS_INI); } // // TGroup is the base class for diagnostic groups. TGroup knows how to // enable/disable itself (by enabling/disabling all the associated setup // items), to load/save from the .ini file and to map/unmap its setup // items to the associated windows sets. The purpose of the map/unmap // operations is to allow setup items to be scrolled. // void TGroup::EnableWindow(BOOL bEnable) { for (int i = 0; i < Windows.GetItemsInContainer(); i++) Windows[i]->EnableWindow(bEnable); } void TGroup::Load() { for (int i = 0; i < Items.GetItemsInContainer(); i++) Items[i]->Load(); } void TGroup::Save() { Cleanup(); for (int i = 0; i < Items.GetItemsInContainer(); i++) Items[i]->Save(); } int TGroup::Map(int x) { // May have to re-validate // if (x == MapAsBefore) x = scrollPos; if (x > Items.GetItemsInContainer() || x == MapToBottom) x = Items.GetItemsInContainer() - Windows.GetItemsInContainer(); if (x < 0 || x == MapToTop) x = 0; scrollPos = x; // Map each item to a window, as long as they fit // for (int i = 0; i < Windows.GetItemsInContainer(); i++, x++) { if (x < Items.GetItemsInContainer()) { *Windows[i] = *Items[x]; Windows[i]->EnableWindow(IsEnable()); } else *Windows[i] = 0; // No more item: "empty" the associated window } return scrollPos; } int TGroup::UnMap() { int i, x; for (i = 0, x = scrollPos; i < Windows.GetItemsInContainer(); i++, x++) { if (x < Items.GetItemsInContainer()) *Items[x] = *Windows[i]; } return scrollPos; } // // class TSysGroup // TSysGroup::TSysGroup(TWindow *Dialog, int nID) { // Create the child windows // for (int i = 0; Dialog->GetDlgItem(nID + i*4); i++) Windows.Add(new TSysWindow(Dialog, nID + i*4)); // Create the predefined system items (read from resources) // HRSRC hRes = GetApplicationObject()->FindResource(IDR_SYS_ITEMS, RT_RCDATA); if (hRes) { HGLOBAL hData = GetApplicationObject()->LoadResource(hRes); if (hData) { LPSTR pData = (LPSTR)::LockResource(hData); // pData now points to a set of strings LPSTR pC = pData; LPSTR pD; while(pC && *pC) { char name[128]; char desc[128]; pD = pC + strlen(pC) + 2; lstrcpy(name, pC); lstrcpy(desc, pD); Items.Add(new TSysItem(name, desc)); pC = pD + strlen(pD) + 2; } #if !defined(__WIN32__) ::UnlockResource(hData); #endif } #if !defined(__WIN32__) ::FreeResource(hData); #endif } // Load the values associated to each item TGroup::Load(); } void TSysGroup::Cleanup() { WritePrivateProfileString(SYS_CLS, 0, 0, SYS_INI); WritePrivateProfileString(SYS_DSC, 0, 0, SYS_INI); } // // class TUsrGroup // TUsrGroup::TUsrGroup(TWindow *Dialog, int nID) { // Create the child windows // for (int i = 0; Dialog->GetDlgItem(nID + i*4); i++) Windows.Add(new TUsrWindow(Dialog, nID + i*4)); // Create the setup items (loaded from IniFile) // const int s = 4096; char *p, *b = new char[s]; GetPrivateProfileString(USR_DSC, 0, "", b, s, USR_INI); for (i = 0, p = b; p && *p; i++, p += strlen(p) + 1) Items.Add(new TUsrItem(p)); delete b; // Load the values associated to each item // TGroup::Load(); } void TUsrGroup::Cleanup() { // WritePrivateProfileString(USR_CLS, 0, 0, USR_INI); WritePrivateProfileString(USR_DSC, 0, 0, USR_INI); } // // TSizableDialog makes a dialog sizable. Two sizes are defined: a small // size and a large size. Call the Toggle function to switch to any size. // To determine the size, define a dummy control in the dialog which // provides the upper-right corner of the small size. // TSizableDialog also know how to center itself accordingly to the parent // window, and how to re-adjust its position after being centered, if gone // out of view. // void TSizableDialog::SetupWindow() { TDialog::SetupWindow(); HWND hBox = GetDlgItem(nBoxID); if (hBox) { ::ShowWindow(hBox, SW_HIDE); TRect rSmall, rLarge; ::GetWindowRect(hBox, (LPRECT)&rSmall); GetWindowRect(rLarge); small.cx = rSmall.left - rLarge.left; small.cy = rSmall.top - rLarge.top; large = rLarge.Size(); tSize = t_max; bMaximized = TRUE; // Tolerate some inaccuracy on the positionning of the small box... // if (abs(small.cx - large.cx) < 4) small.cx = large.cx; if (abs(small.cy - large.cy) < 4) small.cy = large.cy; Toggle(t_min); } Center(); } void TSizableDialog::Toggle(int tType) { TSize newSize; if (tType == t_minmax) tType = (tSize == t_min) ? t_max : t_min; tSize = tType; switch (tType) { case t_min: newSize = small; break; case t_max: newSize = large; break; default: return; } bMaximized = tType != t_max; SetWindowPos(0, TRect(TPoint(0, 0), newSize), SWP_NOZORDER|SWP_NOMOVE); // EnableControls(); } void TSizableDialog::EnableControls() { TRect rParent = GetWindowRect(); HWND hChld = GetWindow(GW_CHILD); while (hChld) { POINT pt[2]; ::GetWindowRect(hChld, (RECT*)&pt); if (!PtInRect(&rParent, pt[0])) ::EnableWindow(hChld, 0); hChld = ::GetWindow(hChld, GW_HWNDNEXT); } } void TSizableDialog::Center() { TRect rPar; TRect rDlg; GetWindowRect(rDlg); HWND hParent = GetParent(); if (!hParent) hParent = GetDesktopWindow(); ::GetWindowRect(hParent, &rPar); int wDlgWidth = rDlg.Width(); int wDlgHeight = (rDlg.bottom - rDlg.top); int w = (rPar.right - rPar.left) - wDlgWidth; int h = (rPar.bottom - rPar.top) - wDlgHeight; int x = rPar.left + (w / 2); int y = rPar.top + (h / 2); MoveWindow(x, y, wDlgWidth , wDlgHeight, FALSE); AdjustPos(); } void TSizableDialog::AdjustPos(void) { int W = GetSystemMetrics(SM_CXSCREEN); int H = GetSystemMetrics(SM_CYSCREEN); TRect r = GetWindowRect(); int w = r.Width(); int h = r.Height(); if (r.left < 0) r.left = 0; if (r.top < 0) r.top = 0; if (r.right > W) r.left = W - w; if (r.bottom > H) r.top -= (H - h); SetWindowPos(0, r.left, r.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } // // TGroupScroll provides the abstraction for a diagnostic group scrollbar. // Associated to a diagnostic group, TGroupScroll simply map/unmap the // diagnostic group on scroll events. It also knows how to hide itself when // scrolling is irrelevant for that group (and show back if necessary): DEFINE_RESPONSE_TABLE1(TGroupScroll, TScrollBar) EV_WM_VSCROLL, END_RESPONSE_TABLE; TGroupScroll::TGroupScroll(TWindow* w, int i, TGroup* g) : group(g), x(0), TScrollBar(w, i) { Create(); Initialize(0); } void TGroupScroll::Initialize(int pos) { min = 0; max = group->Items.GetItemsInContainer() - group->Windows.GetItemsInContainer(); if (max <= min) ShowWindow(SW_HIDE); // Hide itself because scrolling is irrelevant else { SetRange(min, max); TScrollBar::SetPosition(x = pos); EndScroll(); PageMagnitude = group->Windows.GetItemsInContainer() - 1; ShowWindow(SW_SHOW); // Show back itself, in case was previously hidden } } void TGroupScroll::EvVScroll(UINT code, UINT pos, HWND /*hCtl*/) { switch (code) { case SB_LINEDOWN: if (x < max) SetPosition(x + LineMagnitude); break; case SB_LINEUP: if (x > min) SetPosition(x - LineMagnitude); break; case SB_PAGEDOWN: if (x < max) SetPosition(x + PageMagnitude); break; case SB_PAGEUP: if (x > min) SetPosition(x - PageMagnitude); break; case SB_THUMBPOSITION: SetPosition(pos); break; case SB_ENDSCROLL: EndScroll(); break; } } void TGroupScroll::SetPosition(int pos) { x = pos; if (x < min) x = min; if (x > max) x = max; TScrollBar::SetPosition(x); // Perform the logical scroll by first unmaping the group and then // by maping it to the new position:' // group->UnMap(); group->Map(x); } void TGroupScroll::EndScroll() { ::EnableScrollBar(*this, SB_CTL, ESB_ENABLE_BOTH); if (x == min) ::EnableScrollBar(*this, SB_CTL, ESB_DISABLE_LTUP); if (x == max) ::EnableScrollBar(*this, SB_CTL, ESB_DISABLE_RTDN); if (::GetFocus()) { ::InvalidateRect(::GetFocus(), 0, TRUE); ::UpdateWindow(::GetFocus()); } } // // TSetupDialog drives the setup diagnostic dialog box // DEFINE_RESPONSE_TABLE1(TSetupDialog, TDialog) EV_COMMAND(IDC_MORE, CmZoom), EV_COMMAND(IDOK, CmOk), EV_COMMAND(IDC_ADD, CmAddUsr), EV_COMMAND(IDC_DEL, CmDelUsr), EV_COMMAND(IDC_EDT, CmEdtUsr), END_RESPONSE_TABLE; void TSetupDialog::SetupWindow() { TSizableDialog::SetupWindow(); // Create the two groups: // SysGroup = new TSysGroup(this, ID_SYS_CHECK); UsrGroup = new TUsrGroup(this, ID_USR_CHECK); // Create the master enable switch and make it known to each group: // MainEnable = new TMainEnable(this, ID_SYS_ENABLE, SysGroup, UsrGroup); SysGroup->SetMainSwitch(MainEnable); UsrGroup->SetMainSwitch(MainEnable); // Map each group to the first position: // SysGroup->Map(0); UsrGroup->Map(0); // Create the two group scrollbars and attach them to their groups: // SysScroll = new TGroupScroll(this, IDC_SYS_SCROLL, SysGroup); UsrScroll = new TGroupScroll(this, IDC_USR_SCROLL, UsrGroup); // Finally, create the actions buttons and update their enable states: // pDel = new TButton(this, IDC_DEL); pDel->Create(); pEdt = new TButton(this, IDC_EDT); pEdt->Create(); UpdateButtons(); } void TSetupDialog::CleanupWindow() { // Delete everybody // delete MainEnable; delete UsrScroll; delete SysScroll; delete SysGroup; delete UsrGroup; delete pDel; delete pEdt; } void TSetupDialog::CmZoom() { // The uses clicked on the button: resize the dialog box // and update the button title accordingly: // Toggle(TSizableDialog::t_minmax); SetDlgItemText(IDC_MORE, !IsMaximized()? "<<< &Less" : "&More >>>"); } void TSetupDialog::CmOk() { // The user validated the changes: unmap to get the physical states and // save each group: // SysGroup->UnMap(); UsrGroup->UnMap(); SysGroup->Save(); UsrGroup->Save(); MainEnable->Save(); TSizableDialog::CmOk(); } void TSetupDialog::CmAddUsr() { // The user selected the 'Add User Group' command. Execute the AddUser // dialog box // TAddUsrDialog Dialog(this); if (Dialog.Execute() == IDOK) { // Unmap the user group to not lose the last changes, append // the new user group to the end of the group and map the group // by positionning to the bottom: // UsrGroup->UnMap(); UsrGroup->Items.Add(new TUsrItem(Dialog.Class, Dialog.Descr)); int x = UsrGroup->Map(TGroup::MapToBottom); // Don't forget to reset the scrollbar: // if (UsrScroll) UsrScroll->Initialize(x); } UpdateButtons(); } void TSetupDialog::CmDelUsr() { // The user selected the 'Delete User Group' command. Execute the DelUser // dialog box // TDelUsrDialog Dialog(this, &UsrGroup->Items); if (Dialog.Execute() == IDOK) { // Unmap the user group to not lose the last changes, delete // the chosen user group re-map the group: // int x = UsrGroup->UnMap(); UsrGroup->Items.Detach(Dialog.nSel); x = UsrGroup->Map(x - 1); // Don't forget to reset the scrollbar: // if (UsrScroll) UsrScroll->Initialize(x); } UpdateButtons(); } void TSetupDialog::CmEdtUsr() { // The user selected the 'Edit User Group' command. Execute the EdtUser // dialog box // TEdtUsrDialog Dialog(this, &UsrGroup->Items); if (Dialog.Execute() == IDOK) { UsrGroup->Map(TGroup::MapAsBefore); } } void TSetupDialog::UpdateButtons() { // Can only edit/delete not empty user group! // BOOL bEnable = UsrGroup->Items.GetItemsInContainer() > 0; pDel->EnableWindow(bEnable); pEdt->EnableWindow(bEnable); } // // TAddUsrDialog: Prompts the user for two informations - a diagnostic // classname and a diagnostic description. Require the classname and // defaults the description to the classname. // DEFINE_RESPONSE_TABLE1(TAddUsrDialog, TDialog) EV_COMMAND(IDOK, CmOk), END_RESPONSE_TABLE; void TAddUsrDialog::CmOk() { ::GetWindowText(GetDlgItem(IDC_DESCR), Descr, sizeof(Descr)); ::GetWindowText(GetDlgItem(IDC_CLASS), Class, sizeof(Class)); if (!*Class) { // Classname is required // MessageBox( string(*GetModule(), IDS_ERR_ADD_TXT).c_str(), string(*GetModule(), IDS_ERR_ADD_CAP).c_str(), MB_OK|MB_ICONHAND); ::SetFocus(GetDlgItem(IDC_CLASS)); return; } if (!*Descr) // Defaults the description to the classname // strcpy(Descr, Class); // Put the template on the clipboard // CopyToClipboard(this, Descr, Class); TDialog::CmOk(); } // // TItemsDialog: serves as a base class to the dialogs which need to // show to the user a list of setup items. Know how to fill itself, // given an array of setup items. // DEFINE_RESPONSE_TABLE1(TItemsDialog, TDialog) EV_COMMAND(IDOK, CmOk), EV_LBN_DBLCLK(IDC_LIST, LBDblClk), EV_LBN_SELCHANGE(IDC_LIST, LBSelChange), END_RESPONSE_TABLE; void TItemsDialog::SetupWindow() { CHECK(items != 0); pList = new TListBox(this, IDC_LIST); CHECK(pList != 0); pList->Create(); for (int i = 0; i < items->GetItemsInContainer(); i++) pList->AddString((*items)[i]->GetDescr()); } TItemsDialog::~TItemsDialog() { delete pList; } // // TDelUsrDialog: animates the 'Delete User Group' dialog box. Just have // to do the error handling, the base class doing the rest: // void TDelUsrDialog::CmOk() { nSel = pList->GetSelIndex(); if (nSel == LB_ERR) { MessageBox( string(*GetModule(), IDS_ERR_DEL_TXT).c_str(), string(*GetModule(), IDS_ERR_DEL_CAP).c_str(), MB_OK|MB_ICONHAND); ::SetFocus(*pList); } else TItemsDialog::CmOk(); } // // TEdtUsrDialog: animates the 'edit User Group' dialog box. This enable // the user to change classname/description of setup items after having // created them. Also, generates on demand a C++ example on the clipboard: // DEFINE_RESPONSE_TABLE1(TEdtUsrDialog, TItemsDialog) END_RESPONSE_TABLE; void TEdtUsrDialog::LBSelChange() { if (nPrevSel != LB_ERR) { char Class[40], Descr[40]; GetDlgItemText(IDC_DESCR, Descr, sizeof(Descr)); GetDlgItemText(IDC_CLASS, Class, sizeof(Class)); (*items)[nPrevSel]->SetDescr(Descr); (*items)[nPrevSel]->SetClass(Class); pList->SetItemData(nPrevSel, (DWORD)Descr); } if ((nPrevSel = nSel = pList->GetSelIndex()) != LB_ERR) { SetDlgItemText(IDC_DESCR, (*items)[nSel]->GetDescr()); SetDlgItemText(IDC_CLASS, (*items)[nSel]->GetClass()); } } void TEdtUsrDialog::CmOk() { LBSelChange(); char descr[40], className[40]; GetDlgItemText(IDC_DESCR, descr, sizeof(descr)); GetDlgItemText(IDC_CLASS, className, sizeof(className)); CopyToClipboard(this, descr, className); TItemsDialog::CmOk(); }