1 // PropTree.cpp : implementation file
3 // Copyright (C) 1998-2001 Scott Ramsay
5 // http://www.gonavi.com
7 // This material is provided "as is", with absolutely no warranty expressed
8 // or implied. Any use is at your own risk.
10 // Permission to use or copy this software for any purpose is hereby granted
11 // without fee, provided the above notices are retained on all copies.
12 // Permission to modify the code and to distribute modified code is granted,
13 // provided the above notices are retained, and a notice that the code was
14 // modified is included with the above copyright notice.
16 // If you use this code, drop me an email. I'd like to know if you find the code
20 #include "../../../idlib/precompiled.h"
28 static char THIS_FILE[] = __FILE__;
31 #define PROPTREEITEM_EXPANDCOLUMN 16 // width of the expand column
32 #define PROPTREEITEM_COLRNG 5 // width of splitter
34 //static AFX_EXTENSION_MODULE PropTreeDLL = {NULL, NULL};
35 static const CString strOfficeFontName = _T("Tahoma");
36 static const CString strDefaultFontName = _T("MS Sans Serif");
40 /*extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
42 if (dwReason == DLL_PROCESS_ATTACH)
44 TRACE0("PROPTREE.DLL Initializing!\n");
46 if (!AfxInitExtensionModule(PropTreeDLL, hInstance))
49 new CDynLinkLibrary(PropTreeDLL);
53 else if (dwReason == DLL_PROCESS_DETACH)
55 TRACE0("PROPTREE.DLL Terminating!\n");
56 AfxTermExtensionModule(PropTreeDLL);
62 void InitPropTree(HINSTANCE hInstance) {
66 static int CALLBACK FontFamilyProcFonts(const LOGFONT FAR* lplf, const TEXTMETRIC FAR*, ULONG, LPARAM)
69 CString strFont = lplf->lfFaceName;
70 return strFont.CollateNoCase (strOfficeFontName) == 0 ? 0 : 1;
73 /////////////////////////////////////////////////////////////////////////////
76 UINT CPropTree::s_nInstanceCount;
77 CFont* CPropTree::s_pNormalFont;
78 CFont* CPropTree::s_pBoldFont;
79 CPropTreeItem* CPropTree::s_pFound;
81 CPropTree::CPropTree() :
88 m_bDisableInput(FALSE)
92 // init global resources only once
93 if (!s_nInstanceCount)
94 InitGlobalResources();
99 CPropTree::~CPropTree()
105 // free global resource when ALL CPropTrees are destroyed
106 if (!s_nInstanceCount)
107 FreeGlobalResources();
111 BEGIN_MESSAGE_MAP(CPropTree, CWnd)
112 //{{AFX_MSG_MAP(CPropTree)
116 ON_WM_SYSCOLORCHANGE()
121 /////////////////////////////////////////////////////////////////////////////
122 // CPropTree message handlers
124 const POINT& CPropTree::GetOrigin()
130 BOOL CPropTree::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
134 LPCTSTR pszCreateClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW, ::LoadCursor(NULL, IDC_ARROW));
136 return pWnd->Create(pszCreateClass, _T(""), dwStyle, rect, pParentWnd, nID);
140 int CPropTree::OnCreate(LPCREATESTRUCT lpCreateStruct)
142 if (CWnd::OnCreate(lpCreateStruct) == -1)
150 // create CPropTreeList
153 dwStyle = WS_VISIBLE|WS_CHILD|WS_VSCROLL;
155 if (!m_List.Create(dwStyle, rc, this, 100))
157 TRACE0("Failed to create CPropTreeList\n");
161 m_List.SetPropOwner(this);
163 // create CPropTreeInfo
166 dwStyle &= ~WS_VSCROLL;
168 if (!m_Info.Create(_T(""), dwStyle, rc, this))
170 TRACE0("Failed to create CPropTreeInfo\n");
174 m_Info.SetPropOwner(this);
180 CWnd* CPropTree::GetCtrlParent()
186 void CPropTree::OnSize(UINT nType, int cx, int cy)
188 CWnd::OnSize(nType, cx, cy);
189 ResizeChildWindows(cx, cy);
193 void CPropTree::ResizeChildWindows(int cx, int cy)
197 if (IsWindow(m_List.m_hWnd))
198 m_List.MoveWindow(0, 0, cx, cy - m_nInfoHeight);
200 if (IsWindow(m_Info.m_hWnd))
201 m_Info.MoveWindow(0, cy - m_nInfoHeight, cx, m_nInfoHeight);
205 if (IsWindow(m_List.m_hWnd))
206 m_List.MoveWindow(0, 0, cx, cy);
211 void CPropTree::InitGlobalResources()
213 NONCLIENTMETRICS info;
214 info.cbSize = sizeof(info);
216 ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
219 memset(&lf, 0, sizeof (LOGFONT));
222 lf.lfCharSet = (BYTE)GetTextCharsetInfo(dc.GetSafeHdc(), NULL, 0);
224 lf.lfHeight = info.lfMenuFont.lfHeight;
225 lf.lfWeight = info.lfMenuFont.lfWeight;
226 lf.lfItalic = info.lfMenuFont.lfItalic;
228 // check if we should use system font
229 _tcscpy(lf.lfFaceName, info.lfMenuFont.lfFaceName);
231 BOOL fUseSystemFont = (info.lfMenuFont.lfCharSet > SYMBOL_CHARSET);
234 // check for "Tahoma" font existance:
235 if (::EnumFontFamilies(dc.GetSafeHdc(), NULL, FontFamilyProcFonts, 0)==0)
237 // Found! Use MS Office font!
238 _tcscpy(lf.lfFaceName, strOfficeFontName);
242 // Not found. Use default font:
243 _tcscpy(lf.lfFaceName, strDefaultFontName);
247 s_pNormalFont = new CFont;
248 s_pNormalFont->CreateFontIndirect(&lf);
250 lf.lfWeight = FW_BOLD;
251 s_pBoldFont = new CFont;
252 s_pBoldFont->CreateFontIndirect(&lf);
256 void CPropTree::FreeGlobalResources()
260 delete s_pNormalFont;
261 s_pNormalFont = NULL;
272 CFont* CPropTree::GetNormalFont()
274 return s_pNormalFont;
278 CFont* CPropTree::GetBoldFont()
284 CPropTreeItem* CPropTree::GetFocusedItem()
290 CPropTreeItem* CPropTree::GetRootItem()
296 void CPropTree::ClearVisibleList()
298 m_pVisbleList = NULL;
302 CPropTreeItem* CPropTree::GetVisibleList()
304 return m_pVisbleList;
308 void CPropTree::AddToVisibleList(CPropTreeItem* pItem)
313 // check for an empty visible list
315 m_pVisbleList = pItem;
318 // Add the new item to the end of the list
319 CPropTreeItem* pNext;
321 pNext = m_pVisbleList;
322 while (pNext->GetNextVisible())
323 pNext = pNext->GetNextVisible();
325 pNext->SetNextVisible(pItem);
328 pItem->SetNextVisible(NULL);
332 BOOL CPropTree::EnumItems(CPropTreeItem* pItem, ENUMPROPITEMPROC proc, LPARAM lParam)
337 CPropTreeItem* pNext;
339 // don't count the root item in any enumerations
340 if (pItem!=&m_Root && !proc(this, pItem, lParam))
343 // recurse thru all child items
344 pNext = pItem->GetChild();
348 if (!EnumItems(pNext, proc, lParam))
351 pNext = pNext->GetSibling();
358 void CPropTree::SetOriginOffset(LONG nOffset)
360 m_Origin.y = nOffset;
364 void CPropTree::UpdatedItems()
366 if (!IsWindow(m_hWnd))
371 m_List.UpdateResize();
376 void CPropTree::DeleteAllItems()
380 m_nLastUID = 1; // reset uid counter
384 void CPropTree::DeleteItem(CPropTreeItem* pItem)
391 LONG CPropTree::GetColumn()
397 void CPropTree::SetColumn(LONG nColumn)
403 if (rc.IsRectEmpty())
404 nColumn = __max(PROPTREEITEM_EXPANDCOLUMN, nColumn);
406 nColumn = __min(__max(PROPTREEITEM_EXPANDCOLUMN, nColumn), rc.Width() - PROPTREEITEM_EXPANDCOLUMN);
408 m_Origin.x = nColumn;
414 void CPropTree::Delete(CPropTreeItem* pItem)
416 if (pItem && pItem!=&m_Root && SendNotify(PTN_DELETEITEM, pItem))
419 // passing in a NULL item is the same as calling DeleteAllItems
423 // Clear the visible list before anything gets deleted
428 CPropTreeItem* pIter;
429 CPropTreeItem* pNext;
431 pIter = pItem->GetChild();
434 pNext = pIter->GetSibling();
440 if (pItem->GetParent())
442 if (pItem->GetParent()->GetChild()==pItem)
443 pItem->GetParent()->SetChild(pItem->GetSibling());
446 pIter = pItem->GetParent()->GetChild();
447 while (pIter->GetSibling() && pIter->GetSibling()!=pItem)
448 pIter = pIter->GetSibling();
450 if (pIter->GetSibling())
451 pIter->SetSibling(pItem->GetSibling());
457 if (pItem==GetFocusedItem())
458 SetFocusedItem(NULL);
464 void CPropTree::SetFocusedItem(CPropTreeItem* pItem)
467 EnsureVisible(m_pFocus);
469 if (!IsWindow(m_hWnd))
476 void CPropTree::ShowInfoText(BOOL bShow)
483 ResizeChildWindows(rc.Width(), rc.Height());
487 BOOL CPropTree::IsItemVisible(CPropTreeItem* pItem)
492 for (CPropTreeItem* pNext = m_pVisbleList; pNext; pNext = pNext->GetNextVisible())
502 void CPropTree::EnsureVisible(CPropTreeItem* pItem)
507 // item is not scroll visible (expand all parents)
508 if (!IsItemVisible(pItem))
510 CPropTreeItem* pParent;
512 pParent = pItem->GetParent();
516 pParent = pParent->GetParent();
523 ASSERT(IsItemVisible(pItem));
527 m_List.GetClientRect(rc);
528 rc.OffsetRect(0, m_Origin.y);
529 rc.bottom -= pItem->GetHeight();
533 pt = pItem->GetLocation();
535 if (!rc.PtInRect(pt))
542 oy = pt.y - rc.Height() + pItem->GetHeight();
544 m_List.OnVScroll(SB_THUMBTRACK, oy, NULL);
549 CPropTreeItem* CPropTree::InsertItem(CPropTreeItem* pItem, CPropTreeItem* pParent)
557 if (!pParent->GetChild())
558 pParent->SetChild(pItem);
561 // add to end of the sibling list
562 CPropTreeItem* pNext;
564 pNext = pParent->GetChild();
565 while (pNext->GetSibling())
566 pNext = pNext->GetSibling();
568 pNext->SetSibling(pItem);
571 pItem->SetParent(pParent);
572 pItem->SetPropOwner(this);
574 // auto generate a default ID
575 pItem->SetCtrlID(m_nLastUID++);
577 SendNotify(PTN_INSERTITEM, pItem);
586 LONG CPropTree::HitTest(const POINT& pt)
590 CPropTreeItem* pItem;
592 // convert screen to tree coordinates
595 if ((pItem = FindItem(pt))!=NULL)
597 if (!pItem->IsRootLevel() && pt.x >= m_Origin.x - PROPTREEITEM_COLRNG && pt.x <= m_Origin.x + PROPTREEITEM_COLRNG)
600 if (pItem->HitButton(p)) {
604 if (pt.x > m_Origin.x + PROPTREEITEM_COLRNG)
607 if (pItem->HitExpand(p))
610 if (pItem->HitCheckBox(p))
620 CPropTreeItem* CPropTree::FindItem(const POINT& pt)
622 CPropTreeItem* pItem;
626 // convert screen to tree coordinates
629 // search the visible list for the item
630 for (pItem = m_pVisbleList; pItem; pItem = pItem->GetNextVisible())
632 CPoint ipt = pItem->GetLocation();
633 if (p.y>=ipt.y && p.y<ipt.y + pItem->GetHeight())
641 CPropTreeItem* CPropTree::FindItem(UINT nCtrlID)
645 EnumItems(&m_Root, EnumFindItem, nCtrlID);
651 BOOL CALLBACK CPropTree::EnumFindItem(CPropTree*, CPropTreeItem* pItem, LPARAM lParam)
655 if (pItem->GetCtrlID()==(UINT)lParam)
665 BOOL CPropTree::IsDisableInput()
667 return m_bDisableInput;
671 void CPropTree::DisableInput(BOOL bDisable)
673 m_bDisableInput = bDisable;
677 if ((pWnd = GetParent())!=NULL)
678 pWnd->EnableWindow(!bDisable);
682 void CPropTree::SelectItems(CPropTreeItem* pItem, BOOL bSelect)
687 EnumItems(pItem, EnumSelectAll, (LPARAM)bSelect);
691 CPropTreeItem* CPropTree::FocusFirst()
697 SetFocusedItem(m_pVisbleList);
701 SelectItems(NULL, FALSE);
706 SendNotify(PTN_SELCHANGE, m_pFocus);
712 CPropTreeItem* CPropTree::FocusLast()
714 CPropTreeItem* pNext;
715 CPropTreeItem* pChange;
719 pNext = m_pVisbleList;
723 while (pNext->GetNextVisible())
724 pNext = pNext->GetNextVisible();
726 SetFocusedItem(pNext);
730 SelectItems(NULL, FALSE);
735 if (pChange!=m_pFocus)
736 SendNotify(PTN_SELCHANGE, m_pFocus);
742 CPropTreeItem* CPropTree::FocusPrev()
744 CPropTreeItem* pNext;
745 CPropTreeItem* pChange;
751 // get the last visible item
752 pNext = m_pVisbleList;
753 while (pNext && pNext->GetNextVisible())
754 pNext = pNext->GetNextVisible();
758 pNext = m_pVisbleList;
759 while (pNext && pNext->GetNextVisible()!=m_pFocus)
760 pNext = pNext->GetNextVisible();
764 SetFocusedItem(pNext);
768 SelectItems(NULL, FALSE);
772 if (pChange!=m_pFocus)
773 SendNotify(PTN_SELCHANGE, m_pFocus);
779 CPropTreeItem* CPropTree::FocusNext()
781 CPropTreeItem* pNext;
782 CPropTreeItem* pChange;
787 pNext = m_pVisbleList;
789 if (m_pFocus->GetNextVisible())
790 pNext = m_pFocus->GetNextVisible();
795 SetFocusedItem(pNext);
799 SelectItems(NULL, FALSE);
803 if (pChange!=m_pFocus)
804 SendNotify(PTN_SELCHANGE, m_pFocus);
810 void CPropTree::UpdateMoveAllItems()
812 EnumItems(&m_Root, EnumMoveAll);
816 void CPropTree::RefreshItems(CPropTreeItem* pItem)
821 EnumItems(pItem, EnumRefreshAll);
827 BOOL CALLBACK CPropTree::EnumSelectAll(CPropTree*, CPropTreeItem* pItem, LPARAM lParam)
832 pItem->Select((BOOL)lParam);
838 BOOL CALLBACK CPropTree::EnumRefreshAll(CPropTree*, CPropTreeItem* pItem, LPARAM)
849 BOOL CALLBACK CPropTree::EnumMoveAll(CPropTree*, CPropTreeItem* pItem, LPARAM)
860 LRESULT CPropTree::SendNotify(UINT nNotifyCode, CPropTreeItem* pItem)
862 if (!IsWindow(m_hWnd))
865 if (!(GetStyle() & PTS_NOTIFY))
877 case PTN_DELETEALLITEMS:
878 case PTN_ITEMCHANGED:
879 case PTN_ITEMBUTTONCLICK:
881 case PTN_ITEMEXPANDING:
882 case PTN_COLUMNCLICK:
885 lpnm = (LPNMHDR)&nmmp;
892 UINT id = (UINT)::GetMenu(m_hWnd);
893 lpnm->code = nNotifyCode;
894 lpnm->hwndFrom = m_hWnd;
897 return GetParent()->SendMessage(WM_NOTIFY, (WPARAM)id, (LPARAM)lpnm);
904 void CPropTree::OnEnable(BOOL bEnable)
906 CWnd::OnEnable(bEnable);
911 void CPropTree::OnSysColorChange()
913 CWnd::OnSysColorChange();
919 BOOL CPropTree::IsSingleSelection()
921 // right now only support single selection