home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Windows Gam…ming Gurus (2nd Edition)
/
Disc2.iso
/
vc98
/
mfc
/
src
/
ppgcolor.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-16
|
19KB
|
669 lines
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#ifdef AFXCTL_PAGE_SEG
#pragma code_seg(AFXCTL_PAGE_SEG)
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define new DEBUG_NEW
#define RGB_BUTTON_BLACK (GetSysColor(COLOR_WINDOWFRAME))
#define RGB_BUTTON_WHITE (GetSysColor(COLOR_BTNHIGHLIGHT))
#define RGB_BUTTON_LIGHT (GetSysColor(COLOR_BTNFACE))
#define RGB_BUTTON_DARK (GetSysColor(COLOR_BTNSHADOW))
//Colors : Standard colors for QcQp
#define BLACK 0
#define WHITE 1
#define RED 2
#define GREEN 3
#define BLUE 4
#define YELLOW 5
#define MAGENTA 6
#define CYAN 7
#define GRAY 8
#define LIGHTGRAY 9
#define DARKRED 10
#define DARKGREEN 11
#define DARKBLUE 12
#define LIGHTBROWN 13
#define DARKMAGENTA 14
#define DARKCYAN 15
/////////////////////////////////////////////////////////////////////////////
// Structure used to store information about the system colors
struct SysColorsInfo
{
UINT nStrID; // The id of the resource describing this system color
int nColor; // The system color index
};
// 3D Drawing helpers
void _AfxDraw3DFrame(CDC *pDC, CRect rcBox, COLORREF colBottomRight,
COLORREF colTopLeft);
void _AfxDraw3DButtonFrame(CDC *pDC, CRect rcButton, BOOL fFocus);
// Function to initialize the palette
HPALETTE _AfxInitPalette(VOID);
HPALETTE hPal;
// The system colors information
AFX_STATIC_DATA const SysColorsInfo _afxSysColorsList[] =
{
AFX_IDS_COLOR_DESKTOP, COLOR_BACKGROUND,
AFX_IDS_COLOR_APPWORKSPACE, COLOR_APPWORKSPACE,
AFX_IDS_COLOR_WNDBACKGND, COLOR_WINDOW,
AFX_IDS_COLOR_WNDTEXT, COLOR_WINDOWTEXT,
AFX_IDS_COLOR_MENUBAR, COLOR_MENU,
AFX_IDS_COLOR_MENUTEXT, COLOR_MENUTEXT,
AFX_IDS_COLOR_ACTIVEBAR, COLOR_ACTIVECAPTION,
AFX_IDS_COLOR_INACTIVEBAR, COLOR_INACTIVECAPTION,
AFX_IDS_COLOR_ACTIVETEXT, COLOR_CAPTIONTEXT,
AFX_IDS_COLOR_INACTIVETEXT, COLOR_INACTIVECAPTIONTEXT,
AFX_IDS_COLOR_ACTIVEBORDER, COLOR_ACTIVEBORDER,
AFX_IDS_COLOR_INACTIVEBORDER, COLOR_INACTIVEBORDER,
AFX_IDS_COLOR_WNDFRAME, COLOR_WINDOWFRAME,
AFX_IDS_COLOR_SCROLLBARS, COLOR_SCROLLBAR,
AFX_IDS_COLOR_BTNFACE, COLOR_BTNFACE,
AFX_IDS_COLOR_BTNSHADOW, COLOR_BTNSHADOW,
AFX_IDS_COLOR_BTNTEXT, COLOR_BTNTEXT,
AFX_IDS_COLOR_BTNHIGHLIGHT, COLOR_BTNHIGHLIGHT,
AFX_IDS_COLOR_DISABLEDTEXT, COLOR_GRAYTEXT,
AFX_IDS_COLOR_HIGHLIGHT, COLOR_HIGHLIGHT,
AFX_IDS_COLOR_HIGHLIGHTTEXT, COLOR_HIGHLIGHTTEXT,
// End of list
NULL, NULL
};
/////////////////////////////////////////////////////////////////////////////
// CColorButton class
// All buttons are initially unselected
CColorButton::CColorButton()
{
m_fSelected = FALSE;
}
// Set the face color of the button
void CColorButton::SetFaceColor(COLORREF colFace)
{
m_colFace = colFace;
}
// Return the palette reference for the button
COLORREF CColorButton::colGetFaceColor()
{
return m_colFace;
}
// Set a color button's selection state
void CColorButton::SetState(BOOL fSelected)
{
m_fSelected = fSelected;
}
// Static ID shows which color button generated the BN_CLICKED message
UINT CColorButton::idClicked = 0;
// Redraw the color button, and record a change in selection status
void CColorButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC *pDC;
CPalette *pPal;
CRect rcButton;
CBrush *pBrush;
// Get the palette for the item display context
pPal = CPalette::FromHandle(hPal);
VERIFY(pPal);
// Get the device context from the context
pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
VERIFY(pDC);
VERIFY(pDC->SelectPalette(pPal,0));
pDC->RealizePalette();
switch(lpDrawItemStruct->itemAction)
{
case ODA_SELECT:
if (CButton::GetState() & 0x0004)
{
idClicked = lpDrawItemStruct->CtlID;
// Redraw done via the BN_CLICKED notification
}
break;
case ODA_DRAWENTIRE:
rcButton = lpDrawItemStruct->rcItem;
rcButton.InflateRect(-2, -2);
if (m_fSelected)
{
_AfxDraw3DFrame(pDC, rcButton, RGB_BUTTON_DARK, RGB_BUTTON_WHITE);
rcButton.InflateRect(-1, -1);
_AfxDraw3DFrame(pDC, rcButton, RGB_BUTTON_DARK, RGB_BUTTON_WHITE);
rcButton.InflateRect(-1, -1);
}
// Then the button face in the correct color
pBrush = new CBrush(pDC->GetNearestColor(m_colFace));
pDC->FillRect(&rcButton, pBrush);
delete pBrush;
// Drop through to draw Frame.
case ODA_FOCUS:
rcButton = lpDrawItemStruct->rcItem;
_AfxDraw3DButtonFrame(pDC, rcButton, GetState() & 0x0008);
}
}
/////////////////////////////////////////////////////////////////////////////
// CColorPropPage implementation
BEGIN_MESSAGE_MAP(CColorPropPage, CStockPropPage)
//{{AFX_MSG_MAP(CColorPropPage)
ON_CBN_SELCHANGE(AFX_IDC_COLORPROP, OnSelchangeColorprop)
ON_BN_CLICKED(AFX_IDC_COLOR_BLACK, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_WHITE, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_RED, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_GREEN, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_BLUE, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_YELLOW, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_MAGENTA, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_CYAN, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_GRAY, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_LIGHTGRAY, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_DARKRED, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_DARKGREEN, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_DARKBLUE, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_LIGHTBROWN, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_DARKMAGENTA, OnSelect)
ON_BN_CLICKED(AFX_IDC_COLOR_DARKCYAN, OnSelect)
ON_CBN_SELCHANGE(AFX_IDC_SYSTEMCOLORS, OnSelchangeSystemcolors)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CColorPropPage::CColorPropPage() :
CStockPropPage(IDD, AFX_IDS_COLOR_PPG_CAPTION)
{
hPal = _AfxInitPalette();
//{{AFX_DATA_INIT(CColorPropPage)
//}}AFX_DATA_INIT
}
BOOL CColorPropPage::OnInitDialog()
{
UINT iButton;
for (iButton = 0; iButton < NBUTTONS; iButton++)
{
VERIFY(m_Buttons[iButton].SubclassDlgItem(AFX_IDC_COLOR_BLACK + iButton, this));
m_Buttons[iButton].SetFaceColor(PALETTEINDEX(iButton));
}
m_pSelectedButton = NULL;
CStockPropPage::OnInitDialog();
FillSysColors();
OnObjectsChanged();
IgnoreApply(AFX_IDC_COLORPROP);
return TRUE;
}
void CColorPropPage::FillSysColors()
{
const SysColorsInfo* pInfo = &_afxSysColorsList[0];
while (pInfo->nStrID != NULL)
{
CString strColor;
strColor.LoadString(pInfo->nStrID);
int nIndex = m_SysColors.AddString(strColor);
if (nIndex != CB_ERR)
m_SysColors.SetItemData(nIndex, (DWORD)pInfo->nColor);
pInfo++;
}
// Initially no system color is selected
m_SysColors.SetCurSel(-1);
}
void CColorPropPage::DoDataExchange(CDataExchange* pDX)
{
//{{AFX_DATA_MAP(CColorPropPage)
DDX_Control(pDX, AFX_IDC_SYSTEMCOLORS, m_SysColors);
DDX_Control(pDX, AFX_IDC_COLORPROP, m_ColorProp);
//}}AFX_DATA_MAP
if (pDX->m_bSaveAndValidate)
{
unsigned long color = 0;
BOOL bChanged = TRUE;
int nIndex = m_SysColors.GetCurSel();
// did the user pick a particular button?
// or did they select a system colour from the dropdown?
if (m_pSelectedButton != NULL)
{
PALETTEENTRY pe;
GetPaletteEntries(hPal, m_pSelectedButton->GetDlgCtrlID() - AFX_IDC_COLOR_BLACK, 1, &pe);
color = RGB(pe.peRed, pe.peGreen, pe.peBlue);
}
else if (nIndex != CB_ERR)
{
color = 0x80000000 | m_SysColors.GetItemData(nIndex);
}
else
bChanged = FALSE;
if (bChanged)
SetColorProp(pDX, color, m_strPropName);
}
else
{
unsigned long color;
GetColorProp(pDX, &color, m_strPropName);
if (color & 0x80000000)
{
// System color
// Remove any previously selected buttons
SetButton(NULL);
int index;
int cSysColors = m_SysColors.GetCount();
unsigned long iSysColor = color & 0x000000ff;
for (index = 0; index < cSysColors; index++)
{
if (m_SysColors.GetItemData(index) == iSysColor)
{
m_SysColors.SetCurSel(index);
break;
}
}
}
else
{
// Normal color
for (int index = 0; index < NBUTTONS; index++)
{
PALETTEENTRY pe;
GetPaletteEntries(hPal, index, 1, &pe);
if (color == RGB(pe.peRed, pe.peGreen, pe.peBlue))
SetButton(&m_Buttons[index]);
}
}
}
}
BOOL CColorPropPage::SetColorProp(CDataExchange* /* pDX */,
unsigned long color, LPCTSTR pszPropName)
{
USES_CONVERSION;
COleDispatchDriver PropDispDriver;
ULONG nObjects;
LPDISPATCH* ppDisp = GetObjectArray(&nObjects);
for (ULONG i = 0; i < nObjects; i++)
{
DISPID dwDispID;
// Get the Dispatch ID for the property and if successful set the value
LPCOLESTR lpOleStr = T2COLE(pszPropName);
if (SUCCEEDED(ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr,
1, m_lcid, &dwDispID)))
{
// Set property
PropDispDriver.AttachDispatch(ppDisp[i], FALSE);
TRY
{
PropDispDriver.SetProperty(dwDispID, VT_I4, color);
}
CATCH(COleDispatchException, e)
{
// Display message box for dispatch exceptions.
e->ReportError(MB_ICONEXCLAMATION | MB_OK);
DELETE_EXCEPTION(e);
}
AND_CATCH_ALL(e)
{
// Ignore other exceptions.
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
PropDispDriver.DetachDispatch();
}
}
return TRUE;
}
BOOL CColorPropPage::GetColorProp(CDataExchange* /* pDX */,
unsigned long* pcolor, LPCTSTR pszPropName)
{
USES_CONVERSION;
COleDispatchDriver PropDispDriver;
ULONG nObjects;
LPDISPATCH* ppDisp = GetObjectArray(&nObjects);
for (ULONG i = 0; i < nObjects; i++)
{
DISPID dwDispID;
// Get the Dispatch ID for the property and if successful get the value
LPCOLESTR lpOleStr = T2COLE(pszPropName);
if (SUCCEEDED(ppDisp[i]->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpOleStr,
1, m_lcid, &dwDispID)))
{
// Get property
unsigned long color = 0;
PropDispDriver.AttachDispatch(ppDisp[i], FALSE);
TRY
PropDispDriver.GetProperty(dwDispID, VT_I4, &color);
END_TRY
PropDispDriver.DetachDispatch();
if (i != 0 && color != *pcolor)
*pcolor = 0;
else
*pcolor = color;
}
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CColorPropPage message handlers
void CColorPropPage::OnSelect()
{
SetButton((CColorButton *)(GetDlgItem(CColorButton::idClicked)));
}
void CColorPropPage::SetButton(CColorButton *Button)
{
if (m_pSelectedButton != NULL)
{
m_pSelectedButton->SetState(FALSE);
m_pSelectedButton->Invalidate(FALSE);
}
m_pSelectedButton = Button;
if (m_pSelectedButton != NULL)
{
Button->SetState(TRUE);
Button->Invalidate(FALSE);
}
// Deselect any system color currently selected
m_SysColors.SetCurSel(-1);
}
void CColorPropPage::OnSelchangeColorprop()
{
OnSelchangePropname(m_ColorProp);
}
///////////////////////////////////////////////////////////////////////////////
// 3D Drawing helpers
void _AfxDraw3DFrame(CDC *pDC, CRect rcBox, COLORREF colBottomRight, COLORREF colTopLeft)
{
CPen *pPen2, *pPen, *pOldPen;
pPen = new CPen(PS_SOLID, 1, colBottomRight);
pOldPen = pDC->SelectObject(pPen);
pDC->MoveTo(rcBox.right-1, rcBox.top);
pDC->LineTo(rcBox.right-1, rcBox.bottom-1);
pDC->LineTo(rcBox.left-1, rcBox.bottom-1);
pPen2 = new CPen(PS_SOLID, 1, colTopLeft);
pDC->SelectObject(pPen2);
delete pPen;
pDC->MoveTo(rcBox.left, rcBox.bottom-2);
pDC->LineTo(rcBox.left, rcBox.top);
pDC->LineTo(rcBox.right-1, rcBox.top);
pDC->SelectObject(pOldPen);
delete pPen2;
}
void _AfxDraw3DButtonFrame(CDC *pDC, CRect rcButton, BOOL fFocus)
{
CPen *pPen, *pOldPen;
CBrush GrayBrush(RGB_BUTTON_LIGHT);
CBrush BlackBrush(RGB_BUTTON_BLACK);
pPen = new CPen(PS_SOLID, 1, RGB_BUTTON_BLACK);
pOldPen = pDC->SelectObject(pPen);
// Draw gray outside
pDC->FrameRect(&rcButton, &GrayBrush);
rcButton.InflateRect(-1, -1);
if (fFocus)
{
// Draw inside of border
pDC->FrameRect(&rcButton, &BlackBrush);
// Draw curved border on outside;
rcButton.InflateRect(1, 1);
}
else
{
// Prepare inside border
pDC->FrameRect(&rcButton, &GrayBrush);
}
pDC->MoveTo(rcButton.left+1, rcButton.top);
pDC->LineTo(rcButton.right-1, rcButton.top);
pDC->MoveTo(rcButton.left+1, rcButton.bottom-1);
pDC->LineTo(rcButton.right-1, rcButton.bottom-1);
pDC->MoveTo(rcButton.left, rcButton.top+1);
pDC->LineTo(rcButton.left, rcButton.bottom-1);
pDC->MoveTo(rcButton.right-1, rcButton.top+1);
pDC->LineTo(rcButton.right-1, rcButton.bottom-1);
pDC->SelectObject(pOldPen);
delete pPen;
}
HPALETTE _AfxInitPalette(VOID)
{
NPLOGPALETTE pPal; // Local palette
HPALETTE pal;
//Allocates space for QcQp Palette 'pPal'
pPal = (NPLOGPALETTE) LocalAlloc(LPTR,
(sizeof (LOGPALETTE) +
(sizeof (PALETTEENTRY) * CColorPropPage::NBUTTONS)));
if (pPal == NULL)
return NULL;
//Initialize 'pPal' fields
pPal->palNumEntries = CColorPropPage::NBUTTONS;
pPal->palVersion = 0x300;
//Inits Every color of our palette
pPal->palPalEntry[BLACK].peRed = 0;
pPal->palPalEntry[BLACK].peGreen = 0;
pPal->palPalEntry[BLACK].peBlue = 0;
pPal->palPalEntry[BLACK].peFlags = (BYTE) 0;
pPal->palPalEntry[WHITE].peRed = 255;
pPal->palPalEntry[WHITE].peGreen = 255;
pPal->palPalEntry[WHITE].peBlue = 255;
pPal->palPalEntry[WHITE].peFlags = (BYTE) 0;
pPal->palPalEntry[RED].peRed = 255;
pPal->palPalEntry[RED].peGreen = 0;
pPal->palPalEntry[RED].peBlue = 0;
pPal->palPalEntry[RED].peFlags = (BYTE) 0;
pPal->palPalEntry[GREEN].peRed = 0;
pPal->palPalEntry[GREEN].peGreen = 255;
pPal->palPalEntry[GREEN].peBlue = 0;
pPal->palPalEntry[GREEN].peFlags = (BYTE) 0;
pPal->palPalEntry[BLUE].peRed = 0;
pPal->palPalEntry[BLUE].peGreen = 0;
pPal->palPalEntry[BLUE].peBlue = 255;
pPal->palPalEntry[BLUE].peFlags = (BYTE) 0;
pPal->palPalEntry[YELLOW].peRed = 255;
pPal->palPalEntry[YELLOW].peGreen = 255;
pPal->palPalEntry[YELLOW].peBlue = 0;
pPal->palPalEntry[YELLOW].peFlags = (BYTE) 0;
pPal->palPalEntry[MAGENTA].peRed = 255;
pPal->palPalEntry[MAGENTA].peGreen = 0;
pPal->palPalEntry[MAGENTA].peBlue = 255;
pPal->palPalEntry[MAGENTA].peFlags = (BYTE) 0;
pPal->palPalEntry[CYAN].peRed = 0;
pPal->palPalEntry[CYAN].peGreen = 255;
pPal->palPalEntry[CYAN].peBlue = 255;
pPal->palPalEntry[CYAN].peFlags = (BYTE) 0;
pPal->palPalEntry[GRAY].peRed = 128;
pPal->palPalEntry[GRAY].peGreen = 128;
pPal->palPalEntry[GRAY].peBlue = 128;
pPal->palPalEntry[GRAY].peFlags = (BYTE) 0;
pPal->palPalEntry[LIGHTGRAY].peRed = 192;
pPal->palPalEntry[LIGHTGRAY].peGreen = 192;
pPal->palPalEntry[LIGHTGRAY].peBlue = 192;
pPal->palPalEntry[LIGHTGRAY].peFlags = (BYTE) 0;
pPal->palPalEntry[DARKRED].peRed = 128;
pPal->palPalEntry[DARKRED].peGreen = 0;
pPal->palPalEntry[DARKRED].peBlue = 0;
pPal->palPalEntry[DARKRED].peFlags = (BYTE) 0;
pPal->palPalEntry[DARKGREEN].peRed = 0;
pPal->palPalEntry[DARKGREEN].peGreen = 128;
pPal->palPalEntry[DARKGREEN].peBlue = 0;
pPal->palPalEntry[DARKGREEN].peFlags = (BYTE) 0;
pPal->palPalEntry[DARKBLUE].peRed = 0;
pPal->palPalEntry[DARKBLUE].peGreen = 0;
pPal->palPalEntry[DARKBLUE].peBlue = 128;
pPal->palPalEntry[DARKBLUE].peFlags = (BYTE) 0;
pPal->palPalEntry[LIGHTBROWN].peRed = 128;
pPal->palPalEntry[LIGHTBROWN].peGreen = 128;
pPal->palPalEntry[LIGHTBROWN].peBlue = 0;
pPal->palPalEntry[LIGHTBROWN].peFlags = (BYTE) 0;
pPal->palPalEntry[DARKMAGENTA].peRed = 128;
pPal->palPalEntry[DARKMAGENTA].peGreen= 0;
pPal->palPalEntry[DARKMAGENTA].peBlue = 128;
pPal->palPalEntry[DARKMAGENTA].peFlags= (BYTE) 0;
pPal->palPalEntry[DARKCYAN].peRed = 0;
pPal->palPalEntry[DARKCYAN].peGreen = 128;
pPal->palPalEntry[DARKCYAN].peBlue = 128;
pPal->palPalEntry[DARKCYAN].peFlags = (BYTE) 0;
//Creates the logical palette
pal = ::CreatePalette((LPLOGPALETTE)pPal);
//Free allocated memory
VERIFY(LocalFree((HLOCAL)pPal) == NULL);
return pal;
}
void CColorPropPage::OnSelchangeSystemcolors()
{
if (m_pSelectedButton != NULL)
{
m_pSelectedButton->SetState(FALSE);
m_pSelectedButton->Invalidate(FALSE);
m_pSelectedButton = NULL;
}
}
BOOL CColorPropPage::OnEditProperty(DISPID dispid)
{
return CStockPropPage::OnEditProperty(dispid, m_ColorProp);
}
void CColorPropPage::OnObjectsChanged()
{
ULONG nObjects;
if (GetObjectArray(&nObjects) != NULL && m_hWnd != NULL)
{
FillPropnameList(GUID_COLOR, 0, m_ColorProp);
if ( m_ColorProp.GetCount() == 0 )
{
// No color properties: disable everything.
UINT iButton;
for (iButton = 0; iButton < NBUTTONS; iButton++)
m_Buttons[iButton].EnableWindow(FALSE);
m_SysColors.EnableWindow(FALSE);
}
}
if (m_hWnd != NULL)
OnSelchangeColorprop();
}
/////////////////////////////////////////////////////////////////////////////
// Class factory for Color property page
#ifdef _AFXDLL
#ifdef AFXCTL_FACT_SEG
#pragma code_seg(AFXCTL_FACT_SEG)
#endif
IMPLEMENT_OLECREATE_EX(CColorPropPage, "OCxx.CColorPropPage",
0x0be35201,0x8f91,0x11ce,0x9d,0xe3,0x00,0xaa,0x00,0x4b,0xb8,0x51)
BOOL CColorPropPage::CColorPropPageFactory::UpdateRegistry(BOOL bRegister)
{
if (bRegister)
return AfxOleRegisterPropertyPageClass(AfxGetInstanceHandle(),
m_clsid, AFX_IDS_COLOR_PPG);
else
return AfxOleUnregisterClass(m_clsid, NULL);
}
#endif //_AFXDLL
/////////////////////////////////////////////////////////////////////////////
// Force any extra compiler-generated code into AFX_INIT_SEG
#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif
IMPLEMENT_DYNCREATE(CColorPropPage, CStockPropPage)