]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/common/PropTree/PropTree.cpp
hello world
[icculus/iodoom3.git] / neo / tools / common / PropTree / PropTree.cpp
1 // PropTree.cpp : implementation file
2 //
3 //  Copyright (C) 1998-2001 Scott Ramsay
4 //      sramsay@gonavi.com
5 //      http://www.gonavi.com
6 //
7 //  This material is provided "as is", with absolutely no warranty expressed
8 //  or implied. Any use is at your own risk.
9 // 
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.
15 // 
16 //      If you use this code, drop me an email.  I'd like to know if you find the code
17 //      useful.
18
19 //#include "stdafx.h"
20 #include "../../../idlib/precompiled.h"
21 #pragma hdrstop
22
23 #include "PropTree.h"
24
25 #ifdef _DEBUG
26 #define new DEBUG_NEW
27 #undef THIS_FILE
28 static char THIS_FILE[] = __FILE__;
29 #endif
30
31 #define PROPTREEITEM_EXPANDCOLUMN               16                      // width of the expand column
32 #define PROPTREEITEM_COLRNG                             5                       // width of splitter
33
34 //static AFX_EXTENSION_MODULE PropTreeDLL = {NULL, NULL};
35 static const CString strOfficeFontName  = _T("Tahoma");
36 static const CString strDefaultFontName = _T("MS Sans Serif");
37
38 HINSTANCE ghInst;
39
40 /*extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
41 {
42         if (dwReason == DLL_PROCESS_ATTACH)
43         {
44                 TRACE0("PROPTREE.DLL Initializing!\n");
45                 
46                 if (!AfxInitExtensionModule(PropTreeDLL, hInstance))
47                         return 0;
48
49                 new CDynLinkLibrary(PropTreeDLL);
50
51                 ghInst = hInstance;
52         }
53         else if (dwReason == DLL_PROCESS_DETACH)
54         {
55                 TRACE0("PROPTREE.DLL Terminating!\n");
56                 AfxTermExtensionModule(PropTreeDLL);
57         }
58
59         return 1;
60 }*/
61
62 void InitPropTree(HINSTANCE hInstance) {
63         ghInst = hInstance;
64 }
65
66 static int CALLBACK FontFamilyProcFonts(const LOGFONT FAR* lplf, const TEXTMETRIC FAR*, ULONG, LPARAM)
67 {
68         ASSERT(lplf != NULL);
69         CString strFont = lplf->lfFaceName;
70         return strFont.CollateNoCase (strOfficeFontName) == 0 ? 0 : 1;
71 }
72
73 /////////////////////////////////////////////////////////////////////////////
74 // CPropTree
75
76 UINT CPropTree::s_nInstanceCount;
77 CFont* CPropTree::s_pNormalFont;
78 CFont* CPropTree::s_pBoldFont;
79 CPropTreeItem* CPropTree::s_pFound;
80
81 CPropTree::CPropTree() :
82         m_bShowInfo(TRUE),
83         m_nInfoHeight(50),
84         m_pVisbleList(NULL),
85         m_Origin(100,0),
86         m_nLastUID(1),
87         m_pFocus(NULL),
88         m_bDisableInput(FALSE)
89 {
90         m_Root.Expand();
91
92         // init global resources only once
93         if (!s_nInstanceCount)
94                 InitGlobalResources();
95         s_nInstanceCount++;
96 }
97
98
99 CPropTree::~CPropTree()
100 {
101         DeleteAllItems();
102
103         s_nInstanceCount--;
104
105         // free global resource when ALL CPropTrees are destroyed
106         if (!s_nInstanceCount)
107                 FreeGlobalResources();
108 }
109
110
111 BEGIN_MESSAGE_MAP(CPropTree, CWnd)
112         //{{AFX_MSG_MAP(CPropTree)
113         ON_WM_CREATE()
114         ON_WM_SIZE()
115         ON_WM_ENABLE()
116         ON_WM_SYSCOLORCHANGE()
117         //}}AFX_MSG_MAP
118 END_MESSAGE_MAP()
119
120
121 /////////////////////////////////////////////////////////////////////////////
122 // CPropTree message handlers
123
124 const POINT& CPropTree::GetOrigin()
125 {
126         return m_Origin;
127 }
128
129
130 BOOL CPropTree::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
131 {
132         CWnd* pWnd = this;
133
134         LPCTSTR pszCreateClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW, ::LoadCursor(NULL, IDC_ARROW));
135
136         return pWnd->Create(pszCreateClass, _T(""), dwStyle, rect, pParentWnd, nID);
137 }
138
139
140 int CPropTree::OnCreate(LPCREATESTRUCT lpCreateStruct) 
141 {
142         if (CWnd::OnCreate(lpCreateStruct) == -1)
143                 return -1;
144
145         DWORD dwStyle;
146         CRect rc;
147
148         GetClientRect(rc);
149
150         // create CPropTreeList
151         //
152
153         dwStyle = WS_VISIBLE|WS_CHILD|WS_VSCROLL;
154
155         if (!m_List.Create(dwStyle, rc, this, 100))
156         {
157                 TRACE0("Failed to create CPropTreeList\n");
158                 return -1;
159         }
160
161         m_List.SetPropOwner(this);
162
163         // create CPropTreeInfo
164         //
165
166         dwStyle &= ~WS_VSCROLL;
167
168         if (!m_Info.Create(_T(""), dwStyle, rc, this))
169         {
170                 TRACE0("Failed to create CPropTreeInfo\n");
171                 return -1;
172         }
173
174         m_Info.SetPropOwner(this);
175
176         return 0;
177 }
178
179
180 CWnd* CPropTree::GetCtrlParent()
181 {
182         return &m_List;
183 }
184
185
186 void CPropTree::OnSize(UINT nType, int cx, int cy) 
187 {
188         CWnd::OnSize(nType, cx, cy);
189         ResizeChildWindows(cx, cy);
190 }
191
192
193 void CPropTree::ResizeChildWindows(int cx, int cy)
194 {
195         if (m_bShowInfo)
196         {
197                 if (IsWindow(m_List.m_hWnd))
198                         m_List.MoveWindow(0, 0, cx, cy - m_nInfoHeight);
199
200                 if (IsWindow(m_Info.m_hWnd))
201                         m_Info.MoveWindow(0, cy - m_nInfoHeight, cx, m_nInfoHeight);
202         }
203         else
204         {
205                 if (IsWindow(m_List.m_hWnd))
206                         m_List.MoveWindow(0, 0, cx, cy);
207         }
208 }
209
210
211 void CPropTree::InitGlobalResources()
212 {
213         NONCLIENTMETRICS info;
214         info.cbSize = sizeof(info);
215
216         ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
217
218         LOGFONT lf;
219         memset(&lf, 0, sizeof (LOGFONT));
220
221         CWindowDC dc(NULL);
222         lf.lfCharSet = (BYTE)GetTextCharsetInfo(dc.GetSafeHdc(), NULL, 0);
223
224         lf.lfHeight = info.lfMenuFont.lfHeight;
225         lf.lfWeight = info.lfMenuFont.lfWeight;
226         lf.lfItalic = info.lfMenuFont.lfItalic;
227
228         // check if we should use system font
229         _tcscpy(lf.lfFaceName, info.lfMenuFont.lfFaceName);
230
231         BOOL fUseSystemFont = (info.lfMenuFont.lfCharSet > SYMBOL_CHARSET);
232         if (!fUseSystemFont)
233         {
234                 // check for "Tahoma" font existance:
235                 if (::EnumFontFamilies(dc.GetSafeHdc(), NULL, FontFamilyProcFonts, 0)==0)
236                 {
237                         // Found! Use MS Office font!
238                         _tcscpy(lf.lfFaceName, strOfficeFontName);
239                 }
240                 else
241                 {
242                         // Not found. Use default font:
243                         _tcscpy(lf.lfFaceName, strDefaultFontName);
244                 }
245         }
246
247         s_pNormalFont = new CFont;
248         s_pNormalFont->CreateFontIndirect(&lf);
249
250         lf.lfWeight = FW_BOLD;
251         s_pBoldFont = new CFont;
252         s_pBoldFont->CreateFontIndirect(&lf);
253 }
254
255
256 void CPropTree::FreeGlobalResources()
257 {
258         if (s_pNormalFont)
259         {
260                 delete s_pNormalFont;
261                 s_pNormalFont = NULL;
262         }
263
264         if (s_pBoldFont)
265         {
266                 delete s_pBoldFont;
267                 s_pBoldFont = NULL;
268         }
269 }
270
271
272 CFont* CPropTree::GetNormalFont()
273 {
274         return s_pNormalFont;
275 }
276
277
278 CFont* CPropTree::GetBoldFont()
279 {
280         return s_pBoldFont;
281 }
282
283
284 CPropTreeItem* CPropTree::GetFocusedItem()
285 {
286         return m_pFocus;
287 }
288
289
290 CPropTreeItem* CPropTree::GetRootItem()
291 {
292         return &m_Root;
293 }
294
295
296 void CPropTree::ClearVisibleList()
297 {
298         m_pVisbleList = NULL;
299 }
300
301
302 CPropTreeItem* CPropTree::GetVisibleList()
303 {
304         return m_pVisbleList;
305 }
306
307
308 void CPropTree::AddToVisibleList(CPropTreeItem* pItem)
309 {
310         if (!pItem)
311                 return;
312
313         // check for an empty visible list
314         if (!m_pVisbleList)
315                 m_pVisbleList = pItem;
316         else
317         {
318                 // Add the new item to the end of the list
319                 CPropTreeItem* pNext;
320
321                 pNext = m_pVisbleList;
322                 while (pNext->GetNextVisible())
323                         pNext = pNext->GetNextVisible();
324
325                 pNext->SetNextVisible(pItem);
326         }
327
328         pItem->SetNextVisible(NULL);
329 }
330
331
332 BOOL CPropTree::EnumItems(CPropTreeItem* pItem, ENUMPROPITEMPROC proc, LPARAM lParam)
333 {
334         if (!pItem || !proc)
335                 return FALSE;
336
337         CPropTreeItem* pNext;
338
339         // don't count the root item in any enumerations
340         if (pItem!=&m_Root && !proc(this, pItem, lParam))
341                 return FALSE;
342
343         // recurse thru all child items
344         pNext = pItem->GetChild();
345
346         while (pNext)
347         {
348                 if (!EnumItems(pNext, proc, lParam))
349                         return FALSE;
350
351                 pNext = pNext->GetSibling();
352         }
353
354         return TRUE;
355 }
356
357
358 void CPropTree::SetOriginOffset(LONG nOffset)
359 {
360         m_Origin.y = nOffset;
361 }
362
363         
364 void CPropTree::UpdatedItems()
365 {
366         if (!IsWindow(m_hWnd))
367                 return;
368
369         Invalidate();
370
371         m_List.UpdateResize();
372         m_List.Invalidate();
373 }
374
375
376 void CPropTree::DeleteAllItems()
377 {
378         Delete(NULL);
379         UpdatedItems();
380         m_nLastUID = 1; // reset uid counter
381 }
382
383
384 void CPropTree::DeleteItem(CPropTreeItem* pItem)
385 {
386         Delete(pItem);
387         UpdatedItems();
388 }
389
390
391 LONG CPropTree::GetColumn()
392 {
393         return m_Origin.x;
394 }
395
396
397 void CPropTree::SetColumn(LONG nColumn)
398 {
399         CRect rc;
400
401         GetClientRect(rc);
402         
403         if (rc.IsRectEmpty())
404                 nColumn = __max(PROPTREEITEM_EXPANDCOLUMN, nColumn);
405         else
406                 nColumn = __min(__max(PROPTREEITEM_EXPANDCOLUMN, nColumn), rc.Width() - PROPTREEITEM_EXPANDCOLUMN);
407
408         m_Origin.x = nColumn;
409
410         Invalidate();
411 }
412
413
414 void CPropTree::Delete(CPropTreeItem* pItem)
415 {
416         if (pItem && pItem!=&m_Root && SendNotify(PTN_DELETEITEM, pItem))
417                 return;
418
419         // passing in a NULL item is the same as calling DeleteAllItems
420         if (!pItem)
421                 pItem = &m_Root;
422
423         // Clear the visible list before anything gets deleted
424         ClearVisibleList();
425
426         // delete children
427
428         CPropTreeItem* pIter;
429         CPropTreeItem* pNext;
430
431         pIter = pItem->GetChild();
432         while (pIter)
433         {
434                 pNext = pIter->GetSibling();
435                 DeleteItem(pIter);
436                 pIter = pNext;
437         }
438
439         // unlink from tree
440         if (pItem->GetParent())
441         {
442                 if (pItem->GetParent()->GetChild()==pItem)
443                         pItem->GetParent()->SetChild(pItem->GetSibling());
444                 else
445                 {
446                         pIter = pItem->GetParent()->GetChild();
447                         while (pIter->GetSibling() && pIter->GetSibling()!=pItem)
448                                 pIter = pIter->GetSibling();
449
450                         if (pIter->GetSibling())
451                                 pIter->SetSibling(pItem->GetSibling());
452                 }
453         }
454
455         if (pItem!=&m_Root)
456         {
457                 if (pItem==GetFocusedItem())
458                         SetFocusedItem(NULL);
459                 delete pItem;
460         }
461 }
462
463
464 void CPropTree::SetFocusedItem(CPropTreeItem* pItem)
465 {
466         m_pFocus = pItem;
467         EnsureVisible(m_pFocus);
468
469         if (!IsWindow(m_hWnd))
470                 return;
471
472         Invalidate();
473 }
474
475
476 void CPropTree::ShowInfoText(BOOL bShow)
477 {
478         m_bShowInfo = bShow;
479
480         CRect rc;
481
482         GetClientRect(rc);
483         ResizeChildWindows(rc.Width(), rc.Height());
484 }
485
486
487 BOOL CPropTree::IsItemVisible(CPropTreeItem* pItem)
488 {
489         if (!pItem)
490                 return FALSE;
491
492         for (CPropTreeItem* pNext = m_pVisbleList; pNext; pNext = pNext->GetNextVisible())
493         {
494                 if (pNext==pItem)
495                         return TRUE;
496         }
497
498         return FALSE;
499 }
500
501
502 void CPropTree::EnsureVisible(CPropTreeItem* pItem)
503 {
504         if (!pItem)
505                 return;
506
507         // item is not scroll visible (expand all parents)
508         if (!IsItemVisible(pItem))
509         {
510                 CPropTreeItem* pParent;
511
512                 pParent = pItem->GetParent();
513                 while (pParent)
514                 {
515                         pParent->Expand();
516                         pParent = pParent->GetParent();
517                 }
518
519                 UpdatedItems();
520                 UpdateWindow();
521         }
522
523         ASSERT(IsItemVisible(pItem));
524
525         CRect rc;
526
527         m_List.GetClientRect(rc);
528         rc.OffsetRect(0, m_Origin.y);
529         rc.bottom -= pItem->GetHeight();
530
531         CPoint pt;
532
533         pt = pItem->GetLocation();
534
535         if (!rc.PtInRect(pt))
536         {
537                 LONG oy;
538
539                 if (pt.y < rc.top)
540                         oy = pt.y;
541                 else
542                         oy = pt.y - rc.Height() + pItem->GetHeight();
543
544                 m_List.OnVScroll(SB_THUMBTRACK, oy, NULL);
545         }
546 }
547
548
549 CPropTreeItem* CPropTree::InsertItem(CPropTreeItem* pItem, CPropTreeItem* pParent)
550 {
551         if (!pItem)
552                 return NULL;
553
554         if (!pParent)
555                 pParent = &m_Root;
556
557         if (!pParent->GetChild())
558                 pParent->SetChild(pItem);
559         else
560         {
561                 // add to end of the sibling list
562                 CPropTreeItem* pNext;
563
564                 pNext = pParent->GetChild();
565                 while (pNext->GetSibling())
566                         pNext = pNext->GetSibling();
567
568                 pNext->SetSibling(pItem);
569         }
570
571         pItem->SetParent(pParent);
572         pItem->SetPropOwner(this);
573
574         // auto generate a default ID
575         pItem->SetCtrlID(m_nLastUID++);
576
577         SendNotify(PTN_INSERTITEM, pItem);
578
579         UpdatedItems();
580
581         return pItem;
582 }
583
584
585
586 LONG CPropTree::HitTest(const POINT& pt)
587 {
588         POINT p = pt;
589
590         CPropTreeItem* pItem;
591
592         // convert screen to tree coordinates
593         p.y += m_Origin.y;
594
595         if ((pItem = FindItem(pt))!=NULL)
596         {
597                 if (!pItem->IsRootLevel() && pt.x >= m_Origin.x - PROPTREEITEM_COLRNG && pt.x <= m_Origin.x + PROPTREEITEM_COLRNG)
598                         return HTCOLUMN;
599
600                 if (pItem->HitButton(p)) {
601                         return HTBUTTON;
602                 }
603
604                 if (pt.x > m_Origin.x + PROPTREEITEM_COLRNG)
605                         return HTATTRIBUTE;
606
607                 if (pItem->HitExpand(p))
608                         return HTEXPAND;
609
610                 if (pItem->HitCheckBox(p))
611                         return HTCHECKBOX;
612
613                 return HTLABEL;
614         }
615
616         return HTCLIENT;
617 }
618
619
620 CPropTreeItem* CPropTree::FindItem(const POINT& pt)
621 {
622         CPropTreeItem* pItem;
623
624         CPoint p = pt;
625
626         // convert screen to tree coordinates
627         p.y += m_Origin.y;
628
629         // search the visible list for the item
630         for (pItem = m_pVisbleList; pItem; pItem = pItem->GetNextVisible())
631         {
632                 CPoint ipt = pItem->GetLocation();
633                 if (p.y>=ipt.y && p.y<ipt.y + pItem->GetHeight())
634                         return pItem;
635         }
636
637         return NULL;
638 }
639
640
641 CPropTreeItem* CPropTree::FindItem(UINT nCtrlID)
642 {
643         s_pFound = NULL;
644
645         EnumItems(&m_Root, EnumFindItem, nCtrlID);
646
647         return s_pFound;
648 }
649
650
651 BOOL CALLBACK CPropTree::EnumFindItem(CPropTree*, CPropTreeItem* pItem, LPARAM lParam)
652 {
653         ASSERT(pItem!=NULL);
654
655         if (pItem->GetCtrlID()==(UINT)lParam)
656         {
657                 s_pFound = pItem;
658                 return FALSE;
659         }
660
661         return TRUE;
662 }
663
664
665 BOOL CPropTree::IsDisableInput()
666 {
667         return m_bDisableInput;
668 }
669
670
671 void CPropTree::DisableInput(BOOL bDisable)
672 {
673         m_bDisableInput = bDisable;
674
675         CWnd* pWnd;
676
677         if ((pWnd = GetParent())!=NULL)
678                 pWnd->EnableWindow(!bDisable);
679 }
680
681
682 void CPropTree::SelectItems(CPropTreeItem* pItem, BOOL bSelect)
683 {
684         if (!pItem)
685                 pItem = &m_Root;
686
687         EnumItems(pItem, EnumSelectAll, (LPARAM)bSelect);
688 }
689
690
691 CPropTreeItem* CPropTree::FocusFirst()
692 {
693         CPropTreeItem *pold;
694
695         pold = m_pFocus;
696
697         SetFocusedItem(m_pVisbleList);
698
699         if (m_pFocus)
700         {
701                 SelectItems(NULL, FALSE);
702                 m_pFocus->Select();
703         }
704
705         if (pold!=m_pFocus)
706                 SendNotify(PTN_SELCHANGE, m_pFocus);
707
708         return m_pFocus;
709 }
710
711
712 CPropTreeItem* CPropTree::FocusLast()
713 {
714         CPropTreeItem* pNext;
715         CPropTreeItem* pChange;
716
717         pChange = m_pFocus;
718
719         pNext = m_pVisbleList;
720
721         if (pNext)
722         {
723                 while (pNext->GetNextVisible())
724                         pNext = pNext->GetNextVisible();
725
726                 SetFocusedItem(pNext);
727
728                 if (m_pFocus)
729                 {
730                         SelectItems(NULL, FALSE);
731                         m_pFocus->Select();
732                 }
733         }
734
735         if (pChange!=m_pFocus)
736                 SendNotify(PTN_SELCHANGE, m_pFocus);
737
738         return pNext;
739 }
740
741
742 CPropTreeItem* CPropTree::FocusPrev()
743 {
744         CPropTreeItem* pNext;
745         CPropTreeItem* pChange;
746
747         pChange = m_pFocus;
748
749         if (m_pFocus==NULL)
750         {
751                 // get the last visible item
752                 pNext = m_pVisbleList;
753                 while (pNext && pNext->GetNextVisible())
754                         pNext = pNext->GetNextVisible();
755         }
756         else
757         {
758                 pNext = m_pVisbleList;
759                 while (pNext && pNext->GetNextVisible()!=m_pFocus)
760                         pNext = pNext->GetNextVisible();
761         }
762
763         if (pNext)
764                 SetFocusedItem(pNext);
765         
766         if (m_pFocus)
767         {
768                 SelectItems(NULL, FALSE);
769                 m_pFocus->Select();
770         }
771
772         if (pChange!=m_pFocus)
773                 SendNotify(PTN_SELCHANGE, m_pFocus);
774
775         return pNext;
776 }
777
778
779 CPropTreeItem* CPropTree::FocusNext()
780 {
781         CPropTreeItem* pNext;
782         CPropTreeItem* pChange;
783
784         pChange = m_pFocus;
785
786         if (m_pFocus==NULL)
787                 pNext = m_pVisbleList;
788         else
789         if (m_pFocus->GetNextVisible())
790                 pNext = m_pFocus->GetNextVisible();
791         else
792                 pNext = NULL;
793
794         if (pNext)
795                 SetFocusedItem(pNext);
796
797         if (m_pFocus)
798         {
799                 SelectItems(NULL, FALSE);
800                 m_pFocus->Select();
801         }
802
803         if (pChange!=m_pFocus)
804                 SendNotify(PTN_SELCHANGE, m_pFocus);
805
806         return pNext;
807 }
808
809
810 void CPropTree::UpdateMoveAllItems()
811 {
812         EnumItems(&m_Root, EnumMoveAll);
813 }
814
815
816 void CPropTree::RefreshItems(CPropTreeItem* pItem)
817 {
818         if (!pItem)
819                 pItem = &m_Root;
820
821         EnumItems(pItem, EnumRefreshAll);
822
823         UpdatedItems();
824 }
825
826
827 BOOL CALLBACK CPropTree::EnumSelectAll(CPropTree*, CPropTreeItem* pItem, LPARAM lParam)
828 {
829         if (!pItem)
830                 return FALSE;
831
832         pItem->Select((BOOL)lParam);
833
834         return TRUE;
835 }
836
837
838 BOOL CALLBACK CPropTree::EnumRefreshAll(CPropTree*, CPropTreeItem* pItem, LPARAM)
839 {
840         if (!pItem)
841                 return FALSE;
842
843         pItem->OnRefresh();
844
845         return TRUE;
846 }
847
848
849 BOOL CALLBACK CPropTree::EnumMoveAll(CPropTree*, CPropTreeItem* pItem, LPARAM)
850 {
851         if (!pItem)
852                 return FALSE;
853
854         pItem->OnMove();
855
856         return TRUE;
857 }
858
859
860 LRESULT CPropTree::SendNotify(UINT nNotifyCode, CPropTreeItem* pItem)
861 {
862         if (!IsWindow(m_hWnd))
863                 return 0L;
864
865         if (!(GetStyle() & PTS_NOTIFY))
866                 return 0L;
867
868         NMPROPTREE nmmp;
869         LPNMHDR lpnm;
870
871         lpnm = NULL;
872
873         switch (nNotifyCode)
874         {
875                 case PTN_INSERTITEM:
876                 case PTN_DELETEITEM:
877                 case PTN_DELETEALLITEMS:
878                 case PTN_ITEMCHANGED:
879                 case PTN_ITEMBUTTONCLICK:
880                 case PTN_SELCHANGE:
881                 case PTN_ITEMEXPANDING:
882                 case PTN_COLUMNCLICK:
883                 case PTN_PROPCLICK:
884                 case PTN_CHECKCLICK:
885                         lpnm = (LPNMHDR)&nmmp;
886                         nmmp.pItem = pItem;
887                         break;
888         }
889
890         if (lpnm)
891         {
892                 UINT id = (UINT)::GetMenu(m_hWnd);
893                 lpnm->code = nNotifyCode;
894                 lpnm->hwndFrom = m_hWnd;
895                 lpnm->idFrom = id;
896         
897                 return GetParent()->SendMessage(WM_NOTIFY, (WPARAM)id, (LPARAM)lpnm);
898         }
899
900         return 0L;
901 }
902
903
904 void CPropTree::OnEnable(BOOL bEnable) 
905 {
906         CWnd::OnEnable(bEnable);
907         Invalidate();
908 }
909
910
911 void CPropTree::OnSysColorChange() 
912 {
913         CWnd::OnSysColorChange();
914         
915         Invalidate();   
916 }
917
918
919 BOOL CPropTree::IsSingleSelection()
920 {
921         // right now only support single selection
922         return TRUE;
923 }