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.

693 lines
16 KiB

  1. // File: BitmapButton.cpp
  2. #include "precomp.h"
  3. #include "GenControls.h"
  4. #include <windowsx.h>
  5. static const UINT IDT_FLASH = 1;
  6. static const UINT FLASH_INTERVAL = 500;
  7. CButton::CButton() :
  8. m_pNotify(NULL)
  9. {
  10. m_sizeIcon.cx = 16;
  11. m_sizeIcon.cy = 16;
  12. }
  13. CButton::~CButton()
  14. {
  15. }
  16. BOOL CButton::Create(
  17. HWND hWndParent,
  18. INT_PTR nId,
  19. LPCTSTR szTitle,
  20. DWORD dwStyle,
  21. IButtonChange *pNotify
  22. )
  23. {
  24. if (!CFillWindow::Create(
  25. hWndParent, // Window parent
  26. nId, // ID of the child window
  27. szTitle, // Window name
  28. 0, // Window style; WS_CHILD|WS_VISIBLE will be added to this
  29. WS_EX_CONTROLPARENT // Extended window style
  30. ))
  31. {
  32. return(FALSE);
  33. }
  34. m_pNotify = pNotify;
  35. if (NULL != m_pNotify)
  36. {
  37. m_pNotify->AddRef();
  38. }
  39. // Create the Win32 button
  40. CreateWindowEx(0, TEXT("button"), szTitle,
  41. dwStyle|WS_CHILD|WS_VISIBLE|BS_NOTIFY,
  42. 0, 0, 10, 10,
  43. GetWindow(),
  44. reinterpret_cast<HMENU>(nId),
  45. reinterpret_cast<HINSTANCE>(GetWindowLongPtr(hWndParent, GWLP_HINSTANCE)),
  46. NULL);
  47. return(TRUE);
  48. }
  49. // Set the icon displayed with this button
  50. void CButton::SetIcon(
  51. HICON hIcon // The icon to use for this button
  52. )
  53. {
  54. SendMessage(GetChild(), BM_SETIMAGE, IMAGE_ICON, reinterpret_cast<LPARAM>(hIcon));
  55. m_sizeIcon.cx = 16;
  56. m_sizeIcon.cy = 16;
  57. // If we actually stored an icon, get its info
  58. hIcon = GetIcon();
  59. if (NULL != hIcon)
  60. {
  61. ICONINFO iconinfo;
  62. if (GetIconInfo(hIcon, &iconinfo))
  63. {
  64. if (NULL != iconinfo.hbmColor)
  65. {
  66. CBitmapButton::GetBitmapSizes(&iconinfo.hbmColor, &m_sizeIcon, 1);
  67. DeleteObject(iconinfo.hbmColor);
  68. }
  69. if (NULL != iconinfo.hbmMask)
  70. {
  71. DeleteObject(iconinfo.hbmMask);
  72. }
  73. }
  74. }
  75. }
  76. // Get the icon displayed with this button
  77. HICON CButton::GetIcon()
  78. {
  79. return(reinterpret_cast<HICON>(SendMessage(GetChild(), BM_GETIMAGE, IMAGE_ICON, 0)));
  80. }
  81. // Set the bitmap displayed with this button
  82. void CButton::SetBitmap(
  83. HBITMAP hBitmap // The bitmap to use for this button
  84. )
  85. {
  86. SendMessage(GetChild(), BM_SETIMAGE, IMAGE_BITMAP, reinterpret_cast<LPARAM>(hBitmap));
  87. }
  88. // Get the bitmap displayed with this button
  89. HBITMAP CButton::GetBitmap()
  90. {
  91. return(reinterpret_cast<HBITMAP>(SendMessage(GetChild(), BM_GETIMAGE, IMAGE_BITMAP, 0)));
  92. }
  93. // Get/set the checked state of the button
  94. void CButton::SetChecked(
  95. BOOL bCheck // TRUE if the button should be checked
  96. )
  97. {
  98. Button_SetCheck(GetChild(), bCheck);
  99. }
  100. BOOL CButton::IsChecked()
  101. {
  102. return(Button_GetCheck(GetChild()));
  103. }
  104. void CButton::GetDesiredSize(SIZE *psize)
  105. {
  106. static const int DefDlgUnitWidth = 50;
  107. static const int DefDlgUnitHeight = 14;
  108. static const int PushButtonBorder = 4;
  109. static const int CheckLeftBorder = 5;
  110. static const int CheckOtherBorder = 1;
  111. HWND child = GetChild();
  112. SIZE sizeMinPush = { 0, 0 };
  113. *psize = sizeMinPush;
  114. DWORD dwStyle = GetWindowLong(GetChild(), GWL_STYLE);
  115. switch (dwStyle&(BS_ICON|BS_BITMAP))
  116. {
  117. case BS_ICON:
  118. {
  119. *psize = m_sizeIcon;
  120. break;
  121. }
  122. case BS_BITMAP:
  123. {
  124. HBITMAP hImg = GetBitmap();
  125. if (NULL == hImg)
  126. {
  127. break;
  128. }
  129. CBitmapButton::GetBitmapSizes(&hImg, psize, 1);
  130. break;
  131. }
  132. default: // Text
  133. {
  134. // HACKHACK georgep: Button text should not be too large
  135. TCHAR szTitle[80];
  136. GetWindowText(child, szTitle, ARRAY_ELEMENTS(szTitle));
  137. HDC hdc = GetDC(child);
  138. HFONT hf = GetWindowFont(child);
  139. HFONT hOld = reinterpret_cast<HFONT>(SelectObject(hdc, hf));
  140. GetTextExtentPoint(hdc, szTitle, lstrlen(szTitle), psize);
  141. TEXTMETRIC tm;
  142. GetTextMetrics(hdc, &tm);
  143. sizeMinPush.cx = tm.tmAveCharWidth * DefDlgUnitWidth / 4;
  144. sizeMinPush.cy = tm.tmHeight * DefDlgUnitHeight / 8;
  145. SelectObject(hdc, hOld);
  146. ReleaseDC(child, hdc);
  147. break;
  148. }
  149. }
  150. switch (dwStyle&(BS_PUSHBUTTON|BS_CHECKBOX|BS_RADIOBUTTON))
  151. {
  152. case BS_CHECKBOX:
  153. case BS_RADIOBUTTON:
  154. {
  155. psize->cx += CheckLeftBorder + GetSystemMetrics(SM_CXMENUCHECK) + CheckOtherBorder;
  156. psize->cy += CheckOtherBorder*2;
  157. int cy = GetSystemMetrics(SM_CYMENUCHECK);
  158. psize->cy = max(psize->cy, cy);
  159. break;
  160. }
  161. case BS_PUSHBUTTON:
  162. default:
  163. psize->cx += PushButtonBorder*2;
  164. psize->cy += PushButtonBorder*2;
  165. psize->cx = max(psize->cx, sizeMinPush.cx);
  166. psize->cy = max(psize->cy, sizeMinPush.cy);
  167. break;
  168. }
  169. }
  170. LRESULT CButton::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  171. {
  172. switch (message)
  173. {
  174. HANDLE_MSG(hwnd, WM_COMMAND , OnCommand);
  175. case WM_DESTROY:
  176. if (NULL != m_pNotify)
  177. {
  178. m_pNotify->Release();
  179. m_pNotify = NULL;
  180. }
  181. break;
  182. }
  183. return(CFillWindow::ProcessMessage(hwnd, message, wParam, lParam));
  184. }
  185. void CButton::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
  186. {
  187. // Change the HWND to this and forward to the parent
  188. HWND hwndThis = GetWindow();
  189. switch (codeNotify)
  190. {
  191. case BN_CLICKED:
  192. if (NULL != m_pNotify)
  193. {
  194. m_pNotify->OnClick(this);
  195. break;
  196. }
  197. FORWARD_WM_COMMAND(GetParent(hwndThis), id, hwndThis, codeNotify, ::SendMessage);
  198. break;
  199. case BN_SETFOCUS:
  200. SetHotControl(this);
  201. break;
  202. }
  203. }
  204. CBitmapButton::CBitmapButton() :
  205. m_hbStates(NULL),
  206. m_nInputStates(0),
  207. m_nCustomStates(0),
  208. m_nCustomState(0),
  209. m_bHot(FALSE),
  210. m_nFlashState(NoFlash)
  211. {
  212. }
  213. CBitmapButton::~CBitmapButton()
  214. {
  215. if (NULL != m_hbStates)
  216. {
  217. DeleteObject(m_hbStates);
  218. m_hbStates = NULL;
  219. }
  220. }
  221. BOOL CBitmapButton::Create(
  222. HWND hWndParent, // The parent of the button
  223. int nId, // The ID for WM_COMMAND messages
  224. HBITMAP hbStates, // The 2D array of bitmaps for the states of the button,
  225. // vertically in the order specified in the StateBitmaps enum
  226. // and horizontally in the custom states order
  227. UINT nInputStates, // The number of input states (Normal, Pressed, Hot, Disabled)
  228. UINT nCustomStates, // The number of custom states
  229. IButtonChange *pNotify // The click handler
  230. )
  231. {
  232. // Copy the bitmap handle; note that we now own this bitmap, even if the
  233. // create fails
  234. m_hbStates = hbStates;
  235. // Must have a "normal" bitmap
  236. ASSERT(NULL!=hbStates && Normal<nInputStates && 1<=nCustomStates);
  237. if (!CButton::Create(
  238. hWndParent, // Window parent
  239. nId, // ID of the child window
  240. TEXT("NMButton"), // Window name
  241. BS_OWNERDRAW|BS_NOTIFY|BS_PUSHBUTTON|WS_TABSTOP, // Window style; WS_CHILD|WS_VISIBLE will be added to this
  242. pNotify
  243. ))
  244. {
  245. return(FALSE);
  246. }
  247. m_nInputStates = nInputStates;
  248. m_nCustomStates = nCustomStates;
  249. return(TRUE);
  250. }
  251. // Creates the button, using the bitmaps specified
  252. BOOL CBitmapButton::Create(
  253. HWND hWndParent, // The parent of the button
  254. int nId, // The ID for WM_COMMAND messages
  255. HINSTANCE hInst, // The instance to load the bitmap from
  256. int nIdBitmap, // The ID of the bitmap to use
  257. BOOL bTranslateColors, // Use system background colors
  258. UINT nInputStates, // The number of input states (Normal, Pressed, Hot, Disabled)
  259. UINT nCustomStates, // The number of custom states
  260. IButtonChange *pNotify // The click handler
  261. )
  262. {
  263. HBITMAP hb;
  264. LoadBitmaps(hInst, &nIdBitmap, &hb, 1, bTranslateColors);
  265. return(Create(hWndParent, nId, hb, nInputStates, nCustomStates, pNotify));
  266. }
  267. // Return the size of the "normal" bitmap.
  268. void CBitmapButton::GetDesiredSize(SIZE *ppt)
  269. {
  270. // Note that I don't want CButton::GetDesiredSize
  271. CGenWindow::GetDesiredSize(ppt);
  272. BITMAP bm;
  273. // HACKHACK georgep: Only based on the normal bitmap
  274. if (NULL == m_hbStates || 0 == m_nInputStates || 0 == m_nCustomStates
  275. || 0 == GetObject(m_hbStates, sizeof(BITMAP), &bm))
  276. {
  277. return;
  278. }
  279. ppt->cx += bm.bmWidth/m_nCustomStates;
  280. ppt->cy += bm.bmHeight/m_nInputStates;
  281. }
  282. #if FALSE
  283. void DumpWindow(HWND hwnd, LPCTSTR pszPrefix)
  284. {
  285. TCHAR szTemp[80];
  286. wsprintf(szTemp, TEXT("%s: %d "), pszPrefix, GetWindowLong(hwnd, GWL_ID));
  287. GetWindowText(hwnd, szTemp+lstrlen(szTemp), ARRAY_ELEMENTS(szTemp)-lstrlen(szTemp));
  288. lstrcat(szTemp, TEXT("\n"));
  289. OutputDebugString(szTemp);
  290. }
  291. #endif // FALSE
  292. LRESULT CBitmapButton::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  293. {
  294. switch (message)
  295. {
  296. HANDLE_MSG(hwnd, WM_DRAWITEM , OnDrawItem);
  297. HANDLE_MSG(hwnd, WM_SETCURSOR, OnSetCursor);
  298. HANDLE_MSG(hwnd, WM_TIMER , OnTimer);
  299. case WM_ENABLE:
  300. SchedulePaint();
  301. break;
  302. }
  303. return(CButton::ProcessMessage(hwnd, message, wParam, lParam));
  304. }
  305. void CBitmapButton::OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem)
  306. {
  307. int nState = Normal;
  308. int state = lpDrawItem->itemState;
  309. // If pressed or selected, show the pressed bitmap
  310. if ((((state&ODS_DISABLED) == ODS_DISABLED) || !IsWindowEnabled(GetWindow())) && m_nInputStates > Disabled)
  311. {
  312. nState = Disabled;
  313. }
  314. // If pressed or selected, show the pressed bitmap
  315. else if ((state&ODS_SELECTED) == ODS_SELECTED && m_nInputStates > Pressed)
  316. {
  317. nState = Pressed;
  318. }
  319. // If hot, show the hot bitmap
  320. else if ((m_nFlashState != ForceNormal) && ((m_nFlashState == ForceHot) || IsHot()) && m_nInputStates > Hot)
  321. {
  322. nState = Hot;
  323. }
  324. // Otherwise show the normal bitmap
  325. else
  326. {
  327. nState = Normal;
  328. }
  329. // Draw in the upper left
  330. HDC hdcDraw = lpDrawItem->hDC;
  331. HDC hdcTemp = CreateCompatibleDC(hdcDraw);
  332. if (NULL != hdcTemp)
  333. {
  334. HPALETTE hPal = GetPalette();
  335. HPALETTE hOld = NULL;
  336. if (NULL != hPal)
  337. {
  338. hOld = SelectPalette(hdcDraw, hPal, TRUE);
  339. RealizePalette(hdcDraw);
  340. SelectPalette(hdcTemp, hPal, TRUE);
  341. RealizePalette(hdcTemp);
  342. }
  343. // This will tell me the size of an individual bitmap
  344. SIZE size;
  345. // Do not use an override
  346. CBitmapButton::GetDesiredSize(&size);
  347. if (NULL != SelectObject(hdcTemp, m_hbStates))
  348. {
  349. BitBlt(hdcDraw,
  350. lpDrawItem->rcItem.left, lpDrawItem->rcItem.top,
  351. size.cx, size.cy,
  352. hdcTemp, m_nCustomState*size.cx, nState*size.cy, SRCCOPY);
  353. // BUGBUG georgep: We should clear any "uncovered" area here
  354. }
  355. DeleteDC(hdcTemp);
  356. if (NULL != hPal)
  357. {
  358. SelectPalette(hdcDraw, hOld, TRUE);
  359. }
  360. }
  361. FORWARD_WM_DRAWITEM(hwnd, lpDrawItem, CButton::ProcessMessage);
  362. }
  363. BOOL CBitmapButton::OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg)
  364. {
  365. SetHotControl(this);
  366. return(FORWARD_WM_SETCURSOR(hwnd,hwndCursor, codeHitTest, msg, CButton::ProcessMessage));
  367. }
  368. void CBitmapButton::SetCustomState(UINT nCustomState)
  369. {
  370. ASSERT(m_nCustomState < m_nCustomStates);
  371. if (m_nCustomState == nCustomState)
  372. {
  373. // Nothing to do
  374. return;
  375. }
  376. m_nCustomState = nCustomState;
  377. SchedulePaint();
  378. }
  379. void CBitmapButton::SetHot(BOOL bHot)
  380. {
  381. bHot = (bHot != FALSE);
  382. if (m_bHot == bHot)
  383. {
  384. return;
  385. }
  386. m_bHot = bHot;
  387. SchedulePaint();
  388. }
  389. // Change to flashing mode
  390. void CBitmapButton::SetFlashing(int nSeconds)
  391. {
  392. HWND hwndThis = GetWindow();
  393. if (0 == nSeconds)
  394. {
  395. KillTimer(hwndThis, IDT_FLASH);
  396. // This means to stop flashing
  397. if (IsFlashing())
  398. {
  399. m_nFlashState = NoFlash;
  400. SchedulePaint();
  401. }
  402. }
  403. else
  404. {
  405. if (NULL == hwndThis)
  406. {
  407. // I need a window to do this
  408. return;
  409. }
  410. m_endFlashing = GetTickCount() + nSeconds*1000;
  411. if (!IsFlashing())
  412. {
  413. SetTimer(hwndThis, IDT_FLASH, FLASH_INTERVAL, NULL);
  414. OnTimer(hwndThis, IDT_FLASH);
  415. }
  416. }
  417. }
  418. void CBitmapButton::OnTimer(HWND hwnd, UINT id)
  419. {
  420. if (IDT_FLASH == id)
  421. {
  422. if (static_cast<int>(GetTickCount() - m_endFlashing) > 0)
  423. {
  424. SetFlashing(0);
  425. }
  426. else
  427. {
  428. m_nFlashState = (ForceNormal==m_nFlashState ? ForceHot : ForceNormal);
  429. SchedulePaint();
  430. }
  431. }
  432. }
  433. // Helper function for getting the sizes of an array of bitmaps
  434. void CBitmapButton::GetBitmapSizes(HBITMAP parts[], SIZE sizes[], int nParts)
  435. {
  436. for (--nParts; nParts>=0; --nParts)
  437. {
  438. if (NULL == parts[nParts])
  439. {
  440. sizes[nParts].cx = sizes[nParts].cy = 0;
  441. continue;
  442. }
  443. BITMAP bm;
  444. GetObject(parts[nParts], sizeof(bm), &bm);
  445. sizes[nParts].cx = bm.bmWidth;
  446. sizes[nParts].cy = bm.bmHeight;
  447. }
  448. }
  449. // I would really rather just use LoadImage with the proper flags, but it turns
  450. // out that Win95 then tries to write into a read-only resource, which faults.
  451. // So I have to make a copy of the BITMAPINFO with the color table and change
  452. // it myself.
  453. static HBITMAP MyLoadImage(HINSTANCE hInst, int id)
  454. {
  455. // Load up the bitmap resource bits
  456. HRSRC hFound = FindResource(hInst, MAKEINTRESOURCE(id), RT_BITMAP);
  457. if (NULL == hFound)
  458. {
  459. return(NULL);
  460. }
  461. HGLOBAL hLoaded = LoadResource(hInst, hFound);
  462. if (NULL == hLoaded)
  463. {
  464. return(NULL);
  465. }
  466. HBITMAP ret = NULL;
  467. LPVOID lpBits = LockResource(hLoaded);
  468. if (NULL != lpBits)
  469. {
  470. BITMAPINFO *pbmi = reinterpret_cast<BITMAPINFO*>(lpBits);
  471. // create a "shortcut"
  472. BITMAPINFOHEADER &bmih = pbmi->bmiHeader;
  473. // Only deal with 8bpp, uncompressed image
  474. if (bmih.biSize == sizeof(BITMAPINFOHEADER)
  475. && 1 == bmih.biPlanes
  476. && BI_RGB == bmih.biCompression)
  477. {
  478. // Determine the length of the color table
  479. UINT nColors = bmih.biClrUsed;
  480. if (0 == nColors)
  481. {
  482. nColors = 1 << bmih.biBitCount;
  483. }
  484. ASSERT(nColors <= static_cast<UINT>(1<<bmih.biBitCount));
  485. // Make a copy of the BITMAPINFO and color table so I can change
  486. // the value of one of the table entries.
  487. struct
  488. {
  489. BITMAPINFO bmi;
  490. RGBQUAD rgb[256];
  491. } mbmi;
  492. CopyMemory(&mbmi, pbmi, sizeof(BITMAPINFOHEADER)+nColors*sizeof(RGBQUAD));
  493. // This is a "packed DIB" so the pixels are immediately after the
  494. // color table
  495. LPBYTE pPixels = reinterpret_cast<LPBYTE>(&pbmi->bmiColors[nColors]);
  496. BYTE byFirst = pPixels[0];
  497. switch (bmih.biBitCount)
  498. {
  499. case 8:
  500. break;
  501. case 4:
  502. byFirst = (byFirst >> 4) & 0x0f;
  503. break;
  504. case 1:
  505. byFirst = (byFirst >> 7) & 0x01;
  506. break;
  507. default:
  508. goto CleanUp;
  509. }
  510. ASSERT(static_cast<UINT>(byFirst) < nColors);
  511. // Change the value of the first pixel to be the 3DFace color
  512. RGBQUAD &rgbChange = mbmi.bmi.bmiColors[byFirst];
  513. COLORREF cr3DFace = GetSysColor(COLOR_3DFACE);
  514. rgbChange.rgbRed = GetRValue(cr3DFace);
  515. rgbChange.rgbGreen = GetGValue(cr3DFace);
  516. rgbChange.rgbBlue = GetBValue(cr3DFace);
  517. // Create the DIB section and copy the bits into it
  518. LPVOID lpDIBBits;
  519. ret = CreateDIBSection(NULL, &mbmi.bmi, DIB_RGB_COLORS,
  520. &lpDIBBits, NULL, 0);
  521. if (NULL != ret)
  522. {
  523. // Round the width up to the nearest DWORD
  524. int widthBytes = (bmih.biWidth*bmih.biBitCount+7)/8;
  525. widthBytes = (widthBytes+3)&~3;
  526. CopyMemory(lpDIBBits, pPixels, widthBytes*bmih.biHeight);
  527. }
  528. }
  529. CleanUp:
  530. UnlockResource(hLoaded);
  531. }
  532. FreeResource(hLoaded);
  533. return(ret);
  534. }
  535. //Helper function for loading up a bunch of bitmaps
  536. void CBitmapButton::LoadBitmaps(
  537. HINSTANCE hInst, // The instance to load the bitmap from
  538. const int ids[], // Array of bitmap ID's
  539. HBITMAP bms[], // Array of HBITMAP's for storing the result
  540. int nBmps, // Number of entries in the arrays
  541. BOOL bTranslateColors // Use system background colors
  542. )
  543. {
  544. for (--nBmps; nBmps>=0; --nBmps)
  545. {
  546. if (0 == ids[nBmps])
  547. {
  548. bms[nBmps] = NULL;
  549. }
  550. else
  551. {
  552. // #define TRYBMPFILE
  553. #ifdef TRYBMPFILE
  554. bms[nBmps] = NULL;
  555. // This is useful for the designer to try out different bitmaps
  556. TCHAR szFile[80];
  557. wsprintf(szFile, TEXT("%d.bmp"), ids[nBmps]);
  558. if (((DWORD)-1) != GetFileAttributes(szFile))
  559. {
  560. int nLoadFlags = LR_CREATEDIBSECTION;
  561. if (bTranslateColors)
  562. {
  563. nLoadFlags |= LR_LOADMAP3DCOLORS|LR_LOADTRANSPARENT;
  564. }
  565. bms[nBmps] = (HBITMAP)LoadImage(_Module.GetModuleInstance(),
  566. szFile, IMAGE_BITMAP, 0, 0, nLoadFlags|LR_LOADFROMFILE);
  567. }
  568. if (NULL == bms[nBmps])
  569. #endif // TRYBMPFILE
  570. {
  571. if (bTranslateColors)
  572. {
  573. //
  574. // LAURABU 2/21/99 -- LoadImage with translated colors only works
  575. // on Win9x if your resources
  576. // are NOT read-only, since Win9x tries to write into the resource
  577. // memory temporarily. It faults if not.
  578. //
  579. bms[nBmps] = MyLoadImage(hInst, ids[nBmps]);
  580. }
  581. if (NULL == bms[nBmps])
  582. {
  583. bms[nBmps] = (HBITMAP)LoadImage(hInst,
  584. MAKEINTRESOURCE(ids[nBmps]), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
  585. }
  586. }
  587. }
  588. }
  589. }