/************************************************************************
*
* Extended dialog classes
*
* Written by Anna Metcalfe (code@annasplace.me.uk)
*
************************************************************************
*
* Filename : NGDialog.cpp
*
* Compiler : Microsoft Visual C++ 6.0, Service Pack 3 or later
* Microsoft Visual C++.NET
*
* Target
* Environment : Win98/Me/NT/2000/XP
*
* NOTE:
*
* Your are free to use this code in your own products, PROVIDED
* this notice is not removed or modified.
*
************************************************************************/
#include "StdAfx.h"
#include <AfxPriv.h> // For HID_BASE_CONTROL definition
#include "NGDebugTrace.h"
#include "NGMacros.h"
#include "NGUtils.h"
#include "NGDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// ID value for ComboBox edit controls
// Refer to "C/C++ Q & A" in the April 1996 edition of MSJ for more details
#define IDC_COMBOEDIT 1001
/////////////////////////////////////////////////////////////////////////////
// Helper functions (NOT exported)
//
// These should really be virtuals in CNGDialog and CNGPropertyPage
// The only reason they aren't is that this way we don't end up with
// two sets of identical code.
//
// One day I'll get around to building a C++ class template to overcome this...
typedef enum { CTRL_NONE = -1,
CTRL_STATIC,
CTRL_BUTTON,
CTRL_EDIT,
CTRL_LISTBOX,
CTRL_COMBOBOX,
CTRL_LISTCTRL,
CTRL_TREECTRL,
CTRL_SLIDER,
CTRL_UNKNOWN } CTRL_TYPE;
// Use ::GetClassName() in the Win32 SDK to determine the type
// of a control from its hWnd
static CTRL_TYPE IdentifyCtrl(HWND hWnd)
{
// These are the window class names for the most common ones!
static LPCTSTR szWndClass[] = {
_T("static"), // Static text
_T("button"), // Button
_T("edit"), // Edit box
_T("listbox"), // List box
_T("combobox"), // Combo box
_T("SysListView32"), // List control
_T("SysTreeView32"), // Tree control
_T("msctls_trackbar32") // Slider control
};
int eType = CTRL_NONE;
static TCHAR szClass[20];
if (::IsWindow(hWnd))
{
// Get the window class of hWnd
::GetClassName(hWnd, szClass, sizeof(szClass) );
for (eType = 0; eType < CTRL_UNKNOWN; eType++)
{
if (::lstrcmpi( szWndClass[eType], szClass ) == 0)
{
break;
}
}
}
return (CTRL_TYPE)eType;
}
// Given the HWND of a control, return it's ID.
// This function is only necessary to handle radio buttons (nasty, horrible things!)
static UINT GetCtrlID(CWnd* pDlg, HWND hWndCtrl)
{
UINT uCtrlID = 0;
if (::IsChild(pDlg->GetSafeHwnd(), hWndCtrl))
{
while ( (hWndCtrl != NULL) && (::GetParent(hWndCtrl) != pDlg->GetSafeHwnd()) )
{
hWndCtrl = ::GetParent(hWndCtrl);
}
// Find out whether we're dealing with a radio button...
if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
{
// ...Radio Buttons are actually multiple controls, with only the first
// (usually) having an ID. Hence we need to walk up the Z order to
// the first one, which should have the group property (WS_GROUP)
// and a valid ID...
while ( (::GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP) == 0)
{
hWndCtrl = ::GetNextWindow(hWndCtrl, GW_HWNDPREV);
}
}
uCtrlID = ::GetDlgCtrlID(hWndCtrl); // Found the group control for this radio button
}
return uCtrlID;
}
// Identifies control notifications which could result in a change in the
// VALUE of a control (not necessarily the same as its contents!)
// TO DO: Handle slider and other common controls correctly
static BOOL IsCtrlValueChanging(CWnd* pDlg, UINT uID, HWND hWnd, int nCode)
{
//TRACE1("Control ID: %d\n", uID);
BOOL bResult = FALSE;
switch (::IdentifyCtrl(hWnd))
{
case CTRL_EDIT:
//TRACE1("\tEdit Control: nCode = %d\n", nCode);
if (EN_KILLFOCUS == nCode)
{
CEdit* pEdit = (CEdit*)pDlg->GetDlgItem(uID);
if (pEdit->GetModify()) // Edit control modified since last UpdateData()
{
pEdit->SetModify(FALSE); // Set as unmodified As UpdateData() about to be called
bResult = TRUE;
}
}
break;
case CTRL_BUTTON:
if (BN_CLICKED == nCode)
{
// TO DO: Walk to parent radio button if ID is IDC_STATIC
// If we don't do this pBtn will be NULL and this will fail
CButton* pBtn = (CButton*)pDlg->GetDlgItem(uID);
if (pBtn != NULL)
{
UINT nStyle = pBtn->GetButtonStyle();
if ( ( (nStyle & BS_CHECKBOX) == BS_CHECKBOX) ||
( (nStyle & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX) )
{
//TRACE1("\tCheckbox Control: nCode = %d\n", nCode);
bResult = TRUE;
}
else if ( ( (nStyle & BS_RADIOBUTTON) == BS_RADIOBUTTON) ||
( (nStyle & BS_AUTORADIOBUTTON) == BS_AUTORADIOBUTTON) )
{
//TRACE1("\tRadio Button Control: nCode = %d\n", nCode);
bResult = TRUE;
}
else
{
//TRACE1("\tOther button Control: nCode = %d\n", nCode);
bResult = TRUE;
}
}
}
break;
case CTRL_LISTBOX:
//TRACE1("\tListBox Control: nCode = %d\n", nCode);
if (LBN_SELCHANGE == nCode)
{
bResult = TRUE;
}
break;
case CTRL_COMBOBOX:
//TRACE1("\tComboBox Control: nCode = %d\n", nCode);
if (CBN_SELCHANGE == nCode)
{
bResult = TRUE;
}
else if (CBN_KILLFOCUS == nCode)
{
// When a combo box loses the focus we need to treat the edit control
// within it in the same way as any other edit control.
// ComboBox edit controls have an ID of 1001 [refer to "C/C++ Q & A"
// in the April 1996 edition of MSJ for more details]
CComboBox* pComboBox = (CComboBox*)pDlg->GetDlgItem(uID);
ASSERT(pComboBox != NULL);
CEdit* pEdit = (CEdit*)pComboBox->GetDlgItem(IDC_COMBOEDIT);
if ( (pEdit != NULL) && (pEdit->GetModify()) )
{ // If the edit control has been modified since the
pEdit->SetModify(FALSE); // last UpdateData(), mark it as unmodified
bResult = TRUE; // [UpdateData() is about to be called]
}
}
break;
case CTRL_SLIDER: // TO DO: Crack WM_NOTIFY messages
//TRACE1("\tSlider Control: nCode = %d\n", nCode);
break;
default:
break;
}
return bResult;
}
/////////////////////////////////////////////////////////////////////////////
// CNGDialog class
IMPLEMENT_DYNAMIC(CNGDialog, CNGDialog_BASE)
CNGDialog::CNGDialog(UINT uID /*= 0*/, CWnd* pParentWnd /*= NULL*/)
: CNGDialog_BASE(uID, pParentWnd)
{
m_pDoc = NULL;
m_pServer = NULL;
m_bModified = false;
m_bLockCtrlUpdates = false;
m_rectInitialPosition.SetRectEmpty();
m_rectPosition.SetRectEmpty();
//{{AFX_DATA_INIT(CNGDialog)
//}}AFX_DATA_INIT
}
CNGDialog::~CNGDialog(void)
{
}
void CNGDialog::DoDataExchange(CDataExchange* pDX)
{
CNGDialog_BASE::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CNGDialog)
//}}AFX_DATA_MAP
// These are out of Classwizard's grasp as it can't handle the syntax
}
BEGIN_MESSAGE_MAP(CNGDialog, CNGDialog_BASE)
//{{AFX_MSG_MAP(CNGDialog)
ON_WM_MOVE()
ON_WM_HELPINFO()
ON_WM_CONTEXTMENU()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// Override the default message processing to try to figure out
// when the value of a control has changed
//
// NOTES:
// 1. UpdateData(TRUE) is automatically called if the control value
// could have changed, and DDV validation succeeds
//
// 2. The detection of changed controls is not perfect, and hence the Apply
// button could be erroneously enabled under certain circumstances.
//
BOOL CNGDialog::OnCommand(WPARAM wParam, LPARAM lParam)
{
BOOL bResult = FALSE;
// Crack message parameters
// This bit was nicked from CPropertySheet::OnCommand()
UINT uID = LOWORD(wParam);
HWND hWnd = (HWND)lParam;
int nCode = HIWORD(wParam);
UINT uCtrlID = uID; // We'll need this later
// If the command notification was a control value change
// which could be linked up via DDX update the DDX member
// vars and send a WM_COMMAND notification on the affected
// control (this allows a generic ON_COMMAND handler to be
// used, irrespective of the type of the control)
if (hWnd != NULL)
{
// Catch ENTER keypresses on a control
// and convert to a CN_COMMAND call for that control
// This allows the control focused when ENTER was pressed to receive
// the change notification...
if (uID == IDOK)
{
// Walk up from the focused window until we find a valid control
HWND hWndFocusCtrl = ::GetFocus();
while ( (hWndFocusCtrl != NULL) && (::GetParent(hWndFocusCtrl) != m_hWnd) )
{
hWndFocusCtrl=::GetParent(hWndFocusCtrl);
}
// If the focused control is the one we orignially received the
// notification from it must be the OK button itself, so
// let it through unchanged
if ( (hWndFocusCtrl == NULL) || (hWndFocusCtrl == hWnd) )
{
return CNGDialog_BASE::OnCommand(wParam, lParam);
}
// Fire a CN_COMMAND notification on the focused control
hWnd = hWndFocusCtrl;
wParam = MAKEWPARAM(uID, CN_COMMAND);
lParam = (LPARAM)hWnd;
::SendMessage(hWnd, WM_COMMAND, wParam, lParam);
// Move the focus to the next control
::PostMessage(m_hWnd, WM_NEXTDLGCTL, 0, 0L);
bResult = TRUE; // Prevent dialog from closing
}
else if (nCode == CN_COMMAND)
{
uCtrlID = ::GetCtrlID(this, hWnd);
hWnd = ::GetDlgItem(GetSafeHwnd(), uCtrlID);
ASSERT(uCtrlID != 0);
ASSERT(uCtrlID != 65535);
// If the ID of the control in the message isn't the same as that
// of the control we've identified, pass it through
// (this can happen with radio buttons)
if (uID != uCtrlID)
{
bResult = CNGDialog_BASE::OnCommand(wParam, lParam);
}
}
else
{
// All non-button controls end up here...
bResult = CNGDialog_BASE::OnCommand(wParam, lParam);
}
if (::IsCtrlValueChanging(this, uCtrlID, hWnd, nCode) && !m_bLockCtrlUpdates)
{
bResult = OnCtrlValueChanging(uCtrlID, hWnd, nCode);
}
}
else
{
bResult = CNGDialog_BASE::OnCommand(wParam, lParam);
}
return bResult;
}
// Called when the value of a control has been changed
BOOL CNGDialog::OnCtrlValueChanging(UINT uCtrlID, HWND hWnd, int nCode)
{
UNREFERENCED_PARAMETER(hWnd);
UNREFERENCED_PARAMETER(nCode);
BOOL bResult = UpdateData(TRUE); // Try to retrieve dialog data via DoDataExchange()
if (bResult)
{ // Validation succeeded...
OnCmdMsg(uCtrlID, 0, NULL, NULL); // ...so generate a WM_COMMAND notification...
SetModified(); // ...and mark the dialog as changed
}
return bResult;
}
// This override is necessary to prevent re-entrant calls to UpdateData() via OnCommand()
void CNGDialog::OnOK(void)
{
ASSERT_VALID(this);
m_bLockCtrlUpdates = true;
CNGDialog_BASE::OnOK();
m_bLockCtrlUpdates = false;
}
/////////////////////////////////////////////////////////////////////////////
// CNGDialog Help Support
// Context help generated by pressing F1 or using the "?" button on the title
// bar of the dialog.
BOOL CNGDialog::OnHelpInfo(HELPINFO* pHelpInfo)
{
if (pHelpInfo->iContextType == HELPINFO_WINDOW)
{
// Launch WinHelp or HtmlHelp
// The HELP_WM_HELP flag brings up pop-up help and expects an array
// of DWORD pairs of the control ID and the context help ID
UINT uCtrlID = pHelpInfo->iCtrlId;
if ( (uCtrlID != 0) && (uCtrlID != IDC_STATIC) )
{
CTRLID_HELPID_PAIR dwHelpIds( uCtrlID,
uCtrlID + HID_BASE_CONTROL);
::WinHelp( (HWND)pHelpInfo->hItemHandle,
AfxGetApp()->m_pszHelpFilePath,
HELP_WM_HELP,
(DWORD)&dwHelpIds);
}
}
return TRUE;
}
// Context help generated by right clicking the control and selecting the
// "Whats this ?" menu.
void CNGDialog::OnContextMenu(CWnd* pWnd, CPoint point)
{
// The title bar has a system provided context menu which is displayed for
// a right mouse button up
if (HTCLIENT == OnNcHitTest(point))
{
//TRACE("OnContextMenu - HTCIENT\n");
CWnd* pChildWnd;
if (pWnd == this) //if the control on the dialog is disabled
{
// get the hWnd of the disabled child window
ScreenToClient(&point);
pChildWnd = ChildWindowFromPoint(point, CWP_ALL);
}
else
{
// for OK, Cancel and Apply (if enabled) buttons
pChildWnd = pWnd;
}
// Launch WinHelp or HtmlHelp
// The HELP_CONTEXTMENU flag brings up the "Whats this ?" menu and selecting
// the menu in turn brings up the pop-up help. It expects an array
// of DWORD pairs of the control ID and the context help ID
UINT uCtrlID = pChildWnd->GetDlgCtrlID();
if ( (uCtrlID != 0) && (uCtrlID != IDC_STATIC) )
{
CTRLID_HELPID_PAIR dwHelpIds( uCtrlID,
uCtrlID + HID_BASE_CONTROL);
::WinHelp( (HWND)pWnd->m_hWnd,
AfxGetApp()->m_pszHelpFilePath,
HELP_CONTEXTMENU,
(DWORD)&dwHelpIds);
}
}
}
/////////////////////////////////////////////////////////////////////////////
// CNGDialog operations
BOOL CNGDialog::SetServer(CObject* pServer)
{
m_pServer = pServer;
ASSERT_VALID(m_pServer);
return (m_pServer != NULL);
}
BOOL CNGDialog::SetDocument(CDocument* pDocument)
{
m_pDoc = pDocument;
ASSERT_VALID(m_pDoc);
return (m_pDoc != NULL);
}
void CNGDialog::SetModified(BOOL bChanged /*= TRUE*/)
{
m_bModified = bChanged;
}
BOOL CNGDialog::EnableDlgControl(UINT uID, BOOL bEnable)
{
CWnd* pWnd = GetDlgItem(uID);
if (NULL != pWnd)
{
pWnd->EnableWindow(bEnable);
return TRUE;
}
ASSERT(FALSE);
return FALSE;
}
BOOL CNGDialog::ShowDlgControl(UINT uID, BOOL bShow)
{
CWnd* pWnd = GetDlgItem(uID);
if (NULL != pWnd)
{
pWnd->ShowWindow(bShow);
return TRUE;
}
ASSERT(FALSE);
return FALSE;
}
BOOL CNGDialog::SetReadOnly(UINT uID, BOOL bReadOnly /*= TRUE*/)
{
CEdit* pCtrl = static_cast<CEdit*>( GetDlgItem(uID) );
if (NULL != pCtrl)
{
pCtrl->SetReadOnly(bReadOnly);
return TRUE;
}
ASSERT(FALSE);
return FALSE;
}
BOOL CNGDialog::UpdateData(BOOL bSaveAndValidate /*= TRUE*/)
{
if (IsWindow(m_hWnd))
{
return CNGDialog_BASE::UpdateData(bSaveAndValidate);
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// CNGPropertySheet overrides
// Override to initialise data BEFORE the screen controls
BOOL CNGDialog::OnInitDialog(void)
{
// Update initial data from the server
OnInitData();
BOOL bResult = CNGDialog_BASE::OnInitDialog();
SetInitialPosition();
return bResult;
}
void CNGDialog::OnInitData(void)
{
if (IsWindow(m_hWnd))
{
UpdateData(FALSE);
}
}
/////////////////////////////////////////////////////////////////////////////
// CNGPropertySheet message handlers
void CNGDialog::OnMove(int x, int y)
{
CNGDialog_BASE::OnMove(x, y);
GetWindowRect(&m_rectPosition);
}
/////////////////////////////////////////////////////////////////////////////
// Implementation
void CNGDialog::SetInitialPosition(void)
{
// If we've got a position from last time use it to position
// our window this time
if (!m_rectInitialPosition.IsRectEmpty())
{
// As the property sheet is not scaleable at the moment we need
// to make sure we change the size of m_rectPosition to match
// the size of the window as it is now (remember that adding or
// deleting pages between invokations may change the size)
m_rectInitialPosition.BottomRight() =
CPoint( m_rectInitialPosition.left + m_rectPosition.Width(),
m_rectInitialPosition.top + m_rectPosition.Height() );
MoveWindow(m_rectInitialPosition);
}
}
/////////////////////////////////////////////////////////////////////////////
// CNGDialog message handlers
/////////////////////////////////////////////////////////////////////////////
// CNGPropertyPage property page
IMPLEMENT_DYNAMIC(CNGPropertyPage, CNGPropertyPage_BASE)
CNGPropertyPage::CNGPropertyPage( UINT uID /*= 0*/,
UINT uIDCaption /*= 0*/,
UINT uTitleID /*= 0*/,
UINT uSubtitleID /*= 0*/)
: CNGPropertyPage_BASE(uID, uIDCaption, uTitleID, uSubtitleID)
{
m_pDoc = NULL;
m_pServer = NULL;
m_bLockCtrlUpdates = false;
m_bModified = false;
//{{AFX_DATA_INIT(CNGPropertyPage)
//}}AFX_DATA_INIT
}
CNGPropertyPage::CNGPropertyPage( LPCTSTR pszCaption,
UINT uIDCaption /*= 0*/,
UINT uTitleID /*= 0*/,
UINT uSubtitleID /*= 0*/)
: CNGPropertyPage_BASE(pszCaption, uIDCaption, uTitleID, uSubtitleID)
{
m_pDoc = NULL;
m_pServer = NULL;
m_bLockCtrlUpdates = false;
}
CNGPropertyPage::~CNGPropertyPage(void)
{
}
void CNGPropertyPage::DoDataExchange(CDataExchange* pDX)
{
CNGPropertyPage_BASE::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CNGPropertyPage)
//}}AFX_DATA_MAP
// These are out of Classwizard's grasp as it can't handle the syntax
}
BEGIN_MESSAGE_MAP(CNGPropertyPage, CNGPropertyPage_BASE)
//{{AFX_MSG_MAP(CNGPropertyPage)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// Override to initialise data BEFORE the screen controls
BOOL CNGPropertyPage::OnInitDialog(void)
{
OnInitData();
return CNGPropertyPage_BASE::OnInitDialog();
}
void CNGPropertyPage::OnInitData(void)
{
if (IsWindow(m_hWnd))
{
UpdateData(FALSE);
}
}
// Override the default message processing to try to figure out
// when the value of a control has changed, so that we can enable
// the Apply button
//
// NOTES:
// 1. UpdateData(TRUE) is automatically called if the control value
// could have changed, and DDV validation succeeds
//
// 2. The detection of changed controls is not perfect, and hence the Apply
// button could be erroneously enabled under certain circumstances.
//
BOOL CNGPropertyPage::OnCommand(WPARAM wParam, LPARAM lParam)
{
BOOL bResult = FALSE;
// Crack message parameters
// This bit was nicked from CPropertySheet::OnCommand()
UINT uID = LOWORD(wParam);
HWND hWnd = (HWND)lParam;
int nCode = HIWORD(wParam);
UINT uCtrlID = uID; // We'll need this later
// If the command notification was a control value change
// which could be linked up via DDX update the DDX member
// vars and send a WM_COMMAND notification on the affected
// control (this allows a generic ON_COMMAND handler to be
// used, irrespective of the type of the control)
if (hWnd != NULL)
{
// Catch ENTER keypresses on a control
// and convert to a CN_COMMAND call for that control
// This allows the control focused when ENTER was pressed to receive
// the change notification...
if (uID == IDOK)
{
// Walk up from the focused window until we find a valid control
HWND hWndFocusCtrl = ::GetFocus();
while ( (hWndFocusCtrl != NULL) && (::GetParent(hWndFocusCtrl) != m_hWnd) )
{
hWndFocusCtrl=::GetParent(hWndFocusCtrl);
}
// If the focused control is the one we orignially received the
// notification from it must be the OK button itself, so
// let it through unchanged
if ( (hWndFocusCtrl == NULL) || (hWndFocusCtrl == hWnd) )
{
return CNGPropertyPage_BASE::OnCommand(wParam, lParam);
}
// CN_COMMAND notification fires on the focused control
hWnd = hWndFocusCtrl;
}
else if (nCode == CN_COMMAND)
{
uCtrlID = ::GetCtrlID(this, hWnd);
hWnd = ::GetDlgItem(GetSafeHwnd(), uCtrlID);
ASSERT(uCtrlID != 0);
ASSERT(uCtrlID != 65535);
// If the ID of the control in the message isn't the same as that
// of the control we've identified, pass it through
// (this can happen with radio buttons)
if (uID != uCtrlID)
{
bResult = CNGPropertyPage_BASE::OnCommand(wParam, lParam);
}
}
else
{
// All non-button controls end up here...
bResult = CNGPropertyPage_BASE::OnCommand(wParam, lParam);
}
if (::IsCtrlValueChanging(this, uCtrlID, hWnd, nCode) && !m_bLockCtrlUpdates)
{
bResult = OnCtrlValueChanging(uCtrlID, hWnd, nCode);
}
}
else
{
bResult = CNGPropertyPage_BASE::OnCommand(wParam, lParam);
}
return bResult;
}
// Called when the value of a control has been changed
BOOL CNGPropertyPage::OnCtrlValueChanging(UINT uCtrlID, HWND hWnd, int nCode)
{
UNREFERENCED_PARAMETER(hWnd);
UNREFERENCED_PARAMETER(nCode);
BOOL bResult = UpdateData(TRUE); // Try to retrieve dialog data via DoDataExchange()
if (bResult)
{ // Validation succeeded...
OnCmdMsg(uCtrlID, 0, NULL, NULL); // ...so generate a WM_COMMAND notification...
SetModified(); // ...and mark the page as changed
}
return bResult;
}
BOOL CNGPropertyPage::OnApply(void)
{
ASSERT_VALID(this);
BOOL bResult = CNGPropertyPage_BASE::OnApply();
m_bModified = FALSE;
return bResult;
}
// Override to let the parent know when a page becomes active
BOOL CNGPropertyPage::OnSetActive(void)
{
CNGPropertySheet* pParent = DYNAMIC_DOWNCAST(CNGPropertySheet, GetParent());
if (pParent != NULL)
{
pParent->OnSetActive(this);
}
return CNGPropertyPage_BASE::OnSetActive();
}
// Override to:
// 1. Let the parent know when a page becomes inactive
// 2. Prevent reentrant calls to UpdateData() via OnCommand()
BOOL CNGPropertyPage::OnKillActive(void)
{
ASSERT_VALID(this);
m_bLockCtrlUpdates = true;
CNGPropertySheet* pParent = DYNAMIC_DOWNCAST(CNGPropertySheet, GetParent());
if (pParent != NULL)
{
pParent->OnKillActive(this);
}
BOOL bResult = CNGPropertyPage_BASE::OnKillActive();
m_bLockCtrlUpdates = false;
return bResult;
}
/////////////////////////////////////////////////////////////////////////////
// CNGPropertyPage operations
BOOL CNGPropertyPage::SetServer(CObject* pServer)
{
m_pServer = pServer;
ASSERT_VALID(m_pServer);
return (m_pServer != NULL);
}
BOOL CNGPropertyPage::SetDocument(CDocument* pDocument)
{
m_pDoc = pDocument;
ASSERT_VALID(m_pDoc);
return (m_pDoc != NULL);
}
void CNGPropertyPage::SetModified(BOOL bChanged /*= TRUE*/)
{
m_bModified = bChanged;
CNGPropertyPage_BASE::SetModified(bChanged);
}
BOOL CNGPropertyPage::EnableDlgControl(UINT uID, BOOL bEnable)
{
CWnd* pWnd = GetDlgItem(uID);
if (NULL != pWnd)
{
pWnd->EnableWindow(bEnable);
return TRUE;
}
ASSERT(FALSE);
return FALSE;
}
BOOL CNGPropertyPage::ShowDlgControl(UINT uID, BOOL bShow)
{
CWnd* pWnd = GetDlgItem(uID);
if (NULL != pWnd)
{
pWnd->ShowWindow(bShow);
return TRUE;
}
ASSERT(FALSE);
return FALSE;
}
BOOL CNGPropertyPage::SetReadOnly(UINT uID, BOOL bReadOnly /*= TRUE*/)
{
CEdit* pCtrl = static_cast<CEdit*>( GetDlgItem(uID) );
if (NULL != pCtrl)
{
pCtrl->SetReadOnly(bReadOnly);
return TRUE;
}
ASSERT(FALSE);
return FALSE;
}
BOOL CNGPropertyPage::UpdateData(BOOL bSaveAndValidate /*= TRUE*/)
{
if (IsWindow(m_hWnd))
{
return CNGPropertyPage_BASE::UpdateData(bSaveAndValidate);
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// CNGPropertyPage message handlers
/////////////////////////////////////////////////////////////////////////////
// CNGPropertySheet
IMPLEMENT_DYNAMIC(CNGPropertySheet, CNGPropertySheet_BASE)
CNGPropertySheet::CNGPropertySheet(UINT uIDCaption, CWnd* pParentWnd, UINT iSelectPage)
:CNGPropertySheet_BASE(uIDCaption, pParentWnd, iSelectPage)
{
m_rectInitialPosition.SetRectEmpty();
m_rectPosition.SetRectEmpty();
EnableStackedTabs(FALSE);
}
CNGPropertySheet::CNGPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
:CNGPropertySheet_BASE(pszCaption, pParentWnd, iSelectPage)
{
m_rectInitialPosition.SetRectEmpty();
m_rectPosition.SetRectEmpty();
EnableStackedTabs(FALSE);
}
CNGPropertySheet::~CNGPropertySheet(void)
{
}
BEGIN_MESSAGE_MAP(CNGPropertySheet, CNGPropertySheet_BASE)
//{{AFX_MSG_MAP(CNGPropertySheet)
ON_WM_MOVE()
ON_WM_HELPINFO()
ON_WM_CONTEXTMENU()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CNGPropertySheet overrides
BOOL CNGPropertySheet::OnInitDialog(void)
{
// Add a'?' button to the title bar for context help support
ModifyStyleEx(0, WS_EX_CONTEXTHELP);
BOOL bResult = CNGPropertySheet_BASE::OnInitDialog();
SetInitialPosition();
return bResult;
}
void CNGPropertySheet::OnSetActive(CPropertyPage* pPage)
{
ASSERT_VALID(this);
UNREFERENCED_PARAMETER(pPage);
}
void CNGPropertySheet::OnKillActive(CPropertyPage* pPage)
{
ASSERT_VALID(this);
UNREFERENCED_PARAMETER(pPage);
}
BOOL CNGPropertySheet::OnCommand(WPARAM wParam, LPARAM lParam)
{
BOOL bResult = FALSE;
// Crack message parameters
// This bit was nicked from CPropertySheet::OnCommand()
UINT uID = LOWORD(wParam);
HWND hWnd = (HWND)lParam;
//int nCode = HIWORD(wParam);
//UINT uCtrlID = uID; // We'll need this later
// If the command notification was a control value change
// which could be linked up via DDX update the DDX member
// vars and send a WM_COMMAND notification on the affected
// control (this allows a generic ON_COMMAND handler to be
// used, irrespective of the type of the control)
if (hWnd != NULL)
{
// Catch ENTER keypresses on a control
// and convert to a CN_COMMAND call for that control
// This allows the control focused when ENTER was pressed to receive
// the change notification...
if (uID == IDOK)
{
// Walk up from the focused window until we find a valid control
HWND hWndFocusCtrl = ::GetFocus();
while ( (hWndFocusCtrl != NULL) && (::GetParent(hWndFocusCtrl) != m_hWnd) )
{
hWndFocusCtrl =::GetParent(hWndFocusCtrl);
}
// If the focused control is the one we orignially received the
// notification from it must be the OK button itself, so
// let it through unchanged
if ( (hWndFocusCtrl == NULL) || (hWndFocusCtrl == hWnd) )
{
return CNGPropertySheet_BASE::OnCommand(wParam, lParam);
}
// Fire a CN_COMMAND notification on the focused control
hWnd = hWndFocusCtrl;
wParam = MAKEWPARAM(uID, CN_COMMAND);
lParam = (LPARAM)hWnd;
::SendMessage(hWnd, WM_COMMAND, wParam, lParam);
// Move the focus to the next control
::PostMessage(m_hWnd, WM_NEXTDLGCTL, 0, 0L);
bResult = TRUE; // Prevent sheet from closing
}
else
{
bResult = CNGPropertySheet_BASE::OnCommand(wParam, lParam);
}
}
return bResult;
}
/////////////////////////////////////////////////////////////////////////////
// CNGPropertySheet operations
BOOL CNGPropertySheet::EnableDlgControl(UINT uID, BOOL bEnable)
{
CWnd* pWnd = GetDlgItem(uID);
if (NULL != pWnd)
{
pWnd->EnableWindow(bEnable);
return TRUE;
}
ASSERT(FALSE);
return FALSE;
}
BOOL CNGPropertySheet::ShowDlgControl(UINT uID, BOOL bShow)
{
CWnd* pWnd = GetDlgItem(uID);
if (NULL != pWnd)
{
pWnd->ShowWindow(bShow);
return TRUE;
}
ASSERT(FALSE);
return FALSE;
}
BOOL CNGPropertySheet::UpdateData(BOOL bSaveAndValidate /*= TRUE*/)
{
if (IsWindow(m_hWnd))
{
return CNGPropertySheet_BASE::UpdateData(bSaveAndValidate);
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// CNGPropertySheet message handlers
void CNGPropertySheet::OnMove(int x, int y)
{
CNGPropertySheet_BASE::OnMove(x, y);
GetWindowRect(&m_rectPosition);
}
/////////////////////////////////////////////////////////////////////////////
// CNGPropertySheet Help Support
// Context help generated by pressing F1 or using the "?" button on the title
// bar of the property sheet.
BOOL CNGPropertySheet::OnHelpInfo(HELPINFO* pHelpInfo)
{
if (pHelpInfo->iContextType == HELPINFO_WINDOW)
{
// Launch WinHelp or HtmlHelp
// The HELP_WM_HELP flag brings up pop-up help and expects an array
// of DWORD pairs of the control ID and the context help ID
UINT uCtrlID = pHelpInfo->iCtrlId;
if ( (uCtrlID != 0) && (uCtrlID != IDC_STATIC) )
{
CTRLID_HELPID_PAIR dwHelpIds( uCtrlID,
uCtrlID + HID_BASE_CONTROL);
::WinHelp( (HWND)pHelpInfo->hItemHandle,
AfxGetApp()->m_pszHelpFilePath,
HELP_WM_HELP,
(DWORD)&dwHelpIds);
}
}
return TRUE;
}
// Context help generated by right clicking the control and selecting the
// "Whats this ?" menu.
void CNGPropertySheet::OnContextMenu(CWnd* pWnd, CPoint point)
{
// The title bar has a system provided context menu which is displayed for
// a right mouse button up
if (HTCLIENT == OnNcHitTest(point))
{
//TRACE("OnContextMenu - HTCIENT\n");
CWnd* pChildWnd;
if (pWnd == this) //if the control on the Sheet is disabled
{
// get the hWnd of the disabled child window
ScreenToClient(&point);
pChildWnd = ChildWindowFromPoint(point, CWP_ALL);
}
else
{
// for OK, Cancel and Apply (if enabled) buttons
pChildWnd = pWnd;
}
// Launch WinHelp or HtmlHelp
// The HELP_CONTEXTMENU flag brings up the "Whats this ?" menu and selecting
// the menu in turn brings up the pop-up help. It expects an array
// of DWORD pairs of the control ID and the context help ID
UINT uCtrlID = pChildWnd->GetDlgCtrlID();
if ( (uCtrlID != 0) && (uCtrlID != IDC_STATIC) )
{
CTRLID_HELPID_PAIR dwHelpIds( uCtrlID,
uCtrlID + HID_BASE_CONTROL);
::WinHelp( (HWND)pWnd->m_hWnd,
AfxGetApp()->m_pszHelpFilePath,
HELP_CONTEXTMENU,
(DWORD)&dwHelpIds);
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Implementation
void CNGPropertySheet::SetInitialPosition(void)
{
// If we've got a position from last time use it to position
// our window this time
if (!m_rectInitialPosition.IsRectEmpty())
{
// As the property sheet is not scaleable at the moment we need
// to make sure we change the size of m_rectPosition to match
// the size of the window as it is now (remember that adding or
// deleting pages between invokations may change the size)
m_rectInitialPosition.BottomRight() =
CPoint( m_rectInitialPosition.left + m_rectPosition.Width(),
m_rectInitialPosition.top + m_rectPosition.Height() );
MoveWindow(m_rectInitialPosition);
}
}