UIFixTB.cpp
上传用户:yatsl7111
上传日期:2007-01-08
资源大小:1433k
文件大小:15k
源码类别:

图形图象

开发平台:

Visual C++

  1. f////////////////////////////////////////////////////////////////
  2. // Copyright 1998 Paul DiLascia
  3. // If this code works, it was written by Paul DiLascia.
  4. // If not, I don't know who wrote it.
  5. //
  6. // CFixMFCToolBar fixes sizing bugs in MFC CToolBar, so it works with
  7. // modern toolbars in versions of comctl32.dll > 4.70.
  8. //
  9. // Most of this code is copied from MFC, with slight modifications marked "PD"
  10. //
  11. #include "StdAfx.h"
  12. #include "UIFixTB.h"
  13. #include "UIModulVer.h"
  14. #ifdef _DEBUG
  15. #define new DEBUG_NEW
  16. #undef THIS_FILE
  17. static char THIS_FILE[] = __FILE__;
  18. #endif
  19. IMPLEMENT_DYNAMIC(CFixMFCToolBar, CToolBar)
  20. BEGIN_MESSAGE_MAP(CFixMFCToolBar, CToolBar)
  21. ON_MESSAGE(TB_SETBITMAPSIZE, OnSetBitmapSize)
  22. ON_MESSAGE(TB_SETBUTTONSIZE, OnSetButtonSize)
  23. ON_MESSAGE(WM_SETTINGCHANGE, OnSettingChange)
  24. ON_MESSAGE(WM_SETFONT,   OnSettingChange)
  25. END_MESSAGE_MAP()
  26. /////////////////
  27. // This function gets the version number of comctl32.dll.
  28. //
  29. static int GetVerComCtl32()
  30. {
  31. CModuleVersion ver;
  32. DLLVERSIONINFO dvi;
  33. VERIFY(ver.DllGetVersion(_T("comctl32.dll"), dvi));
  34. return dvi.dwMajorVersion*100 + dvi.dwMinorVersion;
  35. }
  36. int CFixMFCToolBar::iVerComCtl32 = GetVerComCtl32();
  37. CFixMFCToolBar::CFixMFCToolBar()
  38. {
  39. m_bShowDropdownArrowWhenVertical = FALSE;
  40. }
  41. CFixMFCToolBar::~CFixMFCToolBar()
  42. {
  43. }
  44. //////////////////
  45. // These functions duplicate functionalityin VC 6.0
  46. // Need to set transparent/flat style before setting
  47. // button/image size or font to allow zero-height border.
  48. //
  49. LRESULT CFixMFCToolBar::OnSetBitmapSize(WPARAM, LPARAM lp)
  50. {
  51. return OnSizeHelper(m_sizeImage, lp);
  52. }
  53. LRESULT CFixMFCToolBar::OnSetButtonSize(WPARAM, LPARAM lp)
  54. {
  55. return OnSizeHelper(m_sizeButton, lp);
  56. }
  57. LRESULT CFixMFCToolBar::OnSettingChange(WPARAM, LPARAM lp)
  58. {
  59. return OnSizeHelper(CSize(0,0), lp);
  60. }
  61. LRESULT CFixMFCToolBar::OnSizeHelper(CSize& sz, LPARAM lp)
  62. {
  63. ASSERT(iVerComCtl32 > 0);
  64. BOOL bModStyle =FALSE;
  65. DWORD dwStyle =0;
  66. if (iVerComCtl32 >= 471) {
  67. dwStyle = GetStyle();
  68. bModStyle = ModifyStyle(0, TBSTYLE_TRANSPARENT|TBSTYLE_FLAT);
  69. }
  70. LRESULT lRet = Default();
  71. if (lRet)
  72. sz = lp;
  73. if (bModStyle)
  74. SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
  75. return lRet;
  76. }
  77. //////////////////
  78. // **PD**
  79. // This is the all-important function that gets the true size of a button,
  80. // instead of using m_sizeButton. And it's virtual, so you can override if
  81. // my algorithm doesn't work, as will surely be the case in some circumstances.
  82. //
  83. CSize CFixMFCToolBar::GetButtonSize(TBBUTTON* pData, int iButton)
  84. {
  85. ASSERT(iVerComCtl32 > 0);
  86. // Get the actual size of the button, not what's in m_sizeButton.
  87. // Make sure to do SendMessage instead of calling MFC's GetItemRect,
  88. // which has all sorts of bad side-effects! (Go ahead, take a look at it.)
  89. // 
  90. CRect rc;
  91. SendMessage(TB_GETITEMRECT, iButton, (LPARAM)&rc);
  92. CSize sz = rc.Size();
  93. ////////////////
  94. // Now must do special case for various versions of comctl32.dll,
  95. //
  96. DWORD dwStyle = pData[iButton].fsStyle;
  97. if ((pData[iButton].fsState & TBSTATE_WRAP)) {
  98. if (dwStyle & TBSTYLE_SEP) {
  99. // this is the last separator in the row (eg vertically docked)
  100. // fudge the height, and ignore the width. TB_GETITEMRECT will return
  101. // size = (8 x 22) even for a separator in vertical toolbar
  102. //
  103. if (iVerComCtl32 <= 470)
  104. sz.cy -= 3; // empircally good fudge factor
  105. else if (iVerComCtl32 != 471)
  106. sz.cy = sz.cx;
  107. sz.cx = 0; // separator takes no width if it's the last one
  108. } else if (dwStyle & TBSTYLE_DROPDOWN &&
  109. !m_bShowDropdownArrowWhenVertical) {
  110. // ignore width of dropdown
  111. sz.cx = 0;
  112. }
  113. }
  114. return sz;
  115. }
  116. //////////////////
  117. // **PD**
  118. // Part 2 of correction for MFC is to recalculate everything when the bar
  119. // goes from docked to undocked because the AdjustSize calculation happens
  120. // when the bar is in the old state, and thus wrong. After the bar is
  121. // docked/undocked, I'll recalculate with the new style and commit the change.
  122. //
  123. void CFixMFCToolBar::OnBarStyleChange(DWORD dwOldStyle, DWORD dwNewStyle)
  124. {
  125. CToolBar::OnBarStyleChange(dwOldStyle, dwNewStyle);
  126. if (dwOldStyle != dwNewStyle) {
  127. DWORD dwMode = 0;
  128. if ((dwNewStyle & CBRS_SIZE_DYNAMIC) && (dwNewStyle & CBRS_FLOATING))
  129. dwMode = LM_HORZ | LM_MRUWIDTH;
  130. else if (dwNewStyle & CBRS_ORIENT_HORZ)
  131. dwMode = LM_HORZ | LM_HORZDOCK;
  132. else
  133. dwMode =  LM_VERTDOCK;
  134. CalcDynamicLayout(-1, dwMode | LM_COMMIT);
  135. }
  136. }
  137. ////////////////////////////////////////////////////////////////
  138. // Stuff below is copied from MFC; only mod is to call GetButtonSize.
  139. #ifdef _MAC
  140. #define CX_OVERLAP  1
  141. #else
  142. #define CX_OVERLAP  0
  143. #endif
  144. CSize CFixMFCToolBar::CalcSize(TBBUTTON* pData, int nCount)
  145. {
  146. ASSERT(pData != NULL && nCount > 0);
  147. CPoint cur(0,0);
  148. CSize sizeResult(0,0);
  149. int cyTallestOnRow = 0;
  150. for (int i = 0; i < nCount; i++)
  151. {
  152. if (pData[i].fsState & TBSTATE_HIDDEN)
  153. continue;
  154. // **PD**
  155. // Load actual size of button into a local variable
  156. // called m_sizeButton. C++ will use this instead of
  157. // CToolBar::m_sizeButton.
  158. //
  159. CSize m_sizeButton = GetButtonSize(pData, i);
  160. // **PD**
  161. // I also changed the logic below to be more correct.
  162. cyTallestOnRow = max(cyTallestOnRow, m_sizeButton.cy);
  163. sizeResult.cx = max(cur.x + m_sizeButton.cx, sizeResult.cx);
  164. sizeResult.cy = max(cur.y + m_sizeButton.cy, sizeResult.cy);
  165. cur.x += m_sizeButton.cx - CX_OVERLAP;
  166. if (pData[i].fsState & TBSTATE_WRAP)
  167. {
  168. cur.x = 0;
  169. cur.y += cyTallestOnRow;
  170. cyTallestOnRow = 0;
  171. if (pData[i].fsStyle & TBSTYLE_SEP)
  172. cur.y += m_sizeButton.cy;
  173. }
  174. }
  175. return sizeResult;
  176. }
  177. int CFixMFCToolBar::WrapToolBar(TBBUTTON* pData, int nCount, int nWidth)
  178. {
  179. ASSERT(pData != NULL && nCount > 0);
  180. int nResult = 0;
  181. int x = 0;
  182. for (int i = 0; i < nCount; i++)
  183. {
  184. pData[i].fsState &= ~TBSTATE_WRAP;
  185. if (pData[i].fsState & TBSTATE_HIDDEN)
  186. continue;
  187. int dx, dxNext;
  188. // **PD**
  189. // Load actual size of button into a local variable
  190. // called m_sizeButton. C++ will use this instead of
  191. // CToolBar::m_sizeButton.
  192. //
  193. CSize m_sizeButton = GetButtonSize(pData, i);
  194. dx = m_sizeButton.cx;
  195. dxNext = dx - CX_OVERLAP;
  196. if (x + dx > nWidth)
  197. {
  198. BOOL bFound = FALSE;
  199. for (int j = i; j >= 0  &&  !(pData[j].fsState & TBSTATE_WRAP); j--)
  200. {
  201. // Find last separator that isn't hidden
  202. // a separator that has a command ID is not
  203. // a separator, but a custom control.
  204. if ((pData[j].fsStyle & TBSTYLE_SEP) &&
  205. (pData[j].idCommand == 0) &&
  206. !(pData[j].fsState & TBSTATE_HIDDEN))
  207. {
  208. bFound = TRUE; i = j; x = 0;
  209. pData[j].fsState |= TBSTATE_WRAP;
  210. nResult++;
  211. break;
  212. }
  213. }
  214. if (!bFound)
  215. {
  216. for (int j = i - 1; j >= 0 && !(pData[j].fsState & TBSTATE_WRAP); j--)
  217. {
  218. // Never wrap anything that is hidden,
  219. // or any custom controls
  220. if ((pData[j].fsState & TBSTATE_HIDDEN) ||
  221. ((pData[j].fsStyle & TBSTYLE_SEP) &&
  222. (pData[j].idCommand != 0)))
  223. continue;
  224. bFound = TRUE; i = j; x = 0;
  225. pData[j].fsState |= TBSTATE_WRAP;
  226. nResult++;
  227. break;
  228. }
  229. if (!bFound)
  230. x += dxNext;
  231. }
  232. }
  233. else
  234. x += dxNext;
  235. }
  236. return nResult + 1;
  237. }
  238. //////////////////////////////////////////////////////////////////////////
  239. // **PD**
  240. // Functions below are NOT actually modified. They're only here because they
  241. // calls the modified functions above, which are NOT virtual.
  242. //////////////////////////////////////////////////////////////////////////
  243. void  CFixMFCToolBar::SizeToolBar(TBBUTTON* pData, int nCount, int nLength, BOOL bVert)
  244. {
  245. ASSERT(pData != NULL && nCount > 0);
  246. if (!bVert)
  247. {
  248. int nMin, nMax, nTarget, nCurrent, nMid;
  249. // Wrap ToolBar as specified
  250. nMax = nLength;
  251. nTarget = WrapToolBar(pData, nCount, nMax);
  252. // Wrap ToolBar vertically
  253. nMin = 0;
  254. nCurrent = WrapToolBar(pData, nCount, nMin);
  255. if (nCurrent != nTarget)
  256. {
  257. while (nMin < nMax)
  258. {
  259. nMid = (nMin + nMax) / 2;
  260. nCurrent = WrapToolBar(pData, nCount, nMid);
  261. if (nCurrent == nTarget)
  262. nMax = nMid;
  263. else
  264. {
  265. if (nMin == nMid)
  266. {
  267. WrapToolBar(pData, nCount, nMax);
  268. break;
  269. }
  270. nMin = nMid;
  271. }
  272. }
  273. }
  274. CSize size = CalcSize(pData, nCount);
  275. WrapToolBar(pData, nCount, size.cx);
  276. }
  277. else
  278. {
  279. CSize sizeMax, sizeMin, sizeMid;
  280. // Wrap ToolBar vertically
  281. WrapToolBar(pData, nCount, 0);
  282. sizeMin = CalcSize(pData, nCount);
  283. // Wrap ToolBar horizontally
  284. WrapToolBar(pData, nCount, 32767);
  285. sizeMax = CalcSize(pData, nCount);
  286. while (sizeMin.cx < sizeMax.cx)
  287. {
  288. sizeMid.cx = (sizeMin.cx + sizeMax.cx) / 2;
  289. WrapToolBar(pData, nCount, sizeMid.cx);
  290. sizeMid = CalcSize(pData, nCount);
  291. if (nLength < sizeMid.cy)
  292. {
  293. if (sizeMin == sizeMid)
  294. {
  295. WrapToolBar(pData, nCount, sizeMax.cx);
  296. return;
  297. }
  298. sizeMin = sizeMid;
  299. }
  300. else if (nLength > sizeMid.cy)
  301. sizeMax = sizeMid;
  302. else
  303. return;
  304. }
  305. }
  306. }
  307. struct _AFX_CONTROLPOS
  308. {
  309. int nIndex, nID;
  310. CRect rectOldPos;
  311. };
  312. CSize CFixMFCToolBar::CalcLayout(DWORD dwMode, int nLength)
  313. {
  314. ASSERT_VALID(this);
  315. ASSERT(::IsWindow(m_hWnd));
  316. if (dwMode & LM_HORZDOCK)
  317. ASSERT(dwMode & LM_HORZ);
  318. int nCount;
  319. TBBUTTON* pData;
  320. CSize sizeResult(0,0);
  321. // Load Buttons
  322. {
  323. nCount = SendMessage(TB_BUTTONCOUNT, 0, 0);
  324. if (nCount != 0)
  325. {
  326. int i;
  327. pData = new TBBUTTON[nCount];
  328. for (i = 0; i < nCount; i++)
  329. GetButton(i, &pData[i]); // **PD** renamed from _GetButton
  330. }
  331. }
  332. if (nCount > 0)
  333. {
  334. if (!(m_dwStyle & CBRS_SIZE_FIXED))
  335. {
  336. BOOL bDynamic = m_dwStyle & CBRS_SIZE_DYNAMIC;
  337. if (bDynamic && (dwMode & LM_MRUWIDTH))
  338. SizeToolBar(pData, nCount, m_nMRUWidth);
  339. else if (bDynamic && (dwMode & LM_HORZDOCK))
  340. SizeToolBar(pData, nCount, 32767);
  341. else if (bDynamic && (dwMode & LM_VERTDOCK))
  342. SizeToolBar(pData, nCount, 0);
  343. else if (bDynamic && (nLength != -1))
  344. {
  345. CRect rect; rect.SetRectEmpty();
  346. CalcInsideRect(rect, (dwMode & LM_HORZ));
  347. BOOL bVert = (dwMode & LM_LENGTHY);
  348. int nLen = nLength + (bVert ? rect.Height() : rect.Width());
  349. SizeToolBar(pData, nCount, nLen, bVert);
  350. }
  351. else if (bDynamic && (m_dwStyle & CBRS_FLOATING))
  352. SizeToolBar(pData, nCount, m_nMRUWidth);
  353. else
  354. SizeToolBar(pData, nCount, (dwMode & LM_HORZ) ? 32767 : 0);
  355. }
  356. sizeResult = CalcSize(pData, nCount);
  357. if (dwMode & LM_COMMIT)
  358. {
  359. _AFX_CONTROLPOS* pControl = NULL;
  360. int nControlCount = 0;
  361. BOOL bIsDelayed = m_bDelayedButtonLayout;
  362. m_bDelayedButtonLayout = FALSE;
  363. for(int i = 0; i < nCount; i++)
  364. if ((pData[i].fsStyle & TBSTYLE_SEP) && (pData[i].idCommand != 0))
  365. nControlCount++;
  366. if (nControlCount > 0)
  367. {
  368. pControl = new _AFX_CONTROLPOS[nControlCount];
  369. nControlCount = 0;
  370. for(int i = 0; i < nCount; i++)
  371. {
  372. if ((pData[i].fsStyle & TBSTYLE_SEP) && (pData[i].idCommand != 0))
  373. {
  374. pControl[nControlCount].nIndex = i;
  375. pControl[nControlCount].nID = pData[i].idCommand;
  376. CRect rect;
  377. GetItemRect(i, &rect);
  378. ClientToScreen(&rect);
  379. pControl[nControlCount].rectOldPos = rect;
  380. nControlCount++;
  381. }
  382. }
  383. }
  384. if ((m_dwStyle & CBRS_FLOATING) && (m_dwStyle & CBRS_SIZE_DYNAMIC))
  385. m_nMRUWidth = sizeResult.cx;
  386. for (i = 0; i < nCount; i++)
  387. SetButton(i, &pData[i]); // **PD** renamed from _SetButton
  388. if (nControlCount > 0)
  389. {
  390. for (int i = 0; i < nControlCount; i++)
  391. {
  392. CWnd* pWnd = GetDlgItem(pControl[i].nID);
  393. if (pWnd != NULL)
  394. {
  395. CRect rect;
  396. pWnd->GetWindowRect(&rect);
  397. CPoint pt = rect.TopLeft() - pControl[i].rectOldPos.TopLeft();
  398. GetItemRect(pControl[i].nIndex, &rect);
  399. pt = rect.TopLeft() + pt;
  400. pWnd->SetWindowPos(NULL, pt.x, pt.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
  401. }
  402. }
  403. delete[] pControl;
  404. }
  405. m_bDelayedButtonLayout = bIsDelayed;
  406. }
  407. delete[] pData;
  408. }
  409. //BLOCK: Adjust Margins
  410. {
  411. CRect rect; rect.SetRectEmpty();
  412. CalcInsideRect(rect, (dwMode & LM_HORZ));
  413. sizeResult.cy -= rect.Height();
  414. sizeResult.cx -= rect.Width();
  415. CSize size = CControlBar::CalcFixedLayout((dwMode & LM_STRETCH), (dwMode & LM_HORZ));
  416. sizeResult.cx = max(sizeResult.cx, size.cx);
  417. sizeResult.cy = max(sizeResult.cy, size.cy);
  418. }
  419. return sizeResult;
  420. }
  421. CSize CFixMFCToolBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
  422. {
  423. DWORD dwMode = bStretch ? LM_STRETCH : 0;
  424. dwMode |= bHorz ? LM_HORZ : 0;
  425. return CalcLayout(dwMode);
  426. }
  427. CSize CFixMFCToolBar::CalcDynamicLayout(int nLength, DWORD dwMode)
  428. {
  429. if ((nLength == -1) && !(dwMode & LM_MRUWIDTH) && !(dwMode & LM_COMMIT) &&
  430. ((dwMode & LM_HORZDOCK) || (dwMode & LM_VERTDOCK)))
  431. {
  432. return CalcFixedLayout(dwMode & LM_STRETCH, dwMode & LM_HORZDOCK);
  433. }
  434. return CalcLayout(dwMode, nLength);
  435. }
  436. /////////////////////////////////////////////////////////////////////////////
  437. // CToolBar attribute access
  438. // **PD** I renamed this from _GetButton.
  439. //
  440. void CFixMFCToolBar::GetButton(int nIndex, TBBUTTON* pButton) const
  441. {
  442. CToolBar* pBar = (CToolBar*)this;
  443. VERIFY(pBar->SendMessage(TB_GETBUTTON, nIndex, (LPARAM)pButton));
  444. // TBSTATE_ENABLED == TBBS_DISABLED so invert it
  445. pButton->fsState ^= TBSTATE_ENABLED;
  446. }
  447. // **PD** I renamed this from _SetButton.
  448. //
  449. void CFixMFCToolBar::SetButton(int nIndex, TBBUTTON* pButton)
  450. {
  451. // get original button state
  452. TBBUTTON button;
  453. VERIFY(SendMessage(TB_GETBUTTON, nIndex, (LPARAM)&button));
  454. // prepare for old/new button comparsion
  455. button.bReserved[0] = 0;
  456. button.bReserved[1] = 0;
  457. // TBSTATE_ENABLED == TBBS_DISABLED so invert it
  458. pButton->fsState ^= TBSTATE_ENABLED;
  459. pButton->bReserved[0] = 0;
  460. pButton->bReserved[1] = 0;
  461. // nothing to do if they are the same
  462. if (memcmp(pButton, &button, sizeof(TBBUTTON)) != 0)
  463. {
  464. // don't redraw everything while setting the button
  465. DWORD dwStyle = GetStyle();
  466. ModifyStyle(WS_VISIBLE, 0);
  467. VERIFY(SendMessage(TB_DELETEBUTTON, nIndex, 0));
  468. VERIFY(SendMessage(TB_INSERTBUTTON, nIndex, (LPARAM)pButton));
  469. ModifyStyle(0, dwStyle & WS_VISIBLE);
  470. // invalidate appropriate parts
  471. if (((pButton->fsStyle ^ button.fsStyle) & TBSTYLE_SEP) ||
  472. ((pButton->fsStyle & TBSTYLE_SEP) && pButton->iBitmap != button.iBitmap))
  473. {
  474. // changing a separator
  475. Invalidate(FALSE);
  476. }
  477. else
  478. {
  479. // invalidate just the button
  480. CRect rect;
  481. if (SendMessage(TB_GETITEMRECT, nIndex, (LPARAM)&rect))
  482. InvalidateRect(rect, FALSE);    // don't erase background
  483. }
  484. }
  485. }