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

CToolbarDialog - dialog with floating toolbar

Rate me:
Please Sign up or sign in to vote.
4.80/5 (22 votes)
6 Apr 20043 min read 158.9K   6.3K   56  
A CDialog derived class that contains a floating toolbar.
//   Author:      Lucian Barbulescu      lucian@xts.ro
//
//   Disclaimer
//   ----------
//   THIS SOFTWARE AND THE ACCOMPANYING FILES ARE DISTRIBUTED "AS IS" AND WITHOUT
//   ANY WARRANTIES WHETHER EXPRESSED OR IMPLIED. NO REPONSIBILITIES FOR POSSIBLE
//   DAMAGES OR EVEN FUNCTIONALITY CAN BE TAKEN. THE USER MUST ASSUME THE ENTIRE
//   RISK OF USING THIS SOFTWARE.
//
//   Terms of use
//   ------------
//   THIS SOFTWARE IS FREE FOR PERSONAL USE OR FREEWARE APPLICATIONS.
//   IF YOU USE THIS SOFTWARE IN COMMERCIAL OR SHAREWARE APPLICATIONS YOU
//   MUST HAVE THE PERMISION OF THE AUTHOR.
//
//
//	 History
//   -------
//
//   v1.0.0 - 7 April 2004		- First release
//	
//   v1.0.1 - 8 April 2004		- FIXED BUG: when toolbar is attached to the right or 
//                              the left no dragging possible by clicking on the handle
//                              (thanks to ReorX for informing me about this bug)
//
//                              - FIXED BUG: when the floating toolbar is docked the 
//                              parent dialog does not receives the focus
//
//   v1.0.2 - 9 April 2004      - ADDED: VC6 compatibility
//                              - ADDED: Tooltips support 
//                              (thanks to Randy More for his article about adding 
//                              tooltips to toolbars in dialogs)
//                              - FIXED BUG: UPDATE_COMMAND_UI does not work 
//                              (thanks to =[ Abin ]= for informing me about this bug)

#include "stdafx.h"
#include <afxpriv.h>
#include "ToolbarDialog.h"

// CSmartToolbar

IMPLEMENT_DYNAMIC(CSmartToolbar, CToolBar)
CSmartToolbar::CSmartToolbar()
{
	m_bState = TS_HIDDEN;
	m_bNoMove = false;
}

CSmartToolbar::~CSmartToolbar()
{
}


BEGIN_MESSAGE_MAP(CSmartToolbar, CToolBar)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
END_MESSAGE_MAP()

// CSmartToolbar message handlers

LRESULT CSmartToolbar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
{
	if (IsWindowVisible())
	{
		//CFrameWnd *pParent = (CFrameWnd *)GetParent();
		CFrameWnd *pParent = (CFrameWnd *)m_pMessageReceiver;
		if (pParent)
			OnUpdateCmdUI(pParent, (BOOL)wParam);
	}
	return 0L;
}


void CSmartToolbar::OnLButtonDown(UINT nFlags, CPoint point)
{
	if(m_bState & TS_FIXED)
	{
		int ht = GetToolBarCtrl().HitTest(&point);
		if((ht < 0)||(ht >= GetToolBarCtrl().GetButtonCount()))
		{
			SetCapture();
			CPoint newPoint = point;
			this->MapWindowPoints(m_pMessageReceiver,&newPoint,1);
			m_pMessageReceiver->SendMessage(TDM_BUTTONDOWN,WPARAM(0),LPARAM(&newPoint));
		}
		else
			m_bNoMove = true;
	}

	CToolBar::OnLButtonDown(nFlags, point);
}

void CSmartToolbar::OnLButtonUp(UINT nFlags, CPoint point)
{
	if(m_bState & TS_FIXED)
	{
		if(!m_bNoMove)
		{
			ReleaseCapture();
			CPoint newPoint = point;
			this->MapWindowPoints(m_pMessageReceiver,&newPoint,1);
			m_pMessageReceiver->SendMessage(TDM_BUTTONUP,WPARAM(0),LPARAM(&newPoint));
		}
		else
			m_bNoMove = false;
	}

	CToolBar::OnLButtonUp(nFlags, point);
}

void CSmartToolbar::OnMouseMove(UINT nFlags, CPoint point)
{
	if((m_bState & TS_FIXED) && !m_bNoMove && (nFlags & MK_LBUTTON))
	{
		CPoint newPoint = point;
		this->MapWindowPoints(m_pMessageReceiver,&newPoint,1);
		m_pMessageReceiver->SendMessage(TDM_MOUSEMOVE,WPARAM(0),LPARAM(&newPoint));
	}

	CToolBar::OnMouseMove(nFlags, point);
}

void CSmartToolbar::Initialize(CWnd* pParent, UINT uTemplateID)
{
	DWORD style = WS_CHILD|WS_VISIBLE|CBRS_FLYBY|CBRS_GRIPPER|CBRS_SIZE_DYNAMIC|CBRS_TOOLTIPS;

	if(m_bState & TS_FIXED)
		style|=(m_bState&TS_FIXED);
	else
		style|= TS_TOP;
	CreateEx(pParent,TBSTYLE_FLAT,style);

	LoadToolBar(uTemplateID);
}

void CSmartToolbar::UpdateToolbarStyle(DWORD dwStyle)
{
	m_bState = dwStyle;

	if(dwStyle == TS_HIDDEN)
	{
		//hide the toolbar and return
		ModifyStyle(WS_VISIBLE,0);
		return;
	}
	else
	{
		//ensure the toolbar is visible
		ModifyStyle(0,WS_VISIBLE);
	}

	DWORD dwCurrentStyle = GetBarStyle();

	dwCurrentStyle &= ~(CBRS_ALIGN_ANY);
	dwCurrentStyle &= ~(CBRS_BORDER_ANY);

	//now decide where to place the toolbar
	if(dwStyle == TS_FLOATING){
		SetBarStyle((dwCurrentStyle & ~CBRS_GRIPPER) | TS_TOP);
	}
	else{
		SetBarStyle(dwCurrentStyle | dwStyle | CBRS_GRIPPER);
	}
}


// CFloatToolbarDlg

IMPLEMENT_DYNAMIC(CFloatToolbarDlg, CWnd)
CFloatToolbarDlg::CFloatToolbarDlg(CSmartToolbar* pToolbar)
{
	m_pToolbar = pToolbar;
	m_bMoving = false;

}

CFloatToolbarDlg::~CFloatToolbarDlg()
{
}


BEGIN_MESSAGE_MAP(CFloatToolbarDlg, CWnd)
	ON_WM_CLOSE()
	ON_WM_LBUTTONUP()
	ON_WM_SETCURSOR()
	ON_WM_MOUSEMOVE()
	ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()



// CFloatToolbarDlg message handlers


BOOL CFloatToolbarDlg::ShowWindow(int nCmdShow)
{
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST,AFX_IDW_CONTROLBAR_LAST,0);
	return CWnd::ShowWindow(nCmdShow);
}

void CFloatToolbarDlg::CreateToolDlg(CWnd* pParent)
{
	BOOL bSuccess;

	// Register window class
	CString csClassName = AfxRegisterWndClass(CS_OWNDC|CS_HREDRAW|CS_VREDRAW,
		::LoadCursor(NULL, IDC_APPSTARTING),
		CBrush(::GetSysColor(COLOR_BTNFACE)));

	SIZE sz;
	m_pToolbar->GetToolBarCtrl().GetMaxSize(&sz);

	// Create popup window
	bSuccess = CreateEx(WS_EX_TOOLWINDOW,						// Extended style
		csClassName,								// Classname
		_T("Toolbar"),								// Title
		WS_POPUP | WS_CAPTION | WS_SYSMENU ,   // style
		0,0,
		sz.cx+6,100,
		pParent->GetSafeHwnd(),						// handle to parent
		0,											// No menu
		NULL);    



	CRect rWnd, rClient;
	GetWindowRect(rWnd);
	GetClientRect(rClient);

	rWnd.bottom = rWnd.top + rWnd.Height() - rClient.Height() + sz.cy;

	MoveWindow(rWnd);

	CenterWindow();

}



void CFloatToolbarDlg::OnClose()
{
	GetParent()->SendMessage(TDM_FLOATCLOSE,WPARAM(0),LPARAM(0));
}

LRESULT CFloatToolbarDlg::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM lParam)
{
	SendMessageToDescendants(WM_IDLEUPDATECMDUI,
		wParam, lParam, TRUE, TRUE);
	return 0;
}

BOOL CFloatToolbarDlg::OnToolTipText(UINT nID, NMHDR* pNMHDR, LRESULT* pResult)
{
	return (BOOL)(GetParent()->SendMessage(WM_NOTIFY,(WPARAM)nID,(LPARAM)pNMHDR));
}


void CFloatToolbarDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default

	if(m_bMoving)
	{
		m_bMoving = false;
		ReleaseCapture();
		CPoint newPoint = point;
		this->MapWindowPoints(GetParent(),&newPoint,1);
		GetParent()->SendMessage(TDM_BUTTONUP,WPARAM(0),LPARAM(&newPoint));
	}

	CWnd::OnLButtonUp(nFlags, point);
}

BOOL CFloatToolbarDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	if(message == WM_LBUTTONDOWN)
	{
		if(nHitTest == HTCAPTION)
		{
			m_bMoving = true;
			SetCapture();

			ShowWindow(SW_HIDE);

			CPoint pt;
			GetCursorPos(&pt);
			GetParent()->ScreenToClient(&pt);

			GetParent()->SendMessage(TDM_BUTTONDOWN,WPARAM(0),LPARAM(&pt));
		}
	}

	return CWnd::OnSetCursor(pWnd, nHitTest, message);
}

void CFloatToolbarDlg::OnMouseMove(UINT nFlags, CPoint point)
{
	if(nFlags & MK_LBUTTON)
	{
		if(m_bMoving)
		{
			CPoint newPoint = point;
			this->MapWindowPoints(GetParent(),&newPoint,1);
			GetParent()->SendMessage(TDM_MOUSEMOVE,WPARAM(0),LPARAM(&newPoint));
		}
	}

	CWnd::OnMouseMove(nFlags, point);
}

BOOL CFloatToolbarDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
	HWND hWnd = m_pToolbar->m_hWnd;

	if((LPARAM)hWnd == lParam)
	{
		GetParent()->SendMessage(TDM_TOOLBTNPRESS,wParam,LPARAM(0));
	}


	return CWnd::OnCommand(wParam, lParam);
}




// CToolbarDialog dialog

IMPLEMENT_DYNAMIC(CToolbarDialog, CDialog)
CToolbarDialog::CToolbarDialog(UINT uID, CWnd* pParent, UINT uToolbarID, DWORD dwToolbarState)
	: CDialog(uID, pParent),m_ptOffset(0,0),m_ptDialogOffset(0,0),
	m_rcOldToolRect(0,0,0,0)
{
	m_pToolbar = new CSmartToolbar();
	m_pFloatToolbar = new CFloatToolbarDlg(m_pToolbar);
	m_pToolbar->SetTolbarState(dwToolbarState);
	m_uToolbarID = uToolbarID;
	m_dwToolbarState = dwToolbarState;

   WORD HatchBits[8] = { 0x11, 0x22, 0x44, 0x88, 0x11,
      0x22, 0x44, 0x88 };

   // Use the bit pattern to create a bitmap.
   CBitmap bm;
   bm.CreateBitmap(8,8,1,1, HatchBits);

   m_brBrush.CreatePatternBrush(&bm);
}

CToolbarDialog::~CToolbarDialog()
{
	delete m_pFloatToolbar;
	delete m_pToolbar;
}

void CToolbarDialog::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CToolbarDialog, CDialog)
	ON_WM_SIZE()
	ON_MESSAGE(TDM_BUTTONDOWN,OnToolbarLButtonDown)
	ON_MESSAGE(TDM_BUTTONUP,OnToolbarLButtonUp)
	ON_MESSAGE(TDM_MOUSEMOVE,OnToolbarMouseMove)
	ON_MESSAGE(TDM_FLOATCLOSE,OnFloatToolbarClose)
	ON_MESSAGE(TDM_TOOLBTNPRESS,OnToolbarButtonPressed)
	ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
END_MESSAGE_MAP()


// CToolbarDialog message handlers



BOOL CToolbarDialog::OnInitDialog()
{
	CDialog::OnInitDialog();
	m_pToolbar->Initialize(this, m_uToolbarID);
	m_pToolbar->SetMessageReceiver(this);

	m_pFloatToolbar->CreateToolDlg(this);

	//initialy there is no toolbar
	//so I have to "lie" the PositionToolbar function
	//I set the "current state" to TS_HIDDEN
	DWORD dwState = m_dwToolbarState;
	m_dwToolbarState = TS_HIDDEN;
	PositionToolbar(dwState);


	return TRUE;  // return TRUE unless you set the focus to a control
	// EXCEPTION: OCX Property Pages should return FALSE
}

void CToolbarDialog::OnSize(UINT nType, int cx, int cy)
{
	CDialog::OnSize(nType, cx, cy);

}

LRESULT CToolbarDialog::OnKickIdle(WPARAM, LPARAM)
{ 
	if(m_pToolbar->GetParent() != this)
		AfxCallWndProc(m_pFloatToolbar,m_pFloatToolbar->GetSafeHwnd(),WM_IDLEUPDATECMDUI,(WPARAM)FALSE, 0);
	else
        SendMessageToDescendants(WM_IDLEUPDATECMDUI,(WPARAM)FALSE, 0, TRUE, TRUE);
	
	return 0;
}

void CToolbarDialog::PositionToolbar(DWORD dwPosition)
{	
	//ensure the floating toolbar is hidden if this is required
	if((dwPosition != TS_FLOATING) && (m_pToolbar->GetParent() == m_pFloatToolbar))
	{
		m_pFloatToolbar->ShowWindow(SW_HIDE);
		m_pToolbar->SetParent(this);
	}
	m_pToolbar->UpdateToolbarStyle(dwPosition);
	//based on the new style of the toolbar several actions have 
	//to be performed
	
	CPoint ptOffset(0,0);
	CPoint ptDialogOffset(0,0);
	if(dwPosition & TS_FIXED)
	{
		//the offset of the controls and the new size of the dialog
		//have to be computed
		CRect rcClientStart;
		CRect rcClientNow;
		GetClientRect(rcClientStart);
		RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST,
					0, reposQuery, rcClientNow);

		//ptOffset.SetPoint(rcClientNow.left - rcClientStart.left,
		//				rcClientNow.top - rcClientStart.top);

		ptOffset.x = rcClientNow.left - rcClientStart.left;
		ptOffset.y = rcClientNow.top - rcClientStart.top;

		//ptDialogOffset.SetPoint(rcClientStart.Width() - rcClientNow.Width(),
		//					rcClientStart.Height() - rcClientNow.Height());

		ptDialogOffset.x = rcClientStart.Width() - rcClientNow.Width();
		ptDialogOffset.y = rcClientStart.Height() - rcClientNow.Height();
	}

	// We need to resize the dialog to make room for control bars.
	// First, figure out how big the control bars are.

	//now we have to move the controls, but only if this is realy neaded

	if((ptOffset - m_ptOffset) != CSize(0,0))
	{
		CRect  rcChild;
		CWnd* pwndChild = GetWindow(GW_CHILD);
		while (pwndChild)
		{
			pwndChild->GetWindowRect(rcChild);
			ScreenToClient(rcChild);
			rcChild.OffsetRect(ptOffset - m_ptOffset);
			pwndChild->MoveWindow(rcChild, TRUE);
			pwndChild = pwndChild->GetNextWindow();
		}
	}
	
	// Adjust the dialog window dimensions if this is realy neaded
	if((ptDialogOffset - m_ptDialogOffset) != CSize(0,0))
	{
		CRect rcWindow;
		GetWindowRect(rcWindow);
		rcWindow.right += ptDialogOffset.x - m_ptDialogOffset.x;
		rcWindow.bottom += ptDialogOffset.y - m_ptDialogOffset.y;

		MoveWindow(rcWindow, TRUE);

		RedrawWindow();
	}

	//if the new style is TS_FLOATING, set the new parent of the toolbar
	if(dwPosition == TS_FLOATING)
	{
		m_pToolbar->SetParent(m_pFloatToolbar);

		m_pFloatToolbar->ShowWindow(SW_SHOW);
	}


	m_ptDialogOffset = ptDialogOffset;
	m_ptOffset = ptOffset;
	
	
	// And position the control bars
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
}

LONG CToolbarDialog::OnToolbarLButtonDown(WPARAM wParam, LPARAM lParam)
{
	CPoint* pPoint = (CPoint*)lParam;
	CRect rcToolRect;

	ComputeToolbarRect(*pPoint,&rcToolRect);

	DrawToolRect(rcToolRect);

	return 0;
}

LONG CToolbarDialog::OnToolbarMouseMove(WPARAM wParam, LPARAM lParam)
{
	CPoint* pPoint = (CPoint*)lParam;
	CRect rcToolRect;

	//TRACE2("x=%d, y=%d \n",pPoint->x, pPoint->y);
	ComputeToolbarRect(*pPoint,&rcToolRect);

	DrawToolRect(rcToolRect);

	return 0;
}

LONG CToolbarDialog::OnFloatToolbarClose(WPARAM wParam, LPARAM lParam)
{

	m_dwToolbarState = TS_HIDDEN;

	PositionToolbar(m_dwToolbarState);
	return 0;
}

LONG CToolbarDialog::OnToolbarButtonPressed(WPARAM wParam, LPARAM lParam)
{
	ToolbarButtonCommand(UINT(wParam));
	return 0;
}


LONG CToolbarDialog::OnToolbarLButtonUp(WPARAM wParam, LPARAM lParam)
{

	PositionToolbar(m_dwToolbarState);
	
	if(m_dwToolbarState == TS_FLOATING)
	{
		CRect rcWnd = m_rcOldToolRect;
		ClientToScreen(rcWnd);
		m_pFloatToolbar->MoveWindow(rcWnd);
	}
	else
		SetFocus();

	DrawToolRect(CRect(0,0,0,0));
	
	return 0;
}

void CToolbarDialog::ComputeToolbarRect(CPoint ptPoint, CRect* pToolRect)
{
   SIZE sz;
   m_pToolbar->GetToolBarCtrl().GetMaxSize(&sz);
   long min, max;
   min = sz.cx > sz.cy ? sz.cy : sz.cx; 
   max = sz.cx > sz.cy ? sz.cx : sz.cy;

   pToolRect->SetRect(0,0,0,0);

   CRect chRect;
   GetClientRect(chRect);

   if(ptPoint.y <= (chRect.top + 30))
   {
      //the rectangle is a horisontal one
      pToolRect->right = max;
      pToolRect->bottom = min;
	  m_dwToolbarState = TS_TOP;
   }
   else if(ptPoint.y >= (chRect.bottom - 30))
   {
      //the rectangle is a horisontal one
      pToolRect->right = max;
      pToolRect->bottom = min;
	  m_dwToolbarState = TS_BOTTOM;
   }
   else if(ptPoint.x <= (chRect.left + 30))
   {
      //the rectangle is a vertical one
      pToolRect->right = min;
      pToolRect->bottom = max;
	  m_dwToolbarState = TS_LEFT;
   }
   else if(ptPoint.x >= (chRect.right - 30))
   {
      //the rectangle is a vertical one
      pToolRect->right = min;
      pToolRect->bottom = max;
	  m_dwToolbarState = TS_RIGHT;
   }
   else
   {
      CRect rcFloatRect;
	  m_pFloatToolbar->GetWindowRect(rcFloatRect);

      pToolRect->right = rcFloatRect.Width();
      pToolRect->bottom = rcFloatRect.Height();

	  m_dwToolbarState = TS_FLOATING;
   }

   pToolRect->OffsetRect(ptPoint);
}

void CToolbarDialog::DrawToolRect(CRect rcToolRect)
{
	//erase the old rectangle
	RedrawWindow(m_rcOldToolRect);

	//if the new Tool Rect is 0,0,0,0, do not draw it
	if(rcToolRect != CRect(0,0,0,0))
	{
		CDC* pDC = GetDC();

		pDC->FrameRect(rcToolRect,&m_brBrush);

		ReleaseDC(pDC);
	}

	m_rcOldToolRect = rcToolRect;

}

BOOL CToolbarDialog::OnCommand(WPARAM wParam, LPARAM lParam)
{
	HWND hWnd = m_pToolbar->m_hWnd;

	if((LPARAM)hWnd == lParam)
	{
		OnToolbarButtonPressed(wParam,LPARAM(0));
	}

	return CDialog::OnCommand(wParam, lParam);
}


//this function must be implemented in your class
LONG CToolbarDialog::ToolbarButtonCommand(UINT uButtonID)
{
	ASSERT(FALSE);
	return 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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) CS Romania
Romania Romania
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions