ToolBarEx.cpp
上传用户:diziting
上传日期:2007-01-02
资源大小:56k
文件大小:69k
源码类别:

工具条

开发平台:

Visual C++

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) 1997,'98 by Joerg Koenig
  3. // All rights reserved
  4. //
  5. // Distribute freely, except: don't remove my name from the source or
  6. // documentation (don't take credit for my work), mark your changes (don't
  7. // get me blamed for your possible bugs), don't alter or remove this
  8. // notice.
  9. // No warrantee of any kind, express or implied, is included with this
  10. // software; use at your own risk, responsibility for damages (if any) to
  11. // anyone resulting from the use of this software rests entirely with the
  12. // user.
  13. //
  14. // Send bug reports, bug fixes, enhancements, requests, flames, etc., and
  15. // I'll try to keep a version up to date.  I can be reached as follows:
  16. //    J.Koenig@adg.de                 (company site)
  17. //    Joerg.Koenig@rhein-neckar.de    (private site)
  18. /////////////////////////////////////////////////////////////////////////////
  19. // last revised: $Date: 10.05.98 17:45 $ $Revision: 2 $
  20. // ToolBarEx.cpp : implementation file
  21. //
  22. // Description:
  23. // CToolBarEx provides additional features to the standard toolbar
  24. // "CToolBar". The main addition is the flat mode (last seen in
  25. // Developer Studio 5.0).
  26. // There are no special requirements for having the flat mode in your
  27. // application (no special comctl32.dll or what ever)!
  28. // If you need custom draw abilities, then you have to use VC++ >= 4.2
  29. // However, the flat mode should work with older versions of VC++ too (let
  30. // me know of your experiences!)
  31. //
  32. // Usage:
  33. // The only task you have to perform, is to
  34. // #include "ToolBarEx.h"
  35. // in either StdAfx.h or MainFrm.h and to change the type of
  36. // CMainFrame::m_wndToolBar from CToolBar to CToolBarEx.
  37. // Don't forget to recompile :-)
  38. // if you want to reduce flickering, then you have to include
  39. // the header of Keith Rule's CMemDC class in your <stdafx.h>.
  40. // CToolBarEx will use it automagically then.
  41. // If you want a "real cool" 3D look, then you have to exchange the call
  42. // to "EnableDocking()" in your CMainFrame::OnCreate() method to
  43. // "FrameEnableDocking()". This will ensure, that the toolbar has the
  44. // same outfit as in Office or DevStudio.
  45. //
  46. // Acknowledgements:
  47. // o The main idea of how to draw a separator and the gripper is stolen
  48. // from Roger Onslow's MSIE flat toolbar.
  49. // Thanks for saving my time, Roger ;-)
  50. // o The embossed drawing of a disabled image came from
  51. // Victor M. Vogelpoel (victorv@telic.nl)
  52. // o Some hints for buttons with text came from
  53. // David Bates (bates@econ.ubc.ca)
  54. // (I'm still thinking, text on toolbar-buttons is broken.
  55. // That has to be tooltip's work. However, texts on buttons
  56. // now work)
  57. // o Thanks to Patrick Liechty (patrickl@code3.code3.com) for
  58. // the reports of his experiences with VC++ 4.0/4.1
  59. // o Thanks to Jeng-Yuan Sheu (m8501511@chu.edu.tw) for the
  60. // enhanced checked button.
  61. // o Thanks to Todd C. Wilson (todd@mediatec.com) for his
  62. // bug report and code-enhancement for users of VC++ 4.2b
  63. // o Matthias Drigalla <matthias.drigalla@bluewin.ch> has pointed
  64. // me out, that the timer is not released in all circumstances.
  65. // o thanks to Jonathan Chin <jonathan.chin@scitec.com.au> for his
  66. // experiences with UNICODE and for his enhancement to conform to
  67. // the standard interface (the flat toolbar should not perform any
  68. // hit test (and hence displaying the raised button state) if the
  69. // parent window (application) does not have the focus or is disabled.)
  70. // o thanks to Wang Ruopeng (ripple@thinker.ep.tsinghua.edu.cn) for
  71. // the enhanced outfit of checked buttons
  72. //
  73. // o Many thanks to Victor Vogelpoel, Jonathan Chin and Patrick Liechty
  74. // for their help to test and correct some bugs in the enhanced version.
  75. // Their work made this class much cleaner. Thanks.
  76. //
  77. // o Wolfgang Loch (W.Loch@intershop.de) sent in a fix for undockable bars
  78. // o Thanks to John Armstrong (jarmstrong@runge.com.au) for his fix to
  79. // the adjustment of gripperspace in "classic" mode
  80. // o Thanks to Anatoly Ivasyuk (aivasyuk@clark.net) for a much cooler method
  81. // to draw checked buttons that does not have the cursor on it.
  82. //
  83. //
  84. // (known) bugs and limitations:
  85. // o the CDRF_NEWFONT notification is still untested ...
  86. // o Assigning texts to buttons may cause the buttons to
  87. // resize horizontally without notified by CToolBar. This
  88. // leads to a wrong calculation inside CalcDynamicLayout()
  89. // and CalcFixedLayout(). One could override both these
  90. // functions in derived classes to avoid that problem,
  91. // but that would be a greater pain ...
  92. // o some features of the toolbars seen in Office97/DevStudio
  93. // are not implemented (for instance text-only buttons or
  94. // the way how image+text buttons are displayed.
  95. //
  96. // if you find others (and have a solution for them ?!), please let me know:
  97. // Joerg.Koenig@rhein-neckar.de (private site) or
  98. // J.Koenig@adg.de (company site)
  99. //
  100. // Changes:
  101. // 05/10/98
  102. // o buttons that are checked *and* disabled are now looking ok.
  103. // o don't draw a gripper if the bar is not dockable
  104. // o do not adjust space for gripper in "classic" mode
  105. //
  106. // 01/17/98
  107. // o If you have Keith Rule's "MemDC.h" included via
  108. // "stdafx.h", then CToolBarEx uses it to reduce
  109. // flickering.
  110. // o release the timer in all circumstances
  111. // o made the class compile even if UNICODE is #define'ed
  112. // o do not make any hit test (and display a raised button state)
  113. // if the application does not have the focus.
  114. // o Changed the outfit of a checked button that does not have
  115. // the cursor on it.
  116. // Major enhancements:
  117. // o added easy way to add controls to a toolbar
  118. // (both: replacing a button with a control and
  119. // inserting/appending new controls)
  120. // o Added many functions especially for the often
  121. // requested need for additional controls
  122. // o ALT-Drag feature implemented. You just can move buttons
  123. // (and controls!) from one CToolBarEx-object to another
  124. // in the style of the Developer Studio. There is no additional
  125. // requirement for having this feature in your application.
  126. // Just follow the online-help for how to enable customizable
  127. // toolbars. CToolBarEx uses the same technique.
  128. //
  129. // 11/25/97
  130. // o Some minor modifications to compile with VC++ 4.0/4.1 and 4.2b
  131. // o checked buttons now look hilighted (as in Office97/DevStudio)
  132. //
  133. // 11/07/97
  134. // (2 minor bugs have been occured as a result of the last update :)
  135. // o The WRAP state of a separator will be ignored now, if
  136. // the bar is docked vertically
  137. // o Draw an image transparently. This is needed only if one
  138. // uses 256 color images.
  139. //
  140. // 10/30/97
  141. // o texts on buttons now work
  142. // o gripper improved for a closer look like Office97
  143. // o disabled images now look embossed
  144. // o a separator is drawn only if it has no WRAP state set
  145. #include "stdafx.h"
  146. #include "ToolBarEx.h"
  147. #ifndef __AFXPRIV_H__
  148. #include <afxpriv.h> // one of the most interesting headers ;-)
  149. // if you plan to change CToolBarEx, then you
  150. // should better insert it in your "stdafx.h"
  151. // to speed up compilation
  152. #endif
  153. #ifdef _MEMDC_H_
  154. //#undef _MEMDC_H_ // this is for testing purposes
  155. #endif
  156. #ifdef _DEBUG
  157. #define new DEBUG_NEW
  158. #undef THIS_FILE
  159. static char THIS_FILE[] = __FILE__;
  160. #endif
  161. /////////////////////////////////////////////////////////////////////////////
  162. // local helper class CCustomDrawInfo
  163. //
  164. // The helper class CCustomDrawInfo handles the messaging to the docking
  165. // frame of the toolbar in flat mode only. If flat-mode is disabled, then
  166. // MFC's own messanger will be used.
  167. //
  168. // A few words about custom draw on toolbars:
  169. // o custom draw is possible for MFC >= 4.2 only (older versions don't know
  170. //   anything about certain structures ...)
  171. // o MFC does not set the "rc" member of NMCUSTOMDRAW to the rectangle of the
  172. //  button that will be drawn. However, we do, so watch out, whether the
  173. //  toolbar is flat or not (or ignore the "rc" member in both cases).
  174. //  If the current mode is not "flat", then MFC's art of message arrives ...
  175. // o MFC does not send a message for separators, so we too don't do it.
  176. // o It seems that MFC toolbars never send *ERASE notifications; instead they
  177. //   send TBN_QUERYDELETE for instance.
  178. // o The CDRF_NEWFONT notification result is ignored (in flat mode. Never
  179. //   tried with original MFC, because it is broken on toolbars).
  180. /////////////////////////////////////////////////////////////////////////////
  181. class CCustomDrawInfo {
  182. #if _MFC_VER >= 0x0420
  183. NMCUSTOMDRAW m_CDRW; // custom draw information holder
  184. LRESULT m_PrePaint; // result from prepaint notification
  185. LRESULT m_ItemPrePaint; // dito for specific item
  186. CToolBarEx * m_pToolBar; // the real sender of the notification
  187. CWnd * m_pReceiver; // the receiver of the notification
  188. LRESULT NotifyParent();
  189. #endif // _MFC_VER
  190. public: // construction
  191. CCustomDrawInfo( CDC & dc, CToolBarEx * pToolBar );
  192. public:
  193. // NotifyItemPrePaint() returns TRUE,
  194. // if the user wants to do the default
  195. // (CDRF_DODEFAULT) or FALSE, if the
  196. // user wants to skip (CDRF_SKIPDEFAULT)
  197. // Note that CDRF_SKIPDEFAULT is not
  198. // allowed for CDDS_PREPAINT, CDDS_POSTPAINT !
  199. // and CDDS_ITEMPOSTPAINT
  200. void NotifyPrePaint();
  201. BOOL NotifyItemPrePaint(int item);
  202. void NotifyItemPostPaint(int item);
  203. void NotifyPostPaint();
  204. };
  205. #if _MFC_VER >= 0x420
  206. LRESULT CCustomDrawInfo :: NotifyParent() {
  207. LRESULT lRes = CDRF_DODEFAULT;
  208. if( m_pReceiver )
  209. lRes = m_pReceiver->SendMessage(WM_NOTIFY,
  210. WPARAM(m_CDRW.hdr.idFrom),
  211. LPARAM(&m_CDRW));
  212. return lRes;
  213. }
  214. CCustomDrawInfo :: CCustomDrawInfo( CDC & dc, CToolBarEx * pBar )
  215. : m_PrePaint(0)
  216. , m_ItemPrePaint(0)
  217. {
  218. VERIFY((m_pToolBar = pBar) != 0);
  219. VERIFY((m_CDRW.hdc = dc.GetSafeHdc()) != 0);
  220. HWND hwnd = pBar->GetSafeHwnd();
  221. VERIFY(::IsWindow(hwnd));
  222. // initialise the NMHDR member of the customdraw structure
  223. m_CDRW.hdr.hwndFrom = hwnd;
  224. m_CDRW.hdr.idFrom = UINT(::GetWindowLong(hwnd, GWL_ID));
  225. m_CDRW.hdr.code = NM_CUSTOMDRAW;
  226. // Do not use CControlBar::GetDockingFrame() to receive
  227. // the parent. CWnd::GetParent() is inacceptable too.
  228. // Both these functions don't work, if the toolbar is
  229. // floating in the air!
  230. m_pReceiver = pBar->GetParentFrame();
  231. if( m_pReceiver )
  232. VERIFY(::IsWindow(m_pReceiver->GetSafeHwnd()));
  233. }
  234. void CCustomDrawInfo :: NotifyPrePaint() {
  235. // fill the customdraw structure with values for CDDS_PREPAINT
  236. m_CDRW.dwDrawStage = CDDS_PREPAINT;
  237. // the rest of the structure stays undefined in this stage
  238. // of drawing.
  239. m_PrePaint = NotifyParent();
  240. }
  241. BOOL CCustomDrawInfo :: NotifyItemPrePaint( int nItem ) {
  242. BOOL bRet = TRUE; // we assume to do the default
  243. if( m_PrePaint & CDRF_NOTIFYITEMDRAW ) {
  244. m_CDRW.dwDrawStage = CDDS_ITEMPREPAINT;
  245. m_pToolBar->GetItemRect(nItem, &m_CDRW.rc);
  246. m_CDRW.dwItemSpec = DWORD(m_pToolBar->GetItemID(nItem));
  247. UINT uStyle = m_pToolBar->GetButtonStyle(nItem);
  248. BOOL bEnable = m_pToolBar->GetToolBarCtrl()
  249. .IsButtonEnabled(m_CDRW.dwItemSpec);
  250. m_CDRW.uItemState = (bEnable ? 0 : CDIS_DISABLED) |
  251. (((uStyle & TBBS_PRESSED) || (uStyle & TBBS_CHECKED)) ?
  252. CDIS_CHECKED : 0);
  253. m_CDRW.lItemlParam = 0;
  254. m_ItemPrePaint = NotifyParent();
  255. if( m_ItemPrePaint & CDRF_SKIPDEFAULT )
  256. bRet = FALSE;
  257. }
  258. return bRet;
  259. }
  260. void CCustomDrawInfo :: NotifyItemPostPaint( int nItem ) {
  261. if( m_ItemPrePaint & CDRF_NOTIFYPOSTPAINT ) {
  262. m_CDRW.dwDrawStage = CDDS_ITEMPOSTPAINT;
  263. // the rest of the data has not been changed since ITEMPREPAINT
  264. // make sure it is so:
  265. ASSERT(m_pToolBar->GetItemID(nItem) == m_CDRW.dwItemSpec);
  266. NotifyParent();
  267. }
  268. }
  269. void CCustomDrawInfo :: NotifyPostPaint() {
  270. if( m_PrePaint & CDRF_NOTIFYPOSTPAINT ) {
  271. m_CDRW.dwDrawStage = CDDS_POSTPAINT;
  272. NotifyParent();
  273. }
  274. }
  275. #else // _MFC_VER < 4.2
  276. CCustomDrawInfo :: CCustomDrawInfo( CDC & dc, CToolBarEx * pParent ) {
  277. }
  278. void CCustomDrawInfo :: NotifyPrePaint() {
  279. }
  280. void CCustomDrawInfo :: NotifyPostPaint() {
  281. }
  282. BOOL CCustomDrawInfo :: NotifyItemPrePaint( int ) {
  283. return TRUE; // we always make the drawing by ourself
  284. }
  285. void CCustomDrawInfo :: NotifyItemPostPaint( int ) {
  286. }
  287. #endif // _MFC_VER
  288. /////////////////////////////////////////////////////////////////////////////
  289. // CToolBarEx  --  class static data
  290. HCURSOR CToolBarEx :: m_hOrigCursor = 0;
  291. HCURSOR CToolBarEx :: m_hDragCursor = 0;
  292. HCURSOR CToolBarEx :: m_hNoDragCursor = 0;
  293. BOOL CToolBarEx :: m_bDragging = FALSE;
  294. BOOL CToolBarEx :: m_bDragCursor = FALSE;
  295. int CToolBarEx :: m_nDragButton = -1;
  296. CToolBarEx * CToolBarEx :: m_pDropBar = 0;
  297. CPoint CToolBarEx :: m_ptDrop(0,0);
  298. /////////////////////////////////////////////////////////////////////////////
  299. // CToolBarEx
  300. CToolBarEx::CToolBarEx()
  301. : m_bFlatLook(TRUE)
  302. , m_clrBtnFace(::GetSysColor(COLOR_BTNFACE))
  303. , m_clrBtnHilight(::GetSysColor(COLOR_BTNHILIGHT))
  304. , m_clrBtnShadow(::GetSysColor(COLOR_BTNSHADOW))
  305. , m_clrBtnLight(::GetSysColor(COLOR_3DLIGHT))
  306. , m_nLastBtn(-1)
  307. , m_uTimerEvent(0)
  308. , m_pControls(0)
  309. , m_bReal3DBorder(FALSE) // set to TRUE, if you're using FrameEnableDocking()
  310. , m_bDragChild(FALSE)
  311. {
  312. CalculateOffset();
  313. // create the default font, used for buttons with text
  314. CFont Font;
  315. BOOL bOldSys = FALSE;
  316. if( ! Font.CreateStockObject( DEFAULT_GUI_FONT ) ) {
  317. // older versions of Windows* (NT 3.51 for instance)
  318. // fail with DEFAULT_GUI_FONT
  319. VERIFY( Font.CreateStockObject( SYSTEM_FONT ) );
  320. bOldSys = TRUE;
  321. }
  322. LOGFONT logfont ;
  323. Font.GetLogFont( &logfont ) ;
  324. if( bOldSys ) {
  325. logfont.lfWeight = 400;
  326. _tcscpy(logfont.lfFaceName,_T("MS Sans Serif"));
  327. }
  328. logfont.lfHeight = 6 ;
  329. logfont.lfWidth = 0 ; // let windows compute this.
  330. VERIFY( m_GuiFont.CreateFontIndirect( &logfont ) ) ;
  331. }
  332. CToolBarEx::~CToolBarEx()
  333. {
  334. if( m_pControls ) {
  335. for( POSITION pos = m_pControls->GetHeadPosition() ; pos ; )
  336. delete m_pControls->GetNext(pos);
  337. delete m_pControls;
  338. }
  339. }
  340. IMPLEMENT_DYNAMIC(CToolBarEx, CToolBar)
  341. BEGIN_MESSAGE_MAP(CToolBarEx, CToolBar)
  342. //{{AFX_MSG_MAP(CToolBarEx)
  343. ON_WM_PAINT()
  344. ON_WM_SYSCOLORCHANGE()
  345. ON_WM_NCCALCSIZE()
  346. ON_WM_MOUSEMOVE()
  347. ON_WM_NCPAINT()
  348. ON_WM_TIMER()
  349. ON_WM_CREATE()
  350. ON_WM_LBUTTONDOWN()
  351. ON_WM_LBUTTONUP()
  352. ON_WM_WINDOWPOSCHANGING()
  353. ON_WM_CAPTURECHANGED()
  354. ON_WM_PARENTNOTIFY()
  355. ON_WM_KILLFOCUS()
  356. //}}AFX_MSG_MAP
  357. ON_MESSAGE(TB_SETBUTTONSIZE, OnSetButtonSize)
  358. ON_MESSAGE(TB_SETBITMAPSIZE, OnSetBitmapSize)
  359. ON_MESSAGE(TB_ADDBITMAP, OnAddBitmap)
  360. ON_MESSAGE(TB_DELETEBUTTON, OnDeleteButton)
  361. #ifdef _MEMDC_H_
  362. ON_WM_ERASEBKGND()
  363. #endif
  364. END_MESSAGE_MAP()
  365. /////////////////////////////////////////////////////////////////////////////
  366. // CToolBarEx message handlers
  367. LRESULT CToolBarEx :: OnSetButtonSize(WPARAM wParam, LPARAM lParam) {
  368. LRESULT lResult = CToolBar::OnSetButtonSize(wParam, lParam);
  369. if( lResult )
  370. CalculateOffset();
  371. return lResult;
  372. }
  373. LRESULT CToolBarEx :: OnSetBitmapSize(WPARAM wParam, LPARAM lParam) {
  374. LRESULT lResult = CToolBar::OnSetBitmapSize(wParam, lParam);
  375. if( lResult )
  376. CalculateOffset();
  377. return lResult;
  378. }
  379. void CToolBarEx :: SetFlatLook( BOOL bFlat ) {
  380. if( bFlat != m_bFlatLook ) {
  381. m_bFlatLook = bFlat;
  382. if( ::IsWindow(GetSafeHwnd()) ) {
  383.             //flat tool bars have gripper space at the left, cause non-client to resize
  384.             SetWindowPos(0, 0,0,0,0,SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER);
  385. // force a repaint of all buttons
  386. Invalidate();
  387. // erase/draw the gripper
  388. OnNcPaint();
  389. // if the "FrameEnableDocking()" function is used, we have to adjust
  390. // the bars inside the parent.
  391. VERIFY(::IsWindow(m_hwndParent));
  392. ((CFrameWnd*)(CWnd::FromHandle(m_hwndParent)))->RecalcLayout();
  393. }
  394. }
  395. }
  396. void CToolBarEx::OnPaint() 
  397. {
  398. HIMAGELIST hImg = GetImageList();
  399. #ifdef _DEBUG
  400. if( hImg == 0 ) {
  401. TRACE0("CToolBarEx::OnPaint(): could not get image listn");
  402. }
  403. #endif
  404. if( m_bFlatLook && hImg ) {
  405. CRect rcUpdate;
  406. if( ! GetUpdateRect(rcUpdate) )
  407. return;
  408. if( HasButtonText() )
  409. CalculateOffset(); // strings may have been added
  410. // attach image-list for even more MFC feeling :)
  411. CImageList imglist;
  412. imglist.Attach(hImg);
  413. POINT cursor;
  414. ::GetCursorPos(&cursor);
  415. ScreenToClient(&cursor);
  416. #ifdef _MEMDC_H_
  417. // if you have Keith Rule's CMemDC class inserted
  418. // in "stdafx.h", then we use it here ...
  419. CPaintDC dcp(this);
  420. CMemDC dc(&dcp);
  421. dc.FillSolidRect(rcUpdate, m_clrBtnFace);
  422. #else // _MEMDC_H_
  423. CPaintDC dc(this); // device context for painting
  424. #endif // _MEMDC_H_
  425. CFont * pOldFont = dc.SelectObject(&m_GuiFont);
  426. // Now it's time for the first custom-draw-notification...
  427. CCustomDrawInfo cdrw(dc, this);
  428. cdrw.NotifyPrePaint();
  429. register const int nBtn = GetToolBarCtrl().GetButtonCount();
  430. for( register int i = 0; i < nBtn; ++i ) {
  431. CRect rc;
  432. GetItemRect(i, rc);
  433. int nBitmap; UINT uID, uStyleState;
  434. GetButtonInfo(i, uID, uStyleState, nBitmap);
  435. WORD wStyle = LOWORD(uStyleState);
  436. WORD wState = HIWORD(uStyleState);
  437. if( wState & TBSTATE_HIDDEN )
  438. continue;
  439. if( wStyle == TBSTYLE_SEP ) {
  440. if( !(wState & TBSTATE_WRAP) || ! IsFloating() )
  441. DrawSeparator(dc, rc);
  442. } else {
  443. if( ! CRect().IntersectRect(rcUpdate, rc) )
  444. continue; // this button needs no repaint
  445. BOOL bBtnDown = (wState & TBSTATE_CHECKED) || (wState & TBSTATE_PRESSED);
  446. BOOL bBtnEnabled = GetToolBarCtrl().IsButtonEnabled(int(uID));
  447. BOOL bHasCursor = rc.PtInRect(cursor);
  448. COLORREF clrRect = (bBtnDown && !bHasCursor) ? m_clrBtnLight : m_clrBtnFace;
  449. // maybe the button has text
  450. dc.SetTextColor(RGB(0,0,0));
  451. dc.SetBkColor(clrRect);
  452. if( HasButtonText() )
  453. // There is a bug in CToolBar: If there are texts assigned
  454. // to buttons, then the button-widths may change transparently
  455. // (without notified by CToolBar), so we recalculate the
  456. // horizontal offset here:
  457. m_sizeOffset.cx = (rc.Width() - m_sizeImage.cx) / 2;
  458. if( ! cdrw.NotifyItemPrePaint(i) )
  459. continue; // parent has already drawn the button
  460. dc.FillSolidRect(rc, clrRect);
  461. // it seems, that CDC::Draw3dRect() changes the background color
  462. COLORREF clrBk = dc.GetBkColor();
  463. if( bBtnDown ) {
  464. // draw a pressed button
  465. dc.Draw3dRect(rc, m_clrBtnShadow, m_clrBtnHilight);
  466. if( ! bHasCursor ) {
  467. // if the button does not have the cursor on it,
  468. // then the pressed button is somewhat lighter
  469. // then the other buttons.
  470. CRect rcCheck = rc;
  471. rcCheck.DeflateRect(1,1);
  472. // draw an invisible frame around the hilighted area
  473. dc.Draw3dRect(rcCheck, m_clrBtnFace, m_clrBtnFace);
  474. rcCheck.DeflateRect(1,1);
  475. CBrush *pBrush = CDC::GetHalftoneBrush();
  476. dc.SetTextColor(m_clrBtnHilight);
  477. dc.SetBkColor(m_clrBtnFace);
  478. dc.FillRect(rcCheck, pBrush);
  479. dc.SetTextColor(RGB(0,0,0));
  480. dc.SetBkColor(clrRect);
  481. }
  482. } else if( bHasCursor && ! bBtnDown && bBtnEnabled )
  483. // draw a normal button
  484. dc.Draw3dRect(rc, m_clrBtnHilight, m_clrBtnShadow);
  485. else if( ! bBtnDown && bBtnEnabled )
  486. // Draw an invisible rect around the button.
  487. // This prevents us from erasing the background
  488. // if the button was formed before
  489. // (that would cause the button to flicker ...)
  490. dc.Draw3dRect(rc, m_clrBtnFace, m_clrBtnFace);
  491. dc.SetBkColor(clrBk);
  492. // the point where to start with the image
  493. CPoint pt(rc.left + m_sizeOffset.cx + bBtnDown,
  494.   rc.top + m_sizeOffset.cy + bBtnDown);
  495. imglist.Draw(&dc, nBitmap, pt, ILD_TRANSPARENT);
  496. CString strText = GetButtonText(i);
  497. if( strText.GetLength() ) {
  498. CRect rectText(
  499. rc.left+3+bBtnDown,
  500. rc.top+m_sizeOffset.cy+m_sizeImage.cy+1+bBtnDown,
  501. rc.right-3+bBtnDown,
  502. rc.bottom-3+bBtnDown
  503. );
  504. int nBkMode = dc.SetBkMode(TRANSPARENT) ;
  505. dc.DrawText(strText, rectText, DT_CENTER|DT_VCENTER|DT_NOCLIP);
  506. dc.SetBkMode(nBkMode) ;
  507. }
  508. if( ! bBtnEnabled ) {
  509. // gray out that button
  510. rc.DeflateRect(bBtnDown,bBtnDown);
  511. DrawDisabledButton(dc, rc);
  512. }
  513. cdrw.NotifyItemPostPaint(i);
  514. }
  515. }
  516. dc.SelectObject(pOldFont);
  517. if( ! m_bDeleteImgList )
  518. imglist.Detach();
  519. // last but not least: inform the parent for end of painting
  520. cdrw.NotifyPostPaint();
  521. } else
  522. // classic mode (or couldn't receive imagelist)
  523. CToolBar::OnPaint();
  524. }
  525. void CToolBarEx :: DrawDisabledButton( CDC & dc, const CRect & rc ) const {
  526. // create a monochrome memory DC
  527. CDC ddc;
  528. ddc.CreateCompatibleDC(0);
  529. CBitmap bmp;
  530. bmp.CreateCompatibleBitmap(&ddc, rc.Width(), rc.Height());
  531. CBitmap * pOldBmp = ddc.SelectObject(&bmp);
  532. // build a mask
  533. ddc.PatBlt(0, 0, rc.Width(), rc.Height(), WHITENESS);
  534. dc.SetBkColor(m_clrBtnFace);
  535. ddc.BitBlt(0, 0, rc.Width(), rc.Height(), &dc, rc.left, rc.top, SRCCOPY);
  536. dc.SetBkColor(m_clrBtnHilight);
  537. ddc.BitBlt(0, 0, rc.Width(), rc.Height(), &dc, rc.left, rc.top, SRCPAINT);
  538. // Copy the image from the toolbar into the memory DC
  539. // and draw it (grayed) back into the toolbar.
  540. dc.FillSolidRect(rc.left, rc.top, rc.Width(), rc.Height(), m_clrBtnFace);
  541. dc.SetBkColor(RGB(0, 0, 0));
  542. dc.SetTextColor(RGB(255, 255, 255));
  543. CBrush brShadow, brHilight;
  544. brHilight.CreateSolidBrush(m_clrBtnHilight);
  545. brShadow.CreateSolidBrush(m_clrBtnShadow);
  546. CBrush * pOldBrush = dc.SelectObject(&brHilight);
  547. dc.BitBlt(rc.left+1, rc.top+1, rc.Width(), rc.Height(), &ddc, 0, 0, 0x00E20746L);
  548. dc.SelectObject(&brShadow);
  549. dc.BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &ddc, 0, 0, 0x00E20746L);
  550. // reset DCs
  551. dc.SelectObject(pOldBrush);
  552. ddc.SelectObject(pOldBmp);
  553. ddc.DeleteDC();
  554. bmp.DeleteObject();
  555. }
  556. void CToolBarEx :: DrawSeparator( CDC & dc, CRect & rc ) const {
  557.     BOOL bHorz = ((m_dwStyle & CBRS_ORIENT_HORZ) != 0) ? TRUE : FALSE;
  558. // make sure, this separator is not a placeholder for
  559. // another control.
  560. if( rc.Width() <= 8 ) {
  561. if( bHorz ) {
  562. // draw the separator bar in the middle
  563. int x = (rc.left + rc.right) / 2;
  564. rc.left = x-1; rc.right = x+1;
  565. dc.Draw3dRect(
  566. rc,
  567. m_clrBtnShadow,
  568. m_clrBtnHilight
  569. );
  570. } else {
  571. // draw the separator bar in the middle
  572. rc.left = rc.left - m_sizeButton.cx;
  573. rc.right = rc.left + m_sizeButton.cx;
  574. rc.top = rc.bottom+1;
  575. rc.bottom = rc.top+3;
  576. int y = (rc.top+rc.bottom)/2;
  577. rc.top = y-1; rc.bottom = y+1;
  578. dc.Draw3dRect(
  579. rc,
  580. m_clrBtnShadow,
  581. m_clrBtnHilight
  582. );
  583. }
  584. }
  585. }
  586. void CToolBarEx :: DrawGripper( CDC & dc ) const { 
  587.     // Do not draw a gripper if the bar is floating or not
  588. // dockable.
  589. if( (m_dwStyle & CBRS_FLOATING) || m_dwDockStyle == 0 )
  590. return;
  591. CRect gripper;
  592. GetWindowRect(gripper);
  593. ScreenToClient(gripper);
  594. gripper.OffsetRect(-gripper.left, -gripper.top);
  595. if( m_dwStyle & CBRS_ORIENT_HORZ ) {
  596. // gripper at left
  597. gripper.DeflateRect(4, 4);
  598. gripper.right = gripper.left+3;
  599.         dc.Draw3dRect(
  600. gripper,
  601. m_clrBtnHilight,
  602.             m_clrBtnShadow
  603. );
  604. gripper.OffsetRect(3, 0);
  605.         dc.Draw3dRect(
  606. gripper,
  607. m_clrBtnHilight,
  608.             m_clrBtnShadow
  609. );
  610. } else {
  611. // gripper at top
  612. gripper.DeflateRect(4, 4);
  613. gripper.bottom = gripper.top+3;
  614. dc.Draw3dRect(
  615. gripper,
  616. m_clrBtnHilight,
  617.             m_clrBtnShadow
  618. );
  619. gripper.OffsetRect(0, 3);
  620.         dc.Draw3dRect(
  621. gripper,
  622. m_clrBtnHilight,
  623.             m_clrBtnShadow
  624. );
  625. }
  626. }
  627. void CToolBarEx :: OnUpdateCmdUI( CFrameWnd* pTarget, BOOL bDisableIfNoHndler ) {
  628. if( m_bFlatLook ) {
  629. // save current styles
  630. register const int nBtn = GetToolBarCtrl().GetButtonCount();
  631. register int nIdx;
  632. for( nIdx = 0; nIdx < nBtn; ++nIdx )
  633. m_Styles.SetAtGrow(nIdx, GetButtonStyle(nIdx));
  634. // do base class processing
  635. CToolBar::OnUpdateCmdUI(pTarget,bDisableIfNoHndler);
  636. //check whether styles have been changed
  637. for( nIdx = 0; nIdx < nBtn; ++nIdx ) {
  638. if( m_Styles[nIdx] != GetButtonStyle(nIdx) )
  639. // invalidate that button
  640. InvalidateButton(nIdx);
  641. }
  642. } else
  643. // simply delegate
  644. CToolBar::OnUpdateCmdUI(pTarget,bDisableIfNoHndler);
  645. }
  646. void CToolBarEx::OnSysColorChange() 
  647. {
  648. CToolBar::OnSysColorChange();
  649. m_clrBtnFace    = ::GetSysColor(COLOR_BTNFACE);
  650. m_clrBtnHilight = ::GetSysColor(COLOR_BTNHILIGHT);
  651. m_clrBtnShadow  = ::GetSysColor(COLOR_BTNSHADOW);
  652. m_clrBtnLight   = ::GetSysColor(COLOR_3DLIGHT);
  653. }
  654. void CToolBarEx::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp) 
  655. {
  656. CToolBar::OnNcCalcSize(bCalcValidRects, lpncsp);
  657. if( m_bFlatLook ) {
  658. // adjust non-client area for gripper at left or top
  659. if( m_dwStyle & CBRS_ORIENT_HORZ ) {
  660. lpncsp->rgrc[0].left += 4;
  661. lpncsp->rgrc[0].right += 4;
  662. } else {
  663. lpncsp->rgrc[0].top += 6;
  664. lpncsp->rgrc[0].bottom += 6;
  665. }
  666. }
  667. }
  668. void CToolBarEx::OnMouseMove(UINT nFlags, CPoint point) 
  669. {
  670. if( m_bDragging ) {
  671. DragMove();
  672. return;
  673. }
  674. if( m_bFlatLook && IsTopParentActive() && GetTopLevelParent()->IsWindowEnabled()) {
  675. register const int nBtn = GetToolBarCtrl().GetButtonCount();
  676. const int nLastBtn = m_nLastBtn;
  677. m_nLastBtn = -1;
  678. for( register int i = 0 ; i < nBtn ; ++i ) {
  679. CRect rc;
  680. GetItemRect(i, rc);
  681. const BOOL bBtnEnabled = GetToolBarCtrl().IsButtonEnabled(int(GetItemID(i)));
  682. const BOOL bSep = GetButtonStyle(i) & TBBS_SEPARATOR;
  683. if( bSep || ! bBtnEnabled )
  684. continue;
  685. const BOOL bHasCursor = rc.PtInRect(point);
  686. if( bHasCursor && bBtnEnabled ) {
  687. if( nLastBtn != i ) {
  688. // force a repaint of the button with the cursor on it
  689. InvalidateRect(rc, FALSE);
  690. }
  691. m_nLastBtn = i;
  692. } else if( !bHasCursor && i == nLastBtn ) {
  693. // force a repaint of the last formed button
  694. InvalidateRect(rc, FALSE);
  695. }
  696. }
  697. // One problem occures with WM_MOUSEMOVE: we cannot detect
  698. // that the mouse leaves the window. If the mouse moves quick
  699. // enough, then the last formed button stays visible. To
  700. // resolve this problem, we set a timer and check, whether
  701. // the mouse is outside the window ...
  702. KillTimer(m_uTimerEvent);
  703. m_uTimerEvent = SetTimer(1, 250, 0);
  704. }
  705. CToolBar::OnMouseMove(nFlags, point);
  706. }
  707. void CToolBarEx::OnNcPaint() 
  708. {
  709. if( m_bFlatLook ) {
  710. // get window DC that is clipped to the non-client area
  711. CWindowDC dc(this);
  712. CRect rectClient;
  713. GetClientRect(rectClient);
  714. CRect rectWindow;
  715. GetWindowRect(rectWindow);
  716. ScreenToClient(rectWindow);
  717. rectClient.OffsetRect(-rectWindow.left, -rectWindow.top);
  718. dc.ExcludeClipRect(rectClient);
  719. // draw borders in non-client area
  720. rectWindow.OffsetRect(-rectWindow.left, -rectWindow.top);
  721. Draw3DBorders(&dc, rectWindow);
  722. dc.IntersectClipRect(rectWindow);
  723. #ifdef _MEMDC_H_
  724. // You're using Keith Rule's CMemDC. In this case
  725. // we have to make sure that WM_ERASEBKGND
  726. // will not be sent.
  727. dc.FillSolidRect(rectWindow, m_clrBtnFace);
  728. #else
  729. // erase parts not drawn
  730. SendMessage(WM_ERASEBKGND, (WPARAM)dc.m_hDC);
  731. #endif
  732. DrawGripper(dc);
  733. } else
  734. CToolBar::OnNcPaint();
  735. }
  736. void CToolBarEx :: Draw3DBorders(CDC * pDC, CRect & rect) {
  737. ASSERT_VALID(this);
  738. ASSERT_VALID(pDC);
  739. if( m_bReal3DBorder ) {
  740. DWORD dwStyle = m_dwStyle;
  741. if (!(dwStyle & CBRS_BORDER_ANY))
  742. return;
  743. COLORREF clr = (m_dwStyle & CBRS_BORDER_3D) ? m_clrBtnHilight : m_clrBtnShadow;
  744. if(m_dwStyle & CBRS_BORDER_LEFT)
  745. pDC->FillSolidRect(0, 0, 1, rect.Height() - 1, clr);
  746. if(m_dwStyle & CBRS_BORDER_TOP)
  747. pDC->FillSolidRect(0, 0, rect.Width()-1 , 1, clr);
  748. if(m_dwStyle & CBRS_BORDER_RIGHT)
  749. pDC->FillSolidRect(rect.right, 1, -1, rect.Height() - 1, m_clrBtnShadow);
  750. if(m_dwStyle & CBRS_BORDER_BOTTOM)
  751. pDC->FillSolidRect(0, rect.bottom, rect.Width()-1, -1, m_clrBtnShadow);
  752. // if undockable toolbar at top of frame, apply special formatting to mesh
  753. // properly with frame menu
  754. if(!m_pDockContext) {
  755. pDC->FillSolidRect(0,0,rect.Width(),1,m_clrBtnShadow);
  756. pDC->FillSolidRect(0,1,rect.Width(),1,m_clrBtnHilight);
  757. }
  758. if (dwStyle & CBRS_BORDER_LEFT)
  759. ++rect.left;
  760. if (dwStyle & CBRS_BORDER_TOP)
  761. ++rect.top;
  762. if (dwStyle & CBRS_BORDER_RIGHT)
  763. --rect.right;
  764. if (dwStyle & CBRS_BORDER_BOTTOM)
  765. --rect.bottom;
  766. } else
  767. DrawBorders(pDC, rect);
  768. }
  769. void CToolBarEx::OnTimer(UINT nIDEvent) 
  770. {
  771. if( nIDEvent == m_uTimerEvent ) {
  772. if( m_nLastBtn >= 0 ) {
  773. POINT pt;
  774. ::GetCursorPos(&pt);
  775. CRect rc;
  776. GetWindowRect(rc);
  777. if( ! rc.PtInRect(pt) ) {
  778. InvalidateButton(m_nLastBtn);
  779. m_nLastBtn = -1;
  780. }
  781. }
  782. if( m_nLastBtn < 0 )
  783. KillTimer(nIDEvent);
  784. } else
  785. CToolBar::OnTimer(nIDEvent);
  786. }
  787. int CToolBarEx::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  788. {
  789. if (CToolBar::OnCreate(lpCreateStruct) == -1)
  790. return -1;
  791. // Save the parent at creation time. It may change, if
  792. // the toolbar is floating; but we want to know of the
  793. // "real" parent (for notification messages)!
  794. m_hwndParent = lpCreateStruct->hwndParent;
  795. return 0;
  796. }
  797. void CToolBarEx::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) 
  798. {
  799. CToolBar::OnWindowPosChanging(lpwndpos);
  800. // If moved just force a redraw. This stops the partial redraw bug.
  801. if( !(lpwndpos->flags & SWP_NOMOVE) )
  802. Invalidate(FALSE);
  803. }
  804. #define PADWIDTH(x) (((x)*8+31)&~31)/8
  805. HIMAGELIST CToolBarEx :: GetImageList() {
  806. m_bDeleteImgList = FALSE;
  807. HIMAGELIST hImg = 0;
  808. #ifdef TB_GETIMAGELIST
  809. // Some older versions of VC++ do not know of
  810. // the TB_GETIMAGELIST macro (defined in commctrl.h).
  811. hImg = HIMAGELIST(SendMessage(TB_GETIMAGELIST));
  812. #ifdef _DEBUG
  813. if( hImg == 0 ) {
  814. TRACE0("CToolBarEx::GetImageList(): could not get image listn");
  815. }
  816. #endif
  817. #endif // TB_GETIMAGELIST
  818. if( ! hImg ) {
  819. // comctl32.dll version prior to 4.70 doesn't know
  820. // anything of the TB_GETIMAGELIST message
  821. if( m_hbmImageWell != 0 ) {
  822. // Yep - we have a valid image.
  823. // But beware: Do not use this bitmap directly.
  824. // We make the copy by ourself. CopyImage() (for
  825. // instace) produces inacceptable copies under
  826. // some circumstances ...
  827. CImageList imglist;
  828. CBitmap bmp;
  829. // retrieve the size of the bitmap
  830. BITMAP bmHdr;
  831. ::GetObject(m_hbmImageWell, sizeof(BITMAP), &bmHdr);
  832. DWORD dwWidth, dwHeight = bmHdr.bmHeight;
  833. if (bmHdr.bmBitsPixel > 8)
  834. dwWidth = PADWIDTH(bmHdr.bmWidth * 3);
  835. else
  836. dwWidth = PADWIDTH(bmHdr.bmWidth);
  837. // copy the bitmap
  838. CClientDC cdc(this);
  839. CDC dc1, dc2;
  840. dc1.CreateCompatibleDC(&cdc);
  841. dc2.CreateCompatibleDC(&cdc);
  842. bmp.CreateCompatibleBitmap(&cdc, dwWidth, dwHeight);
  843. CBitmap * pOBmp = dc1.SelectObject(&bmp);
  844. HGDIOBJ hOObj = ::SelectObject(dc2.GetSafeHdc(), m_hbmImageWell);
  845. dc1.BitBlt(0,0,dwWidth,dwHeight,&dc2,0,0,SRCCOPY);
  846. ::SelectObject(dc2.GetSafeHdc(), hOObj);
  847. dc1.SelectObject(pOBmp);
  848. dc1.DeleteDC();
  849. dc2.DeleteDC();
  850. imglist.Create(m_sizeImage.cx, m_sizeImage.cy,TRUE,dwWidth/m_sizeImage.cx,1);
  851. imglist.SetBkColor(m_clrBtnFace);
  852. imglist.Add(&bmp,m_clrBtnFace);
  853. hImg = imglist.Detach();
  854. bmp.DeleteObject();
  855. m_bDeleteImgList = TRUE;
  856. }
  857. }
  858. return hImg;
  859. }
  860. void CToolBarEx::InvalidateButton(int nIndex) {
  861. if( nIndex < 0 || nIndex >= GetToolBarCtrl().GetButtonCount() )
  862. return;
  863. CRect rc;
  864. GetItemRect(nIndex, rc);
  865. InvalidateRect(rc, FALSE);
  866. }
  867. BOOL CToolBarEx :: IsSeparator(int nIndex) const {
  868. if( nIndex >= 0 && nIndex < GetToolBarCtrl().GetButtonCount() )
  869. if( GetButtonStyle(nIndex) == TBBS_SEPARATOR ) {
  870. // make sure this is a "real" separator
  871. CRect rc;
  872. GetItemRect(nIndex, rc);
  873. if( rc.Width() <= 8 )
  874. return TRUE;
  875. }
  876. return FALSE;
  877. }
  878. BOOL CToolBarEx :: IsControl(int nIndex) const {
  879. if( nIndex >= 0 && nIndex < GetToolBarCtrl().GetButtonCount() )
  880. if( GetButtonStyle(nIndex) == TBBS_SEPARATOR ) {
  881. // make sure this is a placeholder for a control
  882. CRect rc;
  883. GetItemRect(nIndex, rc);
  884. if( rc.Width() > 8 )
  885. return TRUE;
  886. }
  887. return FALSE;
  888. }
  889. CWnd * CToolBarEx :: GetControl(int idx, BOOL bIdxIsID) const {
  890. UINT uID = bIdxIsID ? UINT(idx) : GetItemID(idx);
  891. for( CWnd * pWnd = GetWindow(GW_CHILD); pWnd; pWnd = pWnd->GetNextWindow() )
  892. if( UINT(::GetWindowLong(pWnd->GetSafeHwnd(), GWL_ID)) == uID )
  893. return pWnd;
  894. return 0;
  895. }
  896. CWnd * CToolBarEx :: CtrlReplace(
  897. CRuntimeClass * pClass,
  898. CRect & rc,
  899. UINT ID,
  900. DWORD dwStyle ) {
  901. int nIndex = CommandToIndex(ID);
  902. if(nIndex < 0) {
  903. TRACE1("CToolBarEx::CtrlReplace(): 0x%x is not a valid ID for this toolbar.n,", ID);
  904. return 0;
  905. }
  906. int nWidth = rc.Width();
  907. if( nWidth < 0 )
  908. nWidth = -nWidth;
  909. SetButtonInfo(nIndex, ID, TBBS_SEPARATOR, nWidth);
  910. CWnd * pCtrl = CreateControl(pClass, rc, ID, dwStyle);
  911. return pCtrl;
  912. }
  913. CWnd * CToolBarEx :: CtrlInsert(
  914. CRuntimeClass * pClass,
  915. CRect & rc,
  916. UINT ID,
  917. int nBefore,
  918. DWORD dwStyle) {
  919. BOOL bAppend = FALSE;
  920. CToolBarCtrl & wndToolBarCtrl = GetToolBarCtrl();
  921. if( nBefore < 0 || nBefore > wndToolBarCtrl.GetButtonCount() )
  922. bAppend = TRUE;
  923. int nWidth = rc.Width();
  924. if( nWidth < 0 )
  925. nWidth = -nWidth;
  926. TBBUTTON tbButton;
  927. tbButton.iBitmap = nWidth;
  928. tbButton.idCommand = ID;
  929. tbButton.fsState = TBSTATE_ENABLED;
  930. tbButton.fsStyle = TBSTYLE_SEP;
  931. tbButton.dwData = 0;
  932. tbButton.iString = 0;
  933. if( bAppend )
  934. wndToolBarCtrl.AddButtons(1, &tbButton);
  935. else
  936. wndToolBarCtrl.InsertButton(nBefore, &tbButton);
  937. CWnd * pCtrl = CreateControl(pClass, rc, ID, dwStyle);
  938. return pCtrl;
  939. }
  940. CWnd * CToolBarEx :: CreateControl(
  941. CRuntimeClass * pClass,
  942. CRect & rc,
  943. UINT ID,
  944. DWORD dwStyle) {
  945. if( ! pClass || ! pClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)) ) {
  946. TRACE0("CToolBarEx::CreateControl(): given class is NULL or not derived from CWnd.n");
  947. return 0;
  948. }
  949. CWnd * pCtrl = 0;
  950. BOOL bSelfDeleting = TRUE;
  951. CString strClass = pClass->m_lpszClassName;
  952. if( strClass == TEXT("CComboBox") ) {
  953. pCtrl = new CComboBox();
  954. bSelfDeleting = FALSE;
  955. } else if( strClass == TEXT("CEdit") ) {
  956. pCtrl = new CEdit();
  957. bSelfDeleting = FALSE;
  958. } else {
  959. pCtrl = (CWnd *)pClass->CreateObject();
  960. if( pCtrl == 0 ) {
  961. TRACE1("CToolBarEx::CreateControl(): dynamic create of control %hs failed.n",
  962. pClass->m_lpszClassName);
  963. return 0;
  964. }
  965. }
  966. // create the control itself
  967. CRect rect = rc;
  968. BOOL bCreate = FALSE;
  969. if( pCtrl->IsKindOf(RUNTIME_CLASS(CComboBox)) )
  970. bCreate = ((CComboBox*)pCtrl)->Create(WS_CHILD|WS_VISIBLE|dwStyle, rect, this, ID);
  971. else if( pCtrl->IsKindOf(RUNTIME_CLASS(CEdit)) )
  972. bCreate = ((CEdit*)pCtrl)->Create(WS_CHILD|WS_VISIBLE|dwStyle, rect, this, ID);
  973. else
  974. bCreate = pCtrl->Create(0, 0, WS_CHILD|WS_VISIBLE|dwStyle, rect, this, ID);
  975. if( ! bCreate ) {
  976. TRACE1("CToolBarEx::CreateControl(): could not Create() control.n,", ID);
  977. if( ! bSelfDeleting )
  978. delete pCtrl;
  979. return 0;
  980. }
  981. if( ! bSelfDeleting ) {
  982. // we have to remember this control, so we can delete it later
  983. if( ! m_pControls )
  984. m_pControls = new CObList();
  985. m_pControls->AddTail(pCtrl);
  986. }
  987. RepositionControls();
  988. return pCtrl;
  989. }
  990. void CToolBarEx :: RepositionControls() {
  991. // Walk all descendents.
  992. for( CWnd * pWnd = GetWindow(GW_CHILD); pWnd; pWnd = pWnd->GetNextWindow() ) {
  993. // If that window is a child of this
  994. // toolbar, then reposition this control.
  995. if( pWnd->GetParent() == this ) {
  996. DWORD dwID = ::GetWindowLong(pWnd->GetSafeHwnd(), GWL_ID);
  997. int idx = CommandToIndex(dwID);
  998. ASSERT(idx >= 0);
  999. CRect rc;
  1000. GetItemRect(idx, rc);
  1001. pWnd->SetWindowPos(0, rc.left, rc.top, 0, 0, SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOCOPYBITS);
  1002. pWnd->ShowWindow(SW_SHOW);
  1003. }
  1004. }
  1005. }
  1006. void CToolBarEx :: RecalcLayout() {
  1007. // Recalculate the size of the bar.
  1008. BOOL bHorz = (m_dwStyle & CBRS_ORIENT_HORZ) != 0;
  1009. if ((m_dwStyle & CBRS_FLOATING) && (m_dwStyle & CBRS_SIZE_DYNAMIC))
  1010. CalcDynamicLayout(0, LM_HORZ | LM_MRUWIDTH | LM_COMMIT);
  1011. else if (bHorz)
  1012. CalcDynamicLayout(0, LM_HORZ | LM_HORZDOCK | LM_COMMIT);
  1013. else
  1014. CalcDynamicLayout(0, LM_VERTDOCK | LM_COMMIT);
  1015. RepositionControls();
  1016. // recalculate the parent frame
  1017. if( m_dwStyle & CBRS_FLOATING ) {
  1018. ASSERT(m_pDockBar != 0);
  1019. ((CMiniDockFrameWnd *)m_pDockBar->GetParent())->RecalcLayout();
  1020. } else {
  1021. ((CFrameWnd*)GetParentFrame())->RecalcLayout();
  1022. }
  1023. }
  1024. HBITMAP CToolBarEx :: GetBitmap(int nBtnID) {
  1025. int nBitmap = SendMessage(TB_GETBITMAP, WPARAM(nBtnID));
  1026. return GetBitmap(nBitmap, m_sizeImage);
  1027. }
  1028. HBITMAP CToolBarEx :: GetBitmap(int nIndex, const CSize & sizeBitmap) {
  1029. HIMAGELIST hImgList = GetImageList();
  1030. if( ! hImgList )
  1031. return 0;
  1032. CImageList imglist;
  1033. imglist.Attach(hImgList);
  1034. HICON hIcon = imglist.ExtractIcon(nIndex);
  1035. CBitmap bmp;
  1036. if( hIcon ) {
  1037. CClientDC cdc(this) ;
  1038. CDC dc;
  1039. dc.CreateCompatibleDC(&cdc);
  1040. VERIFY(bmp.CreateCompatibleBitmap(&cdc, sizeBitmap.cx, sizeBitmap.cy));
  1041. CBitmap* pOldBmp = dc.SelectObject(&bmp);
  1042. CBrush brush ;
  1043. VERIFY(brush.CreateSolidBrush(m_clrBtnFace));
  1044. ::DrawIconEx(
  1045. dc.GetSafeHdc(),
  1046. 0,
  1047. 0,
  1048. hIcon,
  1049. sizeBitmap.cx,
  1050. sizeBitmap.cy,
  1051. 0,
  1052. (HBRUSH)brush,
  1053. DI_NORMAL
  1054. );
  1055. dc.SelectObject( pOldBmp );
  1056. dc.DeleteDC();
  1057. // the icon is not longer needed
  1058. DestroyIcon(hIcon);
  1059. } else
  1060. TRACE1("CToolBarEx::GetBitmap(): unable to extract bitmap with index %dn", nIndex);
  1061. if( ! m_bDeleteImgList )
  1062. imglist.Detach();
  1063. return hIcon ? HBITMAP(bmp.Detach()) : 0;
  1064. }
  1065. LRESULT CToolBarEx::OnAddBitmap(WPARAM wParam, LPARAM lParam) {
  1066. // work around a bug in CToolBar:
  1067. // if one calls CToolBar::GetToolBarCtrl().AddBitmap(...),
  1068. // then CToolBar does not realize this. This can lead to
  1069. // confusing button images ...
  1070. int nButtons = int(wParam);
  1071. LPTBADDBITMAP pAddBmp = LPTBADDBITMAP(lParam);
  1072. if( pAddBmp->hInst != HINST_COMMCTRL ) {
  1073. // This workaround does not work, if one
  1074. // specifies "HINST_COMMCTRL" to the "hInst"
  1075. // member of pAddBmp, because we cannot access
  1076. // the internals of commctl32.dll ...
  1077. TRACE0("Adding a bitmapn");
  1078. HBITMAP hBitmap;
  1079. if( pAddBmp->hInst != 0 )
  1080. // Have to load the bitmap "pAddBmp->nID"
  1081. // contains the resource-ID of the bitmap.
  1082. hBitmap = ::LoadBitmap(pAddBmp->hInst, MAKEINTRESOURCE(pAddBmp->nID));
  1083. else
  1084. // "pAddBmp->nID" is the handle of the bitmap.
  1085. hBitmap = HBITMAP(pAddBmp->nID);
  1086. if(hBitmap == 0)
  1087. return HRESULT(-1);
  1088. // You really should use CToolBarEx as a replacement for
  1089. // CToolBar. So make sure you have set up a toolbar
  1090. // properly before you begin to customize it.
  1091. ASSERT(m_hbmImageWell);
  1092. // retrieve number of images currently stored in CToolBar
  1093. BITMAP bitmap;
  1094. VERIFY(::GetObject(m_hbmImageWell, sizeof(BITMAP), &bitmap));
  1095. int nImageCount = bitmap.bmWidth / m_sizeImage.cx;
  1096. CClientDC cdc(this);
  1097. CDC dcOld, dcNew;
  1098. dcOld.CreateCompatibleDC(&cdc);
  1099. dcNew.CreateCompatibleDC(&cdc);
  1100. HGDIOBJ hOldBmp = ::SelectObject(dcOld.GetSafeHdc(), m_hbmImageWell);
  1101. // create the new bitmap and make it big enough to
  1102. // hold all images (old+new)
  1103. CBitmap bmpNew;
  1104. bmpNew.CreateCompatibleBitmap(
  1105. &cdc,
  1106. bitmap.bmWidth + nButtons * m_sizeImage.cx,
  1107. m_sizeImage.cy);
  1108. CBitmap * pbmpNew = dcNew.SelectObject(&bmpNew);
  1109. dcNew.BitBlt(0,0,bitmap.bmWidth,bitmap.bmHeight,&dcOld,0,0,SRCCOPY);
  1110. ::SelectObject(dcOld.GetSafeHdc(), hBitmap);
  1111. dcNew.BitBlt(bitmap.bmWidth,0,m_sizeImage.cx*nButtons,bitmap.bmHeight,&dcOld,0,0,SRCCOPY);
  1112. ::SelectObject(dcOld.GetSafeHdc(), hOldBmp);
  1113. dcNew.SelectObject(pbmpNew);
  1114. dcOld.DeleteDC();
  1115. dcNew.DeleteDC();
  1116. VERIFY(bmpNew.GetObject(sizeof(BITMAP), &bitmap));
  1117. HRESULT hRes = DefWindowProc(TB_ADDBITMAP, wParam, lParam);
  1118. // syncronize toolbarcontrol's bitmap with our's
  1119. AddReplaceBitmap(HBITMAP(bmpNew.Detach()));
  1120. return HRESULT(nImageCount);
  1121. }
  1122. return DefWindowProc(TB_ADDBITMAP, wParam, lParam);
  1123. }
  1124. // intercept TB_DELETEBUTTON, so we can delete controls too.
  1125. LRESULT CToolBarEx::OnDeleteButton(WPARAM wParam, LPARAM lParam) {
  1126. CWnd * pControl = GetControl(int(wParam));
  1127. if( pControl ) {
  1128. // this is the control associated with the button to delete
  1129. BOOL bMustDelete = FALSE;
  1130. if( m_pControls ) {
  1131. // It is really a good idea to add a control via the
  1132. // CToolBarEx own members. This will guarantee that
  1133. // all resources are freed.
  1134. POSITION pos = m_pControls->Find(pControl);
  1135. if( pos ) {
  1136. m_pControls->RemoveAt(pos);
  1137. bMustDelete = TRUE;
  1138. }
  1139. }
  1140. pControl->DestroyWindow();
  1141. if( bMustDelete )
  1142. delete pControl;
  1143. }
  1144. return DefWindowProc(TB_DELETEBUTTON, wParam, lParam);
  1145. }
  1146. #ifdef _MEMDC_H_
  1147. BOOL CToolBarEx::OnEraseBkgnd(CDC* pDC) 
  1148. {
  1149. return IsFlatLook() ? FALSE : CToolBar::OnEraseBkgnd(pDC);
  1150. }
  1151. #endif
  1152. /////////////////////////////////////////////////////////////////////////////
  1153. // ALT-drag
  1154. // To keep the users of CToolBarEx from inserting cursor-resources, we
  1155. // create the cursors on the fly. This makes usage of CToolBarEx as easy as
  1156. // possible:
  1157. static const BYTE ANDmaskDrop[] = { 
  1158.     0xFF, 0xFF, 0xFF, 0xFF,   // line 1
  1159.     0xFF, 0xFF, 0xFF, 0xFF,   // line 2
  1160.     0xF3, 0xFF, 0xFF, 0xFF,   // line 3
  1161.     0xF1, 0xFF, 0xFF, 0xFF,   // line 4
  1162.  
  1163.     0xF0, 0xFF, 0xFF, 0xFF,   // line 5
  1164.     0xF0, 0x7F, 0xFF, 0xFF,   // line 6
  1165.     0xF0, 0x3F, 0xFF, 0xFF,   // line 7
  1166.     0xF0, 0x1F, 0xFF, 0xFF,   // line 8
  1167.  
  1168.     0xF0, 0x0F, 0xFF, 0xFF,   // line 9
  1169.     0xF0, 0x07, 0xFF, 0xFF,   // line 10
  1170.     0xF0, 0x03, 0xFF, 0xFF,   // line 11
  1171.     0xF0, 0x01, 0xFF, 0xFF,   // line 12
  1172.  
  1173.     0xF0, 0x00, 0xFF, 0xFF,   // line 13
  1174.     0xF0, 0x0F, 0xFF, 0xFF,   // line 14
  1175.     0xF0, 0x0F, 0xFF, 0xFF,   // line 15
  1176.     0xF1, 0x07, 0xFF, 0xFF,   // line 16
  1177.  
  1178.     0xF3, 0x07, 0xFF, 0xFF,   // line 17
  1179.     0xF6, 0x00, 0x00, 0x3F,   // line 18
  1180.     0xFE, 0x00, 0x00, 0x3F,   // line 19
  1181.     0xFE, 0x00, 0x00, 0x3F,   // line 20
  1182.  
  1183.     0xFE, 0x00, 0x00, 0x3F,   // line 21
  1184.     0xFE, 0x00, 0x00, 0x3F,   // line 22
  1185.     0xFE, 0x00, 0x00, 0x3F,   // line 23
  1186.     0xFE, 0x00, 0x00, 0x3F,   // line 24
  1187.  
  1188.     0xFE, 0x00, 0x00, 0x3F,   // line 25
  1189.     0xFE, 0x00, 0x00, 0x3F,   // line 26
  1190.     0xFF, 0xFF, 0xFF, 0xFF,   // line 27
  1191.     0xFF, 0xFF, 0xFF, 0xFF,   // line 28
  1192.  
  1193.     0xFF, 0xFF, 0xFF, 0xFF,   // line 29
  1194.     0xFF, 0xFF, 0xFF, 0xFF,   // line 30
  1195.     0xFF, 0xFF, 0xFF, 0xFF,   // line 31
  1196.     0xFF, 0xFF, 0xFF, 0xFF    // line 32
  1197. };
  1198.  
  1199. static const BYTE XORmaskDrop[] = { 
  1200.     0x00, 0x00, 0x00, 0x00,   // line 1
  1201.     0x00, 0x00, 0x00, 0x00,   // line 2
  1202.     0x00, 0x00, 0x00, 0x00,   // line 3
  1203.     0x04, 0x00, 0x00, 0x00,   // line 4
  1204.  
  1205.     0x06, 0x00, 0x00, 0x00,   // line 5
  1206.     0x07, 0x00, 0x00, 0x00,   // line 6
  1207.     0x07, 0x80, 0x00, 0x00,   // line 7
  1208.     0x07, 0xC0, 0x00, 0x00,   // line 8
  1209.  
  1210.     0x07, 0xE0, 0x00, 0x00,   // line 9
  1211.     0x07, 0xF0, 0x00, 0x00,   // line 10
  1212.     0x07, 0xF8, 0x00, 0x00,   // line 11
  1213.     0x07, 0xFC, 0x00, 0x00,   // line 12
  1214.  
  1215.     0x07, 0xE0, 0x00, 0x00,   // line 13
  1216.     0x07, 0x60, 0x00, 0x00,   // line 14
  1217.     0x06, 0x60, 0x00, 0x00,   // line 15
  1218.     0x04, 0x30, 0x00, 0x00,   // line 16
  1219.  
  1220.     0x00, 0x30, 0x00, 0x00,   // line 17
  1221.     0x00, 0x18, 0x00, 0x00,   // line 18
  1222.     0x00, 0xDD, 0xFF, 0x00,   // line 19
  1223.     0x00, 0xAC, 0xAA, 0x00,   // line 20
  1224.  
  1225.     0x00, 0xCD, 0x55, 0x00,   // line 21
  1226.     0x00, 0xA0, 0xAA, 0x00,   // line 22
  1227.     0x00, 0xD5, 0x55, 0x00,   // line 23
  1228.     0x00, 0xAA, 0xAA, 0x00,   // line 24
  1229.  
  1230.     0x00, 0x00, 0x00, 0x00,   // line 25
  1231.     0x00, 0x00, 0x00, 0x00,   // line 26
  1232.     0x00, 0x00, 0x00, 0x00,   // line 27
  1233.     0x00, 0x00, 0x00, 0x00,   // line 28
  1234.  
  1235.     0x00, 0x00, 0x00, 0x00,   // line 29
  1236.     0x00, 0x00, 0x00, 0x00,   // line 30
  1237.     0x00, 0x00, 0x00, 0x00,   // line 31
  1238.     0x00, 0x00, 0x00, 0x00    // line 32
  1239. };
  1240. static const BYTE ANDmaskNoDrop[] = { 
  1241.     0xFF, 0xFF, 0xFF, 0xFF,   // line 1
  1242.     0xFF, 0xFF, 0xFF, 0xFF,   // line 2
  1243.     0xF3, 0xFF, 0xFF, 0xFF,   // line 3
  1244.     0xF1, 0xFF, 0xFF, 0xFF,   // line 4
  1245.  
  1246.     0xF0, 0xFF, 0xFF, 0xFF,   // line 5
  1247.     0xF0, 0x7F, 0xFF, 0xFF,   // line 6
  1248.     0xF0, 0x3F, 0xFF, 0xFF,   // line 7
  1249.     0xF0, 0x1F, 0xFF, 0xFF,   // line 8
  1250.  
  1251.     0xF0, 0x0F, 0xFF, 0xFF,   // line 9
  1252.     0xF0, 0x07, 0xFF, 0xFF,   // line 10
  1253.     0xF0, 0x03, 0xFF, 0xFF,   // line 11
  1254.     0xF0, 0x01, 0xFF, 0xFF,   // line 12
  1255.  
  1256.     0xF0, 0x00, 0xFF, 0xFF,   // line 13
  1257.     0xF0, 0x0F, 0xFF, 0xFF,   // line 14
  1258.     0xF0, 0x0F, 0xFF, 0xFF,   // line 15
  1259.     0xF1, 0x07, 0xFF, 0xFF,   // line 16
  1260.  
  1261.     0xF3, 0x07, 0xFF, 0xFF,   // line 17
  1262.     0xF6, 0x00, 0x00, 0x3F,   // line 18
  1263.     0xFE, 0x00, 0x00, 0x3F,   // line 19
  1264.     0xFE, 0x00, 0x00, 0x3F,   // line 20
  1265.  
  1266.     0xFE, 0x00, 0x00, 0x3F,   // line 21
  1267.     0xFE, 0x00, 0x00, 0x0F,   // line 22
  1268.     0xFE, 0x00, 0x00, 0x0F,   // line 23
  1269.     0xFE, 0x00, 0x00, 0x0F,   // line 24
  1270.  
  1271.     0xFE, 0x00, 0x00, 0x0F,   // line 25
  1272.     0xFE, 0x00, 0x00, 0x0F,   // line 26
  1273.     0xFF, 0xFF, 0x80, 0x0F,   // line 27
  1274.     0xFF, 0xFF, 0x80, 0x0F,   // line 28
  1275.  
  1276.     0xFF, 0xFF, 0x80, 0x0F,   // line 29
  1277.     0xFF, 0xFF, 0x80, 0x0F,   // line 30
  1278.     0xFF, 0xFF, 0x80, 0x0F,   // line 31
  1279.     0xFF, 0xFF, 0x80, 0x0F    // line 32
  1280. };
  1281.  
  1282. static const BYTE XORmaskNoDrop[] = {
  1283.     0x00, 0x00, 0x00, 0x00,   // line 1
  1284.     0x00, 0x00, 0x00, 0x00,   // line 2
  1285.     0x00, 0x00, 0x00, 0x00,   // line 3
  1286.     0x04, 0x00, 0x00, 0x00,   // line 4
  1287.  
  1288.     0x06, 0x00, 0x00, 0x00,   // line 5
  1289.     0x07, 0x00, 0x00, 0x00,   // line 6
  1290.     0x07, 0x80, 0x00, 0x00,   // line 7
  1291.     0x07, 0xC0, 0x00, 0x00,   // line 8
  1292.  
  1293.     0x07, 0xE0, 0x00, 0x00,   // line 9
  1294.     0x07, 0xF0, 0x00, 0x00,   // line 10
  1295.     0x07, 0xF8, 0x00, 0x00,   // line 11
  1296.     0x07, 0xFC, 0x00, 0x00,   // line 12
  1297.  
  1298.     0x07, 0xE0, 0x00, 0x00,   // line 13
  1299.     0x07, 0x60, 0x00, 0x00,   // line 14
  1300.     0x06, 0x60, 0x00, 0x00,   // line 15
  1301.     0x04, 0x30, 0x00, 0x00,   // line 16
  1302.  
  1303.     0x00, 0x30, 0x00, 0x00,   // line 17
  1304.     0x00, 0x18, 0x00, 0x00,   // line 18
  1305.     0x00, 0xDD, 0xFF, 0x00,   // line 19
  1306.     0x00, 0xAC, 0xAA, 0x00,   // line 20
  1307.  
  1308.     0x00, 0xCD, 0x55, 0x00,   // line 21
  1309.     0x00, 0xA0, 0x80, 0x00,   // line 22
  1310.     0x00, 0xD5, 0x3F, 0xE0,   // line 23
  1311.     0x00, 0xAA, 0xA7, 0x20,   // line 24
  1312.  
  1313.     0x00, 0x00, 0x22, 0x20,   // line 25
  1314.     0x00, 0x00, 0x30, 0x60,   // line 26
  1315.     0x00, 0x00, 0x38, 0xE0,   // line 27
  1316.     0x00, 0x00, 0x30, 0x60,   // line 28
  1317.  
  1318.     0x00, 0x00, 0x22, 0x20,   // line 29
  1319.     0x00, 0x00, 0x27, 0x20,   // line 30
  1320.     0x00, 0x00, 0x3F, 0xE0,   // line 31
  1321.     0x00, 0x00, 0x00, 0x00    // line 32
  1322. };
  1323. void CToolBarEx::OnLButtonDown(UINT nFlags, CPoint point) 
  1324. {
  1325. if( ::GetAsyncKeyState(VK_MENU) & (1<<15) )
  1326. // one of the ALT keys is pressed too - begin drag operation
  1327. if( BeginDrag() )
  1328. return;
  1329. if( ::GetAsyncKeyState(VK_SHIFT) & (1<<15) )
  1330. // disable the old-style drag
  1331. return;
  1332. CToolBar::OnLButtonDown(nFlags, point);
  1333. }
  1334. void CToolBarEx::OnLButtonUp(UINT nFlags, CPoint point) 
  1335. {
  1336. if( m_bDragging )
  1337. EndDrag();
  1338. else
  1339. CToolBar::OnLButtonUp(nFlags, point);
  1340. }
  1341. BOOL CToolBarEx :: BeginDrag() {
  1342. TRACE0("beginning drag operationn");
  1343. VERIFY(!m_hDragCursor);
  1344. VERIFY(!m_hNoDragCursor);
  1345. if( !(::GetWindowLong(GetToolBarCtrl().GetSafeHwnd(),GWL_STYLE) & CCS_ADJUSTABLE) )
  1346. return FALSE; // Bar is not adjustable
  1347. register const int nBtn = GetToolBarCtrl().GetButtonCount();
  1348. const int nLastBtn = m_nLastBtn;
  1349. m_nLastBtn = -1;
  1350. CPoint pt;
  1351. ::GetCursorPos(&pt);
  1352. ScreenToClient(&pt);
  1353. m_nDragButton = -1;
  1354. // have a look for whether the button is valid and
  1355. // - if so - draw a dragging border around it.
  1356. for( register int i = 0 ; i < nBtn ; ++i ) {
  1357. if( IsSeparator(i) )
  1358. continue; // real separators are not draggable
  1359. CRect rc;
  1360. GetItemRect(i, rc);
  1361. const BOOL bHasCursor = rc.PtInRect(pt);
  1362. if( bHasCursor ) {
  1363. // OK we've found the button. Now ask for deletion:
  1364. if( ! DoQueryDelete(i) )
  1365. // the app does not allow the removal ...
  1366. return FALSE;
  1367. m_nDragButton = i;
  1368. CClientDC cdc(this);
  1369. cdc.DrawDragRect(rc, CSize(2,2),0,CSize(0,0));
  1370. break;
  1371. }
  1372. }
  1373. if( m_nDragButton < 0 )
  1374. return FALSE; // nothing to drag ...
  1375. VERIFY(m_hDragCursor = ::CreateCursor(0, 4, 2, 32, 32, ANDmaskDrop, XORmaskDrop));
  1376. VERIFY(m_hNoDragCursor = ::CreateCursor(0, 4, 2, 32, 32, ANDmaskNoDrop, XORmaskNoDrop));
  1377. // capture the mouse during the drag operation
  1378. SetCapture();
  1379. // make sure we receive keyboard-input
  1380. SetFocus();
  1381. m_hOrigCursor = ::SetCursor(m_hDragCursor);
  1382. m_bDragCursor = TRUE;
  1383. m_bDragging = TRUE;
  1384. ::GetCursorPos(&pt);
  1385. SetMarker(m_pDropBar=this, m_ptDrop=pt);
  1386. return m_bDragging;
  1387. }
  1388. BOOL CToolBarEx :: IsValidDropTarget(const CWnd * pWnd) const {
  1389. return (pWnd &&
  1390. pWnd->IsKindOf(RUNTIME_CLASS(CToolBarEx)) &&
  1391. (::GetWindowLong(((CToolBarEx*)pWnd)->GetToolBarCtrl()
  1392. .GetSafeHwnd(),GWL_STYLE) & CCS_ADJUSTABLE) &&
  1393. ((const CToolBarEx*)pWnd)->GetParentFrame() == GetParentFrame())
  1394. ? TRUE : FALSE;
  1395. }
  1396. void CToolBarEx :: DragMove() {
  1397. //TRACE0("dragmove in progressn");
  1398. CPoint pt;
  1399. ::GetCursorPos(&pt);
  1400. const CWnd * pWnd = WindowFromPoint(pt);
  1401. // is the cursor moving over an adjustable toolbar ?
  1402. BOOL bToolBar = IsValidDropTarget(pWnd);
  1403. // If the window under the cursor is not a toolbar, then
  1404. // check whether this window is a child of a toolbar.
  1405. while( ! bToolBar && (pWnd = pWnd->GetParent()) != 0 )
  1406. bToolBar = IsValidDropTarget(pWnd);
  1407. // check whether we have to switch the cursor
  1408. if( bToolBar && ! m_bDragCursor ) {
  1409. ::SetCursor(m_hDragCursor);
  1410. m_bDragCursor = TRUE;
  1411. } else if( ! bToolBar && m_bDragCursor ) {
  1412. ::SetCursor(m_hNoDragCursor);
  1413. m_bDragCursor = FALSE;
  1414. }
  1415. SetMarker(m_pDropBar = (bToolBar ? (CToolBarEx*)pWnd : 0), m_ptDrop = pt);
  1416. }
  1417. void CToolBarEx :: EndDrag(BOOL bDoMove) {
  1418. TRACE0("ending drag operationn");
  1419. // remove the marker
  1420. SetMarker(0, CPoint(0,0));
  1421. VERIFY(::SetCursor(m_hOrigCursor));
  1422. ::DestroyCursor(m_hDragCursor);
  1423. ::DestroyCursor(m_hNoDragCursor);
  1424. m_hDragCursor = 0;
  1425. m_hNoDragCursor = 0;
  1426. if( m_nDragButton >= 0 ) {
  1427. CToolBarCtrl & wndTBCtrl = GetToolBarCtrl();
  1428. register const int nBtn = wndTBCtrl.GetButtonCount();
  1429. if( m_bDragCursor && bDoMove ) {
  1430. // move the button to a different location
  1431. // make sure the last "DragMove()" has done its work correctly:
  1432. ASSERT(m_pDropBar != 0);
  1433. ASSERT_KINDOF(CToolBarEx, m_pDropBar);
  1434. ASSERT(::GetWindowLong(m_pDropBar->GetToolBarCtrl()
  1435. .GetSafeHwnd(),GWL_STYLE) & CCS_ADJUSTABLE);
  1436. // have a look for where to drop the button
  1437. int nDropBtn = m_pDropBar->FindDropButton(m_ptDrop);
  1438. TBBUTTON tbButton;
  1439. memset(&tbButton, 0, sizeof(TBBUTTON)); // not the safest, but the easiest way
  1440. // to zero out all members ;-)
  1441. if( m_pDropBar == this ) {
  1442. // move the button around, but stay on *this* toolbar
  1443. if( nDropBtn == m_nDragButton+1 || (nDropBtn < 0 && m_nDragButton == nBtn-1) ) {
  1444. // simply insert a separator before the dragged button,
  1445. // if there is still none
  1446. if( m_nDragButton > 0 && !IsSeparator(m_nDragButton-1) ) {
  1447. tbButton.iBitmap = 8;
  1448. tbButton.fsState = TBSTATE_ENABLED;
  1449. tbButton.fsStyle = TBSTYLE_SEP;
  1450. if( DoQueryInsert(tbButton, m_nDragButton) )
  1451. wndTBCtrl.InsertButton(m_nDragButton, &tbButton);
  1452. }
  1453. } else if( nDropBtn == m_nDragButton && m_nDragButton > 0 ) {
  1454. // Remove the separator immediately before the dragged button.
  1455. // if there is no such separator, then do nothing
  1456. if( IsSeparator(nDropBtn-1) )
  1457. if( DoQueryDelete(nDropBtn-1) )
  1458. wndTBCtrl.DeleteButton(nDropBtn-1);
  1459. } else {
  1460. wndTBCtrl.GetButton(m_nDragButton, &tbButton);
  1461. if( DoQueryInsert(tbButton, (nDropBtn>=0) ? nDropBtn : nBtn) ) {
  1462. CWnd * pControl = 0;
  1463. if( IsControl(m_nDragButton) ) {
  1464. // Beware: The TB_DELETEBUTTON message causes the toolbar
  1465. // to destroy the associated control.
  1466. // To avoid this we temporary set the parent of the
  1467. // control to NULL.
  1468. pControl = GetControl(m_nDragButton);
  1469. VERIFY(pControl != 0);
  1470. pControl->SetParent(0);
  1471. }
  1472. if( nDropBtn >= 0 )
  1473. // have to insert
  1474. wndTBCtrl.InsertButton(nDropBtn, &tbButton);
  1475. else
  1476. // append the button
  1477. wndTBCtrl.AddButtons(1, &tbButton);
  1478. // delete the button at its original location
  1479. // we do not need to ask the owner, because this
  1480. // was already done in "BeginDrag()"
  1481. wndTBCtrl.DeleteButton(
  1482. (m_nDragButton < nDropBtn || nDropBtn < 0)
  1483. ? m_nDragButton
  1484. : m_nDragButton+1
  1485. );
  1486. // Reconnect the control (if any)
  1487. if( pControl )
  1488. pControl->SetParent(this);
  1489. if( m_nDragButton == wndTBCtrl.GetButtonCount()-1 )
  1490. // remove trailing separators too
  1491. RemoveTrailingSeparators();
  1492. }
  1493. }
  1494. } else {
  1495. // move the button to a different toolbar
  1496. wndTBCtrl.GetButton(m_nDragButton, &tbButton);
  1497. CToolBarCtrl & wndDropTBCtrl = m_pDropBar->GetToolBarCtrl();
  1498. if( m_pDropBar->DoQueryInsert(tbButton, (nDropBtn>=0) ? nDropBtn : wndDropTBCtrl.GetButtonCount()) ) {
  1499. // Get the bitmap of the dragged button and resize it to
  1500. // the image-size of the destination-bar.
  1501. int nDestBitmap = 0;
  1502. if( IsControl(m_nDragButton) ) {
  1503. CRect rc;
  1504. GetItemRect(m_nDragButton, rc);
  1505. nDestBitmap = rc.Width();
  1506. } else {
  1507. HBITMAP hBmp = GetBitmap(tbButton.iBitmap, m_pDropBar->m_sizeImage);
  1508. CBitmap bmp;
  1509. if( hBmp ) {
  1510. bmp.Attach(hBmp);
  1511. nDestBitmap = wndDropTBCtrl.AddBitmap(1, &bmp);
  1512. }
  1513. }
  1514. tbButton.iBitmap = nDestBitmap;
  1515. //tbButton.iString = nDestString;
  1516. tbButton.iString = -1;
  1517. BOOL bInsertOK;
  1518. if( nDropBtn >= 0 )
  1519. bInsertOK = wndDropTBCtrl.InsertButton(nDropBtn, &tbButton);
  1520. else
  1521. bInsertOK = wndDropTBCtrl.AddButtons(1, &tbButton);
  1522. if( bInsertOK ) {
  1523. // transfer the string too, if any (check target first)
  1524. if( m_pDropBar->HasButtonText() && HasButtonText() )
  1525. {
  1526. // let the CToolBar class do all the leg work
  1527. m_pDropBar->SetButtonText(
  1528. m_pDropBar->CommandToIndex(tbButton.idCommand),
  1529. GetButtonText(m_nDragButton)
  1530. );
  1531. }
  1532. // check whether the dragged button was a control in real life and
  1533. // - if so - move that control to its new parent.
  1534. CheckMoveControl(m_pDropBar, tbButton);
  1535. wndTBCtrl.DeleteButton(m_nDragButton);
  1536. if( m_nDragButton == wndTBCtrl.GetButtonCount()-1 )
  1537. // remove trailing separators too
  1538. RemoveTrailingSeparators();
  1539. m_pDropBar->RecalcLayout();
  1540. }
  1541. }
  1542. }
  1543. } else {
  1544. // remove the button from the toolbar
  1545. if( bDoMove ) {
  1546. wndTBCtrl.DeleteButton(m_nDragButton);
  1547. if( m_nDragButton == wndTBCtrl.GetButtonCount() )
  1548. // remove trailing separators too
  1549. RemoveTrailingSeparators();
  1550. } else
  1551. // User has aborted the drag-operation.
  1552. // Remove the drag-border from the button
  1553. InvalidateButton(m_nDragButton);
  1554. }
  1555. // Recalculate the size of the bar.and the parent
  1556. RecalcLayout();
  1557. }
  1558. m_bDragging = FALSE;
  1559. // mouse capture is not longer needed
  1560. ReleaseCapture();
  1561. }
  1562. void CToolBarEx :: CheckMoveControl( CToolBarEx * pToolBar, const TBBUTTON & tbButton ) {
  1563. ASSERT_VALID(pToolBar);
  1564. CWnd * pControl = GetControl(tbButton.idCommand, TRUE);
  1565. if( pControl ) {
  1566. // now change the parent of the control, so that it jumps to the
  1567. // other toolbar
  1568. pControl->SetParent(pToolBar);
  1569. // remove the control from our list (if it's present there) and
  1570. // add it to the target's list
  1571. if( m_pControls ) {
  1572. POSITION pos = m_pControls->Find(pControl);
  1573. if(pos) {
  1574. m_pControls->RemoveAt(pos);
  1575. if( ! pToolBar->m_pControls )
  1576. pToolBar->m_pControls = new CObList();
  1577. pToolBar->m_pControls->AddTail(pControl);
  1578. }
  1579. }
  1580. }
  1581. }
  1582. void CToolBarEx :: RemoveTrailingSeparators() {
  1583. CToolBarCtrl & wndTBCtrl = GetToolBarCtrl();
  1584. register const int nBtn = wndTBCtrl.GetButtonCount();
  1585. register int i = nBtn;
  1586. while( i && IsSeparator(--i) )
  1587. if( DoQueryDelete(i) )
  1588. wndTBCtrl.DeleteButton(i);
  1589. }
  1590. int CToolBarEx :: FindDropButton( const CPoint & point ) {
  1591. CPoint pt = point;
  1592. ScreenToClient(&pt);
  1593. CRect rc;
  1594. // find the button which is closest to the cursor
  1595. register const int nBtn = GetToolBarCtrl().GetButtonCount();
  1596. for( register int i = 0 ; i < nBtn ; ++i ) {
  1597. GetItemRect(i, rc);
  1598. if( rc.PtInRect(pt) )
  1599. // insert the button to drop before this button:
  1600. return (pt.x - rc.left < rc.right - pt.x)
  1601. ? i
  1602. : ((i==nBtn-1)
  1603. ? -1
  1604. : i+1);
  1605. }
  1606. // have to append the button
  1607. return -1;
  1608. }
  1609. void CToolBarEx :: GetMarkerRect( int nButton, CRect & rc ) {
  1610. register const int nBtn = GetToolBarCtrl().GetButtonCount();
  1611. if( nButton < 0 || nButton > nBtn ) {
  1612. // set the marker behind the last button
  1613. GetItemRect(nBtn-1, rc);
  1614. rc.right += 3;
  1615. rc.left = rc.right-6;
  1616. } else {
  1617. // set the marker before the given button
  1618. GetItemRect(nButton, rc);
  1619. rc.left -= 3;
  1620. rc.right = rc.left+6;
  1621. }
  1622. rc.DeflateRect(0,1);
  1623. }
  1624. void CToolBarEx :: ShowMarker( const CRect & rcMarker, CBitmap & bmpArea ) {
  1625. ASSERT( bmpArea.GetSafeHandle() == 0 );
  1626. CClientDC  WinDC(this);
  1627. CDC MemDC; MemDC.CreateCompatibleDC(&WinDC);
  1628. bmpArea.CreateCompatibleBitmap(&WinDC, rcMarker.Width(), rcMarker.Height());
  1629. CBitmap * pOldBmp = MemDC.SelectObject(&bmpArea);
  1630. CPen pen(PS_SOLID, 1, RGB(0,0,0));
  1631. CPen * pOldPen = WinDC.SelectObject(&pen);
  1632. // save original area:
  1633. MemDC.BitBlt(0,0,rcMarker.Width(),rcMarker.Height(),
  1634. &WinDC, rcMarker.left, rcMarker.top, SRCCOPY);
  1635. WinDC.MoveTo(rcMarker.TopLeft());
  1636. WinDC.LineTo(rcMarker.right, rcMarker.top);
  1637. WinDC.MoveTo(rcMarker.left+1, rcMarker.top+1);
  1638. WinDC.LineTo(rcMarker.right-1, rcMarker.top+1);
  1639. WinDC.MoveTo(rcMarker.left+2, rcMarker.top+2);
  1640. WinDC.LineTo(rcMarker.left+2, rcMarker.bottom-2);
  1641. WinDC.MoveTo(rcMarker.left+3, rcMarker.top+2);
  1642. WinDC.LineTo(rcMarker.left+3, rcMarker.bottom-2);
  1643. WinDC.MoveTo(rcMarker.left, rcMarker.bottom-1);
  1644. WinDC.LineTo(rcMarker.right, rcMarker.bottom-1);
  1645. WinDC.MoveTo(rcMarker.left+1, rcMarker.bottom-2);
  1646. WinDC.LineTo(rcMarker.right-1, rcMarker.bottom-2);
  1647. MemDC.SelectObject(pOldBmp);
  1648. MemDC.DeleteDC();
  1649. WinDC.SelectObject(pOldPen);
  1650. }
  1651. void CToolBarEx :: RestoreMarker( const CRect & rcArea, CBitmap & bmpArea ) {
  1652. if( bmpArea.GetSafeHandle() == 0 )
  1653. return;
  1654. CClientDC  WinDC(this);
  1655. CDC MemDC; MemDC.CreateCompatibleDC(&WinDC);
  1656. CBitmap * pOldBmp = MemDC.SelectObject(&bmpArea);
  1657. WinDC.BitBlt(rcArea.left, rcArea.top, rcArea.Width(), rcArea.Height(),
  1658. &MemDC, 0, 0, SRCCOPY);
  1659. MemDC.SelectObject(pOldBmp);
  1660. MemDC.DeleteDC();
  1661. }
  1662. void CToolBarEx :: SetMarker( CToolBarEx * pBar, const CPoint & point ) {
  1663. static CToolBarEx * pLastToolBar = 0;
  1664. static CRect lastRect(0,0,0,0);
  1665. static CBitmap bmpLastSavedArea;
  1666. CRect rcMarker;
  1667. // retrieve proposed rectangle for the marker
  1668. if( pBar != 0 ) {
  1669. int nDropBtn = pBar->FindDropButton(point);
  1670. pBar->GetMarkerRect(nDropBtn, rcMarker);
  1671. if(rcMarker == lastRect)
  1672. return; // don't need to erase/draw
  1673. }
  1674. // restore the previously marked area:
  1675. if( pLastToolBar ) {
  1676. pLastToolBar->RestoreMarker(lastRect, bmpLastSavedArea);
  1677. bmpLastSavedArea.DeleteObject();
  1678. }
  1679. // draw the marker
  1680. if( pBar != 0 ) {
  1681. pBar->ShowMarker(rcMarker, bmpLastSavedArea);
  1682. lastRect = rcMarker;
  1683. }
  1684. pLastToolBar = pBar;
  1685. }
  1686. BOOL CToolBarEx :: DoQueryDelete(int nButton) {
  1687. ASSERT(nButton >= 0);
  1688. TBBUTTON tbButton;
  1689. if( ! GetToolBarCtrl().GetButton(nButton, &tbButton) ) {
  1690. TRACE1("CToolBarEx::DoQueryDelete(): could not retrieve button %dn", nButton);
  1691. return FALSE;
  1692. }
  1693. return QueryDeleteInsert(tbButton, TRUE, nButton);
  1694. }
  1695. BOOL CToolBarEx :: QueryDeleteInsert(TBBUTTON & tbButton, BOOL bDelete, int nIndex) {
  1696. TBNOTIFY tbn;
  1697. memset(&tbn, 0, sizeof(TBNOTIFY));
  1698. tbn.hdr.hwndFrom = GetSafeHwnd();
  1699. tbn.hdr.idFrom = UINT(::GetWindowLong(tbn.hdr.hwndFrom, GWL_ID));
  1700. tbn.hdr.code = bDelete ? TBN_QUERYDELETE : TBN_QUERYINSERT;
  1701. tbn.iItem = nIndex;
  1702. memcpy((void *)(&tbn.tbButton), (const void *)(&tbButton), sizeof(TBBUTTON));
  1703. CString strText;
  1704. if( bDelete ) {
  1705. strText = GetButtonText(nIndex);
  1706. tbn.cchText = strText.GetLength();
  1707. tbn.pszText = strText.GetBuffer(tbn.cchText);
  1708. }
  1709. ASSERT(GetParentFrame() != 0);
  1710. ASSERT(::IsWindow(GetParentFrame()->GetSafeHwnd()));
  1711. BOOL bRet = GetParentFrame()->SendMessage(
  1712. WM_NOTIFY,
  1713. WPARAM(tbn.hdr.idFrom),
  1714. LPARAM(&tbn)
  1715. );
  1716. if( bDelete )
  1717. strText.ReleaseBuffer();
  1718. return bRet;
  1719. }
  1720. void CToolBarEx::OnCaptureChanged(CWnd *pWnd) 
  1721. {
  1722. if( m_bDragging )
  1723. // without the mouse-capture we cannot complete the drag-operation
  1724. EndDrag(FALSE);
  1725. CToolBar::OnCaptureChanged(pWnd);
  1726. }
  1727. BOOL CToolBarEx :: PreTranslateMessage( MSG * pMsg ) {
  1728. if( m_bDragging &&
  1729. (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP) && 
  1730. int(pMsg->wParam) == VK_ESCAPE ) {
  1731. // user pressed ESC to abort drag operation
  1732. EndDrag(FALSE);
  1733. return TRUE;
  1734. }
  1735. return CToolBar::PreTranslateMessage(pMsg);
  1736. }
  1737. void CToolBarEx::OnParentNotify(UINT message, LPARAM lParam) 
  1738. {
  1739. if( LOWORD(message) == WM_LBUTTONDOWN && (::GetAsyncKeyState(VK_MENU) & (1<<15)) ) {
  1740. // I see no chance to abort the child's message processing.
  1741. // That's why we set a flag here. If we lose the focus (a click
  1742. // in a child's area will activate that child window), then
  1743. // we start the real drag-operation (that would return the
  1744. // focus to the toolbar).
  1745. // This solution is somewhat obfuscated, so if you know of
  1746. // a better way -- let me know.
  1747. m_bDragChild = TRUE;
  1748. SetFocus();
  1749. }
  1750. CToolBar::OnParentNotify(message, lParam);
  1751. }
  1752. void CToolBarEx::OnKillFocus(CWnd* pNewWnd) 
  1753. {
  1754. CToolBar::OnKillFocus(pNewWnd);
  1755. if( m_bDragChild ) {
  1756. // See OnParentNotify() above ...
  1757. m_bDragChild = FALSE;
  1758. if( GetCapture() != this )
  1759. BeginDrag();
  1760. }
  1761. }
  1762. /////////////////////////////////////////////////////////////////////////////
  1763. // helpers for docking 
  1764. /////////////////////////////////////////////////////////////////////////////
  1765. // We need our own version of a dock bar, because the original
  1766. // MFC implementation overlapps toolbars. CToolBarEx don't want
  1767. // such a overlapping, because this makes it impossible to draw
  1768. // a real 3d border ...
  1769. class CToolDockBar : public CDockBar {
  1770. DECLARE_DYNAMIC(CToolDockBar)
  1771. public:
  1772. // this is the one and only method of interest
  1773. virtual CSize CalcFixedLayout(BOOL bStretch, BOOL bHorz);
  1774. };
  1775. IMPLEMENT_DYNAMIC(CToolDockBar, CDockBar);
  1776. CSize CToolDockBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
  1777. {
  1778. ASSERT_VALID(this);
  1779. CSize sizeFixed = CControlBar::CalcFixedLayout(bStretch, bHorz);
  1780. // get max size
  1781. CSize sizeMax;
  1782. if (!m_rectLayout.IsRectEmpty())
  1783. sizeMax = m_rectLayout.Size();
  1784. else
  1785. {
  1786. CRect rectFrame;
  1787. CFrameWnd* pFrame = GetParentFrame();
  1788. pFrame->GetClientRect(&rectFrame);
  1789. sizeMax = rectFrame.Size();
  1790. }
  1791. // prepare for layout
  1792. AFX_SIZEPARENTPARAMS layout;
  1793. layout.hDWP = m_bLayoutQuery ?
  1794. NULL : ::BeginDeferWindowPos(m_arrBars.GetSize());
  1795. int cxBorder = 2, cyBorder = 2;
  1796. CPoint pt(-cxBorder, -cyBorder);
  1797. int nWidth = 0;
  1798. BOOL bWrapped = FALSE;
  1799. // layout all the control bars
  1800. for (int nPos = 0; nPos < m_arrBars.GetSize(); nPos++)
  1801. {
  1802. CControlBar* pBar = GetDockedControlBar(nPos);
  1803. void* pVoid = m_arrBars[nPos];
  1804. if (pBar != NULL)
  1805. {
  1806. if(pBar->IsKindOf(RUNTIME_CLASS(CToolBarEx)) && ((CToolBarEx*)pBar)->IsFlatLook())
  1807. ((CToolBarEx*)pBar)->m_bReal3DBorder = TRUE,
  1808. cxBorder = cyBorder = 0;
  1809. else if(pBar->IsKindOf(RUNTIME_CLASS(CToolBarEx)) && !((CToolBarEx*)pBar)->IsFlatLook())
  1810. ((CToolBarEx*)pBar)->m_bReal3DBorder = FALSE,
  1811. cxBorder = cyBorder = 2;
  1812. else
  1813. cxBorder = cyBorder = 2;
  1814. if (pBar->IsVisible())
  1815. {
  1816. // get ideal rect for bar
  1817. DWORD dwMode = 0;
  1818. if ((pBar->m_dwStyle & CBRS_SIZE_DYNAMIC) &&
  1819. (pBar->m_dwStyle & CBRS_FLOATING))
  1820. dwMode |= LM_HORZ | LM_MRUWIDTH;
  1821. else if (pBar->m_dwStyle & CBRS_ORIENT_HORZ)
  1822. dwMode |= LM_HORZ | LM_HORZDOCK;
  1823. else
  1824. dwMode |=  LM_VERTDOCK;
  1825. CSize sizeBar = pBar->CalcDynamicLayout(-1, dwMode);
  1826. CRect rect(pt, sizeBar);
  1827. // get current rect for bar
  1828. CRect rectBar;
  1829. pBar->GetWindowRect(&rectBar);
  1830. ScreenToClient(&rectBar);
  1831. if (bHorz)
  1832. {
  1833. // Offset Calculated Rect out to Actual
  1834. if (rectBar.left > rect.left && !m_bFloating)
  1835. rect.OffsetRect(rectBar.left - rect.left, 0);
  1836. // If ControlBar goes off the right, then right justify
  1837. if (rect.right > sizeMax.cx && !m_bFloating)
  1838. {
  1839. int x = rect.Width() - cxBorder;
  1840. x = max(sizeMax.cx - x, pt.x);
  1841. rect.OffsetRect(x - rect.left, 0);
  1842. }
  1843. // If ControlBar has been wrapped, then left justify
  1844. if (bWrapped)
  1845. {
  1846. bWrapped = FALSE;
  1847. rect.OffsetRect(-(rect.left + cxBorder), 0);
  1848. }
  1849. // If ControlBar is completely invisible, then wrap it
  1850. else if ((rect.left >= (sizeMax.cx - cxBorder)) &&
  1851. (nPos > 0) && (m_arrBars[nPos - 1] != NULL))
  1852. {
  1853. m_arrBars.InsertAt(nPos, (CObject*)NULL);
  1854. pBar = NULL; pVoid = NULL;
  1855. bWrapped = TRUE;
  1856. }
  1857. if (!bWrapped)
  1858. {
  1859. if (rect != rectBar)
  1860. {
  1861. if (!m_bLayoutQuery &&
  1862. !(pBar->m_dwStyle & CBRS_FLOATING))
  1863. {
  1864. pBar->m_pDockContext->m_rectMRUDockPos = rect;
  1865. }
  1866. AfxRepositionWindow(&layout, pBar->m_hWnd, &rect);
  1867. }
  1868. pt.x = rect.left + sizeBar.cx - cxBorder;
  1869. nWidth = max(nWidth, sizeBar.cy);
  1870. }
  1871. }
  1872. else
  1873. {
  1874. // Offset Calculated Rect out to Actual
  1875. if (rectBar.top > rect.top && !m_bFloating)
  1876. rect.OffsetRect(0, rectBar.top - rect.top);
  1877. // If ControlBar goes off the bottom, then bottom justify
  1878. if (rect.bottom > sizeMax.cy && !m_bFloating)
  1879. {
  1880. int y = rect.Height() - cyBorder;
  1881. y = max(sizeMax.cy - y, pt.y);
  1882. rect.OffsetRect(0, y - rect.top);
  1883. }
  1884. // If ControlBar has been wrapped, then top justify
  1885. if (bWrapped)
  1886. {
  1887. bWrapped = FALSE;
  1888. rect.OffsetRect(0, -(rect.top + cyBorder));
  1889. }
  1890. // If ControlBar is completely invisible, then wrap it
  1891. else if ((rect.top >= (sizeMax.cy - cyBorder)) &&
  1892. (nPos > 0) && (m_arrBars[nPos - 1] != NULL))
  1893. {
  1894. m_arrBars.InsertAt(nPos, (CObject*)NULL);
  1895. pBar = NULL; pVoid = NULL;
  1896. bWrapped = TRUE;
  1897. }
  1898. if (!bWrapped)
  1899. {
  1900. if (rect != rectBar)
  1901. {
  1902. if (!m_bLayoutQuery &&
  1903. !(pBar->m_dwStyle & CBRS_FLOATING))
  1904. {
  1905. pBar->m_pDockContext->m_rectMRUDockPos = rect;
  1906. }
  1907. AfxRepositionWindow(&layout, pBar->m_hWnd, &rect);
  1908. }
  1909. pt.y = rect.top + sizeBar.cy - cyBorder;
  1910. nWidth = max(nWidth, sizeBar.cx);
  1911. }
  1912. }
  1913. }
  1914. if (!bWrapped)
  1915. {
  1916. // handle any delay/show hide for the bar
  1917. pBar->RecalcDelayShow(&layout);
  1918. }
  1919. }
  1920. if (pBar == NULL && pVoid == NULL && nWidth != 0)
  1921. {
  1922. // end of row because pBar == NULL
  1923. if (bHorz)
  1924. {
  1925. pt.y += nWidth - cyBorder;
  1926. sizeFixed.cx = max(sizeFixed.cx, pt.x);
  1927. sizeFixed.cy = max(sizeFixed.cy, pt.y);
  1928. pt.x = -cxBorder;
  1929. }
  1930. else
  1931. {
  1932. pt.x += nWidth - cxBorder;
  1933. sizeFixed.cx = max(sizeFixed.cx, pt.x);
  1934. sizeFixed.cy = max(sizeFixed.cy, pt.y);
  1935. pt.y = -cyBorder;
  1936. }
  1937. nWidth = 0;
  1938. }
  1939. }
  1940. if (!m_bLayoutQuery)
  1941. {
  1942. // move and resize all the windows at once!
  1943. if (layout.hDWP == NULL || !::EndDeferWindowPos(layout.hDWP))
  1944. TRACE0("Warning: DeferWindowPos failed - low system resources.n");
  1945. }
  1946. // adjust size for borders on the dock bar itself
  1947. CRect rect;
  1948. rect.SetRectEmpty();
  1949. CalcInsideRect(rect, bHorz);
  1950. if ((!bStretch || !bHorz) && sizeFixed.cx != 0)
  1951. sizeFixed.cx += -rect.right + rect.left;
  1952. if ((!bStretch || bHorz) && sizeFixed.cy != 0)
  1953. sizeFixed.cy += -rect.bottom + rect.top;
  1954. return sizeFixed;
  1955. }
  1956. // dwDockBarMap
  1957. const DWORD dwDockBarMap[4][2] =
  1958. {
  1959. { AFX_IDW_DOCKBAR_TOP,      CBRS_TOP    },
  1960. { AFX_IDW_DOCKBAR_BOTTOM,   CBRS_BOTTOM },
  1961. { AFX_IDW_DOCKBAR_LEFT,     CBRS_LEFT   },
  1962. { AFX_IDW_DOCKBAR_RIGHT,    CBRS_RIGHT  },
  1963. };
  1964. // Unfortunataly a simple rewrite of CFrameWnd's EnableDocking() is not possible,
  1965. // because we have not enough permissions to access some data in this class.
  1966. // That's why we call CFrameWnd::EnableDocking() first and exchange all occurencies
  1967. // of CDockBar objects with our own version of a dock bar.
  1968. void FrameEnableDocking(CFrameWnd * pFrame, DWORD dwDockStyle) {
  1969. ASSERT_VALID(pFrame);
  1970. // must be CBRS_ALIGN_XXX or CBRS_FLOAT_MULTI only
  1971. ASSERT((dwDockStyle & ~(CBRS_ALIGN_ANY|CBRS_FLOAT_MULTI)) == 0);
  1972. pFrame->EnableDocking(dwDockStyle);
  1973. for (int i = 0; i < 4; i++) {
  1974. if (dwDockBarMap[i][1] & dwDockStyle & CBRS_ALIGN_ANY) {
  1975. CDockBar* pDock = (CDockBar*)pFrame->GetControlBar(dwDockBarMap[i][0]);
  1976. // make sure the dock bar is of correct type
  1977. if( pDock == 0 || ! pDock->IsKindOf(RUNTIME_CLASS(CToolDockBar)) ) {
  1978. BOOL bNeedDelete = ! pDock->m_bAutoDelete;
  1979. pDock->m_pDockSite->RemoveControlBar(pDock);
  1980. pDock->m_pDockSite = 0; // avoid problems in destroying the dockbar
  1981. pDock->DestroyWindow();
  1982. if( bNeedDelete )
  1983. delete pDock;
  1984. pDock = 0;
  1985. }
  1986. if( pDock == 0 ) {
  1987. pDock = new CToolDockBar;
  1988. if (!pDock->Create(pFrame,
  1989. WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_CHILD|WS_VISIBLE |
  1990. dwDockBarMap[i][1], dwDockBarMap[i][0])) {
  1991. AfxThrowResourceException();
  1992. }
  1993. }
  1994. }
  1995. }
  1996. }