diff options
author | Petr Mrázek <peterix@gmail.com> | 2013-12-02 00:55:24 +0100 |
---|---|---|
committer | Petr Mrázek <peterix@gmail.com> | 2013-12-02 00:55:24 +0100 |
commit | 6aa9bd0f77dcb5128167fae62e32aa5252fe85c6 (patch) | |
tree | 632994a61888929af9289927d338bd19a2b3f32c /mmc_updater/depends/win32cpp/tab.h | |
parent | 613699b3626aea750093ab7eaaeccaa28c0e87c6 (diff) | |
download | MultiMC-6aa9bd0f77dcb5128167fae62e32aa5252fe85c6.tar MultiMC-6aa9bd0f77dcb5128167fae62e32aa5252fe85c6.tar.gz MultiMC-6aa9bd0f77dcb5128167fae62e32aa5252fe85c6.tar.lz MultiMC-6aa9bd0f77dcb5128167fae62e32aa5252fe85c6.tar.xz MultiMC-6aa9bd0f77dcb5128167fae62e32aa5252fe85c6.zip |
Renew the updater branch
Now with some actual consensus on what the updater will do!
Diffstat (limited to 'mmc_updater/depends/win32cpp/tab.h')
-rw-r--r-- | mmc_updater/depends/win32cpp/tab.h | 1658 |
1 files changed, 1658 insertions, 0 deletions
diff --git a/mmc_updater/depends/win32cpp/tab.h b/mmc_updater/depends/win32cpp/tab.h new file mode 100644 index 00000000..15699144 --- /dev/null +++ b/mmc_updater/depends/win32cpp/tab.h @@ -0,0 +1,1658 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// tab.h +// Declaration of the CTab and CMDITab classes + +#ifndef _WIN32XX_TAB_H_ +#define _WIN32XX_TAB_H_ + +#include "wincore.h" +#include "dialog.h" +#include "gdi.h" +#include "default_resource.h" + +namespace Win32xx +{ + + struct TabPageInfo + { + TCHAR szTabText[MAX_MENU_STRING]; + int iImage; // index of this tab's image + int idTab; // identifier for this tab (optional) + CWnd* pView; // pointer to the view window + }; + + class CTab : public CWnd + { + protected: + // Declaration of the CSelectDialog class, a nested class of CTab + // It creates the dialog to choose which tab to activate + class CSelectDialog : public CDialog + { + public: + CSelectDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent = NULL); + virtual ~CSelectDialog() {} + virtual void AddItem(LPCTSTR szString); + virtual BOOL IsTab() const { return FALSE; } + + protected: + virtual BOOL OnInitDialog(); + virtual void OnOK(); + virtual void OnCancel() { EndDialog(-2); } + + private: + CSelectDialog(const CSelectDialog&); // Disable copy construction + CSelectDialog& operator = (const CSelectDialog&); // Disable assignment operator + + std::vector<tString> m_vItems; + int IDC_LIST; + + }; + public: + CTab(); + virtual ~CTab(); + virtual int AddTabPage(WndPtr pView, LPCTSTR szTabText, HICON hIcon, UINT idTab); + virtual int AddTabPage(WndPtr pView, LPCTSTR szTabText, int nID_Icon, UINT idTab = 0); + virtual int AddTabPage(WndPtr pView, LPCTSTR szTabText); + virtual CRect GetCloseRect() const; + virtual CRect GetListRect() const; + virtual HMENU GetListMenu(); + virtual BOOL GetTabsAtTop() const; + virtual int GetTabIndex(CWnd* pWnd) const; + virtual TabPageInfo GetTabPageInfo(UINT nTab) const; + virtual int GetTextHeight() const; + virtual void RecalcLayout(); + virtual void RemoveTabPage(int nPage); + virtual void SelectPage(int nPage); + virtual void SetFixedWidth(BOOL bEnabled); + virtual void SetOwnerDraw(BOOL bEnabled); + virtual void SetShowButtons(BOOL bShow); + virtual void SetTabIcon(int i, HICON hIcon); + virtual void SetTabsAtTop(BOOL bTop); + virtual void SetTabText(UINT nTab, LPCTSTR szText); + virtual void SwapTabs(UINT nTab1, UINT nTab2); + + // Attributes + std::vector <TabPageInfo>& GetAllTabs() const { return (std::vector <TabPageInfo>&) m_vTabPageInfo; } + HIMAGELIST GetImageList() const { return m_himlTab; } + BOOL GetShowButtons() const { return m_bShowButtons; } + int GetTabHeight() const { return m_nTabHeight; } + CWnd* GetActiveView() const { return m_pActiveView; } + void SetTabHeight(int nTabHeight) { m_nTabHeight = nTabHeight; NotifyChanged();} + + // Wrappers for Win32 Macros + void AdjustRect(BOOL fLarger, RECT *prc) const; + int GetCurFocus() const; + int GetCurSel() const; + BOOL GetItem(int iItem, LPTCITEM pitem) const; + int GetItemCount() const; + int HitTest(TCHITTESTINFO& info) const; + void SetCurFocus(int iItem) const; + int SetCurSel(int iItem) const; + DWORD SetItemSize(int cx, int cy) const; + int SetMinTabWidth(int cx) const; + void SetPadding(int cx, int cy) const; + + protected: + virtual void DrawCloseButton(CDC& DrawDC); + virtual void DrawListButton(CDC& DrawDC); + virtual void DrawTabs(CDC& dcMem); + virtual void DrawTabBorders(CDC& dcMem, CRect& rcTab); + virtual void OnCreate(); + virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam); + virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam); + virtual void OnMouseLeave(WPARAM wParam, LPARAM lParam); + virtual void OnMouseMove(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNCHitTest(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam); + virtual void NotifyChanged(); + virtual void Paint(); + virtual void PreCreate(CREATESTRUCT& cs); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual void SetTabSize(); + virtual void ShowListDialog(); + virtual void ShowListMenu(); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CTab(const CTab&); // Disable copy construction + CTab& operator = (const CTab&); // Disable assignment operator + + SIZE GetMaxTabSize() const; + void ShowActiveView(CWnd* pView); + + std::vector<TabPageInfo> m_vTabPageInfo; + std::vector<WndPtr> m_vTabViews; + CFont m_Font; + HIMAGELIST m_himlTab; + HMENU m_hListMenu; + CWnd* m_pActiveView; + BOOL m_bShowButtons; // Show or hide the close and list button + BOOL m_IsTracking; + BOOL m_IsClosePressed; + BOOL m_IsListPressed; + BOOL m_IsListMenuActive; + int m_nTabHeight; + }; + + //////////////////////////////////////// + // Declaration of the CTabbedMDI class + class CTabbedMDI : public CWnd + { + public: + CTabbedMDI(); + virtual ~CTabbedMDI(); + virtual CWnd* AddMDIChild(CWnd* pView, LPCTSTR szTabText, int idMDIChild = 0); + virtual void CloseActiveMDI(); + virtual void CloseAllMDIChildren(); + virtual void CloseMDIChild(int nTab); + virtual CWnd* GetActiveMDIChild() const; + virtual int GetActiveMDITab() const; + virtual CWnd* GetMDIChild(int nTab) const; + virtual int GetMDIChildCount() const; + virtual int GetMDIChildID(int nTab) const; + virtual LPCTSTR GetMDIChildTitle(int nTab) const; + virtual HMENU GetListMenu() const { return GetTab().GetListMenu(); } + virtual CTab& GetTab() const {return (CTab&)m_Tab;} + virtual BOOL LoadRegistrySettings(tString tsRegistryKeyName); + virtual void RecalcLayout(); + virtual BOOL SaveRegistrySettings(tString tsRegistryKeyName); + virtual void SetActiveMDIChild(CWnd* pWnd); + virtual void SetActiveMDITab(int nTab); + + protected: + virtual HWND Create(CWnd* pParent); + virtual CWnd* NewMDIChildFromID(int idMDIChild); + virtual void OnCreate(); + virtual void OnDestroy(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); + virtual void OnWindowPosChanged(WPARAM wParam, LPARAM lParam); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CTabbedMDI(const CTabbedMDI&); // Disable copy construction + CTabbedMDI& operator = (const CTabbedMDI&); // Disable assignment operator + + CTab m_Tab; + }; + +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + ///////////////////////////////////////////////////////////// + // Definitions for the CSelectDialog class nested within CTab + // + inline CTab::CSelectDialog::CSelectDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent) : + CDialog(lpTemplate, pParent), IDC_LIST(121) + { + } + + inline BOOL CTab::CSelectDialog::OnInitDialog() + { + for (UINT u = 0; u < m_vItems.size(); ++u) + { + SendDlgItemMessage(IDC_LIST, LB_ADDSTRING, 0, (LPARAM) m_vItems[u].c_str()); + } + + return true; + } + + inline void CTab::CSelectDialog::AddItem(LPCTSTR szString) + { + m_vItems.push_back(szString); + } + + inline void CTab::CSelectDialog::OnOK() + { + int iSelect = (int)SendDlgItemMessage(IDC_LIST, LB_GETCURSEL, 0, 0); + if (iSelect != LB_ERR) + EndDialog(iSelect); + else + EndDialog(-2); + } + + + ////////////////////////////////////////////////////////// + // Definitions for the CTab class + // + inline CTab::CTab() : m_hListMenu(NULL), m_pActiveView(NULL), m_bShowButtons(FALSE), m_IsTracking(FALSE), m_IsClosePressed(FALSE), + m_IsListPressed(FALSE), m_IsListMenuActive(FALSE), m_nTabHeight(0) + { + // Create and assign the image list + m_himlTab = ImageList_Create(16, 16, ILC_MASK|ILC_COLOR32, 0, 0); + + // Set the tab control's font + NONCLIENTMETRICS info = {0}; + info.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); + m_Font.CreateFontIndirect(&info.lfStatusFont); + } + + inline CTab::~CTab() + { + ImageList_Destroy(m_himlTab); + + if (IsMenu(m_hListMenu)) ::DestroyMenu(m_hListMenu); + } + + inline int CTab::AddTabPage(WndPtr pView, LPCTSTR szTabText, HICON hIcon, UINT idTab) + { + assert(pView.get()); + assert(lstrlen(szTabText) < MAX_MENU_STRING); + + m_vTabViews.push_back(pView); + + TabPageInfo tpi = {0}; + tpi.pView = pView.get(); + tpi.idTab = idTab; + lstrcpyn(tpi.szTabText, szTabText, MAX_MENU_STRING); + if (hIcon) + tpi.iImage = ImageList_AddIcon(GetImageList(), hIcon); + else + tpi.iImage = -1; + + int iNewPage = (int)m_vTabPageInfo.size(); + m_vTabPageInfo.push_back(tpi); + + if (m_hWnd) + { + TCITEM tie = {0}; + tie.mask = TCIF_TEXT | TCIF_IMAGE; + tie.iImage = tpi.iImage; + tie.pszText = tpi.szTabText; + TabCtrl_InsertItem(m_hWnd, iNewPage, &tie); + + SetTabSize(); + SelectPage(iNewPage); + NotifyChanged(); + } + + return iNewPage; + } + + inline int CTab::AddTabPage(WndPtr pView, LPCTSTR szTabText, int idIcon, UINT idTab /* = 0*/) + { + HICON hIcon = (HICON)LoadImage(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(idIcon), IMAGE_ICON, 0, 0, LR_SHARED); + return AddTabPage(pView, szTabText, hIcon, idTab); + } + + inline int CTab::AddTabPage(WndPtr pView, LPCTSTR szTabText) + { + return AddTabPage(pView, szTabText, (HICON)0, 0); + } + + inline void CTab::DrawCloseButton(CDC& DrawDC) + { + // The close button isn't displayed on Win95 + if (GetWinVersion() == 1400) return; + + if (!m_bShowButtons) return; + if (!GetActiveView()) return; + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_FIXEDWIDTH)) return; + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) return; + + // Determine the close button's drawing position relative to the window + CRect rcClose = GetCloseRect(); + + CPoint pt = GetCursorPos(); + ScreenToClient(pt); + UINT uState = rcClose.PtInRect(pt)? m_IsClosePressed? 2: 1: 0; + + // Draw the outer highlight for the close button + if (!IsRectEmpty(&rcClose)) + { + switch (uState) + { + case 0: + { + DrawDC.CreatePen(PS_SOLID, 1, RGB(232, 228, 220)); + + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + break; + } + + case 1: + { + // Draw outline, white at top, black on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + } + + break; + case 2: + { + // Draw outline, black on top, white on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + } + break; + } + + // Manually draw close button + DrawDC.CreatePen(PS_SOLID, 1, RGB(64, 64, 64)); + + DrawDC.MoveTo(rcClose.left + 3, rcClose.top +3); + DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.left + 4, rcClose.top +3); + DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -3); + + DrawDC.MoveTo(rcClose.left + 3, rcClose.top +4); + DrawDC.LineTo(rcClose.right - 3, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -3, rcClose.top +3); + DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -3, rcClose.top +4); + DrawDC.LineTo(rcClose.left + 3, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -4, rcClose.top +3); + DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -3); + } + } + + inline void CTab::DrawListButton(CDC& DrawDC) + { + // The list button isn't displayed on Win95 + if (GetWinVersion() == 1400) return; + + if (!m_bShowButtons) return; + if (!GetActiveView()) return; + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_FIXEDWIDTH)) return; + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) return; + + // Determine the list button's drawing position relative to the window + CRect rcList = GetListRect(); + + CPoint pt = GetCursorPos(); + ScreenToClient(pt); + UINT uState = rcList.PtInRect(pt)? 1: 0; + if (m_IsListMenuActive) uState = 2; + + // Draw the outer highlight for the list button + if (!IsRectEmpty(&rcList)) + { + switch (uState) + { + case 0: + { + DrawDC.CreatePen(PS_SOLID, 1, RGB(232, 228, 220)); + + DrawDC.MoveTo(rcList.left, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.top); + DrawDC.LineTo(rcList.left, rcList.top); + DrawDC.LineTo(rcList.left, rcList.bottom); + break; + } + + case 1: + { + // Draw outline, white at top, black on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.MoveTo(rcList.left, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.LineTo(rcList.left, rcList.top); + DrawDC.LineTo(rcList.left, rcList.bottom); + } + + break; + case 2: + { + // Draw outline, black on top, white on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.MoveTo(rcList.left, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.LineTo(rcList.left, rcList.top); + DrawDC.LineTo(rcList.left, rcList.bottom); + } + break; + } + + // Manually draw list button + DrawDC.CreatePen(PS_SOLID, 1, RGB(64, 64, 64)); + + int MaxLength = (int)(0.65 * rcList.Width()); + int topGap = 1 + rcList.Height()/3; + for (int i = 0; i <= MaxLength/2; i++) + { + int Length = MaxLength - 2*i; + DrawDC.MoveTo(rcList.left +1 + (rcList.Width() - Length)/2, rcList.top +topGap +i); + DrawDC.LineTo(rcList.left +1 + (rcList.Width() - Length)/2 + Length, rcList.top +topGap +i); + } + } + } + + inline void CTab::DrawTabs(CDC& dcMem) + { + // Draw the tab buttons: + for (int i = 0; i < TabCtrl_GetItemCount(m_hWnd); ++i) + { + CRect rcItem; + TabCtrl_GetItemRect(m_hWnd, i, &rcItem); + if (!rcItem.IsRectEmpty()) + { + if (i == TabCtrl_GetCurSel(m_hWnd)) + { + dcMem.CreateSolidBrush(RGB(248,248,248)); + dcMem.SetBkColor(RGB(248,248,248)); + } + else + { + dcMem.CreateSolidBrush(RGB(200,200,200)); + dcMem.SetBkColor(RGB(200,200,200)); + } + + dcMem.CreatePen(PS_SOLID, 1, RGB(160, 160, 160)); + dcMem.RoundRect(rcItem.left+1, rcItem.top, rcItem.right+2, rcItem.bottom, 6, 6); + + if (rcItem.Width() >= 24) + { + TCHAR szText[30]; + TCITEM tcItem = {0}; + tcItem.mask = TCIF_TEXT | TCIF_IMAGE; + tcItem.cchTextMax = 30; + tcItem.pszText = szText; + TabCtrl_GetItem(m_hWnd, i, &tcItem); + int xImage; + int yImage; + int yOffset = 0; + if (ImageList_GetIconSize(m_himlTab, &xImage, &yImage)) + yOffset = (rcItem.Height() - yImage)/2; + + // Draw the icon + ImageList_Draw(m_himlTab, tcItem.iImage, dcMem, rcItem.left+5, rcItem.top+yOffset, ILD_NORMAL); + + // Draw the text + dcMem.SelectObject(&m_Font); + + // Calculate the size of the text + CRect rcText = rcItem; + + int iImageSize = 20; + int iPadding = 4; + if (tcItem.iImage >= 0) + rcText.left += iImageSize; + + rcText.left += iPadding; + dcMem.DrawText(szText, -1, rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS); + } + } + } + } + + inline void CTab::DrawTabBorders(CDC& dcMem, CRect& rcTab) + { + BOOL IsBottomTab = (BOOL)GetWindowLongPtr(GWL_STYLE) & TCS_BOTTOM; + + // Draw a lighter rectangle touching the tab buttons + CRect rcItem; + TabCtrl_GetItemRect(m_hWnd, 0, &rcItem); + int left = rcItem.left +1; + int right = rcTab.right; + int top = rcTab.bottom; + int bottom = top + 3; + + if (!IsBottomTab) + { + bottom = MAX(rcTab.top, m_nTabHeight +4); + top = bottom -3; + } + + dcMem.CreateSolidBrush(RGB(248,248,248)); + dcMem.CreatePen(PS_SOLID, 1, RGB(248,248,248)); + if (!rcItem.IsRectEmpty()) + { + dcMem.Rectangle(left, top, right, bottom); + + // Draw a darker line below the rectangle + dcMem.CreatePen(PS_SOLID, 1, RGB(160, 160, 160)); + if (IsBottomTab) + { + dcMem.MoveTo(left-1, bottom); + dcMem.LineTo(right, bottom); + } + else + { + dcMem.MoveTo(left-1, top-1); + dcMem.LineTo(right, top-1); + } + + // Draw a lighter line over the darker line for the selected tab + dcMem.CreatePen(PS_SOLID, 1, RGB(248,248,248)); + TabCtrl_GetItemRect(m_hWnd, TabCtrl_GetCurSel(m_hWnd), &rcItem); + OffsetRect(&rcItem, 1, 1); + + if (IsBottomTab) + { + dcMem.MoveTo(rcItem.left, bottom); + dcMem.LineTo(rcItem.right, bottom); + } + else + { + dcMem.MoveTo(rcItem.left, top-1); + dcMem.LineTo(rcItem.right, top-1); + } + } + } + + inline CRect CTab::GetCloseRect() const + { + CRect rcClose; + if (GetShowButtons()) + { + rcClose= GetClientRect(); + int Gap = 2; + int cx = GetSystemMetrics(SM_CXSMICON) -1; + int cy = GetSystemMetrics(SM_CYSMICON) -1; + rcClose.right -= Gap; + rcClose.left = rcClose.right - cx; + + if (GetTabsAtTop()) + rcClose.top = Gap; + else + rcClose.top = MAX(Gap, rcClose.bottom - m_nTabHeight); + + rcClose.bottom = rcClose.top + cy; + } + return rcClose; + } + + inline HMENU CTab::GetListMenu() + { + if (IsMenu(m_hListMenu)) + ::DestroyMenu(m_hListMenu); + + m_hListMenu = CreatePopupMenu(); + + // Add the menu items + for(UINT u = 0; u < MIN(GetAllTabs().size(), 9); ++u) + { + TCHAR szMenuString[MAX_MENU_STRING+1]; + TCHAR szTabText[MAX_MENU_STRING]; + lstrcpyn(szTabText, GetAllTabs()[u].szTabText, MAX_MENU_STRING -4); + wsprintf(szMenuString, _T("&%d %s"), u+1, szTabText); + AppendMenu(m_hListMenu, MF_STRING, IDW_FIRSTCHILD +u, szMenuString); + } + if (GetAllTabs().size() >= 10) + AppendMenu(m_hListMenu, MF_STRING, IDW_FIRSTCHILD +9, _T("More Windows")); + + // Add a checkmark to the menu + int iSelected = GetCurSel(); + if (iSelected < 9) + CheckMenuItem(m_hListMenu, iSelected, MF_BYPOSITION|MF_CHECKED); + + return m_hListMenu; + } + + inline CRect CTab::GetListRect() const + { + CRect rcList; + if (GetShowButtons()) + { + CRect rcClose = GetCloseRect(); + rcList = rcClose; + rcList.OffsetRect( -(rcClose.Width() + 2), 0); + rcList.InflateRect(-1, 0); + } + return rcList; + } + + inline SIZE CTab::GetMaxTabSize() const + { + CSize Size; + + for (int i = 0; i < TabCtrl_GetItemCount(m_hWnd); i++) + { + CClientDC dcClient(this); + dcClient.SelectObject(&m_Font); + std::vector<TCHAR> vTitle(MAX_MENU_STRING, _T('\0')); + TCHAR* pszTitle = &vTitle.front(); + TCITEM tcItem = {0}; + tcItem.mask = TCIF_TEXT |TCIF_IMAGE; + tcItem.cchTextMax = MAX_MENU_STRING; + tcItem.pszText = pszTitle; + TabCtrl_GetItem(m_hWnd, i, &tcItem); + CSize TempSize = dcClient.GetTextExtentPoint32(pszTitle, lstrlen(pszTitle)); + + int iImageSize = 0; + int iPadding = 6; + if (tcItem.iImage >= 0) + iImageSize = 20; + TempSize.cx += iImageSize + iPadding; + + if (TempSize.cx > Size.cx) + Size = TempSize; + } + + return Size; + } + + inline BOOL CTab::GetTabsAtTop() const + // Returns TRUE if the contol's tabs are placed at the top + { + DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE); + return (!(dwStyle & TCS_BOTTOM)); + } + + inline int CTab::GetTextHeight() const + { + CClientDC dcClient(this); + dcClient.SelectObject(&m_Font); + CSize szText = dcClient.GetTextExtentPoint32(_T("Text"), lstrlen(_T("Text"))); + return szText.cy; + } + + inline int CTab::GetTabIndex(CWnd* pWnd) const + { + assert(pWnd); + + for (int i = 0; i < (int)m_vTabPageInfo.size(); ++i) + { + if (m_vTabPageInfo[i].pView == pWnd) + return i; + } + + return -1; + } + + inline TabPageInfo CTab::GetTabPageInfo(UINT nTab) const + { + assert (nTab < m_vTabPageInfo.size()); + + return m_vTabPageInfo[nTab]; + } + + inline void CTab::NotifyChanged() + { + NMHDR nmhdr = {0}; + nmhdr.hwndFrom = m_hWnd; + nmhdr.code = UWM_TAB_CHANGED; + GetParent()->SendMessage(WM_NOTIFY, 0L, (LPARAM)&nmhdr); + } + + inline void CTab::OnCreate() + { + SetFont(&m_Font, TRUE); + + // Assign ImageList unless we are owner drawn + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) + TabCtrl_SetImageList(m_hWnd, m_himlTab); + + for (int i = 0; i < (int)m_vTabPageInfo.size(); ++i) + { + // Add tabs for each view. + TCITEM tie = {0}; + tie.mask = TCIF_TEXT | TCIF_IMAGE; + tie.iImage = m_vTabPageInfo[i].iImage; + tie.pszText = m_vTabPageInfo[i].szTabText; + TabCtrl_InsertItem(m_hWnd, i, &tie); + } + + int HeightGap = 5; + SetTabHeight(MAX(20, (GetTextHeight() + HeightGap))); + SelectPage(0); + } + + inline void CTab::OnLButtonDown(WPARAM /*wParam*/, LPARAM lParam) + { + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + + if (GetCloseRect().PtInRect(pt)) + { + m_IsClosePressed = TRUE; + SetCapture(); + CClientDC dc(this); + DrawCloseButton(dc); + } + else + m_IsClosePressed = FALSE; + + if (GetListRect().PtInRect(pt)) + { + ShowListMenu(); + } + } + + inline void CTab::OnLButtonUp(WPARAM /*wParam*/, LPARAM lParam) + { + ReleaseCapture(); + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + if (m_IsClosePressed && GetCloseRect().PtInRect(pt)) + { + RemoveTabPage(GetCurSel()); + if (GetActiveView()) + GetActiveView()->RedrawWindow(); + } + + m_IsClosePressed = FALSE; + } + + inline void CTab::OnMouseLeave(WPARAM /*wParam*/, LPARAM /*lParam*/) + { + CClientDC dc(this); + DrawCloseButton(dc); + DrawListButton(dc); + + m_IsTracking = FALSE; + } + + inline void CTab::OnMouseMove(WPARAM /*wParam*/, LPARAM /*lParam*/) + { + if (!m_IsListMenuActive && m_IsListPressed) + { + m_IsListPressed = FALSE; + } + + if (!m_IsTracking) + { + TRACKMOUSEEVENT TrackMouseEventStruct = {0}; + TrackMouseEventStruct.cbSize = sizeof(TrackMouseEventStruct); + TrackMouseEventStruct.dwFlags = TME_LEAVE; + TrackMouseEventStruct.hwndTrack = m_hWnd; + _TrackMouseEvent(&TrackMouseEventStruct); + m_IsTracking = TRUE; + } + + CClientDC dc(this); + DrawCloseButton(dc); + DrawListButton(dc); + } + + inline LRESULT CTab::OnNCHitTest(WPARAM wParam, LPARAM lParam) + { + // Ensure we have an arrow cursor when the tab has no view window + if (0 == GetAllTabs().size()) + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + // Cause WM_LBUTTONUP and WM_LBUTTONDOWN messages to be sent for buttons + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + ScreenToClient(pt); + if (GetCloseRect().PtInRect(pt)) return HTCLIENT; + if (GetListRect().PtInRect(pt)) return HTCLIENT; + + return CWnd::WndProcDefault(WM_NCHITTEST, wParam, lParam); + } + + inline LRESULT CTab::OnNotifyReflect(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + { + // Display the newly selected tab page + int nPage = GetCurSel(); + ShowActiveView(m_vTabPageInfo[nPage].pView); + } + break; + } + + return 0L; + } + + inline void CTab::Paint() + { + // Microsoft's drawing for a tab control is rubbish, so we do our own. + // We use double buffering and regions to eliminate flicker + + // Create the memory DC and bitmap + CClientDC dcView(this); + CMemDC dcMem(&dcView); + CRect rcClient = GetClientRect(); + dcMem.CreateCompatibleBitmap(&dcView, rcClient.Width(), rcClient.Height()); + + if (0 == GetItemCount()) + { + // No tabs, so simply display a grey background and exit + COLORREF rgbDialog = GetSysColor(COLOR_BTNFACE); + dcView.SolidFill(rgbDialog, rcClient); + return; + } + + // Create a clipping region. Its the overall tab window's region, + // less the region belonging to the individual tab view's client area + CRgn rgnSrc1 = ::CreateRectRgn(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); + CRect rcTab = GetClientRect(); + TabCtrl_AdjustRect(m_hWnd, FALSE, &rcTab); + if (rcTab.Height() < 0) + rcTab.top = rcTab.bottom; + if (rcTab.Width() < 0) + rcTab.left = rcTab.right; + + CRgn rgnSrc2 = ::CreateRectRgn(rcTab.left, rcTab.top, rcTab.right, rcTab.bottom); + CRgn rgnClip = ::CreateRectRgn(0, 0, 0, 0); + ::CombineRgn(rgnClip, rgnSrc1, rgnSrc2, RGN_DIFF); + + // Use the region in the memory DC to paint the grey background + dcMem.SelectClipRgn(&rgnClip); + HWND hWndParent = ::GetParent(m_hWnd); + CDC dcParent = ::GetDC(hWndParent); + HBRUSH hBrush = (HBRUSH) SendMessage(hWndParent, WM_CTLCOLORDLG, (WPARAM)dcParent.GetHDC(), (LPARAM)hWndParent); + dcMem.SelectObject(FromHandle(hBrush)); + dcMem.PaintRgn(&rgnClip); + + // Draw the tab buttons on the memory DC: + DrawTabs(dcMem); + + // Draw buttons and tab borders + DrawCloseButton(dcMem); + DrawListButton(dcMem); + DrawTabBorders(dcMem, rcTab); + + // Now copy our from our memory DC to the window DC + dcView.SelectClipRgn(&rgnClip); + dcView.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &dcMem, 0, 0, SRCCOPY); + } + + inline void CTab::PreCreate(CREATESTRUCT &cs) + { + // For Tabs on the bottom, add the TCS_BOTTOM style + cs.style = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE; + } + + inline void CTab::PreRegisterClass(WNDCLASS &wc) + { + wc.lpszClassName = WC_TABCONTROL; + } + + inline void CTab::RecalcLayout() + { + if (IsWindow()) + { + if (GetActiveView()) + { + // Set the tab sizes + SetTabSize(); + + // Position the View over the tab control's display area + CRect rc = GetClientRect(); + TabCtrl_AdjustRect(m_hWnd, FALSE, &rc); + GetActiveView()->SetWindowPos(NULL, rc, SWP_SHOWWINDOW); + } + else + RedrawWindow(); + } + } + + inline void CTab::RemoveTabPage(int nPage) + { + if ((nPage < 0) || (nPage > (int)m_vTabPageInfo.size() -1)) + return; + + // Remove the tab + TabCtrl_DeleteItem(m_hWnd, nPage); + + // Remove the TapPageInfo entry + std::vector<TabPageInfo>::iterator itTPI = m_vTabPageInfo.begin() + nPage; + CWnd* pView = (*itTPI).pView; + int iImage = (*itTPI).iImage; + if (iImage >= 0) + TabCtrl_RemoveImage(m_hWnd, iImage); + + if (pView == m_pActiveView) + m_pActiveView = 0; + + (*itTPI).pView->Destroy(); + m_vTabPageInfo.erase(itTPI); + + std::vector<WndPtr>::iterator itView; + for (itView = m_vTabViews.begin(); itView < m_vTabViews.end(); ++itView) + { + if ((*itView).get() == pView) + { + m_vTabViews.erase(itView); + break; + } + } + + if (IsWindow()) + { + if (m_vTabPageInfo.size() > 0) + { + SetTabSize(); + SelectPage(0); + } + else + ShowActiveView(NULL); + + NotifyChanged(); + } + } + + inline void CTab::SelectPage(int nPage) + { + if ((nPage >= 0) && (nPage < GetItemCount())) + { + if (nPage != GetCurSel()) + SetCurSel(nPage); + + ShowActiveView(m_vTabPageInfo[nPage].pView); + } + } + + inline void CTab::SetFixedWidth(BOOL bEnabled) + { + DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE); + if (bEnabled) + SetWindowLongPtr(GWL_STYLE, dwStyle | TCS_FIXEDWIDTH); + else + SetWindowLongPtr(GWL_STYLE, dwStyle & ~TCS_FIXEDWIDTH); + + RecalcLayout(); + } + + inline void CTab::SetOwnerDraw(BOOL bEnabled) + // Enable or disable owner draw + { + DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE); + if (bEnabled) + { + SetWindowLongPtr(GWL_STYLE, dwStyle | TCS_OWNERDRAWFIXED); + TabCtrl_SetImageList(m_hWnd, NULL); + } + else + { + SetWindowLongPtr(GWL_STYLE, dwStyle & ~TCS_OWNERDRAWFIXED); + TabCtrl_SetImageList(m_hWnd, m_himlTab); + } + + RecalcLayout(); + } + + inline void CTab::SetShowButtons(BOOL bShow) + { + m_bShowButtons = bShow; + RecalcLayout(); + } + + inline void CTab::SetTabIcon(int i, HICON hIcon) + // Changes or sets the tab's icon + { + assert (GetItemCount() > i); + TCITEM tci = {0}; + tci.mask = TCIF_IMAGE; + GetItem(i, &tci); + if (tci.iImage >= 0) + { + ImageList_ReplaceIcon(GetImageList(), i, hIcon); + } + else + { + int iImage = ImageList_AddIcon(GetImageList(), hIcon); + tci.iImage = iImage; + TabCtrl_SetItem(m_hWnd, i, &tci); + m_vTabPageInfo[i].iImage = iImage; + } + } + + inline void CTab::SetTabsAtTop(BOOL bTop) + // Positions the tabs at the top or botttom of the control + { + DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE); + + if (bTop) + dwStyle &= ~TCS_BOTTOM; + else + dwStyle |= TCS_BOTTOM; + + SetWindowLongPtr(GWL_STYLE, dwStyle); + RecalcLayout(); + } + + inline void CTab::SetTabSize() + { + if (GetItemCount() > 0) + { + CRect rc = GetClientRect(); + TabCtrl_AdjustRect(m_hWnd, FALSE, &rc); + + int xGap = 2; + if (m_bShowButtons) xGap += GetCloseRect().Width() + GetListRect().Width() +2; + + int nItemWidth = MIN( GetMaxTabSize().cx, (rc.Width() - xGap)/GetItemCount() ); + nItemWidth = MAX(nItemWidth, 0); + SendMessage(TCM_SETITEMSIZE, 0L, MAKELPARAM(nItemWidth, m_nTabHeight)); + NotifyChanged(); + } + } + + inline void CTab::SetTabText(UINT nTab, LPCTSTR szText) + { + // Allows the text to be changed on an existing tab + if (nTab < GetAllTabs().size()) + { + TCITEM Item = {0}; + std::vector<TCHAR> vTChar(MAX_MENU_STRING+1, _T('\0')); + TCHAR* pTChar = &vTChar.front(); + lstrcpyn(pTChar, szText, MAX_MENU_STRING); + Item.mask = TCIF_TEXT; + Item.pszText = pTChar; + + if (TabCtrl_SetItem(m_hWnd, nTab, &Item)) + lstrcpyn(m_vTabPageInfo[nTab].szTabText, pTChar, MAX_MENU_STRING); + } + } + + inline void CTab::ShowActiveView(CWnd* pView) + // Sets or changes the View window displayed within the tab page + { + // Hide the old view + if (GetActiveView() && (GetActiveView()->IsWindow())) + GetActiveView()->ShowWindow(SW_HIDE); + + // Assign the view window + m_pActiveView = pView; + + if (m_pActiveView && m_hWnd) + { + if (!m_pActiveView->IsWindow()) + { + // The tab control is already created, so create the new view too + GetActiveView()->Create(this); + } + + // Position the View over the tab control's display area + CRect rc = GetClientRect(); + TabCtrl_AdjustRect(m_hWnd, FALSE, &rc); + GetActiveView()->SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW); + GetActiveView()->SetFocus(); + } + } + + inline void CTab::ShowListMenu() + // Displays the list of windows in a popup menu + { + if (!m_IsListPressed) + { + m_IsListPressed = TRUE; + HMENU hMenu = GetListMenu(); + + CPoint pt(GetListRect().left, GetListRect().top + GetTabHeight()); + ClientToScreen(pt); + + // Choosing the frame's hwnd for the menu's messages will automatically theme the popup menu + HWND MenuHwnd = GetAncestor()->GetHwnd(); + int nPage = 0; + m_IsListMenuActive = TRUE; + nPage = TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, pt.x, pt.y, MenuHwnd, NULL) - IDW_FIRSTCHILD; + if ((nPage >= 0) && (nPage < 9)) SelectPage(nPage); + if (nPage == 9) ShowListDialog(); + m_IsListMenuActive = FALSE; + } + + CClientDC dc(this); + DrawListButton(dc); + } + + inline void CTab::ShowListDialog() + { + // Definition of a dialog template which displays a List Box + unsigned char dlg_Template[] = + { + 0x01,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xc8,0x00,0xc8,0x90,0x03, + 0x00,0x00,0x00,0x00,0x00,0xdc,0x00,0x8e,0x00,0x00,0x00,0x00,0x00,0x53,0x00,0x65, + 0x00,0x6c,0x00,0x65,0x00,0x63,0x00,0x74,0x00,0x20,0x00,0x57,0x00,0x69,0x00,0x6e, + 0x00,0x64,0x00,0x6f,0x00,0x77,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x01,0x4d, + 0x00,0x53,0x00,0x20,0x00,0x53,0x00,0x68,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x20, + 0x00,0x44,0x00,0x6c,0x00,0x67,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x01,0x50,0x40,0x00,0x7a,0x00,0x25,0x00,0x0f,0x00,0x01, + 0x00,0x00,0x00,0xff,0xff,0x80,0x00,0x4f,0x00,0x4b,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x50,0x7a,0x00,0x7a,0x00,0x25, + 0x00,0x0f,0x00,0x02,0x00,0x00,0x00,0xff,0xff,0x80,0x00,0x43,0x00,0x61,0x00,0x6e, + 0x00,0x63,0x00,0x65,0x00,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x00,0x01,0x01,0x21,0x50,0x06,0x00,0x06,0x00,0xcf,0x00,0x6d,0x00,0x79, + 0x00,0x00,0x00,0xff,0xff,0x83,0x00,0x00,0x00,0x00,0x00 + }; + + // Display the modal dialog. The dialog is defined in the dialog template rather + // than in the resource script (rc) file. + CSelectDialog MyDialog((LPCDLGTEMPLATE) dlg_Template); + for(UINT u = 0; u < GetAllTabs().size(); ++u) + { + MyDialog.AddItem(GetAllTabs()[u].szTabText); + } + + int iSelected = (int)MyDialog.DoModal(); + if (iSelected >= 0) SelectPage(iSelected); + } + + inline void CTab::SwapTabs(UINT nTab1, UINT nTab2) + { + if ((nTab1 < GetAllTabs().size()) && (nTab2 < GetAllTabs().size()) && (nTab1 != nTab2)) + { + int nPage = GetCurSel(); + TabPageInfo T1 = GetTabPageInfo(nTab1); + TabPageInfo T2 = GetTabPageInfo(nTab2); + + TCITEM Item1 = {0}; + Item1.mask = TCIF_IMAGE | TCIF_PARAM | TCIF_RTLREADING | TCIF_STATE | TCIF_TEXT; + GetItem(nTab1, &Item1); + TCITEM Item2 = {0}; + Item2.mask = TCIF_IMAGE | TCIF_PARAM | TCIF_RTLREADING | TCIF_STATE | TCIF_TEXT; + GetItem(nTab2, &Item2); + TabCtrl_SetItem(m_hWnd, nTab1, &Item2); + TabCtrl_SetItem(m_hWnd, nTab2, &Item1); + + m_vTabPageInfo[nTab1] = T2; + m_vTabPageInfo[nTab2] = T1; + SelectPage(nPage); + } + } + + inline LRESULT CTab::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch(uMsg) + { + case WM_PAINT: + if (GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED) + { + // Remove all pending paint requests + PAINTSTRUCT ps; + BeginPaint(ps); + EndPaint(ps); + + // Now call our local Paint + Paint(); + return 0; + } + break; + + case WM_ERASEBKGND: + if (GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED) + return 0; + break; + case WM_KILLFOCUS: + m_IsClosePressed = FALSE; + break; + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + OnLButtonDown(wParam, lParam); + break; + case WM_LBUTTONUP: + OnLButtonUp(wParam, lParam); + break; + case WM_MOUSEMOVE: + OnMouseMove(wParam, lParam); + break; + case WM_MOUSELEAVE: + OnMouseLeave(wParam, lParam); + break; + case WM_NCHITTEST: + return OnNCHitTest(wParam, lParam); + + case WM_WINDOWPOSCHANGING: + // A little hack to reduce tab flicker + if (IsWindowVisible() && (GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) + { + LPWINDOWPOS pWinPos = (LPWINDOWPOS)lParam; + pWinPos->flags |= SWP_NOREDRAW; + + Paint(); + } + + break; + + case WM_WINDOWPOSCHANGED: + RecalcLayout(); + break; + } + + // pass unhandled messages on for default processing + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + // Wrappers for Win32 Macros + inline void CTab::AdjustRect(BOOL fLarger, RECT *prc) const + { + assert(::IsWindow(m_hWnd)); + TabCtrl_AdjustRect(m_hWnd, fLarger, prc); + } + + inline int CTab::GetCurFocus() const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_GetCurFocus(m_hWnd); + } + + inline int CTab::GetCurSel() const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_GetCurSel(m_hWnd); + } + + inline BOOL CTab::GetItem(int iItem, LPTCITEM pitem) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_GetItem(m_hWnd, iItem, pitem); + } + + inline int CTab::GetItemCount() const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_GetItemCount(m_hWnd); + } + + inline int CTab::HitTest(TCHITTESTINFO& info) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_HitTest(m_hWnd, &info); + } + + inline void CTab::SetCurFocus(int iItem) const + { + assert(::IsWindow(m_hWnd)); + TabCtrl_SetCurFocus(m_hWnd, iItem); + } + + inline int CTab::SetCurSel(int iItem) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_SetCurSel(m_hWnd, iItem); + } + + inline DWORD CTab::SetItemSize(int cx, int cy) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_SetItemSize(m_hWnd, cx, cy); + } + + inline int CTab::SetMinTabWidth(int cx) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_SetMinTabWidth(m_hWnd, cx); + } + + inline void CTab::SetPadding(int cx, int cy) const + { + assert(::IsWindow(m_hWnd)); + TabCtrl_SetPadding(m_hWnd, cx, cy); + } + + //////////////////////////////////////// + // Definitions for the CTabbedMDI class + inline CTabbedMDI::CTabbedMDI() + { + GetTab().SetShowButtons(TRUE); + } + + inline CTabbedMDI::~CTabbedMDI() + { + } + + inline CWnd* CTabbedMDI::AddMDIChild(CWnd* pView, LPCTSTR szTabText, int idMDIChild /*= 0*/) + { + assert(pView); + assert(lstrlen(szTabText) < MAX_MENU_STRING); + + GetTab().AddTabPage(WndPtr(pView), szTabText, 0, idMDIChild); + + // Fake a WM_MOUSEACTIVATE to propogate focus change to dockers + if (IsWindow()) + GetParent()->SendMessage(WM_MOUSEACTIVATE, (WPARAM)GetAncestor(), MAKELPARAM(HTCLIENT,WM_LBUTTONDOWN)); + + return pView; + } + + inline void CTabbedMDI::CloseActiveMDI() + { + int nTab = GetTab().GetCurSel(); + if (nTab >= 0) + GetTab().RemoveTabPage(nTab); + + RecalcLayout(); + } + + inline void CTabbedMDI::CloseAllMDIChildren() + { + while (GetMDIChildCount() > 0) + { + GetTab().RemoveTabPage(0); + } + } + + inline void CTabbedMDI::CloseMDIChild(int nTab) + { + GetTab().RemoveTabPage(nTab); + + if (GetActiveMDIChild()) + GetActiveMDIChild()->RedrawWindow(); + } + + inline HWND CTabbedMDI::Create(CWnd* pParent /* = NULL*/) + { + CLIENTCREATESTRUCT clientcreate ; + clientcreate.hWindowMenu = m_hWnd; + clientcreate.idFirstChild = IDW_FIRSTCHILD ; + DWORD dwStyle = WS_CHILD | WS_VISIBLE | MDIS_ALLCHILDSTYLES; + + // Create the MDICLIENT view window + if (!CreateEx(0, _T("MDICLIENT"), _T(""), + dwStyle, 0, 0, 0, 0, pParent, NULL, (PSTR) &clientcreate)) + throw CWinException(_T("CMDIClient::Create ... CreateEx failed")); + + return m_hWnd; + } + + inline CWnd* CTabbedMDI::GetActiveMDIChild() const + { + CWnd* pView = NULL; + int nTab = GetTab().GetCurSel(); + if (nTab >= 0) + { + TabPageInfo tbi = GetTab().GetTabPageInfo(nTab); + pView = tbi.pView; + } + + return pView; + } + + inline int CTabbedMDI::GetActiveMDITab() const + { + return GetTab().GetCurSel(); + } + + inline CWnd* CTabbedMDI::GetMDIChild(int nTab) const + { + assert(nTab >= 0); + assert(nTab < GetMDIChildCount()); + return GetTab().GetTabPageInfo(nTab).pView; + } + + inline int CTabbedMDI::GetMDIChildCount() const + { + return (int) GetTab().GetAllTabs().size(); + } + + inline int CTabbedMDI::GetMDIChildID(int nTab) const + { + assert(nTab >= 0); + assert(nTab < GetMDIChildCount()); + return GetTab().GetTabPageInfo(nTab).idTab; + } + + inline LPCTSTR CTabbedMDI::GetMDIChildTitle(int nTab) const + { + assert(nTab >= 0); + assert(nTab < GetMDIChildCount()); + return GetTab().GetTabPageInfo(nTab).szTabText; + } + + inline BOOL CTabbedMDI::LoadRegistrySettings(tString tsRegistryKeyName) + { + BOOL bResult = FALSE; + + if (0 != tsRegistryKeyName.size()) + { + tString tsKey = _T("Software\\") + tsRegistryKeyName + _T("\\MDI Children"); + HKEY hKey = 0; + RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey); + if (hKey) + { + DWORD dwType = REG_BINARY; + DWORD BufferSize = sizeof(TabPageInfo); + TabPageInfo tbi = {0}; + int i = 0; + TCHAR szNumber[16]; + tString tsSubKey = _T("MDI Child "); + tsSubKey += _itot(i, szNumber, 10); + + // Fill the DockList vector from the registry + while (0 == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&tbi, &BufferSize)) + { + CWnd* pWnd = NewMDIChildFromID(tbi.idTab); + if (pWnd) + { + AddMDIChild(pWnd, tbi.szTabText, tbi.idTab); + i++; + tsSubKey = _T("MDI Child "); + tsSubKey += _itot(i, szNumber, 10); + bResult = TRUE; + } + else + { + TRACE(_T("Failed to get TabbedMDI info from registry")); + bResult = FALSE; + break; + } + } + + // Load Active MDI Tab from the registry + tsSubKey = _T("Active MDI Tab"); + int nTab; + dwType = REG_DWORD; + BufferSize = sizeof(int); + if(ERROR_SUCCESS == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&nTab, &BufferSize)) + SetActiveMDITab(nTab); + else + SetActiveMDITab(0); + + RegCloseKey(hKey); + } + } + + if (!bResult) + CloseAllMDIChildren(); + + return bResult; + } + + inline CWnd* CTabbedMDI::NewMDIChildFromID(int /*idMDIChild*/) + { + // Override this function to create new MDI children from IDs as shown below + CWnd* pView = NULL; + /* switch(idTab) + { + case ID_SIMPLE: + pView = new CViewSimple; + break; + case ID_RECT: + pView = new CViewRect; + break; + default: + TRACE(_T("Unknown MDI child ID\n")); + break; + } */ + + return pView; + } + + inline void CTabbedMDI::OnCreate() + { + GetTab().Create(this); + GetTab().SetFixedWidth(TRUE); + GetTab().SetOwnerDraw(TRUE); + } + + inline void CTabbedMDI::OnDestroy(WPARAM /*wParam*/, LPARAM /*lParam*/ ) + { + CloseAllMDIChildren(); + } + + inline LRESULT CTabbedMDI::OnNotify(WPARAM /*wParam*/, LPARAM lParam) + { + LPNMHDR pnmhdr = (LPNMHDR)lParam; + if (pnmhdr->code == UWM_TAB_CHANGED) + RecalcLayout(); + + return 0L; + } + + inline void CTabbedMDI::OnWindowPosChanged(WPARAM /*wParam*/, LPARAM /*lParam*/) + { + RecalcLayout(); + } + + inline void CTabbedMDI::RecalcLayout() + { + if (GetTab().IsWindow()) + { + if (GetTab().GetItemCount() >0) + { + CRect rcClient = GetClientRect(); + GetTab().SetWindowPos(NULL, rcClient, SWP_SHOWWINDOW); + GetTab().UpdateWindow(); + } + else + { + CRect rcClient = GetClientRect(); + GetTab().SetWindowPos(NULL, rcClient, SWP_HIDEWINDOW); + Invalidate(); + } + } + } + + inline BOOL CTabbedMDI::SaveRegistrySettings(tString tsRegistryKeyName) + { + if (0 != tsRegistryKeyName.size()) + { + tString tsKeyName = _T("Software\\") + tsRegistryKeyName; + HKEY hKey = NULL; + HKEY hKeyMDIChild = NULL; + + try + { + if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL)) + throw (CWinException(_T("RegCreateKeyEx Failed"))); + + RegDeleteKey(hKey, _T("MDI Children")); + if (ERROR_SUCCESS != RegCreateKeyEx(hKey, _T("MDI Children"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyMDIChild, NULL)) + throw (CWinException(_T("RegCreateKeyEx Failed"))); + + for (int i = 0; i < GetMDIChildCount(); ++i) + { + TCHAR szNumber[16]; + tString tsSubKey = _T("MDI Child "); + tsSubKey += _itot(i, szNumber, 10); + TabPageInfo pdi = GetTab().GetTabPageInfo(i); + if (ERROR_SUCCESS != RegSetValueEx(hKeyMDIChild, tsSubKey.c_str(), 0, REG_BINARY, (LPBYTE)&pdi, sizeof(TabPageInfo))) + throw (CWinException(_T("RegSetValueEx Failed"))); + } + + // Add Active Tab to the registry + tString tsSubKey = _T("Active MDI Tab"); + int nTab = GetActiveMDITab(); + if(ERROR_SUCCESS != RegSetValueEx(hKeyMDIChild, tsSubKey.c_str(), 0, REG_DWORD, (LPBYTE)&nTab, sizeof(int))) + throw (CWinException(_T("RegSetValueEx failed"))); + + RegCloseKey(hKeyMDIChild); + RegCloseKey(hKey); + } + catch (const CWinException& e) + { + // Roll back the registry changes by deleting the subkeys + if (hKey) + { + if (hKeyMDIChild) + { + RegDeleteKey(hKeyMDIChild, _T("MDI Children")); + RegCloseKey(hKeyMDIChild); + } + + RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str()); + RegCloseKey(hKey); + } + + e.what(); + return FALSE; + } + } + + return TRUE; + } + + inline void CTabbedMDI::SetActiveMDIChild(CWnd* pWnd) + { + assert(pWnd); + int nPage = GetTab().GetTabIndex(pWnd); + if (nPage >= 0) + GetTab().SelectPage(nPage); + } + + inline void CTabbedMDI::SetActiveMDITab(int iTab) + { + assert(::IsWindow(m_hWnd)); + assert(GetTab().IsWindow()); + GetTab().SelectPage(iTab); + } + + inline LRESULT CTabbedMDI::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch(uMsg) + { + case WM_DESTROY: + OnDestroy(wParam, lParam); + break; + + case WM_WINDOWPOSCHANGED: + OnWindowPosChanged(wParam, lParam); + break; + } + + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + +} // namespace Win32xx + +#endif // _WIN32XX_TAB_H_ |