High DPI optimizations
[TortoiseGit.git] / src / Utils / TreePropSheet / TreePropSheet.cpp
blobeed612b7bf68de5cf2e1e3465b3896bf72957ae1
1 /********************************************************************
3 * Copyright (c) 2002 Sven Wiegand <mail@sven-wiegand.de>
5 * You can use this and modify this in any way you want,
6 * BUT LEAVE THIS HEADER INTACT.
8 * Redistribution is appreciated.
10 * $Workfile:$
11 * $Revision:$
12 * $Modtime:$
13 * $Author:$
15 * Revision History:
16 * $History:$
18 *********************************************************************/
21 #include "stdafx.h"
22 #include "TreePropSheet.h"
23 #include "PropPageFrameDefault.h"
24 #include "HighColorTab.hpp"
26 #ifdef _DEBUG
27 #define new DEBUG_NEW
28 #undef THIS_FILE
29 static char THIS_FILE[] = __FILE__;
30 #endif
34 namespace TreePropSheet
37 //-------------------------------------------------------------------
38 // class CTreePropSheet
39 //-------------------------------------------------------------------
41 BEGIN_MESSAGE_MAP(CTreePropSheet, CPropertySheet)
42 //{{AFX_MSG_MAP(CTreePropSheet)
43 ON_WM_DESTROY()
44 //}}AFX_MSG_MAP
45 ON_MESSAGE(PSM_ADDPAGE, OnAddPage)
46 ON_MESSAGE(PSM_REMOVEPAGE, OnRemovePage)
47 ON_MESSAGE(PSM_SETCURSEL, OnSetCurSel)
48 ON_MESSAGE(PSM_SETCURSELID, OnSetCurSelId)
49 ON_MESSAGE(PSM_ISDIALOGMESSAGE, OnIsDialogMessage)
51 ON_NOTIFY(TVN_SELCHANGINGA, s_unPageTreeId, OnPageTreeSelChanging)
52 ON_NOTIFY(TVN_SELCHANGINGW, s_unPageTreeId, OnPageTreeSelChanging)
53 ON_NOTIFY(TVN_SELCHANGEDA, s_unPageTreeId, OnPageTreeSelChanged)
54 ON_NOTIFY(TVN_SELCHANGEDW, s_unPageTreeId, OnPageTreeSelChanged)
55 END_MESSAGE_MAP()
57 IMPLEMENT_DYNAMIC(CTreePropSheet, CPropertySheet)
59 const UINT CTreePropSheet::s_unPageTreeId = 0x7EEE;
61 CTreePropSheet::CTreePropSheet()
62 : CPropertySheet(),
63 m_bPageTreeSelChangedActive(FALSE),
64 m_bTreeViewMode(TRUE),
65 m_bPageCaption(FALSE),
66 m_bTreeImages(FALSE),
67 m_nPageTreeWidth(150),
68 m_pwndPageTree(nullptr),
69 m_pFrame(nullptr)
73 CTreePropSheet::CTreePropSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
74 : CPropertySheet(nIDCaption, pParentWnd, iSelectPage),
75 m_bPageTreeSelChangedActive(FALSE),
76 m_bTreeViewMode(TRUE),
77 m_bPageCaption(FALSE),
78 m_bTreeImages(FALSE),
79 m_nPageTreeWidth(150),
80 m_pwndPageTree(nullptr),
81 m_pFrame(nullptr)
86 CTreePropSheet::CTreePropSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
87 : CPropertySheet(pszCaption, pParentWnd, iSelectPage),
88 m_bPageTreeSelChangedActive(FALSE),
89 m_bTreeViewMode(TRUE),
90 m_bPageCaption(FALSE),
91 m_bTreeImages(FALSE),
92 m_nPageTreeWidth(150),
93 m_pwndPageTree(nullptr),
94 m_pFrame(nullptr)
99 CTreePropSheet::~CTreePropSheet()
104 /////////////////////////////////////////////////////////////////////
105 // Operationen
107 BOOL CTreePropSheet::SetTreeViewMode(BOOL bTreeViewMode /* = TRUE */, BOOL bPageCaption /* = FALSE */, BOOL bTreeImages /* = FALSE */)
109 if (IsWindow(m_hWnd))
111 // needs to becalled, before the window has been created
112 ASSERT(FALSE);
113 return FALSE;
116 m_bTreeViewMode = bTreeViewMode;
117 if (m_bTreeViewMode)
119 m_bPageCaption = bPageCaption;
120 m_bTreeImages = bTreeImages;
123 return TRUE;
127 BOOL CTreePropSheet::SetTreeWidth(int nWidth)
129 if (IsWindow(m_hWnd))
131 // needs to be called, before the window is created.
132 ASSERT(FALSE);
133 return FALSE;
136 m_nPageTreeWidth = nWidth;
138 return TRUE;
142 void CTreePropSheet::SetEmptyPageText(LPCTSTR lpszEmptyPageText)
144 m_strEmptyPageMessage = lpszEmptyPageText;
148 DWORD CTreePropSheet::SetEmptyPageTextFormat(DWORD dwFormat)
150 DWORD dwPrevFormat = m_pFrame->GetMsgFormat();
151 m_pFrame->SetMsgFormat(dwFormat);
152 return dwPrevFormat;
156 BOOL CTreePropSheet::SetTreeDefaultImages(CImageList *pImages)
158 if (pImages->GetImageCount() != 2)
160 ASSERT(FALSE);
161 return FALSE;
164 if (m_DefaultImages.GetSafeHandle())
165 m_DefaultImages.DeleteImageList();
166 m_DefaultImages.Create(pImages);
168 // update, if necessary
169 if (IsWindow(m_hWnd))
170 RefillPageTree();
172 return TRUE;
176 BOOL CTreePropSheet::SetTreeDefaultImages(UINT unBitmapID, int cx, COLORREF crMask)
178 if (m_DefaultImages.GetSafeHandle())
179 m_DefaultImages.DeleteImageList();
180 if (!m_DefaultImages.Create(unBitmapID, cx, 0, crMask))
181 return FALSE;
183 if (m_DefaultImages.GetImageCount() != 2)
185 m_DefaultImages.DeleteImageList();
186 return FALSE;
189 return TRUE;
193 CTreeCtrl* CTreePropSheet::GetPageTreeControl()
195 return m_pwndPageTree;
199 /////////////////////////////////////////////////////////////////////
200 // public helpers
202 BOOL CTreePropSheet::SetPageIcon(CPropertyPage *pPage, HICON hIcon)
204 pPage->m_psp.dwFlags|= PSP_USEHICON;
205 pPage->m_psp.hIcon = hIcon;
206 return TRUE;
210 BOOL CTreePropSheet::SetPageIcon(CPropertyPage *pPage, UINT unIconId)
212 HICON hIcon = AfxGetApp()->LoadIcon(unIconId);
213 if (!hIcon)
214 return FALSE;
216 return SetPageIcon(pPage, hIcon);
220 BOOL CTreePropSheet::SetPageIcon(CPropertyPage *pPage, CImageList &Images, int nImage)
222 HICON hIcon = Images.ExtractIcon(nImage);
223 if (!hIcon)
224 return FALSE;
226 return SetPageIcon(pPage, hIcon);
230 BOOL CTreePropSheet::DestroyPageIcon(CPropertyPage *pPage)
232 if (!pPage || !(pPage->m_psp.dwFlags&PSP_USEHICON) || !pPage->m_psp.hIcon)
233 return FALSE;
235 DestroyIcon(pPage->m_psp.hIcon);
236 pPage->m_psp.dwFlags&= ~PSP_USEHICON;
237 pPage->m_psp.hIcon = nullptr;
239 return TRUE;
243 /////////////////////////////////////////////////////////////////////
244 // Overridable implementation helpers
246 CString CTreePropSheet::GenerateEmptyPageMessage(LPCTSTR lpszEmptyPageMessage, LPCTSTR lpszCaption)
248 CString strMsg;
249 strMsg.Format(lpszEmptyPageMessage, lpszCaption);
250 return strMsg;
254 CTreeCtrl* CTreePropSheet::CreatePageTreeObject()
256 return new CTreeCtrl;
260 CPropPageFrame* CTreePropSheet::CreatePageFrame()
262 return new CPropPageFrameDefault;
266 /////////////////////////////////////////////////////////////////////
267 // Implementation helpers
269 void CTreePropSheet::MoveChildWindows(int nDx, int nDy)
271 CWnd *pWnd = GetWindow(GW_CHILD);
272 while (pWnd)
274 CRect rect;
275 pWnd->GetWindowRect(rect);
276 rect.OffsetRect(nDx, nDy);
277 ScreenToClient(rect);
278 pWnd->MoveWindow(rect);
280 pWnd = pWnd->GetNextWindow();
285 void CTreePropSheet::RefillPageTree()
287 if (!IsWindow(m_hWnd))
288 return;
290 m_pwndPageTree->DeleteAllItems();
292 CTabCtrl *pTabCtrl = GetTabControl();
293 if (!IsWindow(pTabCtrl->GetSafeHwnd()))
295 ASSERT(FALSE);
296 return;
299 const int nPageCount = pTabCtrl->GetItemCount();
301 // rebuild image list
302 if (m_bTreeImages)
304 for (int i = m_Images.GetImageCount()-1; i >= 0; --i)
305 m_Images.Remove(i);
307 // add page images
308 CImageList *pPageImages = pTabCtrl->GetImageList();
309 if (pPageImages)
311 for (int nImage = 0; nImage < pPageImages->GetImageCount(); ++nImage)
313 HICON hIcon = pPageImages->ExtractIcon(nImage);
314 m_Images.Add(hIcon);
315 DestroyIcon(hIcon);
319 // add default images
320 if (m_DefaultImages.GetSafeHandle())
322 HICON hIcon;
324 // add default images
325 hIcon = m_DefaultImages.ExtractIcon(0);
326 if (hIcon)
328 m_Images.Add(hIcon);
329 DestroyIcon(hIcon);
331 hIcon = m_DefaultImages.ExtractIcon(1);
333 m_Images.Add(hIcon);
334 DestroyIcon(hIcon);
339 // insert tree items
340 for (int nPage = 0; nPage < nPageCount; ++nPage)
342 // Get title and image of the page
343 CString strPagePath;
345 TCITEM ti = { 0 };
346 ti.mask = TCIF_TEXT|TCIF_IMAGE;
347 ti.cchTextMax = MAX_PATH;
348 ti.pszText = strPagePath.GetBuffer(ti.cchTextMax + 1);
349 ASSERT(ti.pszText);
350 if (!ti.pszText)
351 return;
353 pTabCtrl->GetItem(nPage, &ti);
354 strPagePath.ReleaseBuffer();
356 // Create an item in the tree for the page
357 HTREEITEM hItem = CreatePageTreeItem(ti.pszText);
358 ASSERT(hItem);
359 if (hItem)
361 m_pwndPageTree->SetItemData(hItem, nPage);
363 // set image
364 if (m_bTreeImages)
366 int nImage = ti.iImage;
367 if (nImage < 0 || nImage >= m_Images.GetImageCount())
368 nImage = m_DefaultImages.GetSafeHandle()? m_Images.GetImageCount()-1 : -1;
370 m_pwndPageTree->SetItemImage(hItem, nImage, nImage);
372 m_pwndPageTree->Expand(m_pwndPageTree->GetParentItem(hItem), TVE_EXPAND);
378 HTREEITEM CTreePropSheet::CreatePageTreeItem(LPCTSTR lpszPath, HTREEITEM hParent /* = TVI_ROOT */)
380 CString strPath(lpszPath);
381 CString strTopMostItem(SplitPageTreePath(strPath));
383 // Check if an item with the given text does already exist
384 HTREEITEM hItem = nullptr;
385 HTREEITEM hChild = m_pwndPageTree->GetChildItem(hParent);
386 while (hChild)
388 if (m_pwndPageTree->GetItemText(hChild) == strTopMostItem)
390 hItem = hChild;
391 break;
393 hChild = m_pwndPageTree->GetNextItem(hChild, TVGN_NEXT);
396 // If item with that text does not already exist, create a new one
397 if (!hItem)
399 hItem = m_pwndPageTree->InsertItem(strTopMostItem, hParent);
400 m_pwndPageTree->SetItemData(hItem, (DWORD_PTR)-1);
401 if (!strPath.IsEmpty() && m_bTreeImages && m_DefaultImages.GetSafeHandle())
402 // set folder image
403 m_pwndPageTree->SetItemImage(hItem, m_Images.GetImageCount()-2, m_Images.GetImageCount()-2);
405 if (!hItem)
407 ASSERT(FALSE);
408 return nullptr;
411 if (strPath.IsEmpty())
412 return hItem;
413 else
414 return CreatePageTreeItem(strPath, hItem);
418 CString CTreePropSheet::SplitPageTreePath(CString &strRest)
420 int nSeparatorPos = 0;
421 #pragma warning(push)
422 #pragma warning(disable: 4127) // conditional expression constant
423 while (TRUE)
425 nSeparatorPos = strRest.Find(L"::", nSeparatorPos);
426 if (nSeparatorPos == -1)
428 CString strItem(strRest);
429 strRest.Empty();
430 return strItem;
432 else if (nSeparatorPos>0)
434 // if there is an odd number of backslashes infront of the
435 // separator, than do not interpret it as separator
436 int nBackslashCount = 0;
437 for (int nPos = nSeparatorPos-1; nPos >= 0 && strRest[nPos] == L'\\'; --nPos, ++nBackslashCount);
438 if (nBackslashCount%2 == 0)
439 break;
440 else
441 ++nSeparatorPos;
444 #pragma warning(pop)
446 CString strItem(strRest.Left(nSeparatorPos));
447 strItem.Replace(L"\\::", L"::");
448 strItem.Replace(L"\\\\", L"\\");
449 strRest = strRest.Mid(nSeparatorPos+2);
450 return strItem;
454 BOOL CTreePropSheet::KillActiveCurrentPage()
456 HWND hCurrentPage = PropSheet_GetCurrentPageHwnd(m_hWnd);
457 if (!IsWindow(hCurrentPage))
459 ASSERT(FALSE);
460 return TRUE;
463 // Check if the current page is really active (if page is invisible
464 // an virtual empty page is the active one.
465 if (!::IsWindowVisible(hCurrentPage))
466 return TRUE;
468 // Try to deactivate current page
469 PSHNOTIFY pshn;
470 pshn.hdr.code = PSN_KILLACTIVE;
471 pshn.hdr.hwndFrom = m_hWnd;
472 pshn.hdr.idFrom = GetDlgCtrlID();
473 pshn.lParam = 0;
474 if (::SendMessage(hCurrentPage, WM_NOTIFY, pshn.hdr.idFrom, (LPARAM)&pshn))
475 // current page does not allow page change
476 return FALSE;
478 // Hide the page
479 ::ShowWindow(hCurrentPage, SW_HIDE);
481 return TRUE;
485 HTREEITEM CTreePropSheet::GetPageTreeItem(int nPage, HTREEITEM hRoot /* = TVI_ROOT */)
487 // Special handling for root case
488 if (hRoot == TVI_ROOT)
489 hRoot = m_pwndPageTree->GetNextItem(TVI_ROOT, TVGN_ROOT);
491 // Check parameters
492 if (nPage < 0 || nPage >= GetPageCount())
494 ASSERT(FALSE);
495 return nullptr;
498 if (!hRoot)
500 ASSERT(FALSE);
501 return nullptr;
504 // we are performing a simple linear search here, because we are
505 // expecting only little data
506 HTREEITEM hItem = hRoot;
507 while (hItem)
509 if ((signed)m_pwndPageTree->GetItemData(hItem) == nPage)
510 return hItem;
511 if (m_pwndPageTree->ItemHasChildren(hItem))
513 HTREEITEM hResult = GetPageTreeItem(nPage, m_pwndPageTree->GetNextItem(hItem, TVGN_CHILD));
514 if (hResult)
515 return hResult;
518 hItem = m_pwndPageTree->GetNextItem(hItem, TVGN_NEXT);
521 // we've found nothing, if we arrive here
522 return hItem;
526 BOOL CTreePropSheet::SelectPageTreeItem(int nPage)
528 HTREEITEM hItem = GetPageTreeItem(nPage);
529 if (!hItem)
530 return FALSE;
532 return m_pwndPageTree->SelectItem(hItem);
536 BOOL CTreePropSheet::SelectCurrentPageTreeItem()
538 CTabCtrl *pTab = GetTabControl();
539 if (!IsWindow(pTab->GetSafeHwnd()))
540 return FALSE;
542 return SelectPageTreeItem(pTab->GetCurSel());
546 void CTreePropSheet::UpdateCaption()
548 HWND hPage = PropSheet_GetCurrentPageHwnd(GetSafeHwnd());
549 BOOL bRealPage = IsWindow(hPage) && ::IsWindowVisible(hPage);
550 HTREEITEM hItem = m_pwndPageTree->GetSelectedItem();
551 if (!hItem)
552 return;
554 CString strCaption = m_pwndPageTree->GetItemText(hItem);
556 // if empty page, then update empty page message
557 if (!bRealPage)
558 m_pFrame->SetMsgText(GenerateEmptyPageMessage(m_strEmptyPageMessage, strCaption));
560 // if no captions are displayed, cancel here
561 if (!m_pFrame->GetShowCaption())
562 return;
564 // get tab control, to the the images from
565 CTabCtrl *pTabCtrl = GetTabControl();
566 if (!IsWindow(pTabCtrl->GetSafeHwnd()))
568 ASSERT(FALSE);
569 return;
572 if (m_bTreeImages)
574 // get image from tree
575 int nImage;
576 m_pwndPageTree->GetItemImage(hItem, nImage, nImage);
577 HICON hIcon = m_Images.ExtractIcon(nImage);
578 m_pFrame->SetCaption(strCaption, hIcon);
579 if (hIcon)
580 DestroyIcon(hIcon);
582 else if (bRealPage)
584 // get image from hidden (original) tab provided by the original
585 // implementation
586 CImageList *pImages = pTabCtrl->GetImageList();
587 if (pImages)
589 TCITEM ti = { 0 };
590 ti.mask = TCIF_IMAGE;
592 HICON hIcon = nullptr;
593 if (pTabCtrl->GetItem((int)m_pwndPageTree->GetItemData(hItem), &ti))
594 hIcon = pImages->ExtractIcon(ti.iImage);
596 m_pFrame->SetCaption(strCaption, hIcon);
597 if (hIcon)
598 DestroyIcon(hIcon);
600 else
601 m_pFrame->SetCaption(strCaption);
603 else
604 m_pFrame->SetCaption(strCaption);
608 void CTreePropSheet::ActivatePreviousPage()
610 if (!IsWindow(m_hWnd))
611 return;
613 if (!IsWindow(m_pwndPageTree->GetSafeHwnd()))
615 // normal tab property sheet. Simply use page index
616 int nPageIndex = GetActiveIndex();
617 if (nPageIndex<0 || nPageIndex>=GetPageCount())
618 return;
620 int nPrevIndex = (nPageIndex==0)? GetPageCount()-1 : nPageIndex-1;
621 SetActivePage(nPrevIndex);
623 else
625 // property sheet with page tree.
626 // we need a more sophisticated handling here, than simply using
627 // the page index, because we won't skip empty pages.
628 // so we have to walk the page tree
629 HTREEITEM hItem = m_pwndPageTree->GetSelectedItem();
630 ASSERT(hItem);
631 if (!hItem)
632 return;
634 HTREEITEM hPrevItem = nullptr;
635 if ((hPrevItem=m_pwndPageTree->GetPrevSiblingItem(hItem))!=0)
637 while (m_pwndPageTree->ItemHasChildren(hPrevItem))
639 hPrevItem = m_pwndPageTree->GetChildItem(hPrevItem);
640 while (m_pwndPageTree->GetNextSiblingItem(hPrevItem))
641 hPrevItem = m_pwndPageTree->GetNextSiblingItem(hPrevItem);
644 else
645 hPrevItem=m_pwndPageTree->GetParentItem(hItem);
647 if (!hPrevItem)
649 // no prev item, so cycle to the last item
650 hPrevItem = m_pwndPageTree->GetRootItem();
652 #pragma warning(push)
653 #pragma warning(disable: 4127) // conditional expression constant
654 while (TRUE)
656 while (m_pwndPageTree->GetNextSiblingItem(hPrevItem))
657 hPrevItem = m_pwndPageTree->GetNextSiblingItem(hPrevItem);
659 if (m_pwndPageTree->ItemHasChildren(hPrevItem))
660 hPrevItem = m_pwndPageTree->GetChildItem(hPrevItem);
661 else
662 break;
664 #pragma warning(pop)
667 if (hPrevItem)
668 m_pwndPageTree->SelectItem(hPrevItem);
673 void CTreePropSheet::ActivateNextPage()
675 if (!IsWindow(m_hWnd))
676 return;
678 if (!IsWindow(m_pwndPageTree->GetSafeHwnd()))
680 // normal tab property sheet. Simply use page index
681 int nPageIndex = GetActiveIndex();
682 if (nPageIndex<0 || nPageIndex>=GetPageCount())
683 return;
685 int nNextIndex = (nPageIndex==GetPageCount()-1)? 0 : nPageIndex+1;
686 SetActivePage(nNextIndex);
688 else
690 // property sheet with page tree.
691 // we need a more sophisticated handling here, than simply using
692 // the page index, because we won't skip empty pages.
693 // so we have to walk the page tree
694 HTREEITEM hItem = m_pwndPageTree->GetSelectedItem();
695 ASSERT(hItem);
696 if (!hItem)
697 return;
699 HTREEITEM hNextItem = nullptr;
700 if ((hNextItem=m_pwndPageTree->GetChildItem(hItem))!=0)
702 else if ((hNextItem=m_pwndPageTree->GetNextSiblingItem(hItem))!=0)
704 else if (m_pwndPageTree->GetParentItem(hItem))
706 while (!hNextItem)
708 hItem = m_pwndPageTree->GetParentItem(hItem);
709 if (!hItem)
710 break;
712 hNextItem = m_pwndPageTree->GetNextSiblingItem(hItem);
716 if (!hNextItem)
717 // no next item -- so cycle to the first item
718 hNextItem = m_pwndPageTree->GetRootItem();
720 if (hNextItem)
721 m_pwndPageTree->SelectItem(hNextItem);
726 /////////////////////////////////////////////////////////////////////
727 // Overridings
729 BOOL CTreePropSheet::OnInitDialog()
731 int iconWidth = GetSystemMetrics(SM_CXSMICON);
732 int iconHeight = GetSystemMetrics(SM_CYSMICON);
733 if (m_bTreeViewMode)
735 // be sure, there are no stacked tabs, because otherwise the
736 // page caption will be to large in tree view mode
737 EnableStackedTabs(FALSE);
739 // Initialize image list.
740 if (m_DefaultImages.GetSafeHandle())
742 IMAGEINFO ii;
743 m_DefaultImages.GetImageInfo(0, &ii);
744 if (ii.hbmImage) DeleteObject(ii.hbmImage);
745 if (ii.hbmMask) DeleteObject(ii.hbmMask);
746 m_Images.Create(ii.rcImage.right-ii.rcImage.left, ii.rcImage.bottom-ii.rcImage.top, ILC_COLOR32|ILC_MASK, 0, 1);
748 else
749 m_Images.Create(iconWidth, iconHeight, ILC_COLOR32 | ILC_MASK, 0, 1);
752 // perform default implementation
753 BOOL bResult = CPropertySheet::OnInitDialog();
754 HighColorTab::UpdateImageList(*this, iconWidth, iconHeight);
756 if (!m_bTreeViewMode)
757 // stop here, if we would like to use tabs
758 return bResult;
760 // Get tab control...
761 CTabCtrl *pTab = GetTabControl();
762 if (!IsWindow(pTab->GetSafeHwnd()))
764 ASSERT(FALSE);
765 return bResult;
768 // ... and hide it
769 pTab->ShowWindow(SW_HIDE);
770 pTab->EnableWindow(FALSE);
772 // Place another (empty) tab ctrl, to get a frame instead
773 CRect rectFrame;
774 pTab->GetWindowRect(rectFrame);
775 ScreenToClient(rectFrame);
777 m_pFrame = CreatePageFrame();
778 if (!m_pFrame)
780 ASSERT(FALSE);
781 AfxThrowMemoryException();
783 m_pFrame->Create(WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS, rectFrame, this, 0xFFFF);
784 m_pFrame->ShowCaption(m_bPageCaption);
786 // Lets make place for the tree ctrl
787 HDC hdc = ::GetDC(nullptr);
788 int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
789 ::ReleaseDC(nullptr, hdc);
790 const int nTreeWidth = m_nPageTreeWidth * dpiX / 96;
791 const int nTreeSpace = 5;
793 CRect rectSheet;
794 GetWindowRect(rectSheet);
795 rectSheet.right+= nTreeWidth;
796 SetWindowPos(nullptr, -1, -1, rectSheet.Width(), rectSheet.Height(), SWP_NOZORDER | SWP_NOMOVE);
797 CenterWindow();
799 MoveChildWindows(nTreeWidth, 0);
801 // Lets calculate the rectangle for the tree ctrl
802 CRect rectTree(rectFrame);
803 rectTree.right = rectTree.left + nTreeWidth - nTreeSpace;
805 // calculate caption height
806 NONCLIENTMETRICS metrics = { 0 };
807 metrics.cbSize = sizeof(NONCLIENTMETRICS);
808 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &metrics, FALSE);
809 m_pFrame->SetCaptionHeight(metrics.iCaptionHeight);
811 // if no caption should be displayed, make the window smaller in
812 // height
813 if (!m_bPageCaption)
815 // make frame smaller
816 m_pFrame->GetWnd()->GetWindowRect(rectFrame);
817 ScreenToClient(rectFrame);
818 rectFrame.top += metrics.iCaptionHeight;
819 m_pFrame->GetWnd()->MoveWindow(rectFrame);
821 // move all child windows up
822 MoveChildWindows(0, -metrics.iCaptionHeight);
824 // modify rectangle for the tree ctrl
825 rectTree.bottom -= metrics.iCaptionHeight;
827 // make us smaller
828 CRect rect;
829 GetWindowRect(rect);
830 rect.top += metrics.iCaptionHeight / 2;
831 rect.bottom -= metrics.iCaptionHeight - metrics.iCaptionHeight / 2;
832 if (GetParent())
833 GetParent()->ScreenToClient(rect);
834 MoveWindow(rect);
837 // finally create the tree control
838 const DWORD dwTreeStyle = TVS_SHOWSELALWAYS|TVS_TRACKSELECT|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS;
839 m_pwndPageTree = CreatePageTreeObject();
840 if (!m_pwndPageTree)
842 ASSERT(FALSE);
843 AfxThrowMemoryException();
846 // MFC7-support here (Thanks to Rainer Wollgarten)
847 // YT: Cast tree control to CWnd and calls CWnd::CreateEx in all cases (VC 6 and7).
848 ((CWnd*)m_pwndPageTree)->CreateEx(
849 WS_EX_CLIENTEDGE|WS_EX_NOPARENTNOTIFY|TVS_EX_DOUBLEBUFFER,
850 L"SysTreeView32", L"PageTree",
851 WS_TABSTOP|WS_CHILD|WS_VISIBLE|dwTreeStyle,
852 rectTree, this, s_unPageTreeId);
854 if (m_bTreeImages)
856 m_pwndPageTree->SetImageList(&m_Images, TVSIL_NORMAL);
857 m_pwndPageTree->SetImageList(&m_Images, TVSIL_STATE);
859 SetWindowTheme(m_pwndPageTree->GetSafeHwnd(), L"Explorer", nullptr);
861 // Fill the tree ctrl
862 RefillPageTree();
864 // Select item for the current page
865 if (pTab->GetCurSel() > -1)
866 SelectPageTreeItem(pTab->GetCurSel());
868 return bResult;
872 void CTreePropSheet::OnDestroy()
874 CPropertySheet::OnDestroy();
876 if (m_Images.GetSafeHandle())
877 m_Images.DeleteImageList();
879 delete m_pwndPageTree;
880 m_pwndPageTree = nullptr;
882 delete m_pFrame;
883 m_pFrame = nullptr;
887 LRESULT CTreePropSheet::OnAddPage(WPARAM wParam, LPARAM lParam)
889 LRESULT lResult = DefWindowProc(PSM_ADDPAGE, wParam, lParam);
890 if (!m_bTreeViewMode)
891 return lResult;
893 RefillPageTree();
894 SelectCurrentPageTreeItem();
896 return lResult;
900 LRESULT CTreePropSheet::OnRemovePage(WPARAM wParam, LPARAM lParam)
902 LRESULT lResult = DefWindowProc(PSM_REMOVEPAGE, wParam, lParam);
903 if (!m_bTreeViewMode)
904 return lResult;
906 RefillPageTree();
907 SelectCurrentPageTreeItem();
909 return lResult;
913 LRESULT CTreePropSheet::OnSetCurSel(WPARAM wParam, LPARAM lParam)
915 LRESULT lResult = DefWindowProc(PSM_SETCURSEL, wParam, lParam);
916 if (!m_bTreeViewMode)
917 return lResult;
919 SelectCurrentPageTreeItem();
920 UpdateCaption();
921 return lResult;
924 LRESULT CTreePropSheet::OnSetCurSelId(WPARAM wParam, LPARAM lParam)
926 return OnSetCurSel(wParam, lParam);
930 void CTreePropSheet::OnPageTreeSelChanging(NMHDR *pNotifyStruct, LRESULT *plResult)
932 *plResult = 0;
933 if (m_bPageTreeSelChangedActive)
934 return;
935 else
936 m_bPageTreeSelChangedActive = TRUE;
938 NMTREEVIEW *pTvn = reinterpret_cast<NMTREEVIEW*>(pNotifyStruct);
939 int nPage = (int)m_pwndPageTree->GetItemData(pTvn->itemNew.hItem);
940 if (nPage < 0)
942 HTREEITEM nextItem = m_pwndPageTree->GetChildItem(pTvn->itemNew.hItem);
943 nPage = (int)m_pwndPageTree->GetItemData(nextItem);
945 BOOL bResult;
946 if (nPage >= (int)m_pwndPageTree->GetCount())
947 bResult = KillActiveCurrentPage();
948 else
949 bResult = SetActivePage((int)nPage);
951 if (!bResult)
952 // prevent selection to change
953 *plResult = TRUE;
955 // Set focus to tree ctrl (I guess that's what the user expects)
956 m_pwndPageTree->SetFocus();
958 m_bPageTreeSelChangedActive = FALSE;
960 return;
964 void CTreePropSheet::OnPageTreeSelChanged(NMHDR * /*pNotifyStruct*/, LRESULT *plResult)
966 *plResult = 0;
968 UpdateCaption();
970 return;
974 LRESULT CTreePropSheet::OnIsDialogMessage(WPARAM wParam, LPARAM lParam)
976 MSG *pMsg = reinterpret_cast<MSG*>(lParam);
977 if (pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_TAB && GetKeyState(VK_CONTROL)&0x8000)
979 if (GetKeyState(VK_SHIFT)&0x8000)
980 ActivatePreviousPage();
981 else
982 ActivateNextPage();
983 return TRUE;
986 if (pMsg->message == WM_KEYDOWN && GetKeyState(VK_CONTROL) & 0x8000 && ((pMsg->wParam == VK_PRIOR) || pMsg->wParam == VK_NEXT))
988 if (pMsg->wParam == VK_PRIOR)
989 ActivatePreviousPage();
990 else
991 ActivateNextPage();
992 return TRUE;
995 return CPropertySheet::DefWindowProc(PSM_ISDIALOGMESSAGE, wParam, lParam);
998 } //namespace TreePropSheet