// ========================================================================== // Class Implementation : COXPropertiesWnd // ========================================================================== // Source file : OXPropertiesWnd.cpp // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. #include "stdafx.h" #include "OXPropertiesWnd.h" #include "OXListEdit.h" #include "OXMaskedEdit.h" #include "OXSkins.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #pragma warning(disable : 4355) ///////////////////////////////////////////////////////////////////////////// // COXPropertiesWnd COXPropertiesWnd::COXPropertiesWnd() : m_wndTree(this) { m_bSortCategories = TRUE; m_bCategorized = TRUE; m_hCurrentProp = NULL; m_pEditWnd = NULL; m_hEditProp = NULL; m_pFrameSkin = NULL; } COXPropertiesWnd::~COXPropertiesWnd() { m_mapProp2Cat.RemoveAll(); m_mapProp2Desc.RemoveAll(); m_mapProp2Editor.RemoveAll(); m_listCategories.RemoveAll(); } BEGIN_MESSAGE_MAP(COXPropertiesWnd, CFrameWnd) //{{AFX_MSG_MAP(COXPropertiesWnd) ON_WM_CREATE() ON_WM_SIZE() ON_COMMAND(ID_CATEGORIZED, OnCategorized) ON_COMMAND(ID_ALPHABETIC, OnAlphabetic) ON_WM_PAINT() ON_UPDATE_COMMAND_UI(ID_CATEGORIZED, OnUpdateCategorized) ON_UPDATE_COMMAND_UI(ID_ALPHABETIC, OnUpdateAlphabetic) //}}AFX_MSG_MAP ON_NOTIFY(LVN_ITEMCHANGED, _TREE_ID, OnItemChanged) ON_MESSAGE(WM_USER_QUERY_PROPERTIESWND, OnQueryPropertiesWnd) ON_MESSAGE(WM_USER_STARTEDITING, OnStartEditing) ON_MESSAGE(WM_USER_ADJUSTLASTCOLUMN, OnUserAdjustLastColumn) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXPropertiesWnd message handlers int COXPropertiesWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; ModifyStyleEx(WS_EX_CLIENTEDGE, 0); // Create the combo if (!m_wndCombo.Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, CRect(0, 0, 0, 300), this, _COMBO_ID)) { TRACE0("Failed to create the combo box.\n"); return -1; // fail to create } // Create the toolbar if (!m_wndToolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC, IDR_PROPERTIES_TOOLBAR) || !m_wndToolBar.LoadToolBar(IDR_PROPERTIES_TOOLBAR)) { TRACE0("Failed to create toolbar.\n"); return -1; // fail to create } m_wndToolBar.SetOwner(this); // Create the category list if (!m_wndTree.Create(WS_CHILD | WS_VISIBLE | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_SHOWSELALWAYS, CRect(0, 0, 0, 0), this, _TREE_ID, TVOXS_VGRID | TVOXS_HGRID | TVOXS_COLUMNHDR | TVOXS_NOSORTHEADER | TVOXS_NOFOCUSRECT | TVOXS_EXTENDCOLUMNS | TVOXS_ROWSEL | TVOXS_FLGRID)) { TRACE0("Failed to create the category list.\n"); return -1; // fail to create } // Create the description pane // Create the combo if (!m_wndDescription.Create(NULL, NULL, WS_CHILD | WS_VISIBLE, CRect(0, 0, 0, 0), this, _DESCRIPTION_ID)) { TRACE0("Failed to create the description pane.\n"); return -1; // fail to create } m_wndTree.SetHorizontalGridColor(::GetSysColor(COLOR_3DFACE)); m_wndTree.SetVerticalGridColor(::GetSysColor(COLOR_3DFACE)); // Set up the two columns LV_COLUMN lvc; memset(&lvc, 0, sizeof(lvc)); lvc.fmt = LVCFMT_LEFT; lvc.cx = 100; lvc.pszText = _T("Property"); lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; m_wndTree.SetColumn(0,&lvc); m_wndTree.InsertColumn(1, _T("Value"), LVCFMT_LEFT, 100, 1); m_wndTree.m_wndEdit.SetBorder(FALSE); return 0; } void COXPropertiesWnd::OnSize(UINT nType, int cx, int cy) { CFrameWnd::OnSize(nType, cx, cy); CRect rectClient; GetClientRect(rectClient); m_wndCombo.MoveWindow(rectClient); // Get the height of the combo box CRect rectCombo; m_wndCombo.GetClientRect(rectCombo); if (!(m_wndCombo.GetStyle() & WS_VISIBLE)) rectCombo.bottom = rectCombo.top; // Position the toolbar below the combo box CRect rectToolBar = rectClient; rectToolBar.top += rectCombo.Height(); rectToolBar.bottom = rectToolBar.top + 25; m_wndToolBar.MoveWindow(rectToolBar); if (!(m_wndToolBar.GetStyle() & WS_VISIBLE)) rectToolBar.bottom = rectToolBar.top; // Position the description pane at the bottom CRect rectDescription = rectClient; rectDescription.top = rectDescription.bottom - 50; m_wndDescription.MoveWindow(rectDescription); if (!(m_wndDescription.GetStyle() & WS_VISIBLE)) rectDescription.bottom = rectDescription.top; // Position the tree in the space that remains CRect rectTree = rectClient; rectTree.top += rectCombo.Height() + rectToolBar.Height(); rectTree.bottom -= rectDescription.Height(); rectTree.DeflateRect(1, 1); m_wndTree.MoveWindow(rectTree); // If an item is being edited reposition the editor if (m_pEditWnd != NULL) { HPROPERTY hSelectedProp = GetSelectedProperty(); CRect rectSubItem; int iItemIndex = m_wndTree.GetItemIndex(hSelectedProp); m_wndTree.GetSubItemRect(iItemIndex, 1, LVIR_LABEL, rectSubItem); rectSubItem.DeflateRect(0, 0, 1, 1); m_pEditWnd->MoveWindow(rectSubItem); } } void COXPropertiesWnd::OnCategorized() { if (m_bCategorized) return; m_bCategorized = TRUE; m_wndTree.ModifyStyle(0, TVS_LINESATROOT); // Insert the categories POSITION catpos = m_listCategories.GetHeadPosition(); for(int i=0; iIsKindOf(RUNTIME_CLASS(CWnd))); } HCATEGORY hCategory = NULL; if (lpszCategory != NULL) { hCategory= FindCategory(lpszCategory); if (hCategory == NULL) { hCategory = InsertCategory(lpszCategory); CString tmpStr = lpszCategory; m_listCategories.AddTail(tmpStr); } } HPROPERTY hProp = m_wndTree.InsertItem(lpszName, hCategory); if (hProp == NULL) return NULL; // Insert the sub item m_wndTree.SetSubItem(hProp, 1, 0, NULL); // Set up the editing mode to none CStringArray saDummy; m_wndTree.SetEditMode(hProp, OXET_NOEDIT, saDummy, 0); m_wndTree.SetEditMode(hProp, OXET_NOEDIT, saDummy, 1); // Sort if (hCategory != NULL) m_wndTree.SortChildren(hCategory); else { if(m_bSortCategories) m_wndTree.SortChildren(m_wndTree.GetRootItem()); else m_wndTree.MoveItem(hProp,m_wndTree.GetRootItem(),TVI_FIRST); } // Insert an entry in the property to category map CString strCategory = _T(""); if (hCategory != NULL) strCategory = m_wndTree.GetItemText(hCategory); m_mapProp2Cat.SetAt(hProp, strCategory); // Insert an entry in the property to description map m_mapProp2Desc.SetAt(hProp, lpszDescription); // Insert an entry to the editor map if (pEditorClass != NULL) m_mapProp2Editor.SetAt(hProp, pEditorClass); // Set the initial value (if any) if (lpszInitialValue != NULL) SetPropertyValue(hProp, lpszInitialValue); return hProp; } BOOL COXPropertiesWnd::DeleteProperty(HPROPERTY hProperty) { ASSERT(hProperty != NULL); FinishEditing(); if (!OnDeleteProperty(hProperty)) return FALSE; HTREEITEM hParent = m_wndTree.GetParentItem(hProperty); // Remove the property handle from the maps m_mapProp2Cat.RemoveKey(hProperty); m_mapProp2Desc.RemoveKey(hProperty); m_mapProp2Editor.RemoveKey(hProperty); BOOL bRes = m_wndTree.DeleteItem((HTREEITEM) hProperty); if (hParent != m_wndTree.GetRootItem()) { // The parent is a category COXTreeItem* pParent = m_wndTree.GetXItem(hParent); if (pParent->GetChildrenCount() == 0) { CString strCategory = m_wndTree.GetItemText(hParent); if(POSITION pos = m_listCategories.Find(strCategory)) { m_listCategories.RemoveAt(pos); } m_wndTree.DeleteItem(hParent); // delete the empty category } } return bRes; } BOOL COXPropertiesWnd::SetPropertyValue(HPROPERTY hProperty, LPCTSTR lpszValue) { ASSERT(hProperty != NULL); return m_wndTree.SetItemText(hProperty, lpszValue, 1); } CString COXPropertiesWnd::GetPropertyValue(HPROPERTY hProperty) { ASSERT(hProperty != NULL); return m_wndTree.GetItemText(hProperty, 1); } HPROPERTY COXPropertiesWnd::FindProperty(LPCTSTR lpszName, LPCTSTR lpszCategory) { // First try to find the category HCATEGORY hCategory = NULL; if (lpszCategory != NULL) hCategory = FindCategory(lpszCategory); HTREEITEM hParent = hCategory; if (hParent == NULL) hParent = m_wndTree.GetRootItem(); // At this point we know that if the property exist it is a child of hParent if (m_wndTree.ItemHasChildren(hParent)) { HTREEITEM hNextItem, hChildItem = m_wndTree.GetChildItem(hParent); while (hChildItem != NULL) { hNextItem = m_wndTree.GetNextItem(hChildItem, TVGN_NEXT); if (m_wndTree.GetItemText(hChildItem) == lpszName) return hChildItem; hChildItem = hNextItem; } } return NULL; } void COXPropertiesWnd::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here CRect rectClient; GetClientRect(rectClient); dc.FillSolidRect(rectClient, GetFrameSkin()->GetBackgroundColor()); CBrush brBackground; brBackground.CreateSolidBrush(::GetSysColor(COLOR_3DFACE)); dc.FrameRect(rectClient, &brBackground); // Draw a gray border around the tree CRect rectTree; m_wndTree.GetWindowRect(rectTree); ScreenToClient(rectTree); rectTree.InflateRect(1, 1); CBrush brBorder; brBorder.CreateSolidBrush(GetFrameSkin()->GetBorderColor()); dc.FrameRect(rectTree, &brBorder); } void COXPropertiesWnd::PostNcDestroy() { // DO NOTHING } void COXPropertiesWnd::OnUpdateCategorized(CCmdUI* pCmdUI) { pCmdUI->SetCheck(m_bCategorized); } void COXPropertiesWnd::OnUpdateAlphabetic(CCmdUI* pCmdUI) { pCmdUI->SetCheck(!m_bCategorized); } void COXPropertiesWnd::SetCategorized(BOOL bCategorized) { m_bCategorized = bCategorized; if (bCategorized) OnCategorized(); else OnAlphabetic(); } CComboBoxEx& COXPropertiesWnd::GetComboBoxEx() { return m_wndCombo; } COXCoolToolBar& COXPropertiesWnd::GetToolBar() { return m_wndToolBar; } void COXPropertiesWnd::ShowCombo(BOOL bShow) { if (bShow) m_wndCombo.ModifyStyle(0, WS_VISIBLE); else m_wndCombo.ModifyStyle(WS_VISIBLE, 0); } void COXPropertiesWnd::ShowToolBar(BOOL bShow) { if (bShow) m_wndToolBar.ModifyStyle(0, WS_VISIBLE); else m_wndToolBar.ModifyStyle(WS_VISIBLE, 0); } void COXPropertiesWnd::ShowDescription(BOOL bShow) { if (bShow) m_wndDescription.ModifyStyle(0, WS_VISIBLE); else m_wndDescription.ModifyStyle(WS_VISIBLE, 0); } HPROPERTY COXPropertiesWnd::GetSelectedProperty() { HTREEITEM hSelected = m_wndTree.GetNextItem(TVI_ROOT, TVGN_FIRSTSELECTED); if (hSelected == NULL) return NULL; // no property is selected CString strDummy; if (m_mapProp2Cat.Lookup(hSelected, strDummy)) return hSelected; // this is indeed a property; else return NULL; // it is a category } BOOL COXPropertiesWnd::OnPropertyValueChanged(HPROPERTY /*hProperty*/, LPCTSTR /*lpszOldValue*/, LPCTSTR /*lpszNewValue*/) { return TRUE; // accept the new value } void COXPropertiesWnd::OnItemChanged(NMHDR* /*pNMHDR*/, LRESULT* /*pResult*/) { CString strName, strDescription; HPROPERTY hSelectedProp = GetSelectedProperty(); if (hSelectedProp == NULL) { strName = _T(""); strDescription = _T(""); } else { strName = m_wndTree.GetItemText(hSelectedProp); m_mapProp2Desc.Lookup(hSelectedProp, strDescription); SendMessage(WM_USER_STARTEDITING, 0, (LPARAM) hSelectedProp); } m_wndDescription.SetDescription(strName, strDescription); } LRESULT COXPropertiesWnd::OnQueryPropertiesWnd(WPARAM /*wParam*/, LPARAM /*lParam*/) { return TRUE; // yes, this is a COXPropertiesWnd } LRESULT COXPropertiesWnd::OnStartEditing(WPARAM /*wParam*/, LPARAM lParam) { HPROPERTY hSelectedProp = (HPROPERTY) lParam; // Finish editing first if (m_pEditWnd != NULL) FinishEditing(); else StartEditing(hSelectedProp); return 0; } void COXPropertiesWnd::StartEditing(HPROPERTY hSelectedProp) { // Create the editing control and place it over the sub-item CEdit* pEditor; BOOL bFound = m_mapProp2Editor.Lookup(hSelectedProp, pEditor); if (bFound) { m_pEditWnd = pEditor; CRect rectSubItem; int iItemIndex = m_wndTree.GetItemIndex(hSelectedProp); m_wndTree.GetSubItemRect(iItemIndex, 1, LVIR_LABEL, rectSubItem); rectSubItem.DeflateRect(0, 0, 1, 1); // Create the editor window if not created already if (!::IsWindow(m_pEditWnd->m_hWnd)) m_pEditWnd->Create(WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, rectSubItem, &m_wndTree, _EDITOR_ID); else { m_pEditWnd->MoveWindow(rectSubItem); m_pEditWnd->ShowWindow(SW_SHOW); } m_pEditWnd->SetOwner(&m_wndTree); m_pEditWnd->SetFont(m_wndTree.GetFont()); // Set the text of the editing window CString strText = GetPropertyValue(hSelectedProp); COXEdit* pOXEdit = DYNAMIC_DOWNCAST(COXEdit, m_pEditWnd); COXMaskedEdit* pOXMaskedEdit = DYNAMIC_DOWNCAST(COXMaskedEdit, m_pEditWnd); if (pOXEdit != NULL) { pOXEdit->EmptyData(); pOXEdit->SetInputData(strText); } else if (pOXMaskedEdit != NULL) pOXMaskedEdit->SetText(strText); else m_pEditWnd->SetWindowText(strText); m_hEditProp = hSelectedProp; } } void COXPropertiesWnd::FinishEditing(BOOL bSave) { if (m_hEditProp == NULL) { return; } if (bSave) { // Call OnPropertyValueChanged(...) and save the value CString strText; COXMaskedEdit* pMaskedEdit = DYNAMIC_DOWNCAST(COXMaskedEdit, m_pEditWnd); if (pMaskedEdit != NULL) strText = pMaskedEdit->GetText(); else m_pEditWnd->GetWindowText(strText); BOOL bSaveValue = OnPropertyValueChanged(m_hEditProp, GetPropertyValue(m_hEditProp), strText); if (bSaveValue) SetPropertyValue(m_hEditProp, strText); } // Hide the editor m_pEditWnd->ShowWindow(SW_HIDE); m_pEditWnd = NULL; m_hEditProp = NULL; m_wndTree.SelectItem(NULL); } BOOL COXPropertiesWnd::PreTranslateMessage(MSG* pMsg) { return CFrameWnd::PreTranslateMessage(pMsg); } LRESULT COXPropertiesWnd::OnUserAdjustLastColumn(WPARAM /*wParam*/, LPARAM /*lParam*/) { // If an item is being edited reposition the editor if (m_pEditWnd != NULL) { HPROPERTY hSelectedProp = GetSelectedProperty(); CRect rectSubItem; int iItemIndex = m_wndTree.GetItemIndex(hSelectedProp); m_wndTree.GetSubItemRect(iItemIndex, 1, LVIR_LABEL, rectSubItem); rectSubItem.DeflateRect(0, 0, 1, 1); m_pEditWnd->MoveWindow(rectSubItem); } return 0; } BOOL COXPropertiesWnd::IsDescendant(CWnd* pWndParent, CWnd* pWndChild) // helper for detecting whether child descendent of parent // (works with owned popups as well) { ASSERT(pWndParent!=NULL); ASSERT(::IsWindow(pWndParent->GetSafeHwnd())); ASSERT(pWndChild!=NULL); ASSERT(::IsWindow(pWndChild->GetSafeHwnd())); HWND hWndParent=pWndParent->GetSafeHwnd(); HWND hWndChild=pWndChild->GetSafeHwnd(); do { if (hWndParent == hWndChild) return TRUE; // check for permanent-owned window first CWnd* pWnd=CWnd::FromHandlePermanent(hWndChild); if(pWnd!=NULL) hWndChild=pWnd->GetOwner()->GetSafeHwnd(); else hWndChild=(::GetWindowLongPtr(hWndChild,GWL_STYLE)&WS_CHILD) ? ::GetParent(hWndChild) : ::GetWindow(hWndChild,GW_OWNER); } while (hWndChild != NULL); return FALSE; } BOOL COXPropertiesWnd::OnDeleteProperty(HPROPERTY /*hProperty*/) { return TRUE; } BOOL COXPropertiesWnd::DeleteAll() { // Loop through all the properties using the m_mapProp2Cat map CArray arrToBeDeleted; POSITION pos = m_mapProp2Cat.GetStartPosition(); while (pos != NULL) { HPROPERTY hProperty; CString strCategory; m_mapProp2Cat.GetNextAssoc(pos, hProperty, strCategory); arrToBeDeleted.Add(hProperty); } BOOL bAllDeleted = TRUE; for (int i = 0; i < arrToBeDeleted.GetSize(); i++) if (!DeleteProperty(arrToBeDeleted[i])) bAllDeleted = FALSE; return bAllDeleted; } COXFrameSkin* COXPropertiesWnd::GetFrameSkin() { // Check if the app is derived from COXSkinnedApp COXSkinnedApp* pSkinnedApp = DYNAMIC_DOWNCAST(COXSkinnedApp, AfxGetApp()); if (pSkinnedApp != NULL && pSkinnedApp->GetCurrentSkin() != NULL) return pSkinnedApp->GetCurrentSkin()->GetFrameSkin(); else { // Create a classic skin for this class if not created already if (m_pFrameSkin == NULL) m_pFrameSkin = new COXFrameSkinClassic(); return m_pFrameSkin; } } void COXPropertiesWnd::SetSortCategories(BOOL bSortCategories /*= TRUE*/ ) { m_bSortCategories = bSortCategories; } /* Used to jump to the next property (if it's the end of the current category, the first property in the next category gets selected). Nish - June 14, 2005 */ BOOL COXPropertiesWnd::SelectNextProperty() { HTREEITEM hSelected = m_wndTree.GetNextItem(TVI_ROOT, TVGN_FIRSTSELECTED); if (hSelected == NULL) return FALSE; // no property is selected HTREEITEM hNext = m_wndTree.GetNextItem(hSelected, TVGN_CHILD); if (hNext == NULL) hNext = m_wndTree.GetNextItem(hSelected, TVGN_NEXT); if (hNext == NULL) hNext = m_wndTree.GetNextItem(m_wndTree.GetParentItem(hSelected), TVGN_NEXT); if (hNext == NULL) return FALSE; // no more properties FinishEditing(FALSE); m_wndTree.SelectItem(hNext); m_wndTree.SetItemState(hNext,LVIS_SELECTED, LVIS_SELECTED); StartEditing(hNext); if(m_wndTree.ItemHasChildren(hNext)) return SelectNextProperty(); return TRUE; }