diff --git a/ext/PropertyGrid b/ext/PropertyGrid deleted file mode 160000 index 7f40af3..0000000 --- a/ext/PropertyGrid +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7f40af32bc7f121078d3c2b90a2656146d65cb3a diff --git a/ext/PropertyGrid/.gitignore b/ext/PropertyGrid/.gitignore new file mode 100644 index 0000000..b30f005 --- /dev/null +++ b/ext/PropertyGrid/.gitignore @@ -0,0 +1,2 @@ +build*/ +*.user diff --git a/ext/PropertyGrid/CMakeLists.txt b/ext/PropertyGrid/CMakeLists.txt new file mode 100644 index 0000000..72ddab4 --- /dev/null +++ b/ext/PropertyGrid/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.5) + +project(PropertyGrid LANGUAGES CXX) + +set(CMAKE_MFC_FLAG 2) +add_compile_definitions(_AFXDLL) + +option(PROPERTYGRID_BUILD_APP "Build with test application" ON) + +add_library(PropertyGrid STATIC + CustomItem.h + DynDialogEx.cpp + DynDialogEx.h + DynDialogItemEx.cpp + DynDialogItemEx.h + ListDynDialogEx.cpp + ListDynDialogEx.h + PropertyGrid.cpp + PropertyGrid.h + PropertyGridCombo.cpp + PropertyGridCombo.h + PropertyGridDirectoryPicker.cpp + PropertyGridDirectoryPicker.h + PropertyGridInPlaceEdit.cpp + PropertyGridInPlaceEdit.h + PropertyGridMonthCalCtrl.cpp + PropertyGridMonthCalCtrl.h + stdafx.cpp + stdafx.h +) + +target_link_libraries(PropertyGrid PRIVATE shlwapi.lib) + +if (PROPERTYGRID_BUILD_APP) + add_subdirectory(test) +endif() diff --git a/ext/PropertyGrid/CustomItem.h b/ext/PropertyGrid/CustomItem.h new file mode 100644 index 0000000..9f74f7c --- /dev/null +++ b/ext/PropertyGrid/CustomItem.h @@ -0,0 +1,32 @@ +#pragma once +#include "PropertyGrid.h" + +class ICustomItem +{ +public: + // basic required stuff + virtual CPropertyGrid::EEditMode GetEditMode() = 0; + virtual void DrawItem(CDC& dc, CRect rc, bool focused) = 0; + + // validation + virtual void ValidateChanges() {} + + // mouse stuff + virtual bool OnLButtonDown(CRect rc, CPoint pt) { return false; } + virtual void OnMouseMove(CRect rc, CPoint pt) {} + virtual void OnLButtonUp(CRect rc, CPoint pt) {} + + // in-place edit + virtual string GetStringForInPlaceEdit() { return ""; } + virtual bool OnItemEdited(string strNewValue) { return false; } + + // dropdown edit + virtual void ShowDropDown(CRect rc) {} + + // modal edit + virtual bool OnEditItem() { return false; } + +protected: + friend class CPropertyGrid; + CPropertyGrid* m_pGrid; +}; diff --git a/ext/PropertyGrid/DynDialogEx.cpp b/ext/PropertyGrid/DynDialogEx.cpp new file mode 100644 index 0000000..49faba6 --- /dev/null +++ b/ext/PropertyGrid/DynDialogEx.cpp @@ -0,0 +1,610 @@ +// DynDialogItemEx.cpp: implementation of the CDynDialogItemEx class. +// +// Written by Marcel Scherpenisse +// mailto:Marcel_Scherpenisse@insad.nl +// +// This code may be used in compiled form in any way you desire. This +// file may be redistributed unmodified by any means PROVIDING it is +// not sold for profit without the authors written consent, and +// providing that this notice and the authors name and all copyright +// notices remains intact. If the source code in this file is used in +// any commercial application then a statement along the lines of +// "Portions copyright (c) Marcel Scherpenisse, 2002" must be included in +// the startup banner, "About" box or printed documentation. An email +// letting me know that you are using it would be nice as well. +// +// This file is provided "as is" with no expressed or implied warranty. +// The author accepts no liability for any damage/loss of business that +// this product may cause. +// +// Expect bugs! +////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" +#include "DynDialogEx.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CDynDialogEx dialog + + +CDynDialogEx::CDynDialogEx(CWnd* pParent /*=NULL*/) +: CDialog() +{ + //{{AFX_DATA_INIT(CDynDialogEx) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT + + m_DialogTemplate.style = WS_CAPTION | WS_SYSMENU | WS_VISIBLE | DS_SETFONT; + m_DialogTemplate.dwExtendedStyle = WS_EX_DLGMODALFRAME; + m_DialogTemplate.x = 0; + m_DialogTemplate.y = 0; + m_DialogTemplate.cx = 0; // 4 horizontal units are the width of one character + m_DialogTemplate.cy = 0; // 8 vertical units are the height of one character + m_DialogTemplate.cdit = 0; // nr of dialog items in the dialog + + m_pParentWnd = pParent; + m_strCaption = _T(""); + m_pFont = NULL; + m_wFontSize = 0; + m_nCurRow = FIRSTROW1; + m_bAddSystemButtons = TRUE; + + m_bIsFontCreated = FALSE; + m_bModelessDlg = FALSE; +} + +CDynDialogEx::~CDynDialogEx() +{ + CDynDialogItemEx *pDynDialogItemEx = NULL; + for (INT_PTR i = m_arrDlgItemPtr.size() - 1; i >= 0; i--) { + pDynDialogItemEx = m_arrDlgItemPtr[i]; + if (pDynDialogItemEx != NULL) { + delete pDynDialogItemEx; + pDynDialogItemEx = NULL; + } + } + + if (m_bIsFontCreated) { + delete m_pFont; + } +} + +CWnd *CDynDialogEx::GetParent() +{ + return m_pParentWnd; +} + +void CDynDialogEx::AddStyles(DWORD dwStyles) +{ + m_DialogTemplate.style |= dwStyles; +} + +void CDynDialogEx::RemoveStyles(DWORD dwStyles) +{ + m_DialogTemplate.style &= (~dwStyles); +} + +void CDynDialogEx::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CDynDialogEx) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP + + CDynDialogItemEx *pDynDialogItemEx = NULL; + for (INT_PTR i = m_arrDlgItemPtr.size() - 1; i >= 0; i--) { + pDynDialogItemEx = m_arrDlgItemPtr[i]; + if (pDynDialogItemEx != NULL) { + pDynDialogItemEx->DoDataExchange(pDX); + } + } +} + +BEGIN_MESSAGE_MAP(CDynDialogEx, CDialog) + //{{AFX_MSG_MAP(CDynDialogEx) + ON_WM_CREATE() + //}}AFX_MSG_MAP + ON_MESSAGE(WM_HELP, OnHelpMsg) + ON_WM_MEASUREITEM() // TMB! 06-12-2001 Adjusted.. was wrongly an ON_MESSAGE()!! + ON_WM_DRAWITEM() // TMB! 06-12-2001 Adjusted.. was wrongly an ON_MESSAGE()!! +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CDynDialogEx message handlers + +int CDynDialogEx::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CDialog::OnCreate(lpCreateStruct) == -1) + return -1; + + //if we have no font, create one here + if (m_pFont == NULL) { + LOGFONT LogFont; + + // Can do better??? + memset(&LogFont, 0x00, sizeof(LogFont)); + strncpy(LogFont.lfFaceName, _T("MS Sans Serif"), LF_FACESIZE); + LogFont.lfHeight = 8; + + m_pFont = new CFont; + m_pFont->CreateFontIndirect(&LogFont); + SetFont(m_pFont); + m_bIsFontCreated = TRUE; + } + + //Create all the controls on the dialog + CDynDialogItemEx *pDynDialogItemEx = NULL; + for (int i = 0; i < m_arrDlgItemPtr.size(); i++) { + pDynDialogItemEx = m_arrDlgItemPtr[i]; + if (pDynDialogItemEx != NULL) { + if (!pDynDialogItemEx->IsDlgItemSubclassed()) { + if (!pDynDialogItemEx->CreateEx(this)) { + AfxMessageBox(_T("Failed to create DlgItem.")); + } + else if (pDynDialogItemEx->GetSafeHwnd() != NULL) { + pDynDialogItemEx->SetFont(m_pFont, FALSE); + } + } + } + } + + return 0; +} + +BOOL CDynDialogEx::OnInitDialog() +{ + //Reposition all the controls on the dialog... + CDynDialogItemEx *pDynDialogItemEx = NULL; + for (int i = 0; i < m_arrDlgItemPtr.size(); i++) { + pDynDialogItemEx = m_arrDlgItemPtr[i]; + if (pDynDialogItemEx != NULL) { + if (!pDynDialogItemEx->IsDlgItemSubclassed() && !pDynDialogItemEx->SetWindowPos(this)) { + CString strMessage; + strMessage.Format(_T("Failed SetWindowPos for control %s."), pDynDialogItemEx->GetClassName()); + AfxMessageBox(strMessage); + } + } + } + + CDialog::OnInitDialog(); + + CenterWindow(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +int CDynDialogEx::DoModal() +{ + //Do we need OK and Cancel buttons?? + if (m_bAddSystemButtons) { + AddSystemButtons(); + } + + // + // Get font info from mainwindow of the application + // + CFont* pParentFont = m_pFont; + if (pParentFont == NULL && m_pParentWnd != NULL) { + pParentFont = m_pParentWnd->GetFont(); + } + if (pParentFont == NULL && AfxGetApp()->m_pActiveWnd != NULL) { + pParentFont = AfxGetApp()->m_pActiveWnd->GetFont(); + } + LOGFONT LogFont; + memset(&LogFont, 0x00, sizeof(LogFont)); + if (pParentFont != NULL) { + pParentFont->GetLogFont(&LogFont); + } + else { + // Can do better??? + strncpy(LogFont.lfFaceName, _T("MS Sans Serif"), LF_FACESIZE); + LogFont.lfHeight = 8; + } + + //Prework for setting font in dialog... + int cWC = MultiByteToWideChar(CP_ACP, 0, LogFont.lfFaceName, -1, NULL, 0); + int nFontNameLen = cWC + 1; + WCHAR *szFontName = new WCHAR[nFontNameLen]; + // Copy the string + MultiByteToWideChar(CP_ACP, 0, LogFont.lfFaceName, -1, (LPWSTR) szFontName, cWC); + szFontName[cWC] = 0; + nFontNameLen = (cWC) * sizeof(WCHAR); + + if (m_wFontSize == 0) { + m_wFontSize = (unsigned short)LogFont.lfHeight; + } + + //Prework for setting caption in dialog... + cWC = MultiByteToWideChar(CP_ACP, 0, m_strCaption, -1, NULL, 0); + int szBoxLen = cWC + 1; + WCHAR *szBoxCaption = new WCHAR[szBoxLen]; + // Copy the string + MultiByteToWideChar(CP_ACP, 0, m_strCaption, -1, (LPWSTR) szBoxCaption, cWC); + szBoxCaption[cWC] = 0; + szBoxLen = (cWC) * sizeof(WCHAR); + + INT_PTR iRet = -1; + //Here 's the stuff to build the dialog template in memory + //without the controls being in the template + //(Our first try, was this same template with some additional code + //for each control placed on it, that's why this class is cold Ex :) + //This gave some problems on WIN9x systems, where EDIT boxes + //were not shown with 3D-look, but as flat controls) + int nBufferSize = sizeof(DLGTEMPLATE) + (2 * sizeof(WORD)) /*menu and class*/ + szBoxLen /*size of caption*/ + + sizeof(WORD) /*fontsize*/ + nFontNameLen /*size of fontname*/; + + //Are there any subclassed controls... + if (m_DialogTemplate.cdit > 0) { + nBufferSize = (nBufferSize + 3) & ~3; // adjust size to make first control DWORD aligned + + CDynDialogItemEx *pDynDialogItemEx = NULL; + for (int i = 0; i < m_arrDlgItemPtr.size(); i++) { + pDynDialogItemEx = m_arrDlgItemPtr[i]; + if (pDynDialogItemEx != NULL) { + if (pDynDialogItemEx->IsDlgItemSubclassed()) { + int nItemLength = sizeof(DLGITEMTEMPLATE) + 3 * sizeof(WORD); + nItemLength += (pDynDialogItemEx->GetCaptionLength() + 1) * sizeof(WCHAR); + + if (i != m_DialogTemplate.cdit - 1) { // the last control does not need extra bytes + nItemLength = (nItemLength + 3) & ~3; // take into account gap so next control is DWORD aligned + } + nBufferSize += nItemLength; + } + } + } + } + + HLOCAL hLocal = LocalAlloc(LHND, nBufferSize); + if (hLocal != NULL) { + BYTE* pBuffer = (BYTE*)LocalLock(hLocal); + if (pBuffer == NULL) { + LocalFree(hLocal); + AfxMessageBox(_T("CDynDialogEx::DoModal() : LocalLock Failed")); + } + + BYTE *pdest = pBuffer; + // transfer DLGTEMPLATE structure to the buffer + memcpy(pdest, &m_DialogTemplate, sizeof(DLGTEMPLATE)); // DLGTemplate + pdest += sizeof(DLGTEMPLATE); + *(WORD*)pdest = 0; // no menu -- WORD to say it is 0 bytes + pdest += sizeof(WORD); // Increment + *(WORD*)(pdest + 1) = 0; // use default window class -- WORD to say it is 0 bytes + pdest += sizeof(WORD); // Increment + memcpy(pdest, szBoxCaption, szBoxLen); // Caption + pdest += szBoxLen; + + *(WORD*)pdest = m_wFontSize; // font size + pdest += sizeof(WORD); + memcpy(pdest, szFontName, nFontNameLen); // font name + pdest += nFontNameLen; + + // will now transfer the information for each one of subclassed controls... + if (m_DialogTemplate.cdit > 0) { + CDynDialogItemEx *pDynDialogItemEx = NULL; + for (int i = 0; i < m_arrDlgItemPtr.size(); i++) { + pDynDialogItemEx = m_arrDlgItemPtr[i]; + if (pDynDialogItemEx != NULL) { + if (pDynDialogItemEx->IsDlgItemSubclassed()) { + pdest = pDynDialogItemEx->FillBufferWithItemTemplate(pdest); + } + } + } + } + ASSERT(pdest - pBuffer == nBufferSize); // just make sure we did not overrun the heap + + //Next lines to make sure that MFC makes no ASSERTION when PREVIOUS/NEXT is pressed:) + if (m_lpDialogTemplate != NULL) { + m_lpDialogTemplate = NULL; + } + + //These are the MFC functions, which do the job... + if (m_bModelessDlg) { + iRet = CreateIndirect((LPDLGTEMPLATE)pBuffer, m_pParentWnd); + } + else { + InitModalIndirect((LPDLGTEMPLATE)pBuffer, m_pParentWnd); + iRet = CDialog::DoModal(); + } + + LocalUnlock(hLocal); + LocalFree(hLocal); + + delete [] szBoxCaption; + delete [] szFontName; + return iRet; + } + else { + AfxMessageBox(_T("CDynDialogEx::DoModal() : LocalAllock Failed")); + return -1; + } +} + +void CDynDialogEx::SetFont(CFont *pFont) +{ + m_pFont = pFont; +} + +CFont *CDynDialogEx::GetFont() +{ + return m_pFont; +} + +void CDynDialogEx::SetFontSize(WORD wSize) +{ + m_wFontSize = wSize; +} + +WORD CDynDialogEx::GetFontSize() +{ + return m_wFontSize; +} + +void CDynDialogEx::SetUseSystemButtons(BOOL bUse /*= TRUE*/) +{ + m_bAddSystemButtons = bUse; +} + +void CDynDialogEx::GetDlgRect(LPRECT lpRect) +{ + ASSERT(lpRect); + lpRect->left = m_DialogTemplate.x; + lpRect->top = m_DialogTemplate.y; + lpRect->right = lpRect->left + m_DialogTemplate.cx; + lpRect->bottom = lpRect->top + m_DialogTemplate.cy; +} + +void CDynDialogEx::SetDlgRect(LPRECT lpRect) +{ + ASSERT(lpRect); + //#pragma warning(disable : 4244) + m_DialogTemplate.cx = (short)(lpRect->right - lpRect->left); + m_DialogTemplate.cy = (short)(lpRect->bottom - lpRect->top); + m_DialogTemplate.x = (short)(lpRect->left); + m_DialogTemplate.y = (short)(lpRect->top); + //#pragma warning(default : 4244) +} + +void CDynDialogEx::SetDlgRectangle(LPRECT pRect) +{ + RECT rect; + + GetDlgRect(&rect); + if (rect.left > pRect->left) { + rect.left = pRect->left; + } + if (rect.right < pRect->right) { + rect.right = pRect->right + 5; + } + if (rect.top > pRect->top) { + rect.top = pRect->top; + } + if (rect.bottom < pRect->bottom) { + rect.bottom = pRect->bottom + 5; + } + SetDlgRect(&rect); +} + +UINT CDynDialogEx::AddDlgControl(DLGITEMTEMPLATECONTROLS TypeControl, + LPCTSTR lpszCaption, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect /*= NULL*/, + void *pData /*= NULL*/, + UINT nID /*= 0*/) +{ + UINT nRet = 0; + //In case no rectangle given create our own... + CRect Rect(FIXEDCOL1, m_nCurRow, FIXEDCOL2, m_nCurRow + ROWSTEPSIZE); + + //if no rectangle given use our own... + if (pRect == NULL) { + pRect = &Rect; + } + // else { + // m_nCurRow = max(m_nCurRow, pRect->bottom) - m_nCurRow; + // } + + m_nCurRow += (ROWSTEPSIZE); + + //Update dialogtemplate boundaries + SetDlgRectangle(pRect); + + //Create control and add to array of controls + CDynDialogItemEx *pDynDialogItemEx = new CDynDialogItemEx; + if (pDynDialogItemEx != NULL) { + nRet = pDynDialogItemEx->InitDialogItem(TypeControl, dwStyle, dwExtendedStyle, pRect, lpszCaption, nID, FALSE, pData); + m_arrDlgItemPtr.push_back(pDynDialogItemEx); + } + + //Return ID of Control we created. + return nRet; +} + +UINT CDynDialogEx::AddDlgControl(LPCSTR lpszClassName, + LPCTSTR lpszCaption, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect /*= NULL*/, + void *pData /*= NULL*/, + UINT nID /*= 0*/) +{ + UINT nRet = 0; + //In case no rectangle given create our own... + CRect Rect(FIXEDCOL1, m_nCurRow, FIXEDCOL2, m_nCurRow + ROWSTEPSIZE); + + //if no rectangle given use our own... + if (pRect == NULL) { + pRect = &Rect; + Rect.right += INPUTCOL; + } + // else { + // m_nCurRow = max(m_nCurRow, pRect->bottom) - m_nCurRow; + // } + + m_nCurRow += (ROWSTEPSIZE); + + //Update dialogtemplate boundaries + SetDlgRectangle(pRect); + + //Create control and add to array of controls + CDynDialogItemEx *pDynDialogItemEx = new CDynDialogItemEx; + if (pDynDialogItemEx != NULL) { + nRet = pDynDialogItemEx->InitDialogItem(lpszClassName, dwStyle, dwExtendedStyle, pRect, lpszCaption, nID, FALSE, pData); + m_arrDlgItemPtr.push_back(pDynDialogItemEx); + } + + //Return ID of Control we created. + return nRet; +} + +UINT CDynDialogEx::AddSubclassedDlgControl(LPCSTR lpszClassName, + LPCTSTR lpszCaption, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect /*= NULL*/, + UINT nID /*= 0*/) +{ + UINT nRet = 0; + //In case no rectangle given create our own... + CRect Rect(FIXEDCOL1, m_nCurRow, FIXEDCOL2, m_nCurRow + ROWSTEPSIZE); + + //if no rectangle given use our own... + if (pRect == NULL) { + pRect = &Rect; + Rect.right += INPUTCOL; + } + // else { + // m_nCurRow = max(m_nCurRow, pRect->bottom) - m_nCurRow; + // } + + m_nCurRow += (ROWSTEPSIZE); + + //Update dialogtemplate boundaries + SetDlgRectangle(pRect); + + //Create control and add to array of controls + CDynDialogItemEx *pDynDialogItemEx = new CDynDialogItemEx; + if (pDynDialogItemEx != NULL) { + nRet = pDynDialogItemEx->InitDialogItem(lpszClassName, dwStyle, dwExtendedStyle, pRect, lpszCaption, nID, TRUE); + m_arrDlgItemPtr.push_back(pDynDialogItemEx); + m_DialogTemplate.cdit++; + } + + //Return ID of Control we created. + return nRet; +} + +void CDynDialogEx::AddSystemButtons() +{ + m_nCurRow += 6; // Leave some room! + CRect rect(FIXEDCOL1, m_nCurRow, FIXEDCOL2 - 60, m_nCurRow + (long)(ROWSTEPSIZE * 1.2)); + + AddDlgControl(BUTTON, _T("OK"), STYLE_BUTTON, EXSTYLE_BUTTON, &rect, NULL, IDOK); + + // This has to be revised later. + rect.left += 55; + rect.right += 55; + AddDlgControl(BUTTON, _T("Annuleren"), STYLE_BUTTON, EXSTYLE_BUTTON, &rect, NULL, IDCANCEL); + +} + +void CDynDialogEx::SetWindowTitle(LPCSTR lpszCaption) +{ + m_strCaption = lpszCaption; +} + +void CDynDialogEx::SetUseModeless(BOOL bModelessDlg /*= TRUE*/) +{ + m_bModelessDlg = bModelessDlg; +} + +void CDynDialogEx::OnCancel() +{ + if (m_bModelessDlg) { + DestroyWindow(); + } + else { + CDialog::OnCancel(); + } +} + +void CDynDialogEx::OnOK() +{ + if (m_bModelessDlg) { + DestroyWindow(); + } + else { + CDialog::OnOK(); + } +} + +BOOL CDynDialogEx::OnCommand(WPARAM wParam, LPARAM lParam) +{ + //wParam + //The low-order word of wParam identifies the command ID of the menu item, control, or accelerator. + //The high-order word of wParam specifies the notification message if the message is from a control. + //If the message is from an accelerator, the high-order word is 1. + //If the message is from a menu, the high-order word is 0. + + //lParam + //Identifies the control that sends the message if the message is from a control. Otherwise, lParam is 0. + + WORD wControlID = LOWORD(wParam); + WORD wMessageID = HIWORD(wParam); + + if (wControlID != 0) { + switch (wControlID) { + case IDOK: + OnOK(); + return TRUE; + break; + case IDCANCEL: + OnCancel(); + return TRUE; + break; + default: + //if we have a parent send the message to the parent, so we can handle the message over there. + if (m_pParentWnd != NULL) { + ::SendMessage(m_pParentWnd->GetSafeHwnd(), WM_COMMAND, wParam, lParam); + } + break; + } + } + + return CDialog::OnCommand(wParam, lParam); +} + +// Hellup! +afx_msg LRESULT CDynDialogEx::OnHelpMsg(WPARAM wParam, LPARAM lParam) +{ + //lParam <<-- Contains: (LPHELPINFO)lParam + // >> typedef struct tagHELPINFO { + // UINT cbSize; + // int iContextType + // int iCtrlId; + // HANDLE hItemHandle; + // DWORD dwContextId; + // POINT MousePos; + // } HELPINFO, FAR *LPHELPINFO; + + // + // User pressed F1 in dialog, call the OnHelp() function, this can be overridden... + // + OnHelp(); + return TRUE; +} + +// Default implementation of the OnHelp() +// This one is to be overridden by a caller ;) +void CDynDialogEx::OnHelp() +{ + CDialog::OnHelp(); +} diff --git a/ext/PropertyGrid/DynDialogEx.h b/ext/PropertyGrid/DynDialogEx.h new file mode 100644 index 0000000..fe3cdaa --- /dev/null +++ b/ext/PropertyGrid/DynDialogEx.h @@ -0,0 +1,160 @@ +#if !defined(AFX_DYNDIALOGEX_H__CF5AF5E8_BD00_11D3_AA7C_0008C7083CA9__INCLUDED_) +#define AFX_DYNDIALOGEX_H__CF5AF5E8_BD00_11D3_AA7C_0008C7083CA9__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// DynDialogEx.h : header file +// + +#include +#include "DynDialogItemEx.h" + +// Control styles +#define STYLE_EDIT (WS_VISIBLE | WS_CHILD | WS_TABSTOP | SS_LEFT) +#define STYLE_MULTIEDIT (WS_VISIBLE | WS_CHILD | WS_TABSTOP | SS_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | ES_WANTRETURN) +#define STYLE_STATIC (WS_VISIBLE | WS_CHILD | SS_LEFT) +#define STYLE_STATIC_CENTER (WS_VISIBLE | WS_CHILD | SS_CENTER) +#define STYLE_STATIC_RIGHT (WS_VISIBLE | WS_CHILD | SS_RIGHT) +#define STYLE_RADIO (WS_VISIBLE | WS_CHILD | BS_AUTORADIOBUTTON) +#define STYLE_RADIO_GROUP (STYLE_RADIO| WS_GROUP | WS_TABSTOP) +#define STYLE_BUTTON (WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_PUSHBUTTON) +#define STYLE_GROUPBOX (WS_VISIBLE | WS_CHILD | BS_GROUPBOX) +#define STYLE_CHECKBOX (WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | BS_AUTOCHECKBOX) +#define STYLE_COMBOBOX_DROPDOWN (WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | WS_VSCROLL | CBS_DROPDOWN | CBS_SORT | CBS_AUTOHSCROLL) // | CBS_DISABLENOSCROLL) +#define STYLE_COMBOBOX_DROPDOWN_NOSORT (WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | WS_VSCROLL | CBS_DROPDOWN | CBS_AUTOHSCROLL) // | CBS_DISABLENOSCROLL) +#define STYLE_COMBOBOX_SIMPLE (WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | WS_VSCROLL | CBS_SIMPLE | CBS_SORT | CBS_AUTOHSCROLL) // | CBS_DISABLENOSCROLL) +#define STYLE_COMBOBOX_SIMPLE_NOSORT (WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | WS_VSCROLL | CBS_SIMPLE | CBS_AUTOHSCROLL) // | CBS_DISABLENOSCROLL) +#define STYLE_COMBOBOX_DROPDOWNLIST (WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_SORT | CBS_AUTOHSCROLL) // | CBS_DISABLENOSCROLL) +#define STYLE_COMBOBOX_DROPDOWNLIST_NOSORT (WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_AUTOHSCROLL) // | CBS_DISABLENOSCROLL) +#define STYLE_LISTBOX (WS_VISIBLE | WS_CHILD | WS_TABSTOP | LBS_STANDARD) + +#define STYLE_DATETIMEPICKER (WS_VISIBLE | WS_CHILD | WS_TABSTOP | DTS_RIGHTALIGN) +#define STYLE_TIMEPICKER (WS_VISIBLE | WS_CHILD | WS_TABSTOP | DTS_RIGHTALIGN | DTS_TIMEFORMAT) +// Default combo-style +#define STYLE_COMBOBOX (STYLE_COMBOBOX_DROPDOWNLIST) +#define STYLE_COMBOBOX_NOSORT (STYLE_COMBOBOX_DROPDOWNLIST_NOSORT) + +// Control Extended styles +#define EXSTYLE_EDIT (WS_EX_CLIENTEDGE) +#define EXSTYLE_MULTIEDIT (WS_EX_CLIENTEDGE) +#define EXSTYLE_LISTBOX (WS_EX_CLIENTEDGE) +#define EXSTYLE_STATIC (0) +#define EXSTYLE_RADIO (0) +#define EXSTYLE_BUTTON (0) +#define EXSTYLE_GROUPBOX (0) +#define EXSTYLE_CHECKBOX (0) +#define EXSTYLE_COMBOBOX (0) +#define EXSTYLE_DATETIMEPICKER (0) +#define EXSTYLE_TIMEPICKER (0) + +#define ROWSTEPSIZE 12 +#define FIRSTROW1 10 +#define FIRSTROW2 37 +#define FIXEDCOL1 10 +#define FIXEDCOL2 120 +#define INPUTCOL 150 +#define GROWLIMIT 6 + +#define MAX_COLS_PER_DESCR 25 // Just a number.. + +///////////////////////////////////////////////////////////////////////////// +// CDynDialogEx dialog + +class CDynDialogEx : public CDialog +{ + // Construction +public: + CDynDialogEx(CWnd* pParent = NULL); // standard constructor + ~CDynDialogEx(); + + virtual CWnd *GetParent(); + + // Dialog Data + //{{AFX_DATA(CDynDialogEx) + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + virtual UINT AddDlgControl(DLGITEMTEMPLATECONTROLS TypeControl, + LPCTSTR lpszCaption, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect = NULL, + void *pData = NULL, + UINT nID = 0); + virtual UINT AddDlgControl(LPCSTR lpszClassName, + LPCTSTR lpszCaption, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect = NULL, + void *pData = NULL, + UINT nID = 0); + virtual UINT AddSubclassedDlgControl(LPCSTR lpszClassName, + LPCTSTR lpszCaption, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect = NULL, + UINT nID = 0); + void SetWindowTitle(LPCSTR lpszCaption); + void SetFont(CFont *pFont); + CFont *GetFont(); + void SetFontSize(WORD wSize); + WORD GetFontSize(); + void SetUseSystemButtons(BOOL bUse = TRUE); + void SetUseModeless(BOOL bModelessDlg = TRUE); + long GetNumberOfConrols() {return long(m_arrDlgItemPtr.size());} + + //Additional functions by Tom Daffin + void AddStyles(DWORD dwStyles); + void RemoveStyles(DWORD dwStyles); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDynDialogEx) +public: + virtual int DoModal(); +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void OnCancel(); + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + virtual void OnOK(); + //}}AFX_VIRTUAL + virtual void OnHelp(); // To be overridden.. + +protected: + CWnd *m_pParentWnd; + CString m_strCaption; + CFont *m_pFont; + WORD m_wFontSize; + long m_nCurRow; + BOOL m_bAddSystemButtons; + BOOL m_bIsFontCreated; + BOOL m_bModelessDlg; + + DLGTEMPLATE m_DialogTemplate; + + // DOWNGRADE: Switch from CArray to std::vector because MFC 4.2 doesn't have CArray + std::vector m_arrDlgItemPtr; + + // Implementation +protected: + void AddSystemButtons(); + void GetDlgRect(LPRECT lpRect); + void SetDlgRect(LPRECT lpRect); + void SetDlgRectangle(LPRECT pRect); + + + // Generated message map functions + //{{AFX_MSG(CDynDialogEx) + virtual BOOL OnInitDialog(); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + //}}AFX_MSG + afx_msg LRESULT OnHelpMsg(WPARAM wParam, LPARAM lParam); + + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DYNDIALOGEX_H__CF5AF5E8_BD00_11D3_AA7C_0008C7083CA9__INCLUDED_) diff --git a/ext/PropertyGrid/DynDialogItemEx.cpp b/ext/PropertyGrid/DynDialogItemEx.cpp new file mode 100644 index 0000000..0a87365 --- /dev/null +++ b/ext/PropertyGrid/DynDialogItemEx.cpp @@ -0,0 +1,534 @@ +// DynDialogItemEx.cpp: implementation of the CDynDialogItemEx class. +// +// Written by Marcel Scherpenisse +// mailto:Marcel_Scherpenisse@insad.nl +// +// This code may be used in compiled form in any way you desire. This +// file may be redistributed unmodified by any means PROVIDING it is +// not sold for profit without the authors written consent, and +// providing that this notice and the authors name and all copyright +// notices remains intact. If the source code in this file is used in +// any commercial application then a statement along the lines of +// "Portions copyright (c) Marcel Scherpenisse, 2002" must be included in +// the startup banner, "About" box or printed documentation. An email +// letting me know that you are using it would be nice as well. +// +// This file is provided "as is" with no expressed or implied warranty. +// The author accepts no liability for any damage/loss of business that +// this product may cause. +// +// Expect bugs! +////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" +#include "DynDialogItemEx.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +struct _RuntimeLicense { + LPCTSTR lpszRegisteredControlName; + WCHAR *wchLicenseKey; + long lLicenseLength; +}_TAGRUNTIMELICENSE; + +/*mgkgtgnnmnmninigthkgogggvmkhinjggnvm*/ //(MS Multimedia MCI Control - mci32.ocx) +WCHAR pwchMCIMMControl1LicenseKey[] = +{ + 0x006D, 0x0067, 0x006B, 0x0067, 0x0074, 0x0067, + 0x006E, 0x006E, 0x006D, 0x006E, 0x006D, 0x006E, + 0x0069, 0x006E, 0x0069, 0x0067, 0x0074, 0x0068, + 0x006B, 0x0067, 0x006F, 0x0067, 0x0067, 0x0067, + 0x0076, 0x006D, 0x006B, 0x0068, 0x0069, 0x006E, + 0x006A, 0x0067, 0x0067, 0x006E, 0x0076, 0x006D +}; + +/*Copyright (c) 1994 */ //(MS Communications Control - mscomm32.ocx) +WCHAR pwchMSCOMMLibMSComm1LicenseKey[] = +{ + 0x0043, 0x006F, 0x0070, 0x0079, 0x0072, 0x0069, + 0x0067, 0x0068, 0x0074, 0x0020, 0x0028, 0x0063, + 0x0029, 0x0020, 0x0031, 0x0039, 0x0039, 0x0034, + 0x0020 +}; + +/*72E67120-5959-11cf-91F6-C2863C385E30*/ //(MS Flex Grid Control - msflxgrd.ocx) +WCHAR pwchMSFlexGridLibMSFlexGrid1LicenseKey[] = +{ + 0x0037, 0x0032, 0x0045, 0x0036, 0x0037, 0x0031, + 0x0032, 0x0030, 0x002D, 0x0035, 0x0039, 0x0035, + 0x0039, 0x002D, 0x0031, 0x0031, 0x0063, 0x0066, + 0x002D, 0x0039, 0x0031, 0x0046, 0x0036, 0x002D, + 0x0043, 0x0032, 0x0038, 0x0036, 0x0033, 0x0043, + 0x0033, 0x0038, 0x0035, 0x0045, 0x0033, 0x0030 +}; + +/*mgkgtgnnmnmninigthkgogggvmkhinjggnvm*/ //(MS Masked Edit - msmask32.ocx) +WCHAR pwchMSMaskMaskEdBox1LicenseKey[] = +{ + 0x006D, 0x0067, 0x006B, 0x0067, 0x0074, 0x0067, + 0x006E, 0x006E, 0x006D, 0x006E, 0x006D, 0x006E, + 0x0069, 0x006E, 0x0069, 0x0067, 0x0074, 0x0068, + 0x006B, 0x0067, 0x006F, 0x0067, 0x0067, 0x0067, + 0x0076, 0x006D, 0x006B, 0x0068, 0x0069, 0x006E, + 0x006A, 0x0067, 0x0067, 0x006E, 0x0076, 0x006D +}; + +/*GL........*/ //(MS Grid Control - grid32.ocx) +WCHAR pwchMSDBGridDBGridLicenseKey[] = +{ + 0x0047, 0x004C, 0x0005, 0x0008, 0x0001, 0x0005, + 0x0002, 0x0008, 0x0001, 0x0004 +}; + +/*DB4C0D09-400B-101B-A3C9-08002B2F49FB*/ //(MS Picture Clip Control - picclp32.ocx) +WCHAR pwchPicClipPictureClip1LicenseKey[] = +{ + 0x0044, 0x0042, 0x0034, 0x0043, 0x0030, 0x0044, + 0x0030, 0x0039, 0x002D, 0x0034, 0x0030, 0x0030, + 0x0042, 0x002D, 0x0031, 0x0030, 0x0031, 0x0042, + 0x002D, 0x0041, 0x0033, 0x0043, 0x0039, 0x002D, + 0x0030, 0x0038, 0x0030, 0x0030, 0x0032, 0x0042, + 0x0032, 0x0046, 0x0034, 0x0039, 0x0046, 0x0042 +}; + +/*04746E60CE4F11CDB23C0000C076FE*/ //(MS Tab Control - tabctl32.ocx) +static WCHAR pwchTabDlgSSTab1LicenseKey[] = +{ + 0x0030, 0x0034, 0x0037, 0x0034, 0x0036, 0x0045, + 0x0036, 0x0030, 0x0043, 0x0045, 0x0034, 0x0046, + 0x0031, 0x0031, 0x0043, 0x0044, 0x0042, 0x0032, + 0x0033, 0x0043, 0x0030, 0x0030, 0x0030, 0x0030, + 0x0043, 0x0030, 0x0037, 0x0036, 0x0046, 0x0045 +}; + +static _RuntimeLicense RuntimeLicenses[] = +{ + {_T("MCI.MMControl.1"), pwchMCIMMControl1LicenseKey, sizeof(pwchMCIMMControl1LicenseKey)}, + {_T("MSCOMMLib.MSComm.1"), pwchMSCOMMLibMSComm1LicenseKey, sizeof(pwchMSCOMMLibMSComm1LicenseKey)}, + {_T("MSFlexGridLib.MSFlexGrid.1"), pwchMSFlexGridLibMSFlexGrid1LicenseKey, sizeof(pwchMSFlexGridLibMSFlexGrid1LicenseKey)}, + {_T("MSMask.MaskEdBox.1"), pwchMSMaskMaskEdBox1LicenseKey, sizeof(pwchMSMaskMaskEdBox1LicenseKey)}, + {_T("MSDBGrid.DBGrid"), pwchMSDBGridDBGridLicenseKey, sizeof(pwchMSDBGridDBGridLicenseKey)}, + {_T("PicClip.PictureClip.1"), pwchPicClipPictureClip1LicenseKey, sizeof(pwchPicClipPictureClip1LicenseKey)}, + {_T("TabDlg.SSTab.1"), pwchTabDlgSSTab1LicenseKey, sizeof(pwchTabDlgSSTab1LicenseKey)}, + {NULL, NULL, 0} +}; + +static UINT glb_nNextID = WM_USER; // We have to start somewhere... + +UINT GetNewUniqueID(void) +{ + glb_nNextID++; + return glb_nNextID - 1; +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CDynDialogItemEx::CDynDialogItemEx() +: CWnd() +{ + m_eTypeControl = NOCONTROL; + m_strClassName = _T(""); + m_dwStyle = 0; + m_dwStyleEx = 0; + m_strCaption = _T(""); + m_ControlID = 0; + m_pData = NULL; + m_bSubclassed = FALSE; +} + +void CDynDialogItemEx::DoDataExchange(CDataExchange *pDX) +{ + if (m_pData != NULL) { + switch(m_eTypeControl) { +case BUTTON: + if ((m_dwStyle & BS_AUTORADIOBUTTON) == BS_AUTORADIOBUTTON) { + DDX_Radio(pDX, m_ControlID, *(int*)m_pData); + } + else if ((m_dwStyle & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX) { + DDX_Check(pDX, m_ControlID, *(int*)m_pData); + } + else { + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + } + break; +case EDITCONTROL: + DDX_Text(pDX, m_ControlID, *(CString*)m_pData); + break; +case STATICTEXT: + DDX_Text(pDX, m_ControlID, *(CString*)m_pData); + break; +case LISTBOX: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case HSCROLL: + DDX_Scroll(pDX, m_ControlID, *(int*)m_pData); + break; +case COMBOBOX: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case SPIN: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case PROGRES: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case SLIDER: + DDX_Slider(pDX, m_ControlID, *(int*)m_pData); + break; +case HOTKEY: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case LISTCTRL: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case TREECTRL: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case TABCTRL: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case ANIMATE: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case RICHEDIT: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case DATETIMEPICKER: + if ((m_dwStyle & DTS_TIMEFORMAT) == DTS_TIMEFORMAT) { + DDX_DateTimeCtrl(pDX, m_ControlID, *(CTime*)m_pData); + } + else { + DDX_DateTimeCtrl(pDX, m_ControlID, *(COleDateTime*)m_pData); + } + break; +case MONTHCALENDER: + DDX_MonthCalCtrl(pDX, m_ControlID, *(COleDateTime*)m_pData); + break; +case IPADRESS: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +case COMBOBOXEX: + DDX_Control(pDX, m_ControlID, *(CWnd*)m_pData); + break; +default: + break; + } + } + CWnd::DoDataExchange(pDX); +} + +BEGIN_MESSAGE_MAP(CDynDialogItemEx, CWnd) + //{{AFX_MSG_MAP(CDynDialogItemEx) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +char* CDynDialogItemEx::GetClassNameByType(DLGITEMTEMPLATECONTROLS TypeControl) +{ + switch(TypeControl) { +case BUTTON: + return _T("BUTTON"); +case EDITCONTROL: + return _T("EDIT"); +case STATICTEXT: + return _T("STATIC"); +case LISTBOX: + return _T("LISTBOX"); +case HSCROLL: + return _T("SCROLLBAR"); +case COMBOBOX: + return _T("COMBOBOX"); +case SPIN: + return _T("msctls_updown32"); +case PROGRES: + return _T("msctls_progress32"); +case SLIDER: + return _T("msctls_trackbar32"); +case HOTKEY: + return _T("msctls_hotkey32"); +case LISTCTRL: + return _T("SysListView32"); +case TREECTRL: + return _T("SysTreeView32"); +case TABCTRL: + return _T("SysTabControl32"); +case ANIMATE: + return _T("SysAnimate32"); +case RICHEDIT: + return _T("RICHEDIT"); +case DATETIMEPICKER: + return _T("SysDateTimePick32"); +case MONTHCALENDER: + return _T("SysMonthCal32"); +case IPADRESS: + return _T("SysIPAddress32"); +case COMBOBOXEX: + return _T("ComboBoxEx32"); + } + return _T(""); +} + +DLGITEMTEMPLATECONTROLS CDynDialogItemEx::GetClassTypeByName(LPCSTR lpszClassName) +{ + if (memcmp(lpszClassName, _T("BUTTON"), 6) == 0) { + return BUTTON; + } + else if (memcmp(lpszClassName, _T("EDIT"), 4) == 0) { + return EDITCONTROL; + } + else if (memcmp(lpszClassName, _T("STATIC"), 6) == 0) { + return STATICTEXT; + } + else if (memcmp(lpszClassName, _T("LISTBOX"), 7) == 0) { + return LISTBOX; + } + else if (memcmp(lpszClassName, _T("SCROLLBAR"), 9) == 0) { + return HSCROLL; + } + else if (memcmp(lpszClassName, _T("COMBOBOX"), 8) == 0) { + return COMBOBOX; + } + else if (memcmp(lpszClassName, _T("msctls_updown32"), 15) == 0) { + return SPIN; + } + else if (memcmp(lpszClassName, _T("msctls_progress32"), 17) == 0) { + return PROGRES; + } + else if (memcmp(lpszClassName, _T("msctls_trackbar32"), 17) == 0) { + return SLIDER; + } + else if (memcmp(lpszClassName, _T("msctls_hotkey32"), 15) == 0) { + return HOTKEY; + } + else if (memcmp(lpszClassName, _T("SysListView32"), 13) == 0) { + return LISTCTRL; + } + else if (memcmp(lpszClassName, _T("SysTreeView32"), 13) == 0) { + return TREECTRL; + } + else if (memcmp(lpszClassName, _T("SysTabControl32"), 15) == 0) { + return TABCTRL; + } + else if (memcmp(lpszClassName, _T("SysAnimate32"), 12) == 0) { + return ANIMATE; + } + else if (memcmp(lpszClassName, _T("RICHEDIT"), 8) == 0) { + return RICHEDIT; + } + else if (memcmp(lpszClassName, _T("SysDateTimePick32"), 17) == 0) { + return DATETIMEPICKER; + } + else if (memcmp(lpszClassName, _T("SysMonthCal32"), 13) == 0) { + return MONTHCALENDER; + } + else if (memcmp(lpszClassName, _T("SysIPAddress32"), 14) == 0) { + return IPADRESS; + } + else if (memcmp(lpszClassName, _T("ComboBoxEx32"), 12) == 0) { + return COMBOBOXEX; + } + + return NOCONTROL; +} + +UINT CDynDialogItemEx::InitDialogItem(DLGITEMTEMPLATECONTROLS TypeControl, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect, + LPCTSTR lpszCaption, + UINT nID /*= 0*/, + BOOL bSubclassed /*= FALSE*/, + void *pData /*= NULL*/) +{ + m_eTypeControl = TypeControl; + m_strClassName = GetClassNameByType(m_eTypeControl); + m_dwStyle = dwStyle; + m_dwStyleEx = dwExtendedStyle; + m_Rect = pRect; + m_strCaption = lpszCaption; + m_bSubclassed = bSubclassed; + m_pData = pData; + if (nID == 0) { + m_ControlID = ::GetNewUniqueID(); + } + else { + m_ControlID = nID; + } + return m_ControlID; +} + +UINT CDynDialogItemEx::InitDialogItem(LPCSTR lpszClassName, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect, + LPCTSTR lpszCaption, + UINT nID /*= 0*/, + BOOL bSubclassed /*= FALSE*/, + void *pData /*= NULL*/) +{ + m_strClassName = lpszClassName; + m_eTypeControl = GetClassTypeByName(lpszClassName); + m_dwStyle = dwStyle; + m_dwStyleEx = dwExtendedStyle; + m_Rect = pRect; + m_strCaption = lpszCaption; + m_bSubclassed = bSubclassed; + m_pData = pData; + if (nID == 0) { + m_ControlID = ::GetNewUniqueID(); + } + else { + m_ControlID = nID; + } + return m_ControlID; +} + +BOOL CDynDialogItemEx::CreateEx(CWnd *pParent) +{ + BOOL bRet = FALSE; + if (m_eTypeControl == NOCONTROL) { //It will probably be an OCX... + // + // Create the control later.... + // if it's created here then the rectangle is not OK and SetWindowPos doesn't work on OCX's???? + // + bRet = TRUE; + } + else if (m_pData != NULL && IsDataMemberPointerToWnd()) { + bRet = ((CWnd*)m_pData)->CreateEx(m_dwStyleEx, m_strClassName, m_strCaption, m_dwStyle, m_Rect, pParent, m_ControlID); + } + else { + bRet = CWnd::CreateEx(m_dwStyleEx, m_strClassName, m_strCaption, m_dwStyle, m_Rect, pParent, m_ControlID); + } + + return bRet; +} + +BOOL CDynDialogItemEx::SetWindowPos(CWnd *pParent) +{ + BOOL bRet = FALSE; + //Conversion of Dialog units to screenunits + CRect rect(m_Rect); + ((CDialog *)pParent)->MapDialogRect(&rect); + ASSERT(rect.IsRectEmpty() == FALSE); + + if (m_eTypeControl == NOCONTROL) { + BSTR bstrLicKey = GetRuntimeLicense(m_strClassName); + bRet = CreateControl(m_strClassName, m_strCaption, m_dwStyle, rect, pParent, m_ControlID, NULL, FALSE, bstrLicKey); + if (bstrLicKey != NULL) { + ::SysFreeString(bstrLicKey); + } + } + else if (m_pData != NULL && IsDataMemberPointerToWnd()) { + bRet = ((CWnd*)m_pData)->SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOZORDER); + } + else { + bRet = CWnd::SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOZORDER); + } + + return bRet; +} + +void CDynDialogItemEx::SetFont(CFont* pFont, BOOL bRedraw /*= TRUE*/) +{ + if (m_pData != NULL && IsDataMemberPointerToWnd()) { + ((CWnd*)m_pData)->SetFont(pFont, bRedraw); + } + else { + CWnd::SetFont(pFont, bRedraw); + } +} + +PBYTE CDynDialogItemEx::FillBufferWithItemTemplate(BYTE *pdest) +{ + pdest = (BYTE*)(((DWORD)pdest + 3) & ~3); // make the pointer DWORD aligned + + DLGITEMTEMPLATE dlgItemTemplate; + dlgItemTemplate.x = (short)m_Rect.left; + dlgItemTemplate.y = (short)m_Rect.top; + dlgItemTemplate.cx = (short)m_Rect.Width(); + dlgItemTemplate.cy = (short)m_Rect.Height(); + dlgItemTemplate.style = m_dwStyle; + dlgItemTemplate.dwExtendedStyle = m_dwStyleEx; + dlgItemTemplate.id = (USHORT)m_ControlID; + + memcpy(pdest, (void *)&dlgItemTemplate, sizeof(DLGITEMTEMPLATE)); + pdest += sizeof(DLGITEMTEMPLATE); + *(WORD*)pdest = 0xFFFF; // indicating atom value + pdest += sizeof(WORD); + *(WORD*)pdest = (USHORT)m_eTypeControl; // atom value for the control + pdest += sizeof(WORD); + + // transfer the caption even when it is an empty string + WCHAR* pchCaption; + int nChars, nActualChars; + + nChars = m_strCaption.GetLength() + 1; + pchCaption = new WCHAR[nChars]; + nActualChars = MultiByteToWideChar(CP_ACP, 0, m_strCaption, -1, pchCaption, nChars); + ASSERT(nActualChars > 0); + memcpy(pdest, pchCaption, nActualChars * sizeof(WCHAR)); + pdest += nActualChars * sizeof(WCHAR); + delete pchCaption; + + *(WORD*)pdest = 0; // How many bytes in data for control + pdest += sizeof(WORD); + + return pdest; +} + +BSTR CDynDialogItemEx::GetRuntimeLicense(CString &strControlName) +{ + BSTR bstrLicKey = NULL; + int i = 0; + while (RuntimeLicenses[i].lpszRegisteredControlName != NULL) { + if (strControlName.Compare(RuntimeLicenses[i].lpszRegisteredControlName) == 0) { + bstrLicKey = ::SysAllocStringLen(RuntimeLicenses[i].wchLicenseKey, RuntimeLicenses[i].lLicenseLength/sizeof(WCHAR)); + break; + } + i++; + } + return bstrLicKey; +} + +BOOL CDynDialogItemEx::IsDataMemberPointerToWnd() +{ + BOOL bRet = TRUE; + switch(m_eTypeControl) + { + case BUTTON: + if ((m_dwStyle & BS_AUTORADIOBUTTON) == BS_AUTORADIOBUTTON) { + bRet = FALSE; + } + else if ((m_dwStyle & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX) { + bRet = FALSE; + } + break; + case EDITCONTROL: + bRet = FALSE; + break; + case STATICTEXT: + bRet = FALSE; + break; + case HSCROLL: + bRet = FALSE; + break; + case SLIDER: + bRet = FALSE; + break; + case DATETIMEPICKER: + bRet = FALSE; + break; + case MONTHCALENDER: + bRet = FALSE; + break; + default: + break; + } + return bRet; +} diff --git a/ext/PropertyGrid/DynDialogItemEx.h b/ext/PropertyGrid/DynDialogItemEx.h new file mode 100644 index 0000000..b03adcb --- /dev/null +++ b/ext/PropertyGrid/DynDialogItemEx.h @@ -0,0 +1,107 @@ +// DynDialogItemEx.h: interface for the CDynDialogItemEx class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_DYNDIALOGITEMEX_H__CF5AF5E9_BD00_11D3_AA7C_0008C7083CA9__INCLUDED_) +#define AFX_DYNDIALOGITEMEX_H__CF5AF5E9_BD00_11D3_AA7C_0008C7083CA9__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +typedef enum { + NOCONTROL = 0, + BUTTON = 0x0080, + EDITCONTROL = 0x0081, + STATICTEXT = 0x0082, + LISTBOX = 0x0083, + HSCROLL = 0x0084, + COMBOBOX = 0x0085, + SPIN, + PROGRES, + SLIDER, + HOTKEY, + LISTCTRL, + TREECTRL, + TABCTRL, + ANIMATE, + RICHEDIT, + DATETIMEPICKER, + MONTHCALENDER, + IPADRESS, + COMBOBOXEX +} DLGITEMTEMPLATECONTROLS; + +class CDynDialogItemEx : public CWnd +{ +public: + // Construction + CDynDialogItemEx(); + // Operations +public: + UINT InitDialogItem(DLGITEMTEMPLATECONTROLS TypeControl, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect, + LPCTSTR lpszCaption, + UINT nID = 0, + BOOL bSubclassed = FALSE, + void *pData = NULL); + UINT InitDialogItem(LPCSTR lpszClassName, + DWORD dwStyle, + DWORD dwExtendedStyle, + LPRECT pRect, + LPCTSTR lpszCaption, + UINT nID = 0, + BOOL bSubclassed = FALSE, + void *pData = NULL); + BOOL CreateEx(CWnd *pParent); + void SetFont(CFont* pFont, BOOL bRedraw = TRUE); + + // Operations +public: + virtual void DoDataExchange(CDataExchange* pDX); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CDynDialogItemEx) + //}}AFX_VIRTUAL + + // Implementation +public: + //virtual ~CDynDialogItemEx(); + BOOL SetWindowPos(CWnd *pParent); + PBYTE FillBufferWithItemTemplate(BYTE* pdest); + + UINT GetControlID() {return m_ControlID;} + DLGITEMTEMPLATECONTROLS GetControlType() {return m_eTypeControl;} + long GetCaptionLength() {return m_strCaption.GetLength();} + BOOL IsDlgItemSubclassed() {return m_bSubclassed;} + CString GetClassName() {return m_strClassName;} + +protected: + DLGITEMTEMPLATECONTROLS GetClassTypeByName(LPCSTR lpszClassName); + + CString m_strClassName; + DLGITEMTEMPLATECONTROLS m_eTypeControl; + UINT m_ControlID; + UINT m_dwStyle; + UINT m_dwStyleEx; + CRect m_Rect; + CString m_strCaption; + void *m_pData; + BOOL m_bSubclassed; + + // Generated message map functions +protected: + BOOL IsDataMemberPointerToWnd(); + BSTR GetRuntimeLicense(CString &strControlName); + char* GetClassNameByType(DLGITEMTEMPLATECONTROLS TypeControl); + + //{{AFX_MSG(CDynDialogItemEx) + // NOTE - the ClassWizard will add and remove member functions here. + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#endif // !defined(AFX_DYNDIALOGITEMEX_H__CF5AF5E9_BD00_11D3_AA7C_0008C7083CA9__INCLUDED_) diff --git a/ext/PropertyGrid/ListDynDialogEx.cpp b/ext/PropertyGrid/ListDynDialogEx.cpp new file mode 100644 index 0000000..f8a800d --- /dev/null +++ b/ext/PropertyGrid/ListDynDialogEx.cpp @@ -0,0 +1,59 @@ +// ListDynDialogEx.cpp: implementation of the CListDynDialogEx class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ListDynDialogEx.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +#define IDC_LIST1 1600 +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CListDynDialogEx::CListDynDialogEx(CWnd* pParent /*= NULL*/) +: CDynDialogEx(pParent) +{ + m_bAddSystemButtons = FALSE; +} + +CListDynDialogEx::~CListDynDialogEx() +{ +} + +BEGIN_MESSAGE_MAP(CListDynDialogEx, CDynDialogEx) + //{{AFX_MSG_MAP(CListDynDialogEx) + ON_LBN_DBLCLK(IDC_LIST1, OnDblclkList) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +BOOL CListDynDialogEx::OnInitDialog() +{ + BOOL bRet = CDynDialogEx::OnInitDialog(); + + m_lstBox.AddString(_T("First String")); + m_lstBox.AddString(_T("Second String")); + + return bRet; +} + +int CListDynDialogEx::DoModal() +{ + CRect rect(7,7,150,150); + AddDlgControl(_T("LISTBOX"), _T("ListboxText"), STYLE_LISTBOX, EXSTYLE_LISTBOX, &rect, &m_lstBox, IDC_LIST1); + return CDynDialogEx::DoModal(); +} + +void CListDynDialogEx::OnDblclkList() +{ + CString strBuf; + int nIndex = m_lstBox.GetCurSel(); + m_lstBox.GetText(nIndex, strBuf); + AfxMessageBox(strBuf); + OnOK(); +} diff --git a/ext/PropertyGrid/ListDynDialogEx.h b/ext/PropertyGrid/ListDynDialogEx.h new file mode 100644 index 0000000..c11d135 --- /dev/null +++ b/ext/PropertyGrid/ListDynDialogEx.h @@ -0,0 +1,40 @@ +// ListDynDialogEx.h: interface for the CListDynDialogEx class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_LISTDYNDIALOGEX_H__57BA7AFA_B679_4FAC_B6BE_1AD925E45194__INCLUDED_) +#define AFX_LISTDYNDIALOGEX_H__57BA7AFA_B679_4FAC_B6BE_1AD925E45194__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "dyndialogex.h" + +class CListDynDialogEx : public CDynDialogEx +{ +public: + CListDynDialogEx(CWnd* pParent = NULL); + virtual ~CListDynDialogEx(); + + CListBox m_lstBox; + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CListDynDialogEx) +public: + virtual int DoModal(); + //}}AFX_VIRTUAL + + // Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CListDynDialogEx) + virtual BOOL OnInitDialog(); + afx_msg void OnDblclkList(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#endif // !defined(AFX_LISTDYNDIALOGEX_H__57BA7AFA_B679_4FAC_B6BE_1AD925E45194__INCLUDED_) diff --git a/ext/PropertyGrid/PropertyGrid.cpp b/ext/PropertyGrid/PropertyGrid.cpp new file mode 100644 index 0000000..962041c --- /dev/null +++ b/ext/PropertyGrid/PropertyGrid.cpp @@ -0,0 +1,2348 @@ +// PropertyGrid.cpp : implementation file +// + +#include "stdafx.h" +#include "CustomItem.h" +#include "PropertyGrid.h" +#include "PropertyGridDirectoryPicker.h" +#include "PropertyGridMonthCalCtrl.h" +#include "DynDialogEx.h" +#include +#include + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define IDC_MONTHCAL 1023 + +// CPropertyGrid + +static const int margin = 2; + +IMPLEMENT_DYNAMIC(CPropertyGrid, CWnd) +CPropertyGrid::CPropertyGrid() +{ + m_section_id = 0; + m_item_id = 0; + m_resizing_gutter = false; + m_button_pushed = false; + m_button_depressed = false; + m_value_clicked = false; + m_custom_tracking = false; + m_scroll_enabled = false; + m_draw_lines = true; + m_shade_titles = true; + m_draw_gutter = true; + m_focus_disabled = true; + m_bold_modified = false; + m_bold_editables = false; + m_display_mode = DM_CATEGORIZED; + m_control = NULL; + + m_rect_button = CRect(0,0,0,0); + m_ptLast = CPoint(0,0); + + m_strTrue = "True"; + m_strFalse = "False"; + m_strDate = "Date"; + m_strTime = "Time"; + m_strUndefined = ""; + m_strEmpty = ""; + + m_clrBack = GetSysColor(COLOR_WINDOW); + m_clrShade = GetSysColor(COLOR_3DFACE); + m_clrText = GetSysColor(COLOR_WINDOWTEXT); + m_clrTitle = GetSysColor(COLOR_WINDOWTEXT); + m_clrFocus = GetSysColor(COLOR_HIGHLIGHT); + m_clrHilite = GetSysColor(COLOR_HIGHLIGHTTEXT); + m_clrEditable = GetSysColor(COLOR_WINDOWTEXT); + m_clrDisabled = GetSysColor(COLOR_GRAYTEXT); + + m_focused_section = -1; + m_focused_item = -1; +} + +CPropertyGrid::~CPropertyGrid() +{ +} + +// +// customization +// + +bool CPropertyGrid::GetShadeTitles() +{ + return m_shade_titles; +} + +void CPropertyGrid::SetShadeTitles(bool shade_titles) +{ + m_shade_titles = shade_titles; + if (GetSafeHwnd()) + Invalidate(); +} + +bool CPropertyGrid::GetDrawLines() +{ + return m_draw_lines; +} + +void CPropertyGrid::SetDrawLines(bool draw_lines) +{ + m_draw_lines = draw_lines; + if (GetSafeHwnd()) + Invalidate(); +} + +bool CPropertyGrid::GetDrawGutter() +{ + return m_draw_gutter; +} + +void CPropertyGrid::SetDrawGutter(bool draw_gutter) +{ + m_draw_gutter = draw_gutter; + if (GetSafeHwnd()) + Invalidate(); +} + +bool CPropertyGrid::GetFocusDisabled() +{ + return m_focus_disabled; +} + +void CPropertyGrid::SetFocusDisabled(bool focus_disabled) +{ + m_focus_disabled = focus_disabled; + if (GetSafeHwnd()) + Invalidate(); +} + +bool CPropertyGrid::GetBoldModified() +{ + return m_bold_modified; +} + +void CPropertyGrid::SetBoldModified(bool bold_modified) +{ + m_bold_modified = bold_modified; +} + +bool CPropertyGrid::GetBoldEditables() +{ + return m_bold_editables; +} + +void CPropertyGrid::SetBoldEditables(bool bold_editables) +{ + m_bold_editables = bold_editables; +} + +// +// gutter width +// + +int CPropertyGrid::GetGutterWidth() +{ + return m_gutter_width; +} + +void CPropertyGrid::SetGutterWidth(int gutter_width) +{ + m_gutter_width = gutter_width; + if (GetSafeHwnd()) + Invalidate(); +} + +// +// custom colors +// + +void CPropertyGrid::SetTextColor(COLORREF clrText) +{ + if (m_clrText == m_clrEditable) + m_clrEditable = clrText; + m_clrText = clrText; + if (GetSafeHwnd()) + Invalidate(); +} + +void CPropertyGrid::SetTitleColor(COLORREF clrTitle) +{ + m_clrTitle = clrTitle; + if (GetSafeHwnd()) + Invalidate(); +} + +void CPropertyGrid::SetBackColor(COLORREF clrBack) +{ + m_clrBack = clrBack; + if (GetSafeHwnd()) + Invalidate(); +} + +void CPropertyGrid::SetShadeColor(COLORREF clrShade) +{ + m_clrShade = clrShade; + if (GetSafeHwnd()) + Invalidate(); +} + +void CPropertyGrid::SetFocusColor(COLORREF clrFocus) +{ + m_clrFocus = clrFocus; + if (GetSafeHwnd()) + Invalidate(); +} + +void CPropertyGrid::SetHiliteColor(COLORREF clrHilite) +{ + m_clrHilite = clrHilite; + if (GetSafeHwnd()) + Invalidate(); +} + +void CPropertyGrid::SetEditableColor(COLORREF clrEditable) +{ + m_clrEditable = clrEditable; + if (GetSafeHwnd()) + Invalidate(); +} + +void CPropertyGrid::SetDisabledColor(COLORREF clrDisabled) +{ + m_clrDisabled = clrDisabled; + if (GetSafeHwnd()) + Invalidate(); +} + +// +// localization +// + +void CPropertyGrid::SetTrueFalseStrings(string strTrue, string strFalse) +{ + m_strTrue = strTrue; + m_strFalse = strFalse; +} + +void CPropertyGrid::SetOkCancelStrings(string strOk, string strCancel) +{ + m_strOk = strOk; + m_strCancel = strCancel; +} + +void CPropertyGrid::SetDateTimeStrings(string strDate, string strTime) +{ + m_strDate = strDate; + m_strTime = strTime; +} + +void CPropertyGrid::SetUndefinedString(string strUndefined) +{ + m_strUndefined = strUndefined; +} + +void CPropertyGrid::SetEmptyString(string strEmpty) +{ + m_strEmpty = strEmpty; +} + +// +// appearance +// + +void CPropertyGrid::SetDisplayMode(EDisplayMode display_mode) +{ + m_display_mode = display_mode; + RecalcLayout(); +} + +void CPropertyGrid::ExpandAll(bool expand) +{ + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + it->m_collapsed = !expand; + RecalcLayout(); +} + +void CPropertyGrid::ExpandSection(HSECTION hs, bool expand) +{ + CSection* pSection = FindSection(hs); + if (pSection) + { + pSection->m_collapsed = !expand; + RecalcLayout(); + } +} + +bool CPropertyGrid::IsSectionCollapsed(HSECTION hs) +{ + CSection* pSection = FindSection(hs); + if (pSection) + return pSection->m_collapsed; + return false; +} + +string CPropertyGrid::GetItemText(HITEM hItem) +{ + CItem *item = FindItem(hItem); + if (item) { + return item->m_name; + } + return string(); +} + +// +// item management +// + +bool CPropertyGrid::CItem::operator==(const HITEM& item) const +{ + return m_id == item; +} + +bool CPropertyGrid::CItem::operator==(const string& name) const +{ + return m_name == name; +} + +bool CPropertyGrid::CSection::operator==(const HSECTION& section) const +{ + return m_id == section; +} + +void CPropertyGrid::CItem::ValidateChanges() +{ + // save the values + m_undefined_old = m_undefined; + m_nValue_old = m_nValue; + m_dValue_old = m_dValue; + m_strValue_old = m_strValue; + m_bValue_old = m_bValue; + m_dtValue_old = m_dtValue; + m_clrValue_old = m_clrValue; + memcpy(&m_lfValue_old, &m_lfValue, sizeof(LOGFONT)); + + // callback for custom + if (m_type == IT_CUSTOM) + m_pCustom->ValidateChanges(); +} + +HSECTION CPropertyGrid::AddSection(string title, bool collapsed, HSECTION after) +{ + // build it + CSection section; + section.m_id = m_section_id; + section.m_title = title; + section.m_collapsed = collapsed; + + // insert it + // if after does not exist then it is appended + vector::iterator it = find(m_sections.begin(), m_sections.end(), after); + m_sections.insert(it, section); + + // done + RecalcLayout(); + return m_section_id++; +} + +HITEM CPropertyGrid::AddItem(HSECTION hs, EItemType type, string name, void* pValue, bool editable, bool undefined, HITEM after) +{ + // check section exists + vector::iterator it = find(m_sections.begin(), m_sections.end(), hs); + if (it == m_sections.end()) + return -1; + + // check item does not already exists + vector::iterator it2 = find(it->m_items.begin(), it->m_items.end(), name); + if (it2 != it->m_items.end()) + return -1; + + // build the item + CItem item; + item.m_id = m_item_id++; + item.m_type = type; + item.m_name = name; + item.m_editable = editable; + item.m_undefined = undefined; + + // assign the value + if (type == IT_CUSTOM) item.m_pCustom = (ICustomItem*)pValue; + else if (type == IT_STRING || type == IT_TEXT || type == IT_FILE || type == IT_FOLDER) item.m_strValue = *(string*)pValue; + else if (type == IT_COMBO || type == IT_INTEGER) item.m_nValue = *(int*)pValue; + else if (type == IT_DOUBLE) item.m_dValue = *(double*)pValue; + else if (type == IT_BOOLEAN) item.m_bValue = *(bool*)pValue; + else if (type == IT_DATE || type == IT_DATETIME) item.m_dtValue = *(COleDateTime*)pValue; + else if (type == IT_COLOR) item.m_clrValue = *(COLORREF*)pValue; + else if (type == IT_FONT) memcpy(&item.m_lfValue, pValue, sizeof(LOGFONT)); + else assert(false); + + // finish and add + item.ValidateChanges(); + it->m_items.push_back(item); + RecalcLayout(); + return item.m_id; +} + +HITEM CPropertyGrid::AddCustomItem(HSECTION section, string name, ICustomItem* pItem, bool editable, HITEM after) +{ + pItem->m_pGrid = this; + return AddItem(section, IT_CUSTOM, name, pItem, editable, false, after); +} + +HITEM CPropertyGrid::AddStringItem(HSECTION section, string name, string value, bool editable, HITEM after) +{ + return AddItem(section, IT_STRING, name, &value, editable, false, after); +} + +HITEM CPropertyGrid::AddTextItem(HSECTION section, string name, string value, bool editable, HITEM after) +{ + return AddItem(section, IT_TEXT, name, &value, editable, false, after); +} + +HITEM CPropertyGrid::AddIntegerItem(HSECTION section, string name, int value, string format, bool editable, bool undefined, HITEM after) +{ + HITEM it = AddItem(section, IT_INTEGER, name, &value, editable, undefined, after); + CItem* pItem = FindItem(it); + if (pItem) pItem->m_options.push_back(format); + return it; +} + +HITEM CPropertyGrid::AddDoubleItem(HSECTION section, string name, double value, string format, bool editable, bool undefined, HITEM after) +{ + HITEM it = AddItem(section, IT_DOUBLE, name, &value, editable, undefined, after); + CItem* pItem = FindItem(it); + if (pItem) pItem->m_options.push_back(format); + return it; +} + +HITEM CPropertyGrid::AddComboItem(HSECTION section, string name, const vector& values, int cur, bool editable, bool undefined, HITEM after) +{ + HITEM it = AddItem(section, IT_COMBO, name, &cur, editable, undefined, after); + CItem* pItem = FindItem(it); + if (pItem) pItem->m_options = values; + return it; +} + +HITEM CPropertyGrid::AddBoolItem(HSECTION section, string name, bool value, bool editable, bool undefined, HITEM after) +{ + return AddItem(section, IT_BOOLEAN, name, &value, editable, undefined, after); +} + +HITEM CPropertyGrid::AddDateItem(HSECTION section, string name, COleDateTime value, string format, bool editable, bool undefined, HITEM after) +{ + HITEM it = AddItem(section, IT_DATE, name, &value, editable, undefined, after); + CItem* pItem = FindItem(it); + if (pItem) pItem->m_options.push_back(format); + return it; +} + +HITEM CPropertyGrid::AddDateTimeItem(HSECTION section, string name, COleDateTime value, string format, bool editable, bool undefined, HITEM after) +{ + HITEM it = AddItem(section, IT_DATETIME, name, &value, editable, undefined, after); + CItem* pItem = FindItem(it); + if (pItem) pItem->m_options.push_back(format); + return it; +} + +HITEM CPropertyGrid::AddFileItem(HSECTION section, string name, string value, string filter, bool editable, HITEM after) +{ + HITEM it = AddItem(section, IT_FILE, name, &value, editable, false, after); + CItem* pItem = FindItem(it); + if (pItem) pItem->m_options.push_back(filter); + return it; +} + +HITEM CPropertyGrid::AddFolderItem(HSECTION section, string name, string value, string title, bool editable, HITEM after) +{ + HITEM it = AddItem(section, IT_FOLDER, name, &value, editable, false, after); + CItem* pItem = FindItem(it); + if (pItem) pItem->m_options.push_back(title); + return it; +} + +HITEM CPropertyGrid::AddColorItem(HSECTION section, string name, COLORREF value, bool editable, bool undefined, HITEM after) +{ + return AddItem(section, IT_COLOR, name, &value, editable, undefined, after); +} + +HITEM CPropertyGrid::AddFontItem(HSECTION section, string name, LOGFONT value, bool editable, bool undefined, HITEM after) +{ + return AddItem(section, IT_FONT, name, &value, editable, undefined, after); +} + +void CPropertyGrid::ResetContents() +{ + m_sections.clear(); + m_section_id = 0; + m_item_id = 0; + RecalcLayout(); +} + +bool CPropertyGrid::RemoveSection(HSECTION hs) +{ + vector::iterator it = find(m_sections.begin(), m_sections.end(), hs); + if (it == m_sections.end()) return false; + m_sections.erase(it); + return true; +} + +bool CPropertyGrid::RemoveItem(HITEM item) +{ + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + vector::iterator it2 = find(it->m_items.begin(), it->m_items.end(), item); + if (it2 != it->m_items.end()) + { + it->m_items.erase(it2); + return true; + } + } + return false; +} + +int CPropertyGrid::GetNumSections() +{ + return int(m_sections.size()); +} + +int CPropertyGrid::GetSectionSize(HSECTION hs) +{ + CSection* pSection = FindSection(hs); + if (pSection) return int(pSection->m_items.size()); + return 0; +} + +void CPropertyGrid::ValidateChanges() +{ + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + for (vector::iterator it2 = it->m_items.begin(); it2 != it->m_items.end(); ++it2) + it2->ValidateChanges(); + } +} + +CPropertyGrid::CSection* CPropertyGrid::FindSection(HSECTION hs) const +{ + vector::const_iterator it = find(m_sections.begin(), m_sections.end(), hs); + if (it == m_sections.end()) return NULL; + return const_cast(&(*it)); +} + +CPropertyGrid::CItem* CPropertyGrid::FindItem(HITEM hi) const +{ + for (vector::const_iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + vector::const_iterator it2 = find(it->m_items.begin(), it->m_items.end(), hi); + if (it2 != it->m_items.end()) + return const_cast(&(*it2)); + } + return NULL; +} + +bool CPropertyGrid::GetItemValue(HITEM item, string& strValue) const +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + if (pItem->m_undefined) return false; + + // check + if (pItem->m_type == IT_STRING || pItem->m_type == IT_TEXT || pItem->m_type == IT_FILE || pItem->m_type == IT_FOLDER) + { + strValue = pItem->m_strValue; + return true; + } + else if (pItem->m_type == IT_COMBO) + { + if (pItem->m_nValue < 0 || pItem->m_nValue > int(pItem->m_options.size())-1) return false; + strValue = pItem->m_options[pItem->m_nValue]; + return true; + } + return false; +} + +bool CPropertyGrid::GetItemValue(HITEM item, int& nValue) const +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + if (pItem->m_undefined) return false; + + // check + if (pItem->m_type == IT_COMBO || pItem->m_type == IT_INTEGER) + { + nValue = pItem->m_nValue; + return true; + } + return false; +} + +bool CPropertyGrid::GetItemValue(HITEM item, double& dValue) const +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + if (pItem->m_undefined) return false; + + // check + if (pItem->m_type == IT_DOUBLE) + { + dValue = pItem->m_dValue; + return true; + } + return false; +} + +bool CPropertyGrid::GetItemValue(HITEM item, bool& bValue) const +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + if (pItem->m_undefined) return false; + + // check + if (pItem->m_type == IT_BOOLEAN) + { + bValue = pItem->m_bValue; + return true; + } + return false; +} + +bool CPropertyGrid::GetItemValue(HITEM item, COleDateTime& dtValue) const +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + if (pItem->m_undefined) return false; + + // check + if (pItem->m_type == IT_DATE || pItem->m_type == IT_DATETIME) + { + dtValue = pItem->m_dtValue; + return true; + } + return false; +} + +bool CPropertyGrid::GetItemValue(HITEM item, COLORREF& clrValue) const +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + if (pItem->m_undefined) return false; + + // check + if (pItem->m_type == IT_COLOR) + { + clrValue = pItem->m_clrValue; + return true; + } + return false; +} + +bool CPropertyGrid::GetItemValue(HITEM item, LOGFONT& lfValue) const +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + if (pItem->m_undefined) return false; + + // check + if (pItem->m_type == IT_FONT) + { + lfValue = pItem->m_lfValue; + return true; + } + return false; +} + +bool CPropertyGrid::SetItemValue(HITEM item, const string strValue) +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + + // check + if (pItem->m_type == IT_STRING || pItem->m_type == IT_TEXT || pItem->m_type == IT_FILE || pItem->m_type == IT_FOLDER) + { + pItem->m_strValue = strValue; + pItem->m_undefined = false; + Invalidate(); + return true; + } + else if (pItem->m_type == IT_COMBO) + { + for (size_t i=0; im_options.size(); i++) { + if (pItem->m_options.at(i) == strValue) { + return SetItemValue(item, (int) i); + } + } + } + return false; +} + +bool CPropertyGrid::SetItemValue(HITEM item, const int nValue) +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + + // check + if (pItem->m_type == IT_COMBO || pItem->m_type == IT_INTEGER) + { + pItem->m_nValue = nValue; + pItem->m_undefined = false; + Invalidate(); + return true; + } + return false; +} + +bool CPropertyGrid::SetItemValue(HITEM item, const double dValue) +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + + // check + if (pItem->m_type == IT_DOUBLE) + { + pItem->m_dValue = dValue; + pItem->m_undefined = false; + Invalidate(); + return true; + } + return false; +} + +bool CPropertyGrid::SetItemValue(HITEM item, const bool bValue) +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + + // check + if (pItem->m_type == IT_BOOLEAN) + { + pItem->m_bValue = bValue; + pItem->m_undefined = false; + Invalidate(); + return true; + } + return false; +} + +bool CPropertyGrid::SetItemValue(HITEM item, const COleDateTime dtValue) +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + + // check + if (pItem->m_type == IT_DATE || pItem->m_type == IT_DATETIME) + { + pItem->m_dtValue = dtValue; + pItem->m_undefined = false; + Invalidate(); + return true; + } + return false; +} + +bool CPropertyGrid::SetItemValue(HITEM item, const COLORREF clrValue) +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + + // check + if (pItem->m_type == IT_COLOR) + { + pItem->m_clrValue = clrValue; + pItem->m_undefined = false; + Invalidate(); + return true; + } + return false; +} + +bool CPropertyGrid::SetItemValue(HITEM item, const LOGFONT lfValue) +{ + // get the item + CItem* pItem = FindItem(item); + if (pItem == NULL) return false; + + // check + if (pItem->m_type == IT_FONT) + { + memcpy(&pItem->m_lfValue, &lfValue, sizeof(LOGFONT)); + pItem->m_undefined = false; + Invalidate(); + return true; + } + return false; +} + +int CPropertyGrid::GetTextMargin() +{ + return 2*margin; +} + +CFont* CPropertyGrid::GetFontNormal() +{ + return &m_fntNormal; +} + +CFont* CPropertyGrid::GetFontBold() +{ + return &m_fntBold; +} + +BEGIN_MESSAGE_MAP(CPropertyGrid, CWnd) + ON_WM_PAINT() + ON_WM_LBUTTONDOWN() + ON_WM_MOUSEMOVE() + ON_WM_CREATE() + ON_WM_LBUTTONUP() + ON_WM_VSCROLL() + ON_WM_ERASEBKGND() + ON_MESSAGE(WM_PG_COMBOSELCHANGED, OnComboSelChanged) + ON_MESSAGE(WM_PG_ENDLABELEDIT, OnEditChanged) + ON_MESSAGE(WM_PG_DATESELCHANGED, OnDateChanged) + ON_WM_LBUTTONDBLCLK() + ON_WM_MOUSEWHEEL() + ON_WM_DESTROY() + ON_WM_SIZE() + ON_WM_GETDLGCODE() + ON_WM_CHAR() + ON_WM_KEYDOWN() +END_MESSAGE_MAP() + +// +// creation and window stuff +// + +void CPropertyGrid::InitControl() +{ + // first gutter + CRect rc; + GetClientRect(&rc); + m_gutter_width = rc.Width()/2; + + // check if already done + if (m_fntNormal.GetSafeHandle() == NULL) + { + // fonts + LOGFONT lf; + if (GetParent() && GetParent()->GetFont()) + { + CFont* pFont = GetParent()->GetFont(); + pFont->GetLogFont(&lf); + m_fntNormal.CreateFontIndirect(&lf); + lf.lfWeight = FW_BOLD; + m_fntBold.CreateFontIndirect(&lf); + } + else + { + m_fntNormal.CreateStockObject(DEFAULT_GUI_FONT); + m_fntNormal.GetLogFont(&lf); + lf.lfWeight = FW_BOLD; + m_fntBold.CreateFontIndirect(&lf); + } + } + + // get line height + CDC* pDC = GetDC(); + CFont* pOldFont = pDC->SelectObject(&m_fntNormal); + m_line_height = pDC->GetTextExtent("Gg").cy + 2*margin; + pDC->SelectObject(pOldFont); + ReleaseDC(pDC); + + // styles + ModifyStyle(0, WS_CLIPCHILDREN); + + // try to get some strings + if (m_strOk.empty()) + { + m_strOk = "OK"; + if (GetParent() && GetParent()->GetDlgItem(IDOK)) + { + CString strOk; + GetParent()->GetDlgItem(IDOK)->GetWindowText(strOk); + m_strOk = strOk; + } + } + if (m_strCancel.empty()) + { + m_strCancel = "Cancel"; + if (GetParent() && GetParent()->GetDlgItem(IDCANCEL)) + { + CString strCancel; + GetParent()->GetDlgItem(IDCANCEL)->GetWindowText(strCancel); + m_strCancel = strCancel; + } + } +} + +int CPropertyGrid::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CWnd::OnCreate(lpCreateStruct) == -1) return -1; + InitControl(); + return 0; +} + +void CPropertyGrid::PreSubclassWindow() +{ + InitControl(); + CWnd::PreSubclassWindow(); +} + +void CPropertyGrid::OnDestroy() +{ + DeleteEditControl(); + CWnd::OnDestroy(); +} + +void CPropertyGrid::OnSize(UINT nType, int cx, int cy) +{ + CWnd::OnSize(nType, cx, cy); + + RecalcLayout(); + + if (m_scrollbar.GetSafeHwnd()) + { + CRect rect; + GetClientRect(&rect); + m_scrollbar.MoveWindow(rect.right - GetSystemMetrics(SM_CXVSCROLL), rect.top, GetSystemMetrics(SM_CXVSCROLL), rect.Height()); + } +} + +// +// painting +// + +BOOL CPropertyGrid::OnEraseBkgnd(CDC* pDC) +{ + return TRUE; +} + +bool item_alpha_sort(vector::iterator it1, vector::iterator it2) +{ + return (it1->m_name.compare(it2->m_name) < 0); +} + +void CPropertyGrid::OnPaint() +{ + // stuff needed + const int sign_size = 8; + + // the scrollbar offset + int top = GetScrollOffset(); + + // the rect + CRect rc_dummy; + GetClientRect(&rc_dummy); + if (m_scroll_enabled) + rc_dummy.right -= GetSystemMetrics(SM_CXVSCROLL); + + // make sure we do not modify this one + // because we use it to bitblt + const CRect rc(rc_dummy); + + // stuff for flicker free drawing + CDC dcMem; + CBitmap bmpMem; + CPaintDC dc(this); + + // create and configure the memdc + dcMem.CreateCompatibleDC(&dc); + bmpMem.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height()); + CBitmap* pOldBmp = dcMem.SelectObject(&bmpMem); + + // brush needed + CBrush brushTitle; + brushTitle.CreateSolidBrush(m_clrTitle); + + // pen needed + CPen penShade(PS_SOLID, 1, m_clrShade); + CPen penTitle(PS_SOLID, 1, m_clrTitle); + + // to make sure we won't leak gdi resources + CBrush* pOldBrush = dcMem.SelectObject(&brushTitle); + CPen* pOldPen = dcMem.SelectObject(&penShade); + CFont* pOldFont = dcMem.SelectObject(&m_fntNormal); + + // needed + int w = rc.Width(); + + // blank + dcMem.FillSolidRect(rc, m_clrBack); + dcMem.SetBkMode(TRANSPARENT); + + // empty text + if (m_sections.empty()) + { + CRect rect = rc; + rect.top += 10; + rect.DeflateRect(rect.Width()/4, 0); + dcMem.DrawText(m_strEmpty.c_str(), rect, DT_CENTER|DT_WORDBREAK|DT_NOPREFIX); + } + else + { + // needed + int sign_left = margin; + + // we start here + int y = -top; + + // alphabetical needs special + if (m_display_mode == DM_ALPHABETICAL) + { + // put all the items in a vector + vector::iterator> lst; + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + for (vector::iterator it2 = it->m_items.begin(); it2 != it->m_items.end(); ++it2) + lst.push_back(it2); + } + + // sort the vector + sort(lst.begin(), lst.end(), item_alpha_sort); + + // display the items + for (vector::iterator>::iterator it2 = lst.begin(); it2 != lst.end(); ++it2) + { + // first reset + (*it2)->m_rcName.SetRectEmpty(); + (*it2)->m_rcValue.SetRectEmpty(); + + // draw if visible + (*it2)->m_rcName = CRect(0, y, w, y+m_line_height); + CRect rcInter = (*it2)->m_rcName; + rcInter.IntersectRect(rc, rcInter); + if (!rcInter.IsRectEmpty()) + DrawItem(dcMem, w, sign_left+sign_size, y, *it2); + + // next line + y += m_line_height; + } + } + else + { + // next iterate on sections + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + // reset + it->m_rcSign.SetRectEmpty(); + it->m_rcTitle.SetRectEmpty(); + + // is visible? + it->m_rcTitle = CRect(0, y, w, y+m_line_height); + CRect rcInter = it->m_rcTitle; + rcInter.IntersectRect(rcInter, rc); + if (m_display_mode == DM_CATEGORIZED && !rcInter.IsRectEmpty()) + { + // first shade rect + if (m_shade_titles) + dcMem.FillSolidRect(0, y, w, m_line_height, m_clrShade); + + // now draw a separator lines + if (m_draw_lines) + { + dcMem.SelectObject(&penShade); + dcMem.MoveTo(0, y); + dcMem.LineTo(w+1, y); + dcMem.MoveTo(0, y+m_line_height); + dcMem.LineTo(w+1, y+m_line_height); + } + + // now draw gutter + if (m_draw_gutter) + { + dcMem.SelectObject(&penShade); + dcMem.MoveTo(m_gutter_width, y); + dcMem.LineTo(m_gutter_width, y+m_line_height+1); + } + + // now draw collapse sign + int sign_top = y+margin+2; + dcMem.SelectObject(&penTitle); + it->m_rcSign = CRect(sign_left, sign_top, sign_left+sign_size+1, sign_top+sign_size+1); + dcMem.FrameRect(it->m_rcSign, &brushTitle); + dcMem.MoveTo(sign_left+2, sign_top+sign_size/2); + dcMem.LineTo(sign_left+2+sign_size/2+1, sign_top+sign_size/2); + if (it->m_collapsed) + { + dcMem.MoveTo(sign_left+sign_size/2, sign_top+2); + dcMem.LineTo(sign_left+sign_size/2, sign_top+2+sign_size/2+1); + } + + // prepare draw text + int title_left = sign_left+sign_size+2*margin; + int title_top = y; + dcMem.SelectObject(&m_fntBold); + it->m_rcTitle = CRect(title_left, title_top, w, title_top+m_line_height); + + // draw focus rect + if (m_focused_section == it->m_id) + { + CSize sz = dcMem.GetTextExtent(it->m_title.c_str()); + int rect_left = title_left; + int rect_top = title_top+(m_line_height-sz.cy)/2; + int rect_width = sz.cx+3*margin; + int rect_height = sz.cy; + dcMem.DrawFocusRect(CRect(rect_left, rect_top, rect_left+rect_width, rect_top+rect_height)); + } + + // now draw text + dcMem.SetTextColor(m_clrTitle); + dcMem.DrawText(it->m_title.c_str(), CRect(title_left+GetTextMargin(), title_top, w, title_top+m_line_height), DT_END_ELLIPSIS|DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX); + } + + // next line + if (m_display_mode == DM_CATEGORIZED) + y+=m_line_height; + + // iterate on items + if (!it->m_collapsed || m_display_mode != DM_CATEGORIZED) + { + for (vector::iterator it2 = it->m_items.begin(); it2 != it->m_items.end(); ++it2) + { + // reset + it2->m_rcName.SetRectEmpty(); + it2->m_rcValue.SetRectEmpty(); + + // is visible? + it2->m_rcName = CRect(0, y, w, y+m_line_height); + CRect rcInter = it2->m_rcName; + rcInter.IntersectRect(rc, rcInter); + if (!rcInter.IsRectEmpty()) + DrawItem(dcMem, w, sign_left+sign_size, y, it2); + + // next line + y+=m_line_height; + } + } + } + } + } + + // Blt the changes to the screen DC. + dc.BitBlt(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, &dcMem, 0, 0, SRCCOPY); + + // Done with off-screen bitmap and DC. + dcMem.SelectObject(pOldBmp); + dcMem.SelectObject(pOldFont); + dcMem.SelectObject(pOldPen); + dcMem.SelectObject(pOldBrush); + bmpMem.DeleteObject(); + dcMem.DeleteDC(); + + // Validate All + ValidateRgn(NULL); + ValidateRect(NULL); +} + +void CPropertyGrid::DrawItem(CDC& dc, int w, int x, int y, vector::iterator& it) +{ + // brush needed + CBrush brushText; + brushText.CreateSolidBrush(m_clrText); + + // pen needed + CPen penShade(PS_SOLID, 1, m_clrShade); + + // to make sure we won't leak gdi resources + CBrush* pOldBrush = dc.SelectObject(&brushText); + CPen* pOldPen = dc.SelectObject(&penShade); + CFont* pOldFont = dc.SelectObject(&m_fntNormal); + + // first shade rect + if (m_shade_titles) + dc.FillSolidRect(0, y, x+2*margin, m_line_height, m_clrShade); + + // now draw a separator line + if (m_draw_lines) + { + dc.SelectObject(&penShade); + dc.MoveTo(0, y+m_line_height); + dc.LineTo(w+1, y+m_line_height); + } + + // now draw gutter + if (m_draw_gutter) + { + dc.SelectObject(&penShade); + dc.MoveTo(m_gutter_width, y); + dc.LineTo(m_gutter_width, y+m_line_height+1); + } + + // needed + int name_left = x+2*margin+GetTextMargin(); + int name_right = m_gutter_width-1; + int value_left = m_gutter_width; + int value_right = w; + + // is being edited? + if (m_focused_item == it->m_id && it->m_editable && GetEditMode(*it) != EM_CUSTOM) + { + value_right -= m_line_height; + + // the rect of the button + m_rect_button = CRect(w-m_line_height, y, w, y+m_line_height); + + UINT pushed = m_button_depressed?DFCS_PUSHED:0; + + // now draw the button + switch (GetEditMode(*it)) + { + case EM_MODAL: + // draw a button + dc.DrawFrameControl(m_rect_button, DFC_BUTTON, DFCS_BUTTONPUSH|pushed); + dc.SelectObject(&m_fntBold); + dc.DrawText("...", m_rect_button, DT_CENTER|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX); + break; + + case EM_DROPDOWN: + // draw an arrow + dc.DrawFrameControl(m_rect_button, DFC_SCROLL, DFCS_SCROLLDOWN|pushed); + break; + + case EM_INPLACE: + // whole area is edit + m_rect_button.left = m_gutter_width; + break; + + default: + assert(false); + } + } + + // update the rects + it->m_rcName = CRect(0, y, m_gutter_width, y+m_line_height); + it->m_rcValue = CRect(value_left, y, value_right, y+m_line_height); + CRect rcValue = it->m_rcValue; + rcValue.left += GetTextMargin(); + + // focused + if (m_focused_item == it->m_id) + { + int rect_left = name_left-2*margin; + int rect_right = name_right; + dc.FillSolidRect(rect_left, y, rect_right-rect_left+1, m_line_height, m_clrFocus); + dc.SetTextColor(m_clrHilite); + } + else + { + dc.SetTextColor(m_clrText); + } + + // put name and value + dc.SelectObject(&m_fntNormal); + dc.DrawText(it->m_name.c_str(), -1, CRect(name_left, y, name_right, y+m_line_height), DT_END_ELLIPSIS|DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX); + + // get back to normal text + if (it->m_editable) dc.SetTextColor(m_clrEditable); + else dc.SetTextColor(m_clrDisabled); + + // custom item + if (it->m_type == IT_CUSTOM) + { + int save = dc.SaveDC(); + it->m_pCustom->DrawItem(dc, it->m_rcValue, m_focused_item == it->m_id); + dc.RestoreDC(save); + } + else + { + // modified flag + bool modified = (it->m_strValue != it->m_strValue_old); + + // now draw text + string strValue = it->m_strValue; + switch (it->m_type) + { + case IT_TEXT: + { + size_t j; + for (;( j = strValue.find( "\r\n" )) != string::npos;) + strValue.replace( j, 2, "�"); + break; + } + + case IT_INTEGER: + { + CString strTemp; + string strFormat = "%d"; + if (it->m_options.size() && !it->m_options.front().empty()) strFormat = it->m_options.front(); + strTemp.Format(strFormat.c_str(), it->m_nValue); + strValue = LPCTSTR(strTemp); + modified = (it->m_nValue != it->m_nValue_old); + break; + } + + case IT_DOUBLE: + { + CString strTemp; + string strFormat = "%g"; + if (it->m_options.size() && !it->m_options.front().empty()) strFormat = it->m_options.front(); + strTemp.Format(strFormat.c_str(), it->m_dValue); + strValue = LPCTSTR(strTemp); + modified = (it->m_dValue != it->m_dValue_old); + break; + } + + case IT_DATE: + { + CString strTemp; + if (it->m_options.size() && !it->m_options.front().empty()) strTemp = it->m_dtValue.Format(it->m_options.front().c_str()); + else strTemp = it->m_dtValue.Format(VAR_DATEVALUEONLY); + strValue = LPCTSTR(strTemp); + modified = (it->m_dtValue != it->m_dtValue_old); + break; + } + + case IT_DATETIME: + { + CString strTemp; + if (it->m_options.size() && !it->m_options.front().empty()) strTemp = it->m_dtValue.Format(it->m_options.front().c_str()); + else strTemp = it->m_dtValue.Format(); + strValue = LPCTSTR(strTemp); + modified = (it->m_dtValue != it->m_dtValue_old); + break; + } + + case IT_BOOLEAN: + { + strValue = it->m_bValue ? m_strTrue : m_strFalse; + modified = (it->m_bValue != it->m_bValue_old); + break; + } + + case IT_COMBO: + { + if (it->m_nValue>=0 && it->m_nValuem_options.size())) + strValue = it->m_options[it->m_nValue]; + modified = (it->m_nValue != it->m_nValue_old); + break; + } + + case IT_FILE: + case IT_FOLDER: + { + TCHAR szBuffer[1024]; + _tcsncpy(szBuffer, strValue.c_str(), 1024); + PathCompactPath(dc.GetSafeHdc(), szBuffer, rcValue.Width()); + strValue = szBuffer; + break; + } + + case IT_COLOR: + { + // draw a sample rectangle + CRect rc = rcValue; + rc.DeflateRect(0,2,0,2); + rc.top++; + rc.right = rc.left + m_line_height; + dc.FrameRect(rc, &brushText); + rc.DeflateRect(1,1); + dc.FillSolidRect(rc, it->m_clrValue); + rcValue.left = rc.right + 3*margin; + + // update the text + CString strTemp; + strTemp.Format("%d; %d; %d", GetRValue(it->m_clrValue), GetGValue(it->m_clrValue), GetBValue(it->m_clrValue)); + strValue = LPCTSTR(strTemp); + modified = (it->m_clrValue != it->m_clrValue_old); + break; + } + + case IT_FONT: + { + CString strTemp; + strTemp.Format("%s; %dpt", it->m_lfValue.lfFaceName, -MulDiv(it->m_lfValue.lfHeight, 72, dc.GetDeviceCaps(LOGPIXELSY))); + strValue = LPCTSTR(strTemp); + modified = (memcmp(&it->m_lfValue, &it->m_lfValue_old, sizeof(LOGFONT))!=0); + break; + } + } + + // we must also take undefined state change into account + modified |= (it->m_undefined != it->m_undefined_old); + + // set proper font + if (modified && m_bold_modified) dc.SelectObject(&m_fntBold); + else if (it->m_editable && m_bold_editables) dc.SelectObject(&m_fntBold); + else dc.SelectObject(&m_fntNormal); + + // now draw it + if (it->m_undefined) strValue = m_strUndefined; + dc.DrawText(strValue.c_str(), -1, rcValue, DT_END_ELLIPSIS|DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX); + } + + // clean up + dc.SelectObject(pOldFont); + dc.SelectObject(pOldPen); + dc.SelectObject(pOldBrush); +} + +// +// mouse interaction +// + +void CPropertyGrid::OnLButtonDown(UINT nFlags, CPoint point) +{ + // destroy edit + SetFocus(); + DeleteEditControl(); + + // click on button? + if (m_rect_button.PtInRect(point)) + { + m_button_pushed = true; + m_button_depressed = true; + SetCapture(); + Invalidate(); + return; + } + + // click on button? + if (m_focused_item != -1) + { + CItem* pItem = FindItem(m_focused_item); + if ( pItem && pItem->m_type == IT_CUSTOM + && GetEditMode(*pItem) == EM_CUSTOM + && pItem->m_pCustom->OnLButtonDown(pItem->m_rcValue, point)) + { + m_custom_tracking = true; + SetCapture(); + Invalidate(); + return; + } + } + + // resizing gutter? + if (abs(point.x-m_gutter_width)<3) + { + ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE)); + m_resizing_gutter = true; + m_ptLast = point; + SetCapture(); + Invalidate(); + return; + } + + // disable focus + m_focused_item = -1; + m_focused_section = -1; + m_rect_button.SetRectEmpty(); + + // did we click on a section + if (m_display_mode == DM_CATEGORIZED) + { + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + if (it->m_rcSign.PtInRect(point)) + { + it->m_collapsed = !it->m_collapsed; + m_focused_section = it->m_id; + RecalcLayout(); + return; + } + else if (it->m_rcTitle.PtInRect(point)) + { + m_focused_section = it->m_id; + Invalidate(); + return; + } + } + } + + // focus + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + if (!it->m_collapsed || m_display_mode != DM_CATEGORIZED) + { + for (vector::iterator it2 = it->m_items.begin(); it2 != it->m_items.end(); ++it2) + { + if (it2->m_rcName.PtInRect(point) || it2->m_rcValue.PtInRect(point)) + { + if (it2->m_editable || m_focus_disabled) + { + m_focused_item = it2->m_id; + GetOwner()->SendMessage(WM_PG_SELECTIONCHANGED, it2->m_id); + if (it2->m_rcValue.PtInRect(point)) + m_value_clicked = (GetEditMode(*it2) == EM_INPLACE || GetEditMode(*it2) == EM_DROPDOWN); + Invalidate(); + return; + } + } + } + } + } + + CWnd::OnLButtonDown(nFlags, point); + Invalidate(); +} + +void CPropertyGrid::OnLButtonDblClk(UINT nFlags, CPoint point) +{ + if (m_focused_item != -1) + { + CItem* pItem = FindItem(m_focused_item); + if (pItem) + { + if (pItem->m_type == IT_BOOLEAN) + { + if (!pItem->m_undefined) + { + pItem->m_bValue = !pItem->m_bValue; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + Invalidate(); + } + } + else if (pItem->m_type == IT_COMBO) + { + if (!pItem->m_undefined) + { + pItem->m_nValue = (pItem->m_nValue+1)%int(pItem->m_options.size()); + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + Invalidate(); + } + } + else if (GetEditMode(*pItem) == EM_MODAL) + { + EditFocusedItem(); + } + } + } + else if (m_focused_section != -1) + { + CSection* pSection = FindSection(m_focused_section); + if (pSection) + { + pSection->m_collapsed = !pSection->m_collapsed; + Invalidate(); + } + } + + CWnd::OnLButtonDblClk(nFlags, point); +} + +void CPropertyGrid::OnMouseMove(UINT nHitTest, CPoint point) +{ + if (m_custom_tracking) + { + CItem* pItem = FindItem(m_focused_item); + if (pItem) + { + pItem->m_pCustom->OnMouseMove(pItem->m_rcValue, point); + Invalidate(); + } + } + else if (m_button_pushed) + { + m_button_depressed = m_rect_button.PtInRect(point)?true:false; + Invalidate(); + } + else if (m_resizing_gutter) + { + ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE)); + m_gutter_width += point.x-m_ptLast.x; + CRect rc; + GetClientRect(&rc); + if (m_gutter_width4*rc.Width()/5) m_gutter_width = 4*rc.Width()/5; + m_ptLast = point; + Invalidate(); + } + else if (!m_control) + { + if (abs(point.x-m_gutter_width)<3) ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE)); + else ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); + } + + CWnd::OnMouseMove(nHitTest, point); +} + +void CPropertyGrid::OnLButtonUp(UINT nFlags, CPoint point) +{ + if (m_custom_tracking) + { + m_custom_tracking = false; + ReleaseCapture(); + Invalidate(); + CItem* pItem = FindItem(m_focused_item); + if (pItem) + pItem->m_pCustom->OnLButtonUp(pItem->m_rcValue, point); + } + else if (m_button_pushed || m_value_clicked) + { + m_button_pushed = false; + m_button_depressed = false; + ReleaseCapture(); + Invalidate(); + + if (m_rect_button.PtInRect(point) || (m_value_clicked && m_focused_item != -1 && FindItem(m_focused_item) && FindItem(m_focused_item)->m_rcValue.PtInRect(point))) + { + m_value_clicked = false; + CItem* pItem = FindItem(m_focused_item); + if (pItem) + { + if (GetEditMode(*pItem) == EM_DROPDOWN) + { + if (pItem->m_type == IT_CUSTOM) + { + CRect rc = m_rect_button; + rc.left = m_gutter_width; + pItem->m_pCustom->ShowDropDown(rc); + } + else if (pItem->m_type == IT_DATE) + { + // the calendar rect + CRect rc = m_rect_button; + rc.left = m_gutter_width; + rc.top += m_line_height; + rc.bottom = rc.top + 100; + ClientToScreen(&rc); + + // create it + m_control = new CPropertyGridMonthCalCtrl; + CPropertyGridMonthCalCtrl* mc = (CPropertyGridMonthCalCtrl*) m_control; + mc->CreateEx(0, MONTHCAL_CLASS, NULL, WS_POPUP|WS_BORDER, rc, GetParent(), 0); + mc->SetCurSel(pItem->m_dtValue); + mc->SetOwner(this); + mc->SizeMinReq(); + + // now position it + CRect rc2; + mc->GetWindowRect(&rc2); + rc2.OffsetRect(rc.right-rc2.right, 0); + mc->SetWindowPos(NULL, rc2.left, rc2.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE|SWP_SHOWWINDOW); + } + else + { + // the combo rect + CRect rc = m_rect_button; + rc.left = m_gutter_width; + rc.top += m_line_height; + rc.bottom = rc.top + 100; + + // create it + m_control = new CPropertyGridCombo(); + CPropertyGridCombo* pCombo = (CPropertyGridCombo*)m_control; + pCombo->Create(WS_CHILD, rc, this, 0); + pCombo->SetColors(m_clrBack, m_clrText, m_clrFocus, m_clrHilite); + pCombo->SetFont(&m_fntNormal); + + if (pItem->m_type == IT_BOOLEAN) + { + pCombo->AddString(m_strTrue); + pCombo->AddString(m_strFalse); + if (!pItem->m_undefined) + pCombo->SetCurSel(pItem->m_bValue?0:1); + } + else + { + for (vector::iterator it = pItem->m_options.begin(); it != pItem->m_options.end(); ++it) + pCombo->AddString(*it); + if (!pItem->m_undefined) + pCombo->SetCurSel(pItem->m_nValue); + } + pCombo->ShowWindow(SW_SHOW); + } + } + else if (GetEditMode(*pItem) == EM_INPLACE) + { + // the in-place edit rect + CRect rc = m_rect_button; + rc.left++; + rc.top += margin; + + // the value + string strValue; + if (pItem->m_type == IT_STRING) + { + strValue = pItem->m_strValue; + } + else if (pItem->m_type == IT_INTEGER) + { + if (!pItem->m_undefined) + { + CString strTemp; + strTemp.Format("%d", pItem->m_nValue); + strValue = LPCTSTR(strTemp); + } + } + else if (pItem->m_type == IT_DOUBLE) + { + if (!pItem->m_undefined) + { + CString strTemp; + strTemp.Format("%g", pItem->m_dValue); + strValue = LPCTSTR(strTemp); + } + } + else if (pItem->m_type == IT_CUSTOM) + { + strValue = pItem->m_pCustom->GetStringForInPlaceEdit(); + } + else + { + assert(false); + } + + // create it + m_control = new CPropertyGridInPlaceEdit(this, rc, WS_CHILD, 1000, strValue); + CPropertyGridInPlaceEdit* pEdit = (CPropertyGridInPlaceEdit*)m_control; + pEdit->SetColors(m_clrBack, m_clrText); + pEdit->SetFont(&m_fntNormal); + pEdit->ShowWindow(SW_SHOW); + } + else if (GetEditMode(*pItem) == EM_MODAL) + { + EditFocusedItem(); + } + else if (GetEditMode(*pItem) == EM_CUSTOM) + { + pItem->m_pCustom->OnLButtonUp(pItem->m_rcValue, point); + } + else + { + assert(false); + } + } + } + } + else if (m_resizing_gutter) + { + ReleaseCapture(); + m_resizing_gutter = false; + ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); + } + + CWnd::OnLButtonUp(nFlags, point); +} + +// +// keyboard interaction +// + +UINT CPropertyGrid::OnGetDlgCode() +{ + return CWnd::OnGetDlgCode()|DLGC_WANTCHARS|DLGC_WANTARROWS; +} + +void CPropertyGrid::MoveForward(HSECTION& focused_section, HITEM& focused_item) +{ + for (int pass = 0; pass<2; pass++) + { + bool found = false; + + bool stop_on_next_valid = false; + if (focused_section == -1 && focused_item == -1) + stop_on_next_valid = true; + + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + if (m_display_mode == DM_CATEGORIZED) + { + if (it->m_id == focused_section) + { + stop_on_next_valid = true; + } + else if (stop_on_next_valid) + { + focused_section = it->m_id; + focused_item = -1; + found = true; + break; + } + } + + if (!it->m_collapsed || m_display_mode != DM_CATEGORIZED) + { + for (vector::iterator it2 = it->m_items.begin(); it2 != it->m_items.end(); ++it2) + { + if (it2->m_id == focused_item) + { + stop_on_next_valid = true; + } + else if (stop_on_next_valid) + { + if (it2->m_editable || m_focus_disabled) + { + focused_section = -1; + focused_item = it2->m_id; + found = true; + break; + } + } + } + + if (found) + break; + } + } + + if (found) + break; + + focused_section = -1; + focused_item = -1; + } +} + +void CPropertyGrid::FocusNextItem() +{ + // simple move forward + MoveForward(m_focused_section, m_focused_item); + + // ensure visible + CRect rc(0,0,0,0); + if (m_focused_section != -1 && FindSection(m_focused_section)) rc = FindSection(m_focused_section)->m_rcTitle; + else if (m_focused_item != -1 && FindItem(m_focused_item)) rc = FindItem(m_focused_item)->m_rcName; + if (!rc.IsRectEmpty()) + { + CRect rect; + GetClientRect(&rect); + rect.IntersectRect(rc, rect); + if (rect.Height() != m_line_height) + OnVScroll(SB_THUMBPOSITION, rc.top, &m_scrollbar); + } + + // done + Invalidate(); +} + +void CPropertyGrid::FocusPrevItem() +{ + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + if (m_display_mode == DM_CATEGORIZED) + { + HSECTION focused_section = it->m_id; + HITEM focused_item = -1; + MoveForward(focused_section, focused_item); + if (focused_section == m_focused_section && focused_item == m_focused_item) + { + m_focused_section = it->m_id; + m_focused_item = -1; + break; + } + } + + if (!it->m_collapsed || m_display_mode != DM_CATEGORIZED) + { + bool found = false; + for (vector::iterator it2 = it->m_items.begin(); it2 != it->m_items.end(); ++it2) + { + if (!it2->m_editable && !m_focus_disabled) + continue; + + HSECTION focused_section = -1; + HITEM focused_item = it2->m_id; + MoveForward(focused_section, focused_item); + if (focused_section == m_focused_section && focused_item == m_focused_item) + { + m_focused_section = -1; + m_focused_item = it2->m_id; + GetOwner()->SendMessage(WM_PG_SELECTIONCHANGED, it2->m_id); + found = true; + break; + } + } + + if (found) + break; + } + } + + // ensure visible + CRect rc(0,0,0,0); + if (m_focused_section != -1 && FindSection(m_focused_section)) rc = FindSection(m_focused_section)->m_rcTitle; + else if (m_focused_item != -1 && FindItem(m_focused_item)) rc = FindItem(m_focused_item)->m_rcName; + if (!rc.IsRectEmpty()) + { + CRect rect; + GetClientRect(&rect); + rect.IntersectRect(rc, rect); + if (rect.Height() != m_line_height) + OnVScroll(SB_THUMBPOSITION, rc.top, &m_scrollbar); + } + + // done + Invalidate(); +} + +void CPropertyGrid::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if (nChar == '*') + { + ExpandAll(true); + } + else if (nChar == '/') + { + ExpandAll(false); + } + else if (nChar == '+' || nChar == '-') + { + if (m_focused_section != -1) + { + CSection* pSection = FindSection(m_focused_section); + if (pSection) pSection->m_collapsed = (nChar=='-'); + RecalcLayout(); + } + } + + CWnd::OnChar(nChar, nRepCnt, nFlags); +} + +void CPropertyGrid::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if (nChar == VK_DOWN) + { + FocusNextItem(); + } + else if (nChar == VK_UP) + { + FocusPrevItem(); + } + else if (nChar == VK_LEFT) + { + if (m_focused_section != -1 && FindSection(m_focused_section) && FindSection(m_focused_section)->m_collapsed == false) + { + ExpandSection(m_focused_section, false); + } + else + { + FocusPrevItem(); + } + } + else if (nChar == VK_RIGHT) + { + if (m_focused_section != -1 && FindSection(m_focused_section) && FindSection(m_focused_section)->m_collapsed == true) + { + ExpandSection(m_focused_section, true); + } + else + { + FocusNextItem(); + } + } + + CWnd::OnKeyDown(nChar, nRepCnt, nFlags); +} + +// +// scrolling +// + +void CPropertyGrid::RecalcLayout() +{ + // save current scroll offset + int offset = GetScrollOffset(); + + // total height + int height = 0; + for (vector::iterator it = m_sections.begin(); it != m_sections.end(); ++it) + { + if (m_display_mode == DM_CATEGORIZED) + height += m_line_height; + if (!it->m_collapsed || m_display_mode != DM_CATEGORIZED) + height += int(it->m_items.size())*m_line_height; + } + + // client rect + CRect rc; + GetClientRect(&rc); + if (height < rc.Height()) + { + if (m_scrollbar.GetSafeHwnd() != NULL) + { + m_scrollbar.EnableScrollBar(ESB_DISABLE_BOTH); + m_scrollbar.ShowScrollBar(FALSE); + } + m_scroll_enabled = false; + } + else + { + if (m_scrollbar.GetSafeHwnd() == NULL) + { + CRect rect = rc; + rect.left = rect.right - GetSystemMetrics(SM_CXVSCROLL); + m_scrollbar.Create(WS_CHILD|SBS_VERT, rect, this, 1000); + } + + m_scrollbar.EnableScrollBar(ESB_ENABLE_BOTH); + + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_ALL; + info.nMin = 0; + info.nMax = height; + info.nPage = rc.Height(); + info.nPos = min(offset, height); + info.nTrackPos = 2; + m_scrollbar.SetScrollInfo(&info); + + m_scrollbar.ShowScrollBar(); + m_scroll_enabled = true; + } + + if (GetSafeHwnd()) + Invalidate(); +} + +int CPropertyGrid::GetScrollOffset() +{ + if (m_scrollbar && m_scrollbar.IsWindowEnabled() == TRUE) + return m_scrollbar.GetScrollPos(); + return 0; +} + +void CPropertyGrid::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + // check + if (!m_scroll_enabled) return; + if (pScrollBar != &m_scrollbar) return; + if (nSBCode == SB_ENDSCROLL) return; + + // set focus to us + SetFocus(); + + // get the scroll info + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_ALL; + m_scrollbar.GetScrollInfo(&info); + int min = info.nMin; + int pos = info.nPos; + int max = info.nMax-info.nPage; + + // the entire rect + CRect rect; + GetClientRect(&rect); + int h = rect.Height(); + + // the rect without the scrollbar + CRect rc(0,0,rect.right-GetSystemMetrics(SM_CXVSCROLL),rect.bottom); + + switch(nSBCode) + { + case SB_TOP: + pScrollBar->SetScrollPos(min); + break; + + case SB_BOTTOM: + pScrollBar->SetScrollPos(max); + break; + + case SB_LINEDOWN: + if (pos+m_line_height>=max) pScrollBar->SetScrollPos(max); + else pScrollBar->SetScrollPos(pos+m_line_height); + break; + + case SB_LINEUP: + if (pos-m_line_height<=min) pScrollBar->SetScrollPos(min); + else pScrollBar->SetScrollPos(pos-m_line_height); + break; + + case SB_PAGEDOWN: + if (pos+h>=max) pScrollBar->SetScrollPos(max); + else pScrollBar->SetScrollPos(pos+h); + break; + + case SB_PAGEUP: + if (pos-h<=min) pScrollBar->SetScrollPos(min); + else pScrollBar->SetScrollPos(pos-h); + break; + + case SB_THUMBPOSITION: + case SB_THUMBTRACK: + int diff = nPos - pos; + if (diff == 0) return; + if (pos <= min && diff<0) return; + if (pos >= max && diff>0) return; + pScrollBar->SetScrollPos(nPos); + } + + Invalidate(); + + CWnd::OnVScroll(nSBCode, nPos, pScrollBar); + +} + +BOOL CPropertyGrid::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) +{ + /*int steps = abs(zDelta)/WHEEL_DELTA; + for (int i=0; i<3*steps; i++) + { + if (zDelta>0) OnVScroll(SB_LINEUP, 0, &m_scrollbar); + if (zDelta<0) OnVScroll(SB_LINEDOWN, 0, &m_scrollbar); + }*/ + if (zDelta>0) OnVScroll(SB_LINEUP, 0, &m_scrollbar); + if (zDelta<0) OnVScroll(SB_LINEDOWN, 0, &m_scrollbar); + + return CWnd::OnMouseWheel(nFlags, zDelta, pt); +} + +// +// editing +// + +CPropertyGrid::EEditMode CPropertyGrid::GetEditMode(CItem& item) +{ + switch (item.m_type) + { + case IT_CUSTOM: + return item.m_pCustom->GetEditMode(); + + case IT_STRING: + case IT_INTEGER: + case IT_DOUBLE: + return EM_INPLACE; + + case IT_COMBO: + case IT_BOOLEAN: + case IT_DATE: + return EM_DROPDOWN; + + case IT_TEXT: + case IT_DATETIME: + case IT_FILE: + case IT_FOLDER: + case IT_COLOR: + case IT_FONT: + return EM_MODAL; + + default: + assert(false); + return EM_CUSTOM; + } +} + +void CPropertyGrid::DeleteEditControl() +{ + // destroy edit + if (m_control) + { + if (m_control->GetSafeHwnd()) + m_control->DestroyWindow(); + delete m_control; + m_control = NULL; + } +} + +LRESULT CPropertyGrid::OnComboSelChanged(WPARAM wParam, LPARAM lParam) +{ + CItem* pItem = FindItem(m_focused_item); + if (pItem) + { + if (pItem->m_type == IT_BOOLEAN) + { + pItem->m_bValue = (wParam == 0); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + DeleteEditControl(); + Invalidate(); + } + else if (pItem->m_type == IT_COMBO) + { + pItem->m_nValue = int(wParam); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + DeleteEditControl(); + Invalidate(); + } + else + { + assert(false); + } + } + return 0; +} + +LRESULT CPropertyGrid::OnEditChanged(WPARAM wParam, LPARAM lParam) +{ + CItem* pItem = FindItem(m_focused_item); + if (pItem) + { + if (pItem->m_type == IT_STRING) + { + pItem->m_strValue = string((char*)wParam); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + DeleteEditControl(); + Invalidate(); + } + else if (pItem->m_type == IT_INTEGER) + { + if (strlen((char*)wParam)) + { + pItem->m_nValue = atoi((char*)wParam); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + } + DeleteEditControl(); + Invalidate(); + } + else if (pItem->m_type == IT_DOUBLE) + { + if (strlen((char*)wParam)) + { + pItem->m_dValue = atof((char*)wParam); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + } + DeleteEditControl(); + Invalidate(); + } + else if (pItem->m_type == IT_CUSTOM) + { + if (pItem->m_pCustom->OnItemEdited(string((char*)wParam))) + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + DeleteEditControl(); + Invalidate(); + } + else + { + assert(false); + } + } + return 0; +} + +LRESULT CPropertyGrid::OnDateChanged(WPARAM wParam, LPARAM lParam) +{ + CItem* pItem = FindItem(m_focused_item); + if (pItem) + { + if (pItem->m_type == IT_DATE) + { + CPropertyGridMonthCalCtrl* mc = (CPropertyGridMonthCalCtrl*) m_control; + mc->GetCurSel(pItem->m_dtValue); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + DeleteEditControl(); + Invalidate(); + + } + else + { + assert(false); + } + } + return 0; +} + +void CPropertyGrid::EditFocusedItem() +{ + CItem* pItem = FindItem(m_focused_item); + if (pItem) + { + if (!pItem->m_editable) + return; + + if (pItem->m_type == IT_TEXT) + { + CDynDialogEx dlg(GetParent()); + dlg.SetUseSystemButtons(FALSE); + dlg.SetWindowTitle(pItem->m_name.c_str()); + dlg.SetFont(&m_fntNormal); + CString strValue = pItem->m_strValue.c_str(); + dlg.AddDlgControl("EDIT", pItem->m_strValue.c_str(), STYLE_EDIT|WS_VSCROLL|WS_HSCROLL|ES_AUTOHSCROLL|ES_AUTOVSCROLL|ES_LEFT|ES_MULTILINE|ES_WANTRETURN, EXSTYLE_EDIT, CRect(7, 7, 200, 100), (void*) &strValue); + dlg.AddDlgControl("BUTTON", m_strOk.c_str(), STYLE_BUTTON, EXSTYLE_BUTTON, CRect(56, 106, 106, 120), NULL, IDOK); + dlg.AddDlgControl("BUTTON", m_strCancel.c_str(), STYLE_BUTTON, EXSTYLE_BUTTON, CRect(110, 106, 160, 120), NULL, IDCANCEL); + if (dlg.DoModal() == IDOK) + { + pItem->m_strValue = LPCTSTR(strValue); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + Invalidate(); + } + } + else if (pItem->m_type == IT_DATETIME) + { + CDynDialogEx dlg(GetParent()); + dlg.SetUseSystemButtons(FALSE); + dlg.SetWindowTitle(pItem->m_name.c_str()); + dlg.SetFont(&m_fntNormal); + COleDateTime dtValueDate = pItem->m_dtValue; + CTime dtValueTime(pItem->m_dtValue.GetYear(), pItem->m_dtValue.GetMonth(), pItem->m_dtValue.GetDay(), pItem->m_dtValue.GetHour(), pItem->m_dtValue.GetMinute(), pItem->m_dtValue.GetSecond()); + dlg.AddDlgControl("STATIC", m_strDate.c_str(), STYLE_STATIC, EXSTYLE_STATIC, CRect(7, 3, 60, 12)); + dlg.AddDlgControl("STATIC", m_strTime.c_str(), STYLE_STATIC, EXSTYLE_STATIC, CRect(67, 3, 120, 12)); + dlg.AddDlgControl("SysDateTimePick32", "", STYLE_DATETIMEPICKER|DTS_SHORTDATEFORMAT, EXSTYLE_DATETIMEPICKER, CRect(7, 13, 60, 26), (void*) &dtValueDate); + dlg.AddDlgControl("SysDateTimePick32", "", STYLE_DATETIMEPICKER|DTS_TIMEFORMAT , EXSTYLE_DATETIMEPICKER, CRect(67, 13, 120, 26), (void*) &dtValueTime); + dlg.AddDlgControl("BUTTON", m_strOk.c_str(), STYLE_BUTTON, EXSTYLE_BUTTON, CRect(7, 37, 60, 51), NULL, IDOK); + dlg.AddDlgControl("BUTTON", m_strCancel.c_str(), STYLE_BUTTON, EXSTYLE_BUTTON, CRect(67, 37, 120, 51), NULL, IDCANCEL); + if (dlg.DoModal() == IDOK) + { + pItem->m_dtValue.SetDateTime(dtValueDate.GetYear(), dtValueDate.GetMonth(), dtValueDate.GetDay(), + dtValueTime.GetHour(), dtValueTime.GetMinute(), dtValueTime.GetSecond()); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + Invalidate(); + } + } + else if (pItem->m_type == IT_COLOR) + { + CColorDialog dlg(pItem->m_clrValue, 0, GetParent()); + if (dlg.DoModal() == IDOK) + { + pItem->m_clrValue = dlg.GetColor(); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + Invalidate(); + } + } + else if (pItem->m_type == IT_FILE) + { + CFileDialog dlg(TRUE, NULL, pItem->m_strValue.c_str(), OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, pItem->m_options.front().c_str(), GetParent()); + if (dlg.DoModal() == IDOK) + { + pItem->m_strValue = dlg.GetPathName(); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + Invalidate(); + } + } + else if (pItem->m_type == IT_FOLDER) + { + CPropertyGridDirectoryPicker::m_strTitle = pItem->m_options.front(); + if (CPropertyGridDirectoryPicker::PickDirectory(pItem->m_strValue, GetParent()->GetSafeHwnd())) + { + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + Invalidate(); + } + } + else if (pItem->m_type == IT_FONT) + { + CFontDialog dlg(&pItem->m_lfValue, CF_EFFECTS|CF_SCREENFONTS, NULL, GetParent()); + if (dlg.DoModal() == IDOK) + { + memcpy(&pItem->m_lfValue, dlg.m_cf.lpLogFont, sizeof(LOGFONT)); + pItem->m_undefined = false; + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + Invalidate(); + } + } + else if (pItem->m_type == IT_CUSTOM) + { + if (pItem->m_pCustom->OnEditItem()) + { + GetOwner()->SendMessage(WM_PG_ITEMCHANGED, pItem->m_id); + Invalidate(); + } + } + else + { + assert(false); + } + } +} diff --git a/ext/PropertyGrid/PropertyGrid.h b/ext/PropertyGrid/PropertyGrid.h new file mode 100644 index 0000000..21237f2 --- /dev/null +++ b/ext/PropertyGrid/PropertyGrid.h @@ -0,0 +1,316 @@ +#pragma once +#include "PropertyGridCombo.h" +#include "PropertyGridInPlaceEdit.h" + +#include +#include +#include +using namespace std; + +// CPropertyGrid + +#define WM_PG_ITEMCHANGED WM_USER+486 +#define WM_PG_SELECTIONCHANGED WM_USER+487 + +typedef UINT HSECTION; +typedef UINT HITEM; + +class ICustomItem; + +class CPropertyGrid : public CWnd +{ + DECLARE_DYNAMIC(CPropertyGrid) + +public: + // display mode + enum EDisplayMode + { + DM_CATEGORIZED = 0, + DM_ALPHABETICAL, + DM_NOSORT + }; + + // editing + enum EEditMode + { + EM_CUSTOM = 0, + EM_INPLACE, + EM_DROPDOWN, + EM_MODAL + }; + + enum EItemType + { + IT_CUSTOM = 0, + IT_STRING, + IT_TEXT, + IT_INTEGER, + IT_DOUBLE, + IT_COMBO, + IT_BOOLEAN, + IT_DATE, + IT_DATETIME, + IT_FILE, + IT_FOLDER, + IT_COLOR, + IT_FONT + }; + +public: + CPropertyGrid(); + virtual ~CPropertyGrid(); + + // customization + bool GetShadeTitles(); + void SetShadeTitles(bool shade_titles); + bool GetDrawLines(); + void SetDrawLines(bool draw_lines); + bool GetDrawGutter(); + void SetDrawGutter(bool draw_gutter); + bool GetFocusDisabled(); + void SetFocusDisabled(bool focus_disabled); + bool GetBoldModified(); + void SetBoldModified(bool bold_modified); + bool GetBoldEditables(); + void SetBoldEditables(bool bold_editables); + + // gutter width + int GetGutterWidth(); + void SetGutterWidth(int gutter_width); + + // custom colors + void SetTextColor(COLORREF clrText); + void SetTitleColor(COLORREF clrText); + void SetBackColor(COLORREF clrBack); + void SetShadeColor(COLORREF clrShade); + void SetFocusColor(COLORREF clrFocus); + void SetHiliteColor(COLORREF clrHilite); + void SetEditableColor(COLORREF clrEditable); + void SetDisabledColor(COLORREF clrDisabled); + + // localization + void SetTrueFalseStrings(string strTrue, string strFalse); + void SetOkCancelStrings(string strOk, string strCancel); + void SetDateTimeStrings(string strDate, string strTime); + void SetUndefinedString(string strUndefined); + void SetEmptyString(string strEmpty); + + // add a section + HSECTION AddSection(string title, bool collapsed = false, HSECTION after = -1); + + // add items + HITEM AddCustomItem(HSECTION, string name, ICustomItem* pItem, bool editable = true, HITEM after = -1); + HITEM AddStringItem(HSECTION section, string name, string value, bool editable = true, HITEM after = -1); + HITEM AddTextItem(HSECTION section, string name, string value, bool editable = true, HITEM after = -1); + HITEM AddIntegerItem(HSECTION section, string name, int value, string format = "", bool editable = true, bool undefined = false, HITEM after = -1); + HITEM AddDoubleItem(HSECTION section, string name, double value, string format = "", bool editable = true, bool undefined = false, HITEM after = -1); + HITEM AddComboItem(HSECTION section, string name, const vector& values, int cur, bool editable = true, bool undefined = false, HITEM after = -1); + HITEM AddBoolItem(HSECTION section, string name, bool value, bool editable = true, bool undefined = false, HITEM after = -1); + HITEM AddDateItem(HSECTION section, string name, COleDateTime value, string format = "", bool editable = true, bool undefined = false, HITEM after = -1); + HITEM AddDateTimeItem(HSECTION section, string name, COleDateTime value, string format = "", bool editable = true, bool undefined = false, HITEM after = -1); + HITEM AddFileItem(HSECTION section, string name, string value, string filter = "", bool editable = true, HITEM after = -1); + HITEM AddFolderItem(HSECTION section, string name, string value, string title = "", bool editable = true, HITEM after = -1); + HITEM AddColorItem(HSECTION section, string name, COLORREF value, bool editable = true, bool undefined = false, HITEM after = -1); + HITEM AddFontItem(HSECTION section, string name, LOGFONT value, bool editable = true, bool undefined = false, HITEM after = -1); + + // contents + void ResetContents(); + bool RemoveSection(HSECTION hs); + bool RemoveItem(HITEM item); + void ValidateChanges(); + + // status + int GetNumSections(); + int GetSectionSize(HSECTION hs); + + // get item value + bool GetItemValue(HITEM item, string& strValue) const; + bool GetItemValue(HITEM item, int& nValue) const; + bool GetItemValue(HITEM item, double& dValue) const; + bool GetItemValue(HITEM item, bool& bValue) const; + bool GetItemValue(HITEM item, COleDateTime& dtValue) const; + bool GetItemValue(HITEM item, COLORREF& clrValue) const; + bool GetItemValue(HITEM item, LOGFONT& lfValue) const; + + // set item value + bool SetItemValue(HITEM item, const string strValue); + bool SetItemValue(HITEM item, const int nValue); + bool SetItemValue(HITEM item, const double nValue); + bool SetItemValue(HITEM item, const bool bValue); + bool SetItemValue(HITEM item, const COleDateTime dtValue); + bool SetItemValue(HITEM item, const COLORREF clrValue); + bool SetItemValue(HITEM item, const LOGFONT lfValue); + + // for custom items + int GetTextMargin(); + CFont* GetFontNormal(); + CFont* GetFontBold(); + + // appearance stuff + void SetDisplayMode(EDisplayMode display_mode); + void ExpandAll(bool expand); + void ExpandSection(HSECTION hs, bool expand); + bool IsSectionCollapsed(HSECTION hs); + + std::string GetItemText(HITEM hItem); + +protected: + class CItem + { + public: + HITEM m_id; + bool m_editable; + bool m_undefined; + EItemType m_type; + string m_name; + + vector m_options; + + int m_nValue; + double m_dValue; + string m_strValue; + bool m_bValue; + COleDateTime m_dtValue; + COLORREF m_clrValue; + LOGFONT m_lfValue; + ICustomItem* m_pCustom; + + bool m_undefined_old; + int m_nValue_old; + double m_dValue_old; + string m_strValue_old; + bool m_bValue_old; + COleDateTime m_dtValue_old; + COLORREF m_clrValue_old; + LOGFONT m_lfValue_old; + + CRect m_rcName; + CRect m_rcValue; + + bool operator==(const HITEM& item) const; + bool operator==(const string& name) const; + + void ValidateChanges(); + }; + + // DOWNGRADE: Commented out to prevent "undefined identifier CPropertyGrid" error + //friend bool item_alpha_sort(vector::iterator it1, vector::iterator it2); + + class CSection + { + public: + HSECTION m_id; + string m_title; + bool m_collapsed; + vector m_items; + + CRect m_rcSign; + CRect m_rcTitle; + + bool operator==(const HSECTION& section) const; + }; + + vector m_sections; + + HSECTION m_focused_section; + HITEM m_focused_item; + + EDisplayMode m_display_mode; + + bool m_shade_titles; + bool m_draw_lines; + bool m_draw_gutter; + bool m_focus_disabled; + bool m_bold_modified; + bool m_bold_editables; + + int m_gutter_width; + bool m_resizing_gutter; + CPoint m_ptLast; + + CFont m_fntNormal; + CFont m_fntBold; + + int m_line_height; + + CRect m_rect_button; + CWnd* m_control; + bool m_button_pushed; + bool m_button_depressed; + bool m_value_clicked; + bool m_custom_tracking; + + HSECTION m_section_id; + HITEM m_item_id; + + string m_strTrue; + string m_strFalse; + string m_strOk; + string m_strCancel; + string m_strDate; + string m_strTime; + string m_strUndefined; + string m_strEmpty; + + COLORREF m_clrText; + COLORREF m_clrTitle; + COLORREF m_clrBack; + COLORREF m_clrShade; + COLORREF m_clrFocus; + COLORREF m_clrHilite; + COLORREF m_clrEditable; + COLORREF m_clrDisabled; + +protected: + DECLARE_MESSAGE_MAP() + + // init control + void InitControl(); + + // drawing + void DrawItem(CDC& dc, int w, int x, int y, vector::iterator& it); + + // item management + CSection* FindSection(HSECTION hs) const; + CItem* FindItem(HITEM hi) const; + HITEM AddItem(HSECTION hs, EItemType type, string name, void* pValue, bool editable, bool undefined, HITEM after); + + // scrolling stuff + CScrollBar m_scrollbar; + bool m_scroll_enabled; + int GetScrollOffset(); + void RecalcLayout(); + + // editing + EEditMode GetEditMode(CItem& item); + void DeleteEditControl(); + void EditFocusedItem(); + + // movement in list + void MoveForward(HSECTION& focused_section, HITEM& focused_item); + + // keyboard + void FocusNextItem(); + void FocusPrevItem(); + +protected: + virtual void PreSubclassWindow(); +public: + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnDestroy(); + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnPaint(); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nHitTest, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); + afx_msg LRESULT OnComboSelChanged(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnEditChanged(WPARAM wParam, LPARAM lParam); + afx_msg LRESULT OnDateChanged(WPARAM wParam, LPARAM lParam); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg UINT OnGetDlgCode(); + afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); +}; diff --git a/ext/PropertyGrid/PropertyGridCombo.cpp b/ext/PropertyGrid/PropertyGridCombo.cpp new file mode 100644 index 0000000..063ad23 --- /dev/null +++ b/ext/PropertyGrid/PropertyGridCombo.cpp @@ -0,0 +1,260 @@ +// PropertyGridCombo.cpp : implementation file +// + +#include "stdafx.h" +#include "PropertyGridCombo.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +static const int margin = 2; + +// CPropertyGridCombo + +IMPLEMENT_DYNCREATE(CPropertyGridCombo, CWnd) + +CPropertyGridCombo::CPropertyGridCombo() +{ + m_pFont = NULL; + m_nSelected = -1; + m_bTracking = false; + + m_clrBack = GetSysColor(COLOR_WINDOW); + m_clrText = GetSysColor(COLOR_WINDOWTEXT); + m_clrFocus = GetSysColor(COLOR_HIGHLIGHT); + m_clrHilite = GetSysColor(COLOR_HIGHLIGHTTEXT); +} + +CPropertyGridCombo::~CPropertyGridCombo() +{ +} + +// +// content management +// + +void CPropertyGridCombo::AddString(string strItem) +{ + m_Items.push_back(strItem); +} + +void CPropertyGridCombo::SetCurSel(int nItem) +{ + m_nSelected = nItem; +} + + +BEGIN_MESSAGE_MAP(CPropertyGridCombo, CWnd) + ON_WM_PAINT() + ON_WM_SHOWWINDOW() + ON_WM_LBUTTONDOWN() + ON_WM_MOUSEMOVE() + ON_WM_LBUTTONUP() + ON_WM_DESTROY() + ON_WM_KILLFOCUS() + ON_WM_KEYDOWN() + ON_WM_GETDLGCODE() +END_MESSAGE_MAP() + +// +// creation +// + +BOOL CPropertyGridCombo::Create(DWORD dwStyle, CRect& rc, CWnd* pParent, int nId) +{ + pParent->ClientToScreen(&rc); + BOOL ret = CWnd::CreateEx(0, AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW), "", dwStyle|WS_POPUP, rc, pParent->GetParent(), nId); + if (ret) SetOwner(pParent); + return ret; +} + +void CPropertyGridCombo::SetFont(CFont* pFont, BOOL bRedraw) +{ + m_pFont = pFont; + CWnd::SetFont(pFont, bRedraw); +} + +void CPropertyGridCombo::SetColors(COLORREF clrBack, COLORREF clrText, COLORREF clrFocus, COLORREF clrHilite) +{ + m_clrBack = clrBack; + m_clrText = clrText; + m_clrFocus = clrFocus; + m_clrHilite = clrHilite; +} + +void CPropertyGridCombo::OnShowWindow(BOOL bShow, UINT nStatus) +{ + if (bShow) + { + // get line height + CDC* pDC = GetDC(); + int save = pDC->SaveDC(); + pDC->SelectObject(m_pFont?m_pFont:GetFont()); + m_line_height = pDC->GetTextExtent("Gg").cy + 2*margin; + pDC->RestoreDC(save); + ReleaseDC(pDC); + + // size control + CRect rc; + GetWindowRect(&rc); + SetWindowPos(NULL, 0, 0, rc.Width(), int(m_Items.size())*m_line_height+2, SWP_NOOWNERZORDER|SWP_NOZORDER|SWP_NOMOVE); + SetFocus(); + } + + CWnd::OnShowWindow(bShow, nStatus); +} + +void CPropertyGridCombo::OnDestroy() +{ + CWnd::OnDestroy(); +} + +void CPropertyGridCombo::OnKillFocus(CWnd* pNewWnd) +{ + CWnd::OnKillFocus(pNewWnd); + DestroyWindow(); +} + +// +// painting +// + +void CPropertyGridCombo::OnPaint() +{ + // check + if (m_nSelected<0) m_nSelected = 0; + if (m_nSelected>int(m_Items.size())-1) m_nSelected = int(m_Items.size())-1; + + // client rect + CRect rc; + GetClientRect(&rc); + + // brush + CBrush brush; + brush.CreateSolidBrush(m_clrBack); + + // pen + CPen pen; + pen.CreatePen(PS_SOLID, 1, m_clrText); + + // the dc + CPaintDC dc(this); + CBrush* pOldBrush = dc.SelectObject(&brush); + CPen* pOldPen = dc.SelectObject(&pen); + CFont* pOldFont = dc.SelectObject(m_pFont); + + // draw + dc.SelectObject(&brush); + dc.SelectObject(&pen); + dc.Rectangle(rc); + + // put items + int i = 0; + int y = 1; + dc.SelectObject(m_pFont); + dc.SetBkMode(TRANSPARENT); + for (vector::iterator it = m_Items.begin(); it != m_Items.end(); ++it) + { + CRect rcItem(0, y, rc.Width(), y+m_line_height); + rcItem.DeflateRect(1,0,1,0); + + if (i == m_nSelected) + { + dc.DrawFocusRect(rcItem); + dc.SetTextColor(m_clrHilite); + + CRect rc = rcItem; + rc.DeflateRect(1,1); + dc.FillSolidRect(rc, m_clrFocus); + } + else + { + dc.SetTextColor(m_clrText); + } + + // do it + rcItem.left += 2*margin; + dc.DrawText(it->c_str(), rcItem, DT_SINGLELINE|DT_VCENTER|DT_LEFT|DT_NOPREFIX); + y += m_line_height; + i++; + } + + // clean up + dc.SelectObject(pOldFont); + dc.SelectObject(pOldPen); + dc.SelectObject(pOldBrush); +} + +// +// mouse interaction +// + +void CPropertyGridCombo::OnLButtonDown(UINT nFlags, CPoint point) +{ + m_nSelected = point.y/m_line_height; + m_bTracking = true; + SetCapture(); + Invalidate(); + + CWnd::OnLButtonDown(nFlags, point); +} + +void CPropertyGridCombo::OnMouseMove(UINT nFlags, CPoint point) +{ + if (m_bTracking) + { + m_nSelected = point.y/m_line_height; + Invalidate(); + } + CWnd::OnMouseMove(nFlags, point); +} + +void CPropertyGridCombo::OnLButtonUp(UINT nFlags, CPoint point) +{ + if (m_bTracking) + { + ReleaseCapture(); + m_bTracking = false; + Invalidate(); + } + + GetOwner()->SendMessage(WM_PG_COMBOSELCHANGED, m_nSelected, 0); +} + +// +// keyboard interaction +// + +UINT CPropertyGridCombo::OnGetDlgCode() +{ + return DLGC_WANTALLKEYS; +} + +void CPropertyGridCombo::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if (nChar == VK_LEFT || nChar == VK_UP) + { + m_nSelected = max(0, m_nSelected-1); + Invalidate(); + } + else if (nChar == VK_RIGHT || nChar == VK_DOWN) + { + m_nSelected = min(int(m_Items.size())-1, m_nSelected+1); + Invalidate(); + } + else if (nChar == VK_ESCAPE) + { + DestroyWindow(); + return; + } + else if (nChar == VK_RETURN || nChar == VK_EXECUTE) + { + GetOwner()->SendMessage(WM_PG_COMBOSELCHANGED, m_nSelected, 0); + return; + } + + CWnd::OnKeyDown(nChar, nRepCnt, nFlags); +} diff --git a/ext/PropertyGrid/PropertyGridCombo.h b/ext/PropertyGrid/PropertyGridCombo.h new file mode 100644 index 0000000..b0b676e --- /dev/null +++ b/ext/PropertyGrid/PropertyGridCombo.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include +using namespace std; + +// CPropertyGridCombo frame + +#define WM_PG_COMBOSELCHANGED WM_USER+487 + +class CPropertyGridCombo : public CWnd +{ + DECLARE_DYNCREATE(CPropertyGridCombo) + +public: + CPropertyGridCombo(); + virtual ~CPropertyGridCombo(); + + BOOL Create(DWORD dwStyle, CRect& rc, CWnd* pParent, int nId); + void SetFont(CFont* pFont, BOOL bRedraw = TRUE); + void SetColors(COLORREF clrBack, COLORREF clrText, COLORREF clrFocus, COLORREF clrHilite); + + void AddString(string strItem); + void SetCurSel(int nItem); + +protected: + vector m_Items; + int m_nSelected; + + CFont* m_pFont; + int m_line_height; + + bool m_bTracking; + + COLORREF m_clrBack; + COLORREF m_clrText; + COLORREF m_clrFocus; + COLORREF m_clrHilite; + +protected: + DECLARE_MESSAGE_MAP() + +public: + afx_msg void OnPaint(); + afx_msg void OnShowWindow(BOOL bShow, UINT nStatus); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnDestroy(); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg UINT OnGetDlgCode(); +}; + + diff --git a/ext/PropertyGrid/PropertyGridDirectoryPicker.cpp b/ext/PropertyGrid/PropertyGridDirectoryPicker.cpp new file mode 100644 index 0000000..59d4be3 --- /dev/null +++ b/ext/PropertyGrid/PropertyGridDirectoryPicker.cpp @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001, nabocorp +// All Rights Reserved +// +//////////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "shlobj.h" +#include "PropertyGridDirectoryPicker.h" + +#define BIF_NEWDIALOGSTYLE 0x0040 + +std::string CPropertyGridDirectoryPicker::m_strTitle = "Choose a directory"; + +CPropertyGridDirectoryPicker::CPropertyGridDirectoryPicker() +{ +} + +CPropertyGridDirectoryPicker::~CPropertyGridDirectoryPicker() +{ +} + +int CALLBACK CPropertyGridDirectoryPicker::BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lp, LPARAM pData) +{ + switch(uMsg) + { + // If the dialog is being initialised + case BFFM_INITIALIZED: + { + // Send a SetSelection message on the passed directory + SendMessage(hwnd,BFFM_SETSELECTION,TRUE,pData); + break; + } + } + return 0; +} + +bool CPropertyGridDirectoryPicker::PickDirectory(std::string &directory, HWND hwnd) +{ + char pszBuffer[MAX_PATH]; + pszBuffer[0] = '\0'; + + // Gets the Shell's default allocator + LPMALLOC pMalloc; + if (::SHGetMalloc(&pMalloc) == NOERROR) + { + BROWSEINFO bi; + LPITEMIDLIST pidl; + + // Get help on BROWSEINFO struct + bi.hwndOwner = hwnd; + bi.pidlRoot = NULL; + bi.pszDisplayName = pszBuffer; + bi.lpszTitle = m_strTitle.c_str(); + bi.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE ; + + // The callback function initialises the dialog with the passed value + bi.lpfn = BrowseCallbackProc; + bi.lParam = LPARAM(directory.c_str()); + + // This next call issues the dialog box. + if ((pidl = ::SHBrowseForFolder(&bi)) != NULL) + { + // Get the full path into pszBuffer + ::SHGetPathFromIDList(pidl, pszBuffer); + // Free the PIDL allocated by SHBrowseForFolder. + pMalloc->Free(pidl); + } + // Release the shell's allocator. + pMalloc->Release(); + } + + // get the result + if (strlen(pszBuffer) != 0) + { + directory = pszBuffer; + return TRUE; + } + return FALSE; +} + diff --git a/ext/PropertyGrid/PropertyGridDirectoryPicker.h b/ext/PropertyGrid/PropertyGridDirectoryPicker.h new file mode 100644 index 0000000..dcfd1e5 --- /dev/null +++ b/ext/PropertyGrid/PropertyGridDirectoryPicker.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2001, nabocorp +// All Rights Reserved +// +//////////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_DIRECTORYPICKER_H__CBBD1C74_A552_11D2_8ECA_00104BDC35E6__INCLUDED_) +#define AFX_DIRECTORYPICKER_H__CBBD1C74_A552_11D2_8ECA_00104BDC35E6__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +class CPropertyGridDirectoryPicker +{ +public: + CPropertyGridDirectoryPicker(); + virtual ~CPropertyGridDirectoryPicker(); + + static bool PickDirectory(std::string&, HWND hwnd); + static std::string m_strTitle; + +protected: + static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lp, LPARAM pData); +}; + +#endif // !defined(AFX_DIRECTORYPICKER_H__CBBD1C74_A552_11D2_8ECA_00104BDC35E6__INCLUDED_) diff --git a/ext/PropertyGrid/PropertyGridInPlaceEdit.cpp b/ext/PropertyGrid/PropertyGridInPlaceEdit.cpp new file mode 100644 index 0000000..85a83df --- /dev/null +++ b/ext/PropertyGrid/PropertyGridInPlaceEdit.cpp @@ -0,0 +1,237 @@ +// InPlaceEdit.cpp : implementation file +// +// Adapted by Chris Maunder +// Copyright (c) 1998-2002. All Rights Reserved. +// +// The code contained in this file is based on the original +// CPropertyGridInPlaceEdit from http://www.codeguru.com/listview/edit_subitems.shtml +// +// This code may be used in compiled form in any way you desire. This +// file may be redistributed unmodified by any means PROVIDING it is +// not sold for profit without the authors written consent, and +// providing that this notice and the authors name and all copyright +// notices remains intact. +// +// An email letting me know how you are using it would be nice as well. +// +// This file is provided "as is" with no expressed or implied warranty. +// The author accepts no liability for any damage/loss of business that +// this product may cause. +// +// For use with CGridCtrl v2.10+ +// +// History: +// 10 May 1998 Uses GVN_ notifications instead of LVN_, +// Sends notification messages to the parent, +// instead of the parent's parent. +// 15 May 1998 There was a problem when editing with the in-place editor, +// there arises a general protection fault in user.exe, with a +// few qualifications: +// (1) This only happens with owner-drawn buttons; +// (2) This only happens in Win95 +// (3) This only happens if the handler for the button does not +// create a new window (even an AfxMessageBox will avoid the +// crash) +// (4) This will not happen if Spy++ is running. +// PreTranslateMessage was added to route messages correctly. +// (Matt Weagle found and fixed this problem) +// 26 Jul 1998 Removed the ES_MULTILINE style - that fixed a few probs! +// 6 Aug 1998 Added nID to the constructor param list +// 6 Sep 1998 Space no longer clears selection when starting edit (Franco Bez) +// 10 Apr 1999 Enter, Tab and Esc key prob fixed (Koay Kah Hoe) +// Workaround for bizzare "shrinking window" problem in CE +// +///////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "TCHAR.h" +#include "PropertyGridInPlaceEdit.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPropertyGridInPlaceEdit + +CPropertyGridInPlaceEdit::CPropertyGridInPlaceEdit(CWnd* pParent, CRect& rect, DWORD dwStyle, UINT nID, string sInitText) +{ + m_sInitText = sInitText.c_str(); + m_bExitOnArrows = FALSE; + m_Rect = rect; // For bizarre CE bug. + + DWORD dwEditStyle = /*WS_BORDER|*/WS_CHILD|/*WS_VISIBLE|*/ES_AUTOHSCROLL|dwStyle; + if (!Create(dwEditStyle, rect, pParent, nID)) + return; + + m_clrBack = GetSysColor(COLOR_WINDOW); + m_clrText = GetSysColor(COLOR_WINDOWTEXT); + m_Brush.CreateSolidBrush(m_clrBack); + + SetFont(pParent->GetFont()); + + SetWindowText(m_sInitText); + SetFocus(); + + SetSel(0, -1); + SetSel(-1, 0); +} + +CPropertyGridInPlaceEdit::~CPropertyGridInPlaceEdit() +{ +} + +void CPropertyGridInPlaceEdit::SetColors(COLORREF clrBack, COLORREF clrText) +{ + m_clrBack = clrBack; + m_clrText = clrText; + m_Brush.DeleteObject(); + m_Brush.CreateSolidBrush(m_clrBack); +} + +BEGIN_MESSAGE_MAP(CPropertyGridInPlaceEdit, CEdit) + //{{AFX_MSG_MAP(CPropertyGridInPlaceEdit) + ON_WM_KILLFOCUS() + ON_WM_CHAR() + ON_WM_KEYDOWN() + ON_WM_GETDLGCODE() + ON_WM_CREATE() + ON_WM_CTLCOLOR_REFLECT( ) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +//////////////////////////////////////////////////////////////////////////// +// CPropertyGridInPlaceEdit message handlers + +// If an arrow key (or associated) is pressed, then exit if +// a) The Ctrl key was down, or +// b) m_bExitOnArrows == TRUE +void CPropertyGridInPlaceEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if ((nChar == VK_PRIOR || nChar == VK_NEXT || + nChar == VK_DOWN || nChar == VK_UP || + nChar == VK_RIGHT || nChar == VK_LEFT) && + (m_bExitOnArrows || GetKeyState(VK_CONTROL) < 0)) + { + GetParent()->SetFocus(); + return; + } + + CEdit::OnKeyDown(nChar, nRepCnt, nFlags); +} + +// As soon as this edit loses focus, kill it. +void CPropertyGridInPlaceEdit::OnKillFocus(CWnd* pNewWnd) +{ + CEdit::OnKillFocus(pNewWnd); + EndEdit(); +} + +void CPropertyGridInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if (nChar == VK_TAB || nChar == VK_RETURN) + { + GetParent()->SetFocus(); // This will destroy this window + return; + } + if (nChar == VK_ESCAPE) + { + CancelEdit(); + return; + } + + CEdit::OnChar(nChar, nRepCnt, nFlags); + + //// Resize edit control if needed + // + //// Get text extent + //CString str; + //GetWindowText( str ); + + //// add some extra buffer + //str += _T(" "); + // + //CWindowDC dc(this); + //CFont *pFontDC = dc.SelectObject(GetFont()); + //CSize size = dc.GetTextExtent( str ); + //dc.SelectObject( pFontDC ); + // + //// Get client rect + //CRect ParentRect; + //GetParent()->GetClientRect( &ParentRect ); + // + //// Check whether control needs to be resized + //// and whether there is space to grow + //if (size.cx > m_Rect.Width()) + //{ + // if( size.cx + m_Rect.left < ParentRect.right ) + // m_Rect.right = m_Rect.left + size.cx; + // else + // m_Rect.right = ParentRect.right; + // MoveWindow( &m_Rect ); + //} +} + +UINT CPropertyGridInPlaceEdit::OnGetDlgCode() +{ + return DLGC_WANTALLKEYS; +} + +//////////////////////////////////////////////////////////////////////////// +// CPropertyGridInPlaceEdit overrides + +// Stoopid win95 accelerator key problem workaround - Matt Weagle. +BOOL CPropertyGridInPlaceEdit::PreTranslateMessage(MSG* pMsg) +{ + // Catch the Alt key so we don't choke if focus is going to an owner drawn button + if (pMsg->message == WM_SYSCHAR) + return TRUE; + return CEdit::PreTranslateMessage(pMsg); +} + +//////////////////////////////////////////////////////////////////////////// +// CPropertyGridInPlaceEdit implementation + +void CPropertyGridInPlaceEdit::CancelEdit() +{ + // restore previous text + if (IsWindow(GetSafeHwnd())) + { + SetWindowText(m_sInitText); + SendMessage(WM_CLOSE, 0, 0); + } +} + +void CPropertyGridInPlaceEdit::EndEdit() +{ + CString str; + + // EFW - BUG FIX - Clicking on a grid scroll bar in a derived class + // that validates input can cause this to get called multiple times + // causing assertions because the edit control goes away the first time. + static BOOL bAlreadyEnding = FALSE; + + if(bAlreadyEnding) + return; + + bAlreadyEnding = TRUE; + GetWindowText(str); + + CWnd* pOwner = GetOwner(); + if (pOwner) + pOwner->SendMessage(WM_PG_ENDLABELEDIT, (WPARAM) LPCTSTR(str), NULL ); + + // Close this window (PostNcDestroy will delete this) + if (IsWindow(GetSafeHwnd())) + SendMessage(WM_CLOSE, 0, 0); + bAlreadyEnding = FALSE; +} + +HBRUSH CPropertyGridInPlaceEdit::CtlColor ( CDC* pDC, UINT nCtlColor ) +{ + pDC->SetTextColor(m_clrText); + pDC->SetBkColor(m_clrBack); + return m_Brush; +} \ No newline at end of file diff --git a/ext/PropertyGrid/PropertyGridInPlaceEdit.h b/ext/PropertyGrid/PropertyGridInPlaceEdit.h new file mode 100644 index 0000000..45713c7 --- /dev/null +++ b/ext/PropertyGrid/PropertyGridInPlaceEdit.h @@ -0,0 +1,89 @@ +////////////////////////////////////////////////////////////////////// +// InPlaceEdit.h : header file +// +// MFC Grid Control - inplace editing class +// +// Written by Chris Maunder +// Copyright (c) 1998-2002. All Rights Reserved. +// +// This code may be used in compiled form in any way you desire. This +// file may be redistributed unmodified by any means PROVIDING it is +// not sold for profit without the authors written consent, and +// providing that this notice and the authors name and all copyright +// notices remains intact. +// +// An email letting me know how you are using it would be nice as well. +// +// This file is provided "as is" with no expressed or implied warranty. +// The author accepts no liability for any damage/loss of business that +// this product may cause. +// +// For use with CGridCtrl v2.10+ +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_GRIDINPLACEEDIT_H__ECD42821_16DF_11D1_992F_895E185F9C72__INCLUDED_) +#define AFX_GRIDINPLACEEDIT_H__ECD42821_16DF_11D1_992F_895E185F9C72__INCLUDED_ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#include +using namespace std; + +#define WM_PG_ENDLABELEDIT WM_USER+488 + +class CPropertyGridInPlaceEdit : public CEdit +{ + // Construction +public: + CPropertyGridInPlaceEdit(CWnd* pParent, CRect& rect, DWORD dwStyle, UINT nID, string sInitText); + void SetColors(COLORREF clrBack, COLORREF clrText); + + // Attributes +public: + + // Operations +public: + void CancelEdit(); + void EndEdit(); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPropertyGridInPlaceEdit) +public: + virtual BOOL PreTranslateMessage(MSG* pMsg); + //}}AFX_VIRTUAL + + // Implementation +public: + virtual ~CPropertyGridInPlaceEdit(); + + // Generated message map functions +protected: + //{{AFX_MSG(CPropertyGridInPlaceEdit) + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg UINT OnGetDlgCode(); + afx_msg HBRUSH CtlColor ( CDC* pDC, UINT nCtlColor ); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + CString m_sInitText; + BOOL m_bExitOnArrows; + CRect m_Rect; + + COLORREF m_clrBack; + COLORREF m_clrText; + CBrush m_Brush; +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Developer Studio will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_GRIDINPLACEEDIT_H__ECD42821_16DF_11D1_992F_895E185F9C72__INCLUDED_) diff --git a/ext/PropertyGrid/PropertyGridMonthCalCtrl.cpp b/ext/PropertyGrid/PropertyGridMonthCalCtrl.cpp new file mode 100644 index 0000000..afb8067 --- /dev/null +++ b/ext/PropertyGrid/PropertyGridMonthCalCtrl.cpp @@ -0,0 +1,67 @@ +// PropertyGridMonthCalCtrl.cpp : implementation file +// + +#include "stdafx.h" +#include "PropertyGridMonthCalCtrl.h" + + +// CPropertyGridMonthCalCtrl + +// DOWNGRADE: Commented out to prevent link error where CRuntimeStatic for CMonthCalCtrl is unresolved +//IMPLEMENT_DYNAMIC(CPropertyGridMonthCalCtrl, CMonthCalCtrl) +CPropertyGridMonthCalCtrl::CPropertyGridMonthCalCtrl() +{ +} + +CPropertyGridMonthCalCtrl::~CPropertyGridMonthCalCtrl() +{ +} + + +BEGIN_MESSAGE_MAP(CPropertyGridMonthCalCtrl, CMonthCalCtrl) + ON_WM_KILLFOCUS() + ON_NOTIFY_REFLECT(MCN_SELECT, OnMcnSelect) + ON_WM_GETDLGCODE() + ON_WM_KEYDOWN() +END_MESSAGE_MAP() + + + +// CPropertyGridMonthCalCtrl message handlers + + +void CPropertyGridMonthCalCtrl::OnKillFocus(CWnd* pNewWnd) +{ + CMonthCalCtrl::OnKillFocus(pNewWnd); + CWnd* pParent = pNewWnd ? pNewWnd->GetParent() : NULL; + if (pParent != this) + DestroyWindow(); +} + +void CPropertyGridMonthCalCtrl::OnMcnSelect(NMHDR *pNMHDR, LRESULT *pResult) +{ + LPNMSELCHANGE pSelChange = reinterpret_cast(pNMHDR); + GetOwner()->SendMessage(WM_PG_DATESELCHANGED); + *pResult = 0; +} + +UINT CPropertyGridMonthCalCtrl::OnGetDlgCode() +{ + return DLGC_WANTALLKEYS; +} + +void CPropertyGridMonthCalCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if (nChar == VK_ESCAPE) + { + DestroyWindow(); + return; + } + else if (nChar == VK_RETURN || nChar == VK_EXECUTE) + { + GetOwner()->SendMessage(WM_PG_DATESELCHANGED); + return; + } + + CMonthCalCtrl::OnKeyDown(nChar, nRepCnt, nFlags); +} diff --git a/ext/PropertyGrid/PropertyGridMonthCalCtrl.h b/ext/PropertyGrid/PropertyGridMonthCalCtrl.h new file mode 100644 index 0000000..c9b6263 --- /dev/null +++ b/ext/PropertyGrid/PropertyGridMonthCalCtrl.h @@ -0,0 +1,25 @@ +#pragma once + +#define WM_PG_DATESELCHANGED WM_USER+489 + +// CPropertyGridMonthCalCtrl + +class CPropertyGridMonthCalCtrl : public CMonthCalCtrl +{ + // DOWNGRADE: Commented out to prevent link error where CRuntimeStatic for CMonthCalCtrl is unresolved + //DECLARE_DYNAMIC(CPropertyGridMonthCalCtrl) + +public: + CPropertyGridMonthCalCtrl(); + virtual ~CPropertyGridMonthCalCtrl(); + +protected: + DECLARE_MESSAGE_MAP() +public: + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnMcnSelect(NMHDR *pNMHDR, LRESULT *pResult); + afx_msg UINT OnGetDlgCode(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); +}; + + diff --git a/ext/PropertyGrid/README.md b/ext/PropertyGrid/README.md new file mode 100644 index 0000000..b6aa91b --- /dev/null +++ b/ext/PropertyGrid/README.md @@ -0,0 +1,5 @@ +# Fully customizable PropertyGrid for MFC 4.2 + +Original project by Nicolas Bonamy at CodeProject: https://www.codeproject.com/Articles/18933/A-fully-customizable-PropertyGrid + +This is a port of that project to CMake with minor changes so it can compile with Visual C++ 6.0. diff --git a/ext/PropertyGrid/stdafx.cpp b/ext/PropertyGrid/stdafx.cpp new file mode 100644 index 0000000..f1ac11e --- /dev/null +++ b/ext/PropertyGrid/stdafx.cpp @@ -0,0 +1,7 @@ +// stdafx.cpp : source file that includes just the standard includes +// PropGrid.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + + diff --git a/ext/PropertyGrid/stdafx.h b/ext/PropertyGrid/stdafx.h new file mode 100644 index 0000000..fa08e4a --- /dev/null +++ b/ext/PropertyGrid/stdafx.h @@ -0,0 +1,29 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__8E104244_6D3A_49FB_B014_338D08085215__INCLUDED_) +#define AFX_STDAFX_H__8E104244_6D3A_49FB_B014_338D08085215__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC Automation classes +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + + +#include +#include +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__8E104244_6D3A_49FB_B014_338D08085215__INCLUDED_) diff --git a/ext/PropertyGrid/test/CMakeLists.txt b/ext/PropertyGrid/test/CMakeLists.txt new file mode 100644 index 0000000..038f169 --- /dev/null +++ b/ext/PropertyGrid/test/CMakeLists.txt @@ -0,0 +1,23 @@ +add_executable(PropGrid WIN32 + CustomTreeCtrl.cpp + CustomTreeCtrl.h + GradientItem.cpp + GradientItem.h + PropGrid.cpp + PropGrid.h + PropGrid.rc + PropGridDlg.cpp + PropGridDlg.h + RectEditDlg.cpp + RectEditDlg.h + RectItem.cpp + RectItem.h + resource.h + SerialItem.cpp + SerialItem.h + TreeItem.cpp + TreeItem.h +) + +target_include_directories(PropGrid PRIVATE "..") +target_link_libraries(PropGrid PRIVATE PropertyGrid) diff --git a/ext/PropertyGrid/test/CustomTreeCtrl.cpp b/ext/PropertyGrid/test/CustomTreeCtrl.cpp new file mode 100644 index 0000000..e42aeac --- /dev/null +++ b/ext/PropertyGrid/test/CustomTreeCtrl.cpp @@ -0,0 +1,88 @@ +// CustomTreeCtrl.cpp : implementation file +// + +#include "stdafx.h" +#include "PropGrid.h" +#include "CustomTreeCtrl.h" +#include "TreeItem.h" +#include ".\customtreectrl.h" + + +// CCustomTreeCtrl + +IMPLEMENT_DYNAMIC(CCustomTreeCtrl, CTreeCtrl) +CCustomTreeCtrl::CCustomTreeCtrl() +{ +} + +CCustomTreeCtrl::~CCustomTreeCtrl() +{ +} + + +BEGIN_MESSAGE_MAP(CCustomTreeCtrl, CTreeCtrl) + ON_WM_GETDLGCODE() + ON_WM_KILLFOCUS() + ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnTvnSelchanged) + ON_WM_KEYDOWN() +END_MESSAGE_MAP() + + + +// CCustomTreeCtrl message handlers + + +UINT CCustomTreeCtrl::OnGetDlgCode() +{ + return DLGC_WANTALLKEYS; +} + +void CCustomTreeCtrl::OnKillFocus(CWnd* pNewWnd) +{ + CTreeCtrl::OnKillFocus(pNewWnd); + DestroyWindow(); +} + +void CCustomTreeCtrl::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult) +{ + LPNMTREEVIEW pNMTreeView = reinterpret_cast(pNMHDR); + if (pNMTreeView->action == TVC_BYMOUSE) + Validate(); + *pResult = 0; +} + +void CCustomTreeCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if (nChar == VK_ESCAPE) + { + DestroyWindow(); + return; + } + else if (nChar == VK_RETURN || nChar == VK_EXECUTE) + { + Validate(); + return; + } + + CTreeCtrl::OnKeyDown(nChar, nRepCnt, nFlags); +} + +void CCustomTreeCtrl::Validate() +{ + if (GetSafeHwnd()) + { + HTREEITEM hItem = GetSelectedItem(); + if (hItem && !ItemHasChildren(hItem)) + { + CString strText = GetItemText(hItem); + m_item->SetValue(LPCTSTR(strText)); + DestroyWindow(); + } + } +} + +void CCustomTreeCtrl::PostNcDestroy() +{ + CTreeCtrl::PostNcDestroy(); + delete this; +} diff --git a/ext/PropertyGrid/test/CustomTreeCtrl.h b/ext/PropertyGrid/test/CustomTreeCtrl.h new file mode 100644 index 0000000..5463ba9 --- /dev/null +++ b/ext/PropertyGrid/test/CustomTreeCtrl.h @@ -0,0 +1,32 @@ +#pragma once +#include "..\PropertyGrid.h" + +class CTreeItem; + +// CCustomTreeCtrl + +class CCustomTreeCtrl : public CTreeCtrl +{ + DECLARE_DYNAMIC(CCustomTreeCtrl) + +public: + CCustomTreeCtrl(); + virtual ~CCustomTreeCtrl(); + + CTreeItem* m_item; + +protected: + DECLARE_MESSAGE_MAP() + + void Validate(); + +public: + afx_msg UINT OnGetDlgCode(); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); +protected: + virtual void PostNcDestroy(); +}; + + diff --git a/ext/PropertyGrid/test/GradientItem.cpp b/ext/PropertyGrid/test/GradientItem.cpp new file mode 100644 index 0000000..a8f6dfd --- /dev/null +++ b/ext/PropertyGrid/test/GradientItem.cpp @@ -0,0 +1,108 @@ +#include "StdAfx.h" +#include "GradientItem.h" + +CGradientItem::CGradientItem(void) +{ + m_clrLeft = RGB(0,0,0); + m_clrRight = RGB(0,255,0); + m_nButtonPushed = 0; +} + +CPropertyGrid::EEditMode CGradientItem::GetEditMode() +{ + return CPropertyGrid::EM_CUSTOM; +} + +void CGradientItem::DrawItem(CDC& dc, CRect rc, bool focused) +{ + CRect rect = rc; + rect.DeflateRect(focused?rc.Height()+2:2, 2); + + CBrush brush; + brush.CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT)); + dc.FrameRect(rect, &brush); + + rect.DeflateRect(1,1); + + TRIVERTEX vert[2] ; + GRADIENT_RECT gRect; + vert [0] .x = rect.left; + vert [0] .y = rect.top; + vert [0] .Red = GetRValue(m_clrLeft)*256; + vert [0] .Green = GetGValue(m_clrLeft)*256; + vert [0] .Blue = GetBValue(m_clrLeft)*256; + vert [0] .Alpha = 0x0000; + + vert [1] .x = rect.right; + vert [1] .y = rect.bottom; + vert [1] .Red = GetRValue(m_clrRight)*256; + vert [1] .Green = GetGValue(m_clrRight)*256; + vert [1] .Blue = GetBValue(m_clrRight)*256; + vert [1] .Alpha = 0x0000; + + gRect.UpperLeft = 0; + gRect.LowerRight = 1; + + // DOWNGRADE: Commented out because MFC 4.2 doesn't have GradientFill + //dc.GradientFill(vert,2,&gRect,1,GRADIENT_FILL_RECT_H); + + if (focused) + { + // for propert clean up + CFont* pOldFont = dc.SelectObject(m_pGrid->GetFontBold()); + + // the left button + CRect rc1 = rc; + rc1.right = rc1.left + rc1.Height(); + dc.DrawFrameControl(rc1, DFC_BUTTON, DFCS_BUTTONPUSH|(m_nButtonPushed==1?DFCS_PUSHED:0)); + dc.SelectObject(m_pGrid->GetFontBold()); + dc.DrawText("...", rc1, DT_CENTER|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX); + + // the right button + CRect rc2 = rc; + rc2.left = rc2.right - rc2.Height(); + dc.DrawFrameControl(rc2, DFC_BUTTON, DFCS_BUTTONPUSH|(m_nButtonPushed==2?DFCS_PUSHED:0)); + dc.SelectObject(m_pGrid->GetFontBold()); + dc.DrawText("...", rc2, DT_CENTER|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX); + + // clean up + dc.SelectObject(pOldFont); + } +} + +bool CGradientItem::OnLButtonDown(CRect rc, CPoint pt) +{ + m_nButtonPushed = 0; + if (rc.PtInRect(pt)) + { + if (pt.x<=rc.left+rc.Height()) + { + m_nButtonPushed = 1; + return true; + } + if (pt.x>=rc.right-rc.Height()) + { + m_nButtonPushed = 2; + return true; + } + } + return false; +} + +void CGradientItem::OnMouseMove(CRect rc, CPoint pt) +{ + OnLButtonDown(rc, pt); +} + +void CGradientItem::OnLButtonUp(CRect rc, CPoint pt) +{ + COLORREF& clr = (m_nButtonPushed==1) ? m_clrLeft : m_clrRight; + m_nButtonPushed = 0; + CColorDialog dlg(clr, 0, m_pGrid); + if (dlg.DoModal() == IDOK) + { + clr = dlg.GetColor(); + m_pGrid->Invalidate(); + } +} + diff --git a/ext/PropertyGrid/test/GradientItem.h b/ext/PropertyGrid/test/GradientItem.h new file mode 100644 index 0000000..ccd741a --- /dev/null +++ b/ext/PropertyGrid/test/GradientItem.h @@ -0,0 +1,21 @@ +#pragma once +#include "..\CustomItem.h" + +class CGradientItem : + public ICustomItem +{ +public: + CGradientItem(void); + +public: + virtual CPropertyGrid::EEditMode GetEditMode(); + virtual void DrawItem(CDC& dc, CRect rc, bool focused); + virtual bool OnLButtonDown(CRect rc, CPoint pt); + virtual void OnMouseMove(CRect rc, CPoint pt); + virtual void OnLButtonUp(CRect rc, CPoint pt); + +protected: + COLORREF m_clrLeft; + COLORREF m_clrRight; + int m_nButtonPushed; //0: none, 1: left, 2:right +}; diff --git a/ext/PropertyGrid/test/PropGrid.cpp b/ext/PropertyGrid/test/PropGrid.cpp new file mode 100644 index 0000000..4c7f0ca --- /dev/null +++ b/ext/PropertyGrid/test/PropGrid.cpp @@ -0,0 +1,71 @@ +// PropGrid.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "PropGrid.h" +#include "PropGridDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +// CPropGridApp + +BEGIN_MESSAGE_MAP(CPropGridApp, CWinApp) + ON_COMMAND(ID_HELP, CWinApp::OnHelp) +END_MESSAGE_MAP() + + +// CPropGridApp construction + +CPropGridApp::CPropGridApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + + +// The one and only CPropGridApp object + +CPropGridApp theApp; + + +// CPropGridApp initialization + +BOOL CPropGridApp::InitInstance() +{ + // InitCommonControls() is required on Windows XP if an application + // manifest specifies use of ComCtl32.dll version 6 or later to enable + // visual styles. Otherwise, any window creation will fail. + InitCommonControls(); + + CWinApp::InitInstance(); + + // Standard initialization + // If you are not using these features and wish to reduce the size + // of your final executable, you should remove from the following + // the specific initialization routines you do not need + // Change the registry key under which our settings are stored + // TODO: You should modify this string to be something appropriate + // such as the name of your company or organization + SetRegistryKey(_T("Local AppWizard-Generated Applications")); + + CPropGridDlg dlg; + m_pMainWnd = &dlg; + INT_PTR nResponse = dlg.DoModal(); + if (nResponse == IDOK) + { + // TODO: Place code here to handle when the dialog is + // dismissed with OK + } + else if (nResponse == IDCANCEL) + { + // TODO: Place code here to handle when the dialog is + // dismissed with Cancel + } + + // Since the dialog has been closed, return FALSE so that we exit the + // application, rather than start the application's message pump. + return FALSE; +} diff --git a/ext/PropertyGrid/test/PropGrid.h b/ext/PropertyGrid/test/PropGrid.h new file mode 100644 index 0000000..70a0de8 --- /dev/null +++ b/ext/PropertyGrid/test/PropGrid.h @@ -0,0 +1,31 @@ +// PropGrid.h : main header file for the PROJECT_NAME application +// + +#pragma once + +#ifndef __AFXWIN_H__ +#error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols + + +// CPropGridApp: +// See PropGrid.cpp for the implementation of this class +// + +class CPropGridApp : public CWinApp +{ +public: + CPropGridApp(); + + // Overrides +public: + virtual BOOL InitInstance(); + + // Implementation + + DECLARE_MESSAGE_MAP() +}; + +extern CPropGridApp theApp; \ No newline at end of file diff --git a/ext/PropertyGrid/test/PropGrid.rc b/ext/PropertyGrid/test/PropGrid.rc new file mode 100644 index 0000000..106245c --- /dev/null +++ b/ext/PropertyGrid/test/PropGrid.rc @@ -0,0 +1,219 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROPGRID_DIALOG DIALOGEX 0, 0, 358, 188 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +CAPTION "PropGrid" +FONT 8, "MS Shell Dlg", 400, 0, 0x0 +BEGIN + CONTROL "Categorized",IDC_CATEGORIZED,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,272,22,55,10 + CONTROL "Alphabetical",IDC_ALPHABETICAL,"Button", + BS_AUTORADIOBUTTON,272,35,55,10 + CONTROL "No Sort",IDC_NOSORT,"Button",BS_AUTORADIOBUTTON,272,48, + 55,10 + CONTROL "Custom colors",IDC_CUSTOM_COLORS,"Button", + BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,256,72,95,10 + PUSHBUTTON "Collapse",IDC_COLLAPSE_ALL,256,85,45,14 + PUSHBUTTON "Expand",IDC_EXPAND_ALL,306,85,45,14 + DEFPUSHBUTTON "OK",IDOK,301,144,50,16 + CONTROL "",IDC_GRID,"Static",SS_BLACKFRAME | SS_NOTIFY,7,7,239, + 174,WS_EX_TRANSPARENT | WS_EX_CLIENTEDGE + GROUPBOX "Display Mode",IDC_STATIC,256,7,95,63 + PUSHBUTTON "Cancel",IDCANCEL,301,165,50,16 +END + +IDD_RECT_EDIT DIALOGEX 0, 0, 123, 114 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Rectangle" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,9,92,50,14 + PUSHBUTTON "Cancel",IDCANCEL,63,92,50,14 + RTEXT "Left",IDC_STATIC,20,18,24,8 + RTEXT "Top",IDC_STATIC,20,33,24,8 + RTEXT "Right",IDC_STATIC,20,48,24,8 + RTEXT "Bottom",IDC_STATIC,20,63,24,8 + EDITTEXT IDC_LEFT,52,15,53,14,ES_AUTOHSCROLL + EDITTEXT IDC_TOP,52,30,53,14,ES_AUTOHSCROLL + EDITTEXT IDC_RIGHT,52,45,53,14,ES_AUTOHSCROLL + EDITTEXT IDC_BOTTOM,52,60,53,14,ES_AUTOHSCROLL + GROUPBOX "",IDC_STATIC,7,3,109,80 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "TODO: " + VALUE "FileDescription", "TODO: " + VALUE "FileVersion", "1.0.0.1" + VALUE "InternalName", "PropGrid.exe" + VALUE "LegalCopyright", "TODO: (c) . All rights reserved." + VALUE "OriginalFilename", "PropGrid.exe" + VALUE "ProductName", "TODO: " + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PROPGRID_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 351 + TOPMARGIN, 7 + BOTTOMMARGIN, 181 + END + + IDD_RECT_EDIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 116 + TOPMARGIN, 7 + BOTTOMMARGIN, 107 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#include ""res\\PropGrid.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON "res\\PropGrid.ico" +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE 9, 1 +#pragma code_page(1252) +#include "res\PropGrid.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/ext/PropertyGrid/test/PropGridDlg.cpp b/ext/PropertyGrid/test/PropGridDlg.cpp new file mode 100644 index 0000000..dbf7778 --- /dev/null +++ b/ext/PropertyGrid/test/PropGridDlg.cpp @@ -0,0 +1,238 @@ +// PropGridDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "PropGrid.h" +#include "PropGridDlg.h" +#include ".\propgriddlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +// CPropGridDlg dialog + + + +CPropGridDlg::CPropGridDlg(CWnd* pParent /*=NULL*/) +: CDialog(CPropGridDlg::IDD, pParent) +{ + m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); +} + +void CPropGridDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_GRID, m_ctrlGrid); +} + +BEGIN_MESSAGE_MAP(CPropGridDlg, CDialog) + ON_WM_PAINT() + ON_WM_QUERYDRAGICON() + ON_BN_CLICKED(IDOK, OnBnClickedOk) + ON_MESSAGE(WM_PG_ITEMCHANGED, OnItemChanged) + ON_BN_CLICKED(IDC_COLLAPSE_ALL, OnBnClickedCollapseAll) + ON_BN_CLICKED(IDC_EXPAND_ALL, OnBnClickedExpandAll) + ON_BN_CLICKED(IDC_CUSTOM_COLORS, OnBnClickedCustomColors) + ON_BN_CLICKED(IDC_CATEGORIZED, OnBnClickedCategorized) + ON_BN_CLICKED(IDC_ALPHABETICAL, OnBnClickedAlphabetical) + ON_BN_CLICKED(IDC_NOSORT, OnBnClickedNosort) +END_MESSAGE_MAP() + + +// CPropGridDlg message handlers + +BOOL CPropGridDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + // Set the icon for this dialog. The framework does this automatically + // when the application's main window is not a dialog + SetIcon(m_hIcon, TRUE); // Set big icon + SetIcon(m_hIcon, FALSE); // Set small icon + + // basic items + HSECTION hs = m_ctrlGrid.AddSection("Basic Items"); + m_ctrlGrid.AddStringItem(hs, "String", "A single line string item"); + m_ctrlGrid.AddTextItem(hs, "Text", "A multi line text item.\r\nSecond line..."); + m_ctrlGrid.AddStringItem(hs, "Disabled item", "A disabled item", false); + m_ctrlGrid.AddIntegerItem(hs, "Integer", 10); + m_ctrlGrid.AddIntegerItem(hs, "Formatted Integer", 8, "%d inches"); + m_ctrlGrid.AddDoubleItem(hs, "Double", 7.33); + m_ctrlGrid.AddDoubleItem(hs, "Formatted Double", 10.25, "%gmm"); + m_ctrlGrid.AddBoolItem(hs, "Boolean", true); + + // custom items + hs = m_ctrlGrid.AddSection("Custom Items"); + m_ctrlGrid.AddCustomItem(hs, "Custom In-place Edit", &m_item_serial); + m_ctrlGrid.AddCustomItem(hs, "Custom Modal Edit", &m_item_rect); + m_ctrlGrid.AddCustomItem(hs, "Ccustom Dropdown Edit", &m_item_tree); + m_ctrlGrid.AddCustomItem(hs, "Full Custom Edit", &m_item_gradient); + + // more items inserted before the custom ones + hs = m_ctrlGrid.AddSection("More Items", true, hs); + + // a combo + vector v; + v.push_back("English"); + v.push_back("French"); + v.push_back("German"); + v.push_back("Spanish"); + m_ctrlGrid.AddComboItem(hs, "Combo", v, 2, true); + + // some "simple" + m_ctrlGrid.AddDateItem(hs, "Date", COleDateTime::GetCurrentTime()); + m_ctrlGrid.AddDateItem(hs, "Formatted Date", COleDateTime::GetCurrentTime(), "%A, %d %B %Y"); + m_ctrlGrid.AddDateTimeItem(hs, "Date Time", COleDateTime::GetCurrentTime()); + m_ctrlGrid.AddColorItem(hs, "Color", RGB(255,156,12)); + m_ctrlGrid.AddFileItem(hs, "File", "C:\\AUTOEXEC.BAT", "All Files (*.*)|*.*||"); + m_ctrlGrid.AddFolderItem(hs, "Folder", "C:\\", "Select a folder"); + + // a font + LOGFONT lf; + CFont font; + font.CreatePointFont(80, "Tahoma"); + font.GetLogFont(&lf); + m_ctrlGrid.AddFontItem(hs, "Font", lf, true); + + // customization + hs = m_ctrlGrid.AddSection("Grid customization"); + m_hItemShade = m_ctrlGrid.AddBoolItem(hs, "Shade titles", m_ctrlGrid.GetShadeTitles()); + m_hItemLines = m_ctrlGrid.AddBoolItem(hs, "Draw lines", m_ctrlGrid.GetDrawLines()); + m_hItemGutter = m_ctrlGrid.AddBoolItem(hs, "Draw gutter", m_ctrlGrid.GetDrawGutter()); + m_hItemFocusDis = m_ctrlGrid.AddBoolItem(hs, "Focus disabled items", m_ctrlGrid.GetFocusDisabled()); + + // stuff + m_ctrlGrid.SetOkCancelStrings("OK", "Annuler"); + m_ctrlGrid.SetDateTimeStrings("Date", "Heure"); + + // + ((CButton*)GetDlgItem(IDC_CATEGORIZED))->SetCheck(1); + + return TRUE; // return TRUE unless you set the focus to a control +} + +// If you add a minimize button to your dialog, you will need the code below +// to draw the icon. For MFC applications using the document/view model, +// this is automatically done for you by the framework. + +void CPropGridDlg::OnPaint() +{ + if (IsIconic()) + { + CPaintDC dc(this); // device context for painting + + SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); + + // Center icon in client rectangle + int cxIcon = GetSystemMetrics(SM_CXICON); + int cyIcon = GetSystemMetrics(SM_CYICON); + CRect rect; + GetClientRect(&rect); + int x = (rect.Width() - cxIcon + 1) / 2; + int y = (rect.Height() - cyIcon + 1) / 2; + + // Draw the icon + dc.DrawIcon(x, y, m_hIcon); + } + else + { + CDialog::OnPaint(); + } +} + +// The system calls this function to obtain the cursor to display while the user drags +// the minimized window. +HCURSOR CPropGridDlg::OnQueryDragIcon() +{ + return static_cast(m_hIcon); +} + +void CPropGridDlg::OnBnClickedOk() +{ + //string strValue; + //if (m_ctrlGrid.GetItemValue(m_hItem, strValue)) + // AfxMessageBox(strValue.c_str()); + OnOK(); +} + +LRESULT CPropGridDlg::OnItemChanged(WPARAM wParam, LPARAM lParam) +{ + if (wParam == m_hItemLines) + { + bool draw_lines; + m_ctrlGrid.GetItemValue(m_hItemLines, draw_lines); + m_ctrlGrid.SetDrawLines(draw_lines); + } + else if (wParam == m_hItemShade) + { + bool shade_titles; + m_ctrlGrid.GetItemValue(m_hItemShade, shade_titles); + m_ctrlGrid.SetShadeTitles(shade_titles); + } + else if (wParam == m_hItemGutter) + { + bool draw_gutter; + m_ctrlGrid.GetItemValue(m_hItemGutter, draw_gutter); + m_ctrlGrid.SetDrawGutter(draw_gutter); + } + else if (wParam == m_hItemFocusDis) + { + bool focus_disabled; + m_ctrlGrid.GetItemValue(m_hItemFocusDis, focus_disabled); + m_ctrlGrid.SetFocusDisabled(focus_disabled); + } + + return 0; +} + +void CPropGridDlg::OnBnClickedCollapseAll() +{ + m_ctrlGrid.ExpandAll(false); +} + +void CPropGridDlg::OnBnClickedExpandAll() +{ + m_ctrlGrid.ExpandAll(true); +} + +void CPropGridDlg::OnBnClickedCustomColors() +{ + CButton* pBtn = (CButton*) GetDlgItem(IDC_CUSTOM_COLORS); + if (pBtn->GetCheck() == 0) + { + m_ctrlGrid.SetBackColor(GetSysColor(COLOR_WINDOW)); + m_ctrlGrid.SetShadeColor(GetSysColor(COLOR_3DFACE)); + m_ctrlGrid.SetTextColor(GetSysColor(COLOR_WINDOWTEXT)); + m_ctrlGrid.SetTitleColor(GetSysColor(COLOR_WINDOWTEXT)); + } + else + { + m_ctrlGrid.SetBackColor(RGB(0xFF, 0xFF, 0xE0)); + m_ctrlGrid.SetShadeColor(RGB(0,187,94)); + m_ctrlGrid.SetTextColor(RGB(0,0,192)); + m_ctrlGrid.SetTitleColor(RGB(255,255,255)); + } +} + +void CPropGridDlg::OnBnClickedCategorized() +{ + m_ctrlGrid.SetDisplayMode(CPropertyGrid::DM_CATEGORIZED); + GetDlgItem(IDC_EXPAND_ALL)->EnableWindow(TRUE); + GetDlgItem(IDC_COLLAPSE_ALL)->EnableWindow(TRUE); +} + +void CPropGridDlg::OnBnClickedAlphabetical() +{ + m_ctrlGrid.SetDisplayMode(CPropertyGrid::DM_ALPHABETICAL); + GetDlgItem(IDC_EXPAND_ALL)->EnableWindow(FALSE); + GetDlgItem(IDC_COLLAPSE_ALL)->EnableWindow(FALSE); +} + +void CPropGridDlg::OnBnClickedNosort() +{ + m_ctrlGrid.SetDisplayMode(CPropertyGrid::DM_NOSORT); + GetDlgItem(IDC_EXPAND_ALL)->EnableWindow(FALSE); + GetDlgItem(IDC_COLLAPSE_ALL)->EnableWindow(FALSE); +} diff --git a/ext/PropertyGrid/test/PropGridDlg.h b/ext/PropertyGrid/test/PropGridDlg.h new file mode 100644 index 0000000..75bdda0 --- /dev/null +++ b/ext/PropertyGrid/test/PropGridDlg.h @@ -0,0 +1,54 @@ +// PropGridDlg.h : header file +// + +#pragma once +#include "..\PropertyGrid.h" +#include "GradientItem.h" +#include "SerialItem.h" +#include "RectItem.h" +#include "TreeItem.h" + +// CPropGridDlg dialog +class CPropGridDlg : public CDialog +{ + // Construction +public: + CPropGridDlg(CWnd* pParent = NULL); // standard constructor + + // Dialog Data + enum { IDD = IDD_PROPGRID_DIALOG }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + +protected: + CGradientItem m_item_gradient; + CSerialItem m_item_serial; + CRectItem m_item_rect; + CTreeItem m_item_tree; + + // Implementation +protected: + HICON m_hIcon; + HITEM m_hItemLines; + HITEM m_hItemShade; + HITEM m_hItemGutter; + HITEM m_hItemFocusDis; + + // Generated message map functions + virtual BOOL OnInitDialog(); + afx_msg void OnPaint(); + afx_msg HCURSOR OnQueryDragIcon(); + DECLARE_MESSAGE_MAP() + +public: + CPropertyGrid m_ctrlGrid; + afx_msg LRESULT OnItemChanged(WPARAM, LPARAM); + afx_msg void OnBnClickedOk(); + afx_msg void OnBnClickedCollapseAll(); + afx_msg void OnBnClickedExpandAll(); + afx_msg void OnBnClickedCustomColors(); + afx_msg void OnBnClickedCategorized(); + afx_msg void OnBnClickedAlphabetical(); + afx_msg void OnBnClickedNosort(); +}; diff --git a/ext/PropertyGrid/test/RectEditDlg.cpp b/ext/PropertyGrid/test/RectEditDlg.cpp new file mode 100644 index 0000000..785058c --- /dev/null +++ b/ext/PropertyGrid/test/RectEditDlg.cpp @@ -0,0 +1,43 @@ +// RectEditDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "PropGrid.h" +#include "RectEditDlg.h" + + +// CRectEditDlg dialog + +IMPLEMENT_DYNAMIC(CRectEditDlg, CDialog) +CRectEditDlg::CRectEditDlg(CWnd* pParent /*=NULL*/) +: CDialog(CRectEditDlg::IDD, pParent) +, m_left(10) +, m_top(10) +, m_right(10) +, m_bottom(10) +{ +} + +CRectEditDlg::~CRectEditDlg() +{ +} + +void CRectEditDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Text(pDX, IDC_LEFT, m_left); + DDV_MinMaxInt(pDX, m_left, 1, 1000); + DDX_Text(pDX, IDC_TOP, m_top); + DDV_MinMaxInt(pDX, m_top, 1, 1000); + DDX_Text(pDX, IDC_RIGHT, m_right); + DDV_MinMaxInt(pDX, m_right, 1, 1000); + DDX_Text(pDX, IDC_BOTTOM, m_bottom); + DDV_MinMaxInt(pDX, m_bottom, 1, 1000); +} + + +BEGIN_MESSAGE_MAP(CRectEditDlg, CDialog) +END_MESSAGE_MAP() + + +// CRectEditDlg message handlers diff --git a/ext/PropertyGrid/test/RectEditDlg.h b/ext/PropertyGrid/test/RectEditDlg.h new file mode 100644 index 0000000..5e9708b --- /dev/null +++ b/ext/PropertyGrid/test/RectEditDlg.h @@ -0,0 +1,26 @@ +#pragma once +#include "resource.h" + +// CRectEditDlg dialog + +class CRectEditDlg : public CDialog +{ + DECLARE_DYNAMIC(CRectEditDlg) + +public: + CRectEditDlg(CWnd* pParent = NULL); // standard constructor + virtual ~CRectEditDlg(); + + // Dialog Data + enum { IDD = IDD_RECT_EDIT }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +public: + int m_left; + int m_top; + int m_right; + int m_bottom; +}; diff --git a/ext/PropertyGrid/test/RectItem.cpp b/ext/PropertyGrid/test/RectItem.cpp new file mode 100644 index 0000000..cd071c7 --- /dev/null +++ b/ext/PropertyGrid/test/RectItem.cpp @@ -0,0 +1,46 @@ +#include "StdAfx.h" +#include "RectItem.h" +#include "RectEditDlg.h" + +CRectItem::CRectItem(void) +{ + m_left = 20; + m_top = 20; + m_right = 100; + m_bottom = 100; +} + +CRectItem::~CRectItem(void) +{ +} + +CPropertyGrid::EEditMode CRectItem::GetEditMode() +{ + return CPropertyGrid::EM_MODAL; +} + +void CRectItem::DrawItem(CDC& dc, CRect rc, bool focused) +{ + CString str; + str.Format("%d; %d; %d; %d", m_left, m_top, m_right, m_bottom); + rc.left += m_pGrid->GetTextMargin(); + dc.DrawText(str, rc, DT_SINGLELINE|DT_LEFT|DT_VCENTER|DT_END_ELLIPSIS|DT_NOPREFIX); +} + +bool CRectItem::OnEditItem() +{ + CRectEditDlg dlg(m_pGrid); + dlg.m_left = m_left; + dlg.m_top = m_top; + dlg.m_right = m_right; + dlg.m_bottom = m_bottom; + if (dlg.DoModal() == IDOK) + { + m_left = dlg.m_left; + m_top = dlg.m_top; + m_right = dlg.m_right; + m_bottom = dlg.m_bottom; + return true; + } + return false; +} diff --git a/ext/PropertyGrid/test/RectItem.h b/ext/PropertyGrid/test/RectItem.h new file mode 100644 index 0000000..c347ce9 --- /dev/null +++ b/ext/PropertyGrid/test/RectItem.h @@ -0,0 +1,19 @@ +#pragma once +#include "..\CustomItem.h" + +class CRectItem : public ICustomItem +{ +public: + CRectItem(void); + ~CRectItem(void); + + virtual CPropertyGrid::EEditMode GetEditMode(); + virtual void DrawItem(CDC& dc, CRect rc, bool focused); + virtual bool OnEditItem(); + +protected: + int m_left; + int m_top; + int m_right; + int m_bottom; +}; diff --git a/ext/PropertyGrid/test/SerialItem.cpp b/ext/PropertyGrid/test/SerialItem.cpp new file mode 100644 index 0000000..9aa6fe0 --- /dev/null +++ b/ext/PropertyGrid/test/SerialItem.cpp @@ -0,0 +1,44 @@ +#include "StdAfx.h" +#include ".\serialitem.h" + +CSerialItem::CSerialItem(void) +{ + m_serial = "12348765"; +} + +CSerialItem::~CSerialItem(void) +{ +} + +CPropertyGrid::EEditMode CSerialItem::GetEditMode() +{ + return CPropertyGrid::EM_INPLACE; +} + +void CSerialItem::DrawItem(CDC& dc, CRect rc, bool focused) +{ + string serial = m_serial; + while (serial.length()<8) serial += " "; + serial = serial.substr(0,4) + "-" + serial.substr(4,4); + rc.left += m_pGrid->GetTextMargin(); + dc.DrawText(serial.c_str(), rc, DT_SINGLELINE|DT_LEFT|DT_VCENTER|DT_END_ELLIPSIS|DT_NOPREFIX); +} + +string CSerialItem::GetStringForInPlaceEdit() +{ + return m_serial; +} + +bool CSerialItem::OnItemEdited(string strNewValue) +{ + if (strNewValue.length()!=8) + { + AfxMessageBox("Invalid serial number"); + return false; + } + else + { + m_serial = strNewValue; + return true; + } +} diff --git a/ext/PropertyGrid/test/SerialItem.h b/ext/PropertyGrid/test/SerialItem.h new file mode 100644 index 0000000..8dbed1d --- /dev/null +++ b/ext/PropertyGrid/test/SerialItem.h @@ -0,0 +1,16 @@ +#pragma once +#include "..\CustomItem.h" + +class CSerialItem : public ICustomItem +{ +public: + CSerialItem(void); + ~CSerialItem(void); + virtual CPropertyGrid::EEditMode GetEditMode(); + virtual void DrawItem(CDC& dc, CRect rc, bool focused); + virtual string GetStringForInPlaceEdit(); + virtual bool OnItemEdited(string strNewValue); + +protected: + string m_serial; +}; diff --git a/ext/PropertyGrid/test/TreeItem.cpp b/ext/PropertyGrid/test/TreeItem.cpp new file mode 100644 index 0000000..defde91 --- /dev/null +++ b/ext/PropertyGrid/test/TreeItem.cpp @@ -0,0 +1,47 @@ +#include "StdAfx.h" +#include ".\treeitem.h" + +CTreeItem::CTreeItem(void) +{ +} + +CTreeItem::~CTreeItem(void) +{ +} + +void CTreeItem::SetValue(string value) +{ + m_value = value; + m_pGrid->Invalidate(); +} + +CPropertyGrid::EEditMode CTreeItem::GetEditMode() +{ + return CPropertyGrid::EM_DROPDOWN; +} + +void CTreeItem::DrawItem(CDC& dc, CRect rc, bool focused) +{ + rc.left += m_pGrid->GetTextMargin(); + dc.DrawText(m_value.c_str(), rc, DT_SINGLELINE|DT_LEFT|DT_VCENTER|DT_END_ELLIPSIS|DT_NOPREFIX); +} + +void CTreeItem::ShowDropDown(CRect rc) +{ + rc.top += rc.Height(); + rc.bottom = rc.top + 100; + m_pGrid->ClientToScreen(&rc); + m_tree = new CCustomTreeCtrl; + m_tree->m_item = this; + m_tree->CWnd::CreateEx(0, WC_TREEVIEW, NULL, WS_POPUP|WS_BORDER|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT, rc, m_pGrid->GetParent(), 0); + m_tree->SetOwner(m_pGrid->GetParent()); + HTREEITEM hf = m_tree->InsertItem("Folder 1"); + m_tree->InsertItem("Item 1", hf); + m_tree->InsertItem("Item 2", hf); + m_tree->Expand(hf, TVE_EXPAND); + hf = m_tree->InsertItem("Folder 2"); + m_tree->InsertItem("Item 3", hf); + m_tree->Expand(hf, TVE_EXPAND); + m_tree->ShowWindow(SW_SHOW); + m_tree->RedrawWindow(); +} diff --git a/ext/PropertyGrid/test/TreeItem.h b/ext/PropertyGrid/test/TreeItem.h new file mode 100644 index 0000000..015027c --- /dev/null +++ b/ext/PropertyGrid/test/TreeItem.h @@ -0,0 +1,21 @@ +#pragma once +#include "..\customitem.h" +#include "CustomTreeCtrl.h" + +class CTreeItem : + public ICustomItem +{ +public: + CTreeItem(void); + ~CTreeItem(void); + + virtual CPropertyGrid::EEditMode GetEditMode(); + virtual void DrawItem(CDC& dc, CRect rc, bool focused); + virtual void ShowDropDown(CRect rc); + + void SetValue(string value); + +protected: + string m_value; + CCustomTreeCtrl* m_tree; +}; diff --git a/ext/PropertyGrid/test/res/PropGrid.ico b/ext/PropertyGrid/test/res/PropGrid.ico new file mode 100644 index 0000000..8a84ca3 Binary files /dev/null and b/ext/PropertyGrid/test/res/PropGrid.ico differ diff --git a/ext/PropertyGrid/test/res/PropGrid.manifest b/ext/PropertyGrid/test/res/PropGrid.manifest new file mode 100644 index 0000000..d9d8236 --- /dev/null +++ b/ext/PropertyGrid/test/res/PropGrid.manifest @@ -0,0 +1,22 @@ + + + +Your app description here + + + + + + diff --git a/ext/PropertyGrid/test/res/PropGrid.rc2 b/ext/PropertyGrid/test/res/PropGrid.rc2 new file mode 100644 index 0000000..8cf8213 --- /dev/null +++ b/ext/PropertyGrid/test/res/PropGrid.rc2 @@ -0,0 +1,13 @@ +// +// PropGrid.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/ext/PropertyGrid/test/resource.h b/ext/PropertyGrid/test/resource.h new file mode 100644 index 0000000..ee195d7 --- /dev/null +++ b/ext/PropertyGrid/test/resource.h @@ -0,0 +1,31 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by PropGrid.rc +// +#define IDD_PROPGRID_DIALOG 102 +#define IDR_MAINFRAME 128 +#define IDD_RECT_EDIT 129 +#define IDC_GRID 1000 +#define IDC_COMBO1 1001 +#define IDC_LEFT 1002 +#define IDC_TOP 1003 +#define IDC_COLLAPSE_ALL 1003 +#define IDC_RIGHT 1004 +#define IDC_EXPAND_ALL 1004 +#define IDC_BOTTOM 1005 +#define IDC_CUSTOM_COLORS 1005 +#define IDC_CATEGORIZED 1010 +#define IDC_ALPHABETICAL 1011 +#define IDC_NOSORT 1012 +#define IDC_DATETIMEPICKER1 1013 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 130 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1014 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif