Leaked source code of windows server 2003
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.

671 lines
20 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (C) 1993-1996 Microsoft Corporation. All Rights Reserved.
  4. //
  5. // MODULE: BtnBar.cpp
  6. //
  7. // PURPOSE: Implements a generic button bar.
  8. //
  9. #include "_apipch.h"
  10. #define idtTrack 101
  11. #define idcFolderList 102
  12. #define HOTTRACK_TIMER 100
  13. #define ID_HWNDBAR 2020
  14. extern LPIMAGELIST_LOADIMAGE gpfnImageList_LoadImage;
  15. //#define DEAD
  16. void CBB_ConfigureRects(HWND hwnd);
  17. void CBB_DoHotTracking(HWND hwnd);
  18. void CBB_EndHotTracking(HWND hwnd);
  19. int CBB_HitTest(int x, int y);
  20. void CBB_SetSelBtn(int iSel,HWND hwnd);
  21. //
  22. // FUNCTION: CButtonBar::~CButtonBar()
  23. //
  24. // PURPOSE: Cleans up the resources we allocated during the life of the
  25. // object.
  26. //
  27. void CBB_Cleanup(void)
  28. {
  29. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  30. // Free the GDI resources.
  31. ImageList_Destroy(m_himlButtons);
  32. DeleteObject(m_hpalBkgnd);
  33. DeleteObject(m_hfButton);
  34. DeleteObject(m_hbmpBkgnd);
  35. // Free the button array.
  36. LocalFreeAndNull((LPVOID *)&m_rgButtons);
  37. // NOTE - this is a comment from the original athena source code
  38. //$REVIEW - we can't do this here, because it screws up
  39. // when we have multiple instances of the CButtonBar
  40. // with overlapping creates and destroys. we should
  41. // probably unregister somewhere, but it isn't strictly
  42. // necessary. (EricAn)
  43. // Unregister our window class.
  44. // UnregisterClass(c_szButtonBar, m_hInstance);
  45. return;
  46. }
  47. //
  48. // FUNCTION: CButtonBar::Create()
  49. //
  50. // PURPOSE: Initializes the button bar and creates the button bar window.
  51. //
  52. // PARAMETERS:
  53. // hwndParent - Handle of the window that will be the button bar parent.
  54. // idHwnd - Child window ID for the button bar.
  55. // idButtons - ID of the button icons bitmap.
  56. // idHorzBackground - ID of the horizontal background bitmap.
  57. // idVertBackground - ID of the vertical background bitmap.
  58. // pBtnCreateParams - Pointer to the array of BTNCREATEPARAMS used to create the buttons.
  59. // cParams - Number of buttons in pBtnCreateParams.
  60. // uSide - Side of the parent window the bar should initially attach to.
  61. //
  62. // RETURN VALUE:
  63. // Returns TRUE if successful, or FALSE otherwise.
  64. //
  65. HWND CBB_Create(HWND hwndParent, UINT idButtons,
  66. UINT idHorzBackground,
  67. PBTNCREATEPARAMS pBtnCreateParams, UINT cParams)
  68. {
  69. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  70. int i;
  71. WNDCLASS wc;
  72. BITMAP bm;
  73. RECT rc;
  74. POINT ptL, ptR;
  75. ICONMETRICS im;
  76. HWND hwnd = NULL;
  77. wc.style = CS_DBLCLKS; // Bug #15450
  78. wc.lpfnWndProc = CBB_ButtonBarProc;
  79. wc.cbClsExtra = 0;
  80. wc.cbWndExtra = sizeof(LPVOID);
  81. wc.hInstance = hinstMapiX;
  82. wc.hIcon = 0;
  83. wc.hCursor = 0;
  84. wc.hbrBackground = 0;
  85. wc.lpszMenuName = 0;
  86. wc.lpszClassName = c_szButtonBar;
  87. RegisterClass(&wc);
  88. m_rgButtons = NULL;
  89. m_himlButtons = 0;
  90. m_hbmpBkgnd = 0;
  91. m_hpalBkgnd = 0;
  92. m_hfButton = 0;
  93. m_cButtons = 0;
  94. m_iSelect = -1;
  95. m_iOldSelect = -1;
  96. // This is the information we'll need later to to draw the button bar etc.
  97. // Stash it away for now.
  98. m_cButtons = cParams;
  99. m_rgButtons = LocalAlloc(LMEM_ZEROINIT, sizeof(BUTTON) * m_cButtons);
  100. if (!m_rgButtons) return FALSE;
  101. for (i = 0; i < m_cButtons; i++)
  102. {
  103. m_rgButtons[i].id = pBtnCreateParams[i].id;
  104. m_rgButtons[i].iIcon = pBtnCreateParams[i].iIcon;
  105. LoadString(hinstMapiX, pBtnCreateParams[i].idsLabel,
  106. m_rgButtons[i].szTitle, sizeof(m_rgButtons[i].szTitle));
  107. }
  108. // Load the bitmaps we'll need for drawing.
  109. m_himlButtons = gpfnImageList_LoadImage(hinstMapiX, MAKEINTRESOURCE(idButtons),
  110. c_cxButtons, 0, c_crMask, IMAGE_BITMAP,
  111. 0); //LR_LOADMAP3DCOLORS);
  112. if (!m_himlButtons)
  113. return (FALSE);
  114. // Get the width of the bitmap we're going to use as the background so we
  115. // know how wide to make the window.
  116. if (!LoadBitmapAndPalette(idHorzBackground, &m_hbmpBkgnd, &m_hpalBkgnd))
  117. return (FALSE);
  118. if (!GetObject(m_hbmpBkgnd, sizeof(BITMAP), (LPVOID) &bm))
  119. return (FALSE);
  120. GetClientRect(hwndParent, &rc);
  121. // Get the font we're going to use for the buttons
  122. im.cbSize = sizeof(ICONMETRICS);
  123. SystemParametersInfo(SPI_GETICONMETRICS, 0, (LPVOID) &im, 0);
  124. m_hfButton = CreateFontIndirect(&(im.lfFont));
  125. if (!m_hfButton)
  126. return (FALSE);
  127. ptL.x = ptL.y=0;
  128. ptR.x = rc.right;
  129. ptR.y = bm.bmHeight;
  130. hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
  131. c_szButtonBar,
  132. c_szButtonBar,
  133. WS_CLIPSIBLINGS |
  134. WS_VISIBLE |
  135. WS_CHILD,
  136. ptL.x,
  137. ptL.y,
  138. ptR.x,
  139. ptR.y,
  140. hwndParent,
  141. (HMENU) ID_HWNDBAR,
  142. hinstMapiX,
  143. NULL);
  144. CBB_ConfigureRects(hwnd);
  145. return (hwnd);
  146. }
  147. //
  148. // FUNCTION: CButtonBar::ButtonBarProc()
  149. //
  150. // PURPOSE: Message handler for the button bar window.
  151. //
  152. LRESULT CALLBACK CBB_ButtonBarProc(HWND hwnd, UINT uMsg, WPARAM wParam,
  153. LPARAM lParam)
  154. {
  155. switch (uMsg)
  156. {
  157. case WM_NCCREATE:
  158. SetWindowLong(hwnd, 0, (LONG) ((LPCREATESTRUCT) lParam)->lpCreateParams);
  159. return (TRUE);
  160. /***
  161. case WM_CREATE:
  162. return 0;
  163. break;
  164. case WM_SIZE:
  165. return 0;
  166. break;
  167. case WM_LBUTTONDOWN:
  168. return 0;
  169. break;
  170. case WM_COMMAND:
  171. return 0;
  172. break;
  173. /***/
  174. case WM_PAINT:
  175. CBB_OnPaint(hwnd);
  176. return 0;
  177. break;
  178. case WM_MOUSEMOVE:
  179. CBB_OnMouseMove(hwnd, LOWORD(lParam), HIWORD(lParam), wParam);
  180. return 0;
  181. break;
  182. case WM_LBUTTONUP:
  183. CBB_OnLButtonUp(hwnd, LOWORD(lParam), HIWORD(lParam), wParam);
  184. return 0;
  185. break;
  186. case WM_TIMER:
  187. CBB_OnTimer(hwnd, wParam);
  188. return 0;
  189. break;
  190. case WM_MOUSEACTIVATE:
  191. CBB_OnMouseActivate(hwnd, (HWND) wParam, (INT) LOWORD(lParam), (UINT) HIWORD(lParam));
  192. return 0;
  193. break;
  194. case WM_PALETTECHANGED:
  195. if ((HWND) wParam != hwnd)
  196. {
  197. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  198. HDC hdc = GetDC(hwnd);
  199. HPALETTE hPalOld = SelectPalette(hdc, m_hpalBkgnd, TRUE);
  200. RealizePalette(hdc);
  201. InvalidateRect(hwnd, NULL, TRUE);
  202. SelectPalette(hdc, hPalOld, TRUE);
  203. ReleaseDC(hwnd, hdc);
  204. }
  205. return 0;
  206. }
  207. return (DefWindowProc(hwnd, uMsg, wParam, lParam));
  208. }
  209. void CBB_OnPaint(HWND hwnd)
  210. {
  211. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  212. HDC hdc;
  213. PAINTSTRUCT ps;
  214. BITMAP bm;
  215. HDC hdcMem;
  216. HBITMAP hbmMemOld, hbmMem;
  217. HPALETTE hpalOld;
  218. RECT rc;
  219. HFONT hf;
  220. COLORREF clrText, clrBk;
  221. int cxIndent = 3;
  222. int cyIndent = 3;
  223. int nTop = 0;
  224. int nLeft = 0;
  225. int nButton = 0;
  226. int i=0;
  227. if(!hwnd) goto out;
  228. // Get the size of the background bitmap
  229. GetObject(m_hbmpBkgnd, sizeof(BITMAP), (LPVOID) &bm);
  230. GetClientRect(hwnd, &rc);
  231. hdc = BeginPaint(hwnd, &ps);
  232. hdcMem = CreateCompatibleDC(hdc);
  233. // If we are displaying the buttons ...
  234. {
  235. // Draw the background bitmaps first.
  236. hpalOld = SelectPalette(hdc, m_hpalBkgnd, TRUE);
  237. RealizePalette(hdc);
  238. hbmMemOld = (HBITMAP) SelectObject(hdcMem, (HGDIOBJ) m_hbmpBkgnd);
  239. // If the window is taller or wider than a single bitmap, we may have
  240. // to loop and put a couple out there.
  241. while (nLeft < rc.right)
  242. {
  243. BitBlt(hdc, nLeft, nTop, bm.bmWidth, bm.bmHeight, hdcMem, 0,
  244. 0, SRCCOPY);
  245. nLeft += bm.bmWidth;
  246. }
  247. // Now draw the buttons
  248. nTop = 0;
  249. hf = (HFONT) SelectObject(hdc, m_hfButton);
  250. SetBkMode(hdc, TRANSPARENT);
  251. while (nButton < m_cButtons)
  252. {
  253. if (RectVisible(hdc, &(m_rgButtons[nButton].rcBound)))
  254. {
  255. ImageList_Draw(m_himlButtons, m_rgButtons[nButton].iIcon, hdc,
  256. m_rgButtons[nButton].rcIcon.left, m_rgButtons[nButton].rcIcon.top,
  257. ILD_TRANSPARENT);
  258. // Draw the title of the button that the mouse is over with a
  259. // different color.
  260. if (nButton == m_iSelect)
  261. {
  262. clrBk = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
  263. clrText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
  264. }
  265. else
  266. {
  267. SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  268. SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
  269. }
  270. SetTextAlign(hdc, TA_TOP /* | TA_CENTER */);
  271. if (nButton == m_iSelect)
  272. {
  273. clrText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
  274. ExtTextOut( hdc,
  275. (m_rgButtons[nButton].rcTitle.right - m_rgButtons[nButton].rcTitle.left) / 2 + m_rgButtons[nButton].rcTitle.left,
  276. m_rgButtons[nButton].rcTitle.top,
  277. ETO_OPAQUE | ETO_CLIPPED,
  278. &(m_rgButtons[nButton].rcTitle),
  279. m_rgButtons[nButton].szTitle,
  280. lstrlen(m_rgButtons[nButton].szTitle),
  281. NULL);
  282. clrText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
  283. DrawText(hdc, m_rgButtons[nButton].szTitle, lstrlen(m_rgButtons[nButton].szTitle),
  284. &m_rgButtons[nButton].rcTitle, DT_CENTER | DT_WORDBREAK);
  285. }
  286. else
  287. {
  288. DrawText(hdc, m_rgButtons[nButton].szTitle, lstrlen(m_rgButtons[nButton].szTitle),
  289. &m_rgButtons[nButton].rcTitle, DT_CENTER | DT_WORDBREAK);
  290. }
  291. if (nButton == m_iSelect)
  292. {
  293. SetBkColor(hdc,clrBk);
  294. SetTextColor(hdc, clrText);
  295. }
  296. }
  297. nButton++;
  298. }
  299. SelectObject(hdc, m_hfButton);
  300. if (hpalOld != NULL)
  301. SelectPalette(hdc, hpalOld, TRUE);
  302. SelectObject(hdcMem, hbmMemOld);
  303. }
  304. DeleteObject(hbmMem);
  305. DeleteDC(hdcMem);
  306. EndPaint(hwnd, &ps);
  307. out:
  308. return;
  309. }
  310. //
  311. // FUNCTION: CButtonBar::OnLButtonUp()
  312. //
  313. // PURPOSE: If we are dragging the button bar around, then the user has
  314. // released the bar and we can clean up. If the user wasn't
  315. // dragging, then they have clicked on a button and we send the
  316. // appropriate command message to the parent window.
  317. //
  318. void CBB_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
  319. {
  320. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  321. int iSel = 0;
  322. if (-1 != (iSel = CBB_HitTest(x, y)))
  323. {
  324. // Move command handling from LButtonUp to LButtonDown to avoid
  325. // duplicate messages being sent from double clicks - Nash Bug #15450
  326. if (0 <= iSel)
  327. {
  328. SendMessage(GetParent(hwnd), WM_COMMAND, m_rgButtons[iSel].id, (LPARAM) hwnd);
  329. CBB_SetSelBtn(-1,hwnd);
  330. }
  331. }
  332. return;
  333. }
  334. //
  335. // FUNCTION: CButtonBar::OnMouseMove()
  336. //
  337. // PURPOSE: If the user is dragging the bar around, we need to determine
  338. // which side of the parent window the mouse is closest to and
  339. // move the button bar to that edge.
  340. //
  341. // If the user is not dragging, then we need to decide if the
  342. // mouse is over a button and if so highlight the text.
  343. //
  344. void CBB_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
  345. {
  346. POINT pt = {x, y};
  347. int iSel;
  348. POINT ptScreen = {x, y};
  349. // If we're not dragging the bar around, the just update the button
  350. // selection.
  351. iSel = CBB_HitTest(x, y);
  352. CBB_SetSelBtn(iSel,hwnd);
  353. if (iSel != -1)
  354. SetCursor(LoadCursor(hinstMapiX, MAKEINTRESOURCE(idcurPointedHand)));
  355. else
  356. SetCursor(LoadCursor(NULL, IDC_ARROW));
  357. CBB_DoHotTracking(hwnd);
  358. return;
  359. }
  360. int CBB_OnMouseActivate(HWND hwnd, HWND hwndTopLevel, UINT codeHitTest, UINT msg)
  361. {
  362. return (MA_ACTIVATE);
  363. }
  364. //
  365. // FUNCTION: CButtonBar::ConfigureRects()
  366. //
  367. // PURPOSE: Calculates the rectangles that are necessary for displaying
  368. // the button bar based on the side of the parent window the
  369. // bar is currently attached to.
  370. //
  371. void CBB_ConfigureRects(HWND hwnd)
  372. {
  373. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  374. // Need to gather some font information first. We need the height of the
  375. // folder title font all the time and we need the width of the longest
  376. // button title if we're displayed horizontally.
  377. HDC hdc;
  378. int i;
  379. int cxMaxTitle;
  380. SIZE sizeString;
  381. SIZE sizeRect;
  382. int cyIconTitle;
  383. int cxCenter;
  384. int cyCenter;
  385. TEXTMETRIC tmTitle;
  386. hdc = GetDC(hwnd);
  387. SelectObject(hdc, m_hfButton);
  388. GetTextMetrics(hdc, &tmTitle);
  389. // Button text width
  390. cxMaxTitle = 0;
  391. for (i = 0; i < m_cButtons; i++)
  392. {
  393. GetTextExtentPoint32(hdc, m_rgButtons[i].szTitle,
  394. lstrlen(m_rgButtons[i].szTitle),
  395. &sizeString);
  396. if (sizeString.cx > cxMaxTitle)
  397. cxMaxTitle = sizeString.cx;
  398. }
  399. // Add a little buffer here just to make it look nice.
  400. cxMaxTitle += 10;
  401. ReleaseDC(hwnd, hdc);
  402. // Now calculate the button rectangles. Each button will have three rects
  403. // associated with it. The first rectangle is the overall bounding rect
  404. // which contains the image and title. The next rectangle is the rect for
  405. // the image which is centered horizontally within the bounding rect and
  406. // vertically when combined with the title. The final rect is the title.
  407. // Calculate the initial bounding rectangle based on whether or not we're
  408. // horizontal or vertical. sizeRect is the dimensions of each button's
  409. // bounding rectangle.
  410. {
  411. RECT rcBound,rcWnd;
  412. int cyButton=0,cxButton=0;
  413. ImageList_GetIconSize(m_himlButtons, &cxButton, &cyButton);
  414. GetClientRect(hwnd,&rcWnd);
  415. sizeRect.cx = cxMaxTitle;
  416. sizeRect.cy = rcWnd.bottom - rcWnd.top;
  417. SetRect(&rcBound, 0, 0, sizeRect.cx, sizeRect.cy);
  418. // Also calculate the offsets needed to center the image and text within
  419. // the bound.
  420. cyIconTitle = tmTitle.tmHeight + cyButton;
  421. cxCenter = ((rcBound.right - rcBound.left) - cxButton) / 2;
  422. cyCenter = ((rcBound.bottom - rcBound.top) - cyIconTitle) / 2;
  423. // Now loop through all the buttons
  424. for (i = 0; i < m_cButtons; i++)
  425. {
  426. m_rgButtons[i].rcBound = rcBound;
  427. // Center the image horizontally within the bounding rect.
  428. m_rgButtons[i].rcIcon.left = m_rgButtons[i].rcBound.left + cxCenter;
  429. m_rgButtons[i].rcIcon.top = m_rgButtons[i].rcBound.top + cyCenter;
  430. m_rgButtons[i].rcIcon.right = m_rgButtons[i].rcIcon.left + cxButton;
  431. m_rgButtons[i].rcIcon.bottom = m_rgButtons[i].rcIcon.top + cyButton;
  432. // And the button title below the image
  433. m_rgButtons[i].rcTitle.left = m_rgButtons[i].rcBound.left + 1;
  434. m_rgButtons[i].rcTitle.top = m_rgButtons[i].rcIcon.bottom;
  435. m_rgButtons[i].rcTitle.right = m_rgButtons[i].rcBound.right - 1;
  436. m_rgButtons[i].rcTitle.bottom = m_rgButtons[i].rcTitle.top + (tmTitle.tmHeight);// * 2);
  437. // Offset the rcBound to the next button.
  438. OffsetRect(&rcBound, sizeRect.cx, 0);
  439. }
  440. }
  441. }
  442. //
  443. // FUNCTION: CButtonBar::OnTimer()
  444. //
  445. // PURPOSE: When the timer fires we check to see if the mouse is still
  446. // over the button bar window. If not we remove the selection
  447. // from the active button.
  448. //
  449. void CBB_OnTimer(HWND hwnd, UINT id)
  450. {
  451. POINT pt;
  452. GetCursorPos(&pt);
  453. if (hwnd != WindowFromPoint(pt))
  454. {
  455. CBB_SetSelBtn(-1,hwnd);
  456. }
  457. CBB_EndHotTracking(hwnd);
  458. }
  459. //
  460. // FUNCTION: CButtonBar::DoHotTracking()
  461. //
  462. // PURPOSE: Starts a timer that allows the button bar to track the mouse
  463. // in case it leaves the button bar window.
  464. //
  465. void CBB_DoHotTracking(HWND hwnd)
  466. {
  467. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  468. CBB_EndHotTracking(hwnd);
  469. m_fHotTrackTimer = SetTimer(hwnd, idtTrack, HOTTRACK_TIMER, NULL);
  470. }
  471. //
  472. // FUNCTION: CButtonBar::EndHotTracking()
  473. //
  474. // PURPOSE: If the timer was set to track the mouse, we kill it and reset
  475. // our state.
  476. //
  477. void CBB_EndHotTracking(HWND hwnd)
  478. {
  479. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  480. if (m_fHotTrackTimer)
  481. {
  482. KillTimer(hwnd, idtTrack);
  483. m_fHotTrackTimer = FALSE;
  484. }
  485. }
  486. //
  487. // FUNCTION: CButtonBar::HitTest()
  488. //
  489. // PURPOSE: Returns the button number that the passed in position is
  490. // over. If the mouse is over the menu button, it returns
  491. // -2. Otherwise, if the mouse is not over a button the function
  492. // returns -1.
  493. //
  494. // PARAMETERS:
  495. // x, y - Position in client coordinates to check.
  496. //
  497. int CBB_HitTest(int x, int y)
  498. {
  499. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  500. POINT pt = {x, y};
  501. int i;
  502. // Walk through the different buttons and determine if the point is
  503. // within either their image or title.
  504. for (i = 0; i < m_cButtons; i++)
  505. {
  506. if (PtInRect(&m_rgButtons[i].rcBound, pt))
  507. /* PtInRect(&m_rgButtons[i].rcIcon, pt) ||
  508. PtInRect(&m_rgButtons[i].rcTitle, pt)) */
  509. {
  510. return (i);
  511. }
  512. }
  513. // If we're not over a button then return a default value.
  514. return (-1);
  515. }
  516. //
  517. // FUNCTION: CButtonBar::CBB_SetSelBtn()
  518. //
  519. // PURPOSE: Changes the button selection to the specified button.
  520. //
  521. void CBB_SetSelBtn(int iSel,HWND hwnd)
  522. {
  523. LPPTGDATA lpPTGData=GetThreadStoragePointer();
  524. if (m_iSelect != iSel)
  525. {
  526. HDC hdc = GetDC(hwnd);
  527. // Remove the old selection
  528. if (m_iSelect >= 0)
  529. InvalidateRect(hwnd, &m_rgButtons[m_iSelect].rcTitle, FALSE);
  530. // Add the new selection
  531. if (iSel >= 0)
  532. InvalidateRect(hwnd, &m_rgButtons[iSel].rcTitle, FALSE);
  533. m_iOldSelect = m_iSelect;
  534. // if (m_iOldSelect >= 0)
  535. // DrawFocusRect(hdc, &m_rgButtons[m_iOldSelect].rcBound);
  536. m_iSelect = iSel;
  537. // if (m_iSelect >= 0)
  538. // DrawFocusRect(hdc, &m_rgButtons[m_iSelect].rcBound);
  539. ReleaseDC(hwnd, hdc);
  540. }
  541. return;
  542. }