You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1818 lines
38 KiB
C++

// XTPTabBase.cpp : implementation file
//
// This file is a part of the XTREME CONTROLS MFC class library.
// (c)1998-2012 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 "Common/XTPVC80Helpers.h" // Visual Studio 2005 helper functions
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPColorManager.h"
#include "../Util/XTPControlTheme.h"
#include "Common/XTPDrawHelpers.h"
#include "../Util/XTPGlobal.h"
#include "XTPTabCtrlButtons.h"
#include "XTPTabBase.h"
#include "XTPTabBaseTheme.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
const int TABVIEW_BORDER = 4;
/////////////////////////////////////////////////////////////////////////////
// CXTPTabBase
CXTPTabBase::CXTPTabBase()
: m_pTabCtrl(NULL)
, m_pTheme(NULL)
, m_bSubclassed(TRUE)
, m_bAutoCondensing(FALSE)
, m_bXPBorder(false)
, m_bBoldFont(true)
{
VERIFY(SetTheme(xtpControlThemeDefault));
}
CXTPTabBase::~CXTPTabBase()
{
SAFE_DELETE(m_pNavBtns);
CMDTARGET_RELEASE(m_pTheme);
}
void CXTPTabBase::OnPaint()
{
CPaintDC dc(m_pTabCtrl); // device context for painting
CXTPClientRect rcClient(m_pTabCtrl);
CXTPBufferDC memDC(dc, rcClient);
m_pTheme->DrawTabCtrl(&memDC, this);
}
BOOL CXTPTabBase::OnEraseBkgnd(CDC* /*pDC*/)
{
return TRUE;
}
void CXTPTabBase::RefreshMetrics()
{
if (m_pTheme)
m_pTheme->RefreshMetrics(m_pTabCtrl);
if (::IsWindow(m_pTabCtrl->GetSafeHwnd()))
{
m_pTabCtrl->RedrawWindow();
}
}
BOOL CXTPTabBase::SetTheme(XTPControlTheme eTheme)
{
SAFE_DELETE(m_pTheme);
switch (eTheme)
{
case xtpControlThemeOfficeXP:
m_pTheme = new CXTPTabBaseThemeOfficeXP;
break;
case xtpControlThemeOffice2003:
m_pTheme = new CXTPTabBaseThemeOffice2003;
break;
default:
m_pTheme = new CXTPTabBaseTheme;
break;
}
RefreshMetrics();
return (m_pTheme != NULL);
}
LRESULT CXTPTabBase::OnSetTheme(WPARAM wParam, LPARAM /*lParam*/)
{
XTPControlTheme eTheme = (XTPControlTheme)wParam;
SetTheme(eTheme);
return 0;
}
void CXTPTabBase::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
WPARAM wParam = (WPARAM)uFlags;
LPARAM lParam = (LPARAM)(LPCTSTR)lpszSection;
RefreshMetrics();
m_pTabCtrl->SendMessageToDescendants(WM_SETTINGCHANGE, wParam, lParam, TRUE, FALSE);
}
void CXTPTabBase::OnSysColorChange()
{
RefreshMetrics();
m_pTabCtrl->SendMessageToDescendants(WM_SYSCOLORCHANGE, 0, 0L, TRUE, FALSE);
}
BOOL CXTPTabBase::Init()
{
if (::IsWindow(m_pTabCtrl->GetSafeHwnd()))
{
m_pTabCtrl->SetFont(&XTPAuxData().font);
m_pTabCtrl->Invalidate(FALSE);
}
return TRUE;
}
void CXTPTabBase::PreSubclassWindow()
{
if (m_bSubclassed)
{
// Initialize the control.
Init();
}
}
BOOL CXTPTabBase::PreCreateWindow(CREATESTRUCT& /*cs*/)
{
// When creating controls dynamically Init() must
// be called from OnCreate() and not from
// PreSubclassWindow().
m_bSubclassed = FALSE;
return TRUE;
}
int CXTPTabBase::OnCreate(LPCREATESTRUCT /*lpCreateStruct*/)
{
// Initialize the control.
Init();
return 0;
}
void CXTPTabBase::ImplAttach(CTabCtrl* pTabCtrl)
{
ASSERT_VALID(pTabCtrl); // must be valid
if (pTabCtrl != NULL)
{
m_pTabCtrl = pTabCtrl;
m_pNavBtns = new CXTPTabCtrlButtons;
m_pNavBtns->Create(this);
}
}
void CXTPTabBase::ShowNavButtons(DWORD dwFlags)
{
if (!m_pNavBtns)
return;
m_pNavBtns->m_dwFlags = dwFlags;
if (::IsWindow(m_pNavBtns->GetSafeHwnd()))
{
if (m_pNavBtns->ShowButtons())
{
m_pNavBtns->Refresh();
}
}
}
void CXTPTabBase::OnAddPadding(CString& strLabelText)
{
if (!strLabelText.IsEmpty())
{
CClientDC dc(NULL);
int iSaveDC = dc.SaveDC();
CFont* pfontNormal = NULL;
CFont* pfontSelect = NULL;
if (m_pTabCtrl->GetStyle() & TCS_VERTICAL)
{
pfontNormal = &XTPAuxData().fontVert;
pfontSelect = &XTPAuxData().fontVertBold;
}
else
{
pfontNormal = &XTPAuxData().font;
pfontSelect = &XTPAuxData().fontBold;
}
dc.SelectObject(pfontSelect);
CSize sizeSelect = dc.GetTextExtent(strLabelText);
dc.SelectObject(pfontNormal);
CSize sizeNormal = dc.GetTextExtent(strLabelText);
bool bFront = true;
while (sizeNormal.cx < sizeSelect.cx)
{
if (bFront)
{
strLabelText.Insert(0, _T(' '));
bFront = false;
}
else
{
strLabelText += _T(' ');
bFront = true;
}
sizeNormal = dc.GetTextExtent(strLabelText);
}
dc.RestoreDC(iSaveDC);
}
}
void CXTPTabBase::GetChildRect(CRect& rcChild) const
{
ASSERT_VALID(m_pTabCtrl);
// Get the selected tab index.
int nCurSel = m_pTabCtrl->GetCurSel();
if (nCurSel == -1)
{
rcChild.SetRectEmpty();
return;
}
DWORD dwStyle = m_pTabCtrl->GetStyle();
m_pTabCtrl->GetClientRect(&rcChild);
CRect rcTab;
m_pTabCtrl->GetItemRect(nCurSel, &rcTab);
rcTab.InflateRect(1, 1);
int cy = rcTab.Height() * m_pTabCtrl->GetRowCount();
int cx = rcTab.Width () * m_pTabCtrl->GetRowCount();
// vertical tabs
if (dwStyle & TCS_VERTICAL)
{
// Right
if (dwStyle & TCS_RIGHT)
{
rcChild.top += TABVIEW_BORDER;
rcChild.left += TABVIEW_BORDER;
rcChild.right -= TABVIEW_BORDER + cx;
rcChild.bottom -= TABVIEW_BORDER;
}
// Left
else
{
rcChild.top += TABVIEW_BORDER;
rcChild.left += TABVIEW_BORDER + cx;
rcChild.right -= TABVIEW_BORDER;
rcChild.bottom -= TABVIEW_BORDER;
}
}
// horizontal tabs
else
{
// Bottom
if (dwStyle & TCS_BOTTOM)
{
rcChild.top += TABVIEW_BORDER;
rcChild.left += TABVIEW_BORDER;
rcChild.right -= TABVIEW_BORDER;
rcChild.bottom -= TABVIEW_BORDER + cy;
}
// Top
else
{
rcChild.top += TABVIEW_BORDER + cy;
rcChild.left += TABVIEW_BORDER;
rcChild.right -= TABVIEW_BORDER;
rcChild.bottom -= TABVIEW_BORDER;
}
}
}
/////////////////////////////////////////////////////////////////////////////
// CXTPTabExBase
CXTPTabExBase::CXTPTabExBase()
: m_bInitialUpdate(FALSE)
, m_pParentWnd(NULL)
, m_pParentFrame(NULL)
, m_popupMenuID(0)
, m_nPos(0)
, m_pLastActiveView(NULL)
, m_nOldIndex(-1)
{
m_bBoldFont = false;
// force VC++ to generate the following template functions:
if (GetAutoCondense()) // should always be false!
{
// this block will never be executed, but the compiler can't know that
SetAutoCondense(FALSE);
CXTPTabBase::OnPaint();
CreateTabView(NULL, NULL, NULL);
}
}
CXTPTabExBase::~CXTPTabExBase()
{
}
void CXTPTabExBase::OnThemeChanged()
{
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
CXTPTcbItem* pMember = m_tcbItems.GetAt(pos);
GetTheme()->AdjustBorders(this, pMember);
}
}
BOOL CXTPTabExBase::Init()
{
if (!CXTPTabBase::Init())
return FALSE;
if (m_bInitialUpdate)
{
// fire off initial updates
// NB: this works only if WM_INITIALUPDATE has not been sent to us
// since its handler invalidates the signature
m_pTabCtrl->SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);
}
// Set the font for the tab control.
InitializeFont();
// Get the pointer to the parent window.
m_pParentWnd = m_pTabCtrl->GetParent();
ASSERT_VALID(m_pParentWnd);
// Get the pointer to the parent frame window.
m_pParentFrame = m_pTabCtrl->GetParentFrame();
if (::IsWindow(m_pParentFrame->GetSafeHwnd()))
{
// If floating, get parent frame of the floating frame window.
if (m_pParentFrame->IsKindOf(RUNTIME_CLASS(CMiniDockFrameWnd)))
{
m_pParentFrame = m_pParentFrame->GetParentFrame();
}
}
// Resize all views.
RecalcLayout();
if (m_bAutoCondensing)
{
Condense();
}
// make sure children are clipped.
m_pTabCtrl->ModifyStyle(NULL, WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
return TRUE;
}
BOOL CXTPTabExBase::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
if (nCode < 1)
{
CWnd* pView = GetActiveView();
if (pView && ::IsWindow(pView->m_hWnd))
{
if (pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
}
}
return FALSE;
}
BOOL CXTPTabExBase::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
bool bShift = (::GetKeyState(VK_SHIFT) < 0);
bool bCtrl = (::GetKeyState(VK_CONTROL) < 0);
if (bCtrl && (pMsg->wParam == VK_TAB) || (pMsg->wParam == VK_F6))
{
if (bShift)
{
PrevView();
m_pTabCtrl->SetFocus();
}
else
{
NextView();
m_pTabCtrl->SetFocus();
}
return TRUE;
}
}
return FALSE; // Allow messages to be passed to base class
}
void CXTPTabExBase::OnRButtonDown(UINT /*nFlags*/, CPoint point)
{
if (m_popupMenuID)
{
CPoint pt = point;
m_pTabCtrl->ClientToScreen(&pt);
CMenu menu;
VERIFY(menu.LoadMenu(m_popupMenuID));
CMenu* pPopup = menu.GetSubMenu(m_nPos);
ASSERT(pPopup != NULL);
if (!pPopup)
return;
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y,
m_pParentFrame);
}
}
void CXTPTabExBase::OnDestroy()
{
// Cleanup
RemoveAllTabs();
}
void CXTPTabExBase::EnableRedraw(BOOL bRedraw)
{
m_pTabCtrl->SetRedraw(bRedraw);
if (bRedraw)
{
m_pTabCtrl->Invalidate();
m_pTabCtrl->UpdateWindow();
}
}
BOOL CXTPTabExBase::OnSelchange(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
CWnd* pOwner = m_pTabCtrl->GetOwner();
if (::IsWindow(pOwner->GetSafeHwnd()))
{
*pResult = pOwner->SendMessage(TCN_SELCHANGE,
pNMHDR->idFrom, (LPARAM)m_pTabCtrl);
}
if (*pResult == 0)
{
OnSelChange();
}
return FALSE;
}
void CXTPTabExBase::OnSelChange()
{
// Get the index to the newly selected tab.
int nIndex = m_pTabCtrl->GetCurSel();
if (nIndex == -1)
return;
// Get a pointer to the selected view.
if (m_nOldIndex != -1)
{
CWnd* pOldView = GetView(m_nOldIndex);
if (pOldView != NULL)
{
pOldView->ShowWindow(SW_HIDE);
pOldView->EnableWindow(FALSE);
if (m_pParentFrame && pOldView == m_pParentFrame->GetActiveView())
{
if (m_pLastActiveView && ::IsWindow(m_pLastActiveView->m_hWnd))
{
m_pParentFrame->SetActiveView(m_pLastActiveView);
}
else
{
m_pParentFrame->SetActiveView(NULL, FALSE);
m_pLastActiveView = NULL;
}
}
}
}
// Get a pointer to the selected view.
CWnd* pView = GetView(nIndex);
if (pView != NULL)
{
// If this is a CView object then set the active view
// the parent frame.
if (pView->IsKindOf(RUNTIME_CLASS(CView)))
{
if (m_pParentFrame && m_pParentFrame->IsChild(pView))
{
// Save a pointer to the view if it is not a child of the
// tab control, we will use this rather than setting the view
// to NULL for the frame window.
CView* pActiveView = m_pParentFrame->GetActiveView();
if (pActiveView && pActiveView->GetParent() != m_pTabCtrl)
{
m_pLastActiveView = pActiveView;
}
m_pParentFrame->SetActiveView((CView*)pView);
}
}
ActivateView(pView);
}
}
BOOL CXTPTabExBase::OnSelchanging(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
CWnd* pOwner = m_pTabCtrl->GetOwner();
if (::IsWindow(pOwner->GetSafeHwnd()))
{
*pResult = pOwner->SendMessage(TCN_SELCHANGING,
pNMHDR->idFrom, (LPARAM)m_pTabCtrl);
}
if (*pResult == 0)
{
OnSelChanging();
}
return FALSE;
}
void CXTPTabExBase::OnSelChanging()
{
// Get the index to the selected tab, we will
// need this later to hide old view.
m_nOldIndex = m_pTabCtrl->GetCurSel();
}
void CXTPTabExBase::OnPreWindowPosChanged(WINDOWPOS FAR* lpwndpos)
{
if (lpwndpos->flags & SWP_HIDEWINDOW)
{
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
CWnd* pView = m_tcbItems.GetAt(pos)->pWnd;
ASSERT_VALID(pView);
if (m_pParentFrame && m_pParentFrame->GetActiveView() == pView)
{
// To avoid main window freezing, we must deactivate the view,
// because it's not visible now.
if (m_pLastActiveView && ::IsWindow(m_pLastActiveView->m_hWnd))
{
m_pParentFrame->SetActiveView(m_pLastActiveView);
}
else
{
m_pParentFrame->SetActiveView(NULL, FALSE);
m_pLastActiveView = NULL;
}
}
}
}
else
{
// resize all child views.
RecalcLayout();
}
}
void CXTPTabExBase::OnPostWindowPosChanged()
{
if (m_bAutoCondensing)
{
Condense();
}
}
BOOL CXTPTabExBase::ModifyTabStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags)
{
if (!m_pTabCtrl->ModifyStyle(dwRemove, dwAdd, nFlags))
return FALSE;
// Set the font for the tab control.
InitializeFont();
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CXTPTabExBase tooltip related functions
#if (_MSC_VER <= 1100) // Using Visual C++ 5.0
CToolTipCtrl* CXTPTabExBase::GetTips()
{
return m_pTabCtrl->GetTooltips();
}
void CXTPTabExBase::SetTips(CToolTipCtrl* pWndTip)
{
m_pTabCtrl->SetTooltips(pWndTip);
}
#else
CToolTipCtrl* CXTPTabExBase::GetTips()
{
return m_pTabCtrl->GetToolTips();
}
void CXTPTabExBase::SetTips(CToolTipCtrl* pWndTip)
{
m_pTabCtrl->SetToolTips(pWndTip);
}
#endif//#if (_MSC_VER <= 1100)
void CXTPTabExBase::AddToolTip(UINT nIDTab, LPCTSTR lpszText)
{
// Get the tool tips for the tab control.
CToolTipCtrl* pToolTips = GetTips();
if (pToolTips != NULL)
{
// Add the tool tip.
if (nIDTab == 0)
{
pToolTips->AddTool(m_pTabCtrl, lpszText, NULL, nIDTab);
}
else
{
pToolTips->AddTool(m_pTabCtrl, lpszText, CRect(0, 0, 0, 0), nIDTab);
}
}
}
void CXTPTabExBase::UpdateToolTip(int nIDTab, LPCTSTR lpszText)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT((nIDTab >= 0) && (m_pTabCtrl->GetItemCount() > nIDTab));
// Find the view we want to change the tooltip for and
// reset its tooltip text
POSITION pos = m_tcbItems.FindIndex(nIDTab);
if (pos != NULL)
{
// Get the private data for the view
CXTPTcbItem *pMember = m_tcbItems.GetAt(pos);
ASSERT(pMember);
if (!pMember)
return;
// Reset the tooltip text to the new value
pMember->szToolTipLabel = lpszText;
// Get the tool tips for the tab control.
CToolTipCtrl* pToolTips = GetTips();
if (pToolTips != NULL)
{
// Update the tooltip for the view
pToolTips->UpdateTipText(pMember->szToolTipLabel,
m_pTabCtrl, pMember->uiToolTipId);
}
}
}
void CXTPTabExBase::UpdateToolTip(CRuntimeClass* pViewClass, LPCTSTR lpszText)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(pViewClass != NULL);
if (!pViewClass)
return;
ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
int nIndex = 0;
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
// Get the private data for the view
CXTPTcbItem *pMember = m_tcbItems.GetAt(pos);
ASSERT(pMember);
if (!pMember)
continue;
if (pMember->pWnd->IsKindOf(pViewClass))
{
UpdateToolTip(nIndex, lpszText);
return;
}
nIndex++;
}
}
void CXTPTabExBase::ResetToolTips()
{
// Get the tool tips for the tab control.
CToolTipCtrl* pToolTips = GetTips();
if (pToolTips != NULL)
{
int nIndex = 0;
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
// Get the next item in the list.
CXTPTcbItem* pMember = m_tcbItems.GetAt(pos);
// Update the tooltip for the view
pMember->uiToolTipId = nIndex;
pToolTips->UpdateTipText(pMember->szToolTipLabel,
m_pTabCtrl, nIndex);
++nIndex;
}
}
}
BOOL CXTPTabExBase::EnableToolTipsEx(BOOL bEnable)
{
// Get the tool tips for the tab control.
CToolTipCtrl* pToolTips = GetTips();
if (pToolTips != NULL)
{
pToolTips->Activate(bEnable);
return TRUE;
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// CXTPTabExBase tab view methods.
CWnd* CXTPTabExBase::CreateTabView(CRuntimeClass* pViewClass, CDocument* pDocument, CCreateContext* pContext)
{
if (!pContext && !pViewClass)
return NULL;
ASSERT_VALID(m_pTabCtrl);
ASSERT(pContext || pViewClass != NULL);
ASSERT(pContext || pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
ASSERT(pContext || AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
CCreateContext contextT;
if (pContext == NULL)
{
// if no context specified, generate one from the currently selected
// client if possible.
contextT.m_pCurrentDoc = pDocument;
contextT.m_pNewViewClass = pViewClass;
contextT.m_pLastView = NULL;
contextT.m_pCurrentFrame = NULL;
contextT.m_pNewDocTemplate = NULL;
if (pDocument)
{
contextT.m_pNewDocTemplate = pDocument->GetDocTemplate();
}
pContext = &contextT;
}
if (!pContext->m_pNewViewClass)
return NULL;
CWnd* pWnd = NULL;
TRY
{
pWnd = (CWnd*)pContext->m_pNewViewClass->CreateObject();
if (pWnd == NULL)
{
AfxThrowMemoryException();
}
}
CATCH_ALL(e)
{
TRACE0("Out of memory creating a view.\n");
// Note: DELETE_EXCEPTION(e) not required
return NULL;
}
END_CATCH_ALL
ASSERT_KINDOF(CWnd, pWnd);
if (!pWnd)
return NULL;
ASSERT(pWnd->m_hWnd == NULL); // not yet created.
DWORD dwStyle = AFX_WS_DEFAULT_VIEW;
if (m_pTheme->GetThemeStyle() != xtpControlThemeOffice2000)
{
dwStyle &= ~WS_BORDER;
}
int nTab = (int)m_tcbItems.GetCount();
// Create with the right size (wrong position)
CRect rect(0, 0, 0, 0);
if (!pWnd->Create(NULL, NULL, dwStyle,
rect, m_pTabCtrl, (AFX_IDW_PANE_FIRST + nTab), pContext))
{
TRACE0("Warning: couldn't create client tab for view.\n");
// pWnd will be cleaned up by PostNcDestroy
return NULL;
}
if (pWnd->m_hWnd == NULL)
return NULL;
ASSERT((int)_AfxGetDlgCtrlID(pWnd->m_hWnd) == (AFX_IDW_PANE_FIRST + nTab));
// send initial notification message for the window just created
// only if our initialization has been completed
if (m_bInitialUpdate)
{
pWnd->SendMessage(WM_INITIALUPDATE);
}
pWnd->SetOwner(m_pParentWnd);
return pWnd;
}
BOOL CXTPTabExBase::AddView(LPCTSTR lpszLabel, CRuntimeClass* pViewClass, CDocument* pDoc/*= NULL*/,
CCreateContext *pContext/*= NULL*/, int iIndex/*= -1*/, int iIconIndex/*= -1*/, LPARAM lParam)
{
if (!pContext && !pViewClass)
return NULL;
ASSERT_VALID(m_pTabCtrl);
ASSERT(pContext || pViewClass);
ASSERT(pContext || pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
ASSERT(pContext || AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
// Create the view to be associate with tab.
CWnd* pWnd = CreateTabView(pViewClass, pDoc, pContext);
if (pWnd == NULL) // tabs can be a CWnd, but are usually CViews
return FALSE;
return AddControl(lpszLabel, (CView *)pWnd, iIndex, iIconIndex, lParam);
}
BOOL CXTPTabExBase::AddView(LPCTSTR lpszLabel, CView* pView, int iIndex/*= -1*/, int iIconIndex/*= -1*/, LPARAM lParam)
{
ASSERT(pView != NULL);
if (!pView)
return FALSE;
ASSERT(pView->GetRuntimeClass()->IsDerivedFrom(RUNTIME_CLASS(CView)));
return AddControl(lpszLabel, pView, iIndex, iIconIndex, lParam);
}
BOOL CXTPTabExBase::AddControl(LPCTSTR lpszLabel, CWnd* pWnd, int iIndex/*= -1*/, int iIconIndex/*= -1*/, LPARAM lParam)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT_VALID(pWnd);
ASSERT(pWnd != NULL);
ASSERT(lpszLabel != NULL);
ASSERT(iIndex >= -1);
ASSERT(iIndex <= m_pTabCtrl->GetItemCount());
if (!pWnd)
return FALSE;
pWnd->SetParent(m_pTabCtrl);
// Allocate a new CXTPTcbItem object.
CXTPTcbItem* pMember = new CXTPTcbItem;
if (pMember == NULL)
return FALSE;
pMember->pWnd = pWnd;
pMember->szTabLabel = lpszLabel;
pMember->szToolTipLabel = lpszLabel;
pMember->uiToolTipId = m_pTabCtrl->GetItemCount();
pMember->dwStyle = ::GetWindowLong(pWnd->GetSafeHwnd(), GWL_STYLE);
pMember->dwExStyle = ::GetWindowLong(pWnd->GetSafeHwnd(), GWL_EXSTYLE);
// If no index was specified add the view tab
// to the end.
if (iIndex == -1)
{
iIndex = (int)m_tcbItems.GetCount();
}
// Add the new CXTPTcbItem to the tab item list.
const POSITION pos = m_tcbItems.FindIndex(iIndex);
if (pos != NULL)
{
m_tcbItems.InsertBefore(pos, pMember);
}
else
{
ASSERT(iIndex == m_pTabCtrl->GetItemCount());
m_tcbItems.AddTail(pMember);
}
// Insert the new tab item.
TC_ITEM tci;
tci.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
tci.pszText = (LPTSTR)lpszLabel;
if (iIconIndex != -1)
{
tci.iImage = iIconIndex;
}
else
{
tci.iImage = iIndex;
}
tci.lParam = lParam;
// set window borders based on current theme.
GetTheme()->AdjustBorders(this, pMember);
m_pTabCtrl->InsertItem(iIndex, &tci);
// Create the ToolTip for the tab.
AddToolTip(pMember->uiToolTipId, lpszLabel);
// Since the tool tip count has changed,
// re-initialize the tooltips.
ResetToolTips();
// hide/show views after insertion
SetActiveView(iIndex);
return TRUE;
}
BOOL CXTPTabExBase::UpdateTabLabel(int iTab, CXTPTcbItem* pMember, LPCTSTR lpszLabel)
{
// Text length cannot be longer than 255 characters.
ASSERT(_tcslen(lpszLabel) < 255);
CString strLabel = lpszLabel;
if (strLabel.GetLength() > 255)
{
strLabel = strLabel.Left(255);
}
// if the string is empty return FALSE.
else if (strLabel.IsEmpty())
{
return FALSE;
}
// if CXTPTcbItem pointer is NULL, return FALSE.
if (pMember == NULL)
return FALSE;
TCHAR szText[256];
TC_ITEM tci;
tci.mask = TCIF_TEXT;
tci.cchTextMax = _countof(szText);
tci.pszText = szText;
// get the tab control item and update the text.
m_pTabCtrl->GetItem(iTab, &tci);
STRCPY_S(szText, 256, strLabel);
m_pTabCtrl->SetItem(iTab, &tci);
// Get the tool tips for the tab control and update
// the tip text.
CToolTipCtrl* pToolTips = GetTips();
if (pToolTips == NULL)
return FALSE;
pToolTips->UpdateTipText(strLabel, m_pTabCtrl, iTab);
// update the CXTPTcbItem item label and tip text.
pMember->szTabLabel = strLabel;
pMember->szToolTipLabel = strLabel;
// update other information
pMember->szCondensedLabel = strLabel;
return TRUE;
}
BOOL CXTPTabExBase::SetTabText(int iTab, LPCTSTR lpszLabel)
{
// Get the item at the specified index.
POSITION pos = m_tcbItems.FindIndex (iTab);
if (pos == NULL)
return FALSE;
return UpdateTabLabel (iTab, m_tcbItems.GetAt(pos), lpszLabel);
}
BOOL CXTPTabExBase::SetTabText(CWnd* pView, LPCTSTR lpszLabel)
{
int iTab = 0;
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
// Get the next item in the list.
CXTPTcbItem* pMember = m_tcbItems.GetAt(pos);
if (pView == pMember->pWnd)
return UpdateTabLabel (iTab, m_tcbItems.GetAt(pos), lpszLabel);
++iTab;
}
return FALSE;
}
BOOL CXTPTabExBase::SetTabText(CRuntimeClass* pViewClass, LPCTSTR lpszLabel)
{
int iTab = 0;
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
// Get the next item in the list.
CXTPTcbItem* pMember = m_tcbItems.GetAt(pos);
if (pMember->pWnd->IsKindOf(pViewClass))
return UpdateTabLabel (iTab, m_tcbItems.GetAt(pos), lpszLabel);
++iTab;
}
return FALSE;
}
CWnd* CXTPTabExBase::NextView()
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(m_tcbItems.GetCount() > 0);
// get the current selection and increment by 1.
int iIndex = m_pTabCtrl->GetCurSel();
iIndex++;
// if the new index is greater than or equal to
// the tab count, set the index to 0.
if (iIndex > m_pTabCtrl->GetItemCount()-1)
iIndex = 0;
// set the active view and return a pointer to
// the newly activated view.
SetActiveView(iIndex);
return GetActiveView();
}
CWnd* CXTPTabExBase::PrevView()
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(m_tcbItems.GetCount() > 0);
// get the current selection and increment by 1.
int iIndex = m_pTabCtrl->GetCurSel();
iIndex--;
// if the new index is greater than or equal to
// the tab count, set the index to 0.
if (iIndex < 0)
iIndex = m_pTabCtrl->GetItemCount()-1;
// set the active view and return a pointer to
// the newly activated view.
SetActiveView(iIndex);
return GetActiveView();
}
CWnd* CXTPTabExBase::GetView(int nView)
{
ASSERT_VALID(m_pTabCtrl);
if (nView > -1 && nView < m_pTabCtrl->GetItemCount())
{
POSITION pos = m_tcbItems.FindIndex(nView);
if (pos != NULL)
{
CWnd* pWnd = m_tcbItems.GetAt(pos)->pWnd;
ASSERT_VALID(pWnd);
if (pWnd && ::IsWindow(pWnd->m_hWnd))
{
return pWnd;
}
}
}
return NULL;
}
CWnd* CXTPTabExBase::GetView(CRuntimeClass* pViewClass)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(pViewClass != NULL);
if (!pViewClass)
return 0;
ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
CWnd* pWnd = m_tcbItems.GetAt(pos)->pWnd;
ASSERT_VALID(pWnd);
if (pWnd && ::IsWindow(pWnd->m_hWnd) && pWnd->IsKindOf(pViewClass))
{
return pWnd;
}
}
return NULL;
}
void CXTPTabExBase::ActivateView(CWnd* pNewView)
{
ASSERT_VALID(pNewView); // must be valid
// Hide all views.
int iIndex;
for (iIndex = 0; iIndex < m_tcbItems.GetCount(); ++iIndex)
{
POSITION pos = m_tcbItems.FindIndex(iIndex);
if (pos != NULL)
{
CXTPTcbItem* pTCBItem = m_tcbItems.GetAt(pos);
ASSERT(pTCBItem != NULL);
if (pTCBItem && pTCBItem->pWnd)
{
pTCBItem->pWnd->ShowWindow(SW_HIDE);
pTCBItem->pWnd->EnableWindow(FALSE);
}
}
}
////////// ULF 2003-08-07
if (m_pParentFrame && m_pParentFrame->IsChild(pNewView))
{
CView* pActiveView = m_pParentFrame->GetActiveView();
if (pActiveView && pActiveView->GetParent() == m_pTabCtrl)
{
if (pActiveView != pNewView)
{
// Make the frame window aware of the active view (for message routing)
m_pParentFrame->SetActiveView(DYNAMIC_DOWNCAST(CView, pNewView));
}
}
}
/////////
// Show and enable the window.
pNewView->ShowWindow(SW_SHOW);
pNewView->EnableWindow(TRUE);
pNewView->SetFocus();
// Resize the window.
ResizeTabView(pNewView);
}
void CXTPTabExBase::SetActiveView(CRuntimeClass* pViewClass)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(pViewClass != NULL);
if (!pViewClass)
return;
ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
// Walk through the tabs
int iIndex;
for (iIndex = 0; iIndex < m_tcbItems.GetCount(); ++iIndex)
{
POSITION pos = m_tcbItems.FindIndex(iIndex);
if (pos != NULL)
{
CXTPTcbItem* pTCBItem = m_tcbItems.GetAt(pos);
ASSERT(pTCBItem != NULL);
if (pTCBItem && pTCBItem->pWnd->IsKindOf(pViewClass))
{
// Select new the tab
m_pTabCtrl->SetCurSel(iIndex);
// now active the view and give it focus.
ActivateView(pTCBItem->pWnd);
break;
}
}
}
}
void CXTPTabExBase::SetActiveView(CWnd* pTabView)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(pTabView != NULL);
// Walk through the tabs
int iIndex;
for (iIndex = 0; iIndex < m_tcbItems.GetCount(); ++iIndex)
{
POSITION pos = m_tcbItems.FindIndex(iIndex);
if (pos != NULL)
{
CXTPTcbItem* pTCBItem = m_tcbItems.GetAt(pos);
ASSERT(pTCBItem != NULL);
// Does the tab view match the requested view ?
if (pTCBItem && pTCBItem->pWnd == pTabView)
{
// Select new the tab
m_pTabCtrl->SetCurSel(iIndex);
// now active the view and give it focus.
ActivateView(pTCBItem->pWnd);
break;
}
}
}
}
void CXTPTabExBase::SetActiveView(int nActiveTab)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(nActiveTab >= 0);
// Find the item at the specified index.
POSITION pos = m_tcbItems.FindIndex(nActiveTab);
if (pos != NULL)
{
CXTPTcbItem* pTCBItem = m_tcbItems.GetAt(pos);
ASSERT(pTCBItem != NULL);
if (pTCBItem && pTCBItem->pWnd != NULL)
{
ASSERT_VALID(pTCBItem->pWnd);
// Select new the tab
m_pTabCtrl->SetCurSel(nActiveTab);
// now active the view and give it focus.
ActivateView(pTCBItem->pWnd);
}
}
}
void CXTPTabExBase::RemoveListItem(POSITION pos, BOOL bDestroyWnd/*= TRUE*/)
{
// invalid position
if (pos == NULL)
return;
// remove the page from internal list
CXTPTcbItem *pMember = m_tcbItems.GetAt(pos);
// if this item is invalid, return.
if (!pMember)
return;
if (pMember->pWnd && ::IsWindow(pMember->pWnd->m_hWnd))
{
// And delete the member window, freeing our stored
// values relating to it
if (bDestroyWnd)
{
pMember->pWnd->DestroyWindow();
pMember->pWnd = NULL;
}
else
{
pMember->pWnd->ShowWindow(SW_HIDE);
}
}
// Remove from list and free memory.
m_tcbItems.RemoveAt(pos);
SAFE_DELETE(pMember);
}
void CXTPTabExBase::DeleteView(int nView, BOOL bDestroyWnd/*= TRUE*/)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT((nView >= 0) && (m_pTabCtrl->GetItemCount() > nView));
// Now find the view we want to delete and remove it
POSITION pos = m_tcbItems.FindIndex(nView);
if (pos != NULL)
{
CWnd* pView = m_tcbItems.GetAt(pos)->pWnd;
// Ensure that we get no dangling pointers
if (m_pParentFrame && m_pParentFrame->GetActiveView() == pView)
{
if (m_pLastActiveView && ::IsWindow(m_pLastActiveView->m_hWnd))
{
m_pParentFrame->SetActiveView(m_pLastActiveView);
}
else
{
m_pParentFrame->SetActiveView(NULL, FALSE);
m_pLastActiveView = NULL;
}
}
// Remove item from list, and free memory.
RemoveListItem(pos, bDestroyWnd);
// Remove it from the tab control
m_pTabCtrl->DeleteItem(nView);
int nCount = m_pTabCtrl->GetItemCount();
if (nCount > 0)
{
// Finally, if we have just deleted the active view, reset the
// active tab to be the first view in the list
if (nView == 0)
{
SetActiveView(nView);
}
else if (nView >= nCount)
{
SetActiveView(nCount-1);
}
else
{
SetActiveView(nView-1);
}
// Reset the tooltips for the views we have left...
ResetToolTips();
}
}
}
void CXTPTabExBase::DeleteView(CRuntimeClass* pViewClass, BOOL bDestroyWnd/*= TRUE*/)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(pViewClass != NULL);
if (!pViewClass)
return;
ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
int nView = 0;
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
CWnd* pView = m_tcbItems.GetAt(pos)->pWnd;
ASSERT_VALID(pView);
if (pView->IsKindOf(pViewClass))
{
DeleteView(nView, bDestroyWnd);
return;
}
nView++;
}
}
void CXTPTabExBase::DeleteView(CWnd* pView, BOOL bDestroyWnd/*= TRUE*/)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(pView != NULL);
ASSERT(AfxIsValidAddress(pView, sizeof(CWnd), FALSE));
int nView = 0;
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
if (m_tcbItems.GetAt(pos)->pWnd == pView)
{
DeleteView(nView, bDestroyWnd);
return;
}
nView++;
}
}
LPCTSTR CXTPTabExBase::GetViewName(int nView)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT((nView >= 0) && (m_pTabCtrl->GetItemCount() > nView));
if (nView != -1)
{
POSITION pos = m_tcbItems.FindIndex(nView);
if (pos != NULL)
{
return m_tcbItems.GetAt(pos)->szTabLabel;
}
}
return NULL;
}
LPCTSTR CXTPTabExBase::GetViewName(CRuntimeClass* pViewClass)
{
ASSERT_VALID(m_pTabCtrl);
ASSERT(pViewClass != NULL);
if (!pViewClass)
return NULL;
ASSERT(pViewClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
ASSERT(AfxIsValidAddress(pViewClass, sizeof(CRuntimeClass), FALSE));
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
CXTPTcbItem *pMember = m_tcbItems.GetAt(pos);
ASSERT(pMember);
if (pMember && pMember->pWnd->IsKindOf(pViewClass))
{
return (LPCTSTR)pMember->szTabLabel;
}
}
return NULL;
}
BOOL CXTPTabExBase::IsChildView(CWnd* pWnd)
{
POSITION pos = m_tcbItems.GetHeadPosition();
while (pos)
{
CXTPTcbItem *pItem = m_tcbItems.GetNext(pos);
if (pItem && pItem->pWnd == pWnd)
{
return TRUE;
}
}
return FALSE;
}
BOOL CXTPTabExBase::RemoveAllTabs(BOOL bDestroyWnd/*= TRUE*/)
{
if (m_pParentFrame)
{
// If the parent frames active view belongs to
// us set the active view to NULL.
CView * pActiveView = m_pParentFrame->GetActiveView();
if (IsChildView(pActiveView))
{
if (m_pLastActiveView && ::IsWindow(m_pLastActiveView->m_hWnd) && !IsChildView(m_pLastActiveView))
{
m_pParentFrame->SetActiveView(m_pLastActiveView);
}
else
{
m_pParentFrame->SetActiveView(NULL, FALSE);
}
}
}
m_pLastActiveView = NULL;
m_nOldIndex = -1;
// Free memory and clear tab view list.
while (!m_tcbItems.IsEmpty())
{
POSITION pos = m_tcbItems.FindIndex(0);
if (pos != NULL)
{
RemoveListItem(pos, bDestroyWnd);
}
}
// Remove all tabs from control.
m_pTabCtrl->DeleteAllItems();
return TRUE;
}
int CXTPTabExBase::GetTabFromPoint(CPoint point)
{
TCHITTESTINFO tch;
memset(&tch, 0, sizeof(TCHITTESTINFO));
if (point == CPoint(0, 0))
{
CPoint pt;
GetCursorPos(&pt);
m_pTabCtrl->ScreenToClient(&pt);
tch.pt = pt;
}
else
{
tch.pt = point;
}
return m_pTabCtrl->HitTest(&tch);
}
BOOL CXTPTabExBase::Defer(HDWP& hDWP, CWnd* pView)
{
if (pView && ::IsWindow(pView->m_hWnd))
{
HWND hWnd = pView->m_hWnd;
CRect rcNewSize;
GetChildRect(rcNewSize);
CRect rcOldSize;
pView->GetWindowRect(&rcOldSize);
m_pTabCtrl->ScreenToClient(&rcOldSize);
if (rcNewSize != rcOldSize)
{
int x = (int) rcNewSize.left;
int y = (int) rcNewSize.top;
int cx = (int) rcNewSize.Width();
int cy = (int) rcNewSize.Height();
// Set positioning flags
UINT uFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER;
// if the x-y coordinates have not changed, there is no reason
// to move the dialog item.
if (rcNewSize.TopLeft() == rcOldSize.TopLeft())
uFlags |= SWP_NOMOVE;
// if the cx-cy size has not changed, there is no reason to
// size the dialog item. If size has changed, don't
// copy bits of the client area (i.e. have them invalidated/redrawn)
if (rcNewSize.Size() == rcOldSize.Size())
uFlags |= SWP_NOSIZE;
else
uFlags |= SWP_NOCOPYBITS;
hDWP = ::DeferWindowPos(hDWP, hWnd, 0, x, y, cx, cy, uFlags);
if (hDWP == NULL)
{
TRACE(_T("DeferWindowPos failed for ID %i\n"), GetDlgCtrlID(hWnd));
return FALSE;
}
return TRUE;
}
}
return FALSE;
}
void CXTPTabExBase::RecalcLayout()
{
HDWP hDWP = ::BeginDeferWindowPos((int)m_tcbItems.GetCount());
// Resize all views.
POSITION pos;
for (pos = m_tcbItems.GetHeadPosition(); pos; m_tcbItems.GetNext(pos))
{
CXTPTcbItem* pTCBItem = m_tcbItems.GetAt(pos);
ASSERT(pTCBItem != NULL);
if (!pTCBItem)
continue;
CWnd* pWnd = pTCBItem->pWnd;
ASSERT_VALID(pWnd);
Defer(hDWP, pWnd);
}
::EndDeferWindowPos(hDWP);
}
void CXTPTabExBase::ResizeTabView(CWnd* pView)
{
if (pView && ::IsWindow(pView->m_hWnd))
{
CRect rcChild;
GetChildRect(rcChild);
pView->MoveWindow(rcChild);
}
}
void CXTPTabExBase::InitializeFont()
{
HGDIOBJ hFont = XTPAuxData().font.GetSafeHandle();
// if the tabs are vertical, use the vertical menu font.
if ((m_pTabCtrl->GetStyle() & TCS_VERTICAL) == TCS_VERTICAL)
hFont = XTPAuxData().fontVert.GetSafeHandle();
if (hFont != NULL)
{
if (XTPSystemVersion()->IsWin95())
m_pTabCtrl->PostMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
else
m_pTabCtrl->SendMessage(WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
}
}
void CXTPTabExBase::SetAutoCondense(BOOL bEnable)
{
m_bAutoCondensing = bEnable;
Condense();
}
BOOL CXTPTabExBase::GetAutoCondense()
{
return m_bAutoCondensing;
}
int CXTPTabExBase::CalculateTabWidth(CDC* pDC, CString& sLabel, bool bHasIcon)
{
int len = pDC->GetTextExtent(sLabel).cx + 6 + 6;
if (sLabel.GetLength() == 1) ++len;
if (bHasIcon)
{
len += 16 + 6;
}
return len;
}
// used by tab condensation code
struct CXTPTabExBase::ITEMANDWIDTH
{
CXTPTcbItem* pItem;
int iWidth;
bool bHasIcon;
bool bAtMinLength;
int iItemIndex;
CString sNewLabel;
};
void CXTPTabExBase::Condense()
{
if (!m_pTabCtrl->m_hWnd || m_pTabCtrl->GetItemCount() == 0)
{
return;
}
// create a device context with the tab control's font (used for label
// widths calculations)
CFont *pFont = m_pTabCtrl->GetFont();
VERIFY(pFont != NULL);
CDC *pDC = m_pTabCtrl->GetDC();
CFont *pOldFont = pDC->SelectObject(pFont);
int iTotalWidth = 0;
CList <ITEMANDWIDTH, ITEMANDWIDTH&> lstItemsByWidth;
// the formula for iMinTabWidth is based on empirical data
TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
const int iMinTabWidth = tm.tmAveCharWidth * 6 + 12;
// calculate length of tab labels and fill our tracking list
POSITION p = m_tcbItems.GetHeadPosition();
ASSERT(p != NULL); // there should be items
int idx = 0;
TCITEM tci;
tci.mask = TCIF_IMAGE;
while (p)
{
m_pTabCtrl->GetItem(idx, &tci);
ITEMANDWIDTH iaw;
iaw.bHasIcon = (tci.iImage >= 0);
iaw.pItem = m_tcbItems.GetNext(p);
iaw.iWidth = CalculateTabWidth(pDC, iaw.pItem->szTabLabel, iaw.bHasIcon);
iaw.sNewLabel = iaw.pItem->szTabLabel;
iaw.iItemIndex = idx;
iaw.bAtMinLength = FALSE;
++idx;
lstItemsByWidth.AddTail(iaw);
iTotalWidth += iaw.iWidth;
}
const bool bVertical = (m_pTabCtrl->GetStyle() & TCS_VERTICAL) == TCS_VERTICAL;
const bool bMultiLine =
bVertical || ((m_pTabCtrl->GetStyle() & TCS_MULTILINE) == TCS_MULTILINE);
CRect rc;
m_pTabCtrl->GetClientRect(&rc);
const int client_width = bVertical ? rc.Height() : rc.Width();
POSITION p2;
if (client_width == 0)
{
pDC->SelectObject(pOldFont);
m_pTabCtrl->ReleaseDC(pDC);
return;
}
if (m_bAutoCondensing)
{
iTotalWidth += 4; // 4 pixels extra for the selected tab
if (bMultiLine) ++iTotalWidth;
// start condensing labels until the total width fits the client area,
// or until we can't condense any more
while (iTotalWidth > client_width)
{
// look for widest item
p = lstItemsByWidth.GetHeadPosition();
int iWidest = -1;
while (p)
{
const POSITION p3 = p;
ITEMANDWIDTH& iaw = lstItemsByWidth.GetNext(p);
if (iaw.iWidth > iWidest && !iaw.bAtMinLength)
{
iWidest = iaw.iWidth;
p2 = p3;
}
}
if (iWidest == -1)
{
break;
}
ITEMANDWIDTH& iaw = lstItemsByWidth.GetNext(p2);
const int iOldWidth = iaw.iWidth;
CString& label = iaw.sNewLabel;
// shorten name
if (label.Right(3) != _T("..."))
{
label += _T("...");
}
do
{
if (label.GetLength() == 3 || (iaw.iWidth <= iMinTabWidth))
{
iaw.bAtMinLength = true;
}
else
{
int iLen = label.GetLength();
if ((iLen > 0) && ((iLen-4) >= 0))
{
label.Delete((iLen-4), 1);
}
}
iaw.iWidth = CalculateTabWidth(pDC, label, iaw.bHasIcon);
}
while (!iaw.bAtMinLength && iaw.iWidth >= iOldWidth);
iTotalWidth -= (iOldWidth - iaw.iWidth);
}
}
// update actual tab labels based on new condensed labels
tci.mask = TCIF_TEXT;
p = lstItemsByWidth.GetHeadPosition();
while (p)
{
ITEMANDWIDTH& iaw = lstItemsByWidth.GetNext(p);
// only change if actually different
if (iaw.pItem->szCondensedLabel != iaw.sNewLabel)
{
iaw.pItem->szCondensedLabel = iaw.sNewLabel;
tci.pszText = (TCHAR*)(LPCTSTR)iaw.sNewLabel;
m_pTabCtrl->SetItem(iaw.iItemIndex, &tci);
}
}
pDC->SelectObject(pOldFont);
m_pTabCtrl->ReleaseDC(pDC);
}