Click here to Skip to main content
15,887,746 members
Articles / Desktop Programming / MFC

Automatic Tab Bar for MDI Frameworks

Rate me:
Please Sign up or sign in to vote.
4.80/5 (14 votes)
3 Jan 2003Public Domain 245.4K   5.8K   82  
A dockable bar containing a tabbed list of open windows
// WindowTabCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"       // main symbols
#include "WindowTabCtrl.h"
#include "ViewManager.h"
#include "PopupMenu.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define CX_BORDER  1
#define CY_BORDER  1

// TODO: Uncomment the ff. (__NO_RIGHTCLICK_SEL) if you do not wish to have the 
//       tab selected, when right-clicked.
//#define  __NO_RIGHTCLICK_SEL


/////////////////////////////////////////////////////////////////////////////
// CWindowTabCtrl

CWindowTabCtrl::CWindowTabCtrl() : m_bDisplayIcons(TRUE)
{
	m_crSelColor    = RGB(  0,  0, 255);
	m_crUnselColor  = RGB( 50, 50,  50);
	m_crDocModified = RGB(255,  0,   0);
	
	// Get the current font
	LOGFONT lFont;
	memset(&lFont, 0, sizeof(LOGFONT));
	NONCLIENTMETRICS ncm;
	ncm.cbSize = sizeof(NONCLIENTMETRICS);
	VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 
		sizeof(NONCLIENTMETRICS), &ncm, 0));
	lFont = ncm.lfMessageFont; 
	
	// Create the "Selected" font
	m_SelFont.CreateFontIndirect(&lFont);
	// Create the "UnSelected" font
	m_UnselFont.CreateFontIndirect(&lFont);
}

CWindowTabCtrl::~CWindowTabCtrl()
{
	m_SelFont.DeleteObject();
	m_UnselFont.DeleteObject();
}


BEGIN_MESSAGE_MAP(CWindowTabCtrl, CTabCtrl)
	//{{AFX_MSG_MAP(CWindowTabCtrl)
	ON_NOTIFY_REFLECT(TCN_SELCHANGE, OnSelchange)
	ON_WM_CREATE()
	ON_WM_RBUTTONDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWindowTabCtrl message handlers

void CWindowTabCtrl::OnSelchange(NMHDR* pNMHDR, LRESULT* pResult) 
{
	if (pNMHDR->hwndFrom == NULL)
		return;                    // just to avoid level 4 compilation warning

	int idx = GetCurSel();

	TCITEM tci;
	tci.mask = TCIF_PARAM;
	GetItem(idx, &tci);

	CMDIChildWnd* pChild = reinterpret_cast<CMDIChildWnd*>(tci.lParam);

	static_cast<CMDIFrameWnd*>(AfxGetMainWnd())->MDIActivate(pChild);
	*pResult = 0;
}

void CWindowTabCtrl::SetIconDisplay(BOOL bDisplayIt)
{
	m_bDisplayIcons = bDisplayIt;
	Invalidate();
}

int CWindowTabCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CTabCtrl::OnCreate(lpCreateStruct) == -1)
	{
		TRACE0(_T("OnCreate function call failed!"));
		return -1;
	}
	ModifyStyle(0, TCS_OWNERDRAWFIXED);
	
	HFONT hFont  = reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
	CFont* pFont = CFont::FromHandle(hFont);
	SetFont(pFont);
	
	return 0;
}

/*
void CWindowTabCtrl::PreSubclassWindow() 
{
	CTabCtrl::PreSubclassWindow();
	ModifyStyle(0, TCS_OWNERDRAWFIXED); //TODO must go!
}
*/

void CWindowTabCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
	ASSERT(IsWindow(m_hWnd));

	CRect rcTab   =  CRect(&(lpDrawItemStruct->rcItem));
	int nTabIndex = lpDrawItemStruct->itemID;
	if (nTabIndex < 0 || nTabIndex >= GetItemCount())
		return;
	
	BOOL bSelected = (nTabIndex == GetCurSel());
	
	TCHAR szLabel[_MAX_PATH];
	TCITEM tci;
	tci.mask       = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
	tci.pszText    = szLabel;     
	tci.cchTextMax = _MAX_PATH - 1;    	
	if (!GetItem(nTabIndex, &tci)) 
		return;
	
	CDC dc;
	if (lpDrawItemStruct->hDC == NULL)
		return;

	if (!dc.Attach(lpDrawItemStruct->hDC)) 
		return;
	
	DWORD dwStyle = GetStyle();
    rcTab.NormalizeRect();
	if ((dwStyle & TCS_BOTTOM) == 0)
	{
		rcTab.DeflateRect(CX_BORDER, CY_BORDER);
		rcTab.top += ::GetSystemMetrics(SM_CYEDGE);
	}
	
	dc.SetBkMode(TRANSPARENT);
	dc.FillSolidRect(rcTab, ::GetSysColor(COLOR_BTNFACE));
	
	CMDIChildWnd* pChild = reinterpret_cast<CMDIChildWnd*>(tci.lParam);
	if (pChild == NULL)
		return;
	// Get the active view attached to the active MDI child window.
	CView* pView = reinterpret_cast<CView*>(pChild->GetActiveView());
	if (pView == NULL)
		return;
	CDocument* pDoc = pView->GetDocument();
	if (pDoc == NULL)
		return;

	// Draw image
	if (m_bDisplayIcons)
	{
		CImageList*   pImageList = GetImageList();
		CMDIChildWnd* pViewFrame = reinterpret_cast<CMDIChildWnd*>(tci.lParam);
        HICON hIcon = reinterpret_cast<HICON>(GetClassLong(pViewFrame->m_hWnd, GCL_HICONSM));
		pImageList->Replace(tci.iImage/*nTabIndex*/, hIcon);  // nTabIndex

		if (pImageList && tci.iImage >= 0) 
		{
			rcTab.left += 4;//TODO--dc.GetTextExtent(_T(" ")).cx;		// Set a little margin
			
			// Get the height of image 
			IMAGEINFO info;
			pImageList->GetImageInfo(tci.iImage, &info);
			CRect ImageRect(info.rcImage);
			int nYpos = rcTab.top;
			
			pImageList->Draw(&dc, tci.iImage, CPoint(rcTab.left, nYpos), ILD_TRANSPARENT);
			rcTab.left += ImageRect.Width();
		}
	}
	
	if (bSelected) 
	{
		if (pDoc->IsModified())
			dc.SetTextColor(m_crDocModified);
		else
			dc.SetTextColor(m_crSelColor);
		dc.SelectObject(&m_SelFont);
		rcTab.top -= ::GetSystemMetrics(SM_CYEDGE);
	} 
	else 
	{
		if (pDoc->IsModified())
			dc.SetTextColor(m_crDocModified);
		else
			dc.SetTextColor(m_crUnselColor);
		dc.SelectObject(&m_UnselFont);
	}
	
	dc.DrawText(szLabel, rcTab, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
	dc.Detach();
}


/////////////////////////////////////////////////////////////////////////////
// CWindowTabCtrl operations

void CWindowTabCtrl::SetColors(COLORREF crSelColor, COLORREF crUnselColor, 
							   COLORREF crDocModified)
{
	if ((crSelColor != CLR_NONE) || (crSelColor != CLR_DEFAULT))
		m_crSelColor    = crSelColor;
	if ((crUnselColor != CLR_NONE) || (crUnselColor != CLR_DEFAULT))
		m_crUnselColor  = crUnselColor;
	if ((crDocModified != CLR_NONE) || (crDocModified != CLR_DEFAULT))
		m_crDocModified = crDocModified;

	Invalidate();
}

void CWindowTabCtrl::SetFonts(CFont* pSelFont, CFont* pUnselFont)
{
	ASSERT(pSelFont && pUnselFont);

	LOGFONT lFont;
	memset(&lFont, 0, sizeof(LOGFONT));
	int nSelHeight = 0, nUnselHeight = 0;

	m_SelFont.DeleteObject();
	m_UnselFont.DeleteObject();

	pSelFont->GetLogFont(&lFont);
	m_SelFont.CreateFontIndirect(&lFont);
	nSelHeight = lFont.lfHeight;

	pUnselFont->GetLogFont(&lFont);
	m_UnselFont.CreateFontIndirect(&lFont);
	nUnselHeight = lFont.lfHeight;

	SetFont((nSelHeight > nUnselHeight) ? &m_SelFont : &m_UnselFont);
}

void CWindowTabCtrl::SetFonts(CFont* pFont, BOOL bSelFont)
{
	ASSERT(pFont);

	LOGFONT lFont;
	memset(&lFont, 0, sizeof(LOGFONT));

	if (bSelFont)
	{
		m_SelFont.DeleteObject();
		pFont->GetLogFont(&lFont);
		m_SelFont.CreateFontIndirect(&lFont);
		SetFont(&m_SelFont);
	}
	else
	{
		m_UnselFont.DeleteObject();
		pFont->GetLogFont(&lFont);
		m_UnselFont.CreateFontIndirect(&lFont);
		SetFont(&m_UnselFont);
	}
}

void CWindowTabCtrl::SetFonts(int nSelWeight,   BOOL bSelItalic,   BOOL bSelUnderline,
						  int nUnselWeight, BOOL bUnselItalic, BOOL bUnselUnderline)
{
	// Free any memory currently used by the fonts.
	m_SelFont.DeleteObject();
	m_UnselFont.DeleteObject();

	// Get the current font
	LOGFONT lFont;
	memset(&lFont, 0, sizeof(LOGFONT));
	CFont* pFont = GetFont();
	if (pFont)
		pFont->GetLogFont(&lFont);
	else 
	{
		NONCLIENTMETRICS ncm;
		ncm.cbSize = sizeof(NONCLIENTMETRICS);
		VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 
			sizeof(NONCLIENTMETRICS), &ncm, 0));
		lFont = ncm.lfMessageFont; 
	}

	// Create the "Selected" font
	lFont.lfWeight    = static_cast<LONG>(nSelWeight);
	lFont.lfItalic    = static_cast<BYTE>(bSelItalic);
	lFont.lfUnderline = static_cast<BYTE>(bSelUnderline);
	m_SelFont.CreateFontIndirect(&lFont);

	// Create the "Unselected" font
	lFont.lfWeight    = static_cast<LONG>(nUnselWeight);
	lFont.lfItalic    = static_cast<BYTE>(bUnselItalic);
	lFont.lfUnderline = static_cast<BYTE>(bUnselUnderline);
	m_UnselFont.CreateFontIndirect(&lFont);

	SetFont((nSelWeight > nUnselWeight) ? &m_SelFont : &m_UnselFont);
}

void CWindowTabCtrl::OnRButtonDown(UINT nFlags, CPoint point) 
{
#ifndef __NO_RIGHTCLICK_SEL
	// Select the tab which is clicked and activate the attached view
    TCHITTESTINFO   hti;
    
    hti.pt = CPoint(GetMessagePos());
    ScreenToClient(&hti.pt);
    int iTab = HitTest(&hti);
    if (iTab < 0)
        return;
	SetCurSel(iTab);                    // Select it

	TCITEM tci;
	tci.mask = TCIF_PARAM;
	GetItem(iTab, &tci);

	CMDIChildWnd* pChild = reinterpret_cast<CMDIChildWnd*>(tci.lParam);
	static_cast<CMDIFrameWnd*>(AfxGetMainWnd())->MDIActivate(pChild);
#endif //__NO_RIGHTCLICK_SEL

	// Now, process the popup menu for the tab control
	POINT  ptScreen = point;
	// Convert the mouse point to screen coordinates since that is what is used by
	// the TrackPopupMenu() function.
	ClientToScreen(&ptScreen);
	CPopupMenu cMenu;
	cMenu.CreatePopupMenu();
	
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_CLOSE, _T("&Close"));
	cMenu.AppendMenu(MF_STRING, ID_FILE_SAVE, _T("&Save\tCtrl+S"));
	cMenu.AppendMenu(MF_STRING, ID_FILE_SAVE_AS, _T("Save &As..."));
	cMenu.AppendMenu(MF_SEPARATOR);
	cMenu.AppendMenu(MF_STRING, ID_FILE_PRINT, _T("&Print\tCtrl+P"));
	cMenu.AppendMenu(MF_STRING, ID_FILE_PRINT_PREVIEW, _T("Print Pre&view"));
	
	cMenu.LoadToolBarResource(IDR_TABVIEW_MENU);
	cMenu.RemapMenu(&cMenu);
	cMenu.EnableMenuItems(&cMenu, AfxGetMainWnd());
	
    // insert a separator item at the top
    cMenu.InsertMenu(0, MF_BYPOSITION | MF_SEPARATOR, 0, _T(""));

    // insert title item
    // note: item is not selectable (disabled) but not grayed
    cMenu.InsertMenu(0, MF_BYPOSITION | MF_STRING | MF_DISABLED, 0, _T("Active View Tab"));

    ::SetMenuDefaultItem(cMenu.m_hMenu, 0, TRUE);

	cMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, ptScreen.x, ptScreen.y, AfxGetMainWnd());
	
	cMenu.DestroyMenu();
	
	CTabCtrl::OnRButtonDown(nFlags, point);
}

void CWindowTabCtrl::SetTabStyle(BOOL bButtons, BOOL bFlatButtons)
{
	DWORD dwAdd          = 0;
	DWORD dwCurrentStyle = GetStyle();

	if (!bButtons && (dwCurrentStyle & TCS_BUTTONS))
	{
		ModifyStyle(TCS_BUTTONS | TCS_FLATBUTTONS, 0, 0);
		return;
	}
	else if (!bButtons)
		return;
		
	if (bButtons && (dwCurrentStyle & TCS_BUTTONS) && 
		(dwCurrentStyle & TCS_FLATBUTTONS))
		return;

	if (bButtons)
		dwAdd |= TCS_BUTTONS;
	if (bFlatButtons)
		dwAdd |= TCS_FLATBUTTONS;
	ModifyStyle(0, dwAdd, 0);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Engineer
Japan Japan
Systems Engineer

Comments and Discussions