Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Resource ID Organiser Add-In for Visual C++ 5.0/6.0/.NET

, 10 Jan 2005 CPOL
An application/add-in to organise and renumber resource symbol IDs
resorg103.zip
ResOrg103.exe
resorg104.zip
ResOrg104.exe
resorgaddinsetup.zip
ResOrgAddInSetup.exe
resorgaddin_executables.zip
ResOrg.exe
BXFileDialog.dll
CJ609Lib.dll
NGLib103.dll
ResOrgAddIn.dll
ResOrgCore.dll
ResOrg.cnt
resorg.fts
ResOrg.hlp
resorgnetaddinsetup.zip
ResOrgNETAddInSetup.msi
resorgnetaddin_executables.zip
ResOrgNETAddInToolWindows.ocx
ResOrgNET.exe
CJ609LibVc7.dll
NGLib103Vc7.dll
ResOrgNETAddIn.dll
ResOrgNETCore.dll
ResOrg.cnt
resorg.fts
ResOrg.hlp
ReadMe.rtf
Licence.rtf
resorgnet_executables.zip
ResOrgNET_executables
CJ609LibVc7.dll
NGLib103Vc7.dll
QHTMLight.dll
ResOrg.cnt
resorg.fts
ResOrg.hlp
ResOrgNETAddIn.dll
ResOrgNETAddInToolWindows.ocx
ResOrgNETCore.dll
resorg_demo.zip
ResOrg103.exe
resorg_executables.zip
ResOrgAddIn.dll
BXFileDialog.dll
CJ609Lib.dll
ResOrg.exe
ResOrg.cnt
resorg.fts
ResOrg.hlp
ResOrgCore.dll
NGLib103.dll
resorg_src.zip
BXFileDialog
BXFileDialog.dsp
Lib
Release
Res
bitmap2.bmp
FileOpen.bmp
filesave.bmp
CJLibrary
CJLibrary
CJLibrary.def
CJLibrary.dsp
CJLibrary.dsw
CJLibrarydep.jpg
CJLibraryStatic.dsp
Debug_Unicode
Release
Release_Unicode
res
Include
res
btn_arro.bmp
btn_explorer.bmp
button_images.bmp
cj_logo.bmp
dragging.cur
handcur.cur
headerba.bmp
hsplitba.cur
icr_hand.cur
nodraggi.cur
PushPin.bmp
Toolbar.bmp
vsplitba.cur
Lib
default.doxygen
NGLibrary
Bin
NGLibrary.def
NGLibrary.dsp
NGLibrary.dsw
Release
Res
Drives.bmp
Release
ResOrg.dsw
ResOrgAddIn
Release
Res
HtmlDoc.ico
ResOrg.ico
ResOrgDoc.ico
TBarLrge.bmp
TBarMedm.bmp
Toolbar.bmp
WorkspaceTabs.bmp
ResOrgAddIn.def
ResOrgAddIn.dsp
ResOrgAddIn.odl
ResOrgAddIn_Res.hm
ResOrgApp
Lint
Release
Res
HtmlDoc.ico
MAINFRAM.BMP
ResOrg.ico
ResOrgDoc.ico
TBarLrge.bmp
TBarMedm.bmp
Toolbar.bmp
workspace.ico
WorkspaceTabs.bmp
ResOrgApp.dsp
ResOrgApp_Res.hm
ResOrgApp_Res.resorg
ResOrgCore
Lint
Release
Res
Anna.bmp
BuildOptions.ico
ComCtl Version Prompt.rtf
File Reload Warning Ex.rtf
File Reload Warning.rtf
Global Next Symbol Value Warning Prompt.rtf
information.ico
Invalid Symbol Name.rtf
Mailing List Prompt.rtf
Next Symbol Value Warning Prompt.rtf
project context menu.bmp
project.ico
RenumWiz Completion.rtf
Report.ico
ResOrg Logo.bmp
ResOrg Updated.rtf
ResOrg.ico
resource file.ico
riverblade_logo.bmp
solution.ico
Symbol Delete Warning.rtf
Symbol Name Warning.rtf
Symbol Rename Warning.rtf
SymbolFile.ico
Symbols Delete Warning.rtf
sym_binary.ico
sym_bitmap.ico
sym_command.ico
sym_control.ico
sym_dialog.ico
sym_icon.ico
sym_menu.ico
sym_prompt.ico
sym_resource.ico
sym_string.ico
Toolbar.bmp
VcAddIn.ico
VersionCheck.ico
warning.ico
WizardHeader256.bmp
WizardWatermark256.bmp
ResOrgCore.def
ResOrgCore.dsp
ResOrgCore_Res.hm
ResOrgCore_Res.resorg
Xml
ProblemSymbolReportHtml.xsl
SymbolsReportHtml.xsl
ResOrgNETAddIn
AddIn.def
Release
Res
AddIn.rgs
HtmlDoc.ico
ResOrg.ico
ResOrgDoc.ico
Toolbar.bmp
WorkspaceTabs.bmp
ResOrgNETAddInSetup
Banner.jpg
Intro screen.jpg
Licence.rtf
ReadMe.rtf
Release
ResOrg.ico
ResOrgNETAddInSetup.vdproj
ResOrgNETAddInToolWindows
Release
Res
Edit.ico
Properties.ico
Renumber.ico
ResOrgNETAddInToolWindows.ico
ResOrgNETAddInToolWindowsCtrl.bmp
ResOrgNETAddInToolWindows.def
SatelliteDll
Release
res
AboutBoxCmd.bmp
OpenResOrgCmd.bmp
OptionsCmd.bmp
ViewMainToolWinCmd.bmp
/************************************************************************
 *
 *                   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);
	}
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Anna-Jayne Metcalfe
Founder Riverblade Limited
United Kingdom United Kingdom
I haven't always written software for a living. When I graduated from Surrey University in 1989, it was with an Electronic Engineering degree, but unfortunately that never really gave me the opportunity to do anything particularly interesting (with the possible exception of designing Darth Vader's Codpiece * for the UK Army in 1990).
    * Also known as the Standard Army Bootswitch. But that's another story...
Since the opportunity arose to lead a software team developing C++ software for Avionic Test Systems in 1996, I've not looked back. More recently I've been involved in the development of subsea acoustic navigation systems, digital TV broadcast systems, port security/tracking systems, and most recently software development tools with my own company, Riverblade Ltd.
 
One of my personal specialities is IDE plug-in development. ResOrg was my first attempt at a plug-in, but my day to day work is with Visual Lint, an interactive code analysis tool environment with works within the Visual Studio and Eclipse IDEs or on build servers.
 
I love lots of things, but particularly music, photography and anything connected with history or engineering. I despise ignorant, intolerant and obstructive people - and it shows...I can be a bolshy cow if you wind me up the wrong way...Laugh | :laugh:
 
I'm currently based 15 minutes walk from the beach in Bournemouth on the south coast of England. Since I moved here I've grown to love the place - even if it is full of grockles in Summer!
Follow on   Twitter

| Advertise | Privacy | Mobile
Web02 | 2.8.141022.2 | Last Updated 10 Jan 2005
Article Copyright 2001 by Anna-Jayne Metcalfe
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid