Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

593 lines
12 KiB

  1. // File: Toolbar.cpp
  2. #include "precomp.h"
  3. #include "GenContainers.h"
  4. #include "GenControls.h"
  5. #include <windowsx.h>
  6. // Minimum size for children;
  7. // BUGBUG georgep; Should probably set this to 0 after debugging
  8. const static int MinSize = 10;
  9. // Default m_gap
  10. const static int HGapSize = 4;
  11. // Default m_hMargin
  12. const static int HMargin = 0;
  13. // Default m_vMargin
  14. const static int VMargin = 0;
  15. // Init m_uRightIndex and m_uCenterIndex to very large numbers
  16. CToolbar::CToolbar() :
  17. m_gap(HGapSize),
  18. m_hMargin(HMargin),
  19. m_vMargin(VMargin),
  20. m_nAlignment(TopLeft),
  21. m_uRightIndex(static_cast<UINT>(-1)),
  22. m_bHasCenterChild(FALSE),
  23. m_bReverseOrder(FALSE),
  24. m_bMinDesiredSize(FALSE),
  25. m_bVertical(FALSE)
  26. {
  27. }
  28. BOOL CToolbar::Create(
  29. HWND hWndParent, // The parent of the toolbar window
  30. DWORD dwExStyle // The extended style of the toolbar window
  31. )
  32. {
  33. return(CGenWindow::Create(
  34. hWndParent, // Window parent
  35. 0, // ID of the child window
  36. TEXT("NMToolbar"), // Window name
  37. WS_CLIPCHILDREN, // Window style; WS_CHILD|WS_VISIBLE will be added to this
  38. dwExStyle|WS_EX_CONTROLPARENT // Extended window style
  39. ));
  40. }
  41. // Get the desired size for a child, and make sure it is big enough
  42. static void GetWindowDesiredSize(HWND hwnd, SIZE *ppt)
  43. {
  44. ppt->cx = ppt->cy = 0;
  45. IGenWindow *pWnd = CGenWindow::FromHandle(hwnd);
  46. if (NULL != pWnd)
  47. {
  48. pWnd->GetDesiredSize(ppt);
  49. }
  50. ppt->cx = max(ppt->cx, MinSize);
  51. ppt->cy = max(ppt->cy, MinSize);
  52. }
  53. BOOL IsChildVisible(HWND hwndChild)
  54. {
  55. return((GetWindowLong(hwndChild, GWL_STYLE)&WS_VISIBLE) == WS_VISIBLE);
  56. }
  57. /** Get the total desired size of the child windows: max of heights and sum of
  58. * widths or vice versa for vertical windows.
  59. * @param hwndParent The window whose children are to be examined
  60. * @param size The returned total size
  61. * @param bVertical Whether to flow vertical or horizontal
  62. * @returns The number of visible child windows
  63. */
  64. static int GetChildTotals(HWND hwndParent, SIZE *size, BOOL bVertical)
  65. {
  66. int nChildren = 0;
  67. int xMax=0, xTot=0;
  68. int yMax=0, yTot=0;
  69. for (HWND hwndChild=::GetWindow(hwndParent, GW_CHILD); NULL!=hwndChild;
  70. hwndChild=::GetWindow(hwndChild, GW_HWNDNEXT))
  71. {
  72. if (!IsChildVisible(hwndChild))
  73. {
  74. continue;
  75. }
  76. ++nChildren;
  77. SIZE pt;
  78. GetWindowDesiredSize(hwndChild, &pt);
  79. xTot += pt.cx;
  80. yTot += pt.cy;
  81. if (xMax < pt.cx) xMax = pt.cx;
  82. if (yMax < pt.cy) yMax = pt.cy;
  83. }
  84. if (bVertical)
  85. {
  86. size->cx = xMax;
  87. size->cy = yTot;
  88. }
  89. else
  90. {
  91. size->cx = xTot;
  92. size->cy = yMax;
  93. }
  94. return(nChildren);
  95. }
  96. // Returns the total children desired size, plus the gaps and margins.
  97. void CToolbar::GetDesiredSize(SIZE *ppt)
  98. {
  99. int nChildren = GetChildTotals(GetWindow(), ppt, m_bVertical);
  100. if (nChildren > 1 && !m_bMinDesiredSize)
  101. {
  102. if (m_bVertical)
  103. {
  104. ppt->cy += (nChildren-1) * m_gap;
  105. }
  106. else
  107. {
  108. ppt->cx += (nChildren-1) * m_gap;
  109. }
  110. }
  111. ppt->cx += m_hMargin * 2;
  112. ppt->cy += m_vMargin * 2;
  113. SIZE sizeTemp;
  114. CGenWindow::GetDesiredSize(&sizeTemp);
  115. ppt->cx += sizeTemp.cx;
  116. ppt->cy += sizeTemp.cy;
  117. }
  118. void CToolbar::AdjustPos(POINT *pPos, SIZE *pSize, UINT width)
  119. {
  120. pPos->x = pPos->y = 0;
  121. switch (m_nAlignment)
  122. {
  123. default:
  124. case TopLeft:
  125. // Nothing to do
  126. break;
  127. case Center:
  128. if (m_bVertical)
  129. {
  130. pPos->x = (width - pSize->cx)/2;
  131. }
  132. else
  133. {
  134. pPos->y = (width - pSize->cy)/2;
  135. }
  136. break;
  137. case BottomRight:
  138. if (m_bVertical)
  139. {
  140. pPos->x = (width - pSize->cx);
  141. }
  142. else
  143. {
  144. pPos->y = (width - pSize->cy);
  145. }
  146. break;
  147. case Fill:
  148. if (m_bVertical)
  149. {
  150. pSize->cx = width;
  151. }
  152. else
  153. {
  154. pSize->cy = width;
  155. }
  156. break;
  157. }
  158. }
  159. // Get the first child to layout
  160. HWND CToolbar::GetFirstKid()
  161. {
  162. HWND ret = ::GetWindow(GetWindow(), GW_CHILD);
  163. if (m_bReverseOrder && NULL != ret)
  164. {
  165. ret = ::GetWindow(ret, GW_HWNDLAST);
  166. }
  167. return(ret);
  168. }
  169. // Get the next child to layout
  170. HWND CToolbar::GetNextKid(HWND hwndCurrent)
  171. {
  172. return(::GetWindow(hwndCurrent, m_bReverseOrder ? GW_HWNDPREV : GW_HWNDNEXT));
  173. }
  174. extern HDWP SetWindowPosI(HDWP hdwp, HWND hwndChild, int left, int top, int width, int height);
  175. // Flow child windows according to the fields
  176. void CToolbar::Layout()
  177. {
  178. RECT rc;
  179. GetClientRect(GetWindow(), &rc);
  180. // First see how much extra space we have
  181. SIZE sizeTotal;
  182. int nChildren = GetChildTotals(GetWindow(), &sizeTotal, m_bVertical);
  183. if (0 == nChildren)
  184. {
  185. // No children, so nothing to layout
  186. return;
  187. }
  188. // Add on the margins
  189. sizeTotal.cx += 2*m_hMargin;
  190. sizeTotal.cy += 2*m_vMargin;
  191. if (nChildren > 1 || !m_bHasCenterChild)
  192. {
  193. // Don't layout with children overlapping
  194. rc.right = max(rc.right , sizeTotal.cx);
  195. rc.bottom = max(rc.bottom, sizeTotal.cy);
  196. }
  197. // Calculate the total gaps between children
  198. int tGap = m_bVertical ? rc.bottom - sizeTotal.cy : rc.right - sizeTotal.cx;
  199. int maxGap = (nChildren-1)*m_gap;
  200. if (tGap > maxGap) tGap = maxGap;
  201. tGap = max(tGap, 0); // This can happen if only a center child
  202. // If we fill, then children in a vertical toolbar go from the left to the
  203. // right margin, and similar for a horizontal toolbar
  204. int fill = m_bVertical ? rc.right-2*m_hMargin : rc.bottom-2*m_vMargin;
  205. // Speed up layout by deferring it
  206. HDWP hdwp = BeginDeferWindowPos(nChildren);
  207. HWND hwndChild;
  208. UINT nChild = 0;
  209. // Iterate through the children
  210. UINT uCenterIndex = m_bHasCenterChild ? m_uRightIndex-1 : static_cast<UINT>(-1);
  211. // We need to keep track of whether the middle was skipped in case the
  212. // center control or the first right-aligned control is hidden
  213. BOOL bMiddleSkipped = FALSE;
  214. // Do left/top-aligned children
  215. // The starting point for laying out children
  216. int left = m_hMargin;
  217. int top = m_vMargin;
  218. for (hwndChild=GetFirstKid(); NULL!=hwndChild;
  219. hwndChild=GetNextKid(hwndChild), ++nChild)
  220. {
  221. if (!IsChildVisible(hwndChild))
  222. {
  223. continue;
  224. }
  225. SIZE size;
  226. GetWindowDesiredSize(hwndChild, &size);
  227. if (nChild == uCenterIndex)
  228. {
  229. // Take the window size, subtract all the gaps, and subtract the
  230. // desired size of everybody but this control. That should give
  231. // the "extra" area in the middle
  232. if (m_bVertical)
  233. {
  234. size.cy = rc.bottom - tGap - (sizeTotal.cy - size.cy);
  235. }
  236. else
  237. {
  238. size.cx = rc.right - tGap - (sizeTotal.cx - size.cx);
  239. }
  240. bMiddleSkipped = TRUE;
  241. }
  242. else if (nChild >= m_uRightIndex && !bMiddleSkipped)
  243. {
  244. // Skip the "extra" room in the middle; if there is a centered
  245. // control, then we have already done this
  246. if (m_bVertical)
  247. {
  248. top += rc.bottom - tGap - sizeTotal.cy;
  249. }
  250. else
  251. {
  252. left += rc.right - tGap - sizeTotal.cx;
  253. }
  254. bMiddleSkipped = TRUE;
  255. }
  256. POINT pos;
  257. AdjustPos(&pos, &size, fill);
  258. // Move the window
  259. hdwp = SetWindowPosI(hdwp, hwndChild, pos.x+left, pos.y+top, size.cx, size.cy);
  260. // calculate the gap; don't just use a "fixed" gap, since children
  261. // would move in chunks
  262. int gap = (nChildren<=1) ? 0 : ((tGap * (nChild+1))/(nChildren-1) - (tGap * nChild)/(nChildren-1));
  263. // Update the pos of the next child
  264. if (m_bVertical)
  265. {
  266. top += gap + size.cy;
  267. }
  268. else
  269. {
  270. left += gap + size.cx;
  271. }
  272. }
  273. // Actually move all the windows now
  274. EndDeferWindowPos(hdwp);
  275. }
  276. LRESULT CToolbar::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  277. {
  278. switch (message)
  279. {
  280. HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
  281. }
  282. return(CGenWindow::ProcessMessage(hwnd, message, wParam, lParam));
  283. }
  284. void CToolbar::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
  285. {
  286. FORWARD_WM_COMMAND(GetParent(hwnd), id, hwndCtl, codeNotify, SendMessage);
  287. }
  288. static HWND FindControl(HWND hwndParent, int nID)
  289. {
  290. if (GetWindowLong(hwndParent, GWL_ID) == nID)
  291. {
  292. return(hwndParent);
  293. }
  294. for (hwndParent=GetWindow(hwndParent, GW_CHILD); NULL!=hwndParent;
  295. hwndParent=GetWindow(hwndParent, GW_HWNDNEXT))
  296. {
  297. HWND ret = FindControl(hwndParent, nID);
  298. if (NULL != ret)
  299. {
  300. return(ret);
  301. }
  302. }
  303. return(NULL);
  304. }
  305. IGenWindow *CToolbar::FindControl(int nID)
  306. {
  307. HWND hwndRet = ::FindControl(GetWindow(), nID);
  308. if (NULL == hwndRet)
  309. {
  310. return(NULL);
  311. }
  312. return(FromHandle(hwndRet));
  313. }
  314. CSeparator::CSeparator() :
  315. m_iStyle(Normal)
  316. {
  317. m_desSize.cx = m_desSize.cy = 2;
  318. }
  319. BOOL CSeparator::Create(
  320. HWND hwndParent, UINT iStyle
  321. )
  322. {
  323. m_iStyle = iStyle;
  324. return(CGenWindow::Create(
  325. hwndParent, // Window parent
  326. 0, // ID of the child window
  327. TEXT("NMSeparator"), // Window name
  328. WS_CLIPCHILDREN, // Window style; WS_CHILD|WS_VISIBLE will be added to this
  329. WS_EX_CONTROLPARENT // Extended window style
  330. ));
  331. }
  332. void CSeparator::GetDesiredSize(SIZE *ppt)
  333. {
  334. *ppt = m_desSize;
  335. // Make sure there's room for the child
  336. HWND child = GetFirstChild(GetWindow());
  337. if (NULL == child)
  338. {
  339. // Nothing to do
  340. return;
  341. }
  342. IGenWindow *pChild = FromHandle(child);
  343. if (NULL == pChild)
  344. {
  345. // Don't know what to do
  346. return;
  347. }
  348. SIZE size;
  349. pChild->GetDesiredSize(&size);
  350. ppt->cx = max(ppt->cx, size.cx);
  351. ppt->cy = max(ppt->cy, size.cy);
  352. }
  353. void CSeparator::SetDesiredSize(SIZE *psize)
  354. {
  355. m_desSize = *psize;
  356. OnDesiredSizeChanged();
  357. }
  358. void CSeparator::Layout()
  359. {
  360. HWND hwnd = GetWindow();
  361. HWND child = GetFirstChild(hwnd);
  362. if (NULL == child)
  363. {
  364. // Nothing to do
  365. return;
  366. }
  367. IGenWindow *pChild = FromHandle(child);
  368. if (NULL == pChild)
  369. {
  370. // Don't know what to do
  371. return;
  372. }
  373. // Center the child horizontally and vertically
  374. SIZE size;
  375. pChild->GetDesiredSize(&size);
  376. RECT rcClient;
  377. GetClientRect(hwnd, &rcClient);
  378. rcClient.left += (rcClient.right-rcClient.left-size.cx)/2;
  379. rcClient.top += (rcClient.bottom-rcClient.top-size.cy)/2;
  380. SetWindowPos(child, NULL, rcClient.left, rcClient.top, size.cx, size.cy,
  381. SWP_NOZORDER|SWP_NOACTIVATE);
  382. }
  383. void CSeparator::OnPaint(HWND hwnd)
  384. {
  385. PAINTSTRUCT ps;
  386. HDC hdc = BeginPaint(hwnd, &ps);
  387. RECT rc;
  388. GetClientRect(hwnd, &rc);
  389. int nFlags = BF_LEFT;
  390. if (rc.right < rc.bottom)
  391. {
  392. // this is a vertical separator
  393. // center the drawing
  394. rc.left += (rc.right-rc.left)/2 - 1;
  395. rc.right = 4;
  396. }
  397. else
  398. {
  399. // this is a horizontal separator
  400. nFlags = BF_TOP;
  401. // center the drawing
  402. rc.top += (rc.bottom-rc.top)/2 - 1;
  403. rc.bottom = 4;
  404. }
  405. if (Normal == m_iStyle)
  406. {
  407. DrawEdge(hdc, &rc, EDGE_ETCHED, nFlags);
  408. }
  409. EndPaint(hwnd, &ps);
  410. }
  411. LRESULT CSeparator::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  412. {
  413. switch (message)
  414. {
  415. HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
  416. }
  417. return(CGenWindow::ProcessMessage(hwnd, message, wParam, lParam));
  418. }
  419. BOOL CLayeredView::Create(
  420. HWND hwndParent, // The parent of this window
  421. DWORD dwExStyle // The extended style
  422. )
  423. {
  424. return(CGenWindow::Create(
  425. hwndParent,
  426. 0,
  427. TEXT("NMLayeredView"),
  428. WS_CLIPCHILDREN,
  429. dwExStyle));
  430. }
  431. void CLayeredView::GetDesiredSize(SIZE *psize)
  432. {
  433. CGenWindow::GetDesiredSize(psize);
  434. HWND child = GetFirstChild(GetWindow());
  435. if (NULL == child)
  436. {
  437. return;
  438. }
  439. SIZE sizeContent;
  440. IGenWindow *pChild;
  441. pChild = FromHandle(child);
  442. if (NULL != pChild)
  443. {
  444. // Make sure we can always handle the first window
  445. pChild->GetDesiredSize(&sizeContent);
  446. }
  447. for (child=::GetWindow(child, GW_HWNDNEXT); NULL!=child;
  448. child=::GetWindow(child, GW_HWNDNEXT))
  449. {
  450. if (IsChildVisible(child))
  451. {
  452. pChild = FromHandle(child);
  453. if (NULL != pChild)
  454. {
  455. SIZE sizeTemp;
  456. pChild->GetDesiredSize(&sizeTemp);
  457. sizeContent.cx = max(sizeContent.cx, sizeTemp.cx);
  458. sizeContent.cy = max(sizeContent.cy, sizeTemp.cy);
  459. break;
  460. }
  461. }
  462. }
  463. psize->cx += sizeContent.cx;
  464. psize->cy += sizeContent.cy;
  465. }
  466. void CLayeredView::Layout()
  467. {
  468. HWND hwndThis = GetWindow();
  469. RECT rcClient;
  470. GetClientRect(hwndThis, &rcClient);
  471. // Just move all the children
  472. for (HWND child=GetFirstChild(hwndThis); NULL!=child;
  473. child=::GetWindow(child, GW_HWNDNEXT))
  474. {
  475. switch (m_lStyle)
  476. {
  477. case Center:
  478. {
  479. IGenWindow *pChild = FromHandle(child);
  480. if (NULL != pChild)
  481. {
  482. SIZE size;
  483. pChild->GetDesiredSize(&size);
  484. SetWindowPos(child, NULL,
  485. (rcClient.left+rcClient.right-size.cx)/2,
  486. (rcClient.top+rcClient.bottom-size.cy)/2,
  487. size.cx, size.cy, SWP_NOZORDER|SWP_NOACTIVATE);
  488. break;
  489. }
  490. }
  491. // Fall through
  492. case Fill:
  493. default:
  494. SetWindowPos(child, NULL,
  495. rcClient.left, rcClient.top,
  496. rcClient.right-rcClient.left,
  497. rcClient.bottom-rcClient.top,
  498. SWP_NOZORDER|SWP_NOACTIVATE);
  499. break;
  500. }
  501. }
  502. }