4440 lines
145 KiB
C++
4440 lines
145 KiB
C++
//////////////////////////////////////////////////////////////////////////////
|
||
///
|
||
/// @file propertyGrid.c
|
||
///
|
||
/// @brief A property grid control in Win32 SDK C.
|
||
///
|
||
/// @author David MacDermot
|
||
///
|
||
/// @par Comments:
|
||
/// This source is distributed in the hope that it will be useful,
|
||
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
///
|
||
/// @date 2-27-16
|
||
///
|
||
/// @todo
|
||
///
|
||
/// @bug
|
||
///
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
//DWM 1.1: Version 1.1 changes labelled thus.
|
||
//DWM 1.2: Version 1.2 changes labelled thus.
|
||
//DWM 1.3: Version 1.3 changes labelled thus.
|
||
//DWM 1.4: Version 1.4 changes labelled thus.
|
||
//DWM 1.5: Version 1.5 changes labelled thus.
|
||
//DWM 1.6: Version 1.6 changes labelled thus.
|
||
//DWM 1.7: Version 1.7 changes labelled thus.
|
||
//DWM 1.8: Version 1.8 changes labelled thus.
|
||
//DWM 1.9: Version 1.9 changes labelled thus.
|
||
|
||
//DWM 1.9: Suppress POCC Warning "Argument x to 'sscanf' does not match the format string;
|
||
// expected 'unsigned char *' but found 'unsigned long'"
|
||
#ifdef __POCC__
|
||
#pragma warn(disable:2234)
|
||
#endif
|
||
|
||
#ifndef _WIN32_WINNT // Necessary for WM_MOUSEWHEEL support
|
||
#define _WIN32_WINNT 0x0500
|
||
#endif
|
||
|
||
// MSVC++ Support
|
||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||
#define _CRT_SECURE_NO_WARNINGS
|
||
#endif
|
||
|
||
#define WIN32_LEAN_AND_MEAN
|
||
|
||
#include <windows.h>
|
||
#include <windowsx.h>
|
||
#include <commctrl.h>
|
||
#include <commdlg.h>
|
||
#include <shlobj.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <wchar.h>
|
||
#include <tchar.h>
|
||
#include <uxtheme.h>
|
||
#include "propertyGrid.h"
|
||
|
||
// Begin MSVC++ Support
|
||
|
||
#ifdef _stprintf
|
||
#undef _stprintf
|
||
#endif
|
||
|
||
#ifdef _UNICODE
|
||
#define _tmemcpy wmemcpy
|
||
#define _tmemmove wmemmove
|
||
#define _tmemset wmemset
|
||
#define _stprintf swprintf
|
||
#else
|
||
#define _tmemcpy memcpy
|
||
#define _tmemmove memmove
|
||
#define _tmemset memset
|
||
#define _stprintf _snprintf
|
||
#endif
|
||
|
||
#define ToolTip_AddTool(hwnd,lpti) (BOOL)SNDMSG((hwnd),TTM_ADDTOOL,0,(LPARAM)(LPTOOLINFO)(lpti))
|
||
#define ToolTip_EnumTools(hwnd,iTool,lpti) (BOOL)SNDMSG((hwnd),TTM_ENUMTOOLS,(WPARAM)(UINT)(iTool),(LPARAM)(LPTOOLINFO)(lpti))
|
||
#define ToolTip_UpdateTipText(hwnd,lpti) (void)SNDMSG((hwnd),TTM_UPDATETIPTEXT,0,(LPARAM)(LPTOOLINFO)(lpti))
|
||
#define ToolTip_NewToolRect(hwnd,lpti) (void)SNDMSG((hwnd),TTM_NEWTOOLRECT,0,(LPARAM)(LPTOOLINFO)(lpti))
|
||
|
||
|
||
// End MSVC++ Support
|
||
|
||
#define ID_LISTBOX 2000 ///<An Id for the Listbox
|
||
#define ID_LISTMAP 2001 ///<An Id for the Listmap
|
||
#define ID_PROPDESC 2002 ///<An Id for the Property description
|
||
#define ID_EDIT 2003 ///<An Id for an editor
|
||
#define ID_BUTTON 2004 ///<An Id for the button
|
||
#define ID_IPEDIT 2005 ///<An Id for an editor
|
||
#define ID_DATE 2006 ///<An Id for an editor
|
||
#define ID_TIME 2007 ///<An Id for an editor
|
||
#define ID_COMBO 2008 ///<An Id for an editor
|
||
#define ID_EDITCOMBO 2009 ///<An Id for an editor
|
||
|
||
#define WIDTH_PART0 16 ///< Constant
|
||
#define WIDTH_PART2 20 ///< Constant
|
||
#define HEIGHT_DESC 80 ///< Constant
|
||
#define MINIMUM_ITEM_HEIGHT 20 ///< Constant
|
||
|
||
//DWM 1.2: Converted the following 4 items to constants
|
||
#define SELECT _T("T") ///< PIT_CHECK select
|
||
#define UNSELECT _T("F") ///< PIT_CHECK unselect
|
||
#define CHECKED SELECT ///< PIT_CHECK checked
|
||
#define UNCHECKED UNSELECT ///< PIT_CHECK unchecked
|
||
|
||
//DWM 1.3: Added
|
||
#define WPRC _T("Wprc")
|
||
|
||
//DWM 1.8: Added
|
||
#define FLATCHECKS 0x01 ///< Draw flat checks flag
|
||
|
||
/// @name Macroes
|
||
/// @{
|
||
|
||
/// @def NELEMS(a)
|
||
///
|
||
/// @brief Computes number of elements of an array.
|
||
///
|
||
/// @param a An array.
|
||
#define NELEMS(a) (sizeof(a) / sizeof((a)[0]))
|
||
|
||
/// @def HEIGHT(rect)
|
||
///
|
||
/// @brief Given a RECT, Computes height.
|
||
///
|
||
/// @param rect A RECT struct.
|
||
#define HEIGHT(rect) ((LONG)(rect.bottom - rect.top))
|
||
|
||
/// @def WIDTH(rect)
|
||
///
|
||
/// @brief Given a RECT, Computes width.
|
||
///
|
||
/// @param rect A RECT struct.
|
||
#define WIDTH(rect) ((LONG)(rect.right - rect.left))
|
||
|
||
/// @def ListBox_ItemFromPoint(hwndCtl, xPos, yPos)
|
||
///
|
||
/// @brief Gets the zero-based index of the item nearest the specified point
|
||
/// in a list box.
|
||
///
|
||
/// @param hwndCtl The handle of a listbox.
|
||
/// @param xPos The x coordinate of a point.
|
||
/// @param yPos The y coordinate of a point.
|
||
///
|
||
/// @returns The return value contains the index of the nearest item
|
||
/// in the low-order word. The high-order word is zero if
|
||
/// the specified point is in the client area of the list box,
|
||
/// or one if it is outside the client area.
|
||
#define ListBox_ItemFromPoint(hwndCtl, xPos, yPos) \
|
||
(DWORD)SendMessage((hwndCtl),LB_ITEMFROMPOINT, \
|
||
(WPARAM)0,MAKELPARAM((UINT)(xPos),(UINT)(yPos)))
|
||
|
||
/// @def Refresh(hwnd)
|
||
///
|
||
/// @brief Refresh a window.
|
||
///
|
||
/// @param hwnd The handle to the window.
|
||
#define Refresh(hwnd) RedrawWindow((hwnd), NULL, NULL, \
|
||
RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW)
|
||
|
||
/// @def AllocatedString_Replace(lpszTarget, lpszReplace)
|
||
///
|
||
/// @brief Replace an allocated string.
|
||
///
|
||
/// @param lpszTarget The existing allocated string.
|
||
/// @param lpszReplace The new string.
|
||
#define AllocatedString_Replace(lpszTarget, lpszReplace) \
|
||
(free((lpszTarget)), (lpszTarget) = NewString(lpszReplace))
|
||
|
||
/// @}
|
||
|
||
LPCTSTR g_szClassName = _T("PropGridCtl"); ///< The classname.
|
||
|
||
/// @brief A nice assortment of custom colors.
|
||
static COLORREF g_CustomColors[] = {
|
||
0xDCDCDC, 0xCDCDDB, 0xBDBED2, 0xE3C4C3,
|
||
0xFFE0C0, 0xE0C8AB, 0xD7AFB0, 0xF0BFEB,
|
||
0xE2A7DA, 0x9697FF, 0xBFBFFF, 0xBBDBFF,
|
||
0xCBFFFF, 0xBFEFCB, 0xA6DEB3, 0x82D28B
|
||
};
|
||
|
||
/****************************************************************************/
|
||
//Functions
|
||
static LRESULT CALLBACK Grid_Proc(HWND, UINT, WPARAM, LPARAM);
|
||
static LRESULT CALLBACK ListBox_Proc(HWND, UINT, WPARAM, LPARAM);
|
||
static VOID Grid_NotifyParent(VOID);
|
||
|
||
/// @brief Default window procedure for the grid and child windows.
|
||
///
|
||
/// @param hwnd Handle of grid or child.
|
||
/// @param msg Which message?
|
||
/// @param wParam Message parameter.
|
||
/// @param lParam Message parameter.
|
||
///
|
||
/// @returns LRESULT depends on message.
|
||
static LRESULT DefProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
//DWM 1.3: Added
|
||
return CallWindowProc((WNDPROC)GetProp(hwnd, WPRC), hwnd, msg, wParam, lParam);
|
||
}
|
||
|
||
/// @brief Declare an inline rect.
|
||
///
|
||
/// @param left The left coordinate.
|
||
/// @param top The top coordinate.
|
||
/// @param right The right coordinate.
|
||
/// @param bottom The bottom coordinate.
|
||
static inline PRECT MAKE_PRECT(LONG left, LONG top, LONG right, LONG bottom)
|
||
{
|
||
static RECT rc;
|
||
memset(&rc, 0, sizeof(rc));
|
||
rc.left = left;
|
||
rc.top = top;
|
||
rc.right = right;
|
||
rc.bottom = bottom;
|
||
return &rc;
|
||
}
|
||
|
||
/// @brief Determine if this is running on XP or lower.
|
||
///
|
||
/// @returns TRUE if OS is XP or earlier.
|
||
static BOOL IsWindowsXPorLower(VOID) {
|
||
//DWM 1.4: Added
|
||
OSVERSIONINFO osvi;
|
||
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||
GetVersionEx(&osvi);
|
||
return osvi.dwMajorVersion < 6;
|
||
}
|
||
|
||
#pragma region Instance Data
|
||
|
||
/// @var LISTBOXITEM
|
||
/// @brief An item object
|
||
|
||
/// @var LPLISTBOXITEM
|
||
/// @brief Pointer to an item
|
||
|
||
/// @struct tagLISTBOXITEM
|
||
/// @brief This is the data associated with a grid item
|
||
typedef struct tagLISTBOXITEM {
|
||
LPTSTR lpszCatalog; ///< Catalog (group) name
|
||
LPTSTR lpszPropName;///< Property (item) name
|
||
LPTSTR lpszMisc; ///< Item specific data string
|
||
LPTSTR lpszPropDesc;///< Property (item) description
|
||
LPTSTR lpszCurValue;///< Property (item) value
|
||
INT iItemType; ///< Property (item) type identifier
|
||
BOOL fCollapsed; ///< Catalog (group) collapsed flag
|
||
} LISTBOXITEM , *LPLISTBOXITEM;
|
||
|
||
/// @var INSTANCEDATA
|
||
/// @brief Data for this instance of the control
|
||
|
||
/// @var LPINSTANCEDATA
|
||
/// @brief Pointer instance data
|
||
|
||
/// @struct tagINSTANCEDATA
|
||
/// @brief This is the data associated with an instance of the grid
|
||
typedef struct tagINSTANCEDATA {
|
||
HINSTANCE hInstance; ///< Handle to this instance
|
||
LPLISTBOXITEM lpCurrent;///< Currently selected grid item
|
||
HWND hwndParent; ///< Handle of grid's parent
|
||
HWND hwndToolTip; ///< ToolTip handle
|
||
HWND hwndPropDesc; ///< Description pane handle
|
||
HWND hwndListBox; ///< Handle of the visible list box
|
||
HWND hwndListMap; ///< Handle of the hidden list box
|
||
HWND hwndCtl1; ///< Handle of the primary edit control
|
||
HWND hwndCtl2; ///< Handle of the secondary edit control
|
||
INT iHDivider; ///< Position of horizontal divider
|
||
INT iVDivider; ///< Position of verticle divider
|
||
INT iDescHeight; ///< Height of description pane
|
||
INT iPrevSel; ///< Index of previously selected item
|
||
BOOL fXpOrLower; ///< TRUE is running on Xp or earlier version of OS
|
||
BOOL fGotFocus; ///< TRUE while focus resides within property grid
|
||
BOOL fScrolling; ///< TRUE while scrolling
|
||
BOOL fTracking; ///< TRUE while moving dividers
|
||
LONG nOldDivX; ///< Previous divider x position
|
||
LONG nOldDivY; ///< Previous divider y position
|
||
LONG nDivTop; ///< Horizontal Divider positon limit
|
||
LONG nDivBtm; ///< Horizontal Divider positon limit
|
||
LONG nDivLft; ///< Vertical Divider positon limit
|
||
LONG nDivRht; ///< Vertical Divider positon limit
|
||
} INSTANCEDATA , *LPINSTANCEDATA;
|
||
|
||
static LPINSTANCEDATA g_lpInst; ///< instance data (this) pointer
|
||
|
||
/// @brief Get the Instance data associated with this instance.
|
||
///
|
||
/// @param hControl Handle to Current instance.
|
||
/// @param ppInstanceData - Pointer to the address of an INSTANCEDATA struct.
|
||
///
|
||
/// @returns TRUE if successful
|
||
static BOOL Control_GetInstanceData(HWND hControl, LPINSTANCEDATA * ppInstanceData)
|
||
{
|
||
*ppInstanceData = (LPINSTANCEDATA)GetProp(hControl, (LPCTSTR)_T("lpInsData"));
|
||
if (NULL != *ppInstanceData)
|
||
return TRUE;
|
||
return FALSE;
|
||
}
|
||
|
||
/// @brief Allocate the Instance data associated with this instance.
|
||
///
|
||
/// @param hControl Handle to current instance.
|
||
/// @param pInstanceData Pointer to an INSTANCEDATA struct
|
||
///
|
||
/// @returns TRUE if successful
|
||
static BOOL Control_CreateInstanceData(HWND hControl, LPINSTANCEDATA pInstanceData)
|
||
{
|
||
LPINSTANCEDATA pInst = (LPINSTANCEDATA)malloc(sizeof(INSTANCEDATA));
|
||
memmove(pInst, pInstanceData, sizeof(INSTANCEDATA));
|
||
return SetProp(hControl, (LPCTSTR)_T("lpInsData"), pInst);
|
||
}
|
||
|
||
/// @brief Free the instance data allocation of an instance of the Grid Control.
|
||
///
|
||
/// @param hControl Handle to current instance.
|
||
///
|
||
/// @returns TRUE if successful
|
||
static BOOL Control_FreeInstanceData(HWND hControl)
|
||
{
|
||
LPINSTANCEDATA pInst;
|
||
if (Control_GetInstanceData(hControl, &pInst))
|
||
{
|
||
free((LPINSTANCEDATA)pInst);
|
||
RemoveProp(hControl, (LPCTSTR)_T("lpInsData"));
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/// @brief Get Item data associated with a property grid item.
|
||
///
|
||
/// @param hwnd Handle of a listbox.
|
||
/// @param idx The item index.
|
||
///
|
||
/// @returns a listbox item pointer if successful, otherwise NULL
|
||
static LPLISTBOXITEM ListBox_GetItemDataSafe(HWND hwnd, INT idx)
|
||
{
|
||
LRESULT lres = ListBox_GetItemData(hwnd, idx);
|
||
// Don't return LB_ERR cast to an item!
|
||
if (LB_ERR == lres)
|
||
return NULL;
|
||
return (LPLISTBOXITEM)lres;
|
||
}
|
||
|
||
#pragma endregion Instance Data
|
||
|
||
#pragma region memory management
|
||
|
||
/// @brief Allocate and store a string.
|
||
///
|
||
/// @param str The string to store.
|
||
///
|
||
/// @returns a Pointer to the allocated string.
|
||
static LPTSTR NewString(LPTSTR str)
|
||
{
|
||
if(NULL == str || _T('\0') == *str) str = _T("");
|
||
LPTSTR tmp = (LPTSTR)calloc(_tcslen(str) + 1, sizeof(TCHAR));
|
||
|
||
if(NULL == tmp)
|
||
{
|
||
return (LPTSTR)calloc(1, sizeof(TCHAR));
|
||
}
|
||
return (LPTSTR)_tmemmove(tmp, str, _tcslen(str));
|
||
}
|
||
|
||
/// @brief Allocate and store a string array (double-null-terminated string).
|
||
///
|
||
/// @param szzStr The double-null-terminated string to store.
|
||
///
|
||
/// @returns a Pointer to the allocated string array.
|
||
static LPTSTR NewStringArray(LPTSTR szzStr)
|
||
{
|
||
if(NULL == szzStr || _T('\0') == *szzStr) szzStr = _T("");
|
||
|
||
//Determine total buffer length
|
||
INT iLen = 0;
|
||
//Walk the buffer to find the terminating empty string.
|
||
for (LPTSTR p = szzStr; *p; (p += _tcslen(p) + 1, iLen = p - szzStr)) ;
|
||
|
||
//Allocate for array
|
||
LPTSTR tmp = (LPTSTR)calloc(iLen + 1, sizeof(TCHAR));
|
||
|
||
if(NULL == tmp)
|
||
{
|
||
return (LPTSTR)calloc(1, sizeof(TCHAR));
|
||
}
|
||
return (LPTSTR)_tmemmove(tmp, szzStr, iLen);
|
||
}
|
||
|
||
/// @brief Allocate and populate a list box item data structure.
|
||
///
|
||
/// @param szCatalog The catalog this item belongs to.
|
||
/// @param szPropName The item's name.
|
||
/// @param szCurValue The item's current value.
|
||
/// @param szMisc This data varies with item type.
|
||
/// @param szPropDesc The item's description string.
|
||
/// @param iItemType The item type designation.
|
||
///
|
||
/// @returns a Pointer to the allocated list box item.
|
||
static LPLISTBOXITEM NewItem(LPTSTR szCatalog, LPTSTR szPropName, LPTSTR szCurValue, LPTSTR szMisc, LPTSTR szPropDesc, INT iItemType)
|
||
{
|
||
LPLISTBOXITEM lpItem = (LPLISTBOXITEM)calloc(1, sizeof(LISTBOXITEM));
|
||
|
||
lpItem->iItemType = iItemType;
|
||
lpItem->lpszCatalog = NewString(szCatalog);
|
||
lpItem->lpszPropName = NewString(szPropName);
|
||
lpItem->lpszCurValue = NewString(szCurValue);
|
||
|
||
if (PIT_COMBO == iItemType || PIT_EDITCOMBO == iItemType)
|
||
lpItem->lpszMisc = NewStringArray(szMisc);
|
||
else
|
||
lpItem->lpszMisc = NewString(szMisc);
|
||
|
||
lpItem->lpszPropDesc = NewString(szPropDesc);
|
||
|
||
return lpItem;
|
||
}
|
||
|
||
/// @brief Free a list box item's data structure.
|
||
///
|
||
/// @param lpItem A pointer to a LISTBOXITEM object.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID DeleteItem(LPLISTBOXITEM lpItem)
|
||
{
|
||
free(lpItem->lpszCatalog);
|
||
free(lpItem->lpszPropName);
|
||
if(PIT_CHECK != lpItem->iItemType) //DWM 1.2: Don't attempt to free a constant
|
||
{
|
||
free(lpItem->lpszCurValue);
|
||
free(lpItem->lpszMisc);
|
||
}
|
||
free(lpItem->lpszPropDesc);
|
||
free(lpItem);
|
||
}
|
||
|
||
/// @brief Handle the WM_DELETEIETM message.
|
||
///
|
||
/// @param hwnd The handle of a list box (in this case the list map).
|
||
/// @param lpDeleteItem A pointer to the delete item struct.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnDeleteItem(HWND hwnd, const DELETEITEMSTRUCT * lpDeleteItem)
|
||
{
|
||
if (g_lpInst->hwndListMap == lpDeleteItem->hwndItem)
|
||
DeleteItem((LPLISTBOXITEM)lpDeleteItem->itemData);
|
||
|
||
// Catalogs only reside in visible list box
|
||
if (g_lpInst->hwndListBox == lpDeleteItem->hwndItem)
|
||
{
|
||
if(PIT_CATALOG == ((LPLISTBOXITEM)lpDeleteItem->itemData)->iItemType)
|
||
DeleteItem((LPLISTBOXITEM)lpDeleteItem->itemData);
|
||
}
|
||
}
|
||
|
||
#pragma endregion memory management
|
||
|
||
#pragma region Drawing
|
||
|
||
/// @brief Pass keyboard focus to parent and refresh grid.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID SetFocusToParent(VOID)
|
||
{
|
||
SetFocus(g_lpInst->hwndParent);
|
||
}
|
||
|
||
/// @brief Handle WM_KILLFOCUS in editors.
|
||
///
|
||
/// @param hwnd Handle of the editor.
|
||
/// @param hwndNewFocus Handle of the window that recieved focus.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Editor_OnKillFocus(HWND hwnd, HWND hwndNewFocus)
|
||
{
|
||
//DWM 1.3: Added so that grid selection is drawn inactive
|
||
// when grid doesn't have focus.
|
||
g_lpInst->fGotFocus =
|
||
(NULL != hwndNewFocus &&
|
||
(g_lpInst->hwndListBox == hwndNewFocus ||
|
||
g_lpInst->hwndCtl1 == hwndNewFocus ||
|
||
g_lpInst->hwndCtl2 == hwndNewFocus ||
|
||
g_lpInst->hwndPropDesc == hwndNewFocus ||
|
||
g_lpInst->hwndToolTip == hwndNewFocus));
|
||
|
||
if(!g_lpInst->fGotFocus)
|
||
Refresh(g_lpInst->hwndListBox);
|
||
}
|
||
|
||
/// @brief Handle WM_KILLFOCUS in listbox.
|
||
///
|
||
/// @param hwnd Handle of the editor.
|
||
/// @param hwndNewFocus Handle of the window that recieved focus.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnKillFocus(HWND hwnd, HWND hwndNewFocus)
|
||
{
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
if(PIT_CHECK == g_lpInst->lpCurrent->iItemType)//DWM 1.3: Added
|
||
g_lpInst->lpCurrent->lpszMisc = UNSELECT;
|
||
}
|
||
Editor_OnKillFocus(hwnd, hwndNewFocus);
|
||
}
|
||
|
||
/// @brief Set a control's font to bold or back to normal.
|
||
///
|
||
/// @param hwndCtl The handle of a control.
|
||
/// @param fBold TRUE if bold desired.
|
||
///
|
||
/// @returns HFONT A new font with the desired font weight.
|
||
static HFONT Font_SetBold(HWND hwndCtl, BOOL fBold)
|
||
{
|
||
HFONT hFont;
|
||
LOGFONT lf;
|
||
|
||
// Get a handle to the control's font object
|
||
hFont = (HFONT)SendMessage(hwndCtl, WM_GETFONT, 0, 0);
|
||
|
||
// Pull the handle into a Logical Font UDT type
|
||
GetObject(hFont, sizeof(LOGFONT), &lf);
|
||
|
||
// Determine if that font should be bold or not
|
||
if (fBold)
|
||
lf.lfWeight = FW_BOLD;
|
||
else
|
||
lf.lfWeight = FW_NORMAL;
|
||
|
||
// Create a new font based off the logical font UDT
|
||
return CreateFontIndirect(&lf);
|
||
}
|
||
|
||
/// @brief Draw a filled rectangle of a desired color.
|
||
///
|
||
/// @param hdc A handle to a device context.
|
||
/// @param lprc The address of a RECT structure with drawing coordinates.
|
||
/// @param clr The desired fill color value.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID FillSolidRect(HDC hdc, LPRECT lprc, COLORREF clr)
|
||
{
|
||
HBRUSH hbrush = CreateSolidBrush(clr);
|
||
FillRect(hdc, lprc, hbrush);
|
||
DeleteObject(hbrush);
|
||
}
|
||
|
||
/// @brief Draw a line.
|
||
///
|
||
/// @param hdc A handle to a device context.
|
||
/// @param x1 From point x-coordinate.
|
||
/// @param y1 From point y-coordinate.
|
||
/// @param x2 To point x-coordinate.
|
||
/// @param y2 To point y-coordinate.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID DrawLine(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2)
|
||
{
|
||
MoveToEx(hdc, x1, y1, NULL);
|
||
LineTo(hdc, x2, y2);
|
||
}
|
||
|
||
/// @brief Draw an inverted line.
|
||
///
|
||
/// @param hdc A handle to a device context.
|
||
/// @param x1 From point x-coordinate.
|
||
/// @param y1 From point y-coordinate.
|
||
/// @param x2 To point x-coordinate.
|
||
/// @param y2 To point y-coordinate.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID InvertLine(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2)
|
||
{
|
||
INT nOldMode = SetROP2(hdc, R2_NOT);
|
||
DrawLine(hdc, x1, y1, x2, y2);
|
||
SetROP2(hdc, nOldMode);
|
||
}
|
||
|
||
/// @brief Draw specified borders of a rectangle.
|
||
///
|
||
/// @param hdc A handle to a device context.
|
||
/// @param lprc The address of a RECT structure with drawing coordinates.
|
||
/// @param dwBorder specifies which borders to draw.
|
||
/// @param clr The desired line color value.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID DrawBorder(HDC hdc, LPRECT lprc, DWORD dwBorder, COLORREF clr)
|
||
{
|
||
LOGPEN oLogPen;
|
||
|
||
HPEN hOld;
|
||
GetObject(hOld = (HPEN) SelectObject(hdc, GetStockObject(BLACK_PEN)), sizeof(oLogPen), &oLogPen);
|
||
oLogPen.lopnColor = clr;
|
||
|
||
SelectObject(hdc, CreatePenIndirect(&oLogPen));
|
||
|
||
if (dwBorder & BF_LEFT)
|
||
DrawLine(hdc, lprc->left, lprc->top, lprc->left, lprc->bottom);
|
||
if (dwBorder & BF_TOP)
|
||
DrawLine(hdc, lprc->left, lprc->top, lprc->right, lprc->top);
|
||
if (dwBorder & BF_RIGHT)
|
||
DrawLine(hdc, lprc->right, lprc->top, lprc->right, lprc->bottom);
|
||
if (dwBorder & BF_BOTTOM)
|
||
DrawLine(hdc, lprc->left, lprc->bottom, lprc->right, lprc->bottom);
|
||
|
||
DeleteObject(SelectObject(hdc, hOld));
|
||
}
|
||
|
||
/// @brief Convert string value, Ex: "255 255 255" to RGB COLORREF.
|
||
///
|
||
/// @param src The string to convert.
|
||
///
|
||
/// @returns COLORREF The converted color.
|
||
static COLORREF GetColor(LPTSTR src)
|
||
{
|
||
INT RVal, GVal, BVal;
|
||
RVal = GVal = BVal = 0;
|
||
if (_tcslen(src) > 0)
|
||
_stscanf(src, _T("%d,%d,%d"), &RVal, &GVal, &BVal);
|
||
|
||
return RGB(RVal, GVal, BVal);
|
||
}
|
||
|
||
/// @brief Handle the WM_PAINT message for most of the edit controls.
|
||
/// Obliterate the control's border to get that integrated flat look.
|
||
///
|
||
/// @param hwnd The control's handle.
|
||
/// @param msg The window message (in this case WM_PAINT).
|
||
/// @param wParam The message WPARAM.
|
||
/// @param lParam The message LPARAM.
|
||
///
|
||
/// @returns BOOL Always TRUE.
|
||
static BOOL Editor_OnPaint(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
HDC hdc = GetWindowDC(hwnd);
|
||
RECT rect;
|
||
|
||
// First let the system do its thing
|
||
DefProc(hwnd, msg, wParam, lParam);
|
||
|
||
// Next obliterate the border
|
||
GetWindowRect(hwnd, &rect);
|
||
MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT) & rect.left, 2);
|
||
|
||
rect.top += 2;
|
||
rect.left += 2;
|
||
DrawBorder(hdc, &rect, BF_RECT, GetSysColor(COLOR_WINDOW));
|
||
|
||
rect.top += 1;
|
||
rect.left += 1;
|
||
rect.bottom += 1;
|
||
rect.right += 1;
|
||
|
||
DrawBorder(hdc, &rect, BF_RECT, GetSysColor(COLOR_WINDOW));
|
||
|
||
ReleaseDC(hwnd, hdc);
|
||
return TRUE;
|
||
}
|
||
|
||
/// @brief Set the colors used to paint controls in WM_CTLCOLORLISTBOX handler.
|
||
///
|
||
/// @param hdc Handle of a device context.
|
||
/// @param TxtColr Desired text color.
|
||
/// @param BkColr Desired back color.
|
||
///
|
||
/// @returns HBRUSH A reusable brush object.
|
||
static HBRUSH SetColor(HDC hdc, COLORREF TxtColr, COLORREF BkColr)
|
||
{
|
||
static HBRUSH ReUsableBrush;
|
||
DeleteObject(ReUsableBrush);
|
||
ReUsableBrush = CreateSolidBrush(BkColr);
|
||
SetTextColor(hdc, TxtColr);
|
||
SetBkColor(hdc, BkColr);
|
||
return ReUsableBrush;
|
||
}
|
||
|
||
#pragma endregion Drawing
|
||
|
||
#pragma region ToolTip
|
||
|
||
/// @brief Create the tool tip object that will display tips for grid items.
|
||
///
|
||
/// @param hInstance The handle of an instance.
|
||
/// @param hwndParent The handle of the tooltip's parent.
|
||
///
|
||
/// @returns HWND A handle to a tooltip object.
|
||
static HWND CreateToolTip(HINSTANCE hInstance, HWND hwndParent)
|
||
{
|
||
RECT rect;
|
||
TOOLINFO ti;
|
||
DWORD dwStyle, dwExStyle;
|
||
HWND hwnd;
|
||
|
||
dwStyle = WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP;
|
||
dwExStyle = WS_EX_TOPMOST;
|
||
|
||
hwnd = CreateWindowEx(dwExStyle, // ex style
|
||
TOOLTIPS_CLASS, // class name - defined in commctrl.h
|
||
NULL, // dummy text
|
||
dwStyle, // style
|
||
CW_USEDEFAULT, // x position
|
||
CW_USEDEFAULT, // y position
|
||
CW_USEDEFAULT, // width
|
||
CW_USEDEFAULT, // height
|
||
hwndParent, // parent
|
||
NULL, // ID
|
||
hInstance, // instance
|
||
NULL); // no extra data
|
||
|
||
if (!hwnd)
|
||
return NULL;
|
||
|
||
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||
|
||
SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0);
|
||
|
||
GetClientRect(hwndParent, &rect);
|
||
|
||
// Initialize members of toolinfo structure
|
||
ti.cbSize = sizeof(TOOLINFO);
|
||
ti.uFlags = TTF_SUBCLASS;
|
||
ti.hwnd = hwndParent;
|
||
ti.hinst = hInstance;
|
||
ti.uId = 0;
|
||
ti.lpszText = _T("");
|
||
// ToolTip control will cover the entire window
|
||
ti.rect.left = rect.left;
|
||
ti.rect.top = rect.top;
|
||
ti.rect.right = rect.right;
|
||
ti.rect.bottom = rect.bottom;
|
||
ToolTip_AddTool(hwnd, &ti);
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
#pragma endregion ToolTip
|
||
|
||
#pragma region Static
|
||
|
||
/// @brief Create the static control for the property description pane.
|
||
///
|
||
/// @param hInstance The handle of an instance.
|
||
/// @param hwndParent The handle of the parent (the Property Grid window).
|
||
/// @param id An id tag for this control
|
||
///
|
||
/// @returns HWND A handle to a static control.
|
||
static HWND CreateStatic(HINSTANCE hInstance, HWND hwndParent, INT id)
|
||
{
|
||
DWORD dwStyle, dwExStyle;
|
||
HWND hwnd;
|
||
|
||
dwStyle = WS_CHILD | SS_LEFT;
|
||
|
||
dwExStyle = WS_EX_LEFT | WS_EX_CLIENTEDGE;
|
||
|
||
hwnd = CreateWindowEx(dwExStyle,
|
||
WC_STATIC,
|
||
NULL,
|
||
dwStyle,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
hwndParent,
|
||
(HMENU)id,
|
||
hInstance,
|
||
NULL);
|
||
if (!hwnd)
|
||
return NULL;
|
||
|
||
SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0);
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
#pragma endregion Static
|
||
|
||
#pragma region ListBox and ListMap
|
||
|
||
/// @brief Create the owner draw listbox for item display.
|
||
///
|
||
/// @param hInstance The handle of an instance.
|
||
/// @param hwndParent The handle of the parent (the Property Grid window).
|
||
/// @param id An id tag for this control
|
||
///
|
||
/// @par Comments:
|
||
/// There is some kind of bug where a listbox created as an
|
||
/// LBS_OWNERDRAWVARIABLE will scroll erratically when the mouse
|
||
/// wheel is used.
|
||
///
|
||
/// @returns HWND A handle to the owner draw listbox control.
|
||
static HWND CreateListBox(HINSTANCE hInstance, HWND hwndParent, INT id)
|
||
{
|
||
DWORD dwStyle, dwExStyle;
|
||
HWND hwnd;
|
||
|
||
dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL |
|
||
LBS_NOTIFY | LBS_OWNERDRAWFIXED |
|
||
LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT;
|
||
|
||
dwExStyle = WS_EX_LEFT | WS_EX_RTLREADING | WS_EX_RIGHTSCROLLBAR |
|
||
WS_EX_CLIENTEDGE;
|
||
hwnd = CreateWindowEx(dwExStyle,
|
||
WC_LISTBOX,
|
||
NULL,
|
||
dwStyle,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
hwndParent,
|
||
(HMENU)id,
|
||
hInstance,
|
||
NULL);
|
||
|
||
if (!hwnd)
|
||
return NULL;
|
||
|
||
SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0);
|
||
|
||
// Subclass listbox and save the old proc
|
||
SetProp(hwnd, WPRC, (HANDLE)GetWindowLongPtr(hwnd, GWLP_WNDPROC));
|
||
SubclassWindow(hwnd, ListBox_Proc);
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
/// @brief Create a minimal hidden listbox (the listmap) to store pointers
|
||
/// to all items, including those not displayed.
|
||
///
|
||
/// @param hInstance The handle of an instance.
|
||
/// @param hwndParent The handle of the parent (the Property Grid window).
|
||
/// @param id An id tag for this control
|
||
///
|
||
/// @returns HWND A handle to the hidden listmap control.
|
||
static HWND CreateListMap(HINSTANCE hInstance, HWND hwndParent, INT id)
|
||
{
|
||
DWORD dwStyle, dwExStyle;
|
||
HWND hwnd;
|
||
|
||
dwStyle = WS_CHILD | LBS_OWNERDRAWFIXED; // Want WM_DELETEITEM messages
|
||
|
||
dwExStyle = 0;
|
||
hwnd = CreateWindowEx(dwExStyle,
|
||
WC_LISTBOX,
|
||
NULL,
|
||
dwStyle,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
hwndParent,
|
||
(HMENU)id,
|
||
hInstance,
|
||
NULL);
|
||
|
||
if (!hwnd)
|
||
return NULL;
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
#pragma endregion ListBox and ListMap
|
||
|
||
#pragma region Edit Control
|
||
|
||
/// @brief Window procedure for the edit control.
|
||
///
|
||
/// @param hEdit Handle of editor.
|
||
/// @param msg Which message?
|
||
/// @param wParam Message parameter.
|
||
/// @param lParam Message parameter.
|
||
///
|
||
/// @returns LRESULT depends on message.
|
||
static LRESULT CALLBACK Edit_Proc(HWND hEdit, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
HWND hGParent = GetParent(GetParent(hEdit));
|
||
|
||
// Note: Instance data is attached to Edit's grandparent
|
||
Control_GetInstanceData(hGParent, &g_lpInst);
|
||
|
||
if (WM_DESTROY == msg) // Unsubclass the Edit Control
|
||
{
|
||
SetWindowLongPtr(hEdit, GWLP_WNDPROC, (DWORD_PTR)GetProp(hEdit, WPRC)); //DWM 1.5: fixed cast
|
||
RemoveProp(hEdit, WPRC);
|
||
return 0;
|
||
}
|
||
else if (WM_KILLFOCUS == msg)
|
||
{
|
||
FORWARD_WM_CHAR(hEdit, VK_RETURN, 0, SNDMSG);//DWM 1.6: force update of grid data
|
||
Editor_OnKillFocus(hEdit, (HWND)wParam);
|
||
}
|
||
else if (WM_PAINT == msg) // Obliterate border
|
||
{
|
||
return Editor_OnPaint(hEdit, msg, wParam, lParam);
|
||
}
|
||
else if (WM_MOUSEWHEEL == msg)
|
||
{
|
||
FORWARD_WM_CHAR(hEdit, VK_RETURN, 0, SNDMSG);
|
||
}
|
||
else if (WM_CHAR == msg && VK_RETURN == wParam)
|
||
{
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
TCHAR buf[MAX_PATH];
|
||
Edit_GetText(hEdit, buf, sizeof buf);
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, buf);
|
||
}
|
||
ShowWindow(hEdit, SW_HIDE);
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
Grid_NotifyParent();
|
||
return TRUE; // handle Enter (NO BELL)
|
||
}
|
||
else if (WM_KEYDOWN == msg)
|
||
{
|
||
switch (wParam)
|
||
{
|
||
case VK_TAB:
|
||
if (GetKeyState(VK_SHIFT) & 0x8000)
|
||
{
|
||
FORWARD_WM_CHAR(hEdit, VK_RETURN, 0, SNDMSG);
|
||
}
|
||
else //DWM 1.3: Added Focus to grid parent
|
||
{
|
||
SetFocusToParent();
|
||
}
|
||
return TRUE;
|
||
case VK_ESCAPE:
|
||
ShowWindow(hEdit, SW_HIDE);
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
return FALSE;
|
||
}
|
||
}
|
||
return DefProc(hEdit, msg, wParam, lParam);
|
||
}
|
||
|
||
/// @brief Create an Edit control to edit PIT_EDIT fields.
|
||
///
|
||
/// @param hInstance The handle of an instance.
|
||
/// @param hwndParent The handle of the parent (the visible listbox).
|
||
/// @param id An id tag for this control.
|
||
///
|
||
/// @returns HWND A handle to the edit control.
|
||
static HWND CreateEdit(HINSTANCE hInstance, HWND hwndParent, INT id)
|
||
{
|
||
DWORD dwStyle, dwExStyle;
|
||
HWND hwnd;
|
||
|
||
dwStyle = WS_CHILD | ES_LEFT | ES_AUTOHSCROLL | ES_MULTILINE | ES_WANTRETURN;
|
||
|
||
dwExStyle = WS_EX_LEFT | WS_EX_CLIENTEDGE;
|
||
|
||
hwnd = CreateWindowEx(dwExStyle, WC_EDIT, TEXT(""), dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndParent, (HMENU)id, hInstance, NULL);
|
||
|
||
if (!hwnd)
|
||
return NULL;
|
||
|
||
//DWM 1.4: Added Disable visual styles for this control.
|
||
SetWindowTheme(hwnd, L" ", L" ");
|
||
|
||
SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0L);
|
||
|
||
// Subclass Editor and save the OldProc
|
||
SetProp(hwnd, WPRC, (HANDLE)GetWindowLongPtr(hwnd, GWLP_WNDPROC));
|
||
SubclassWindow(hwnd, Edit_Proc);
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
#pragma endregion Edit Control
|
||
|
||
#pragma region IP Control
|
||
|
||
/// @brief Window procedure for the ipedit control.
|
||
///
|
||
/// @param hwnd Handle of the ipedit or a child edit control.
|
||
/// @param msg Which message?
|
||
/// @param wParam Message parameter.
|
||
/// @param lParam Message parameter.
|
||
///
|
||
/// @par Comments
|
||
/// The ipedit control wraps four edit control children.
|
||
/// Each child needs to be subclassed in order to handle
|
||
/// keyboard events. Each edit control posts some notification
|
||
/// to the parent ipedit control and I use this behavior to capture
|
||
/// and subclass these children. These children are subclassed to
|
||
/// this very procedure and so 'hwnd' could be parent ipedit or one
|
||
/// of the child Edits. Care then, must be taken to differentiate between
|
||
/// child and parent. I do this by getting the class name and restricting
|
||
/// the handling of certain messages to one or the other control type.
|
||
///
|
||
/// @returns LRESULT depends on message.
|
||
static LRESULT CALLBACK IpEdit_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
static TCHAR buf[MAX_PATH];
|
||
HWND hGParent = GetParent(GetParent(hwnd));
|
||
|
||
// Note: Instance data is attached to ipedit's grandparent
|
||
// or the edit field's greatgrandparent
|
||
GetClassName(hwnd, buf, NELEMS(buf));
|
||
BOOL fEdit = (0 == _tcsicmp(buf, WC_EDIT));
|
||
|
||
if (fEdit)
|
||
Control_GetInstanceData(GetParent(hGParent), &g_lpInst);
|
||
else
|
||
Control_GetInstanceData(hGParent, &g_lpInst);
|
||
|
||
HWND hIpEdit = fEdit ? GetParent(hwnd) : hwnd;
|
||
|
||
if (WM_DESTROY == msg) //Unsubclass the ipedit or child edit control
|
||
{
|
||
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (DWORD_PTR)GetProp(hwnd, WPRC)); //DWM 1.5: fixed cast
|
||
RemoveProp(hwnd, WPRC);
|
||
return 0;
|
||
}
|
||
else if (WM_KILLFOCUS == msg)//DWM 1.3: Added
|
||
{
|
||
if(fEdit)
|
||
{
|
||
// Determine if hwndNewfocus is a child of the IpEdit
|
||
if(hIpEdit != GetParent((HWND) wParam)) //No, result of mouse click
|
||
{
|
||
FORWARD_WM_CHAR(hIpEdit, VK_RETURN, 0, SNDMSG);//DWM 1.6: force update of grid data
|
||
Editor_OnKillFocus(hIpEdit, (HWND)wParam);
|
||
}
|
||
}
|
||
}
|
||
else if (WM_CHAR == msg && VK_RETURN == wParam)
|
||
{
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
//DWM 1.9: Updated this code to use DWORD and IPADDRESS macroes instead of 4 byte array and cast.
|
||
DWORD ip;
|
||
TCHAR buf[MAX_PATH];
|
||
if (4 == SNDMSG(hIpEdit, IPM_GETADDRESS, 0, (LPARAM) &ip))
|
||
{
|
||
_stprintf(buf, MAX_PATH, _T("%d.%d.%d.%d"), FIRST_IPADDRESS(ip), SECOND_IPADDRESS(ip), THIRD_IPADDRESS(ip), FOURTH_IPADDRESS(ip));
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, buf);
|
||
}
|
||
}
|
||
ShowWindow(hIpEdit, SW_HIDE);
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
Grid_NotifyParent();
|
||
return TRUE;
|
||
}
|
||
if (fEdit) //Handle keyboard events in the child edit controls
|
||
{
|
||
if (WM_GETDLGCODE == msg)
|
||
{
|
||
return DLGC_WANTALLKEYS;
|
||
}
|
||
else if (WM_MOUSEWHEEL == msg)
|
||
{
|
||
FORWARD_WM_CHAR(hwnd, VK_RETURN, 0, SNDMSG);
|
||
}
|
||
else if (WM_CHAR == msg && VK_TAB == wParam)
|
||
{
|
||
HWND hNext;
|
||
if (GetKeyState(VK_SHIFT) & 0x8000) //Shift tab
|
||
{
|
||
hNext = GetWindow(hwnd, GW_HWNDNEXT);
|
||
if (NULL == hNext) //DWM 1.3: Added Focus to Listbox
|
||
{
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
return TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
hNext = GetWindow(hwnd, GW_HWNDPREV);
|
||
if (NULL == hNext) //DWM 1.3: Added Focus to grid parent
|
||
{
|
||
SetFocusToParent();
|
||
Editor_OnKillFocus(hIpEdit, NULL);
|
||
return TRUE;
|
||
}
|
||
}
|
||
Edit_SetSel(hNext, 0, -1);
|
||
SetFocus(hNext);
|
||
return TRUE;
|
||
}
|
||
else if (WM_CHAR == msg && VK_ESCAPE == wParam)
|
||
{
|
||
ShowWindow(hIpEdit, SW_HIDE);
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
return TRUE;
|
||
}
|
||
else if (WM_PASTE == msg)
|
||
return TRUE; //Do not allow paste
|
||
}
|
||
else //Handle messages (events) in the parent ipedit control
|
||
{
|
||
if (WM_PAINT == msg) // Obliterate border
|
||
{
|
||
return Editor_OnPaint(hwnd, msg, wParam, lParam);
|
||
}
|
||
else if (WM_COMMAND == msg)
|
||
{
|
||
// Each of the control's edit fields posts notifications on showing,
|
||
// the first time they do so we'll grab and subclass them.
|
||
HWND hwndCtl = GET_WM_COMMAND_HWND(wParam, lParam);
|
||
{
|
||
WNDPROC lpfn = (WNDPROC)GetProp(hwndCtl, WPRC);
|
||
if (NULL == lpfn)
|
||
{
|
||
//Subclass child and save the OldProc
|
||
SetProp(hwndCtl, WPRC, (HANDLE)GetWindowLongPtr(hwndCtl, GWLP_WNDPROC));
|
||
SubclassWindow(hwndCtl, IpEdit_Proc);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return DefProc(hwnd, msg, wParam, lParam);
|
||
}
|
||
|
||
/// @brief Create an ipedit control to edit PIT_IP fields.
|
||
///
|
||
/// @param hInstance The handle of an instance.
|
||
/// @param hwndParent The handle of the parent (the visible listbox).
|
||
/// @param id An id tag for this control.
|
||
/// @param lprc A pointer to RECT struct that contains initial size data.
|
||
///
|
||
/// @par Comments
|
||
/// It is not possible to create this control with CW_USEDEFAULT for width
|
||
/// and height. The control cannot be properly resized once created and
|
||
/// so it is necessary to specify width and height at the time of creation.
|
||
///
|
||
/// @returns HWND A handle to the ipedit control.
|
||
static HWND CreateIpEdit(HINSTANCE hInstance, HWND hwndParent, INT id, LPRECT lprc)
|
||
{
|
||
INITCOMMONCONTROLSEX cc;
|
||
cc.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
||
cc.dwICC = ICC_INTERNET_CLASSES;
|
||
if (!InitCommonControlsEx(&cc))
|
||
return NULL;
|
||
|
||
DWORD dwStyle, dwExStyle;
|
||
HWND hwnd;
|
||
|
||
dwStyle = WS_CHILD;
|
||
|
||
dwExStyle = WS_EX_LEFT;
|
||
|
||
hwnd = CreateWindowEx(dwExStyle,
|
||
WC_IPADDRESS,
|
||
NULL,
|
||
dwStyle,
|
||
CW_USEDEFAULT, // x position can be changed after creation
|
||
CW_USEDEFAULT, // y position can be changed after creation
|
||
lprc->right - lprc->left, // width can only be set here
|
||
lprc->bottom - lprc->top, // height can only be set here
|
||
hwndParent, (HMENU)id, hInstance, NULL);
|
||
|
||
if (!hwnd)
|
||
return NULL;
|
||
|
||
SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0L);
|
||
|
||
SetProp(hwnd, WPRC, (HANDLE)GetWindowLongPtr(hwnd, GWLP_WNDPROC));
|
||
SubclassWindow(hwnd, IpEdit_Proc);
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
#pragma endregion IP Control
|
||
|
||
#pragma region Button Control
|
||
|
||
/// @brief Window procedure for the button control.
|
||
///
|
||
/// @param hButton Handle of button control.
|
||
/// @param msg Which message?
|
||
/// @param wParam Message parameter.
|
||
/// @param lParam Message parameter.
|
||
///
|
||
/// @returns LRESULT depends on message.
|
||
static LRESULT CALLBACK Button_Proc(HWND hButton, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
HWND hGParent = GetParent(GetParent(hButton));
|
||
|
||
// Note: Instance data is attached to buttons's grandparent
|
||
Control_GetInstanceData(hGParent, &g_lpInst);
|
||
|
||
if (WM_DESTROY == msg) // Unsubclass the button control
|
||
{
|
||
SetWindowLongPtr(hButton, GWLP_WNDPROC, (DWORD_PTR)GetProp(hButton, WPRC)); //DWM 1.5: fixed cast
|
||
RemoveProp(hButton, WPRC);
|
||
return 0;
|
||
}
|
||
else if (WM_KILLFOCUS == msg)
|
||
{
|
||
ShowWindow(hButton, SW_HIDE);
|
||
Editor_OnKillFocus(hButton, (HWND) wParam);
|
||
}
|
||
else if (WM_MOUSEWHEEL == msg)
|
||
{
|
||
FORWARD_WM_KEYDOWN(hButton, VK_ESCAPE, 0, 0, SNDMSG);
|
||
}
|
||
else if (WM_GETDLGCODE == msg)
|
||
{
|
||
return DLGC_WANTALLKEYS;
|
||
}
|
||
else if (WM_KEYUP == msg && VK_RETURN == wParam)
|
||
{
|
||
FORWARD_WM_KEYUP(hButton, VK_SPACE, 0, 0, SNDMSG);
|
||
return TRUE;
|
||
}
|
||
else if (WM_KEYDOWN == msg)
|
||
{
|
||
switch (wParam)
|
||
{
|
||
case VK_RETURN:
|
||
FORWARD_WM_KEYDOWN(hButton, VK_SPACE, 0, 0, SNDMSG);
|
||
return TRUE;
|
||
case VK_TAB:
|
||
if (GetKeyState(VK_SHIFT) & 0x8000)
|
||
{
|
||
FORWARD_WM_KEYDOWN(hButton, VK_ESCAPE, 0, 0, SNDMSG);
|
||
}
|
||
else //DWM 1.3: Added Focus to grid parent
|
||
{
|
||
ShowWindow(hButton, SW_HIDE);
|
||
SetFocusToParent();
|
||
}
|
||
return FALSE;
|
||
case VK_ESCAPE:
|
||
{
|
||
ShowWindow(hButton, SW_HIDE);
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
return DefProc(hButton, msg, wParam, lParam);
|
||
}
|
||
|
||
/// @brief Create button control to launch dialogs.
|
||
///
|
||
/// @param hInstance The handle of an instance.
|
||
/// @param hwndParent The handle of the parent (the visible listbox).
|
||
/// @param id An id tag for this control.
|
||
///
|
||
/// @returns HWND A handle to the button control.
|
||
static HWND CreateButton(HINSTANCE hInstance, HWND hwndParent, INT id)
|
||
{
|
||
DWORD dwStyle;
|
||
HWND hwnd;
|
||
|
||
dwStyle = WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON;
|
||
|
||
hwnd = CreateWindowEx(0,
|
||
WC_BUTTON,
|
||
TEXT("..."),
|
||
dwStyle,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
hwndParent,
|
||
(HMENU)id,
|
||
hInstance,
|
||
NULL);
|
||
|
||
if (!hwnd)
|
||
return NULL;
|
||
|
||
//DWM 1.4: Added Disable visual styles for this control.
|
||
SetWindowTheme(hwnd, L" ", L" ");
|
||
|
||
SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0L);
|
||
|
||
SetProp(hwnd, WPRC, (HANDLE)GetWindowLongPtr(hwnd, GWLP_WNDPROC));
|
||
SubclassWindow(hwnd, Button_Proc);
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
#pragma endregion Button Control
|
||
|
||
#pragma region Date Picker
|
||
|
||
/// @brief Window Procedure for the datepicker control.
|
||
///
|
||
/// @param hDate Handle of the datepicker.
|
||
/// @param msg Which message?
|
||
/// @param wParam Message parameter.
|
||
/// @param lParam Message parameter.
|
||
///
|
||
/// @par Comments
|
||
/// This procedure handles datepicker controls configured either as
|
||
/// date pickers or time pickers for three grid field types PIT_DATE,
|
||
/// PIT_TIME, and PIT_DATETIME.
|
||
///
|
||
/// @returns LRESULT depends on message.
|
||
static LRESULT CALLBACK DatePicker_Proc(HWND hDate, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
HWND hGParent = GetParent(GetParent(hDate));
|
||
|
||
// Note: Instance data is attached to datepicker's grandparent
|
||
Control_GetInstanceData(hGParent, &g_lpInst);
|
||
|
||
if (WM_DESTROY == msg) // Unsubclass the control
|
||
{
|
||
SetWindowLongPtr(hDate, GWLP_WNDPROC, (DWORD_PTR)GetProp(hDate, WPRC)); //DWM 1.5: fixed cast
|
||
RemoveProp(hDate, WPRC);
|
||
return 0;
|
||
}
|
||
else if (WM_KILLFOCUS == msg) //DWM 1.3: Added this
|
||
{
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
if(PIT_DATETIME == g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
HWND hFocus = (HWND)wParam;
|
||
if(g_lpInst->hwndCtl1 == hFocus ||
|
||
g_lpInst->hwndCtl2 == hFocus) // ignore thise two windows
|
||
return 0;
|
||
|
||
ShowWindow(g_lpInst->hwndCtl2, SW_HIDE);
|
||
}
|
||
}
|
||
FORWARD_WM_CHAR(hDate, VK_RETURN, 0, SNDMSG);//DWM 1.6: force update of grid data
|
||
Editor_OnKillFocus(hDate, (HWND)wParam);
|
||
}
|
||
else if (WM_PAINT == msg) // Obliterate border
|
||
{
|
||
//DWM 1.4: Changed this handler
|
||
if(g_lpInst->fXpOrLower)
|
||
{
|
||
return Editor_OnPaint(hDate, msg, wParam, lParam);
|
||
}
|
||
else
|
||
{
|
||
// First let the system do its thing
|
||
DefProc(hDate, msg, wParam, lParam);
|
||
|
||
// Next obliterate the border
|
||
HDC hdc = GetWindowDC(hDate);
|
||
RECT rect;
|
||
|
||
GetClientRect(hDate, &rect);
|
||
|
||
DrawBorder(hdc, &rect, BF_TOPLEFT, GetSysColor(COLOR_WINDOW));
|
||
rect.top += 1;
|
||
rect.left += 1;
|
||
//DWM 1.9: Added
|
||
#ifdef __POCC__
|
||
rect.bottom -= 2;
|
||
#else
|
||
rect.bottom += 2;
|
||
#endif
|
||
DrawBorder(hdc, &rect, BF_RECT, GetSysColor(COLOR_WINDOW));
|
||
|
||
ReleaseDC(hDate, hdc);
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
else if (WM_MOUSEWHEEL == msg)
|
||
{
|
||
FORWARD_WM_CHAR(hDate, VK_RETURN, 0, SNDMSG);
|
||
}
|
||
else if (WM_GETDLGCODE == msg)
|
||
{
|
||
return DLGC_WANTALLKEYS;
|
||
}
|
||
else if (WM_CHAR == msg && VK_RETURN == wParam)
|
||
{
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
switch (g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
case PIT_DATE:
|
||
{
|
||
SYSTEMTIME st = {0};
|
||
TCHAR buf[MAX_PATH] = {0};
|
||
DateTime_GetSystemtime(g_lpInst->hwndCtl1, &st);
|
||
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buf, MAX_PATH);
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, buf);
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_HIDE);
|
||
}
|
||
break;
|
||
case PIT_TIME:
|
||
{
|
||
SYSTEMTIME st = {0};
|
||
TCHAR buf[MAX_PATH] = {0};
|
||
DateTime_GetSystemtime(g_lpInst->hwndCtl1, &st);
|
||
GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, _T("hh':'mm':'ss tt"), buf, MAX_PATH);
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, buf);
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_HIDE);
|
||
}
|
||
break;
|
||
case PIT_DATETIME:
|
||
{
|
||
SYSTEMTIME st = {0};
|
||
TCHAR buf[MAX_PATH] = {0};
|
||
DateTime_GetSystemtime(g_lpInst->hwndCtl1, &st);
|
||
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, buf, MAX_PATH);
|
||
_tcscat(buf, _T(" "));
|
||
DateTime_GetSystemtime(g_lpInst->hwndCtl2, &st);
|
||
GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, _T("hh':'mm':'ss tt"), (LPTSTR) (buf + _tcslen(buf)), MAX_PATH - _tcslen(buf));
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, buf);
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_HIDE);
|
||
ShowWindow(g_lpInst->hwndCtl2, SW_HIDE);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
Grid_NotifyParent();
|
||
return TRUE; // handle Enter (NO BELL)
|
||
}
|
||
else if (WM_CHAR == msg && VK_TAB == wParam)
|
||
{
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
if (PIT_DATETIME == g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
//DWM 1.3: Fixed focus selection
|
||
HWND hFocus = GetFocus();
|
||
if(g_lpInst->hwndCtl1 == hFocus)
|
||
{
|
||
if (GetKeyState(VK_SHIFT) & 0x8000)
|
||
{
|
||
FORWARD_WM_CHAR(hDate, VK_RETURN, 0, SNDMSG);
|
||
}
|
||
else
|
||
{
|
||
SetFocus(g_lpInst->hwndCtl2);
|
||
}
|
||
}
|
||
else if (g_lpInst->hwndCtl2 == hFocus)
|
||
{
|
||
if (GetKeyState(VK_SHIFT) & 0x8000)
|
||
{
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
else
|
||
{
|
||
FORWARD_WM_CHAR(hDate, VK_RETURN, 0, SNDMSG);
|
||
SetFocusToParent();
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (GetKeyState(VK_SHIFT) & 0x8000)
|
||
{
|
||
FORWARD_WM_CHAR(hDate, VK_RETURN, 0, SNDMSG);
|
||
}
|
||
else
|
||
{
|
||
FORWARD_WM_CHAR(hDate, VK_RETURN, 0, SNDMSG);
|
||
SetFocusToParent();
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
else if (WM_KEYDOWN == msg && VK_ESCAPE == wParam)
|
||
{
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
switch (g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
case PIT_DATE:
|
||
case PIT_TIME:
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_HIDE);
|
||
break;
|
||
case PIT_DATETIME:
|
||
{
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_HIDE);
|
||
ShowWindow(g_lpInst->hwndCtl2, SW_HIDE);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return DefProc(hDate, msg, wParam, lParam);
|
||
}
|
||
|
||
/// @brief Create datepicker control configured either as a date or time picker.
|
||
///
|
||
/// @param hInstance The handle of an instance.
|
||
/// @param hwndParent The handle of the parent (the visible listbox).
|
||
/// @param id An id tag for this control.
|
||
/// @param fDate TRUE to create a date picker, FALSE for time picker.
|
||
///
|
||
/// @returns HWND A handle to the control.
|
||
static HWND CreateDatePicker(HINSTANCE hInstance, HWND hwndParent, INT id, BOOL fDate)
|
||
{
|
||
DWORD dwStyle, dwExStyle;
|
||
HWND hwnd;
|
||
|
||
dwStyle = WS_CHILD | (fDate ? DTS_SHORTDATEFORMAT : DTS_TIMEFORMAT);
|
||
|
||
dwExStyle = WS_EX_LEFT;
|
||
|
||
hwnd = CreateWindowEx(dwExStyle, DATETIMEPICK_CLASS, TEXT(""), dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndParent, (HMENU)id, hInstance, NULL);
|
||
|
||
if (!hwnd)
|
||
return NULL;
|
||
|
||
//DWM 1.4: Added Disable visual styles for this control.
|
||
SetWindowTheme(hwnd, L" ", L" ");
|
||
|
||
SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0L);
|
||
|
||
// Subclass date or timepicker and save the old proc
|
||
SetProp(hwnd, WPRC, (HANDLE)GetWindowLongPtr(hwnd, GWLP_WNDPROC));
|
||
SubclassWindow(hwnd, DatePicker_Proc);
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
#pragma endregion Date Picker
|
||
|
||
#pragma region Combo Box
|
||
|
||
/// @brief Window procedure for the combobox control.
|
||
///
|
||
/// @param hwnd Handle of the combobox or a child edit control.
|
||
/// @param msg Which message?
|
||
/// @param wParam Message parameter.
|
||
/// @param lParam Message parameter.
|
||
///
|
||
/// @par Comments
|
||
/// The combobox control wraps a listbox (drop down) and an edit control
|
||
/// child (if it is an editable list box). The edit control child needs
|
||
/// to be subclassed in order to handle keyboard events. Each child control
|
||
/// posts some notification to the parent combobox control and I use this
|
||
/// behavior to capture and subclass the child edit control. This edit control is
|
||
/// subclassed to this very procedure and so 'hwnd' could be parent combobox or
|
||
/// child edit control. Care then, must be taken to differentiate between
|
||
/// child and parent. I do this by getting the class name and restricting
|
||
/// the handling of certain messages to one or the other control type.
|
||
/// @par Warning
|
||
/// The drop down list control of a combobox is not a child of the combobox
|
||
/// it is the child of the desktop so that the list is not clipped by the
|
||
/// combobox's client area. Do not sub class it to this procedure since
|
||
/// doing so will cause the instance data pointer to be reset to NULL! The
|
||
/// instance data pointer is attached to the PropertyGrid's window.
|
||
///
|
||
/// @returns LRESULT depends on message.
|
||
static LRESULT CALLBACK ComboBox_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
static TCHAR classname[MAX_PATH];
|
||
HWND hGParent = GetParent(GetParent(hwnd));
|
||
|
||
// Note: Instance data is attached to combo's grandparent
|
||
// or the edit field's greatgrandparent
|
||
GetClassName(hwnd, classname, NELEMS(classname));
|
||
BOOL fEdit = (0 == _tcsicmp(classname, WC_EDIT));
|
||
|
||
if (fEdit)
|
||
Control_GetInstanceData(GetParent(hGParent), &g_lpInst);
|
||
else
|
||
Control_GetInstanceData(hGParent, &g_lpInst);
|
||
|
||
if (WM_DESTROY == msg) //Unsubclass the combobox or child edit control
|
||
{
|
||
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (DWORD_PTR)GetProp(hwnd, WPRC)); //DWM 1.5: fixed cast
|
||
RemoveProp(hwnd, WPRC);
|
||
return 0;
|
||
}
|
||
else if (WM_KILLFOCUS == msg)//DWM 1.3: Added
|
||
{
|
||
HWND hFocus = (HWND) wParam;
|
||
if(hwnd != GetParent(hFocus))// not a combobox editor or a combobox
|
||
{
|
||
FORWARD_WM_CHAR(hwnd, VK_RETURN, 0, SNDMSG);//DWM 1.6: force update of grid data
|
||
Editor_OnKillFocus(hwnd, (HWND) wParam);
|
||
}
|
||
}
|
||
else if (WM_PAINT == msg && !fEdit) // Obliterate border (differs from standard method)
|
||
{
|
||
// First let the system do its thing
|
||
DefProc(hwnd, msg, wParam, lParam);
|
||
|
||
// Next obliterate the border
|
||
HDC hdc = GetWindowDC(hwnd);
|
||
RECT rect;
|
||
|
||
GetClientRect(hwnd, &rect);
|
||
|
||
DrawBorder(hdc, &rect, BF_TOPLEFT, GetSysColor(COLOR_WINDOW));
|
||
|
||
rect.top += 1;
|
||
rect.left += 1;
|
||
DrawBorder(hdc, &rect, BF_TOPLEFT, GetSysColor(COLOR_WINDOW));
|
||
|
||
ReleaseDC(hwnd, hdc);
|
||
return TRUE;
|
||
}
|
||
else if (WM_GETDLGCODE == msg)
|
||
{
|
||
return DLGC_WANTALLKEYS;
|
||
}
|
||
else if (WM_COMMAND == msg)
|
||
{
|
||
// The editable combo's edit box posts a notification on loading
|
||
// the first time it does so we'll grab and subclass it.
|
||
HWND hwndCtl = GET_WM_COMMAND_HWND(wParam, lParam);
|
||
{
|
||
WNDPROC lpfn = (WNDPROC)GetProp(hwndCtl, WPRC);
|
||
if (NULL == lpfn)
|
||
{
|
||
// Do not subclass the drop down list
|
||
GetClassName(hwndCtl, classname, NELEMS(classname));
|
||
if (0 == _tcsicmp(classname, WC_EDIT))
|
||
{
|
||
//Subclass edit and save the old proc
|
||
SetProp(hwndCtl, WPRC, (HANDLE)GetWindowLongPtr(hwndCtl, GWLP_WNDPROC));
|
||
SubclassWindow(hwndCtl, ComboBox_Proc);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (WM_CHAR == msg && VK_RETURN == wParam)
|
||
{
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
GetWindowText(hwnd, classname, sizeof classname); //Combo or child edit text is the same
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, classname);
|
||
}
|
||
if (fEdit)
|
||
ShowWindow(GetParent(hwnd), SW_HIDE);
|
||
else
|
||
ShowWindow(hwnd, SW_HIDE);
|
||
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
Grid_NotifyParent();
|
||
return TRUE; // handle Enter (NO BELL)
|
||
}
|
||
else if (WM_CHAR == msg && VK_TAB == wParam)
|
||
{
|
||
if (GetKeyState(VK_SHIFT) & 0x8000)
|
||
{
|
||
FORWARD_WM_CHAR(hwnd, VK_RETURN, 0, SNDMSG);
|
||
}
|
||
else //DWM 1.3: Added Focus to grid parent
|
||
{
|
||
ShowWindow(fEdit ? GetParent(hwnd) : hwnd, SW_HIDE);
|
||
SetFocusToParent();
|
||
Editor_OnKillFocus(hwnd, NULL);
|
||
}
|
||
return TRUE;
|
||
}
|
||
else if (WM_KEYDOWN == msg && VK_ESCAPE == wParam)
|
||
{
|
||
if (fEdit)
|
||
ShowWindow(GetParent(hwnd), SW_HIDE);
|
||
else
|
||
ShowWindow(hwnd, SW_HIDE);
|
||
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
return FALSE;
|
||
}
|
||
return DefProc(hwnd, msg, wParam, lParam);
|
||
}
|
||
|
||
/// @brief Create combobox control configured either as editable or static.
|
||
///
|
||
/// @param hInstance The handle of an instance.
|
||
/// @param hwndParent The handle of the parent (the visible listbox).
|
||
/// @param id An id tag for this control.
|
||
/// @param fEditable TRUE to create an editable combo, FALSE for static.
|
||
///
|
||
/// @returns HWND A handle to the control.
|
||
static HWND CreateCombo(HINSTANCE hInstance, HWND hwndParent, INT id, BOOL fEditable)
|
||
{
|
||
DWORD dwStyle, dwExStyle;
|
||
HWND hwnd;
|
||
|
||
dwStyle = WS_CHILD | CBS_NOINTEGRALHEIGHT | (fEditable ? CBS_DROPDOWN : CBS_DROPDOWNLIST);
|
||
|
||
dwExStyle = WS_EX_LEFT;
|
||
|
||
hwnd = CreateWindowEx(dwExStyle,
|
||
WC_COMBOBOX,
|
||
NULL,
|
||
dwStyle,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
hwndParent,
|
||
(HMENU)id,
|
||
hInstance,
|
||
NULL);
|
||
|
||
if (!hwnd)
|
||
return NULL;
|
||
|
||
//DWM 1.4: Disable visual styles for the time being since the combo looks bad
|
||
// in this grid when drawn using the Vista and later styles.
|
||
SetWindowTheme(hwnd, L" ", L" ");
|
||
|
||
SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0L);
|
||
|
||
SetProp(hwnd, WPRC, (HANDLE)GetWindowLongPtr(hwnd, GWLP_WNDPROC));
|
||
SubclassWindow(hwnd, ComboBox_Proc);
|
||
|
||
return hwnd;
|
||
}
|
||
|
||
#pragma endregion Combo Box
|
||
|
||
#pragma region choose font
|
||
|
||
/// @brief Create a string from a property grid font dialog item.
|
||
///
|
||
/// @param lpLogFontItem The address of a PROPGRIDFONTITEM struct.
|
||
/// @param lpszFont The string representation of PROPGRIDFONTITEM elements.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID LogFontItem_FromString(LPPROPGRIDFONTITEM lpLogFontItem, LPTSTR lpszFont)
|
||
{
|
||
LPLOGFONT lpLf = (LPLOGFONT)lpLogFontItem;
|
||
_stscanf(lpszFont,
|
||
#ifdef _UNICODE
|
||
L"Height: %d " \
|
||
L"Width: %d " \
|
||
L"Escapement: %d " \
|
||
L"Orientation: %d " \
|
||
L"Weight: %d " \
|
||
L"Italic: %hhd " \
|
||
L"Underline: %hhd " \
|
||
L"StrikeOut: %hhd " \
|
||
L"CharSet: %hhd " \
|
||
L"OutPrecision: %hhd " \
|
||
L"ClipPrecision: %hhd " \
|
||
L"Quality: %hhd " \
|
||
L"PitchAndFamily: %hhd " \
|
||
L"FaceName: %32l[^\r\n] " \
|
||
L"Color: %d",
|
||
#else
|
||
"Height: %d " \
|
||
"Width: %d " \
|
||
"Escapement: %d " \
|
||
"Orientation: %d " \
|
||
"Weight: %d " \
|
||
"Italic: %hhd " \
|
||
"Underline: %hhd " \
|
||
"StrikeOut: %hhd " \
|
||
"CharSet: %hhd " \
|
||
"OutPrecision: %hhd " \
|
||
"ClipPrecision: %hhd " \
|
||
"Quality: %hhd " \
|
||
"PitchAndFamily: %hhd " \
|
||
"FaceName: %32[^\r\n] " \
|
||
"Color: %d",
|
||
#endif
|
||
&lpLf->lfHeight,
|
||
&lpLf->lfWidth,
|
||
&lpLf->lfEscapement,
|
||
&lpLf->lfOrientation,
|
||
&lpLf->lfWeight,
|
||
&lpLf->lfItalic,
|
||
&lpLf->lfUnderline,
|
||
&lpLf->lfStrikeOut,
|
||
&lpLf->lfCharSet,
|
||
&lpLf->lfOutPrecision,
|
||
&lpLf->lfClipPrecision,
|
||
&lpLf->lfQuality,
|
||
&lpLf->lfPitchAndFamily,
|
||
(LPTSTR)&lpLf->lfFaceName,
|
||
&lpLogFontItem->crFont);
|
||
}
|
||
|
||
/// @brief Create a string from a property grid font dialog item.
|
||
///
|
||
/// @param lpLogFontItem A pointer to a PROPGRIDFONTITEM.
|
||
///
|
||
/// @returns LPTSTR The string representation of PROPGRIDFONTITEM elements.
|
||
static LPTSTR LogFontItem_ToString(LPPROPGRIDFONTITEM lpLogFontItem)
|
||
{
|
||
static TCHAR buf[MAX_PATH];
|
||
_tmemset(buf, (TCHAR)0, MAX_PATH);
|
||
|
||
LPLOGFONT lpLf = (LPLOGFONT)lpLogFontItem;
|
||
_stprintf(buf, MAX_PATH,
|
||
#ifdef _UNICODE
|
||
L"Height: %d\r\n" \
|
||
L"Width: %d\r\n" \
|
||
L"Escapement: %d\r\n" \
|
||
L"Orientation: %d\r\n" \
|
||
L"Weight: %d\r\n" \
|
||
L"Italic: %d\r\n" \
|
||
L"Underline: %d\r\n" \
|
||
L"StrikeOut: %d\r\n" \
|
||
L"CharSet: %d\r\n" \
|
||
L"OutPrecision: %d\r\n" \
|
||
L"ClipPrecision: %d\r\n" \
|
||
L"Quality: %d\r\n" \
|
||
L"PitchAndFamily: %d\r\n" \
|
||
L"FaceName: %ls\r\n" \
|
||
L"Color: %d",
|
||
#else
|
||
"Height: %d\r\n" \
|
||
"Width: %d\r\n" \
|
||
"Escapement: %d\r\n" \
|
||
"Orientation: %d\r\n" \
|
||
"Weight: %d\r\n" \
|
||
"Italic: %d\r\n" \
|
||
"Underline: %d\r\n" \
|
||
"StrikeOut: %d\r\n" \
|
||
"CharSet: %d\r\n" \
|
||
"OutPrecision: %d\r\n" \
|
||
"ClipPrecision: %d\r\n" \
|
||
"Quality: %d\r\n" \
|
||
"PitchAndFamily: %d\r\n" \
|
||
"FaceName: %s\r\n" \
|
||
"Color: %d",
|
||
#endif
|
||
lpLf->lfHeight,
|
||
lpLf->lfWidth,
|
||
lpLf->lfEscapement,
|
||
lpLf->lfOrientation,
|
||
lpLf->lfWeight,
|
||
lpLf->lfItalic,
|
||
lpLf->lfUnderline,
|
||
lpLf->lfStrikeOut,
|
||
lpLf->lfCharSet,
|
||
lpLf->lfOutPrecision,
|
||
lpLf->lfClipPrecision,
|
||
lpLf->lfQuality,
|
||
lpLf->lfPitchAndFamily,
|
||
lpLf->lfFaceName,
|
||
lpLogFontItem->crFont);
|
||
return buf;
|
||
}
|
||
|
||
#pragma endregion choose font
|
||
|
||
#pragma region browse folder
|
||
|
||
/// @brief Create a string from a property grid file dialog item.
|
||
///
|
||
/// @param lpPgFdItem The address of a PROPGRIDFDITEM struct.
|
||
/// @param lpszFdItem The string representation of PROPGRIDFDITEM elements.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID FileDialogItem_FromString(LPPROPGRIDFDITEM lpPgFdItem, LPTSTR lpszFdItem)
|
||
{
|
||
//DWM 1.2: Added method
|
||
static TCHAR PgFdItem[4][MAX_PATH];
|
||
memset(PgFdItem, (TCHAR)0, sizeof(PgFdItem));
|
||
|
||
_stscanf(lpszFdItem,
|
||
#ifdef _UNICODE
|
||
L"Title: %256l[^\r\n] " \
|
||
L"Path: %256l[^\r\n] " \
|
||
L"Filter: %256l[^\r\n] " \
|
||
L"Default Extension: %3ls",
|
||
#else
|
||
"Title: %256[^\r\n] " \
|
||
"Path: %256[^\r\n] " \
|
||
"Filter: %256[^\r\n] " \
|
||
"Default Extension: %3s",
|
||
#endif
|
||
(LPTSTR)&PgFdItem[0], (LPTSTR)&PgFdItem[1], (LPTSTR)&PgFdItem[2], (LPTSTR)&PgFdItem[3]);
|
||
|
||
for(int i = 0; i < NELEMS(PgFdItem); i++)
|
||
{
|
||
if (0 == _tcscmp(_T("?"), PgFdItem[i]))
|
||
{
|
||
//Convert back to empty string
|
||
PgFdItem[i][0] = (TCHAR)0;
|
||
}
|
||
else if(2 == i)
|
||
{
|
||
//Convert back to double null-terminated string
|
||
for (LPTSTR ptr = PgFdItem[2]; *ptr; ptr++)
|
||
if (_T('\t') == *ptr) *ptr = _T('\0');
|
||
}
|
||
}
|
||
lpPgFdItem->lpszDlgTitle = PgFdItem[0];
|
||
lpPgFdItem->lpszFilePath = PgFdItem[1];
|
||
lpPgFdItem->lpszFilter = PgFdItem[2];
|
||
lpPgFdItem->lpszDefExt = PgFdItem[3];
|
||
}
|
||
|
||
/// @brief Create a string from a property grid file dialog item.
|
||
///
|
||
/// @param lpPgFdItem A pointer to a PROPGRIDFDITEM.
|
||
///
|
||
/// @returns LPTSTR The string representation of PROPGRIDFDITEM elements.
|
||
static LPTSTR FileDialogItem_ToString(LPPROPGRIDFDITEM lpPgFdItem)
|
||
{
|
||
static TCHAR szBuf[3 * MAX_PATH];
|
||
_tmemset(szBuf, (TCHAR)0, NELEMS(szBuf));
|
||
|
||
TCHAR filter[MAX_PATH] = { 0 };
|
||
|
||
//Copy filter string replacing \0 with \t"
|
||
INT iLen = 0;
|
||
for (LPTSTR psz = (LPTSTR)lpPgFdItem->lpszFilter,
|
||
ps = filter, pe = filter + NELEMS(filter) - 1;
|
||
*psz && ps < pe; psz += iLen + 1)
|
||
{
|
||
_tmemmove(ps, psz, (iLen = _tcslen(psz)));
|
||
ps += iLen;
|
||
*ps++ = _T('\t');
|
||
}
|
||
|
||
_stprintf(szBuf, NELEMS(szBuf),
|
||
#ifdef _UNICODE
|
||
L"Title: %ls\r\n" \
|
||
L"Path: %ls\r\n" \
|
||
L"Filter: %ls\r\n" \
|
||
L"Default Extension: %3ls",
|
||
#else
|
||
"Title: %s\r\n" \
|
||
"Path: %s\r\n" \
|
||
"Filter: %s\r\n" \
|
||
"Default Extension: %3s",
|
||
#endif
|
||
0 < _tcslen(lpPgFdItem->lpszDlgTitle) ? lpPgFdItem->lpszDlgTitle : _T("?"), //DWM 1.2: Added default "?"
|
||
0 < _tcslen(lpPgFdItem->lpszFilePath) ? lpPgFdItem->lpszFilePath : _T("?"), //DWM 1.2: Added default "?"
|
||
0 < _tcslen(filter) ? filter : _T("?"), //DWM 1.2: Added default "?"
|
||
0 < _tcslen(lpPgFdItem->lpszDefExt) ? lpPgFdItem->lpszDefExt : _T("?")); //DWM 1.2: Added default "?"
|
||
return szBuf;
|
||
}
|
||
|
||
/// @brief Convert a CHAR string to a WCHAR string.
|
||
///
|
||
/// @param dest Pointer to a buffer that will contain the converted string.
|
||
/// @param ccDest destination buffer size (number of characters)
|
||
/// @param source Constant pointer to a source string to be converted to WCHAR.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID CharToWide(LPWSTR dest, INT ccDest, const LPSTR source)
|
||
{
|
||
//DWM 1.7: Added
|
||
int i = 0;
|
||
|
||
while(source[i] != '\0' && i <= ccDest)
|
||
{
|
||
dest[i] = (WCHAR)source[i];
|
||
++i;
|
||
}
|
||
}
|
||
|
||
/// @brief Convert a path to an item id list object.
|
||
///
|
||
/// @param pszPath The file path string.
|
||
///
|
||
/// @returns LPITEMIDLIST A pointer to an item id list object.
|
||
static LPITEMIDLIST ConvertPathToLpItemIdList(LPTSTR pszPath)
|
||
{
|
||
LPITEMIDLIST pidl;
|
||
LPSHELLFOLDER pDesktopFolder;
|
||
ULONG chEaten;
|
||
ULONG dwAttributes;
|
||
|
||
//DWM 1.7: Added
|
||
#ifdef _UNICODE
|
||
LPWSTR pwzPath = pszPath;
|
||
#else
|
||
int iLen = strlen(pszPath);
|
||
WCHAR * pwzPath = new WCHAR[iLen + 1];
|
||
wmemset(pwzPath, (WCHAR)0, NELEMS(pwzPath));
|
||
CharToWide(pwzPath, NELEMS(pwzPath), pszPath);
|
||
#endif
|
||
|
||
if (SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder)))
|
||
{
|
||
pDesktopFolder->ParseDisplayName(NULL, NULL, pwzPath, &chEaten, &pidl, &dwAttributes);
|
||
pDesktopFolder->Release();
|
||
}
|
||
delete pwzPath;
|
||
return pidl;
|
||
}
|
||
|
||
/// @brief An application-defined callback function used with the
|
||
/// SHBrowseForFolder function.
|
||
///
|
||
/// @param hwnd Handle of the dialog.
|
||
/// @param uMsg Which message?
|
||
/// @param lParam Message parameter.
|
||
/// @param lpData Pointer to message data.
|
||
///
|
||
/// @returns BOOL FALSE.
|
||
static BOOL CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
|
||
{
|
||
//Buffer for the folder dialog
|
||
static TCHAR SelectedDir[MAX_PATH];
|
||
|
||
switch (uMsg)
|
||
{
|
||
case BFFM_INITIALIZED:
|
||
{
|
||
// change the selected folder.
|
||
SNDMSG(hwnd, BFFM_SETSELECTION, TRUE, lpData);
|
||
break;
|
||
}
|
||
case BFFM_SELCHANGED:
|
||
{
|
||
// Set the status window to the currently selected path.
|
||
if (SHGetPathFromIDList((LPITEMIDLIST)lParam, SelectedDir))
|
||
SNDMSG(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)SelectedDir);
|
||
break;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/// @brief Show the folder browser dialog to get a directory pathname.
|
||
///
|
||
/// @param hwnd Handle of the dialog's owner.
|
||
/// @param curPath The path of the currently selected folder.
|
||
/// @param title Browser dialog title.
|
||
/// @param rootPath Path location to begin browsing.
|
||
///
|
||
/// @returns BOOL Depends on message.
|
||
static LPTSTR BrowseFolder(HWND hwnd, LPTSTR curPath, LPTSTR title, LPTSTR rootPath)
|
||
{
|
||
BROWSEINFO bi;
|
||
static TCHAR szDir[MAX_PATH];
|
||
LPITEMIDLIST pidl;
|
||
LPMALLOC pMalloc;
|
||
|
||
if (SUCCEEDED(SHGetMalloc(&pMalloc)))
|
||
{
|
||
memset(&bi, 0, sizeof(bi));
|
||
bi.hwndOwner = hwnd;
|
||
bi.pszDisplayName = 0;
|
||
bi.lpszTitle = title;
|
||
bi.pidlRoot = ConvertPathToLpItemIdList(rootPath);
|
||
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
|
||
bi.lpfn = BrowseCallbackProc;
|
||
bi.lParam = (long)curPath;
|
||
|
||
pidl = SHBrowseForFolder(&bi);
|
||
if (pidl)
|
||
SHGetPathFromIDList(pidl, szDir);
|
||
else
|
||
_tmemset(szDir, (TCHAR)0, MAX_PATH);
|
||
|
||
// free memory used
|
||
pMalloc->Free(pidl);
|
||
pMalloc->Release();
|
||
}
|
||
return szDir;
|
||
}
|
||
|
||
#pragma endregion browse folder
|
||
|
||
#pragma region list box message handlers
|
||
|
||
/// @brief Find the catalog item that matches the given catalog name.
|
||
///
|
||
/// @param hwnd Handle of the listbox.
|
||
/// @param indexStart Index to begin search.
|
||
/// @param szCatalog The catalog name.
|
||
///
|
||
/// @returns INT The index of the catalog otherwise LB_ERR.
|
||
static INT ListBox_FindCatalog(HWND hwnd, INT indexStart, LPCTSTR szCatalog)
|
||
{
|
||
INT ln = _tcslen(szCatalog) + 1;
|
||
if (ln == 1 || indexStart >= ListBox_GetCount(hwnd))
|
||
return LB_ERR;
|
||
|
||
for (INT i = indexStart < 0 ? 0 : indexStart; i < ListBox_GetCount(hwnd); i++)
|
||
{
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(hwnd, i);
|
||
if (NULL != pItem && 0 == _tcsicmp(pItem->lpszCatalog, szCatalog))
|
||
return i;
|
||
}
|
||
return LB_ERR;
|
||
}
|
||
|
||
/// @brief Given the address of a LISTBOXITEM object of type PIT_CATALOG,
|
||
/// collapse it if it is expanded, or expand it if it is collapsed.
|
||
///
|
||
/// @param pItem Pointer to an LISTBOXITEM object.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ToggleCatalog(LPLISTBOXITEM pItem)
|
||
{
|
||
if (NULL != pItem && PIT_CATALOG != pItem->iItemType)
|
||
return;
|
||
|
||
pItem->fCollapsed = !pItem->fCollapsed;
|
||
|
||
if (pItem->fCollapsed)
|
||
{
|
||
for (INT i = ListBox_GetCount(g_lpInst->hwndListBox) - 1; i >= 0; i--)
|
||
{
|
||
LPLISTBOXITEM p = (LPLISTBOXITEM)ListBox_GetItemDataSafe(g_lpInst->hwndListBox, i);
|
||
if (NULL != p && _tcsicmp(p->lpszCatalog, pItem->lpszCatalog) == 0)
|
||
{
|
||
if (PIT_CATALOG != p->iItemType)
|
||
{
|
||
ListBox_DeleteString(g_lpInst->hwndListBox, i);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
INT idx = ListBox_FindCatalog(g_lpInst->hwndListBox, 0, pItem->lpszCatalog);
|
||
if (LB_ERR != idx)
|
||
{
|
||
LPLISTBOXITEM pp;
|
||
for (INT i = ListBox_GetCount(g_lpInst->hwndListMap) - 1; 0 <= i; i--)
|
||
{
|
||
pp = ListBox_GetItemDataSafe(g_lpInst->hwndListMap, i);
|
||
if (NULL != pp->lpszCatalog)
|
||
{
|
||
if (0 == _tcsicmp(pp->lpszCatalog, pItem->lpszCatalog))
|
||
{
|
||
if (PIT_CATALOG != pp->iItemType)
|
||
{
|
||
ListBox_InsertItemData(g_lpInst->hwndListBox, idx + 1, pp);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// @brief Handle the begin scroll event of a listbox.
|
||
///
|
||
/// @param hwnd Handle of a listbox.
|
||
///
|
||
/// @par Comments:
|
||
/// This event is not raised by the listbox but is detected
|
||
/// by indirect means.
|
||
///
|
||
/// @see ListBox_Proc() WM_NCLBUTTONDOWN for details.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnBeginScroll(HWND hwnd)
|
||
{
|
||
g_lpInst->fGotFocus = TRUE;//DWM 1:3: Added
|
||
|
||
if (NULL == g_lpInst->lpCurrent)
|
||
return;
|
||
|
||
switch (g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
case PIT_CHECK:
|
||
g_lpInst->lpCurrent->lpszMisc = UNSELECT;
|
||
break;
|
||
case PIT_STATIC:
|
||
break;
|
||
case PIT_DATETIME:
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_HIDE);
|
||
ShowWindow(g_lpInst->hwndCtl2, SW_HIDE);
|
||
break;
|
||
default:
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_HIDE);
|
||
}
|
||
}
|
||
|
||
/// @brief Handle the end scroll event of a listbox.
|
||
///
|
||
/// @param hwnd Handle of a listbox.
|
||
///
|
||
/// @par Comments:
|
||
/// This event is not raised by the listbox but is detected
|
||
/// by indirect means.
|
||
///
|
||
/// @see ListBox_Proc() WM_SETCURSOR for details.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnEndScroll(HWND hwnd)
|
||
{
|
||
if (NULL == g_lpInst->lpCurrent)
|
||
return;
|
||
|
||
RECT rect;
|
||
memset(&rect, 0, sizeof rect);
|
||
|
||
//Caculate the grid width
|
||
ListBox_GetItemRect(hwnd, ListBox_GetCurSel(hwnd), &rect);
|
||
|
||
rect.top += 1;
|
||
rect.left = g_lpInst->iHDivider + 1;
|
||
|
||
switch (g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
case PIT_CATALOG:
|
||
case PIT_STATIC:
|
||
break; //Don't display anything
|
||
case PIT_CHECK:
|
||
g_lpInst->lpCurrent->lpszMisc = SELECT;
|
||
RedrawWindow(hwnd, &rect, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
|
||
break;
|
||
case PIT_EDIT:
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
{
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
}
|
||
else //Display edit box
|
||
{
|
||
MoveWindow(g_lpInst->hwndCtl1, rect.left, rect.top, WIDTH(rect), HEIGHT(rect), TRUE);
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
break;
|
||
case PIT_COMBO:
|
||
case PIT_EDITCOMBO:
|
||
case PIT_IP:
|
||
case PIT_DATE:
|
||
case PIT_TIME:
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
else //Display editable combobox
|
||
{
|
||
MoveWindow(g_lpInst->hwndCtl1, rect.left, rect.top, WIDTH(rect), HEIGHT(rect), TRUE);
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
break;
|
||
case PIT_DATETIME:
|
||
if (NULL == g_lpInst->hwndCtl1 || NULL == g_lpInst->hwndCtl2)
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
else //Display date and time
|
||
{
|
||
RECT rect0, rect1;
|
||
rect0 = rect1 = rect;
|
||
rect0.right = rect0.left + (rect0.right - rect0.left) / 2;
|
||
rect1.left = rect1.left + (rect1.right - rect1.left) / 2;
|
||
MoveWindow(g_lpInst->hwndCtl1, rect0.left, rect0.top, WIDTH(rect0), HEIGHT(rect0), TRUE);
|
||
MoveWindow(g_lpInst->hwndCtl2, rect1.left, rect1.top, WIDTH(rect1), HEIGHT(rect1), TRUE);
|
||
ShowWindow(g_lpInst->hwndCtl2, SW_SHOW);
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
break;
|
||
default: //Button control
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
{
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
}
|
||
else //Display Button
|
||
{
|
||
if (WIDTH(rect) > 19)
|
||
{
|
||
rect.left = rect.right - 19;
|
||
rect.right -= 2;
|
||
}
|
||
|
||
rect.top += 2;
|
||
rect.bottom -= 2;
|
||
|
||
MoveWindow(g_lpInst->hwndCtl1, rect.left, rect.top, WIDTH(rect), HEIGHT(rect), TRUE);
|
||
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// @brief Handles WM_LBUTTONDOWN message in the listbox.
|
||
///
|
||
/// @param hwnd Handle of listbox.
|
||
/// @param fDoubleClick TRUE if this is a double click event.
|
||
/// @param x The xpos of the mouse.
|
||
/// @param y The ypos of the mouse.
|
||
/// @param keyFlags Set if certain keys down at time of click.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, INT x, INT y, UINT keyFlags)
|
||
{
|
||
if ((x >= g_lpInst->iHDivider - 5) && (x <= g_lpInst->iHDivider + 5))
|
||
{
|
||
//If mouse clicked on divider line, then start resizing
|
||
SetCursor(LoadCursor(NULL, IDC_SIZEWE));
|
||
|
||
RECT rcWindow;
|
||
GetWindowRect(hwnd, &rcWindow);
|
||
rcWindow.left += WIDTH_PART0;
|
||
rcWindow.right -= 10;
|
||
//Do not let mouse leave the list box boundary
|
||
ClipCursor(&rcWindow);
|
||
|
||
RECT rcClient;
|
||
GetClientRect(hwnd, &rcClient);
|
||
|
||
g_lpInst->fTracking = TRUE;
|
||
g_lpInst->nDivTop = rcClient.top;
|
||
g_lpInst->nDivBtm = rcClient.bottom;
|
||
g_lpInst->nOldDivX = x;
|
||
|
||
HDC hdc = GetDC(hwnd);
|
||
InvertLine(hdc, x, rcClient.top, x, rcClient.bottom);
|
||
ReleaseDC(hwnd, hdc);
|
||
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
TCHAR buf[MAX_PATH] = {0};
|
||
|
||
switch (g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
case PIT_CATALOG:
|
||
case PIT_STATIC:
|
||
break; //Ignore
|
||
case PIT_CHECK:
|
||
g_lpInst->lpCurrent->lpszMisc = UNSELECT; //Prevent toggle
|
||
break;
|
||
case PIT_DATETIME:
|
||
//DWM 1.6: force update of grid data
|
||
GetWindowText(g_lpInst->hwndCtl2,buf, sizeof buf);
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, buf);
|
||
ShowWindow(g_lpInst->hwndCtl2, SW_HIDE);
|
||
// Fall through
|
||
default:
|
||
//DWM 1.6: force update of grid data
|
||
GetWindowText(g_lpInst->hwndCtl1,buf, sizeof buf);
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, buf);
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_HIDE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//Capture the mouse
|
||
SetCapture(hwnd);
|
||
}
|
||
else
|
||
{
|
||
if (fDoubleClick)
|
||
{
|
||
INT i = LOWORD(ListBox_ItemFromPoint(hwnd, x, y));
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(hwnd, i);
|
||
if (NULL != pItem)
|
||
{
|
||
if (PIT_CATALOG == pItem->iItemType)
|
||
ToggleCatalog(pItem);
|
||
}
|
||
}
|
||
g_lpInst->fTracking = FALSE;
|
||
}
|
||
}
|
||
|
||
/// @brief Handles WM_LBUTTONUP message in the listbox.
|
||
///
|
||
/// @param hwnd Handle of listbox.
|
||
/// @param x The xpos of the mouse.
|
||
/// @param y The ypos of the mouse.
|
||
/// @param keyFlags Set if certain keys down at time of click.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnLButtonUp(HWND hwnd, INT x, INT y, UINT keyFlags)
|
||
{
|
||
if (g_lpInst->fTracking)
|
||
{
|
||
//If columns were being resized then this indicates
|
||
// that mouse is up so resizing is done. Need to redraw
|
||
// columns to reflect their new widths.
|
||
|
||
g_lpInst->fTracking = FALSE;
|
||
//If mouse was captured then release it
|
||
if (hwnd == GetCapture())
|
||
ReleaseCapture();
|
||
|
||
ClipCursor(NULL);
|
||
|
||
HDC hdc = GetDC(hwnd);
|
||
InvertLine(hdc, x, g_lpInst->nDivTop, x, g_lpInst->nDivBtm);
|
||
ReleaseDC(hwnd, hdc);
|
||
|
||
//Set the divider position to the new value
|
||
g_lpInst->iHDivider = x;
|
||
Refresh(hwnd);
|
||
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
switch (g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
case PIT_CATALOG:
|
||
case PIT_STATIC:
|
||
case PIT_CHECK:
|
||
break; //Ignore
|
||
case PIT_DATETIME:
|
||
ShowWindow(g_lpInst->hwndCtl2, SW_SHOW);
|
||
//Fall through
|
||
default:
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
INT i = LOWORD(ListBox_ItemFromPoint(hwnd, x, y));
|
||
|
||
if (ListBox_GetCurSel(hwnd) != i)
|
||
{
|
||
ListBox_SetCurSel(hwnd, i);
|
||
}
|
||
}
|
||
//Update the fields
|
||
FORWARD_WM_CHAR(g_lpInst->hwndCtl1, VK_RETURN, 0, SNDMSG);
|
||
FORWARD_WM_CHAR(g_lpInst->hwndCtl2, VK_RETURN, 0, SNDMSG);
|
||
|
||
FORWARD_WM_COMMAND(GetParent(hwnd), GetDlgCtrlID(hwnd), hwnd, LBN_SELCHANGE, SNDMSG);
|
||
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
switch(g_lpInst->lpCurrent->iItemType)//DWM 1.3: Added switch
|
||
{
|
||
case PIT_CATALOG:
|
||
case PIT_STATIC:
|
||
return;
|
||
case PIT_CHECK:
|
||
if(0 == _tcsicmp(g_lpInst->lpCurrent->lpszMisc, SELECT))
|
||
{
|
||
FORWARD_WM_KEYDOWN(hwnd, VK_SPACE, 0, 0, SNDMSG);
|
||
break;
|
||
} // else fallthrough
|
||
default:
|
||
FORWARD_WM_KEYDOWN(hwnd, VK_TAB, 0, 0, SNDMSG); //Focus to editor
|
||
}
|
||
}
|
||
}
|
||
|
||
/// @brief Handles WM_MOUSEMOVE message in the listbox.
|
||
///
|
||
/// @param hwnd Handle of listbox.
|
||
/// @param x The xpos of the mouse.
|
||
/// @param y The ypos of the mouse.
|
||
/// @param keyFlags Set if certain keys down at time of move.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnMouseMove(HWND hwnd, INT x, INT y, UINT keyFlags)
|
||
{
|
||
if (g_lpInst->fTracking)
|
||
{
|
||
//Move divider line to the mouse pos. if columns are
|
||
// currently being resized
|
||
HDC hdc = GetDC(hwnd);
|
||
//Remove old divider line
|
||
InvertLine(hdc, g_lpInst->nOldDivX, g_lpInst->nDivTop, g_lpInst->nOldDivX, g_lpInst->nDivBtm);
|
||
//Draw new divider line
|
||
InvertLine(hdc, x, g_lpInst->nDivTop, x, g_lpInst->nDivBtm);
|
||
ReleaseDC(hwnd, hdc);
|
||
|
||
g_lpInst->nOldDivX = x;
|
||
}
|
||
else if ((x >= g_lpInst->iHDivider - 5) && (x <= g_lpInst->iHDivider + 5))
|
||
{
|
||
//Set the cursor to a sizing cursor if the cursor is over the row divider
|
||
SetCursor(LoadCursor(NULL, IDC_SIZEWE));
|
||
}
|
||
//
|
||
//Set the tool tip text
|
||
//
|
||
if (NULL != g_lpInst->hwndToolTip)
|
||
{
|
||
TOOLINFO tiToolInfo;
|
||
//Allocate a buf for lpszText
|
||
TCHAR buf[80];
|
||
TCHAR newText[80] = {0};
|
||
tiToolInfo.lpszText = buf;
|
||
tiToolInfo.cbSize = sizeof(TOOLINFO);
|
||
|
||
//Populate TOOLINFO with the info for the tool (including old text)
|
||
ToolTip_EnumTools(g_lpInst->hwndToolTip, 0, &tiToolInfo);
|
||
|
||
if ((x >= g_lpInst->iHDivider + 5) && !g_lpInst->fTracking)
|
||
{
|
||
INT i = LOWORD(ListBox_ItemFromPoint(hwnd, x, y));
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(hwnd, i);
|
||
if (NULL != pItem)
|
||
{
|
||
switch (pItem->iItemType)
|
||
{
|
||
case PIT_FONT:
|
||
{
|
||
LPTSTR szFmt;
|
||
PROPGRIDFONTITEM pgfi;
|
||
LONG PointSize = 0;
|
||
LogFontItem_FromString(&pgfi, pItem->lpszCurValue);
|
||
HDC hDC = GetDC(hwnd);
|
||
PointSize = -MulDiv(pgfi.logFont.lfHeight, 72, GetDeviceCaps(hDC, LOGPIXELSY));
|
||
ReleaseDC(hwnd, hDC);
|
||
#ifdef _UNICODE
|
||
szFmt = _T("%ls %d");
|
||
#else
|
||
szFmt = _T("%s %d");
|
||
#endif
|
||
//Replace the text in the buf and update the tool
|
||
_stprintf(newText, NELEMS(buf), szFmt, pgfi.logFont.lfFaceName, PointSize);
|
||
}
|
||
break;
|
||
case PIT_CHECK: //Skip this item
|
||
break;
|
||
default:
|
||
//Replace the text in the buf and update the tool
|
||
_tcsncpy(newText, pItem->lpszCurValue, NELEMS(newText) - 1);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if(0 != _tcsncmp(buf, newText, _tcslen(newText))) //DWM 1.4: Do not update unless text changed to reduce flicker
|
||
{
|
||
//Clear out old text
|
||
_tmemset(buf, (TCHAR)0, NELEMS(buf)-1);
|
||
//Replace the text in the buf and update the tool
|
||
_tcsncpy(buf, newText, NELEMS(buf) - 1);
|
||
ToolTip_UpdateTipText(g_lpInst->hwndToolTip, &tiToolInfo);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// @brief Handle the selection of a listbox item of type PIT_EDIT.
|
||
///
|
||
/// @param hwnd The handle of the listbox.
|
||
/// @param rc RECT containing desired coordinates for the edit control.
|
||
/// @param pItem Pointer to a LISTBOXITEM object.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnSelectEdit(HWND hwnd, RECT rc, LPLISTBOXITEM pItem)
|
||
{
|
||
rc.top += 1;
|
||
|
||
//display edit box
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
g_lpInst->hwndCtl1 = CreateEdit(g_lpInst->hInstance, hwnd, ID_EDIT);
|
||
|
||
MoveWindow(g_lpInst->hwndCtl1, rc.left, rc.top, WIDTH(rc), HEIGHT(rc), TRUE);
|
||
|
||
//Set the text in the edit box to the property's current value
|
||
Edit_SetText(g_lpInst->hwndCtl1, pItem->lpszCurValue);
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
Edit_SetSel(g_lpInst->hwndCtl1, 0, -1);
|
||
}
|
||
|
||
/// @brief Handle the selection of a listbox item of type PIT_IP.
|
||
///
|
||
/// @param hwnd The handle of the listbox.
|
||
/// @param rc RECT containing desired coordinates for the ipedit control.
|
||
/// @param pItem Pointer to a LISTBOXITEM object.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnSelectIP(HWND hwnd, RECT rc, LPLISTBOXITEM pItem)
|
||
{
|
||
rc.top += 1;
|
||
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
g_lpInst->hwndCtl1 = CreateIpEdit(g_lpInst->hInstance, hwnd, ID_IPEDIT, &rc);
|
||
|
||
MoveWindow(g_lpInst->hwndCtl1, rc.left, rc.top, WIDTH(rc), HEIGHT(rc), TRUE);
|
||
|
||
//DWM 1.9: Updated this code to use DWORD array and MAKEIPADDRESS macroe instead of 4 byte array and cast.
|
||
//DWM 1.9: This was necessary to support MSVC _stscanf (sscanf - swscanf) beahavior which expects DWORD arguments.
|
||
DWORD ip[4] = { 0, 0, 0, 0 };
|
||
_stscanf(pItem->lpszCurValue, _T("%hhu.%hhu.%hhu.%hhu"), &ip[0], &ip[1], &ip[2], &ip[3]);
|
||
|
||
SNDMSG(g_lpInst->hwndCtl1, IPM_SETADDRESS, 0, MAKEIPADDRESS(ip[0],ip[1],ip[2],ip[3]));
|
||
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
|
||
/// @brief Handle the selection of a listbox item of the following types:
|
||
/// PIT_COLOR, PIT_FONT, PIT_FILE, and PIT_FOLDER.
|
||
///
|
||
/// @param hwnd The handle of the listbox.
|
||
/// @param rc RECT containing desired coordinates for the button control.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnDisplayButton(HWND hwnd, RECT rc)
|
||
{
|
||
if (WIDTH(rc) > 19)
|
||
{
|
||
rc.left = rc.right - 19;
|
||
rc.right -= 2;
|
||
}
|
||
|
||
rc.top += 2;
|
||
rc.bottom -= 2;
|
||
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
g_lpInst->hwndCtl1 = CreateButton(g_lpInst->hInstance, hwnd, ID_BUTTON);
|
||
|
||
MoveWindow(g_lpInst->hwndCtl1, rc.left, rc.top, WIDTH(rc), HEIGHT(rc), TRUE);
|
||
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
|
||
/// @brief Handle the selection of a listbox item of the following types:
|
||
/// PIT_DATE, PIT_TIME, and PIT_DATETIME.
|
||
///
|
||
/// @param hwnd The handle of the listbox.
|
||
/// @param rc RECT containing desired coordinates for the date or timepicker control(s).
|
||
/// @param pItem Pointer to a LISTBOXITEM object.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnSelectDateTime(HWND hwnd, RECT rc, LPLISTBOXITEM pItem)
|
||
{
|
||
rc.top += 1;
|
||
|
||
LPTSTR szFormat;
|
||
TCHAR buf[3];
|
||
_tmemset(buf, (TCHAR)0, 3);
|
||
|
||
SYSTEMTIME st;
|
||
memset(&st, 0, sizeof(SYSTEMTIME));
|
||
|
||
if (PIT_DATE == pItem->iItemType)
|
||
{
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
g_lpInst->hwndCtl1 = CreateDatePicker(g_lpInst->hInstance, hwnd, ID_DATE, TRUE);
|
||
|
||
MoveWindow(g_lpInst->hwndCtl1, rc.left, rc.top, WIDTH(rc), HEIGHT(rc), TRUE);
|
||
|
||
if (NULL != pItem)
|
||
_stscanf(pItem->lpszCurValue, _T("%hd/%hd/%hd"), &st.wMonth, &st.wDay, &st.wYear);
|
||
|
||
DateTime_SetSystemtime(g_lpInst->hwndCtl1, GDT_VALID, &st);
|
||
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
else if (PIT_TIME == pItem->iItemType)
|
||
{
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
g_lpInst->hwndCtl1 = CreateDatePicker(g_lpInst->hInstance, hwnd, ID_TIME, FALSE);
|
||
|
||
MoveWindow(g_lpInst->hwndCtl1, rc.left, rc.top, WIDTH(rc), HEIGHT(rc), TRUE);
|
||
|
||
#ifdef _UNICODE
|
||
szFormat = _T("%hd:%hd:%hd %2ls");
|
||
#else
|
||
szFormat = _T("%hd:%hd:%hd %2s");
|
||
#endif
|
||
|
||
if (NULL != pItem)
|
||
{
|
||
//DWM 1.6: Initialize unused date portion so DTP doesn't default to current date time
|
||
GetLocalTime(&st);
|
||
_stscanf(pItem->lpszCurValue, szFormat, &st.wHour, &st.wMinute, &st.wSecond, &buf);
|
||
}
|
||
if ((0 == _tcsicmp(_T("PM"), buf)) && st.wHour != 12)//DWM 1.6:Added st.wHour != 12
|
||
st.wHour += 12;
|
||
|
||
DateTime_SetSystemtime(g_lpInst->hwndCtl1, GDT_VALID, &st);
|
||
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
else if (PIT_DATETIME == pItem->iItemType)
|
||
{
|
||
RECT rect0, rect1;
|
||
rect0 = rect1 = rc;
|
||
rect0.right = rect0.left + (rect0.right - rect0.left) / 2;
|
||
rect1.left = rect1.left + (rect1.right - rect1.left) / 2;
|
||
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
g_lpInst->hwndCtl1 = CreateDatePicker(g_lpInst->hInstance, hwnd, ID_DATE, TRUE);
|
||
|
||
MoveWindow(g_lpInst->hwndCtl1, rect0.left, rect0.top, WIDTH(rect0), HEIGHT(rect0), TRUE);
|
||
|
||
if (NULL == g_lpInst->hwndCtl2)
|
||
g_lpInst->hwndCtl2 = CreateDatePicker(g_lpInst->hInstance, hwnd, ID_TIME, FALSE);
|
||
|
||
MoveWindow(g_lpInst->hwndCtl2, rect1.left, rect1.top, WIDTH(rect1), HEIGHT(rect1), TRUE);
|
||
|
||
#ifdef _UNICODE
|
||
szFormat = _T("%hd/%hd/%hd %hd:%hd:%hd %2ls");
|
||
#else
|
||
szFormat = _T("%hd/%hd/%hd %hd:%hd:%hd %2s");
|
||
#endif
|
||
|
||
if (NULL != pItem)
|
||
_stscanf(pItem->lpszCurValue, szFormat, &st.wMonth, &st.wDay, &st.wYear, &st.wHour, &st.wMinute, &st.wSecond, &buf);
|
||
|
||
if ((0 == _tcsicmp(_T("PM"), buf)) && st.wHour != 12)//DWM 1.6:Added st.wHour != 12
|
||
st.wHour += 12;
|
||
|
||
DateTime_SetSystemtime(g_lpInst->hwndCtl1, GDT_VALID, &st);
|
||
DateTime_SetSystemtime(g_lpInst->hwndCtl2, GDT_VALID, &st);
|
||
|
||
ShowWindow(g_lpInst->hwndCtl1, SW_SHOW);
|
||
ShowWindow(g_lpInst->hwndCtl2, SW_SHOW);
|
||
SetFocus(g_lpInst->hwndCtl1);
|
||
}
|
||
}
|
||
|
||
/// @brief Handle the selection of a listbox item of the following types:
|
||
/// PIT_COMBO, and PIT_EDITCOMBO.
|
||
///
|
||
/// @param hwnd The handle of the listbox.
|
||
/// @param rc RECT containing desired coordinates for the combobox control.
|
||
/// @param pItem Pointer to a LISTBOXITEM object.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnSelectComboBox(HWND hwnd, RECT rc, LPLISTBOXITEM pItem)
|
||
{
|
||
HWND hCombo;
|
||
|
||
rc.top += 1;
|
||
rc.bottom += 100;
|
||
|
||
if (PIT_COMBO == pItem->iItemType)
|
||
{
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
g_lpInst->hwndCtl1 = CreateCombo(g_lpInst->hInstance, hwnd, ID_COMBO, FALSE);
|
||
|
||
hCombo = g_lpInst->hwndCtl1;
|
||
}
|
||
else //PIT_EDITCOMBO
|
||
{
|
||
if (NULL == g_lpInst->hwndCtl1)
|
||
g_lpInst->hwndCtl1 = CreateCombo(g_lpInst->hInstance, hwnd, ID_EDITCOMBO, TRUE);
|
||
|
||
hCombo = g_lpInst->hwndCtl1;
|
||
}
|
||
ComboBox_ResetContent(hCombo);
|
||
MoveWindow(hCombo, rc.left, rc.top, WIDTH(rc), HEIGHT(rc), TRUE);
|
||
|
||
//Walk the item list and add each string until the empty string
|
||
for (LPTSTR p = pItem->lpszMisc; *p; p += _tcslen(p) + 1)
|
||
{
|
||
if (CB_ERR == ComboBox_FindStringExact(hCombo, 0, p))
|
||
ComboBox_AddString(hCombo, p);
|
||
}
|
||
|
||
ShowWindow(hCombo, SW_SHOW);
|
||
SetFocus(hCombo);
|
||
|
||
//Jump to the property's current value in the combo box
|
||
INT itm = ComboBox_FindStringExact(hCombo, 0, pItem->lpszCurValue);
|
||
if (itm != CB_ERR)
|
||
ComboBox_SetCurSel(hCombo, itm);
|
||
else
|
||
{
|
||
ComboBox_SetCurSel(hCombo, 0);
|
||
ComboBox_SetText(hCombo, pItem->lpszCurValue);
|
||
ComboBox_SetEditSel(hCombo, 0, -1);
|
||
}
|
||
}
|
||
|
||
/// @brief Handles WM_KEYDOWN messages sent to the listbox.
|
||
///
|
||
/// @param hwnd Handle of the listbox.
|
||
/// @param vk The virtual key code.
|
||
/// @param fDown TRUE for keydown (always TRUE).
|
||
/// @param cRepeat The number of times the keystroke is repeated
|
||
/// as a result of the user holding down the key.
|
||
/// @param flags Indicate OEM scan codes etc.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnKeyDown(HWND hwnd, UINT vk, BOOL fDown, INT cRepeat, UINT flags)
|
||
{
|
||
BOOL fHandled = FALSE;
|
||
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
RECT rc;
|
||
memset(&rc, 0, sizeof rc);
|
||
ListBox_GetItemRect(hwnd, ListBox_GetCurSel(hwnd), &rc);
|
||
|
||
if (PIT_CATALOG == g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
if (VK_RIGHT == vk && g_lpInst->lpCurrent->fCollapsed || VK_LEFT == vk && !g_lpInst->lpCurrent->fCollapsed)
|
||
{
|
||
ToggleCatalog(g_lpInst->lpCurrent);
|
||
fHandled = TRUE;
|
||
}
|
||
}
|
||
if (PIT_CHECK == g_lpInst->lpCurrent->iItemType && (VK_SPACE == vk || VK_RETURN == vk))
|
||
{
|
||
if (0 == _tcsicmp(g_lpInst->lpCurrent->lpszMisc, SELECT))
|
||
{
|
||
g_lpInst->lpCurrent->lpszCurValue = (0 == _tcsicmp(g_lpInst->lpCurrent->lpszCurValue, CHECKED) ? UNCHECKED : CHECKED);
|
||
RedrawWindow(hwnd, &rc, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
|
||
Grid_NotifyParent(); //DWM 1.2: Notify of check change
|
||
}
|
||
}
|
||
else if (PIT_CHECK == g_lpInst->lpCurrent->iItemType && VK_ESCAPE == vk)
|
||
{
|
||
g_lpInst->lpCurrent->lpszMisc = UNSELECT;
|
||
RedrawWindow(hwnd, &rc, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
|
||
}
|
||
else if (PIT_CHECK == g_lpInst->lpCurrent->iItemType &&
|
||
VK_LEFT <= vk && vk <= VK_DOWN &&
|
||
0 == _tcsicmp(g_lpInst->lpCurrent->lpszMisc, SELECT))
|
||
{
|
||
fHandled = TRUE;
|
||
}
|
||
else
|
||
{
|
||
if (VK_TAB == vk)
|
||
{
|
||
if (PIT_COLOR == g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
rc.left = g_lpInst->iHDivider + 1 + WIDTH_PART2;
|
||
}
|
||
else
|
||
{
|
||
rc.left = g_lpInst->iHDivider + 1;
|
||
}
|
||
switch (g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
case PIT_CATALOG: //Ignore this
|
||
case PIT_STATIC:
|
||
SetFocusToParent(); //DWM 1.3: Added
|
||
break;
|
||
case PIT_EDIT:
|
||
ListBox_OnSelectEdit(hwnd, rc, g_lpInst->lpCurrent);
|
||
break;
|
||
case PIT_COMBO:
|
||
case PIT_EDITCOMBO:
|
||
ListBox_OnSelectComboBox(hwnd, rc, g_lpInst->lpCurrent);
|
||
break;
|
||
case PIT_IP:
|
||
ListBox_OnSelectIP(hwnd, rc, g_lpInst->lpCurrent);
|
||
break;
|
||
case PIT_CHECK:
|
||
if(GetKeyState(VK_SHIFT) & 0x8000)
|
||
{
|
||
FORWARD_WM_KEYDOWN(hwnd, VK_ESCAPE, cRepeat, flags, SNDMSG);
|
||
}
|
||
else if(0 == _tcsicmp(g_lpInst->lpCurrent->lpszMisc, SELECT))
|
||
{
|
||
g_lpInst->lpCurrent->lpszMisc = UNSELECT;
|
||
SetFocusToParent();
|
||
}
|
||
else
|
||
{
|
||
g_lpInst->lpCurrent->lpszMisc = SELECT;
|
||
}
|
||
RedrawWindow(hwnd, &rc, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
|
||
break;
|
||
case PIT_DATE:
|
||
case PIT_TIME:
|
||
case PIT_DATETIME:
|
||
ListBox_OnSelectDateTime(hwnd, rc, g_lpInst->lpCurrent);
|
||
break;
|
||
default:
|
||
ListBox_OnDisplayButton(hwnd, rc);
|
||
break;
|
||
}
|
||
}
|
||
else if (VK_PRIOR == vk) //Mimic behavior of VS propGrid
|
||
{
|
||
FORWARD_WM_KEYDOWN(hwnd, VK_HOME, cRepeat, flags, SNDMSG);
|
||
fHandled = TRUE;
|
||
}
|
||
else if (VK_NEXT == vk) //Mimic behavior of VS propGrid
|
||
{
|
||
FORWARD_WM_KEYDOWN(hwnd, VK_END, cRepeat, flags, SNDMSG);
|
||
fHandled = TRUE;
|
||
}
|
||
}
|
||
}
|
||
if (!fHandled) //Not fully handled so follow up with default handler
|
||
FORWARD_WM_KEYDOWN(hwnd, vk, cRepeat, flags, DefProc);
|
||
}
|
||
|
||
/// @brief Handles WM_COMMAND messages sent to the listbox.
|
||
///
|
||
/// @param hwnd Handle of listbox.
|
||
/// @param id The id of the sender.
|
||
/// @param hwndCtl The hwnd of the sender.
|
||
/// @param codeNotify The notification code sent.
|
||
///
|
||
/// @par Comments:
|
||
/// We are only concerned with handling button click notifications here.
|
||
/// These notifications originate when the button is clicked for items of
|
||
/// type PIT_COLOR, PIT_FONT, PIT_FILE, and PIT_FOLDER.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID ListBox_OnCommand(HWND hwnd, INT id, HWND hwndCtl, UINT codeNotify)
|
||
{
|
||
if (ID_BUTTON == id)
|
||
{
|
||
if (NULL == g_lpInst->lpCurrent)
|
||
return;
|
||
|
||
//Display the appropriate common dialog depending on what type
|
||
// of chooser is associated with the property
|
||
switch (g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
case PIT_COLOR:
|
||
{
|
||
CHOOSECOLOR cc;
|
||
memset(&cc, 0, sizeof(cc));
|
||
cc.lStructSize = sizeof(cc);
|
||
cc.hwndOwner = hwnd;
|
||
cc.hInstance = (HWND)g_lpInst->hInstance;
|
||
cc.rgbResult = GetColor(g_lpInst->lpCurrent->lpszCurValue);
|
||
cc.lpCustColors = (LPDWORD)g_CustomColors;
|
||
cc.Flags = CC_FULLOPEN | CC_RGBINIT;
|
||
if (ChooseColor(&cc))
|
||
{
|
||
TCHAR buf[MAX_PATH];
|
||
_stprintf(buf, MAX_PATH, _T("%d,%d,%d"), GetRValue(cc.rgbResult), GetGValue(cc.rgbResult), GetBValue(cc.rgbResult));
|
||
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, buf);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case PIT_FILE:
|
||
{
|
||
TCHAR title[MAX_PATH] = { 0 };
|
||
TCHAR filename[MAX_PATH] = { 0 };
|
||
TCHAR path[MAX_PATH] = { 0 };
|
||
TCHAR filter[MAX_PATH] = { 0 };
|
||
TCHAR ext[4] = { 0 };
|
||
|
||
OPENFILENAME ofn;
|
||
memset(&ofn, 0, sizeof(OPENFILENAME));
|
||
|
||
ofn.lStructSize = sizeof(OPENFILENAME);
|
||
ofn.hwndOwner = hwnd;
|
||
ofn.hInstance = g_lpInst->hInstance;
|
||
|
||
_stscanf(g_lpInst->lpCurrent->lpszMisc,
|
||
#ifdef _UNICODE
|
||
L"Title: %32l[^\r\n] " \
|
||
L"Path: %256l[^\r\n] " \
|
||
L"Filter: %256l[^\r\n] " \
|
||
L"Default Extension: %3l[^\r\n] ",
|
||
#else
|
||
"Title: %32[^\r\n] " \
|
||
"Path: %256[^\r\n] " \
|
||
"Filter: %256[^\r\n] " \
|
||
"Default Extension: %3[^\r\n]",
|
||
#endif
|
||
(LPTSTR)&title, (LPTSTR)&path, (LPTSTR)&filter, (LPTSTR)&ext);
|
||
|
||
if (0 != _tcscmp(_T("?"), title)) //DWM 1.2: Added test
|
||
ofn.lpstrTitle = title;
|
||
else
|
||
ofn.lpstrTitle = _T("Select file");
|
||
|
||
if (0 != _tcscmp(_T("?"), path)) //DWM 1.2: Added test
|
||
{
|
||
//Exclude the filename
|
||
for (LPTSTR ptr = path + _tcslen(path) - 1; *ptr; ptr--)
|
||
if (*ptr == _T('\\'))
|
||
{
|
||
*ptr = _T('\0');
|
||
break;
|
||
}
|
||
ofn.lpstrInitialDir = path;
|
||
}
|
||
|
||
if (0 != _tcscmp(_T("?"), filter)) //DWM 1.2: Added test
|
||
{
|
||
//Convert back to double null-terminated string
|
||
for (LPTSTR ptr = filter; *ptr; ptr++)
|
||
if (_T('\t') == *ptr)
|
||
*ptr = _T('\0');
|
||
|
||
ofn.lpstrFilter = filter;
|
||
}
|
||
else
|
||
ofn.lpstrFilter = _T("All Files (*.*)\0*.*\0");
|
||
|
||
if (0 != _tcscmp(_T("?"), ext)) //DWM 1.2: Added test
|
||
{
|
||
ofn.lpstrDefExt = ext;
|
||
}
|
||
else
|
||
{
|
||
ofn.lpstrDefExt = _T("txt");
|
||
}
|
||
ofn.lpstrFile = filename;
|
||
ofn.nMaxFile = MAX_PATH;
|
||
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
|
||
|
||
if (GetOpenFileName(&ofn))
|
||
{
|
||
PROPGRIDFDITEM pgi;
|
||
pgi.lpszDlgTitle = (LPTSTR)ofn.lpstrTitle;
|
||
pgi.lpszFilePath = (LPTSTR)ofn.lpstrFile;
|
||
pgi.lpszFilter = (LPTSTR)ofn.lpstrFilter;
|
||
pgi.lpszDefExt = (LPTSTR)ofn.lpstrDefExt;
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, pgi.lpszFilePath);
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszMisc, FileDialogItem_ToString(&pgi));
|
||
}
|
||
else //DWM 1.2: Reset to unselected file
|
||
{
|
||
PROPGRIDFDITEM pgi;
|
||
pgi.lpszDlgTitle = (LPTSTR)ofn.lpstrTitle;
|
||
pgi.lpszFilePath = _T("");
|
||
pgi.lpszFilter = (LPTSTR)ofn.lpstrFilter;
|
||
pgi.lpszDefExt = (LPTSTR)ofn.lpstrDefExt;
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, pgi.lpszFilePath);
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszMisc, FileDialogItem_ToString(&pgi));
|
||
}
|
||
|
||
}
|
||
break;
|
||
|
||
case PIT_FONT:
|
||
{
|
||
CHOOSEFONT ocf;
|
||
memset(&ocf, 0, sizeof(ocf));
|
||
|
||
PROPGRIDFONTITEM pgfi;
|
||
LogFontItem_FromString(&pgfi, g_lpInst->lpCurrent->lpszCurValue);
|
||
|
||
ocf.lStructSize = sizeof(ocf);
|
||
ocf.hwndOwner = hwnd;
|
||
ocf.hInstance = g_lpInst->hInstance;
|
||
ocf.Flags = CF_INITTOLOGFONTSTRUCT | CF_EFFECTS | CF_SCREENFONTS;
|
||
ocf.lpLogFont = &pgfi.logFont;
|
||
ocf.rgbColors = pgfi.crFont;
|
||
|
||
if (ChooseFont(&ocf))
|
||
{
|
||
pgfi.crFont = ocf.rgbColors;
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, LogFontItem_ToString(&pgfi));
|
||
}
|
||
}
|
||
break;
|
||
|
||
case PIT_FOLDER:
|
||
{
|
||
LPTSTR temp = BrowseFolder(hwnd, g_lpInst->lpCurrent->lpszCurValue, _T(""), _T(""));
|
||
//DWM 1.2: Reset to unselected folder //if (0 < _tcslen(temp))
|
||
AllocatedString_Replace(g_lpInst->lpCurrent->lpszCurValue, temp);
|
||
}
|
||
break;
|
||
}
|
||
ShowWindow(hwndCtl, SW_HIDE);
|
||
SetFocus(hwnd);
|
||
Refresh(hwnd); //Trigger WM_DRAWITEM
|
||
Grid_NotifyParent();
|
||
}
|
||
}
|
||
|
||
#pragma endregion list box message handlers
|
||
|
||
#pragma region grid message handlers
|
||
|
||
/// @brief Handles WM_SIZE message.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param state Specifies the type of resizing requested.
|
||
/// @param cx The width of client area.
|
||
/// @param cy The height of client area.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnSize(HWND hwnd, UINT state, INT cx, INT cy)
|
||
{
|
||
g_lpInst->iVDivider = cy - g_lpInst->iDescHeight;
|
||
|
||
if (NULL != g_lpInst->hwndPropDesc)
|
||
{
|
||
//Size listbox component
|
||
SetWindowPos(g_lpInst->hwndListBox, NULL, 0, 0, cx,
|
||
g_lpInst->iVDivider - 2, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||
|
||
SetWindowPos(g_lpInst->hwndPropDesc, NULL, 0, g_lpInst->iVDivider,
|
||
cx, g_lpInst->iDescHeight, SWP_NOZORDER | SWP_NOACTIVATE);
|
||
}
|
||
else
|
||
{
|
||
//Size listbox component to fill parent
|
||
SetWindowPos(g_lpInst->hwndListBox, NULL, 0, 0, cx, cy,
|
||
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||
}
|
||
if(NULL != g_lpInst->hwndToolTip)//Resize ToolTips
|
||
{
|
||
TOOLINFO ti = { sizeof(ti) };
|
||
ti.hwnd = g_lpInst->hwndListBox;
|
||
ti.uId = 0;
|
||
GetClientRect(hwnd, &ti.rect);
|
||
ToolTip_NewToolRect(g_lpInst->hwndToolTip, &ti);
|
||
}
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
if(PIT_CHECK == g_lpInst->lpCurrent->iItemType)
|
||
g_lpInst->lpCurrent->lpszMisc = UNSELECT; //Prevent toggle
|
||
}
|
||
|
||
FORWARD_WM_COMMAND(hwnd, GetDlgCtrlID(g_lpInst->hwndListBox),
|
||
g_lpInst->hwndListBox, LBN_SELCHANGE, SNDMSG);
|
||
}
|
||
|
||
/// @brief Handles WM_SETCURSOR message.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param hwndCursor The handle of the cursor.
|
||
/// @param codeHitTest The hit test code.
|
||
/// @param msg The windows mouse message related to hit test.
|
||
///
|
||
/// @par Comments:
|
||
/// We are only concerned with detecting the completion of a sizing operation
|
||
/// consequently return false and let the default behavior stand.
|
||
///
|
||
/// @returns BOOL TRUE if handled.
|
||
static BOOL Grid_OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg)
|
||
{
|
||
if (NULL != g_lpInst->lpCurrent)
|
||
{
|
||
if(g_lpInst->fTracking) //Sizing operation concluded
|
||
{
|
||
if(PIT_CHECK == g_lpInst->lpCurrent->iItemType)
|
||
g_lpInst->lpCurrent->lpszMisc = SELECT;
|
||
|
||
g_lpInst->fTracking = FALSE;
|
||
FORWARD_WM_KEYDOWN(g_lpInst->hwndListBox, VK_TAB, 0, 0, SNDMSG); //Focus to editor
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/// @brief Handles WM_LBUTTONDOWN message.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param fDoubleClick TRUE if this is a double click event.
|
||
/// @param x The xpos of the mouse.
|
||
/// @param y The ypos of the mouse.
|
||
/// @param keyFlags Set if certain keys down at time of click.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, INT x, INT y, UINT keyFlags)
|
||
{
|
||
if ((y >= g_lpInst->iVDivider - 5) && (y <= g_lpInst->iVDivider + 5))
|
||
{
|
||
//If mouse clicked on divider line, then start resizing
|
||
SetCursor(LoadCursor(NULL, IDC_SIZENS));
|
||
|
||
RECT rc;
|
||
GetWindowRect(hwnd, &rc);
|
||
rc.top += 10;
|
||
rc.bottom -= 10;
|
||
//Do not let mouse leave the list box boundary
|
||
ClipCursor(&rc);
|
||
|
||
g_lpInst->fTracking = TRUE;
|
||
g_lpInst->nDivLft = 0;
|
||
g_lpInst->nDivRht = WIDTH(rc); //DWM 1.7: Changed from = rc.right
|
||
g_lpInst->nOldDivY = y;
|
||
|
||
HDC hdc = GetDC(hwnd);
|
||
HPEN hOld = (HPEN)SelectObject(hdc, CreatePen(PS_SOLID, 3, 0));
|
||
|
||
InvertLine(hdc, g_lpInst->nDivLft, g_lpInst->nOldDivY,
|
||
g_lpInst->nDivRht, g_lpInst->nOldDivY);
|
||
|
||
DeleteObject(SelectObject(hdc, hOld));
|
||
ReleaseDC(hwnd, hdc);
|
||
|
||
//Capture the mouse
|
||
SetCapture(hwnd);
|
||
}
|
||
}
|
||
|
||
/// @brief Handles WM_LBUTTONUP message.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param x The xpos of the mouse.
|
||
/// @param y The ypos of the mouse.
|
||
/// @param keyFlags Set if certain keys down at time of click.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnLButtonUp(HWND hwnd, INT x, INT y, UINT keyFlags)
|
||
{
|
||
if (g_lpInst->fTracking)
|
||
{
|
||
g_lpInst->fTracking = FALSE;
|
||
//If mouse was captured then release it
|
||
if (hwnd == GetCapture())
|
||
ReleaseCapture();
|
||
|
||
ClipCursor(NULL);
|
||
|
||
//Set the divider position to the new value
|
||
g_lpInst->nOldDivY = y;
|
||
|
||
HDC hdc = GetDC(hwnd);
|
||
HPEN hOld = (HPEN)SelectObject(hdc, CreatePen(PS_SOLID, 3, 0));
|
||
|
||
InvertLine(hdc, g_lpInst->nDivLft, g_lpInst->nOldDivY,
|
||
g_lpInst->nDivRht, g_lpInst->nOldDivY);
|
||
|
||
DeleteObject(SelectObject(hdc, hOld));
|
||
ReleaseDC(hwnd,hdc);
|
||
|
||
//Trigger a resize
|
||
RECT rc;
|
||
GetClientRect(hwnd, &rc);
|
||
g_lpInst->iDescHeight = HEIGHT(rc) - y;
|
||
Grid_OnSize(hwnd, 0, WIDTH(rc), HEIGHT(rc));
|
||
}
|
||
}
|
||
|
||
/// @brief Handles WM_MOUSEMOVE message.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param x The xpos of the mouse.
|
||
/// @param y The ypos of the mouse.
|
||
/// @param keyFlags Set if certain keys down at time of move.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnMouseMove(HWND hwnd, INT x, INT y, UINT keyFlags)
|
||
{
|
||
if ((y >= g_lpInst->iVDivider - 5) && (y <= g_lpInst->iVDivider + 5))
|
||
{
|
||
SetCursor(LoadCursor(NULL, IDC_SIZENS));
|
||
}
|
||
else if (g_lpInst->fTracking)
|
||
{
|
||
//Move divider line to the mouse pos. if columns are
|
||
// currently being resized
|
||
HDC hdc = GetDC(hwnd);
|
||
HPEN hOld = (HPEN)SelectObject(hdc, CreatePen(PS_SOLID, 3, 0));
|
||
|
||
//Remove old divider line
|
||
InvertLine(hdc, g_lpInst->nDivLft, g_lpInst->nOldDivY,
|
||
g_lpInst->nDivRht, g_lpInst->nOldDivY);
|
||
//Draw new divider line
|
||
g_lpInst->nOldDivY = y;
|
||
|
||
InvertLine(hdc, g_lpInst->nDivLft, g_lpInst->nOldDivY,
|
||
g_lpInst->nDivRht, g_lpInst->nOldDivY);
|
||
|
||
DeleteObject(SelectObject(hdc, hOld));
|
||
ReleaseDC(hwnd, hdc);
|
||
}
|
||
else
|
||
{
|
||
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
||
}
|
||
}
|
||
|
||
/// @brief Handles WM_CTLCOLORLISTBOX message sent to the grid.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param hdc The handle of the device context.
|
||
/// @param hwndChild The handle of the listbox.
|
||
/// @param type CTLCOLOR_LISTBOX.
|
||
///
|
||
/// @returns HBRUSH The handle of the brush used to paint the
|
||
/// listbox's background.
|
||
static HBRUSH Grid_OnCtlColorListbox(HWND hwnd, HDC hdc, HWND hwndChild, INT type)
|
||
{
|
||
return SetColor(hdc, GetSysColor(COLOR_MENUTEXT), GetSysColor(COLOR_3DFACE));
|
||
}
|
||
|
||
/// @brief Handles WM_CTLCOLORSTATIC message sent to the grid.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param hdc The handle of the device context.
|
||
/// @param hwndChild The handle of the static.
|
||
/// @param type CTLCOLOR_STATIC.
|
||
///
|
||
/// @returns HBRUSH The handle of the brush used to paint the
|
||
/// static's background.
|
||
static HBRUSH Grid_OnCtlColorStatic(HWND hwnd, HDC hdc, HWND hwndChild, INT type)
|
||
{
|
||
//DWM 1.3: Keep the area between the description and the list refreshed
|
||
if (NULL != g_lpInst->hwndPropDesc)
|
||
{
|
||
RECT rc;
|
||
GetClientRect(hwnd, &rc);
|
||
|
||
HDC hdc = GetDC(hwnd);
|
||
FillSolidRect(hdc,MAKE_PRECT(0, g_lpInst->iVDivider - 2,
|
||
WIDTH(rc), g_lpInst->iVDivider),GetSysColor(COLOR_BTNFACE));
|
||
ReleaseDC(hwnd,hdc);
|
||
}
|
||
return FORWARD_WM_CTLCOLORSTATIC(hwnd, hdc, hwndChild, DefWindowProc);
|
||
}
|
||
|
||
/// @brief Handles WM_MEASUREITEM message sent to the grid when the owner-drawn
|
||
/// listbox is created.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param lpMeasureItem The structure that contains the dimensions of the
|
||
/// owner-drawn listbox.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpMeasureItem)
|
||
{
|
||
lpMeasureItem->itemHeight = MINIMUM_ITEM_HEIGHT; //pixels
|
||
}
|
||
|
||
/// @brief Ensure that a catalog item matching the given catalog name is expanded.
|
||
///
|
||
/// @param szCatalog The catalog name.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_ExpandCatalog(LPCTSTR szCatalog)
|
||
{
|
||
if (NULL != szCatalog)
|
||
{
|
||
INT ln = _tcslen(szCatalog) + 1; //Check for empty string
|
||
if (ln == 1)
|
||
return;
|
||
}
|
||
|
||
for (INT i = 0; i < ListBox_GetCount(g_lpInst->hwndListBox); i++)
|
||
{
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(g_lpInst->hwndListBox, i);
|
||
if (NULL != pItem && PIT_CATALOG == pItem->iItemType &&
|
||
(NULL == szCatalog || 0 == _tcsicmp(pItem->lpszCatalog, szCatalog)))
|
||
{
|
||
if (pItem->fCollapsed)
|
||
ToggleCatalog(pItem);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// @brief Ensure that a catalog item matching the given catalog name is collapsed.
|
||
///
|
||
/// @param szCatalog The catalog name.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_CollapseCatalog(LPCTSTR szCatalog)
|
||
{
|
||
if (NULL != szCatalog)
|
||
{
|
||
INT ln = _tcslen(szCatalog) + 1; //Check for empty string
|
||
if (ln == 1)
|
||
return;
|
||
}
|
||
for (INT i = 0; i < ListBox_GetCount(g_lpInst->hwndListBox); i++)
|
||
{
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(g_lpInst->hwndListBox, i);
|
||
if (NULL != pItem && PIT_CATALOG == pItem->iItemType &&
|
||
(NULL == szCatalog || 0 == _tcsicmp(pItem->lpszCatalog, szCatalog)))
|
||
{
|
||
if (!pItem->fCollapsed)
|
||
ToggleCatalog(pItem);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// @brief Handles WM_COMMAND message.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param id The id of the sender.
|
||
/// @param hwndCtl The hwnd of the sender.
|
||
/// @param codeNotify The notification code sent.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnCommand(HWND hwnd, INT id, HWND hwndCtl, UINT codeNotify)
|
||
{
|
||
if (g_lpInst->hwndListBox == hwndCtl)
|
||
{
|
||
switch (codeNotify)
|
||
{
|
||
case LBN_SELCHANGE:
|
||
{
|
||
SetFocus(hwndCtl);
|
||
INT iCurSel = ListBox_GetCurSel(hwndCtl);
|
||
|
||
//If this is a checkbox, notify of the previous item's change
|
||
LPLISTBOXITEM pItem = (LPLISTBOXITEM)ListBox_GetItemDataSafe(hwndCtl,
|
||
g_lpInst->iPrevSel);
|
||
if (NULL != pItem)
|
||
{
|
||
if (PIT_CHECK == pItem->iItemType)
|
||
{
|
||
if (iCurSel != g_lpInst->iPrevSel &&
|
||
0 == _tcsicmp(pItem->lpszMisc, SELECT))
|
||
pItem->lpszMisc = UNSELECT;
|
||
}
|
||
}
|
||
g_lpInst->lpCurrent = (LPLISTBOXITEM)ListBox_GetItemDataSafe(hwndCtl,
|
||
iCurSel);
|
||
if (NULL == g_lpInst->lpCurrent)
|
||
return;
|
||
|
||
//Display the property description if desired
|
||
if (NULL != g_lpInst->hwndPropDesc)
|
||
Static_SetText(g_lpInst->hwndPropDesc,
|
||
g_lpInst->lpCurrent->lpszPropDesc);
|
||
|
||
//Destroy previous editor
|
||
if (NULL != g_lpInst->hwndCtl1)
|
||
{
|
||
DestroyWindow(g_lpInst->hwndCtl1);
|
||
g_lpInst->hwndCtl1 = NULL;
|
||
}
|
||
if (NULL != g_lpInst->hwndCtl2)
|
||
{
|
||
DestroyWindow(g_lpInst->hwndCtl2);
|
||
g_lpInst->hwndCtl2 = NULL;
|
||
}
|
||
|
||
//Redraw the selection
|
||
if(iCurSel != g_lpInst->iPrevSel)
|
||
{
|
||
RECT rc;
|
||
memset(&rc, 0, sizeof rc);
|
||
|
||
ListBox_GetItemRect(hwndCtl, g_lpInst->iPrevSel, &rc);
|
||
RedrawWindow(hwndCtl, &rc,
|
||
NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
|
||
|
||
ListBox_GetItemRect(hwndCtl, iCurSel, &rc);
|
||
RedrawWindow(hwndCtl, &rc,
|
||
NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
|
||
|
||
g_lpInst->iPrevSel = iCurSel;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// @brief Handles WM_DRAWITEM message sent to the grid when a visual aspect of
|
||
/// the owner-drawn listbox has changed.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param lpDIS The structure that contains information about the item
|
||
/// to be drawn and the type of drawing required.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT *lpDIS)
|
||
{
|
||
UINT nIndex = lpDIS->itemID;
|
||
|
||
if (lpDIS->hwndItem != g_lpInst->hwndListBox)
|
||
return;
|
||
|
||
if (nIndex == (UINT) - 1 || !(lpDIS->itemAction & ODA_DRAWENTIRE))
|
||
return;
|
||
|
||
//Get the item for the current row
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(lpDIS->hwndItem, nIndex);
|
||
if (NULL == pItem)
|
||
return;
|
||
|
||
RECT rectFullItem, rectPart0, rectCatPart1, rectPart1, rectPart2, rectPart3;
|
||
|
||
rectFullItem = lpDIS->rcItem;
|
||
|
||
if (g_lpInst->iHDivider == 0)
|
||
g_lpInst->iHDivider = WIDTH(rectFullItem) / 2;
|
||
|
||
rectPart0 = rectFullItem;
|
||
rectPart0.right = rectPart0.left + WIDTH_PART0;
|
||
|
||
rectCatPart1 = rectFullItem;
|
||
rectCatPart1.left = rectPart0.right;
|
||
|
||
rectPart1 = rectCatPart1;
|
||
rectPart1.right = g_lpInst->iHDivider;
|
||
|
||
rectPart2 = rectFullItem;
|
||
rectPart2.left = g_lpInst->iHDivider + (PIT_CATALOG == pItem->iItemType ? 0 : 1);
|
||
if (PIT_COLOR == pItem->iItemType || PIT_CHECK == pItem->iItemType)
|
||
{
|
||
rectPart2.right = rectPart2.left + WIDTH_PART2;
|
||
}
|
||
else
|
||
rectPart2.right = rectPart2.left;
|
||
|
||
rectPart3 = rectFullItem;
|
||
rectPart3.left = rectPart2.right;
|
||
|
||
//
|
||
//First part of item
|
||
//
|
||
FillRect(lpDIS->hDC, &rectPart0, (HBRUSH) GetStockObject(HOLLOW_BRUSH));
|
||
|
||
if (PIT_CATALOG == pItem->iItemType) //Draw expand / collapse box
|
||
{
|
||
RECT rect2;
|
||
rect2.left = rectPart0.left + 4;
|
||
rect2.top = rectPart0.top + 4;
|
||
rect2.right = rect2.left + 9;
|
||
rect2.bottom = rect2.top + 9;
|
||
|
||
FillRect(lpDIS->hDC, &rect2, GetSysColorBrush(COLOR_WINDOW));
|
||
FrameRect(lpDIS->hDC, &rect2, (HBRUSH) GetStockObject(BLACK_BRUSH));
|
||
|
||
POINT ptCtr;
|
||
ptCtr.x = (LONG) (rect2.left + (WIDTH(rect2) * 0.5));
|
||
ptCtr.y = (LONG) (rect2.top + (HEIGHT(rect2) * 0.5));
|
||
InflateRect(&rect2, -2, -2);
|
||
|
||
DrawLine(lpDIS->hDC, rect2.left, ptCtr.y, rect2.right, ptCtr.y); //Make a -
|
||
if (pItem->fCollapsed) //Convert to +
|
||
DrawLine(lpDIS->hDC, ptCtr.x, rect2.top, ptCtr.x, rect2.bottom);
|
||
}
|
||
|
||
//
|
||
//Second part of item
|
||
//
|
||
|
||
//If it is the selected item, set its background-color
|
||
HFONT oldFont;
|
||
if (PIT_CATALOG == pItem->iItemType)
|
||
{
|
||
FillRect(lpDIS->hDC, &rectCatPart1, (HBRUSH) GetStockObject(HOLLOW_BRUSH));
|
||
|
||
if (nIndex == (UINT)ListBox_GetCurSel(lpDIS->hwndItem) && g_lpInst->fGotFocus)
|
||
{
|
||
InflateRect(&rectCatPart1, -2, -2);
|
||
FrameRect(lpDIS->hDC, &rectCatPart1, GetSysColorBrush(COLOR_BTNSHADOW));
|
||
InflateRect(&rectCatPart1, 2, 2);
|
||
}
|
||
|
||
//Write the property name (bold font, Visual Studio style)
|
||
oldFont = (HFONT) SelectObject(lpDIS->hDC, Font_SetBold(lpDIS->hwndItem, TRUE));
|
||
SetTextColor(lpDIS->hDC, GetSysColor(COLOR_BTNTEXT));
|
||
DrawText(lpDIS->hDC, pItem->lpszCatalog, _tcslen(pItem->lpszCatalog),
|
||
MAKE_PRECT(rectCatPart1.left + 6, rectCatPart1.top + 3,
|
||
rectCatPart1.right - 6, rectCatPart1.bottom + 3),
|
||
DT_NOCLIP | DT_LEFT | DT_SINGLELINE);
|
||
}
|
||
else
|
||
{
|
||
SetBkMode(lpDIS->hDC, TRANSPARENT);
|
||
|
||
FillRect(lpDIS->hDC, &rectPart1,
|
||
GetSysColorBrush(nIndex == (UINT)ListBox_GetCurSel(lpDIS->hwndItem) ?
|
||
(g_lpInst->fGotFocus ? COLOR_HIGHLIGHT : COLOR_BTNFACE) : COLOR_WINDOW));
|
||
|
||
//Write the property name
|
||
oldFont = (HFONT) SelectObject(lpDIS->hDC, Font_SetBold(lpDIS->hwndItem, FALSE));
|
||
SetTextColor(lpDIS->hDC,
|
||
GetSysColor(nIndex == (UINT)ListBox_GetCurSel(lpDIS->hwndItem) ?
|
||
(g_lpInst->fGotFocus ? COLOR_HIGHLIGHTTEXT : COLOR_BTNTEXT) : COLOR_WINDOWTEXT));
|
||
DrawText(lpDIS->hDC, pItem->lpszPropName, _tcslen(pItem->lpszPropName),
|
||
MAKE_PRECT(rectPart1.left + 3, rectPart1.top + 3, rectPart1.right - 3,
|
||
rectPart1.bottom + 3), DT_NOCLIP | DT_LEFT | DT_SINGLELINE);
|
||
|
||
DrawBorder(lpDIS->hDC, &rectPart1, BF_TOPRIGHT,
|
||
GetSysColor(COLOR_BTNFACE));
|
||
}
|
||
DeleteObject(oldFont);
|
||
|
||
//
|
||
//Third part - Draw color box or check box
|
||
//
|
||
if (PIT_CATALOG != pItem->iItemType)
|
||
{
|
||
FillRect(lpDIS->hDC, &rectPart2, GetSysColorBrush(COLOR_WINDOW));
|
||
|
||
RECT rect3 = rectPart2;
|
||
rect3.left += 2;
|
||
rect3.top += 3;
|
||
rect3.bottom = rect3.top + 15;
|
||
rect3.right = rect3.left + 15;
|
||
|
||
if (PIT_COLOR == pItem->iItemType)
|
||
{
|
||
FillSolidRect(lpDIS->hDC, &rect3, GetColor(pItem->lpszCurValue));
|
||
FrameRect(lpDIS->hDC, &rect3, (HBRUSH) GetStockObject(BLACK_BRUSH));
|
||
}
|
||
else if (PIT_CHECK == pItem->iItemType)
|
||
{
|
||
if (0 == _tcsicmp(pItem->lpszMisc, SELECT))
|
||
{
|
||
FillRect(lpDIS->hDC, &rectPart2,
|
||
GetSysColorBrush(COLOR_HIGHLIGHT));
|
||
}
|
||
DrawFrameControl(lpDIS->hDC, &rect3, DFC_BUTTON, DFCS_BUTTONCHECK |
|
||
(_tcsicmp(pItem->lpszCurValue, CHECKED) == 0 ? DFCS_CHECKED : 0));
|
||
|
||
//DWM 1.8: Added
|
||
if (FLATCHECKS & (DWORD)GetWindowLongPtr(lpDIS->hwndItem, GWLP_USERDATA))
|
||
{
|
||
//Make border thin
|
||
FrameRect(lpDIS->hDC, &rect3, GetSysColorBrush(COLOR_BTNFACE));
|
||
InflateRect(&rect3, -1, -1);
|
||
FrameRect(lpDIS->hDC, &rect3, GetSysColorBrush(COLOR_WINDOW));
|
||
}
|
||
}
|
||
}
|
||
DrawBorder(lpDIS->hDC, &rectPart2, BF_TOP, GetSysColor(COLOR_BTNFACE));
|
||
|
||
//
|
||
//Fourth part - Write the initial property value in the second rectangle
|
||
//
|
||
if (PIT_CATALOG != pItem->iItemType)
|
||
{
|
||
FillRect(lpDIS->hDC, &rectPart3, GetSysColorBrush(COLOR_WINDOW));
|
||
}
|
||
if (PIT_CHECK != pItem->iItemType)
|
||
{
|
||
SetBkMode(lpDIS->hDC, TRANSPARENT);
|
||
|
||
if (PIT_FONT == pItem->iItemType)
|
||
{
|
||
TCHAR buf[MAX_PATH] = { 0 };
|
||
LPTSTR szFmt;
|
||
PROPGRIDFONTITEM pgfi;
|
||
TEXTMETRIC tm;
|
||
HFONT hf, hfOld;
|
||
LONG PointSize = 0;
|
||
|
||
LogFontItem_FromString(&pgfi, pItem->lpszCurValue);
|
||
PointSize = -MulDiv(pgfi.logFont.lfHeight, 72,
|
||
GetDeviceCaps(lpDIS->hDC, LOGPIXELSY));
|
||
|
||
if (0 < PointSize)
|
||
{
|
||
if (GetTextMetrics(lpDIS->hDC, &tm))
|
||
pgfi.logFont.lfHeight = tm.tmHeight + 2; //So displayed text is not oversized
|
||
|
||
hf = CreateFontIndirect(&pgfi.logFont);
|
||
hfOld = (HFONT) SelectObject(lpDIS->hDC, hf);
|
||
|
||
SetTextColor(lpDIS->hDC, pgfi.crFont);
|
||
#ifdef _UNICODE
|
||
szFmt = _T("%ls %d");
|
||
#else
|
||
szFmt = _T("%s %d");
|
||
#endif
|
||
_stprintf(buf, MAX_PATH, szFmt, pgfi.logFont.lfFaceName, PointSize);
|
||
DrawText(lpDIS->hDC, buf, _tcslen(buf),
|
||
MAKE_PRECT(rectPart3.left + 3, rectPart3.top + 3,
|
||
rectPart3.right + 3, rectPart3.bottom + 3),
|
||
DT_NOCLIP | DT_LEFT | DT_SINGLELINE);
|
||
|
||
DeleteObject(SelectObject(lpDIS->hDC, hfOld));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SetTextColor(lpDIS->hDC, GetSysColor(COLOR_WINDOWTEXT));
|
||
DrawText(lpDIS->hDC, pItem->lpszCurValue, _tcslen(pItem->lpszCurValue),
|
||
MAKE_PRECT(rectPart3.left + 3, rectPart3.top + 3,
|
||
rectPart3.right + 3, rectPart3.bottom + 3),
|
||
DT_NOCLIP | DT_LEFT | DT_SINGLELINE);
|
||
}
|
||
}
|
||
DrawBorder(lpDIS->hDC, &rectPart3, BF_TOP, GetSysColor(COLOR_BTNFACE));
|
||
}
|
||
|
||
/// @brief Handles WM_SHOWWINDOW message.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param fShow Show/hide flag TRUE for show FALSE for hide.
|
||
/// @param status Status flag.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnShowWindow(HWND hwnd, BOOL fShow, UINT status)
|
||
{
|
||
//DWM 1.3: Added this handler
|
||
if(!fShow) //Hiding so make sure we update the fields
|
||
{
|
||
FORWARD_WM_CHAR(g_lpInst->hwndCtl1, VK_RETURN, 0, SNDMSG);
|
||
FORWARD_WM_CHAR(g_lpInst->hwndCtl2, VK_RETURN, 0, SNDMSG);
|
||
}
|
||
FORWARD_WM_SHOWWINDOW(hwnd, fShow, status, DefWindowProc);
|
||
}
|
||
|
||
#pragma endregion grid message handlers
|
||
|
||
#pragma region public interface handlers
|
||
|
||
/// @brief Handles LB_ADDSTRING message sent to the grid.
|
||
///
|
||
/// @param pgi A pointer to a PROPGRIDITEM object.
|
||
///
|
||
/// @returns INT The index of the item added to the grid.
|
||
static INT Grid_OnAddString(LPPROPGRIDITEM pgi)
|
||
{
|
||
LPLISTBOXITEM lpi;
|
||
TCHAR buf[MAX_PATH];
|
||
_tmemset(buf, (TCHAR)0, MAX_PATH);
|
||
|
||
LPTSTR lpszCurValue = _T("");
|
||
|
||
//Make sure the catalog for this item has been inserted
|
||
INT idx = ListBox_FindCatalog(g_lpInst->hwndListBox, 0, pgi->lpszCatalog);
|
||
|
||
if (LB_ERR == idx) // Must add catalog to the listBox
|
||
{
|
||
lpi = NewItem(pgi->lpszCatalog,_T(""), _T(""),
|
||
_T(""), _T(""), PIT_CATALOG);
|
||
lpi->fCollapsed = TRUE;
|
||
ListBox_AddItemData(g_lpInst->hwndListBox, lpi);
|
||
}
|
||
switch (pgi->iItemType)
|
||
{
|
||
case PIT_CATALOG: //We explicitly added a catalog item to the listbox
|
||
return -2; // catalogs are not added to the list map so skip the rest
|
||
case PIT_EDIT:
|
||
case PIT_STATIC:
|
||
case PIT_COMBO:
|
||
case PIT_EDITCOMBO:
|
||
case PIT_FOLDER:
|
||
lpszCurValue = (LPTSTR)pgi->lpCurValue;
|
||
break;
|
||
case PIT_COLOR:
|
||
_stprintf(buf, MAX_PATH, _T("%d,%d,%d"),
|
||
GetRValue((COLORREF)pgi->lpCurValue),
|
||
GetGValue((COLORREF)pgi->lpCurValue),
|
||
GetBValue((COLORREF)pgi->lpCurValue));
|
||
lpszCurValue = buf;
|
||
break;
|
||
case PIT_FONT:
|
||
lpszCurValue = LogFontItem_ToString((LPPROPGRIDFONTITEM)pgi->lpCurValue);
|
||
break;
|
||
case PIT_CHECK:
|
||
lpszCurValue = (BOOL)pgi->lpCurValue ? CHECKED : UNCHECKED;
|
||
break;
|
||
case PIT_FILE:
|
||
pgi->lpszzCmbItems = FileDialogItem_ToString((LPPROPGRIDFDITEM)pgi->lpCurValue);
|
||
lpszCurValue = ((LPPROPGRIDFDITEM)pgi->lpCurValue)->lpszFilePath;
|
||
break;
|
||
case PIT_IP:
|
||
_stprintf(buf, MAX_PATH,
|
||
_T("%d.%d.%d.%d"),
|
||
FIRST_IPADDRESS((DWORD)pgi->lpCurValue),
|
||
SECOND_IPADDRESS((DWORD)pgi->lpCurValue),
|
||
THIRD_IPADDRESS((DWORD)pgi->lpCurValue),
|
||
FOURTH_IPADDRESS((DWORD)pgi->lpCurValue));
|
||
lpszCurValue = buf;
|
||
break;
|
||
case PIT_DATE:
|
||
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE,
|
||
(LPSYSTEMTIME)pgi->lpCurValue, NULL, buf, MAX_PATH);
|
||
lpszCurValue = buf;
|
||
break;
|
||
case PIT_TIME:
|
||
GetTimeFormat(LOCALE_USER_DEFAULT, 0, (LPSYSTEMTIME)pgi->lpCurValue,
|
||
_T("hh':'mm':'ss tt"), buf, MAX_PATH);
|
||
lpszCurValue = buf;
|
||
break;
|
||
case PIT_DATETIME:
|
||
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE,
|
||
(LPSYSTEMTIME)pgi->lpCurValue, NULL, buf, MAX_PATH);
|
||
_tcscat(buf, _T(" "));
|
||
GetTimeFormat(LOCALE_USER_DEFAULT, 0, (LPSYSTEMTIME)pgi->lpCurValue,
|
||
_T("hh':'mm':'ss tt"), (LPTSTR) (buf + _tcslen(buf)),
|
||
MAX_PATH - _tcslen(buf));
|
||
lpszCurValue = buf;
|
||
break;
|
||
}
|
||
lpi = NewItem(pgi->lpszCatalog, pgi->lpszPropName,
|
||
lpszCurValue, pgi->lpszzCmbItems, pgi->lpszPropDesc, pgi->iItemType);
|
||
|
||
return ListBox_AddItemData(g_lpInst->hwndListMap, lpi);
|
||
}
|
||
|
||
/// @brief Handles LB_DELETESTRING message sent to the grid.
|
||
///
|
||
/// @param nIndex The index of the item to delete.
|
||
///
|
||
/// @returns INT A count of the items remaining in the grid. The return value is
|
||
/// LB_ERR if the index parameter specifies an index greater than
|
||
/// the number of items in the grid.
|
||
static INT Grid_OnDeleteString(INT nIndex)
|
||
{
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(g_lpInst->hwndListMap, nIndex);
|
||
if (NULL == pItem)
|
||
return LB_ERR;
|
||
|
||
INT iItem = ListBox_FindItemData(g_lpInst->hwndListBox, 0, pItem);
|
||
if (LB_ERR != iItem)
|
||
ListBox_DeleteString(g_lpInst->hwndListBox, iItem);
|
||
|
||
return ListBox_DeleteString(g_lpInst->hwndListMap, nIndex);
|
||
}
|
||
|
||
/// @brief Handles LB_GETCURSEL message sent to the grid.
|
||
///
|
||
/// @returns INT The zero-based index of the selected item. If there is no
|
||
/// selection, the return value is LB_ERR.
|
||
static INT Grid_OnGetCurSel(VOID)
|
||
{
|
||
INT iItem = ListBox_GetCurSel(g_lpInst->hwndListBox);
|
||
if (LB_ERR != iItem)
|
||
{
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(g_lpInst->hwndListBox, iItem);
|
||
if (NULL != pItem)
|
||
{
|
||
if (PIT_CATALOG != pItem->iItemType)
|
||
return ListBox_FindItemData(g_lpInst->hwndListMap, 0, pItem);
|
||
}
|
||
}
|
||
//If we get here we didn't succeed
|
||
return LB_ERR;
|
||
}
|
||
|
||
/// @brief Handles LB_GETITEMDATA message sent to the grid.
|
||
///
|
||
/// @param iItem The zero-based index of the item.
|
||
///
|
||
/// @returns LRESULT In this case a pointer to a PROPGRIDITEM object.
|
||
static LRESULT Grid_OnGetItemData(INT iItem)
|
||
{
|
||
static PROPGRIDITEM pgi;
|
||
static PROPGRIDFONTITEM pgfi;
|
||
static PROPGRIDFDITEM pgfdi;
|
||
static SYSTEMTIME st = {0};
|
||
static TCHAR outbuf[MAX_PATH];
|
||
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(g_lpInst->hwndListMap, iItem);
|
||
if (NULL != pItem)
|
||
{
|
||
if (PIT_CATALOG != pItem->iItemType)
|
||
{
|
||
pgi.lpszCatalog = pItem->lpszCatalog;
|
||
pgi.lpszPropName = pItem->lpszPropName;
|
||
pgi.lpszzCmbItems = pItem->lpszMisc;
|
||
pgi.lpszPropDesc = pItem->lpszPropDesc;
|
||
pgi.iItemType = pItem->iItemType;
|
||
|
||
switch (pgi.iItemType)
|
||
{
|
||
case PIT_EDIT:
|
||
case PIT_COMBO:
|
||
case PIT_EDITCOMBO:
|
||
case PIT_STATIC:
|
||
case PIT_FOLDER:
|
||
pgi.lpCurValue = (LPARAM)pItem->lpszCurValue;
|
||
break;
|
||
case PIT_COLOR:
|
||
pgi.lpCurValue = (LPARAM)GetColor(pItem->lpszCurValue);
|
||
break;
|
||
case PIT_FONT:
|
||
LogFontItem_FromString(&pgfi, pItem->lpszCurValue);
|
||
pgi.lpCurValue = (LPARAM) & pgfi;
|
||
break;
|
||
case PIT_CHECK:
|
||
pgi.lpCurValue = (LPARAM) (0 == _tcsicmp(CHECKED,
|
||
pItem->lpszCurValue));
|
||
break;
|
||
case PIT_FILE:
|
||
FileDialogItem_FromString(&pgfdi, pgi.lpszzCmbItems);
|
||
pgi.lpCurValue = (LPARAM) & pgfdi;
|
||
break;
|
||
case PIT_IP:
|
||
{
|
||
//DWM 1.9: Updated this code to use DWORD array and MAKEIPADDRESS macroe instead of 4 byte array and cast.
|
||
//DWM 1.9: This was necessary to support MSVC _stscanf (sscanf - swscanf) beahavior which expects DWORD arguments.
|
||
DWORD ip[4] = { 0, 0, 0, 0 };
|
||
_stscanf(pItem->lpszCurValue, _T("%hhd.%hhd.%hhd.%hhd"),
|
||
&ip[0], &ip[1], &ip[2], &ip[3]);
|
||
pgi.lpCurValue = (LPARAM) MAKEIPADDRESS(ip[0],ip[1],ip[2],ip[3]);
|
||
}
|
||
break;
|
||
case PIT_DATE:
|
||
memset(&st, 0, sizeof(SYSTEMTIME));
|
||
_stscanf(pItem->lpszCurValue, _T("%hd/%hd/%hd"),
|
||
&st.wMonth, &st.wDay, &st.wYear);
|
||
pgi.lpCurValue = (LPARAM) & st;
|
||
break;
|
||
case PIT_TIME:
|
||
memset(&st, 0, sizeof(SYSTEMTIME));
|
||
_tmemset(outbuf, (TCHAR)0, MAX_PATH);
|
||
_stscanf(pItem->lpszCurValue,
|
||
#ifdef _UNICODE
|
||
_T("%hd:%hd:%hd %2ls"),
|
||
#else
|
||
_T("%hd:%hd:%hd %2s"),
|
||
#endif
|
||
&st.wHour, &st.wMinute, &st.wSecond, (LPTSTR)&outbuf);
|
||
if ((0 == _tcsicmp(_T("PM"), outbuf)) && st.wHour != 12)//DWM 1.6:Added st.wHour != 12
|
||
st.wHour += 12;
|
||
|
||
pgi.lpCurValue = (LPARAM)&st;
|
||
break;
|
||
case PIT_DATETIME:
|
||
memset(&st, 0, sizeof(SYSTEMTIME));
|
||
_tmemset(outbuf, (TCHAR)0, MAX_PATH);
|
||
_stscanf(pItem->lpszCurValue,
|
||
#ifdef _UNICODE
|
||
_T("%hd/%hd/%hd %hd:%hd:%hd %2ls"),
|
||
#else
|
||
_T("%hd/%hd/%hd %hd:%hd:%hd %2s"),
|
||
#endif
|
||
&st.wMonth, &st.wDay, &st.wYear,
|
||
&st.wHour, &st.wMinute, &st.wSecond, (LPTSTR)&outbuf);
|
||
if ((0 == _tcsicmp(_T("PM"), outbuf)) && st.wHour != 12)//DWM 1.6:Added st.wHour != 12
|
||
st.wHour += 12;
|
||
|
||
pgi.lpCurValue = (LPARAM) & st;
|
||
break;
|
||
}
|
||
}
|
||
return (LRESULT) & pgi;
|
||
}
|
||
else
|
||
return LB_ERR;
|
||
}
|
||
|
||
/// @brief Handles LB_GETSEL message sent to the grid.
|
||
///
|
||
/// @param iItem The zero-based index of the item.
|
||
///
|
||
/// @returns BOOL TRUE if the item is selected, FALSE if not, or LB_ERR
|
||
/// if an error occurs.
|
||
static BOOL Grid_OnGetSel(INT iItem)
|
||
{
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(g_lpInst->hwndListMap, iItem);
|
||
if (NULL != pItem)
|
||
{
|
||
if (PIT_CATALOG != pItem->iItemType)
|
||
{
|
||
INT i = ListBox_FindItemData(g_lpInst->hwndListBox, 0, pItem);
|
||
if (LB_ERR != i)
|
||
return ListBox_GetSel(g_lpInst->hwndListBox, i);
|
||
else
|
||
return FALSE; //Collapsed Not selected
|
||
}
|
||
}
|
||
return LB_ERR;
|
||
}
|
||
|
||
/// @brief Handles LB_RESETCONTENT message sent to the grid.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnResetContent(VOID)
|
||
{
|
||
ListBox_ResetContent(g_lpInst->hwndListMap);
|
||
|
||
if (NULL != g_lpInst->hwndCtl1)
|
||
{
|
||
DestroyWindow(g_lpInst->hwndCtl1);
|
||
g_lpInst->hwndCtl1 = NULL;
|
||
}
|
||
if (NULL != g_lpInst->hwndCtl2)
|
||
{
|
||
DestroyWindow(g_lpInst->hwndCtl2);
|
||
g_lpInst->hwndCtl2 = NULL;
|
||
}
|
||
ListBox_ResetContent(g_lpInst->hwndListBox);
|
||
Static_SetText(g_lpInst->hwndPropDesc,_T("")); //DWM 1.2: Clear the property pane
|
||
}
|
||
|
||
/// @brief Handles LB_SETCURSEL message sent to the grid.
|
||
///
|
||
/// @param iItem The zero-based index of the item to select, or <20>1 to clear the selection.
|
||
///
|
||
/// @returns LRESULT If an error occurs, the return value is LB_ERR.
|
||
/// If the index parameter is <20>1, the return value is LB_ERR
|
||
/// even though no error occurred.
|
||
static LRESULT Grid_OnSetCurSel(INT iItem)
|
||
{
|
||
LPLISTBOXITEM pItem = ListBox_GetItemDataSafe(g_lpInst->hwndListMap, iItem);
|
||
if (NULL != pItem)
|
||
{
|
||
if (PIT_CATALOG != pItem->iItemType)
|
||
{
|
||
INT i = ListBox_FindItemData(g_lpInst->hwndListBox, 0, pItem);
|
||
if (LB_ERR != i)
|
||
{
|
||
INT ret = ListBox_SetCurSel(g_lpInst->hwndListBox, i);
|
||
FORWARD_WM_COMMAND(GetParent(g_lpInst->hwndListBox),
|
||
GetDlgCtrlID(g_lpInst->hwndListBox), g_lpInst->hwndListBox,
|
||
LBN_SELCHANGE, SNDMSG);
|
||
|
||
if(PIT_CHECK == pItem->iItemType)
|
||
pItem->lpszMisc = SELECT; //DWM 1.2: Ensure Checkbox selected
|
||
|
||
Refresh(g_lpInst->hwndListBox);
|
||
return ret;
|
||
}
|
||
}
|
||
}
|
||
return LB_ERR;
|
||
}
|
||
|
||
/// @brief Handles LB_ADDSTRING message sent to the grid.
|
||
///
|
||
/// @param iItem The zero-based index of the item.
|
||
/// @param pgi A pointer to a PROPGRIDITEM object.
|
||
///
|
||
/// @returns LRESULT The return value is the zero-based index of the item in
|
||
/// the list. If an error occurs, the return value is LB_ERR.
|
||
/// If there is insufficient space to store the data,
|
||
/// the return value is LB_ERRSPACE.
|
||
static LRESULT Grid_OnSetItemData(INT iItem, LPPROPGRIDITEM pgi)
|
||
{
|
||
if (PIT_CATALOG == pgi->iItemType)
|
||
return LB_ERR; //Can't change an item to a catalog
|
||
|
||
LRESULT lrtn = LB_ERR;
|
||
LPLISTBOXITEM lpiCurrent, lpiNew;
|
||
TCHAR buf[MAX_PATH];
|
||
_tmemset(buf, (TCHAR)0, MAX_PATH);
|
||
|
||
LPTSTR lpszCurValue = _T("");
|
||
|
||
//Get the current item
|
||
lpiCurrent = ListBox_GetItemDataSafe(g_lpInst->hwndListMap, iItem);
|
||
if (NULL != lpiCurrent)
|
||
{
|
||
switch (pgi->iItemType)
|
||
{
|
||
case PIT_EDIT:
|
||
case PIT_STATIC:
|
||
case PIT_COMBO:
|
||
case PIT_EDITCOMBO:
|
||
case PIT_FOLDER:
|
||
lpszCurValue = (LPTSTR)pgi->lpCurValue;
|
||
break;
|
||
case PIT_COLOR:
|
||
_stprintf(buf, MAX_PATH, _T("%d,%d,%d"),
|
||
GetRValue((COLORREF)pgi->lpCurValue),
|
||
GetGValue((COLORREF)pgi->lpCurValue),
|
||
GetBValue((COLORREF)pgi->lpCurValue));
|
||
lpszCurValue = buf;
|
||
break;
|
||
case PIT_FONT:
|
||
lpszCurValue = LogFontItem_ToString((LPPROPGRIDFONTITEM)pgi->lpCurValue);
|
||
break;
|
||
case PIT_CHECK:
|
||
lpszCurValue = (BOOL)pgi->lpCurValue ? CHECKED : UNCHECKED;
|
||
break;
|
||
case PIT_FILE:
|
||
pgi->lpszzCmbItems = FileDialogItem_ToString((LPPROPGRIDFDITEM)pgi->lpCurValue);
|
||
lpszCurValue = ((LPPROPGRIDFDITEM)pgi->lpCurValue)->lpszFilePath;
|
||
break;
|
||
case PIT_IP:
|
||
_stprintf(buf, MAX_PATH, _T("%d.%d.%d.%d"),
|
||
FIRST_IPADDRESS((DWORD)pgi->lpCurValue),
|
||
SECOND_IPADDRESS((DWORD)pgi->lpCurValue),
|
||
THIRD_IPADDRESS((DWORD)pgi->lpCurValue),
|
||
FOURTH_IPADDRESS((DWORD)pgi->lpCurValue));
|
||
lpszCurValue = buf;
|
||
break;
|
||
case PIT_DATE:
|
||
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE,
|
||
(LPSYSTEMTIME)pgi->lpCurValue, NULL, buf, MAX_PATH);
|
||
lpszCurValue = buf;
|
||
break;
|
||
case PIT_TIME:
|
||
GetTimeFormat(LOCALE_USER_DEFAULT, 0,
|
||
(LPSYSTEMTIME)pgi->lpCurValue, _T("hh':'mm':'ss tt"),
|
||
buf, MAX_PATH);
|
||
lpszCurValue = buf;
|
||
break;
|
||
case PIT_DATETIME:
|
||
GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE,
|
||
(LPSYSTEMTIME)pgi->lpCurValue, NULL, buf, MAX_PATH);
|
||
_tcscat(buf, _T(" "));
|
||
GetTimeFormat(LOCALE_USER_DEFAULT, 0, (LPSYSTEMTIME)pgi->lpCurValue,
|
||
_T("hh':'mm':'ss tt"), (LPTSTR) (buf + _tcslen(buf)),
|
||
MAX_PATH - _tcslen(buf));
|
||
lpszCurValue = buf;
|
||
break;
|
||
}
|
||
//Do not allow the catalog to change; to do so would necessitate moving
|
||
// the item to a different index throwing off the indexing for items.
|
||
lpiNew = NewItem(lpiCurrent->lpszCatalog, pgi->lpszPropName,
|
||
lpszCurValue, pgi->lpszzCmbItems, pgi->lpszPropDesc, pgi->iItemType);
|
||
|
||
//Set new data
|
||
lrtn = ListBox_SetItemData(g_lpInst->hwndListMap, iItem, lpiNew);
|
||
if(LB_ERR != lrtn)
|
||
{
|
||
lrtn = ListBox_FindItemData(g_lpInst->hwndListBox,0,lpiCurrent);
|
||
if(LB_ERR != lrtn)
|
||
{
|
||
lrtn = ListBox_SetItemData(g_lpInst->hwndListBox, lrtn, lpiNew);
|
||
|
||
//DWM 1.3: Refresh Display of item
|
||
Refresh(g_lpInst->hwndListBox);
|
||
}
|
||
}
|
||
if(LB_ERR == lrtn)
|
||
return LB_ERR;
|
||
|
||
//Reset lpCurrent if necessary
|
||
if(lpiCurrent == g_lpInst->lpCurrent)
|
||
g_lpInst->lpCurrent = lpiNew;
|
||
|
||
//Delete old data
|
||
DeleteItem(lpiCurrent);
|
||
}
|
||
return lrtn;
|
||
}
|
||
|
||
#pragma endregion public interface handlers
|
||
|
||
#pragma region create destroy
|
||
|
||
/// @brief Handles WM_CREATE message.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param lpCreateStruct Pointer to a structure with creation data.
|
||
///
|
||
/// @returns BOOL If an application processes this message,
|
||
/// it should return TRUE to continue creation of the window.
|
||
static BOOL Grid_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
|
||
{
|
||
INSTANCEDATA inst;
|
||
memset(&inst, 0, sizeof(INSTANCEDATA));
|
||
|
||
inst.hInstance = lpCreateStruct->hInstance;
|
||
inst.hwndParent = lpCreateStruct->hwndParent;
|
||
inst.fXpOrLower = IsWindowsXPorLower(); // DWM 1.4: Added
|
||
inst.fGotFocus = FALSE;
|
||
inst.fScrolling = FALSE;
|
||
inst.fTracking = FALSE;
|
||
inst.iDescHeight = HEIGHT_DESC;
|
||
inst.iPrevSel = LB_ERR; //Nothing selected
|
||
|
||
//Create the listbox control
|
||
inst.hwndListBox = CreateListBox(lpCreateStruct->hInstance, hwnd, ID_LISTBOX);
|
||
if (NULL == inst.hwndListBox)
|
||
return FALSE;
|
||
|
||
//And the hidden list map
|
||
inst.hwndListMap = CreateListMap(lpCreateStruct->hInstance, hwnd, ID_LISTMAP);
|
||
if (NULL == inst.hwndListMap)
|
||
return FALSE;
|
||
|
||
return Control_CreateInstanceData(hwnd, &inst);
|
||
}
|
||
|
||
/// @brief Handles WM_DESTROY message.
|
||
///
|
||
/// @param hwnd Handle of Grid.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_OnDestroy(HWND hwnd)
|
||
{
|
||
if (NULL != g_lpInst)
|
||
{
|
||
ListBox_ResetContent(g_lpInst->hwndListMap); //Delete all Items
|
||
DestroyWindow(g_lpInst->hwndListMap);
|
||
DestroyWindow(g_lpInst->hwndListBox);
|
||
|
||
if (NULL != g_lpInst->hwndToolTip)
|
||
DestroyWindow(g_lpInst->hwndToolTip);
|
||
if (NULL != g_lpInst->hwndPropDesc)
|
||
DestroyWindow(g_lpInst->hwndPropDesc);
|
||
if (NULL != g_lpInst->hwndCtl1)
|
||
DestroyWindow(g_lpInst->hwndCtl1);
|
||
if (NULL != g_lpInst->hwndCtl2)
|
||
DestroyWindow(g_lpInst->hwndCtl2);
|
||
|
||
Control_FreeInstanceData(hwnd);
|
||
|
||
//DWM 1.3: Removed PostQuitMessage() call.
|
||
}
|
||
}
|
||
|
||
/// @brief Initialize and register the property grid class.
|
||
///
|
||
/// @param hInstance Handle of application instance.
|
||
///
|
||
/// @returns ATOM If the function succeeds, the atom that uniquely identifies
|
||
/// the class being registered, otherwise 0.
|
||
ATOM InitPropertyGrid(HINSTANCE hInstance)
|
||
{
|
||
WNDCLASSEX wcex;
|
||
|
||
// Get standard listbox information
|
||
wcex.cbSize = sizeof(wcex);
|
||
if (!GetClassInfoEx(NULL, WC_LISTBOX, &wcex))
|
||
return 0;
|
||
|
||
// Add our own stuff
|
||
wcex.lpfnWndProc = (WNDPROC)Grid_Proc;;
|
||
wcex.hInstance = hInstance;
|
||
wcex.lpszClassName = g_szClassName;
|
||
|
||
// Register our new class
|
||
return RegisterClassEx(&wcex);
|
||
}
|
||
|
||
/// @brief Create an new instance of the property grid.
|
||
///
|
||
/// @param hParent Handle of the grid's parent.
|
||
/// @param dwID The ID for this control.
|
||
///
|
||
/// @returns HWND If the function succeeds, the grid handle, otherwise NULL.
|
||
HWND New_PropertyGrid(HWND hParent, DWORD dwID)
|
||
{
|
||
static ATOM aPropertyGrid = 0;
|
||
static HWND hPropertyGrid;
|
||
|
||
HINSTANCE hinst = (HINSTANCE)GetWindowLongPtr(hParent, GWLP_HINSTANCE);
|
||
|
||
//Only need to register the property grid once
|
||
if (!aPropertyGrid)
|
||
aPropertyGrid = InitPropertyGrid(hinst);
|
||
|
||
hPropertyGrid = CreateWindowEx(0, g_szClassName, _T(""),
|
||
WS_CHILD | WS_TABSTOP, 0, 0, 0, 0, hParent, (HMENU)dwID, hinst, NULL);
|
||
|
||
return hPropertyGrid;
|
||
}
|
||
|
||
#pragma endregion create destroy
|
||
|
||
/// @brief Send a PGN_PROPERTYCHANGE notification to grid's parent.
|
||
///
|
||
/// @returns VOID.
|
||
static VOID Grid_NotifyParent(VOID)
|
||
{
|
||
static NMPROPGRID nmPropGrid;
|
||
|
||
// Notify of the change
|
||
if (NULL != g_lpInst->lpCurrent &&
|
||
PIT_CATALOG != g_lpInst->lpCurrent->iItemType &&
|
||
PIT_STATIC != g_lpInst->lpCurrent->iItemType)
|
||
{
|
||
nmPropGrid.hdr.hwndFrom = GetParent(g_lpInst->hwndListBox);
|
||
nmPropGrid.hdr.idFrom = GetDlgCtrlID(nmPropGrid.hdr.hwndFrom);
|
||
nmPropGrid.hdr.code = PGN_PROPERTYCHANGE;
|
||
|
||
LRESULT lres = ListBox_FindItemData(g_lpInst->hwndListMap, 0, g_lpInst->lpCurrent);
|
||
nmPropGrid.iIndex = LB_ERR == lres ? -1 : lres;
|
||
|
||
FORWARD_WM_NOTIFY(g_lpInst->hwndParent, nmPropGrid.hdr.idFrom, &nmPropGrid, SNDMSG);
|
||
}
|
||
}
|
||
|
||
/// @brief Window procedure for the visible owner-drawn listbox control.
|
||
///
|
||
/// @param hList Handle of listbox.
|
||
/// @param msg Which message?
|
||
/// @param wParam Message parameter.
|
||
/// @param lParam Message parameter.
|
||
///
|
||
/// @returns LRESULT depends on message.
|
||
static LRESULT CALLBACK ListBox_Proc(HWND hList, UINT msg,
|
||
WPARAM wParam, LPARAM lParam)
|
||
{
|
||
HWND hParent = GetParent(hList);
|
||
|
||
// Note: Instance data is attached to listbox's parent
|
||
Control_GetInstanceData(hParent, &g_lpInst);
|
||
|
||
switch (msg)
|
||
{
|
||
HANDLE_MSG(hList, WM_COMMAND, ListBox_OnCommand);
|
||
HANDLE_MSG(hList, WM_LBUTTONDOWN, ListBox_OnLButtonDown);
|
||
HANDLE_MSG(hList, WM_LBUTTONDBLCLK, ListBox_OnLButtonDown);
|
||
HANDLE_MSG(hList, WM_LBUTTONUP, ListBox_OnLButtonUp);
|
||
HANDLE_MSG(hList, WM_MOUSEMOVE, ListBox_OnMouseMove);
|
||
HANDLE_MSG(hList, WM_KEYDOWN, ListBox_OnKeyDown);
|
||
HANDLE_MSG(hList, WM_KILLFOCUS, ListBox_OnKillFocus);
|
||
|
||
case WM_SETFOCUS: //DWM 1.3: Focus to ListBox
|
||
g_lpInst->fGotFocus = TRUE;
|
||
Refresh(g_lpInst->hwndListBox);
|
||
return 0;
|
||
|
||
case WM_MBUTTONDOWN:
|
||
case WM_NCLBUTTONDOWN:
|
||
//The listbox doesn't have a scrollbar component, it draws a scroll
|
||
// bar in the non-client area of the control. A mouse click in the
|
||
// non-client area then, equals clicking on a scroll bar. A click
|
||
// on the middle mouse button equals pan, we'll handle that as if
|
||
// it were a scroll event.
|
||
ListBox_OnBeginScroll(hList);
|
||
g_lpInst->fScrolling = TRUE;
|
||
break;
|
||
|
||
case WM_SETCURSOR:
|
||
//Whenever the mouse leaves the non-client area of a listbox, it
|
||
// fires a WM_SETCURSOR message. The same happens when the middle
|
||
// mouse button is released. We can use this behavior to detect the
|
||
// completion of a scrolling operation.
|
||
if (g_lpInst->fScrolling)
|
||
{
|
||
ListBox_OnEndScroll(hList);
|
||
g_lpInst->fScrolling = FALSE;
|
||
}
|
||
break;
|
||
|
||
case WM_GETDLGCODE:
|
||
return DLGC_WANTALLKEYS;
|
||
|
||
case WM_DESTROY: //Unsubclass the listbox Control
|
||
{
|
||
SetWindowLongPtr(hList, GWLP_WNDPROC, (DWORD_PTR)GetProp(hList, WPRC)); //DWM 1.5: fixed cast
|
||
RemoveProp(hList, WPRC);
|
||
return 0;
|
||
}
|
||
}
|
||
return DefProc(hList, msg, wParam, lParam);
|
||
}
|
||
|
||
/// @brief Window procedure and public interface for the property grid.
|
||
///
|
||
/// @param hwnd Handle of grid.
|
||
/// @param msg Which message?
|
||
/// @param wParam Message parameter.
|
||
/// @param lParam Message parameter.
|
||
///
|
||
/// @returns LRESULT depends on message.
|
||
static LRESULT CALLBACK Grid_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
{
|
||
Control_GetInstanceData(hwnd, &g_lpInst);//Update the instance pointer
|
||
|
||
switch (msg)
|
||
{
|
||
HANDLE_MSG(hwnd, WM_COMMAND, Grid_OnCommand);
|
||
HANDLE_MSG(hwnd, WM_CREATE, Grid_OnCreate);
|
||
HANDLE_MSG(hwnd, WM_DESTROY, Grid_OnDestroy);
|
||
HANDLE_MSG(hwnd, WM_SIZE, Grid_OnSize);
|
||
HANDLE_MSG(hwnd, WM_CTLCOLORLISTBOX, Grid_OnCtlColorListbox);
|
||
HANDLE_MSG(hwnd, WM_CTLCOLORSTATIC, Grid_OnCtlColorStatic);
|
||
HANDLE_MSG(hwnd, WM_DRAWITEM, Grid_OnDrawItem);
|
||
HANDLE_MSG(hwnd, WM_MEASUREITEM, Grid_OnMeasureItem);
|
||
HANDLE_MSG(hwnd, WM_DELETEITEM, Grid_OnDeleteItem);
|
||
HANDLE_MSG(hwnd, WM_LBUTTONDOWN, Grid_OnLButtonDown);
|
||
HANDLE_MSG(hwnd, WM_LBUTTONUP, Grid_OnLButtonUp);
|
||
HANDLE_MSG(hwnd, WM_MOUSEMOVE, Grid_OnMouseMove);
|
||
HANDLE_MSG(hwnd, WM_SETCURSOR, Grid_OnSetCursor);
|
||
HANDLE_MSG(hwnd, WM_SHOWWINDOW, Grid_OnShowWindow);
|
||
|
||
case WM_SETFOCUS: //DWM 1.3: Focus to ListBox
|
||
SetFocus(g_lpInst->hwndListBox);
|
||
return 0;
|
||
case LB_ADDSTRING: //PropGrid_AddItem
|
||
return Grid_OnAddString((LPPROPGRIDITEM)lParam);
|
||
case LB_DELETESTRING: //PropGrid_DeleteItem
|
||
return Grid_OnDeleteString((INT)wParam);
|
||
case LB_GETCOUNT: //PropGrid_GetCount
|
||
return ListBox_GetCount(g_lpInst->hwndListMap);
|
||
case LB_GETCURSEL: //PropGrid_GetCurSel
|
||
return Grid_OnGetCurSel();
|
||
case LB_GETHORIZONTALEXTENT: //PropGrid_GetHorizontalExtent
|
||
return ListBox_GetHorizontalExtent(g_lpInst->hwndListBox);
|
||
case LB_GETITEMDATA: //PropGrid_GetItemData
|
||
return Grid_OnGetItemData((INT)wParam);
|
||
case LB_GETITEMHEIGHT: //PropGrid_GetItemHeight
|
||
return ListBox_GetItemHeight(g_lpInst->hwndListBox, wParam);
|
||
case LB_GETITEMRECT: //PropGrid_GetItemRect
|
||
return ListBox_GetItemRect(g_lpInst->hwndListBox, wParam, lParam);
|
||
case LB_GETSEL: //PropGrid_GetSel
|
||
return Grid_OnGetSel((INT)wParam);
|
||
case LB_RESETCONTENT: //PropGrid_ResetContent
|
||
Grid_OnResetContent();
|
||
break;
|
||
case LB_SETCURSEL: //PropGrid_SetCurSel
|
||
return Grid_OnSetCurSel((INT)wParam);
|
||
case LB_SETHORIZONTALEXTENT: //PropGrid_SetHorizontalExtent
|
||
ListBox_SetHorizontalExtent(g_lpInst->hwndListBox, wParam);
|
||
break;
|
||
case LB_SETITEMDATA: //PropGrid_SetItemData
|
||
return Grid_OnSetItemData((INT)wParam, (LPPROPGRIDITEM)lParam);
|
||
case LB_SETITEMHEIGHT: //PropGrid_SetItemHeight
|
||
{
|
||
if (MINIMUM_ITEM_HEIGHT > LOWORD(lParam))
|
||
return LB_ERR;
|
||
LRESULT lres = SNDMSG(g_lpInst->hwndListBox, msg, wParam, lParam);
|
||
Refresh(g_lpInst->hwndListBox);
|
||
return lres;
|
||
}
|
||
case PG_FLATCHECKS: //DWM 1.8: Added
|
||
{
|
||
DWORD dwUserData = (DWORD)GetWindowLongPtr(g_lpInst->hwndListBox, GWLP_USERDATA);
|
||
if (FALSE != (BOOL)wParam)
|
||
dwUserData |= FLATCHECKS;
|
||
else
|
||
dwUserData &= ~FLATCHECKS;
|
||
|
||
return SetWindowLongPtr(g_lpInst->hwndListBox, GWLP_USERDATA,
|
||
(LONG_PTR)dwUserData);
|
||
}
|
||
case PG_EXPANDCATALOGS:
|
||
{
|
||
if (NULL == (LPTSTR)lParam) //Expand all
|
||
{
|
||
Grid_ExpandCatalog(NULL);
|
||
}
|
||
else
|
||
{
|
||
//Walk the catalog list and handle each string until the empty string
|
||
for (LPTSTR p = (LPTSTR)lParam; *p; p += _tcslen(p) + 1)
|
||
Grid_ExpandCatalog((LPCTSTR)p);
|
||
}
|
||
}
|
||
break;
|
||
case PG_COLLAPSECATALOGS:
|
||
{
|
||
if (NULL == (LPTSTR)lParam) //Collapse all
|
||
{
|
||
Grid_CollapseCatalog(NULL);
|
||
}
|
||
else
|
||
{
|
||
for (LPTSTR p = (LPTSTR)lParam; *p; p += _tcslen(p) + 1)
|
||
Grid_CollapseCatalog((LPCTSTR)p);
|
||
}
|
||
}
|
||
break;
|
||
case PG_SHOWTOOLTIPS:
|
||
{
|
||
if ((BOOL)wParam)
|
||
{
|
||
if (NULL == g_lpInst->hwndToolTip)
|
||
{
|
||
g_lpInst->hwndToolTip = CreateToolTip(g_lpInst->hInstance, g_lpInst->hwndListBox);
|
||
|
||
if (NULL != g_lpInst->hwndToolTip)
|
||
{
|
||
TOOLINFO ti = { sizeof(ti) };
|
||
ti.hwnd = g_lpInst->hwndListBox;
|
||
ti.uId = 0;
|
||
GetClientRect(hwnd, &ti.rect);
|
||
ToolTip_NewToolRect(g_lpInst->hwndToolTip, &ti);
|
||
|
||
ShowWindow(g_lpInst->hwndToolTip, SW_SHOW);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DestroyWindow(g_lpInst->hwndToolTip);
|
||
g_lpInst->hwndToolTip = NULL;
|
||
}
|
||
}
|
||
break;
|
||
case PG_SHOWPROPERTYDESC:
|
||
{
|
||
if ((BOOL)wParam)
|
||
{
|
||
if (NULL == g_lpInst->hwndPropDesc)
|
||
g_lpInst->hwndPropDesc = CreateStatic(g_lpInst->hInstance, hwnd, ID_PROPDESC);
|
||
|
||
ShowWindow(g_lpInst->hwndPropDesc, SW_SHOW);
|
||
}
|
||
else
|
||
{
|
||
DestroyWindow(g_lpInst->hwndPropDesc);
|
||
g_lpInst->hwndPropDesc = NULL;
|
||
}
|
||
//Call Grid_OnSize() to position and display the control
|
||
RECT rc;
|
||
GetClientRect(hwnd, &rc);
|
||
Grid_OnSize(hwnd, 0, WIDTH(rc), HEIGHT(rc));
|
||
}
|
||
break;
|
||
}
|
||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||
}
|