Click here to Skip to main content
12,356,154 members (71,236 online)
Click here to Skip to main content

Stats

209.3K views
5.3K downloads
82 bookmarked
Posted

Automatic Tab Bar for MDI Frameworks

, 3 Jan 2003 Public Domain
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

Share

About the Author

Paul Selormey
Engineer
Japan Japan
Systems Engineer

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160621.1 | Last Updated 4 Jan 2003
Article Copyright 2000 by Paul Selormey
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid