📄 toolbarex.cpp
字号:
/////////////////////////////////////////////////////////////////////////////
// Copyright (C) 1997 by Joerg Koenig
// All rights reserved
//
// Distribute freely, except: don't remove my name from the source or
// documentation (don't take credit for my work), mark your changes (don't
// get me blamed for your possible bugs), don't alter or remove this
// notice.
// No warrantee of any kind, express or implied, is included with this
// software; use at your own risk, responsibility for damages (if any) to
// anyone resulting from the use of this software rests entirely with the
// user.
//
// Send bug reports, bug fixes, enhancements, requests, flames, etc., and
// I'll try to keep a version up to date. I can be reached as follows:
// J.Koenig@adg.de (company site)
// Joerg.Koenig@rhein-neckar.de (private site)
/////////////////////////////////////////////////////////////////////////////
// ToolBarEx.cpp : implementation file
//
// Description:
// CToolBarEx provides additional features to the standard toolbar
// "CToolBar". The main addition is the flat mode (last seen in
// Developer Studio 5.0).
// There are no special requirements for having the flat mode in your
// application (no special comctl32.dll or what ever)!
// If you need custom draw abilities, then you have to use VC++ >= 4.2
// However, the flat mode should work with older versions of VC++ too (let
// me know of your experiences!)
//
// Usage:
// The only task you have to perform, is to
// #include "ToolBarEx.h"
// in either StdAfx.h or MainFrm.h and to change the type of
// CMainFrame::m_wndToolBar from CToolBar to CToolBarEx.
// Don't forget to recompile :-)
//
// Acknowledgements:
// o The main idea of how to draw a separator and the gripper is stolen
// from Roger Onslow's MSIE flat toolbar.
// Thanks for saving my time, Roger ;-)
// o The embossed drawing of a disabled image came from
// Victor M. Vogelpoel (victorv@telic.nl)
// o Some hints for buttons with text came from
// David Bates (bates@econ.ubc.ca)
// (I'm still thinking, text on toolbar-buttons is broken.
// That has to be tooltip's work. However, texts on buttons
// now work)
// o Thanks to Patrick Liechty (patrickl@code3.code3.com) for
// the reports of his experiences with VC++ 4.0/4.1
// o Thanks to Jeng-Yuan Sheu (m8501511@chu.edu.tw) for the
// enhanced checked button.
// o Thanks to Todd C. Wilson (todd@mediatec.com) for his
// bug report and code-enhancement for users of VC++ 4.2b
//
//
// (known) bugs and limitations:
// o the CDRF_NEWFONT notification is still untested ...
// o Assigning texts to buttons may cause the buttons to
// resize horizontally without notified by CToolBar. This
// leads to a wrong calculation inside CalcDynamicLayout()
// and CalcFixedLayout(). One could override both these
// functions in derived classes to avoid that problem,
// but that would be a greater pain ...
// o some features of the toolbars seen in Office97/DevStudio
// are not implemented (for instance text-only buttons or
// the way how image+text buttons are displayed. The Alt-drag
// feature too is unimplemented)
//
// if you find others (and have a solution for them ?!), please let me know:
// Joerg.Koenig@rhein-neckar.de (private site) or
// J.Koenig@adg.de (company site)
//
// Changes:
// 11/25/97
// o Some minor modifications to compile with VC++ 4.0/4.1 and 4.2b
// o checked buttons now look hilighted (as in Office97/DevStudio)
//
// 11/07/97
// (2 minor bugs have been occured as a result of the last update :)
// o The WRAP state of a separator will be ignored now, if
// the bar is docked vertically
// o Draw an image transparently. This is needed only if one
// uses 256 color images.
//
// 10/30/97
// o texts on buttons now work
// o gripper improved for a closer look like Office97
// o disabled images now look embossed
// o a separator is drawn only if it has no WRAP state set
#include "stdafx.h"
#include "ToolBarEx.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// local helper class CCustomDrawInfo
//
// The helper class CCustomDrawInfo handles the messaging to the docking
// frame of the toolbar in flat mode only. If flat-mode is disabled, then
// MFC's own messanger will be used.
//
// A few words about custom draw on toolbars:
// o custom draw is possible for MFC >= 4.2 only (older versions don't know
// anything about certain structures ...)
// o MFC does not set the "rc" member of NMCUSTOMDRAW to the rectangle of the
// button that will be drawn. However, we do, so watch out, wether the
// toolbar is flat or not (or ignore the "rc" member in both cases).
// If the current mode is not "flat", then MFC's art of message arrives ...
// o MFC does not send a message for separators, so we too don't do it.
// o It seems that MFC toolbars never send *ERASE notifications; instead they
// send TBN_QUERYDELETE for instance.
// o The CDRF_NEWFONT notification result is ignored (in flat mode. Never
// tried with original MFC, because it is broken on toolbars).
/////////////////////////////////////////////////////////////////////////////
class CCustomDrawInfo
{
#if _MFC_VER >= 0x0420
NMCUSTOMDRAW m_CDRW; // custom draw information holder
LRESULT m_PrePaint; // result from prepaint notification
LRESULT m_ItemPrePaint; // dito for specific item
CToolBarEx * m_pToolBar; // the real sender of the notification
CWnd * m_pReceiver; // the receiver of the notification
LRESULT NotifyParent();
#endif // _MFC_VER
public: // construction
CCustomDrawInfo( CDC & dc, CToolBarEx * pToolBar );
public:
// NotifyItemPrePaint() returns TRUE,
// if the user wants to do the default
// (CDRF_DODEFAULT) or FALSE, if the
// user wants to skip (CDRF_SKIPDEFAULT)
// Note that CDRF_SKIPDEFAULT is not
// allowed for CDDS_PREPAINT, CDDS_POSTPAINT !
// and CDDS_ITEMPOSTPAINT
void NotifyPrePaint();
BOOL NotifyItemPrePaint(int item);
void NotifyItemPostPaint(int item);
void NotifyPostPaint();
};
#if _MFC_VER >= 0x420
LRESULT CCustomDrawInfo :: NotifyParent()
{
LRESULT lRes = CDRF_DODEFAULT;
if( m_pReceiver )
{
lRes = m_pReceiver->SendMessage(WM_NOTIFY,
WPARAM(m_CDRW.hdr.idFrom),
LPARAM(&m_CDRW));
}
return lRes;
}
CCustomDrawInfo :: CCustomDrawInfo( CDC & dc, CToolBarEx * pBar )
: m_PrePaint(0), m_ItemPrePaint(0)
{
VERIFY((m_pToolBar = pBar) != 0);
VERIFY((m_CDRW.hdc = dc.GetSafeHdc()) != 0);
HWND hwnd = pBar->GetSafeHwnd();
VERIFY(::IsWindow(hwnd));
// initialise the NMHDR member of the customdraw structure
m_CDRW.hdr.hwndFrom = hwnd;
m_CDRW.hdr.idFrom = UINT(::GetWindowLong(hwnd, GWL_ID));
m_CDRW.hdr.code = NM_CUSTOMDRAW;
// Do not use CControlBar::GetDockingFrame() to receive
// the parent. CWnd::GetParent() is inacceptable too.
// Both these functions don't work, if the toolbar is
// floating in the air!
m_pReceiver = pBar->GetParentFrame();
if( m_pReceiver )
VERIFY(::IsWindow(m_pReceiver->GetSafeHwnd()));
}
void CCustomDrawInfo :: NotifyPrePaint()
{
// fill the customdraw structure with values for CDDS_PREPAINT
m_CDRW.dwDrawStage = CDDS_PREPAINT;
// the rest of the structure stays undefined in this stage
// of drawing.
m_PrePaint = NotifyParent();
}
BOOL CCustomDrawInfo :: NotifyItemPrePaint( int nItem )
{
BOOL bRet = TRUE; // we assume to do the default
if( m_PrePaint & CDRF_NOTIFYITEMDRAW )
{
m_CDRW.dwDrawStage = CDDS_ITEMPREPAINT;
m_pToolBar->GetItemRect(nItem, &m_CDRW.rc);
m_CDRW.dwItemSpec = DWORD(m_pToolBar->GetItemID(nItem));
UINT uStyle = m_pToolBar->GetButtonStyle(nItem);
BOOL bEnable = m_pToolBar->GetToolBarCtrl()
.IsButtonEnabled(m_CDRW.dwItemSpec);
m_CDRW.uItemState = (bEnable ? 0 : CDIS_DISABLED) |
(((uStyle & TBBS_PRESSED) || (uStyle & TBBS_CHECKED)) ?
CDIS_CHECKED : 0);
m_CDRW.lItemlParam = 0;
m_ItemPrePaint = NotifyParent();
if( m_ItemPrePaint & CDRF_SKIPDEFAULT )
bRet = FALSE;
}
return bRet;
}
void CCustomDrawInfo :: NotifyItemPostPaint( int nItem )
{
if( m_ItemPrePaint & CDRF_NOTIFYPOSTPAINT )
{
m_CDRW.dwDrawStage = CDDS_ITEMPOSTPAINT;
// the rest of the data has not been changed since ITEMPREPAINT
// make sure it is so:
ASSERT(m_pToolBar->GetItemID(nItem) == m_CDRW.dwItemSpec);
NotifyParent();
}
}
void CCustomDrawInfo :: NotifyPostPaint()
{
if( m_PrePaint & CDRF_NOTIFYPOSTPAINT )
{
m_CDRW.dwDrawStage = CDDS_POSTPAINT;
NotifyParent();
}
}
#else // _MFC_VER < 4.2
CCustomDrawInfo :: CCustomDrawInfo( CDC & dc, CToolBarEx * pParent )
{
}
void CCustomDrawInfo :: NotifyPrePaint()
{
}
void CCustomDrawInfo :: NotifyPostPaint()
{
}
BOOL CCustomDrawInfo :: NotifyItemPrePaint( int )
{
return TRUE; // we always make the drawing by ourself
}
void CCustomDrawInfo :: NotifyItemPostPaint( int )
{
}
#endif // _MFC_VER
/////////////////////////////////////////////////////////////////////////////
// CToolBarEx
CToolBarEx::CToolBarEx()
: m_bFlatLook(TRUE)
, m_clrBtnFace(::GetSysColor(COLOR_BTNFACE))
, m_clrBtnHilight(::GetSysColor(COLOR_BTNHILIGHT))
, m_clrBtnShadow(::GetSysColor(COLOR_BTNSHADOW))
, m_clrBtnLight(::GetSysColor(COLOR_3DLIGHT))
, m_nLastBtn(-1)
, m_uTimerEvent(0)
{
// the systems I've tested, made no difference between
// COLOR_BTNFACE and COLOR_3DLIGHT ...
if( m_clrBtnFace == m_clrBtnLight )
m_clrBtnLight = m_clrBtnHilight;
CalculateOffset();
// create the default font, used for buttons with text
CFont Font;
BOOL bOldSys = FALSE;
if( ! Font.CreateStockObject( DEFAULT_GUI_FONT ) )
{
// older versions of Windows* (NT 3.51 for instance)
// fail with DEFAULT_GUI_FONT
VERIFY( Font.CreateStockObject( SYSTEM_FONT ) );
bOldSys = TRUE;
}
LOGFONT logfont ;
Font.GetLogFont( &logfont ) ;
if( bOldSys )
{
logfont.lfWeight = 400;
strcpy(logfont.lfFaceName,"MS Sans Serif");
}
logfont.lfHeight = 6 ;
logfont.lfWidth = 0 ; // let windows compute this.
VERIFY( m_GuiFont.CreateFontIndirect( &logfont ) ) ;
}
CToolBarEx::~CToolBarEx()
{
}
IMPLEMENT_DYNAMIC(CToolBarEx, CToolBar)
BEGIN_MESSAGE_MAP(CToolBarEx, CToolBar)
//{{AFX_MSG_MAP(CToolBarEx)
ON_WM_PAINT()
ON_WM_SYSCOLORCHANGE()
ON_WM_NCCALCSIZE()
ON_WM_MOUSEMOVE()
ON_WM_NCPAINT()
ON_WM_TIMER()
ON_WM_CREATE()
//}}AFX_MSG_MAP
ON_MESSAGE(TB_SETBUTTONSIZE, OnSetButtonSize)
ON_MESSAGE(TB_SETBITMAPSIZE, OnSetBitmapSize)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CToolBarEx message handlers
LRESULT CToolBarEx :: OnSetButtonSize(WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = CToolBar::OnSetButtonSize(wParam, lParam);
if( lResult )
CalculateOffset();
return lResult;
}
LRESULT CToolBarEx :: OnSetBitmapSize(WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = CToolBar::OnSetBitmapSize(wParam, lParam);
if( lResult )
CalculateOffset();
return lResult;
}
void CToolBarEx::OnPaint()
{
HIMAGELIST hImg = GetImageList();
#ifdef _DEBUG
if( hImg == 0 )
{
TRACE0("CToolBarEx::OnPaint(): could not get image list\n");
}
#endif
if( m_bFlatLook && hImg )
{
CRect rcUpdate;
if( ! GetUpdateRect(rcUpdate) )
return;
if( HasButtonText() )
CalculateOffset(); // strings may have been added
// attach image-list for even more MFC feeling :)
CImageList imglist;
imglist.Attach(hImg);
POINT cursor;
::GetCursorPos(&cursor);
ScreenToClient(&cursor);
CPaintDC dc(this); // device context for painting
CFont * pOldFont = dc.SelectObject(&m_GuiFont);
// Now it's time for the first custom-draw-notification...
CCustomDrawInfo cdrw(dc, this);
cdrw.NotifyPrePaint();
register const int nBtn = GetToolBarCtrl().GetButtonCount();
for( register int i = 0; i < nBtn; ++i )
{
CRect rc;
GetItemRect(i, rc);
int nBitmap; UINT uID, uStyleState;
GetButtonInfo(i, uID, uStyleState, nBitmap);
WORD wStyle = LOWORD(uStyleState);
WORD wState = HIWORD(uStyleState);
if( wState & TBSTATE_HIDDEN )
continue;
if( wStyle == TBSTYLE_SEP )
{
if( !(wState & TBSTATE_WRAP) || ! IsFloating() )
DrawSeparator(dc, rc);
}
else
{
if( ! CRect().IntersectRect(rcUpdate, rc) )
continue; // this button needs no repaint
BOOL bBtnDown = (wState & TBSTATE_CHECKED) || (wState & TBSTATE_PRESSED);
BOOL bBtnEnabled = GetToolBarCtrl().IsButtonEnabled(int(uID));
BOOL bHasCursor = rc.PtInRect(cursor);
COLORREF clrRect = (bBtnDown && !bHasCursor) ? m_clrBtnLight : m_clrBtnFace;
// maybe the button has text
dc.SetTextColor(RGB(0,0,0));
dc.SetBkColor(clrRect);
// There is a bug in CToolBar: If there are texts assigned
// to buttons, then the button-widths may change transparently
// (without notified by CToolBar), so we recalculate the
// horizontal offset here:
m_sizeOffset.cx = (rc.Width() - m_sizeImage.cx) / 2;
if( ! cdrw.NotifyItemPrePaint(i) )
continue; // parent has already drawn the button
dc.FillSolidRect(rc, clrRect);
//CBrush brush(clrRect);
//dc.FillRect(rc, &brush);
// it seems, that CDC::Draw3dRect() changes the background color
COLORREF clrBk = dc.GetBkColor();
if( bBtnDown )
{
// draw a pressed button
dc.Draw3dRect(rc, m_clrBtnShadow, m_clrBtnHilight);
if( ! bHasCursor )
{
// draw an invisible frame around the hilighted area
CRect rcCheck = rc;
rcCheck.DeflateRect(1,1);
dc.Draw3dRect(rcCheck, m_clrBtnFace, m_clrBtnFace);
}
}
else
{
if( bHasCursor && ! bBtnDown && bBtnEnabled )
// draw a normal button
dc.Draw3dRect(rc, m_clrBtnHilight, m_clrBtnShadow);
else
{
if( ! bBtnDown && bBtnEnabled )
// Draw an invisible rect around the button.
// This prevents us from erasing the background
// if the button was formed before
// (that would cause the button to flicker ...)
dc.Draw3dRect(rc, m_clrBtnFace, m_clrBtnFace);
}
}
dc.SetBkColor(clrBk);
// the point where to start with the image
CPoint pt(rc.left + m_sizeOffset.cx + bBtnDown,
rc.top + m_sizeOffset.cy + bBtnDown);
imglist.Draw(&dc, nBitmap, pt, ILD_TRANSPARENT);
CString strText = GetButtonText(i);
if( strText.GetLength() )
{
CRect rectText(
rc.left+3+bBtnDown,
rc.top+m_sizeOffset.cy+m_sizeImage.cy+1+bBtnDown,
rc.right-3+bBtnDown,
rc.bottom-3+bBtnDown
);
dc.DrawText(strText, rectText, DT_CENTER|DT_VCENTER|DT_NOCLIP);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -