// XTExcelTabCtrl.cpp : implementation of the CXTExcelTabCtrl class. // // This file is a part of the XTREME CONTROLS MFC class library. // (c)1998-2008 Codejock Software, All Rights Reserved. // // THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE // RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN // CONSENT OF CODEJOCK SOFTWARE. // // THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED // IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO // YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A // SINGLE COMPUTER. // // CONTACT INFORMATION: // support@codejock.com // http://www.codejock.com // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Resource.h" #include "Common/XTPResourceManager.h" #include "Common/XTPColorManager.h" #include "Common/XTPDrawHelpers.h" #include "XTGlobal.h" #include "XTExcelTabCtrl.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif static const int GRIPPER_WIDTH = 6; static const int BUTTON_COUNT = 4; #define XT_IDC_BTN_LEFT 100 #define XT_IDC_BTN_RIGHT 101 #define XT_IDC_BTN_HOME 102 #define XT_IDC_BTN_END 103 ///////////////////////////////////////////////////////////////////////////// // CXTExcelTabCtrlButtonState CXTExcelTabCtrlButtonState::CXTExcelTabCtrlButtonState() : m_rect(0, 0, 0, 0) , m_bPressed(false) , m_bEnabled(true) , m_bHilight(false) { m_iCommand = 0; } void CXTExcelTabCtrlButtonState::SetInfo(CRect rect, int iCommand, XTArrowIcon iconType) { m_rect = rect; m_iCommand = iCommand; m_IconType = iconType; } IMPLEMENT_THEME_HOST(CXTExcelTabCtrl) IMPLEMENT_THEME_REFRESH(CXTExcelTabCtrl, CWnd) ///////////////////////////////////////////////////////////////////////////// // CXTExcelTabCtrl CXTExcelTabCtrl::CXTExcelTabCtrl() : CXTThemeManagerStyleHost(GetThemeFactoryClass()) , m_iBtnLeft(-1) , m_iBtnRight(-1) , m_iBtnHome(-1) , m_iBtnEnd(-1) , m_bPainted(false) , m_iBtnHilight(-1) { m_dwStyle = NULL; m_nCurSel = -1; m_nOffset = 0; m_nClientWidth = 0; m_nClientHeight = 0; m_pNormFont = &XTAuxData().font; m_pBoldFont = &XTAuxData().fontBold; m_bManagingViews = false; m_rectGripper = CRect(0, 0, 0, 0); m_bTracking = false; m_bUserColors = false; m_pWndLastFocus = NULL; m_iGripperPosPerCent = 0; m_xGripperPos = -1; // Initialize the width and height for the left, right, home // and end buttons. m_cy = ::GetSystemMetrics(SM_CYHSCROLL); m_cx = ::GetSystemMetrics(SM_CXHSCROLL)-1; UpdateDefaultColors(); // Create the icons used by the left, right, home and end buttons. CreateButtonIcons(); } CXTExcelTabCtrl::~CXTExcelTabCtrl() { // Cleanup ClearAllItems(); FreeButtonIcons(); } IMPLEMENT_DYNAMIC(CXTExcelTabCtrl, CWnd) BEGIN_MESSAGE_MAP(CXTExcelTabCtrl, CWnd) //{{AFX_MSG_MAP(CXTExcelTabCtrl) ON_WM_CREATE() ON_WM_PAINT() ON_MESSAGE(WM_PRINTCLIENT, OnPrintClient) ON_WM_ERASEBKGND() ON_BN_CLICKED(XT_IDC_BTN_LEFT, OnLeftArrow) ON_BN_CLICKED(XT_IDC_BTN_RIGHT, OnRightArrow) ON_BN_CLICKED(XT_IDC_BTN_HOME, OnHomeArrow) ON_BN_CLICKED(XT_IDC_BTN_END, OnEndArrow) ON_WM_SIZE() ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_SETCURSOR() ON_WM_LBUTTONUP() ON_WM_LBUTTONDBLCLK() ON_WM_HSCROLL() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() BOOL CXTExcelTabCtrl::Create(DWORD dwStyle, const CRect& rect, CWnd* pParentWnd, UINT nID) { ASSERT(pParentWnd != NULL); // must be valid. // Call the base class for creation. if (!CWnd::Create(NULL, NULL, dwStyle, rect, pParentWnd, nID)) { TRACE0("Failed to create flat tab control.\n"); return FALSE; } return TRUE; } int CXTExcelTabCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CWnd::OnCreate(lpCreateStruct) == -1) return -1; // Save the window style. m_dwStyle = lpCreateStruct->style; m_nClientWidth = lpCreateStruct->cx; m_nClientHeight = lpCreateStruct->cy; SetTabStyle(m_dwStyle); return 0; } void CXTExcelTabCtrl::SetGripperPosition(int x, bool bPercent) { // only valid for tabs with horizontal scroll bars if (!(m_dwStyle & FTS_XT_HSCROLL)) return; int x_percent; if (bPercent) { x_percent = x; x = m_nClientWidth * x / 100; } else { x_percent = x * 100 / m_nClientWidth; } // too far to the left ? const int arrowWidth = 0; //GetTotalArrowWidth(); if (x < arrowWidth) x = arrowWidth; // too far to the right ? const int sbExtreme = (m_nClientWidth - 4 * m_cx - m_cy); if (x > sbExtreme) x = sbExtreme; m_xGripperPos = x; m_iGripperPosPerCent = x_percent; m_rectGripper = m_rectTabs; m_rectGripper.left += x; m_rectGripper.right = m_rectGripper.left + GRIPPER_WIDTH; const int isbottom = ((m_dwStyle & FTS_XT_BOTTOM) == FTS_XT_BOTTOM); m_rectSB_H = CRect( m_rectGripper.right, m_rectTabs.top - !isbottom, m_rectTabs.right - m_cy, m_rectTabs.bottom + isbottom); m_wndHScrollBar.MoveWindow(m_rectSB_H); EnableButtons(); InvalidateRect(m_rectTabs); } void CXTExcelTabCtrl::SetTabStyle(DWORD dwStyle) { m_dwStyle = dwStyle; if (!GetSafeHwnd()) return; if (m_dwStyle & FTS_XT_HASHOMEEND) m_dwStyle |= FTS_XT_HASARROWS; // If we use tooltips, create the control. if (m_dwStyle & FTS_XT_TOOLTIPS) { if (m_ToolTip.GetSafeHwnd() == 0) { CString strTipText; XTPResourceManager()->LoadString(&strTipText, XT_IDS_TIPTEXT); m_ToolTip.Create(this); m_ToolTip.Activate(TRUE); m_ToolTip.AddTool(this, strTipText); m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, SHRT_MAX); // Allow multi line tooltips } } else { m_ToolTip.DestroyWindow(); } CRect rectDummy(0, 0, 0, 0); // If we use arrow buttons, create the buttons. if (m_dwStyle & FTS_XT_HASARROWS) { int nIndex = 0; // Create the home button. if (m_dwStyle & FTS_XT_HASHOMEEND) { m_buttons[nIndex].SetInfo(rectDummy, XT_IDC_BTN_HOME, xtArrowIconLeftHome); m_iBtnHome = nIndex; ++nIndex; } else { m_iBtnHome = 3; } // Create the left button. m_buttons[nIndex].SetInfo(rectDummy, XT_IDC_BTN_LEFT, xtArrowIconLeft); m_iBtnLeft = nIndex; ++nIndex; // Create the right button. m_buttons[nIndex].SetInfo(rectDummy, XT_IDC_BTN_RIGHT, xtArrowIconRight); m_iBtnRight = nIndex; ++nIndex; // Create the end button. if (m_dwStyle & FTS_XT_HASHOMEEND) { m_buttons[nIndex].SetInfo(rectDummy, XT_IDC_BTN_END, xtArrowIconRightHome); m_iBtnEnd = nIndex; ++nIndex; } else { m_iBtnEnd = 3; } } if (m_dwStyle & FTS_XT_HSCROLL) { if (m_wndHScrollBar.GetSafeHwnd() == 0) { CRect rc(0, 0, 0, 0); VERIFY(m_wndHScrollBar.Create(WS_VISIBLE | WS_CHILD | SBS_HORZ, rc, this, AFX_IDW_HSCROLL_FIRST)); SetGripperPosition(60, true); } } else { m_wndHScrollBar.DestroyWindow(); m_rectSB_H = CRect(0, 0, 0, 0); } RecalcLayout(); SetGripperPosition(m_iGripperPosPerCent, true); RedrawWindow(); } int CXTExcelTabCtrl::GetGripperPosition() const { return m_xGripperPos; } BOOL CXTExcelTabCtrl::OnEraseBkgnd(CDC* pDC) { if (GetItemCount() == 0) { pDC->FillSolidRect(m_rectViews, GetXtremeColor(COLOR_3DFACE)); } return TRUE; } void CXTExcelTabCtrl::CreateButtonIcons() { // obsolete } void CXTExcelTabCtrl::FreeButtonIcons() { // obsolete } int CXTExcelTabCtrl::GetTabWidth(int nItem) const { CDC *pDC = CDC::FromHandle(::GetDC(m_hWnd)); CFont* pOldFont = pDC->SelectObject((m_nCurSel == nItem) ? m_pBoldFont : m_pNormFont); CSize size = pDC->GetTextExtent(m_tcbItems[nItem]->szTabLabel); pDC->SelectObject(pOldFont); ::ReleaseDC(m_hWnd, pDC->m_hDC); return size.cx + m_cy + (m_cy / 2); } int CXTExcelTabCtrl::GetTotalTabWidth() const { int iWidth = 0; const int cItems = GetItemCount(); const int iOverlap = GetOverlap(); int i; for (i = 0; i < cItems; i++) { iWidth += GetTabWidth(i); if (i != 0) { iWidth -= iOverlap; } } return iWidth; } int CXTExcelTabCtrl::GetTotalArrowWidth() const { int iWidth = 0; if (m_dwStyle & FTS_XT_HASARROWS) iWidth += (m_cx * 2); if (m_dwStyle & FTS_XT_HASHOMEEND) iWidth += (m_cx * 2); return iWidth; } int CXTExcelTabCtrl::GetTotalTabAreaWidth() const { int iWidth; if (m_dwStyle & FTS_XT_HSCROLL) { iWidth = m_xGripperPos; } else { iWidth = m_nClientWidth; } iWidth -= GetTotalArrowWidth(); return iWidth; } void CXTExcelTabCtrl::InvalidateTabs() { if (GetSafeHwnd()) { // invalidate the visible tab area // to minimize flicker - don't erase the background CRect rcTabs; rcTabs.left = GetTotalArrowWidth(); rcTabs.top = m_rectTabs.top; rcTabs.right = rcTabs.left + (GetTotalTabWidth() - m_nOffset); rcTabs.bottom = m_rectTabs.bottom; InvalidateRect(&rcTabs, FALSE); // invalidate the blank area to the right of the tabs if (rcTabs.right < m_nClientWidth) { rcTabs.left = rcTabs.right; rcTabs.right = m_nClientWidth; InvalidateRect(&rcTabs, TRUE); } } } void CXTExcelTabCtrl::EnableButtons() { if (m_dwStyle & FTS_XT_HASARROWS) { m_buttons[m_iBtnLeft].m_bEnabled = (m_nOffset > 0); m_buttons[m_iBtnRight].m_bEnabled = (GetTotalTabWidth() - m_nOffset > GetTotalTabAreaWidth() - 1); if (m_dwStyle & FTS_XT_HASHOMEEND) { m_buttons[m_iBtnHome].m_bEnabled = m_buttons[m_iBtnLeft].m_bEnabled; m_buttons[m_iBtnEnd].m_bEnabled = m_buttons[m_iBtnRight].m_bEnabled; } CRect rc = m_buttons[m_iBtnLeft].m_rect; rc.left = 0; rc.right = GetTotalArrowWidth(); InvalidateRect(rc); } else { InvalidateRect(CRect(0, 0, GetTotalArrowWidth(), m_nClientHeight)); } } BOOL CXTExcelTabCtrl::GetItemRect(int nItem, LPRECT lpRect) { const int cItems = GetItemCount(); if (nItem < 0 || nItem >= cItems) return FALSE; const int iOverlap = GetOverlap(); int x = GetTotalArrowWidth(); int i; for (i = 0; i < nItem; i++) { x += GetTabWidth(i) - iOverlap; } lpRect->left = x - m_nOffset; lpRect->top = m_rectTabs.top; lpRect->right = lpRect->left + GetTabWidth(nItem); lpRect->bottom = m_rectTabs.bottom; return TRUE; } void CXTExcelTabCtrl::ResetMouseOver() { for (int i = 0; i < BUTTON_COUNT; ++i) { m_buttons[i].m_bHilight = false; } } int CXTExcelTabCtrl::ButtonHitTest(CPoint& pt) { ResetMouseOver(); int i; for (i = 0; i < BUTTON_COUNT; ++i) { if (m_buttons[i].m_rect.PtInRect(pt)) { m_buttons[i].m_bHilight = true; return i; } } return -1; } int CXTExcelTabCtrl::HitTest(TCHITTESTINFO* pHitTestInfo) const { // ignore hits on the buttons int iHitX = pHitTestInfo->pt.x; if (iHitX < GetTotalArrowWidth()) return -1; // on or to the right of the the scroll bar/gripper ? if ((m_dwStyle & FTS_XT_HSCROLL) && iHitX > m_rectGripper.left) { return -1; } const int iOverlap = GetOverlap(); // check if any tabs were hit int x = GetTotalArrowWidth() - m_nOffset; const int cItems = GetItemCount(); int i; for (i = 0; i < cItems; i++) { int iTabWidth = GetTabWidth(i); if (i != cItems - 1) { iTabWidth -= iOverlap; } if ((x <= iHitX) && (iHitX <= x + iTabWidth)) { return i; } x += iTabWidth; } // hit point is right of rightmost tab return -1; } void CXTExcelTabCtrl::OnLeftArrow() { // Move the tabs right, ensuring that we move right by one // whole tab each time ala Microsoft Access CPoint Point(GetTotalArrowWidth() + 1 + GetOverlap(), 1); TCHITTESTINFO htInfo; htInfo.pt = Point; int iTab = HitTest(&htInfo); if (iTab != -1) { m_nOffset = 0; RECT rect; if (GetItemRect(iTab - 1, &rect)) { m_nOffset += rect.left; m_nOffset -= GetTotalArrowWidth(); EnableButtons(); InvalidateTabs(); } } } void CXTExcelTabCtrl::OnRightArrow() { // Move the tabs left, ensuring that we move left by one // whole tab each time ala Microsoft Access CPoint Point(GetTotalArrowWidth()+ 1 + GetOverlap(), 1); TCHITTESTINFO htInfo; htInfo.pt = Point; int iTab = HitTest(&htInfo); if (iTab != -1) { RECT rect; if (GetItemRect(iTab + 1, &rect)) { m_nOffset += rect.left - GetTotalArrowWidth(); EnableButtons(); InvalidateTabs(); } } } void CXTExcelTabCtrl::OnHomeArrow() { m_nOffset = 0; EnableButtons(); InvalidateTabs(); } void CXTExcelTabCtrl::OnEndArrow() { m_nOffset = GetTotalTabWidth() - GetTotalTabAreaWidth() + 1; EnableButtons(); InvalidateTabs(); } void CXTExcelTabCtrl::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); m_nClientWidth = cx; m_nClientHeight = cy; RecalcLayout(); SetGripperPosition(m_iGripperPosPerCent, true); SyncScrollBar(); } int CXTExcelTabCtrl::InsertItem(int nItem, LPCTSTR lpszItem, CWnd* pWndControl) { const int cItems = GetItemCount(); if (nItem < 0 || nItem > cItems) return -1; if (pWndControl) { ASSERT(::IsWindow(pWndControl->m_hWnd)); // managed view should be a hidden child ASSERT((::GetWindowLong(pWndControl->m_hWnd, GWL_STYLE) & (WS_VISIBLE | WS_CHILD)) == WS_CHILD); // can't have both managed and non-managed tabs ASSERT(m_bManagingViews || cItems == 0); m_bManagingViews = true; pWndControl->SetParent(this); pWndControl->SetWindowPos(NULL, m_rectViews.left, m_rectViews.top, m_rectViews.Width(), m_rectViews.Height(), SWP_NOZORDER | SWP_NOREDRAW); } // Allocate a new CXTTcbItem object. CXTTcbItem* pMember = new CXTTcbItem; if (pMember == NULL) return -1; pMember->pWnd = pWndControl; pMember->szTabLabel = lpszItem; pMember->szToolTipLabel = lpszItem; pMember->uiToolTipId = GetItemCount(); if (nItem <= m_nCurSel) ++m_nCurSel; // Add the new CXTTcbItem to the tab item list. m_tcbItems.InsertAt(nItem, pMember); if (m_nCurSel < 0) m_nCurSel = 0; EnableButtons(); InvalidateTabs(); SetCurSel(nItem); return nItem; } BOOL CXTExcelTabCtrl::_DeleteItem(int nItem) { const int cItems = (int)m_tcbItems.GetSize(); if (nItem < 0 || nItem >= cItems) return FALSE; // Remove the item from the string arrays. CWnd *pWndControl = m_tcbItems[nItem]->pWnd; if (::IsWindow(pWndControl->GetSafeHwnd())) { pWndControl->ShowWindow(SW_HIDE); pWndControl->SetParent(NULL); } SAFE_DELETE(m_tcbItems[nItem]); m_tcbItems.RemoveAt(nItem); if (m_tcbItems.GetSize() == 0) { m_bManagingViews = false; } return TRUE; } BOOL CXTExcelTabCtrl::DeleteItem(int nItem) { if (!_DeleteItem(nItem)) { return FALSE; } const int cItems = GetItemCount(); ASSERT(cItems >= 0); if (cItems == 0) { m_nCurSel = -1; } else { if (nItem == m_nCurSel) { m_nCurSel = 0; SetCurSel(0); } else if (m_nCurSel > nItem) { --m_nCurSel; } } EnableButtons(); InvalidateTabs(); return TRUE; } void CXTExcelTabCtrl::ClearAllItems() { while (_DeleteItem(0)) { }; } BOOL CXTExcelTabCtrl::DeleteAllItems() { ClearAllItems(); // Reset the currently selected tab to -1 as we have no tabs in our list now. m_nCurSel = -1; EnableButtons(); InvalidateTabs(); return TRUE; } int CXTExcelTabCtrl::SetCurSel(int nItem) { const int cItems = GetItemCount(); if (nItem < 0 || nItem >= cItems) return -1; int iPrevSel = m_nCurSel; m_nCurSel = nItem; // test if we need to center on the selected tab CRect rcItem; if (GetItemRect(nItem, &rcItem)) { // test if the tab is off on the left int iTotalArrowWidth = GetTotalArrowWidth(); rcItem.left -= iTotalArrowWidth; if (rcItem.left <= 0) m_nOffset += rcItem.left; else { // test if the tab is off on the right rcItem.right -= iTotalArrowWidth; int iTabAreaWidth = GetTotalTabAreaWidth(); if (rcItem.right > iTabAreaWidth) m_nOffset += (rcItem.right - iTabAreaWidth); } } // hide/show managed controls ASSERT(iPrevSel >= 0); if (iPrevSel < cItems && m_tcbItems[iPrevSel]->pWnd->GetSafeHwnd()) { m_tcbItems[iPrevSel]->pWnd->ShowWindow(SW_HIDE); } if (m_tcbItems[m_nCurSel]->pWnd->GetSafeHwnd()) { m_tcbItems[m_nCurSel]->pWnd->ShowWindow(SW_SHOW); } // reset scroll bar position SyncScrollBar(); EnableButtons(); InvalidateTabs(); return iPrevSel; } void CXTExcelTabCtrl::OnLButtonDown(UINT nFlags, CPoint point) { TCHITTESTINFO tchtinfo; tchtinfo.pt = point; // sizing grip ? if (m_rectGripper.PtInRect(point)) { ASSERT(m_bTracking == false); SetTracking(true); return; } // on a button ? int iButton = ButtonHitTest(point); if (iButton >= 0) { CXTExcelTabCtrlButtonState& b = m_buttons[iButton]; if (b.m_bEnabled) { b.m_bPressed = true; RedrawWindow(b.m_rect); SetCapture(); } return; } int iTab = HitTest(&tchtinfo); if ((iTab != -1) && (iTab != m_nCurSel)) { // warn parent that the selection is about to change int id = GetDlgCtrlID(); NMHDR hdr; hdr.hwndFrom = m_hWnd; hdr.idFrom = id; hdr.code = TCN_SELCHANGING; if (GetOwner()->SendMessage(WM_NOTIFY, id, (LPARAM)&hdr) == 0) { // parent has given permission for the selection to change SetCurSel(iTab); InvalidateTabs(); // notify parent that the selection has changed hdr.hwndFrom = m_hWnd; hdr.idFrom = id; hdr.code = TCN_SELCHANGE; GetOwner()->SendMessage(WM_NOTIFY, id, (LPARAM)&hdr); } } CWnd::OnLButtonDown(nFlags, point); } void CXTExcelTabCtrl::OnLButtonUp(UINT nFlags, CPoint point) { if (m_bTracking) { SetTracking(false); } // on a button ? int i; for (i = 0; i < BUTTON_COUNT; ++i) { CXTExcelTabCtrlButtonState& b = m_buttons[i]; if (b.m_bEnabled & b.m_bPressed) { PostMessage(WM_COMMAND, b.m_iCommand, NULL); b.m_bPressed = false; RedrawWindow(b.m_rect); ReleaseCapture(); return; } } CWnd::OnLButtonUp(nFlags, point); } void CXTExcelTabCtrl::OnPaint() { CPaintDC dc(this); if (m_dwStyle & FTS_XT_HSCROLL) { dc.ExcludeClipRect(&m_rectSB_H); } CXTPBufferDC memDC(dc, m_rectTabs); OnDraw(&memDC); } LRESULT CXTExcelTabCtrl::OnPrintClient(WPARAM wParam, LPARAM /*lParam*/) { CDC* pDC = CDC::FromHandle((HDC)wParam); if (pDC) { OnDraw(pDC); } return TRUE; } void CXTExcelTabCtrl::OnDraw(CDC* pDC) { CRect rectPaint(m_rectTabs); // this the only part we care about pDC->FillSolidRect(rectPaint, GetXtremeColor(COLOR_3DFACE)); // Exclude the arrow buttons from being repainted. const int iTotalArrowWidth = GetTotalArrowWidth(); CRect rcArrows(rectPaint); rcArrows.top += 1; rcArrows.right = rcArrows.left + iTotalArrowWidth-1; pDC->FillSolidRect(0, (m_dwStyle & FTS_XT_BOTTOM) ? rectPaint.top : rectPaint.bottom - 1, rectPaint.Width(), 1, GetTheme()->m_clr3DShadow); int i; for (i = 0; i < BUTTON_COUNT; ++i) { if (CRect().IntersectRect(rectPaint, m_buttons[i].m_rect)) { GetTheme()->DrawButton(pDC, this, m_buttons[i]); } } if (rectPaint.right >= rcArrows.right) { const int nTotalTabWidth = GetTotalTabWidth(); // Draw the tabs to an offscreen device context, this is done // so that we can "scroll" the entire tab group left or right whenever // any of the arrow buttons are pressed. CDC tabDC; tabDC.CreateCompatibleDC(pDC); CBitmap bitmap; bitmap.CreateCompatibleBitmap(pDC, nTotalTabWidth, m_cy); CBitmap* pOldBitmap = tabDC.SelectObject(&bitmap); // Fill the background color. CRect rcTabs(0, 0, nTotalTabWidth, m_cy); tabDC.FillSolidRect(rcTabs, GetXtremeColor(COLOR_3DFACE)); // Define xy coordinates to draw each tab. const int iOverlap = GetOverlap(); int x = 0; int iSelX = 0; int y = 0; // Draw all of the non-selected tabs first... const int cItems = GetItemCount(); for (i = 0; i < cItems; i++) { if (i != m_nCurSel) { const int cx = GetTheme()->DrawTab(&tabDC, this, CPoint(x, y), false, m_tcbItems[i]); x += cx; } else { iSelX = x; x += GetTabWidth(i); } x -= iOverlap; } // then draw the selected tab. Make sure that m_TabList is not empty. if (m_nCurSel >= 0 && cItems > 0) { GetTheme()->DrawTab(&tabDC, this, CPoint(iSelX, y), true, m_tcbItems[m_nCurSel]); } // blit the bitmap onto the pDC-> pDC->BitBlt(iTotalArrowWidth, rectPaint.top, GetTotalTabAreaWidth(), m_cy, &tabDC, m_nOffset, 0, SRCCOPY); // cleanup. tabDC.SelectObject(pOldBitmap); } if (m_dwStyle & FTS_XT_HSCROLL) { DrawGripper(pDC, m_rectGripper); } } void CXTExcelTabCtrl::DrawGripper(CDC* pDC, CRect rect) const { pDC->Draw3dRect(rect, GetXtremeColor(COLOR_3DHILIGHT), GetXtremeColor(COLOR_3DSHADOW)); rect.InflateRect(-CX_BORDER, -CY_BORDER); // fill the middle pDC->FillSolidRect(rect, GetXtremeColor(COLOR_3DFACE)); } BOOL CXTExcelTabCtrl::PreTranslateMessage(MSG* pMsg) { ASSERT_VALID(this); if (pMsg->message == WM_KEYDOWN) { if (pMsg->wParam == VK_ESCAPE) { if (m_bTracking) { SetTracking(false); } return TRUE; // eat ESC keyboard press... } } if (::IsWindow(m_ToolTip.m_hWnd)) { if (pMsg->message == WM_MOUSEMOVE && pMsg->hwnd == m_hWnd) { CPoint Point(LOWORD(pMsg->lParam), HIWORD(pMsg->lParam)); TCHITTESTINFO htInfo; htInfo.pt = Point; int iTab = HitTest(&htInfo); if (iTab >= 0) { CString strOldTipText; CString strNewTipText = m_tcbItems[iTab]->szToolTipLabel; m_ToolTip.GetText(strOldTipText, this); // If the tip text differs, update the tooltip control. if (strNewTipText != strOldTipText) { m_ToolTip.DelTool(this); m_ToolTip.AddTool(this, strNewTipText); } // Pass on to tooltip. m_ToolTip.RelayEvent(pMsg); } else { m_ToolTip.SendMessage(TTM_POP, 0, 0L); } } else { m_ToolTip.SendMessage(TTM_POP, 0, 0L); } } return CWnd::PreTranslateMessage(pMsg); } void CXTExcelTabCtrl::SetTipText(int nItem, LPCTSTR lpszTabTip) { const int cItems = GetItemCount(); if (nItem < 0 || nItem >= cItems) { ASSERT(0); return; } m_tcbItems[nItem]->szToolTipLabel = lpszTabTip; } CString CXTExcelTabCtrl::GetTipText(int nItem) { const int cItems = GetItemCount(); if (nItem < 0 || nItem >= cItems) { ASSERT(0); return _T(""); } return m_tcbItems[nItem]->szToolTipLabel; } void CXTExcelTabCtrl::Home() { OnHomeArrow(); } void CXTExcelTabCtrl::OnMouseMove(UINT nFlags, CPoint point) { if (m_bTracking) { // resize SetGripperPosition(point.x + m_xTrackingDelta, false); } else { if (ButtonHitTest(point) != m_iBtnHilight) { SetTimer(1, 10, NULL); OnTimer(1); } } CWnd::OnMouseMove(nFlags, point); } BOOL CXTExcelTabCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { if (m_bTracking && GetCapture() != this) { m_bTracking = FALSE; } POINT pt; GetCursorPos(&pt); ScreenToClient(&pt); if (m_bTracking || m_rectGripper.PtInRect(pt)) { ::SetCursor(XTAuxData().hcurHSplitBar); return TRUE; } return CWnd::OnSetCursor(pWnd, nHitTest, message); } void CXTExcelTabCtrl::SetTracking(bool bTracking) { if (m_bTracking == bTracking) { return; } m_bTracking = bTracking; if (bTracking) { SetCapture(); POINT pt; GetCursorPos(&pt); ScreenToClient(&pt); m_xTrackingDelta = GetGripperPosition() - pt.x; m_pWndLastFocus = SetFocus(); } else { ReleaseCapture(); if (::IsWindow(m_pWndLastFocus->GetSafeHwnd())) { m_pWndLastFocus->SetFocus(); } else { m_pWndLastFocus = NULL; } } } void CXTExcelTabCtrl::OnLButtonDblClk(UINT nFlags, CPoint point) { // on a button ? int iButton = ButtonHitTest(point); if (iButton >= 0) { CXTExcelTabCtrlButtonState& b = m_buttons[iButton]; if (b.m_bEnabled) { SetCapture(); b.m_bPressed = true; RedrawWindow(b.m_rect); } return; } CWnd::OnLButtonDblClk(nFlags, point); } void CXTExcelTabCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* /*pScrollBar*/) { CWnd *pWndControl = m_tcbItems[m_nCurSel]->pWnd; if (pWndControl->GetSafeHwnd()) { if (nSBCode == SB_THUMBPOSITION || nSBCode == SB_THUMBTRACK) { int dx = nPos - pWndControl->GetScrollPos(SB_HORZ); if (dx) { pWndControl->SendMessage(LVM_SCROLL, dx, 0); } } // pass message to control's scroll bar _AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); pWndControl->SendMessage( pThreadState->m_lastSentMsg.message, pThreadState->m_lastSentMsg.wParam, (LPARAM)pWndControl->GetScrollBarCtrl(SB_HORZ)->GetSafeHwnd()); // reflect changes in our scroll bar SyncScrollBar(); } } void CXTExcelTabCtrl::SyncScrollBar() { if (!m_wndHScrollBar.m_hWnd) { return; } CWnd *pWndControl = NULL; if (m_nCurSel >= 0) { pWndControl = m_tcbItems[m_nCurSel]->pWnd; } if (pWndControl && pWndControl->GetSafeHwnd()) { pWndControl->ShowScrollBar(SB_HORZ, FALSE); pWndControl->ModifyStyle(WS_HSCROLL, 0, SWP_DRAWFRAME); SCROLLINFO si; ZeroMemory(&si, sizeof(si)); si.cbSize = sizeof(si); si.fMask = SIF_ALL; if (!pWndControl->GetScrollInfo (SB_HORZ, &si) || si.nPage == 0 || si.nMin + (int) si.nPage >= si.nMax) { m_wndHScrollBar.EnableWindow(FALSE); } else { m_wndHScrollBar.EnableWindow(TRUE); m_wndHScrollBar.SetScrollInfo(&si); } } else { m_wndHScrollBar.EnableWindow(FALSE); } } BOOL CXTExcelTabCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) { NMHDR* pNMHDR = (NMHDR*)lParam; ASSERT (pNMHDR != NULL); BOOL bRetVal = CWnd::OnNotify(wParam, lParam, pResult); if (pNMHDR && pNMHDR->code == HDN_ITEMCHANGED) { SyncScrollBar(); } return bRetVal; } void CXTExcelTabCtrl::RecalcLayout() { m_rectViews.left = m_rectTabs.left = 0; m_rectViews.right = m_rectTabs.right = m_nClientWidth; if (m_dwStyle & FTS_XT_BOTTOM) { m_rectTabs.top = m_nClientHeight - m_cy; m_rectViews.top = 0; } else { m_rectTabs.top = 0; m_rectViews.top = m_cy; } m_rectTabs.bottom = m_rectTabs.top + m_cy; m_rectViews.bottom = m_rectViews.top + m_nClientHeight - m_cy; int nIndex; // update managed view positions if (m_bManagingViews) { const int cItems = GetItemCount(); for (nIndex = 0; nIndex < cItems; ++nIndex) { ASSERT(m_tcbItems[nIndex]->pWnd->GetSafeHwnd()); m_tcbItems[nIndex]->pWnd->SetWindowPos(NULL, m_rectViews.left, m_rectViews.top, m_rectViews.Width(), m_rectViews.Height(), SWP_NOZORDER | SWP_NOREDRAW); } } // update arrow buttons if we have them if (m_dwStyle & FTS_XT_HASARROWS) { CRect btnRects[] = { CRect(0, m_rectTabs.top + 1, m_cx, m_rectTabs.bottom), CRect(m_cx, m_rectTabs.top + 1, m_cx * 2, m_rectTabs.bottom), CRect(m_cx * 2, m_rectTabs.top + 1, m_cx * 3, m_rectTabs.bottom), CRect(m_cx * 3, m_rectTabs.top + 1, m_cx * 4, m_rectTabs.bottom) }; const int cButtons = (m_dwStyle & FTS_XT_HASHOMEEND) ? 4 : 2; for (nIndex = 0; nIndex < cButtons; ++nIndex) { m_buttons[nIndex].m_rect = btnRects[nIndex]; } } EnableButtons(); } LPCTSTR CXTExcelTabCtrl::GetItemText(int nIndex) const { if (nIndex < 0 || nIndex >= GetItemCount()) { return NULL; } return m_tcbItems[nIndex]->szTabLabel; } bool CXTExcelTabCtrl::SetItemText(int nIndex, LPCTSTR pszText) { if (nIndex < 0 || nIndex >= GetItemCount()) { return false; } m_tcbItems[nIndex]->szTabLabel = pszText; RecalcLayout(); InvalidateTabs(); return true; } CWnd *CXTExcelTabCtrl::GetItemWindow(int nIndex) const { if (nIndex < 0 || nIndex >= GetItemCount()) { return NULL; } return m_tcbItems[nIndex]->pWnd; } int CXTExcelTabCtrl::GetOverlap() const { return ((m_cy / 2) + 2); } void CXTExcelTabCtrl::UpdateDefaultColors() { GetTheme()->RefreshMetrics(); } void CXTExcelTabCtrl::SetTabShadowColor(COLORREF crShadow) { GetTheme()->m_clr3DShadow.SetCustomValue(crShadow); } COLORREF CXTExcelTabCtrl::GetTabShadowColor() const { return GetTheme()->m_clr3DShadow; } void CXTExcelTabCtrl::SetTabHilightColor(COLORREF crHilight) { GetTheme()->m_clr3DHilight.SetCustomValue(crHilight); } COLORREF CXTExcelTabCtrl::GetTabHilightColor() const { return GetTheme()->m_clr3DHilight; } void CXTExcelTabCtrl::SetTabBackColor(COLORREF crBack) { GetTheme()->m_clr3DFace.SetCustomValue(crBack); } COLORREF CXTExcelTabCtrl::GetTabBackColor() const { return GetTheme()->m_clr3DFace; } void CXTExcelTabCtrl::SetTabTextColor(COLORREF crText) { GetTheme()->m_clrBtnText.SetCustomValue(crText); } COLORREF CXTExcelTabCtrl::GetTabTextColor() const { return GetTheme()->m_clrBtnText; } void CXTExcelTabCtrl::SetSelTabBackColor(COLORREF crBack) { GetTheme()->m_clrWindow.SetCustomValue(crBack); } COLORREF CXTExcelTabCtrl::GetSelTabBackColor() const { return GetTheme()->m_clrWindow; } void CXTExcelTabCtrl::SetSelTabTextColor(COLORREF crText) { GetTheme()->m_clrWindowText.SetCustomValue(crText); } COLORREF CXTExcelTabCtrl::GetSelTabTextColor() const { return GetTheme()->m_clrWindowText; } CScrollBar* CXTExcelTabCtrl::GetScrollBarCtrl(int nBar) const { if (nBar == SB_HORZ && ::IsWindow(m_wndHScrollBar.m_hWnd)) { return (CScrollBar*)&m_wndHScrollBar; } return CWnd::GetScrollBarCtrl(nBar); } CRect CXTExcelTabCtrl::GetTotalButtonRect() const { CRect rect; rect.CopyRect(&m_buttons[0].m_rect); int iWidth = 0; for (int i = 0; i < BUTTON_COUNT; ++i) { iWidth += m_buttons[i].m_rect.Width(); } rect.right = rect.left + iWidth; return rect; } void CXTExcelTabCtrl::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1) { CRect rc; GetWindowRect(rc); CPoint point; GetCursorPos(&point); ScreenToClient(&point); int iBtnHilight = ButtonHitTest(point); if (iBtnHilight == -1) { KillTimer(1); if (m_bPainted == TRUE) { CRect rect = GetTotalButtonRect(); InvalidateRect(&rect); } m_iBtnHilight = iBtnHilight; m_bPainted = FALSE; } // On mouse over, show raised button. else if (!m_bPainted || (iBtnHilight != m_iBtnHilight)) { CRect rect = GetTotalButtonRect(); InvalidateRect(&rect); m_iBtnHilight = iBtnHilight; m_bPainted = TRUE; } } else { CWnd::OnTimer(nIDEvent); } } void CXTExcelTabCtrl::SetTabBackColor(int nIndex, COLORREF crBack) { if (nIndex >= 0 || nIndex < GetItemCount()) { m_tcbItems[nIndex]->crTabBack = crBack; CRect rcItem; GetItemRect(nIndex, &rcItem); InvalidateRect(rcItem); } } void CXTExcelTabCtrl::SetTabTextColor(int nIndex, COLORREF crText) { if (nIndex >= 0 || nIndex < GetItemCount()) { m_tcbItems[nIndex]->crTabText = crText; CRect rcItem; GetItemRect(nIndex, &rcItem); InvalidateRect(rcItem); } } void CXTExcelTabCtrl::SetSelTabBackColor(int nIndex, COLORREF crSelBack) { if (nIndex >= 0 || nIndex < GetItemCount()) { m_tcbItems[nIndex]->crTabSelBack = crSelBack; CRect rcItem; GetItemRect(nIndex, &rcItem); InvalidateRect(rcItem); } } void CXTExcelTabCtrl::SetSelTabTextColor(int nIndex, COLORREF crSelText) { if (nIndex >= 0 || nIndex < GetItemCount()) { m_tcbItems[nIndex]->crTabSelText = crSelText; CRect rcItem; GetItemRect(nIndex, &rcItem); InvalidateRect(rcItem); } } COLORREF CXTExcelTabCtrl::GetTabBackColor(int nIndex) const { return GetTheme()->GetTabBackColor(m_tcbItems[nIndex]); } COLORREF CXTExcelTabCtrl::GetTabTextColor(int nIndex) const { return GetTheme()->GetTabTextColor(m_tcbItems[nIndex]); } COLORREF CXTExcelTabCtrl::GetSelTabBackColor(int nIndex) const { return GetTheme()->GetSelTabBackColor(m_tcbItems[nIndex]); } COLORREF CXTExcelTabCtrl::GetSelTabTextColor(int nIndex) const { return GetTheme()->GetSelTabTextColor(m_tcbItems[nIndex]); } //{{AFX_CODEJOCK_PRIVATE IMPLEMENT_DYNAMIC(CXTFlatTabCtrl, CXTExcelTabCtrl) // deprecated. //}}AFX_CODEJOCK_PRIVATE