Click here to Skip to main content
15,886,872 members
Articles / Desktop Programming / WTL

WTL Toolbar Helper

Rate me:
Please Sign up or sign in to vote.
4.89/5 (28 votes)
11 Dec 2006CPOL9 min read 75.6K   4.1K   64  
Add text, drop-down menus, and comboboxes to a WTL toolbar.
#pragma once

// Define various toolbar button styles in case they are missing
#ifndef TBSTYLE_EX_MIXEDBUTTONS
#define TBSTYLE_EX_MIXEDBUTTONS			0x00000008
#endif

#ifndef BTNS_SHOWTEXT
#define BTNS_SHOWTEXT				0x0040
#endif

#define ATL_SIMPLE_TOOLBAR_PANE_STYLE_EX	(ATL_SIMPLE_TOOLBAR_PANE_STYLE|TBSTYLE_LIST)

/// Class used to expost useful toolbar functionality to a WTL CFrameWnd-derived class
template <class T>
class CToolBarHelper
{
private:
	/// Wrapper class for the Win32 TBBUTTONINFO structure.
	class CTBButtonInfo : public TBBUTTONINFO
	{
	public:
		/// Constructor
		CTBButtonInfo(DWORD dwInitialMask = 0)
		{
			memset(this, 0, sizeof(TBBUTTONINFO));
			cbSize = sizeof(TBBUTTONINFO);
			dwMask = dwInitialMask;
		}
	};

	/// Wrapper class for the Win32 TBBUTTON structure.
	class CTBButton : public TBBUTTON
	{
	public:
		/// Constructor
		CTBButton()
		{
			memset(this, 0, sizeof(TBBUTTON));
		}
	};
private:
	CFont m_fontCombo;			///< Font to use for comboboxes
	CSimpleMap<UINT, UINT> m_mapMenu;	///< Map of command IDs -> menu IDs
public:
	/// Message map
	BEGIN_MSG_MAP(CToolBarHelper<T>)
		COMMAND_CODE_HANDLER(CBN_SELCHANGE, OnSelChangeToolBarCombo)
		NOTIFY_CODE_HANDLER(TBN_DROPDOWN, OnToolbarDropDown)
	END_MSG_MAP()

	/// Modify a toolbar button to have a drop-down button
	void AddToolBarDropDownMenu(HWND hWndToolBar, UINT nButtonID, UINT nMenuID)
	{
		ATLASSERT(hWndToolBar != NULL);
		ATLASSERT(nButtonID > 0);
		// Use built-in WTL toolbar wrapper class
		CToolBarCtrl toolbar(hWndToolBar);
		// Add the necessary style bit (TBSTYLE_EX_DRAWDDARROWS) if
		// not already present
		if ((toolbar.GetExtendedStyle() & TBSTYLE_EX_DRAWDDARROWS) != TBSTYLE_EX_DRAWDDARROWS)
			toolbar.SetExtendedStyle(toolbar.GetExtendedStyle() | TBSTYLE_EX_DRAWDDARROWS);
		// Get existing button style
		CTBButtonInfo tbi(TBIF_STYLE);
		if (toolbar.GetButtonInfo(nButtonID, &tbi) != -1)
		{
			// Modify the button
			tbi.fsStyle |= TBSTYLE_DROPDOWN;
			toolbar.SetButtonInfo(nButtonID, &tbi);
			// We need to remember that this menu ID is associated with the button ID
			// so use a basic map for this.
			m_mapMenu.Add(nButtonID, nMenuID);
		}
	}

	LRESULT OnToolbarDropDown(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
	{
		// Get the toolbar data
		NMTOOLBAR* ptb = reinterpret_cast<NMTOOLBAR*>(pnmh);
		// See if the button ID has an asscociated menu ID
		UINT nMenuID = m_mapMenu.Lookup(ptb->iItem);
		if (nMenuID)
		{
			// Get the toolbar control
			CToolBarCtrl toolbar(pnmh->hwndFrom);
			// Get the button rect
			CRect rect;
			toolbar.GetItemRect(toolbar.CommandToIndex(ptb->iItem), &rect);
			// Create a point
			CPoint pt(rect.left, rect.bottom);
			// Map the points
			toolbar.MapWindowPoints(HWND_DESKTOP, &pt, 1);
			// Load the menu
			CMenu menu;
			if (menu.LoadMenu(nMenuID))
			{
				CMenuHandle menuPopup = menu.GetSubMenu(0);
				ATLASSERT(menuPopup != NULL);
				T* pT = static_cast<T*>(this);
				// Allow the menu items to be initialised (for example,
				// new items could be added here for example)
				pT->PrepareToolBarMenu(nMenuID, menuPopup);
				// Display the menu				
				// Using command bar TrackPopupMenu method means menu icons are displayed
				pT->m_CmdBar.TrackPopupMenu(menuPopup, TPM_RIGHTBUTTON|TPM_VERTICAL, pt.x, pt.y);
			}
		}
		return 0;
	}

	/// Override this Allow the menu items to be enabled/checked/etc.
	virtual void PrepareToolBarMenu(UINT /*nMenuID*/, HMENU /*hMenu*/)
	{
	}

	/// Add text to a toolbar button
	void AddToolbarButtonText(HWND hWndToolBar, UINT nID, LPCTSTR lpsz)
	{
		// Use built-in WTL toolbar wrapper class
		CToolBarCtrl toolbar(hWndToolBar);
		// Set extended style
		if ((toolbar.GetExtendedStyle() & TBSTYLE_EX_MIXEDBUTTONS) != TBSTYLE_EX_MIXEDBUTTONS)
			toolbar.SetExtendedStyle(toolbar.GetExtendedStyle() | TBSTYLE_EX_MIXEDBUTTONS);
		// Get the button index
		int nIndex = toolbar.CommandToIndex(nID);
		CTBButton tb;
		toolbar.GetButton(nIndex, &tb);
		int nStringID = toolbar.AddStrings(lpsz);
		// Alter the button style
		tb.iString = nStringID;
		tb.fsStyle |= TBSTYLE_AUTOSIZE|BTNS_SHOWTEXT;
		// Delete and re-insert the button
		toolbar.DeleteButton(nIndex);
		toolbar.InsertButton(nIndex, &tb);
	}

	/// Add resource string to a toolbar button
	void AddToolbarButtonText(HWND hWndToolBar, UINT nID, UINT nStringID)
	{
		CString str;
		if (str.LoadString(nStringID))
			AddToolbarButtonText(hWndToolBar, nID, str);
	}

	/// Add text to a toolbar button (using tooltip text)
	void AddToolbarButtonText(HWND hWndToolBar, UINT nID)
	{
		TCHAR sz[256];
		if (AtlLoadString(nID, sz, 256) > 0)
		{
			// Add the text following the '\n'
			TCHAR* psz = _tcsrchr(sz, '\n');
			if (psz != NULL)
			{
				// Skip to first character of the tooltip
				psz++;
				// The tooltip text may include the accelerator, i.e.
				//	Open (Ctrl+O)
				// So look for an open brace
				TCHAR* pBrace = _tcschr(psz, '(');
				if (pBrace != NULL)
					*(pBrace - 1) = '\0';
				AddToolbarButtonText(hWndToolBar, nID, psz);
			}
		}
	}	

	/// Create a combobox on a toolbar
	HWND CreateToolbarComboBox(HWND hWndToolBar, UINT nID, UINT nWidth = 16, UINT nHeight = 16,
				   DWORD dwComboStyle = CBS_DROPDOWNLIST)
	{
		T* pT = static_cast<T*>(this);
		// Use built-in WTL toolbar wrapper class
		CToolBarCtrl toolbar(hWndToolBar);
		// Get the size of the combobox font
		CreateComboFont();
		CSize sizeFont = GetComboFontSize();
		// Compute the width and height
		UINT cx = (nWidth + 8) * sizeFont.cx;
		UINT cy = nHeight * sizeFont.cy;
		// Set the button width
		CTBButtonInfo tbi(TBIF_SIZE|TBIF_STATE|TBIF_STYLE);
		// Make sure the underlying button is disabled
		tbi.fsState = 0;
		// BTNS_SHOWTEXT will allow the button size to be altered
		tbi.fsStyle = BTNS_SHOWTEXT;
		tbi.cx = static_cast<WORD>(cx);
		toolbar.SetButtonInfo(nID, &tbi);
		// Get the index of the toolbar button
		int nIndex = toolbar.CommandToIndex(nID);
		// Get the button rect
		CRect rc;
		toolbar.GetItemRect(nIndex, rc);
		rc.bottom = cy;
		// Create the combobox
		DWORD dwStyle = WS_CHILD|WS_VISIBLE|WS_VSCROLL|WS_TABSTOP|dwComboStyle;
		CComboBox combo;
		combo.Create(pT->m_hWnd, rc, NULL, dwStyle, 0, nID);
		combo.SetFont(m_fontCombo);
		combo.SetParent(toolbar);
		// The combobox might not be centred vertically, and we won't know the
		// height until it has been created.  Get the size now and see if it
		// needs to be moved.
		CRect rectToolBar;
		CRect rectCombo;
		toolbar.GetClientRect(&rectToolBar);		
		combo.GetWindowRect(rectCombo);
		// Get the different between the heights of the toolbar and
		// the combobox
		int nDiff = rectToolBar.Height() - rectCombo.Height();
		// If there is a difference, then move the combobox
		if (nDiff > 1)
		{
			toolbar.ScreenToClient(&rectCombo);
			combo.MoveWindow(rectCombo.left, rc.top + (nDiff / 2), rectCombo.Width(), rectCombo.Height());
		}
		return combo;
	}

	/// Create the font to use for comboboxes
	void CreateComboFont()
	{
		if (m_fontCombo == NULL)
		{
			NONCLIENTMETRICS ncm;
			ncm.cbSize = sizeof(NONCLIENTMETRICS);
			::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
			// Create menu font
			m_fontCombo.CreateFontIndirect(&ncm.lfMenuFont);
			ATLASSERT(m_fontCombo != NULL);
		}
	}

	/// Get the size of the default GUI font
	CSize GetComboFontSize()
	{
		ATLASSERT(m_fontCombo != NULL);
		// We need a temporary DC
		const T* pT = static_cast<const T*>(this);
		CClientDC dc(pT->m_hWnd);
		// Select in the menu font
		CFontHandle fontOld = dc.SelectFont(m_fontCombo);
		// Get the font size
		TEXTMETRIC tm;
		dc.GetTextMetrics(&tm);
		// Done with the font
		dc.SelectFont(fontOld);
		// Return the width and height
		return CSize(tm.tmAveCharWidth, tm.tmHeight + tm.tmExternalLeading);
	}

	LRESULT OnSelChangeToolBarCombo(WORD /*wNotifyCode*/, WORD wID, HWND hWndCtl, BOOL& /*bHandled*/)
	{
		T* pT = static_cast<T*>(this);
		// Get the newly selected item index
		CComboBox combo(hWndCtl);
		int nSel = combo.GetCurSel();
		// Get the item text
		CString strItemText;
		combo.GetLBText(nSel, strItemText);
		// Get the item data
		DWORD dwItemData = combo.GetItemData(nSel);
		// Call special function to handle the selection change
		pT->OnToolBarCombo(combo, wID, nSel, strItemText, dwItemData);
		// Set focus to the main window
		pT->SetFocus();
		return TRUE;
	}
};

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)



Comments and Discussions