// 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 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 & GetAllTabs() const { return (std::vector &) 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 m_vTabPageInfo; std::vector 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 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::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::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 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_