ToolBarEx.cpp
上传用户:diziting
上传日期:2007-01-02
资源大小:56k
文件大小:69k
- /////////////////////////////////////////////////////////////////////////////
- // Copyright (C) 1997,'98 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)
- /////////////////////////////////////////////////////////////////////////////
- // last revised: $Date: 10.05.98 17:45 $ $Revision: 2 $
- // 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 :-)
- // if you want to reduce flickering, then you have to include
- // the header of Keith Rule's CMemDC class in your <stdafx.h>.
- // CToolBarEx will use it automagically then.
- // If you want a "real cool" 3D look, then you have to exchange the call
- // to "EnableDocking()" in your CMainFrame::OnCreate() method to
- // "FrameEnableDocking()". This will ensure, that the toolbar has the
- // same outfit as in Office or DevStudio.
- //
- // 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
- // o Matthias Drigalla <matthias.drigalla@bluewin.ch> has pointed
- // me out, that the timer is not released in all circumstances.
- // o thanks to Jonathan Chin <jonathan.chin@scitec.com.au> for his
- // experiences with UNICODE and for his enhancement to conform to
- // the standard interface (the flat toolbar should not perform any
- // hit test (and hence displaying the raised button state) if the
- // parent window (application) does not have the focus or is disabled.)
- // o thanks to Wang Ruopeng (ripple@thinker.ep.tsinghua.edu.cn) for
- // the enhanced outfit of checked buttons
- //
- // o Many thanks to Victor Vogelpoel, Jonathan Chin and Patrick Liechty
- // for their help to test and correct some bugs in the enhanced version.
- // Their work made this class much cleaner. Thanks.
- //
- // o Wolfgang Loch (W.Loch@intershop.de) sent in a fix for undockable bars
- // o Thanks to John Armstrong (jarmstrong@runge.com.au) for his fix to
- // the adjustment of gripperspace in "classic" mode
- // o Thanks to Anatoly Ivasyuk (aivasyuk@clark.net) for a much cooler method
- // to draw checked buttons that does not have the cursor on it.
- //
- //
- // (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.
- //
- // 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:
- // 05/10/98
- // o buttons that are checked *and* disabled are now looking ok.
- // o don't draw a gripper if the bar is not dockable
- // o do not adjust space for gripper in "classic" mode
- //
- // 01/17/98
- // o If you have Keith Rule's "MemDC.h" included via
- // "stdafx.h", then CToolBarEx uses it to reduce
- // flickering.
- // o release the timer in all circumstances
- // o made the class compile even if UNICODE is #define'ed
- // o do not make any hit test (and display a raised button state)
- // if the application does not have the focus.
- // o Changed the outfit of a checked button that does not have
- // the cursor on it.
- // Major enhancements:
- // o added easy way to add controls to a toolbar
- // (both: replacing a button with a control and
- // inserting/appending new controls)
- // o Added many functions especially for the often
- // requested need for additional controls
- // o ALT-Drag feature implemented. You just can move buttons
- // (and controls!) from one CToolBarEx-object to another
- // in the style of the Developer Studio. There is no additional
- // requirement for having this feature in your application.
- // Just follow the online-help for how to enable customizable
- // toolbars. CToolBarEx uses the same technique.
- //
- // 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"
- #ifndef __AFXPRIV_H__
- #include <afxpriv.h> // one of the most interesting headers ;-)
- // if you plan to change CToolBarEx, then you
- // should better insert it in your "stdafx.h"
- // to speed up compilation
- #endif
- #ifdef _MEMDC_H_
- //#undef _MEMDC_H_ // this is for testing purposes
- #endif
- #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, whether 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 -- class static data
- HCURSOR CToolBarEx :: m_hOrigCursor = 0;
- HCURSOR CToolBarEx :: m_hDragCursor = 0;
- HCURSOR CToolBarEx :: m_hNoDragCursor = 0;
- BOOL CToolBarEx :: m_bDragging = FALSE;
- BOOL CToolBarEx :: m_bDragCursor = FALSE;
- int CToolBarEx :: m_nDragButton = -1;
- CToolBarEx * CToolBarEx :: m_pDropBar = 0;
- CPoint CToolBarEx :: m_ptDrop(0,0);
- /////////////////////////////////////////////////////////////////////////////
- // 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)
- , m_pControls(0)
- , m_bReal3DBorder(FALSE) // set to TRUE, if you're using FrameEnableDocking()
- , m_bDragChild(FALSE)
- {
- 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;
- _tcscpy(logfont.lfFaceName,_T("MS Sans Serif"));
- }
- logfont.lfHeight = 6 ;
- logfont.lfWidth = 0 ; // let windows compute this.
- VERIFY( m_GuiFont.CreateFontIndirect( &logfont ) ) ;
- }
- CToolBarEx::~CToolBarEx()
- {
- if( m_pControls ) {
- for( POSITION pos = m_pControls->GetHeadPosition() ; pos ; )
- delete m_pControls->GetNext(pos);
- delete m_pControls;
- }
- }
- 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()
- ON_WM_LBUTTONDOWN()
- ON_WM_LBUTTONUP()
- ON_WM_WINDOWPOSCHANGING()
- ON_WM_CAPTURECHANGED()
- ON_WM_PARENTNOTIFY()
- ON_WM_KILLFOCUS()
- //}}AFX_MSG_MAP
- ON_MESSAGE(TB_SETBUTTONSIZE, OnSetButtonSize)
- ON_MESSAGE(TB_SETBITMAPSIZE, OnSetBitmapSize)
- ON_MESSAGE(TB_ADDBITMAP, OnAddBitmap)
- ON_MESSAGE(TB_DELETEBUTTON, OnDeleteButton)
- #ifdef _MEMDC_H_
- ON_WM_ERASEBKGND()
- #endif
- 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 :: SetFlatLook( BOOL bFlat ) {
- if( bFlat != m_bFlatLook ) {
- m_bFlatLook = bFlat;
- if( ::IsWindow(GetSafeHwnd()) ) {
- //flat tool bars have gripper space at the left, cause non-client to resize
- SetWindowPos(0, 0,0,0,0,SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER);
- // force a repaint of all buttons
- Invalidate();
- // erase/draw the gripper
- OnNcPaint();
- // if the "FrameEnableDocking()" function is used, we have to adjust
- // the bars inside the parent.
- VERIFY(::IsWindow(m_hwndParent));
- ((CFrameWnd*)(CWnd::FromHandle(m_hwndParent)))->RecalcLayout();
- }
- }
- }
- void CToolBarEx::OnPaint()
- {
- HIMAGELIST hImg = GetImageList();
- #ifdef _DEBUG
- if( hImg == 0 ) {
- TRACE0("CToolBarEx::OnPaint(): could not get image listn");
- }
- #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);
- #ifdef _MEMDC_H_
- // if you have Keith Rule's CMemDC class inserted
- // in "stdafx.h", then we use it here ...
- CPaintDC dcp(this);
- CMemDC dc(&dcp);
- dc.FillSolidRect(rcUpdate, m_clrBtnFace);
- #else // _MEMDC_H_
- CPaintDC dc(this); // device context for painting
- #endif // _MEMDC_H_
- 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);
- if( HasButtonText() )
- // 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);
- // 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 ) {
- // if the button does not have the cursor on it,
- // then the pressed button is somewhat lighter
- // then the other buttons.
- CRect rcCheck = rc;
- rcCheck.DeflateRect(1,1);
- // draw an invisible frame around the hilighted area
- dc.Draw3dRect(rcCheck, m_clrBtnFace, m_clrBtnFace);
- rcCheck.DeflateRect(1,1);
- CBrush *pBrush = CDC::GetHalftoneBrush();
- dc.SetTextColor(m_clrBtnHilight);
- dc.SetBkColor(m_clrBtnFace);
- dc.FillRect(rcCheck, pBrush);
- dc.SetTextColor(RGB(0,0,0));
- dc.SetBkColor(clrRect);
- }
- } 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
- );
- int nBkMode = dc.SetBkMode(TRANSPARENT) ;
- dc.DrawText(strText, rectText, DT_CENTER|DT_VCENTER|DT_NOCLIP);
- dc.SetBkMode(nBkMode) ;
- }
-
- if( ! bBtnEnabled ) {
- // gray out that button
- rc.DeflateRect(bBtnDown,bBtnDown);
- DrawDisabledButton(dc, rc);
- }
- cdrw.NotifyItemPostPaint(i);
- }
- }
- dc.SelectObject(pOldFont);
- if( ! m_bDeleteImgList )
- imglist.Detach();
- // last but not least: inform the parent for end of painting
- cdrw.NotifyPostPaint();
- } else
- // classic mode (or couldn't receive imagelist)
- CToolBar::OnPaint();
- }
- void CToolBarEx :: DrawDisabledButton( CDC & dc, const CRect & rc ) const {
- // create a monochrome memory DC
- CDC ddc;
- ddc.CreateCompatibleDC(0);
- CBitmap bmp;
- bmp.CreateCompatibleBitmap(&ddc, rc.Width(), rc.Height());
- CBitmap * pOldBmp = ddc.SelectObject(&bmp);
-
- // build a mask
- ddc.PatBlt(0, 0, rc.Width(), rc.Height(), WHITENESS);
- dc.SetBkColor(m_clrBtnFace);
- ddc.BitBlt(0, 0, rc.Width(), rc.Height(), &dc, rc.left, rc.top, SRCCOPY);
- dc.SetBkColor(m_clrBtnHilight);
- ddc.BitBlt(0, 0, rc.Width(), rc.Height(), &dc, rc.left, rc.top, SRCPAINT);
- // Copy the image from the toolbar into the memory DC
- // and draw it (grayed) back into the toolbar.
- dc.FillSolidRect(rc.left, rc.top, rc.Width(), rc.Height(), m_clrBtnFace);
- dc.SetBkColor(RGB(0, 0, 0));
- dc.SetTextColor(RGB(255, 255, 255));
- CBrush brShadow, brHilight;
- brHilight.CreateSolidBrush(m_clrBtnHilight);
- brShadow.CreateSolidBrush(m_clrBtnShadow);
- CBrush * pOldBrush = dc.SelectObject(&brHilight);
- dc.BitBlt(rc.left+1, rc.top+1, rc.Width(), rc.Height(), &ddc, 0, 0, 0x00E20746L);
- dc.SelectObject(&brShadow);
- dc.BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &ddc, 0, 0, 0x00E20746L);
-
- // reset DCs
- dc.SelectObject(pOldBrush);
- ddc.SelectObject(pOldBmp);
- ddc.DeleteDC();
- bmp.DeleteObject();
- }
- void CToolBarEx :: DrawSeparator( CDC & dc, CRect & rc ) const {
- BOOL bHorz = ((m_dwStyle & CBRS_ORIENT_HORZ) != 0) ? TRUE : FALSE;
- // make sure, this separator is not a placeholder for
- // another control.
- if( rc.Width() <= 8 ) {
- if( bHorz ) {
- // draw the separator bar in the middle
- int x = (rc.left + rc.right) / 2;
- rc.left = x-1; rc.right = x+1;
- dc.Draw3dRect(
- rc,
- m_clrBtnShadow,
- m_clrBtnHilight
- );
- } else {
- // draw the separator bar in the middle
- rc.left = rc.left - m_sizeButton.cx;
- rc.right = rc.left + m_sizeButton.cx;
- rc.top = rc.bottom+1;
- rc.bottom = rc.top+3;
- int y = (rc.top+rc.bottom)/2;
- rc.top = y-1; rc.bottom = y+1;
- dc.Draw3dRect(
- rc,
- m_clrBtnShadow,
- m_clrBtnHilight
- );
- }
- }
- }
- void CToolBarEx :: DrawGripper( CDC & dc ) const {
- // Do not draw a gripper if the bar is floating or not
- // dockable.
- if( (m_dwStyle & CBRS_FLOATING) || m_dwDockStyle == 0 )
- return;
- CRect gripper;
- GetWindowRect(gripper);
- ScreenToClient(gripper);
- gripper.OffsetRect(-gripper.left, -gripper.top);
- if( m_dwStyle & CBRS_ORIENT_HORZ ) {
- // gripper at left
- gripper.DeflateRect(4, 4);
- gripper.right = gripper.left+3;
- dc.Draw3dRect(
- gripper,
- m_clrBtnHilight,
- m_clrBtnShadow
- );
- gripper.OffsetRect(3, 0);
- dc.Draw3dRect(
- gripper,
- m_clrBtnHilight,
- m_clrBtnShadow
- );
- } else {
- // gripper at top
- gripper.DeflateRect(4, 4);
- gripper.bottom = gripper.top+3;
- dc.Draw3dRect(
- gripper,
- m_clrBtnHilight,
- m_clrBtnShadow
- );
- gripper.OffsetRect(0, 3);
- dc.Draw3dRect(
- gripper,
- m_clrBtnHilight,
- m_clrBtnShadow
- );
- }
- }
- void CToolBarEx :: OnUpdateCmdUI( CFrameWnd* pTarget, BOOL bDisableIfNoHndler ) {
- if( m_bFlatLook ) {
- // save current styles
- register const int nBtn = GetToolBarCtrl().GetButtonCount();
- register int nIdx;
- for( nIdx = 0; nIdx < nBtn; ++nIdx )
- m_Styles.SetAtGrow(nIdx, GetButtonStyle(nIdx));
- // do base class processing
- CToolBar::OnUpdateCmdUI(pTarget,bDisableIfNoHndler);
- //check whether styles have been changed
- for( nIdx = 0; nIdx < nBtn; ++nIdx ) {
- if( m_Styles[nIdx] != GetButtonStyle(nIdx) )
- // invalidate that button
- InvalidateButton(nIdx);
- }
- } else
- // simply delegate
- CToolBar::OnUpdateCmdUI(pTarget,bDisableIfNoHndler);
- }
- void CToolBarEx::OnSysColorChange()
- {
- CToolBar::OnSysColorChange();
- m_clrBtnFace = ::GetSysColor(COLOR_BTNFACE);
- m_clrBtnHilight = ::GetSysColor(COLOR_BTNHILIGHT);
- m_clrBtnShadow = ::GetSysColor(COLOR_BTNSHADOW);
- m_clrBtnLight = ::GetSysColor(COLOR_3DLIGHT);
- }
- void CToolBarEx::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp)
- {
- CToolBar::OnNcCalcSize(bCalcValidRects, lpncsp);
- if( m_bFlatLook ) {
- // adjust non-client area for gripper at left or top
- if( m_dwStyle & CBRS_ORIENT_HORZ ) {
- lpncsp->rgrc[0].left += 4;
- lpncsp->rgrc[0].right += 4;
- } else {
- lpncsp->rgrc[0].top += 6;
- lpncsp->rgrc[0].bottom += 6;
- }
- }
- }
- void CToolBarEx::OnMouseMove(UINT nFlags, CPoint point)
- {
- if( m_bDragging ) {
- DragMove();
- return;
- }
- if( m_bFlatLook && IsTopParentActive() && GetTopLevelParent()->IsWindowEnabled()) {
- register const int nBtn = GetToolBarCtrl().GetButtonCount();
- const int nLastBtn = m_nLastBtn;
- m_nLastBtn = -1;
- for( register int i = 0 ; i < nBtn ; ++i ) {
- CRect rc;
- GetItemRect(i, rc);
- const BOOL bBtnEnabled = GetToolBarCtrl().IsButtonEnabled(int(GetItemID(i)));
- const BOOL bSep = GetButtonStyle(i) & TBBS_SEPARATOR;
- if( bSep || ! bBtnEnabled )
- continue;
- const BOOL bHasCursor = rc.PtInRect(point);
- if( bHasCursor && bBtnEnabled ) {
- if( nLastBtn != i ) {
- // force a repaint of the button with the cursor on it
- InvalidateRect(rc, FALSE);
- }
- m_nLastBtn = i;
- } else if( !bHasCursor && i == nLastBtn ) {
- // force a repaint of the last formed button
- InvalidateRect(rc, FALSE);
- }
- }
- // One problem occures with WM_MOUSEMOVE: we cannot detect
- // that the mouse leaves the window. If the mouse moves quick
- // enough, then the last formed button stays visible. To
- // resolve this problem, we set a timer and check, whether
- // the mouse is outside the window ...
- KillTimer(m_uTimerEvent);
- m_uTimerEvent = SetTimer(1, 250, 0);
- }
- CToolBar::OnMouseMove(nFlags, point);
- }
- void CToolBarEx::OnNcPaint()
- {
- if( m_bFlatLook ) {
- // get window DC that is clipped to the non-client area
- CWindowDC dc(this);
- CRect rectClient;
- GetClientRect(rectClient);
- CRect rectWindow;
- GetWindowRect(rectWindow);
- ScreenToClient(rectWindow);
- rectClient.OffsetRect(-rectWindow.left, -rectWindow.top);
- dc.ExcludeClipRect(rectClient);
- // draw borders in non-client area
- rectWindow.OffsetRect(-rectWindow.left, -rectWindow.top);
- Draw3DBorders(&dc, rectWindow);
- dc.IntersectClipRect(rectWindow);
- #ifdef _MEMDC_H_
- // You're using Keith Rule's CMemDC. In this case
- // we have to make sure that WM_ERASEBKGND
- // will not be sent.
- dc.FillSolidRect(rectWindow, m_clrBtnFace);
- #else
- // erase parts not drawn
- SendMessage(WM_ERASEBKGND, (WPARAM)dc.m_hDC);
- #endif
- DrawGripper(dc);
- } else
- CToolBar::OnNcPaint();
- }
- void CToolBarEx :: Draw3DBorders(CDC * pDC, CRect & rect) {
- ASSERT_VALID(this);
- ASSERT_VALID(pDC);
- if( m_bReal3DBorder ) {
- DWORD dwStyle = m_dwStyle;
- if (!(dwStyle & CBRS_BORDER_ANY))
- return;
- COLORREF clr = (m_dwStyle & CBRS_BORDER_3D) ? m_clrBtnHilight : m_clrBtnShadow;
- if(m_dwStyle & CBRS_BORDER_LEFT)
- pDC->FillSolidRect(0, 0, 1, rect.Height() - 1, clr);
- if(m_dwStyle & CBRS_BORDER_TOP)
- pDC->FillSolidRect(0, 0, rect.Width()-1 , 1, clr);
- if(m_dwStyle & CBRS_BORDER_RIGHT)
- pDC->FillSolidRect(rect.right, 1, -1, rect.Height() - 1, m_clrBtnShadow);
- if(m_dwStyle & CBRS_BORDER_BOTTOM)
- pDC->FillSolidRect(0, rect.bottom, rect.Width()-1, -1, m_clrBtnShadow);
- // if undockable toolbar at top of frame, apply special formatting to mesh
- // properly with frame menu
- if(!m_pDockContext) {
- pDC->FillSolidRect(0,0,rect.Width(),1,m_clrBtnShadow);
- pDC->FillSolidRect(0,1,rect.Width(),1,m_clrBtnHilight);
- }
- if (dwStyle & CBRS_BORDER_LEFT)
- ++rect.left;
- if (dwStyle & CBRS_BORDER_TOP)
- ++rect.top;
- if (dwStyle & CBRS_BORDER_RIGHT)
- --rect.right;
- if (dwStyle & CBRS_BORDER_BOTTOM)
- --rect.bottom;
- } else
- DrawBorders(pDC, rect);
- }
- void CToolBarEx::OnTimer(UINT nIDEvent)
- {
- if( nIDEvent == m_uTimerEvent ) {
- if( m_nLastBtn >= 0 ) {
- POINT pt;
- ::GetCursorPos(&pt);
- CRect rc;
- GetWindowRect(rc);
- if( ! rc.PtInRect(pt) ) {
- InvalidateButton(m_nLastBtn);
- m_nLastBtn = -1;
- }
- }
- if( m_nLastBtn < 0 )
- KillTimer(nIDEvent);
- } else
- CToolBar::OnTimer(nIDEvent);
- }
- int CToolBarEx::OnCreate(LPCREATESTRUCT lpCreateStruct)
- {
- if (CToolBar::OnCreate(lpCreateStruct) == -1)
- return -1;
-
- // Save the parent at creation time. It may change, if
- // the toolbar is floating; but we want to know of the
- // "real" parent (for notification messages)!
- m_hwndParent = lpCreateStruct->hwndParent;
- return 0;
- }
- void CToolBarEx::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
- {
- CToolBar::OnWindowPosChanging(lpwndpos);
-
- // If moved just force a redraw. This stops the partial redraw bug.
- if( !(lpwndpos->flags & SWP_NOMOVE) )
- Invalidate(FALSE);
-
- }
- #define PADWIDTH(x) (((x)*8+31)&~31)/8
- HIMAGELIST CToolBarEx :: GetImageList() {
- m_bDeleteImgList = FALSE;
- HIMAGELIST hImg = 0;
- #ifdef TB_GETIMAGELIST
- // Some older versions of VC++ do not know of
- // the TB_GETIMAGELIST macro (defined in commctrl.h).
- hImg = HIMAGELIST(SendMessage(TB_GETIMAGELIST));
- #ifdef _DEBUG
- if( hImg == 0 ) {
- TRACE0("CToolBarEx::GetImageList(): could not get image listn");
- }
- #endif
- #endif // TB_GETIMAGELIST
- if( ! hImg ) {
- // comctl32.dll version prior to 4.70 doesn't know
- // anything of the TB_GETIMAGELIST message
- if( m_hbmImageWell != 0 ) {
- // Yep - we have a valid image.
- // But beware: Do not use this bitmap directly.
- // We make the copy by ourself. CopyImage() (for
- // instace) produces inacceptable copies under
- // some circumstances ...
- CImageList imglist;
- CBitmap bmp;
-
- // retrieve the size of the bitmap
- BITMAP bmHdr;
- ::GetObject(m_hbmImageWell, sizeof(BITMAP), &bmHdr);
-
- DWORD dwWidth, dwHeight = bmHdr.bmHeight;
-
- if (bmHdr.bmBitsPixel > 8)
- dwWidth = PADWIDTH(bmHdr.bmWidth * 3);
- else
- dwWidth = PADWIDTH(bmHdr.bmWidth);
-
- // copy the bitmap
- CClientDC cdc(this);
- CDC dc1, dc2;
- dc1.CreateCompatibleDC(&cdc);
- dc2.CreateCompatibleDC(&cdc);
- bmp.CreateCompatibleBitmap(&cdc, dwWidth, dwHeight);
- CBitmap * pOBmp = dc1.SelectObject(&bmp);
- HGDIOBJ hOObj = ::SelectObject(dc2.GetSafeHdc(), m_hbmImageWell);
- dc1.BitBlt(0,0,dwWidth,dwHeight,&dc2,0,0,SRCCOPY);
- ::SelectObject(dc2.GetSafeHdc(), hOObj);
- dc1.SelectObject(pOBmp);
- dc1.DeleteDC();
- dc2.DeleteDC();
- imglist.Create(m_sizeImage.cx, m_sizeImage.cy,TRUE,dwWidth/m_sizeImage.cx,1);
- imglist.SetBkColor(m_clrBtnFace);
- imglist.Add(&bmp,m_clrBtnFace);
- hImg = imglist.Detach();
- bmp.DeleteObject();
- m_bDeleteImgList = TRUE;
- }
- }
- return hImg;
- }
- void CToolBarEx::InvalidateButton(int nIndex) {
- if( nIndex < 0 || nIndex >= GetToolBarCtrl().GetButtonCount() )
- return;
- CRect rc;
- GetItemRect(nIndex, rc);
- InvalidateRect(rc, FALSE);
- }
- BOOL CToolBarEx :: IsSeparator(int nIndex) const {
- if( nIndex >= 0 && nIndex < GetToolBarCtrl().GetButtonCount() )
- if( GetButtonStyle(nIndex) == TBBS_SEPARATOR ) {
- // make sure this is a "real" separator
- CRect rc;
- GetItemRect(nIndex, rc);
- if( rc.Width() <= 8 )
- return TRUE;
- }
- return FALSE;
- }
- BOOL CToolBarEx :: IsControl(int nIndex) const {
- if( nIndex >= 0 && nIndex < GetToolBarCtrl().GetButtonCount() )
- if( GetButtonStyle(nIndex) == TBBS_SEPARATOR ) {
- // make sure this is a placeholder for a control
- CRect rc;
- GetItemRect(nIndex, rc);
- if( rc.Width() > 8 )
- return TRUE;
- }
- return FALSE;
- }
- CWnd * CToolBarEx :: GetControl(int idx, BOOL bIdxIsID) const {
- UINT uID = bIdxIsID ? UINT(idx) : GetItemID(idx);
- for( CWnd * pWnd = GetWindow(GW_CHILD); pWnd; pWnd = pWnd->GetNextWindow() )
- if( UINT(::GetWindowLong(pWnd->GetSafeHwnd(), GWL_ID)) == uID )
- return pWnd;
- return 0;
- }
- CWnd * CToolBarEx :: CtrlReplace(
- CRuntimeClass * pClass,
- CRect & rc,
- UINT ID,
- DWORD dwStyle ) {
- int nIndex = CommandToIndex(ID);
- if(nIndex < 0) {
- TRACE1("CToolBarEx::CtrlReplace(): 0x%x is not a valid ID for this toolbar.n,", ID);
- return 0;
- }
- int nWidth = rc.Width();
- if( nWidth < 0 )
- nWidth = -nWidth;
- SetButtonInfo(nIndex, ID, TBBS_SEPARATOR, nWidth);
- CWnd * pCtrl = CreateControl(pClass, rc, ID, dwStyle);
- return pCtrl;
- }
- CWnd * CToolBarEx :: CtrlInsert(
- CRuntimeClass * pClass,
- CRect & rc,
- UINT ID,
- int nBefore,
- DWORD dwStyle) {
- BOOL bAppend = FALSE;
- CToolBarCtrl & wndToolBarCtrl = GetToolBarCtrl();
- if( nBefore < 0 || nBefore > wndToolBarCtrl.GetButtonCount() )
- bAppend = TRUE;
- int nWidth = rc.Width();
- if( nWidth < 0 )
- nWidth = -nWidth;
- TBBUTTON tbButton;
- tbButton.iBitmap = nWidth;
- tbButton.idCommand = ID;
- tbButton.fsState = TBSTATE_ENABLED;
- tbButton.fsStyle = TBSTYLE_SEP;
- tbButton.dwData = 0;
- tbButton.iString = 0;
- if( bAppend )
- wndToolBarCtrl.AddButtons(1, &tbButton);
- else
- wndToolBarCtrl.InsertButton(nBefore, &tbButton);
- CWnd * pCtrl = CreateControl(pClass, rc, ID, dwStyle);
- return pCtrl;
- }
- CWnd * CToolBarEx :: CreateControl(
- CRuntimeClass * pClass,
- CRect & rc,
- UINT ID,
- DWORD dwStyle) {
- if( ! pClass || ! pClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)) ) {
- TRACE0("CToolBarEx::CreateControl(): given class is NULL or not derived from CWnd.n");
- return 0;
- }
- CWnd * pCtrl = 0;
- BOOL bSelfDeleting = TRUE;
- CString strClass = pClass->m_lpszClassName;
- if( strClass == TEXT("CComboBox") ) {
- pCtrl = new CComboBox();
- bSelfDeleting = FALSE;
- } else if( strClass == TEXT("CEdit") ) {
- pCtrl = new CEdit();
- bSelfDeleting = FALSE;
- } else {
- pCtrl = (CWnd *)pClass->CreateObject();
- if( pCtrl == 0 ) {
- TRACE1("CToolBarEx::CreateControl(): dynamic create of control %hs failed.n",
- pClass->m_lpszClassName);
- return 0;
- }
- }
- // create the control itself
- CRect rect = rc;
- BOOL bCreate = FALSE;
- if( pCtrl->IsKindOf(RUNTIME_CLASS(CComboBox)) )
- bCreate = ((CComboBox*)pCtrl)->Create(WS_CHILD|WS_VISIBLE|dwStyle, rect, this, ID);
- else if( pCtrl->IsKindOf(RUNTIME_CLASS(CEdit)) )
- bCreate = ((CEdit*)pCtrl)->Create(WS_CHILD|WS_VISIBLE|dwStyle, rect, this, ID);
- else
- bCreate = pCtrl->Create(0, 0, WS_CHILD|WS_VISIBLE|dwStyle, rect, this, ID);
-
- if( ! bCreate ) {
- TRACE1("CToolBarEx::CreateControl(): could not Create() control.n,", ID);
- if( ! bSelfDeleting )
- delete pCtrl;
- return 0;
- }
- if( ! bSelfDeleting ) {
- // we have to remember this control, so we can delete it later
- if( ! m_pControls )
- m_pControls = new CObList();
- m_pControls->AddTail(pCtrl);
- }
- RepositionControls();
- return pCtrl;
- }
- void CToolBarEx :: RepositionControls() {
- // Walk all descendents.
- for( CWnd * pWnd = GetWindow(GW_CHILD); pWnd; pWnd = pWnd->GetNextWindow() ) {
- // If that window is a child of this
- // toolbar, then reposition this control.
- if( pWnd->GetParent() == this ) {
- DWORD dwID = ::GetWindowLong(pWnd->GetSafeHwnd(), GWL_ID);
- int idx = CommandToIndex(dwID);
- ASSERT(idx >= 0);
- CRect rc;
- GetItemRect(idx, rc);
- pWnd->SetWindowPos(0, rc.left, rc.top, 0, 0, SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOCOPYBITS);
- pWnd->ShowWindow(SW_SHOW);
- }
- }
- }
- void CToolBarEx :: RecalcLayout() {
- // Recalculate the size of the bar.
- BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) != 0;
- if ((m_dwStyle & CBRS_FLOATING) && (m_dwStyle & CBRS_SIZE_DYNAMIC))
- CalcDynamicLayout(0, LM_HORZ | LM_MRUWIDTH | LM_COMMIT);
- else if (bHorz)
- CalcDynamicLayout(0, LM_HORZ | LM_HORZDOCK | LM_COMMIT);
- else
- CalcDynamicLayout(0, LM_VERTDOCK | LM_COMMIT);
- RepositionControls();
- // recalculate the parent frame
- if( m_dwStyle & CBRS_FLOATING ) {
- ASSERT(m_pDockBar != 0);
- ((CMiniDockFrameWnd *)m_pDockBar->GetParent())->RecalcLayout();
- } else {
- ((CFrameWnd*)GetParentFrame())->RecalcLayout();
- }
- }
- HBITMAP CToolBarEx :: GetBitmap(int nBtnID) {
- int nBitmap = SendMessage(TB_GETBITMAP, WPARAM(nBtnID));
- return GetBitmap(nBitmap, m_sizeImage);
- }
- HBITMAP CToolBarEx :: GetBitmap(int nIndex, const CSize & sizeBitmap) {
- HIMAGELIST hImgList = GetImageList();
- if( ! hImgList )
- return 0;
- CImageList imglist;
- imglist.Attach(hImgList);
- HICON hIcon = imglist.ExtractIcon(nIndex);
- CBitmap bmp;
- if( hIcon ) {
- CClientDC cdc(this) ;
- CDC dc;
- dc.CreateCompatibleDC(&cdc);
- VERIFY(bmp.CreateCompatibleBitmap(&cdc, sizeBitmap.cx, sizeBitmap.cy));
- CBitmap* pOldBmp = dc.SelectObject(&bmp);
- CBrush brush ;
- VERIFY(brush.CreateSolidBrush(m_clrBtnFace));
- ::DrawIconEx(
- dc.GetSafeHdc(),
- 0,
- 0,
- hIcon,
- sizeBitmap.cx,
- sizeBitmap.cy,
- 0,
- (HBRUSH)brush,
- DI_NORMAL
- );
- dc.SelectObject( pOldBmp );
- dc.DeleteDC();
- // the icon is not longer needed
- DestroyIcon(hIcon);
- } else
- TRACE1("CToolBarEx::GetBitmap(): unable to extract bitmap with index %dn", nIndex);
- if( ! m_bDeleteImgList )
- imglist.Detach();
- return hIcon ? HBITMAP(bmp.Detach()) : 0;
- }
- LRESULT CToolBarEx::OnAddBitmap(WPARAM wParam, LPARAM lParam) {
- // work around a bug in CToolBar:
- // if one calls CToolBar::GetToolBarCtrl().AddBitmap(...),
- // then CToolBar does not realize this. This can lead to
- // confusing button images ...
- int nButtons = int(wParam);
- LPTBADDBITMAP pAddBmp = LPTBADDBITMAP(lParam);
- if( pAddBmp->hInst != HINST_COMMCTRL ) {
- // This workaround does not work, if one
- // specifies "HINST_COMMCTRL" to the "hInst"
- // member of pAddBmp, because we cannot access
- // the internals of commctl32.dll ...
- TRACE0("Adding a bitmapn");
- HBITMAP hBitmap;
- if( pAddBmp->hInst != 0 )
- // Have to load the bitmap "pAddBmp->nID"
- // contains the resource-ID of the bitmap.
- hBitmap = ::LoadBitmap(pAddBmp->hInst, MAKEINTRESOURCE(pAddBmp->nID));
- else
- // "pAddBmp->nID" is the handle of the bitmap.
- hBitmap = HBITMAP(pAddBmp->nID);
- if(hBitmap == 0)
- return HRESULT(-1);
- // You really should use CToolBarEx as a replacement for
- // CToolBar. So make sure you have set up a toolbar
- // properly before you begin to customize it.
- ASSERT(m_hbmImageWell);
- // retrieve number of images currently stored in CToolBar
- BITMAP bitmap;
- VERIFY(::GetObject(m_hbmImageWell, sizeof(BITMAP), &bitmap));
- int nImageCount = bitmap.bmWidth / m_sizeImage.cx;
- CClientDC cdc(this);
- CDC dcOld, dcNew;
- dcOld.CreateCompatibleDC(&cdc);
- dcNew.CreateCompatibleDC(&cdc);
- HGDIOBJ hOldBmp = ::SelectObject(dcOld.GetSafeHdc(), m_hbmImageWell);
- // create the new bitmap and make it big enough to
- // hold all images (old+new)
- CBitmap bmpNew;
- bmpNew.CreateCompatibleBitmap(
- &cdc,
- bitmap.bmWidth + nButtons * m_sizeImage.cx,
- m_sizeImage.cy);
- CBitmap * pbmpNew = dcNew.SelectObject(&bmpNew);
- dcNew.BitBlt(0,0,bitmap.bmWidth,bitmap.bmHeight,&dcOld,0,0,SRCCOPY);
- ::SelectObject(dcOld.GetSafeHdc(), hBitmap);
- dcNew.BitBlt(bitmap.bmWidth,0,m_sizeImage.cx*nButtons,bitmap.bmHeight,&dcOld,0,0,SRCCOPY);
- ::SelectObject(dcOld.GetSafeHdc(), hOldBmp);
- dcNew.SelectObject(pbmpNew);
- dcOld.DeleteDC();
- dcNew.DeleteDC();
- VERIFY(bmpNew.GetObject(sizeof(BITMAP), &bitmap));
- HRESULT hRes = DefWindowProc(TB_ADDBITMAP, wParam, lParam);
- // syncronize toolbarcontrol's bitmap with our's
- AddReplaceBitmap(HBITMAP(bmpNew.Detach()));
- return HRESULT(nImageCount);
- }
- return DefWindowProc(TB_ADDBITMAP, wParam, lParam);
- }
- // intercept TB_DELETEBUTTON, so we can delete controls too.
- LRESULT CToolBarEx::OnDeleteButton(WPARAM wParam, LPARAM lParam) {
- CWnd * pControl = GetControl(int(wParam));
- if( pControl ) {
- // this is the control associated with the button to delete
- BOOL bMustDelete = FALSE;
- if( m_pControls ) {
- // It is really a good idea to add a control via the
- // CToolBarEx own members. This will guarantee that
- // all resources are freed.
- POSITION pos = m_pControls->Find(pControl);
- if( pos ) {
- m_pControls->RemoveAt(pos);
- bMustDelete = TRUE;
- }
- }
- pControl->DestroyWindow();
- if( bMustDelete )
- delete pControl;
- }
- return DefWindowProc(TB_DELETEBUTTON, wParam, lParam);
- }
- #ifdef _MEMDC_H_
- BOOL CToolBarEx::OnEraseBkgnd(CDC* pDC)
- {
- return IsFlatLook() ? FALSE : CToolBar::OnEraseBkgnd(pDC);
- }
- #endif
- /////////////////////////////////////////////////////////////////////////////
- // ALT-drag
- // To keep the users of CToolBarEx from inserting cursor-resources, we
- // create the cursors on the fly. This makes usage of CToolBarEx as easy as
- // possible:
- static const BYTE ANDmaskDrop[] = {
- 0xFF, 0xFF, 0xFF, 0xFF, // line 1
- 0xFF, 0xFF, 0xFF, 0xFF, // line 2
- 0xF3, 0xFF, 0xFF, 0xFF, // line 3
- 0xF1, 0xFF, 0xFF, 0xFF, // line 4
-
- 0xF0, 0xFF, 0xFF, 0xFF, // line 5
- 0xF0, 0x7F, 0xFF, 0xFF, // line 6
- 0xF0, 0x3F, 0xFF, 0xFF, // line 7
- 0xF0, 0x1F, 0xFF, 0xFF, // line 8
-
- 0xF0, 0x0F, 0xFF, 0xFF, // line 9
- 0xF0, 0x07, 0xFF, 0xFF, // line 10
- 0xF0, 0x03, 0xFF, 0xFF, // line 11
- 0xF0, 0x01, 0xFF, 0xFF, // line 12
-
- 0xF0, 0x00, 0xFF, 0xFF, // line 13
- 0xF0, 0x0F, 0xFF, 0xFF, // line 14
- 0xF0, 0x0F, 0xFF, 0xFF, // line 15
- 0xF1, 0x07, 0xFF, 0xFF, // line 16
-
- 0xF3, 0x07, 0xFF, 0xFF, // line 17
- 0xF6, 0x00, 0x00, 0x3F, // line 18
- 0xFE, 0x00, 0x00, 0x3F, // line 19
- 0xFE, 0x00, 0x00, 0x3F, // line 20
-
- 0xFE, 0x00, 0x00, 0x3F, // line 21
- 0xFE, 0x00, 0x00, 0x3F, // line 22
- 0xFE, 0x00, 0x00, 0x3F, // line 23
- 0xFE, 0x00, 0x00, 0x3F, // line 24
-
- 0xFE, 0x00, 0x00, 0x3F, // line 25
- 0xFE, 0x00, 0x00, 0x3F, // line 26
- 0xFF, 0xFF, 0xFF, 0xFF, // line 27
- 0xFF, 0xFF, 0xFF, 0xFF, // line 28
-
- 0xFF, 0xFF, 0xFF, 0xFF, // line 29
- 0xFF, 0xFF, 0xFF, 0xFF, // line 30
- 0xFF, 0xFF, 0xFF, 0xFF, // line 31
- 0xFF, 0xFF, 0xFF, 0xFF // line 32
- };
-
- static const BYTE XORmaskDrop[] = {
- 0x00, 0x00, 0x00, 0x00, // line 1
- 0x00, 0x00, 0x00, 0x00, // line 2
- 0x00, 0x00, 0x00, 0x00, // line 3
- 0x04, 0x00, 0x00, 0x00, // line 4
-
- 0x06, 0x00, 0x00, 0x00, // line 5
- 0x07, 0x00, 0x00, 0x00, // line 6
- 0x07, 0x80, 0x00, 0x00, // line 7
- 0x07, 0xC0, 0x00, 0x00, // line 8
-
- 0x07, 0xE0, 0x00, 0x00, // line 9
- 0x07, 0xF0, 0x00, 0x00, // line 10
- 0x07, 0xF8, 0x00, 0x00, // line 11
- 0x07, 0xFC, 0x00, 0x00, // line 12
-
- 0x07, 0xE0, 0x00, 0x00, // line 13
- 0x07, 0x60, 0x00, 0x00, // line 14
- 0x06, 0x60, 0x00, 0x00, // line 15
- 0x04, 0x30, 0x00, 0x00, // line 16
-
- 0x00, 0x30, 0x00, 0x00, // line 17
- 0x00, 0x18, 0x00, 0x00, // line 18
- 0x00, 0xDD, 0xFF, 0x00, // line 19
- 0x00, 0xAC, 0xAA, 0x00, // line 20
-
- 0x00, 0xCD, 0x55, 0x00, // line 21
- 0x00, 0xA0, 0xAA, 0x00, // line 22
- 0x00, 0xD5, 0x55, 0x00, // line 23
- 0x00, 0xAA, 0xAA, 0x00, // line 24
-
- 0x00, 0x00, 0x00, 0x00, // line 25
- 0x00, 0x00, 0x00, 0x00, // line 26
- 0x00, 0x00, 0x00, 0x00, // line 27
- 0x00, 0x00, 0x00, 0x00, // line 28
-
- 0x00, 0x00, 0x00, 0x00, // line 29
- 0x00, 0x00, 0x00, 0x00, // line 30
- 0x00, 0x00, 0x00, 0x00, // line 31
- 0x00, 0x00, 0x00, 0x00 // line 32
- };
- static const BYTE ANDmaskNoDrop[] = {
- 0xFF, 0xFF, 0xFF, 0xFF, // line 1
- 0xFF, 0xFF, 0xFF, 0xFF, // line 2
- 0xF3, 0xFF, 0xFF, 0xFF, // line 3
- 0xF1, 0xFF, 0xFF, 0xFF, // line 4
-
- 0xF0, 0xFF, 0xFF, 0xFF, // line 5
- 0xF0, 0x7F, 0xFF, 0xFF, // line 6
- 0xF0, 0x3F, 0xFF, 0xFF, // line 7
- 0xF0, 0x1F, 0xFF, 0xFF, // line 8
-
- 0xF0, 0x0F, 0xFF, 0xFF, // line 9
- 0xF0, 0x07, 0xFF, 0xFF, // line 10
- 0xF0, 0x03, 0xFF, 0xFF, // line 11
- 0xF0, 0x01, 0xFF, 0xFF, // line 12
-
- 0xF0, 0x00, 0xFF, 0xFF, // line 13
- 0xF0, 0x0F, 0xFF, 0xFF, // line 14
- 0xF0, 0x0F, 0xFF, 0xFF, // line 15
- 0xF1, 0x07, 0xFF, 0xFF, // line 16
-
- 0xF3, 0x07, 0xFF, 0xFF, // line 17
- 0xF6, 0x00, 0x00, 0x3F, // line 18
- 0xFE, 0x00, 0x00, 0x3F, // line 19
- 0xFE, 0x00, 0x00, 0x3F, // line 20
-
- 0xFE, 0x00, 0x00, 0x3F, // line 21
- 0xFE, 0x00, 0x00, 0x0F, // line 22
- 0xFE, 0x00, 0x00, 0x0F, // line 23
- 0xFE, 0x00, 0x00, 0x0F, // line 24
-
- 0xFE, 0x00, 0x00, 0x0F, // line 25
- 0xFE, 0x00, 0x00, 0x0F, // line 26
- 0xFF, 0xFF, 0x80, 0x0F, // line 27
- 0xFF, 0xFF, 0x80, 0x0F, // line 28
-
- 0xFF, 0xFF, 0x80, 0x0F, // line 29
- 0xFF, 0xFF, 0x80, 0x0F, // line 30
- 0xFF, 0xFF, 0x80, 0x0F, // line 31
- 0xFF, 0xFF, 0x80, 0x0F // line 32
- };
-
- static const BYTE XORmaskNoDrop[] = {
- 0x00, 0x00, 0x00, 0x00, // line 1
- 0x00, 0x00, 0x00, 0x00, // line 2
- 0x00, 0x00, 0x00, 0x00, // line 3
- 0x04, 0x00, 0x00, 0x00, // line 4
-
- 0x06, 0x00, 0x00, 0x00, // line 5
- 0x07, 0x00, 0x00, 0x00, // line 6
- 0x07, 0x80, 0x00, 0x00, // line 7
- 0x07, 0xC0, 0x00, 0x00, // line 8
-
- 0x07, 0xE0, 0x00, 0x00, // line 9
- 0x07, 0xF0, 0x00, 0x00, // line 10
- 0x07, 0xF8, 0x00, 0x00, // line 11
- 0x07, 0xFC, 0x00, 0x00, // line 12
-
- 0x07, 0xE0, 0x00, 0x00, // line 13
- 0x07, 0x60, 0x00, 0x00, // line 14
- 0x06, 0x60, 0x00, 0x00, // line 15
- 0x04, 0x30, 0x00, 0x00, // line 16
-
- 0x00, 0x30, 0x00, 0x00, // line 17
- 0x00, 0x18, 0x00, 0x00, // line 18
- 0x00, 0xDD, 0xFF, 0x00, // line 19
- 0x00, 0xAC, 0xAA, 0x00, // line 20
-
- 0x00, 0xCD, 0x55, 0x00, // line 21
- 0x00, 0xA0, 0x80, 0x00, // line 22
- 0x00, 0xD5, 0x3F, 0xE0, // line 23
- 0x00, 0xAA, 0xA7, 0x20, // line 24
-
- 0x00, 0x00, 0x22, 0x20, // line 25
- 0x00, 0x00, 0x30, 0x60, // line 26
- 0x00, 0x00, 0x38, 0xE0, // line 27
- 0x00, 0x00, 0x30, 0x60, // line 28
-
- 0x00, 0x00, 0x22, 0x20, // line 29
- 0x00, 0x00, 0x27, 0x20, // line 30
- 0x00, 0x00, 0x3F, 0xE0, // line 31
- 0x00, 0x00, 0x00, 0x00 // line 32
- };
- void CToolBarEx::OnLButtonDown(UINT nFlags, CPoint point)
- {
- if( ::GetAsyncKeyState(VK_MENU) & (1<<15) )
- // one of the ALT keys is pressed too - begin drag operation
- if( BeginDrag() )
- return;
-
- if( ::GetAsyncKeyState(VK_SHIFT) & (1<<15) )
- // disable the old-style drag
- return;
-
- CToolBar::OnLButtonDown(nFlags, point);
- }
- void CToolBarEx::OnLButtonUp(UINT nFlags, CPoint point)
- {
- if( m_bDragging )
- EndDrag();
- else
- CToolBar::OnLButtonUp(nFlags, point);
- }
- BOOL CToolBarEx :: BeginDrag() {
- TRACE0("beginning drag operationn");
- VERIFY(!m_hDragCursor);
- VERIFY(!m_hNoDragCursor);
- if( !(::GetWindowLong(GetToolBarCtrl().GetSafeHwnd(),GWL_STYLE) & CCS_ADJUSTABLE) )
- return FALSE; // Bar is not adjustable
- register const int nBtn = GetToolBarCtrl().GetButtonCount();
- const int nLastBtn = m_nLastBtn;
- m_nLastBtn = -1;
- CPoint pt;
- ::GetCursorPos(&pt);
- ScreenToClient(&pt);
- m_nDragButton = -1;
- // have a look for whether the button is valid and
- // - if so - draw a dragging border around it.
- for( register int i = 0 ; i < nBtn ; ++i ) {
- if( IsSeparator(i) )
- continue; // real separators are not draggable
- CRect rc;
- GetItemRect(i, rc);
- const BOOL bHasCursor = rc.PtInRect(pt);
- if( bHasCursor ) {
- // OK we've found the button. Now ask for deletion:
- if( ! DoQueryDelete(i) )
- // the app does not allow the removal ...
- return FALSE;
- m_nDragButton = i;
- CClientDC cdc(this);
- cdc.DrawDragRect(rc, CSize(2,2),0,CSize(0,0));
- break;
- }
- }
- if( m_nDragButton < 0 )
- return FALSE; // nothing to drag ...
- VERIFY(m_hDragCursor = ::CreateCursor(0, 4, 2, 32, 32, ANDmaskDrop, XORmaskDrop));
- VERIFY(m_hNoDragCursor = ::CreateCursor(0, 4, 2, 32, 32, ANDmaskNoDrop, XORmaskNoDrop));
- // capture the mouse during the drag operation
- SetCapture();
- // make sure we receive keyboard-input
- SetFocus();
- m_hOrigCursor = ::SetCursor(m_hDragCursor);
- m_bDragCursor = TRUE;
- m_bDragging = TRUE;
- ::GetCursorPos(&pt);
- SetMarker(m_pDropBar=this, m_ptDrop=pt);
- return m_bDragging;
- }
- BOOL CToolBarEx :: IsValidDropTarget(const CWnd * pWnd) const {
- return (pWnd &&
- pWnd->IsKindOf(RUNTIME_CLASS(CToolBarEx)) &&
- (::GetWindowLong(((CToolBarEx*)pWnd)->GetToolBarCtrl()
- .GetSafeHwnd(),GWL_STYLE) & CCS_ADJUSTABLE) &&
- ((const CToolBarEx*)pWnd)->GetParentFrame() == GetParentFrame())
- ? TRUE : FALSE;
- }
- void CToolBarEx :: DragMove() {
- //TRACE0("dragmove in progressn");
- CPoint pt;
- ::GetCursorPos(&pt);
- const CWnd * pWnd = WindowFromPoint(pt);
- // is the cursor moving over an adjustable toolbar ?
- BOOL bToolBar = IsValidDropTarget(pWnd);
-
- // If the window under the cursor is not a toolbar, then
- // check whether this window is a child of a toolbar.
- while( ! bToolBar && (pWnd = pWnd->GetParent()) != 0 )
- bToolBar = IsValidDropTarget(pWnd);
- // check whether we have to switch the cursor
- if( bToolBar && ! m_bDragCursor ) {
- ::SetCursor(m_hDragCursor);
- m_bDragCursor = TRUE;
- } else if( ! bToolBar && m_bDragCursor ) {
- ::SetCursor(m_hNoDragCursor);
- m_bDragCursor = FALSE;
- }
- SetMarker(m_pDropBar = (bToolBar ? (CToolBarEx*)pWnd : 0), m_ptDrop = pt);
- }
- void CToolBarEx :: EndDrag(BOOL bDoMove) {
- TRACE0("ending drag operationn");
- // remove the marker
- SetMarker(0, CPoint(0,0));
- VERIFY(::SetCursor(m_hOrigCursor));
- ::DestroyCursor(m_hDragCursor);
- ::DestroyCursor(m_hNoDragCursor);
- m_hDragCursor = 0;
- m_hNoDragCursor = 0;
- if( m_nDragButton >= 0 ) {
- CToolBarCtrl & wndTBCtrl = GetToolBarCtrl();
- register const int nBtn = wndTBCtrl.GetButtonCount();
- if( m_bDragCursor && bDoMove ) {
- // move the button to a different location
- // make sure the last "DragMove()" has done its work correctly:
- ASSERT(m_pDropBar != 0);
- ASSERT_KINDOF(CToolBarEx, m_pDropBar);
- ASSERT(::GetWindowLong(m_pDropBar->GetToolBarCtrl()
- .GetSafeHwnd(),GWL_STYLE) & CCS_ADJUSTABLE);
- // have a look for where to drop the button
- int nDropBtn = m_pDropBar->FindDropButton(m_ptDrop);
- TBBUTTON tbButton;
- memset(&tbButton, 0, sizeof(TBBUTTON)); // not the safest, but the easiest way
- // to zero out all members ;-)
- if( m_pDropBar == this ) {
- // move the button around, but stay on *this* toolbar
- if( nDropBtn == m_nDragButton+1 || (nDropBtn < 0 && m_nDragButton == nBtn-1) ) {
- // simply insert a separator before the dragged button,
- // if there is still none
- if( m_nDragButton > 0 && !IsSeparator(m_nDragButton-1) ) {
- tbButton.iBitmap = 8;
- tbButton.fsState = TBSTATE_ENABLED;
- tbButton.fsStyle = TBSTYLE_SEP;
- if( DoQueryInsert(tbButton, m_nDragButton) )
- wndTBCtrl.InsertButton(m_nDragButton, &tbButton);
- }
- } else if( nDropBtn == m_nDragButton && m_nDragButton > 0 ) {
- // Remove the separator immediately before the dragged button.
- // if there is no such separator, then do nothing
- if( IsSeparator(nDropBtn-1) )
- if( DoQueryDelete(nDropBtn-1) )
- wndTBCtrl.DeleteButton(nDropBtn-1);
- } else {
- wndTBCtrl.GetButton(m_nDragButton, &tbButton);
- if( DoQueryInsert(tbButton, (nDropBtn>=0) ? nDropBtn : nBtn) ) {
- CWnd * pControl = 0;
- if( IsControl(m_nDragButton) ) {
- // Beware: The TB_DELETEBUTTON message causes the toolbar
- // to destroy the associated control.
- // To avoid this we temporary set the parent of the
- // control to NULL.
- pControl = GetControl(m_nDragButton);
- VERIFY(pControl != 0);
- pControl->SetParent(0);
- }
- if( nDropBtn >= 0 )
- // have to insert
- wndTBCtrl.InsertButton(nDropBtn, &tbButton);
- else
- // append the button
- wndTBCtrl.AddButtons(1, &tbButton);
- // delete the button at its original location
- // we do not need to ask the owner, because this
- // was already done in "BeginDrag()"
- wndTBCtrl.DeleteButton(
- (m_nDragButton < nDropBtn || nDropBtn < 0)
- ? m_nDragButton
- : m_nDragButton+1
- );
- // Reconnect the control (if any)
- if( pControl )
- pControl->SetParent(this);
- if( m_nDragButton == wndTBCtrl.GetButtonCount()-1 )
- // remove trailing separators too
- RemoveTrailingSeparators();
- }
- }
- } else {
- // move the button to a different toolbar
- wndTBCtrl.GetButton(m_nDragButton, &tbButton);
- CToolBarCtrl & wndDropTBCtrl = m_pDropBar->GetToolBarCtrl();
- if( m_pDropBar->DoQueryInsert(tbButton, (nDropBtn>=0) ? nDropBtn : wndDropTBCtrl.GetButtonCount()) ) {
- // Get the bitmap of the dragged button and resize it to
- // the image-size of the destination-bar.
- int nDestBitmap = 0;
- if( IsControl(m_nDragButton) ) {
- CRect rc;
- GetItemRect(m_nDragButton, rc);
- nDestBitmap = rc.Width();
- } else {
- HBITMAP hBmp = GetBitmap(tbButton.iBitmap, m_pDropBar->m_sizeImage);
- CBitmap bmp;
- if( hBmp ) {
- bmp.Attach(hBmp);
- nDestBitmap = wndDropTBCtrl.AddBitmap(1, &bmp);
- }
- }
- tbButton.iBitmap = nDestBitmap;
- //tbButton.iString = nDestString;
- tbButton.iString = -1;
- BOOL bInsertOK;
- if( nDropBtn >= 0 )
- bInsertOK = wndDropTBCtrl.InsertButton(nDropBtn, &tbButton);
- else
- bInsertOK = wndDropTBCtrl.AddButtons(1, &tbButton);
- if( bInsertOK ) {
- // transfer the string too, if any (check target first)
- if( m_pDropBar->HasButtonText() && HasButtonText() )
- {
- // let the CToolBar class do all the leg work
- m_pDropBar->SetButtonText(
- m_pDropBar->CommandToIndex(tbButton.idCommand),
- GetButtonText(m_nDragButton)
- );
- }
- // check whether the dragged button was a control in real life and
- // - if so - move that control to its new parent.
- CheckMoveControl(m_pDropBar, tbButton);
- wndTBCtrl.DeleteButton(m_nDragButton);
- if( m_nDragButton == wndTBCtrl.GetButtonCount()-1 )
- // remove trailing separators too
- RemoveTrailingSeparators();
- m_pDropBar->RecalcLayout();
- }
- }
- }
- } else {
- // remove the button from the toolbar
- if( bDoMove ) {
- wndTBCtrl.DeleteButton(m_nDragButton);
- if( m_nDragButton == wndTBCtrl.GetButtonCount() )
- // remove trailing separators too
- RemoveTrailingSeparators();
- } else
- // User has aborted the drag-operation.
- // Remove the drag-border from the button
- InvalidateButton(m_nDragButton);
- }
- // Recalculate the size of the bar.and the parent
- RecalcLayout();
- }
- m_bDragging = FALSE;
- // mouse capture is not longer needed
- ReleaseCapture();
- }
- void CToolBarEx :: CheckMoveControl( CToolBarEx * pToolBar, const TBBUTTON & tbButton ) {
- ASSERT_VALID(pToolBar);
- CWnd * pControl = GetControl(tbButton.idCommand, TRUE);
- if( pControl ) {
- // now change the parent of the control, so that it jumps to the
- // other toolbar
- pControl->SetParent(pToolBar);
- // remove the control from our list (if it's present there) and
- // add it to the target's list
- if( m_pControls ) {
- POSITION pos = m_pControls->Find(pControl);
- if(pos) {
- m_pControls->RemoveAt(pos);
- if( ! pToolBar->m_pControls )
- pToolBar->m_pControls = new CObList();
- pToolBar->m_pControls->AddTail(pControl);
- }
- }
- }
- }
- void CToolBarEx :: RemoveTrailingSeparators() {
- CToolBarCtrl & wndTBCtrl = GetToolBarCtrl();
- register const int nBtn = wndTBCtrl.GetButtonCount();
- register int i = nBtn;
- while( i && IsSeparator(--i) )
- if( DoQueryDelete(i) )
- wndTBCtrl.DeleteButton(i);
- }
- int CToolBarEx :: FindDropButton( const CPoint & point ) {
- CPoint pt = point;
- ScreenToClient(&pt);
- CRect rc;
- // find the button which is closest to the cursor
- register const int nBtn = GetToolBarCtrl().GetButtonCount();
- for( register int i = 0 ; i < nBtn ; ++i ) {
- GetItemRect(i, rc);
- if( rc.PtInRect(pt) )
- // insert the button to drop before this button:
- return (pt.x - rc.left < rc.right - pt.x)
- ? i
- : ((i==nBtn-1)
- ? -1
- : i+1);
- }
-
- // have to append the button
- return -1;
- }
- void CToolBarEx :: GetMarkerRect( int nButton, CRect & rc ) {
- register const int nBtn = GetToolBarCtrl().GetButtonCount();
- if( nButton < 0 || nButton > nBtn ) {
- // set the marker behind the last button
- GetItemRect(nBtn-1, rc);
- rc.right += 3;
- rc.left = rc.right-6;
- } else {
- // set the marker before the given button
- GetItemRect(nButton, rc);
- rc.left -= 3;
- rc.right = rc.left+6;
- }
- rc.DeflateRect(0,1);
- }
- void CToolBarEx :: ShowMarker( const CRect & rcMarker, CBitmap & bmpArea ) {
- ASSERT( bmpArea.GetSafeHandle() == 0 );
- CClientDC WinDC(this);
- CDC MemDC; MemDC.CreateCompatibleDC(&WinDC);
- bmpArea.CreateCompatibleBitmap(&WinDC, rcMarker.Width(), rcMarker.Height());
- CBitmap * pOldBmp = MemDC.SelectObject(&bmpArea);
- CPen pen(PS_SOLID, 1, RGB(0,0,0));
- CPen * pOldPen = WinDC.SelectObject(&pen);
- // save original area:
- MemDC.BitBlt(0,0,rcMarker.Width(),rcMarker.Height(),
- &WinDC, rcMarker.left, rcMarker.top, SRCCOPY);
- WinDC.MoveTo(rcMarker.TopLeft());
- WinDC.LineTo(rcMarker.right, rcMarker.top);
- WinDC.MoveTo(rcMarker.left+1, rcMarker.top+1);
- WinDC.LineTo(rcMarker.right-1, rcMarker.top+1);
- WinDC.MoveTo(rcMarker.left+2, rcMarker.top+2);
- WinDC.LineTo(rcMarker.left+2, rcMarker.bottom-2);
- WinDC.MoveTo(rcMarker.left+3, rcMarker.top+2);
- WinDC.LineTo(rcMarker.left+3, rcMarker.bottom-2);
- WinDC.MoveTo(rcMarker.left, rcMarker.bottom-1);
- WinDC.LineTo(rcMarker.right, rcMarker.bottom-1);
- WinDC.MoveTo(rcMarker.left+1, rcMarker.bottom-2);
- WinDC.LineTo(rcMarker.right-1, rcMarker.bottom-2);
- MemDC.SelectObject(pOldBmp);
- MemDC.DeleteDC();
- WinDC.SelectObject(pOldPen);
- }
- void CToolBarEx :: RestoreMarker( const CRect & rcArea, CBitmap & bmpArea ) {
- if( bmpArea.GetSafeHandle() == 0 )
- return;
- CClientDC WinDC(this);
- CDC MemDC; MemDC.CreateCompatibleDC(&WinDC);
- CBitmap * pOldBmp = MemDC.SelectObject(&bmpArea);
- WinDC.BitBlt(rcArea.left, rcArea.top, rcArea.Width(), rcArea.Height(),
- &MemDC, 0, 0, SRCCOPY);
- MemDC.SelectObject(pOldBmp);
- MemDC.DeleteDC();
- }
- void CToolBarEx :: SetMarker( CToolBarEx * pBar, const CPoint & point ) {
- static CToolBarEx * pLastToolBar = 0;
- static CRect lastRect(0,0,0,0);
- static CBitmap bmpLastSavedArea;
- CRect rcMarker;
- // retrieve proposed rectangle for the marker
- if( pBar != 0 ) {
- int nDropBtn = pBar->FindDropButton(point);
- pBar->GetMarkerRect(nDropBtn, rcMarker);
- if(rcMarker == lastRect)
- return; // don't need to erase/draw
- }
- // restore the previously marked area:
- if( pLastToolBar ) {
- pLastToolBar->RestoreMarker(lastRect, bmpLastSavedArea);
- bmpLastSavedArea.DeleteObject();
- }
- // draw the marker
- if( pBar != 0 ) {
- pBar->ShowMarker(rcMarker, bmpLastSavedArea);
- lastRect = rcMarker;
- }
- pLastToolBar = pBar;
- }
- BOOL CToolBarEx :: DoQueryDelete(int nButton) {
- ASSERT(nButton >= 0);
- TBBUTTON tbButton;
- if( ! GetToolBarCtrl().GetButton(nButton, &tbButton) ) {
- TRACE1("CToolBarEx::DoQueryDelete(): could not retrieve button %dn", nButton);
- return FALSE;
- }
- return QueryDeleteInsert(tbButton, TRUE, nButton);
- }
- BOOL CToolBarEx :: QueryDeleteInsert(TBBUTTON & tbButton, BOOL bDelete, int nIndex) {
- TBNOTIFY tbn;
- memset(&tbn, 0, sizeof(TBNOTIFY));
- tbn.hdr.hwndFrom = GetSafeHwnd();
- tbn.hdr.idFrom = UINT(::GetWindowLong(tbn.hdr.hwndFrom, GWL_ID));
- tbn.hdr.code = bDelete ? TBN_QUERYDELETE : TBN_QUERYINSERT;
- tbn.iItem = nIndex;
- memcpy((void *)(&tbn.tbButton), (const void *)(&tbButton), sizeof(TBBUTTON));
- CString strText;
- if( bDelete ) {
- strText = GetButtonText(nIndex);
- tbn.cchText = strText.GetLength();
- tbn.pszText = strText.GetBuffer(tbn.cchText);
- }
- ASSERT(GetParentFrame() != 0);
- ASSERT(::IsWindow(GetParentFrame()->GetSafeHwnd()));
- BOOL bRet = GetParentFrame()->SendMessage(
- WM_NOTIFY,
- WPARAM(tbn.hdr.idFrom),
- LPARAM(&tbn)
- );
-
- if( bDelete )
- strText.ReleaseBuffer();
- return bRet;
- }
- void CToolBarEx::OnCaptureChanged(CWnd *pWnd)
- {
- if( m_bDragging )
- // without the mouse-capture we cannot complete the drag-operation
- EndDrag(FALSE);
- CToolBar::OnCaptureChanged(pWnd);
- }
- BOOL CToolBarEx :: PreTranslateMessage( MSG * pMsg ) {
- if( m_bDragging &&
- (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP) &&
- int(pMsg->wParam) == VK_ESCAPE ) {
- // user pressed ESC to abort drag operation
- EndDrag(FALSE);
- return TRUE;
- }
- return CToolBar::PreTranslateMessage(pMsg);
- }
- void CToolBarEx::OnParentNotify(UINT message, LPARAM lParam)
- {
- if( LOWORD(message) == WM_LBUTTONDOWN && (::GetAsyncKeyState(VK_MENU) & (1<<15)) ) {
- // I see no chance to abort the child's message processing.
- // That's why we set a flag here. If we lose the focus (a click
- // in a child's area will activate that child window), then
- // we start the real drag-operation (that would return the
- // focus to the toolbar).
- // This solution is somewhat obfuscated, so if you know of
- // a better way -- let me know.
- m_bDragChild = TRUE;
- SetFocus();
- }
- CToolBar::OnParentNotify(message, lParam);
- }
- void CToolBarEx::OnKillFocus(CWnd* pNewWnd)
- {
- CToolBar::OnKillFocus(pNewWnd);
-
- if( m_bDragChild ) {
- // See OnParentNotify() above ...
- m_bDragChild = FALSE;
- if( GetCapture() != this )
- BeginDrag();
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // helpers for docking
- /////////////////////////////////////////////////////////////////////////////
- // We need our own version of a dock bar, because the original
- // MFC implementation overlapps toolbars. CToolBarEx don't want
- // such a overlapping, because this makes it impossible to draw
- // a real 3d border ...
- class CToolDockBar : public CDockBar {
- DECLARE_DYNAMIC(CToolDockBar)
- public:
- // this is the one and only method of interest
- virtual CSize CalcFixedLayout(BOOL bStretch, BOOL bHorz);
- };
- IMPLEMENT_DYNAMIC(CToolDockBar, CDockBar);
- CSize CToolDockBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
- {
- ASSERT_VALID(this);
- CSize sizeFixed = CControlBar::CalcFixedLayout(bStretch, bHorz);
- // get max size
- CSize sizeMax;
- if (!m_rectLayout.IsRectEmpty())
- sizeMax = m_rectLayout.Size();
- else
- {
- CRect rectFrame;
- CFrameWnd* pFrame = GetParentFrame();
- pFrame->GetClientRect(&rectFrame);
- sizeMax = rectFrame.Size();
- }
- // prepare for layout
- AFX_SIZEPARENTPARAMS layout;
- layout.hDWP = m_bLayoutQuery ?
- NULL : ::BeginDeferWindowPos(m_arrBars.GetSize());
- int cxBorder = 2, cyBorder = 2;
- CPoint pt(-cxBorder, -cyBorder);
- int nWidth = 0;
- BOOL bWrapped = FALSE;
- // layout all the control bars
- for (int nPos = 0; nPos < m_arrBars.GetSize(); nPos++)
- {
- CControlBar* pBar = GetDockedControlBar(nPos);
- void* pVoid = m_arrBars[nPos];
- if (pBar != NULL)
- {
- if(pBar->IsKindOf(RUNTIME_CLASS(CToolBarEx)) && ((CToolBarEx*)pBar)->IsFlatLook())
- ((CToolBarEx*)pBar)->m_bReal3DBorder = TRUE,
- cxBorder = cyBorder = 0;
- else if(pBar->IsKindOf(RUNTIME_CLASS(CToolBarEx)) && !((CToolBarEx*)pBar)->IsFlatLook())
- ((CToolBarEx*)pBar)->m_bReal3DBorder = FALSE,
- cxBorder = cyBorder = 2;
- else
- cxBorder = cyBorder = 2;
- if (pBar->IsVisible())
- {
- // get ideal rect for bar
- DWORD dwMode = 0;
- if ((pBar->m_dwStyle & CBRS_SIZE_DYNAMIC) &&
- (pBar->m_dwStyle & CBRS_FLOATING))
- dwMode |= LM_HORZ | LM_MRUWIDTH;
- else if (pBar->m_dwStyle & CBRS_ORIENT_HORZ)
- dwMode |= LM_HORZ | LM_HORZDOCK;
- else
- dwMode |= LM_VERTDOCK;
- CSize sizeBar = pBar->CalcDynamicLayout(-1, dwMode);
- CRect rect(pt, sizeBar);
- // get current rect for bar
- CRect rectBar;
- pBar->GetWindowRect(&rectBar);
- ScreenToClient(&rectBar);
- if (bHorz)
- {
- // Offset Calculated Rect out to Actual
- if (rectBar.left > rect.left && !m_bFloating)
- rect.OffsetRect(rectBar.left - rect.left, 0);
- // If ControlBar goes off the right, then right justify
- if (rect.right > sizeMax.cx && !m_bFloating)
- {
- int x = rect.Width() - cxBorder;
- x = max(sizeMax.cx - x, pt.x);
- rect.OffsetRect(x - rect.left, 0);
- }
- // If ControlBar has been wrapped, then left justify
- if (bWrapped)
- {
- bWrapped = FALSE;
- rect.OffsetRect(-(rect.left + cxBorder), 0);
- }
- // If ControlBar is completely invisible, then wrap it
- else if ((rect.left >= (sizeMax.cx - cxBorder)) &&
- (nPos > 0) && (m_arrBars[nPos - 1] != NULL))
- {
- m_arrBars.InsertAt(nPos, (CObject*)NULL);
- pBar = NULL; pVoid = NULL;
- bWrapped = TRUE;
- }
- if (!bWrapped)
- {
- if (rect != rectBar)
- {
- if (!m_bLayoutQuery &&
- !(pBar->m_dwStyle & CBRS_FLOATING))
- {
- pBar->m_pDockContext->m_rectMRUDockPos = rect;
- }
- AfxRepositionWindow(&layout, pBar->m_hWnd, &rect);
- }
- pt.x = rect.left + sizeBar.cx - cxBorder;
- nWidth = max(nWidth, sizeBar.cy);
- }
- }
- else
- {
- // Offset Calculated Rect out to Actual
- if (rectBar.top > rect.top && !m_bFloating)
- rect.OffsetRect(0, rectBar.top - rect.top);
- // If ControlBar goes off the bottom, then bottom justify
- if (rect.bottom > sizeMax.cy && !m_bFloating)
- {
- int y = rect.Height() - cyBorder;
- y = max(sizeMax.cy - y, pt.y);
- rect.OffsetRect(0, y - rect.top);
- }
- // If ControlBar has been wrapped, then top justify
- if (bWrapped)
- {
- bWrapped = FALSE;
- rect.OffsetRect(0, -(rect.top + cyBorder));
- }
- // If ControlBar is completely invisible, then wrap it
- else if ((rect.top >= (sizeMax.cy - cyBorder)) &&
- (nPos > 0) && (m_arrBars[nPos - 1] != NULL))
- {
- m_arrBars.InsertAt(nPos, (CObject*)NULL);
- pBar = NULL; pVoid = NULL;
- bWrapped = TRUE;
- }
- if (!bWrapped)
- {
- if (rect != rectBar)
- {
- if (!m_bLayoutQuery &&
- !(pBar->m_dwStyle & CBRS_FLOATING))
- {
- pBar->m_pDockContext->m_rectMRUDockPos = rect;
- }
- AfxRepositionWindow(&layout, pBar->m_hWnd, &rect);
- }
- pt.y = rect.top + sizeBar.cy - cyBorder;
- nWidth = max(nWidth, sizeBar.cx);
- }
- }
- }
- if (!bWrapped)
- {
- // handle any delay/show hide for the bar
- pBar->RecalcDelayShow(&layout);
- }
- }
- if (pBar == NULL && pVoid == NULL && nWidth != 0)
- {
- // end of row because pBar == NULL
- if (bHorz)
- {
- pt.y += nWidth - cyBorder;
- sizeFixed.cx = max(sizeFixed.cx, pt.x);
- sizeFixed.cy = max(sizeFixed.cy, pt.y);
- pt.x = -cxBorder;
- }
- else
- {
- pt.x += nWidth - cxBorder;
- sizeFixed.cx = max(sizeFixed.cx, pt.x);
- sizeFixed.cy = max(sizeFixed.cy, pt.y);
- pt.y = -cyBorder;
- }
- nWidth = 0;
- }
- }
- if (!m_bLayoutQuery)
- {
- // move and resize all the windows at once!
- if (layout.hDWP == NULL || !::EndDeferWindowPos(layout.hDWP))
- TRACE0("Warning: DeferWindowPos failed - low system resources.n");
- }
- // adjust size for borders on the dock bar itself
- CRect rect;
- rect.SetRectEmpty();
- CalcInsideRect(rect, bHorz);
- if ((!bStretch || !bHorz) && sizeFixed.cx != 0)
- sizeFixed.cx += -rect.right + rect.left;
- if ((!bStretch || bHorz) && sizeFixed.cy != 0)
- sizeFixed.cy += -rect.bottom + rect.top;
- return sizeFixed;
- }
- // dwDockBarMap
- const DWORD dwDockBarMap[4][2] =
- {
- { AFX_IDW_DOCKBAR_TOP, CBRS_TOP },
- { AFX_IDW_DOCKBAR_BOTTOM, CBRS_BOTTOM },
- { AFX_IDW_DOCKBAR_LEFT, CBRS_LEFT },
- { AFX_IDW_DOCKBAR_RIGHT, CBRS_RIGHT },
- };
- // Unfortunataly a simple rewrite of CFrameWnd's EnableDocking() is not possible,
- // because we have not enough permissions to access some data in this class.
- // That's why we call CFrameWnd::EnableDocking() first and exchange all occurencies
- // of CDockBar objects with our own version of a dock bar.
- void FrameEnableDocking(CFrameWnd * pFrame, DWORD dwDockStyle) {
- ASSERT_VALID(pFrame);
- // must be CBRS_ALIGN_XXX or CBRS_FLOAT_MULTI only
- ASSERT((dwDockStyle & ~(CBRS_ALIGN_ANY|CBRS_FLOAT_MULTI)) == 0);
- pFrame->EnableDocking(dwDockStyle);
- for (int i = 0; i < 4; i++) {
- if (dwDockBarMap[i][1] & dwDockStyle & CBRS_ALIGN_ANY) {
- CDockBar* pDock = (CDockBar*)pFrame->GetControlBar(dwDockBarMap[i][0]);
-
- // make sure the dock bar is of correct type
- if( pDock == 0 || ! pDock->IsKindOf(RUNTIME_CLASS(CToolDockBar)) ) {
- BOOL bNeedDelete = ! pDock->m_bAutoDelete;
- pDock->m_pDockSite->RemoveControlBar(pDock);
- pDock->m_pDockSite = 0; // avoid problems in destroying the dockbar
- pDock->DestroyWindow();
- if( bNeedDelete )
- delete pDock;
- pDock = 0;
- }
- if( pDock == 0 ) {
- pDock = new CToolDockBar;
- if (!pDock->Create(pFrame,
- WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_CHILD|WS_VISIBLE |
- dwDockBarMap[i][1], dwDockBarMap[i][0])) {
- AfxThrowResourceException();
- }
- }
- }
- }
- }