Click here to Skip to main content
15,886,110 members
Articles / Desktop Programming / MFC

An MFC extension library to enable DLL plug-in technology for your application using MESSAGE_MAPs

Rate me:
Please Sign up or sign in to vote.
4.85/5 (47 votes)
8 Jun 2004CPOL17 min read 577K   8.6K   238  
A plug-in architecture which allows you to write plug-in DLLs for your application and extend/modify its functionality.
// VViewManager.cpp : implementation file
//

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

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

#define ID_VIEWTAB           1005
#define ID_DEFDOCTIPS        _T("A new unsaved file")
#define ID_DEFCAPTION        _T("Open File Tabs Bar")
#define ID_TABHEIGHT         26

//
// This function adds a title entry to a popup menu
//
void AddMenuTitle(CMenu* popup, LPCSTR title)
{
    // insert a separator item at the top
    popup->InsertMenu(0, MF_BYPOSITION | MF_SEPARATOR, 0, title);

    // insert title item
    // note: item is not selectable (disabled) but not grayed
    popup->InsertMenu(0, MF_BYPOSITION | MF_STRING | MF_DISABLED, 0, title);

    SetMenuDefaultItem(popup->m_hMenu, 0, TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// CVViewManager

IMPLEMENT_DYNAMIC(CViewManager, CControlBar)

CViewManager::CViewManager()
{
	m_bClosing      = FALSE;
	m_nLMargin      = 10;
	m_nDockID       = 0;
	m_bWin2000      = TRUE;
}

CViewManager::~CViewManager()
{
	m_arViews.RemoveAll();
	m_arViewTitles.RemoveAll();
}

BEGIN_MESSAGE_MAP(CViewManager, CControlBar)
	//{{AFX_MSG_MAP(CViewManager)
	ON_WM_SIZE()
	ON_WM_CREATE()
	ON_WM_RBUTTONDOWN()
	ON_WM_WINDOWPOSCHANGING()
	ON_WM_WINDOWPOSCHANGED()
	//}}AFX_MSG_MAP
	ON_NOTIFY(TTN_NEEDTEXT, 0, OnViewManagerToolTip)
END_MESSAGE_MAP()

void CViewManager::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
{
	CMDIFrameWnd* pFrame = static_cast<CMDIFrameWnd *>(AfxGetApp()->m_pMainWnd);
	if (pFrame == NULL)
		return;        // this is not meant for us...

	// Get the active MDI child window.
	CMDIChildWnd* pChild = static_cast<CMDIChildWnd *>(pFrame->GetActiveFrame());
	// Get the active view attached to the active MDI child window.
	CView* pActiveView = reinterpret_cast<CView *>(pChild->GetActiveView());
    if (pActiveView == NULL) //...Is there a view anyway?
	{
		//...since there is no view hide the tab control, otherwise it looks...guess!
		m_ViewTabCtrl.ShowWindow(SW_HIDE);
		return;
	}
	else
	{
		//...we might have hidden the tab control, show it now
		if (!m_ViewTabCtrl.IsWindowVisible())
			m_ViewTabCtrl.ShowWindow(SW_SHOW);
	}

	// Now, we go...
	int iSel = -1;
	CString strWin;
	for (int t = 0; t < m_arViewTitles.GetSize(); t++)
	{
		CView* pViewAt = static_cast<CView *>(m_arViews.GetAt(t));
		pViewAt->GetParent()->GetWindowText(strWin);
		// ...if there is window title change since the last update set the new title
		if (strWin != m_arViewTitles.GetAt(t))
			SetViewName(strWin, pViewAt);
		// ...find the active view from the view list
		if (pActiveView == static_cast<CView *>(m_arViews.GetAt(t))) 
			iSel = t;
	}
	m_ViewTabCtrl.SetCurSel(iSel);   // set the tab for the active view

	// Be polite! update the dialog controls added to the CSizingControlBar
	UpdateDialogControls(pTarget, bDisableIfNoHndler);
}

////////////////////////////////////////////////////////////////////////////
// CViewManager	operations

void CViewManager::AddView(const TCHAR* csName, CView* pView)
{
	if (m_bClosing) 
		return;

	CString cs(csName);

	m_arViews.Add(pView);
	m_arViewTitles.Add(cs);

	if (m_ViewTabCtrl.GetSafeHwnd())
	{
		TCITEM tci;
		tci.mask    = TCIF_TEXT | TCIF_PARAM | TCIF_IMAGE;
		tci.pszText = cs.LockBuffer();
		tci.lParam  = reinterpret_cast<LPARAM>(pView);
		tci.iImage  = 0;                       //TODO
		m_ViewTabCtrl.InsertItem(m_ViewTabCtrl.GetItemCount(), &tci);
		cs.UnlockBuffer();
	}
}

void CViewManager::RemoveView(CView* pView)
{
	if (m_bClosing || m_arViews.GetSize() <= 0) 
		return;

	int nTabs;

	if (m_ViewTabCtrl.GetSafeHwnd())
	{
		for (nTabs = 0; nTabs < m_ViewTabCtrl.GetItemCount(); nTabs++)
		{
			TCITEM tci;
			tci.mask = TCIF_PARAM;
			m_ViewTabCtrl.GetItem(nTabs, &tci);
			if (tci.lParam == reinterpret_cast<LPARAM>(pView))
			{
				m_ViewTabCtrl.DeleteItem(nTabs);
				break;
			}
		}
	}

	for (nTabs = 0; nTabs < m_arViews.GetSize(); nTabs++)
	{
		if (static_cast<CView *>(m_arViews.GetAt(nTabs)) == pView)
		{
			m_arViewTitles.RemoveAt(nTabs);
			m_arViews.RemoveAt(nTabs);
			return;
		}
	}
}

void CViewManager::RemoveAll()
{
	m_arViews.RemoveAll();
	m_arViewTitles.RemoveAll();
}

void CViewManager::SetViewName(const TCHAR* cs, CView* pView)
{
	if (m_bClosing || m_arViews.GetSize() <= 0) 
		return;

	int nTabs;
	CString csName(cs);

	if (m_ViewTabCtrl.GetSafeHwnd())
	{
		for (nTabs = 0; nTabs < m_ViewTabCtrl.GetItemCount(); nTabs++)
		{
			TCITEM tci;
			tci.mask = TCIF_PARAM;
			m_ViewTabCtrl.GetItem(nTabs, &tci);
			if (tci.lParam == reinterpret_cast<LPARAM>(pView))
			{
				tci.mask = TCIF_PARAM | TCIF_TEXT;
				tci.pszText = csName.LockBuffer();
				m_ViewTabCtrl.SetItem(nTabs, &tci);
				csName.UnlockBuffer();
				m_ViewTabCtrl.Invalidate();
				break;
			}
		}
	}

	for (nTabs = 0; nTabs < m_arViews.GetSize(); nTabs++)
	{
		if (static_cast<CView *>(m_arViews.GetAt(nTabs)) == pView)
		{
			m_arViewTitles.SetAt(nTabs, csName);
			return;
		}
	}
}

int CViewManager::GetWindowNum()
{
	return m_arViews.GetSize();
}

void CViewManager::OnActivateView(const BOOL bActivate, CView* pView)
{
	if (bActivate)
	{
		if (m_ViewTabCtrl.GetSafeHwnd())
		{
			for (int nTabs = 0; nTabs < m_ViewTabCtrl.GetItemCount(); nTabs++)
			{
				TCITEM tci;
				tci.mask = TCIF_PARAM;
				m_ViewTabCtrl.GetItem(nTabs, &tci);
				if (tci.lParam == reinterpret_cast<LPARAM>(pView))
				{
					m_ViewTabCtrl.SetCurSel(nTabs);
					m_ViewTabCtrl.Invalidate();
					break;
				}
			}
		}
	}
}


/////////////////////////////////////////////////////////////////////////////
// CViewManager message handlers

void CViewManager::OnSize(UINT nType, int cx, int cy) 
{
	CControlBar::OnSize(nType, cx, cy);
	
	CRect rect, rcClient;
	if (m_ViewTabCtrl.GetSafeHwnd())
	{
		DWORD dwStyle = m_ViewTabCtrl.GetStyle();
		if (dwStyle & TCS_BOTTOM)
		{
			GetClientRect(rect);
			GetClientRect(rcClient);
			int nxOffset = GetSystemMetrics(SM_CXSIZEFRAME);
			m_ViewTabCtrl.SetWindowPos(&wndTop, rcClient.left + nxOffset + m_nLMargin, 
				rcClient.top, rect.Width() - nxOffset * 5 - m_nLMargin, 
				ID_TABHEIGHT, SWP_SHOWWINDOW);
			
			m_sizeMRU = CSize(cx, cy);
		}
		else
		{
			GetClientRect(rect);
			int nxOffset = GetSystemMetrics(SM_CXSIZEFRAME);
			m_ViewTabCtrl.SetWindowPos(&wndTop, nxOffset + m_nLMargin, 3, 
				rect.Width() - nxOffset * 5 - m_nLMargin, ID_TABHEIGHT, SWP_SHOWWINDOW);
			
			m_sizeMRU = CSize(cx, cy);
		}
	}
}

int CViewManager::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CControlBar::OnCreate(lpCreateStruct) == -1)
		return -1;

    m_ViewTabImages.Create(16, 16, ILC_MASK, 5, 5);	

	m_ViewTabCtrl.Create(WS_CHILD | WS_VISIBLE | WS_EX_NOPARENTNOTIFY | 
			TCS_BUTTONS	| TCS_FLATBUTTONS |		 // pLaY with this!
		TCS_TOOLTIPS | TCS_SINGLELINE | TCS_FOCUSNEVER | TCS_FORCELABELLEFT, 
		CRect(0, 0, 0, 0), this, ID_VIEWTAB);

	TabCtrl_SetExtendedStyle(m_ViewTabCtrl.m_hWnd, TCS_EX_FLATSEPARATORS);

	m_ViewTabCtrl.SetImageList(&m_ViewTabImages);
	// Build the image list here
    HICON hIcon = AfxGetApp()->LoadStandardIcon(IDI_APPLICATION);
//    HICON hIcon = AfxGetApp()->LoadIcon(IDR_DEMOTYPE);
	m_ViewTabImages.Add(hIcon);
	// Enable tooltips for all controls
	EnableToolTips(TRUE);
	
	return 0;
}

void CViewManager::OnRButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	POINT  ptScreen = point;
	// Convert the mouse point to screen coordinates since that is what is used by
	// the TrackPopupMenu() function.
	ClientToScreen(&ptScreen);
	CMenu cMenu;
	cMenu.CreatePopupMenu();
	
	cMenu.AppendMenu(MF_STRING, ID_VIEW_VIEWTAB, _T("Op&en File Tabs"));
	cMenu.AppendMenu(MF_STRING, ID_VIEW_FULLSCREEN, _T("F&ull Screen"));
	cMenu.AppendMenu(MF_SEPARATOR);
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_CLOSE_ALL, _T("C&lose All"));
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_SAVE_ALL, _T("&Save All"));
	cMenu.AppendMenu(MF_SEPARATOR);
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_NEW, _T("&New Windows"));
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_ARRANGE, _T("&Arrange Icons"));
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_CASCADE, _T("&Cascade Windows"));
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_TILE_HORZ, _T("Tile &Horizontally"));
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_TILE_VERT, _T("Tile &Vertically"));
	cMenu.AppendMenu(MF_SEPARATOR);
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_NEXT, _T("Ne&xt Window"));
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_PREVIOUS, _T("P&revious Window"));
	cMenu.AppendMenu(MF_SEPARATOR);
	cMenu.AppendMenu(MF_STRING, ID_WINDOW_MANAGE, _T("&Windows..."));

    // 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("Open File Tabs Bar"));

    ::SetMenuDefaultItem(cMenu.m_hMenu, 0, TRUE);
	
	cMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, ptScreen.x, ptScreen.y, AfxGetMainWnd());
	
	cMenu.DestroyMenu();

	CControlBar::OnRButtonDown(nFlags, point);
}

BOOL CViewManager::OnViewManagerToolTip(NMHDR* pNMHDR, LRESULT* pResult)
{
    *pResult = 0;
    TCHITTESTINFO hti;
	TOOLTIPTEXT *pTTT = reinterpret_cast<TOOLTIPTEXT *>(pNMHDR);    
	UINT nID = pNMHDR->idFrom;

	// if there are some dialog controls, progress the tooltips
    if (pTTT->uFlags & TTF_IDISHWND)    
	{
        // idFrom is actually the HWND of the tool
        nID = ::GetDlgCtrlID((HWND)nID);        
		if (nID)        
		{
            pTTT->lpszText = MAKEINTRESOURCE(nID);
            pTTT->hinst    = AfxGetResourceHandle();            
			return TRUE;
        }    
	} 

	// Now, address the view tab tooltips
    hti.pt = CPoint(GetMessagePos());
    m_ViewTabCtrl.ScreenToClient(&hti.pt);
    int nTab = m_ViewTabCtrl.HitTest(&hti);

	if (nTab >= 0)
	{
		CView* pView   = static_cast<CView *> (m_arViews.GetAt(nTab));
		ASSERT(pView != NULL);
		CDocument* pDoc = pView->GetDocument();
		CString strTabText = pDoc->GetPathName();
		if (strTabText.IsEmpty()) //... the document is not yet saved?
			strTabText = ID_DEFDOCTIPS;

		lstrcpy(pTTT->lpszText, strTabText);
		return TRUE;
	}
	return FALSE;
}

BOOL CViewManager::CreateViewManager(CMDIFrameWnd *pMDIFrameWnd, UINT uID)
{
    if (!Create(ID_DEFCAPTION, pMDIFrameWnd, m_sizeDefault, uID))
	{
		TRACE0(_T("Failed to create Tab bar\n"));  
		return FALSE;      // fail to create
	}

	SetBarStyle(GetBarStyle() | CBRS_TOP |
		CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
	EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM);
 	pMDIFrameWnd->DockControlBar(this); 

	return TRUE;
}

BOOL CViewManager::Create(LPCTSTR lpszWindowName, CWnd* pParentWnd,
                               CSize sizeDefault, UINT nID, DWORD dwStyle)
{
    // must have a parent
    ASSERT_VALID(pParentWnd);
    // cannot be both fixed and dynamic
    // (CBRS_SIZE_DYNAMIC is used for resizng when floating)
    ASSERT (!((dwStyle & CBRS_SIZE_FIXED) && (dwStyle & CBRS_SIZE_DYNAMIC)));

    m_dwStyle = dwStyle & CBRS_ALL; // save the control bar styles
	m_sizeDefault = sizeDefault;    // set fixed size
	m_sizeMRU     = sizeDefault;
    // register and create the window - skip CControlBar::Create()
    CString strWndclass = ::AfxRegisterWndClass(CS_DBLCLKS,
        ::LoadCursor(NULL, IDC_ARROW), ::GetSysColorBrush(COLOR_BTNFACE), 0);

    dwStyle &= ~CBRS_ALL;
	dwStyle |= WS_CLIPCHILDREN | CCS_NOPARENTALIGN | CCS_NOMOVEY | CCS_NORESIZE;
    if (!CWnd::Create(strWndclass, lpszWindowName, dwStyle,
        CRect(0, 0, 0, 0), pParentWnd, nID))
        return FALSE;

    return TRUE;
}

CSize CViewManager::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
{
	ASSERT_VALID(this);
	ASSERT(::IsWindow(m_hWnd));
	
	if (bStretch) // if not docked stretch to fit
		return CSize(bHorz ? 32767 : m_sizeDefault.cx, bHorz ? m_sizeDefault.cy : 32767);
	
	CClientDC dc(NULL);
	HFONT hFont    = reinterpret_cast<HFONT>(SendMessage(WM_GETFONT));
	HFONT hOldFont = NULL;
	if (hFont != NULL)
		hOldFont = reinterpret_cast<HFONT>(dc.SelectObject(hFont));
	TEXTMETRIC tm;
	VERIFY(dc.GetTextMetrics(&tm));
	if (hOldFont != NULL)
		dc.SelectObject(hOldFont);
	
	// get border information
	CSize size;
	CRect rcInside, rcWnd; 
	rcInside.SetRectEmpty();
	CalcInsideRect(rcInside, bHorz);
	GetParentFrame()->GetWindowRect(&rcWnd);
	size.cx = rcWnd.Width();
	size.cy = tm.tmHeight + tm.tmInternalLeading/* - 1 */+ 
		::GetSystemMetrics(SM_CYBORDER) * 5 - rcInside.Height();
	
	return size;
}

CSize CViewManager::CalcDynamicLayout(int nLength, DWORD dwMode)
{
	UNREFERENCED_PARAMETER(nLength);	   // clear that level 4 warning...

	if (dwMode & LM_HORZDOCK)
	{
		ASSERT(dwMode & LM_HORZ);
		return CalcFixedLayout(dwMode & LM_STRETCH, dwMode & LM_HORZ);
	}

	if (m_sizeMRU.cx > m_sizeDefault.cx)
		return m_sizeMRU;
	else
		return m_sizeDefault;
}

void CViewManager::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) 
{
	lpwndpos->flags |= SWP_FRAMECHANGED;
	CControlBar::OnWindowPosChanging(lpwndpos);
	
	UINT nOldDockID = m_nDockID;
	m_nDockID = GetParent()->GetDlgCtrlID();
	if (nOldDockID == m_nDockID)
		return;
	else
	{
		DWORD dwStyle = m_ViewTabCtrl.GetStyle();
		switch(m_nDockID)
		{
		case AFX_IDW_DOCKBAR_TOP:
			if (dwStyle & TCS_BOTTOM)
			{
				DWORD dwNewStyle = dwStyle & ~TCS_BOTTOM;
				m_ViewTabCtrl.ModifyStyle(dwStyle, dwNewStyle);
			}
			break;
		case AFX_IDW_DOCKBAR_BOTTOM:
			if ((dwStyle & TCS_BOTTOM) == 0)
			{
				DWORD dwNewStyle = dwStyle | TCS_BOTTOM;
				m_ViewTabCtrl.ModifyStyle(dwStyle, dwNewStyle);
			}
			break;
		case AFX_IDW_DOCKBAR_LEFT:
			break;
		case AFX_IDW_DOCKBAR_RIGHT:
			break;
		default:
				DWORD dwNewStyle = dwStyle & ~TCS_BOTTOM;
				m_ViewTabCtrl.ModifyStyle(dwStyle, dwNewStyle);
			break;
		}
	}
}

void CViewManager::DoPaint(CDC* pDC)
{
	CRect rect;
	GetClientRect(rect);

	// clean background
	COLORREF clr = ::GetSysColor(COLOR_BTNFACE);
	pDC->FillSolidRect(rect, clr);	

	// draw the gripper
	DrawGripper(pDC);
	// It is better to let the underlining control bar take the last shot
	CControlBar::DoPaint(pDC);
}

void CViewManager::DrawGripper(CDC* pDC)
{
	if( (m_dwStyle & CBRS_FLOATING) || m_dwDockStyle == 0 )
		return;

	COLORREF clrBtnHilight = ::GetSysColor(COLOR_BTNHILIGHT);
	COLORREF clrBtnShadow  = ::GetSysColor(COLOR_BTNSHADOW);

	CRect rcGrip;					 
	GetWindowRect(&rcGrip);
	ScreenToClient(&rcGrip);
	rcGrip.OffsetRect(-rcGrip.left, -rcGrip.top);

	if(m_dwStyle & CBRS_ORIENT_HORZ) 
	{
		// gripper at left
		rcGrip.DeflateRect(4, 4);
		rcGrip.right = rcGrip.left + 3;
        pDC->Draw3dRect(rcGrip, clrBtnHilight, clrBtnShadow);
		if (!m_bWin2000)
		{
			rcGrip.OffsetRect(3, 0);
			pDC->Draw3dRect(rcGrip,	clrBtnHilight, clrBtnShadow);
		}
	}
	else 
	{
		// gripper at top
		rcGrip.DeflateRect(4, 4);
		rcGrip.bottom = rcGrip.top + 3;
		pDC->Draw3dRect(rcGrip, clrBtnHilight, clrBtnShadow);
		if (!m_bWin2000)
		{
			rcGrip.OffsetRect(0, 3);
			pDC->Draw3dRect(rcGrip, clrBtnHilight, clrBtnShadow);
		}
	}
}


void CViewManager::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos) 
{
	OnSize(0, lpwndpos->cx, lpwndpos->cy);
	CControlBar::OnWindowPosChanged(lpwndpos);
}

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 The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Sirius Analytical Instruments
United Kingdom United Kingdom
A research and development programmer working for a pharmaceutical instrument company for the past 17 years.

I am one of those lucky people who enjoys his work and spends more time than he should either doing work or reseaching new stuff. I can also be found on playing DDO on the Cannith server (Send a tell to "Maetrim" who is my current main)

I am also a keep fit fanatic, doing cross country running and am seriously into [url]http://www.ryushinkan.co.uk/[/url] Karate at this time of my life, training from 4-6 times a week and recently achieved my 1st Dan after 6 years.

Comments and Discussions