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.

402 lines
12 KiB

  1. /***************************************************************************/
  2. /** Microsoft Windows **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /***************************************************************************/
  5. /****************************************************************************
  6. ICONLBOX.C
  7. Implementation for IconListBox class
  8. May 93, JimH
  9. See ICONLBOX.H for details on use.
  10. ****************************************************************************/
  11. #include "npcommon.h"
  12. #include <windows.h>
  13. #include <memory.h>
  14. #include <iconlbox.h>
  15. #if defined(DEBUG)
  16. static const char szFileName[] = __FILE__;
  17. #define _FILENAME_DEFINED_ONCE szFileName
  18. #endif
  19. #include <npassert.h>
  20. /****************************************************************************
  21. IconListBox constructor
  22. Some initialization is done here, and some is done in SetHeight
  23. (when window handles are known.)
  24. ****************************************************************************/
  25. IconListBox::IconListBox(HINSTANCE hInst, int nCtlID,
  26. int iconWidth, int iconHeight) :
  27. _nCtlID(nCtlID), _hInst(hInst),
  28. _iconWidth(iconWidth), _iconHeight(iconHeight),
  29. _hbrSelected(NULL), _hbrUnselected(NULL),
  30. _fCombo(FALSE), _cIcons(0), _cTabs(0),_iCurrentMaxHorzExt(0),
  31. _hwndDialog(NULL), _hwndListBox(NULL)
  32. {
  33. _colSel = ::GetSysColor(COLOR_HIGHLIGHT);
  34. _colSelText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
  35. _colUnsel = ::GetSysColor(COLOR_WINDOW);
  36. _colUnselText = ::GetSysColor(COLOR_WINDOWTEXT);
  37. }
  38. /****************************************************************************
  39. IconListBox destructor
  40. deletes any GDI objects created by IconListBox
  41. ****************************************************************************/
  42. IconListBox::~IconListBox()
  43. {
  44. for (int i = 0; i < _cIcons; i++)
  45. {
  46. if (_aIcons[i].hbmSelected)
  47. {
  48. if (_aIcons[i].hbmSelected)
  49. {
  50. ::DeleteObject(_aIcons[i].hbmSelected);
  51. ::DeleteObject(_aIcons[i].hbmUnselected);
  52. }
  53. // Subsequent _aIcons may have used the same bitmap.
  54. // Mark those as already deleted.
  55. for (int j = i + 1; j < _cIcons; j++)
  56. {
  57. if (_aIcons[j].nResID == _aIcons[i].nResID)
  58. {
  59. _aIcons[j].hbmSelected = NULL;
  60. _aIcons[j].hbmUnselected = NULL;
  61. }
  62. }
  63. }
  64. }
  65. if (_hbrSelected)
  66. ::DeleteObject(_hbrSelected);
  67. if (_hbrUnselected)
  68. ::DeleteObject(_hbrUnselected);
  69. }
  70. /****************************************************************************
  71. IconListBox::SetHeight
  72. This function MUST be called in reponse to the WM_MEASUREITEM message.
  73. It creates some GDI objects, and initializes class variables not known
  74. at construction time.
  75. ****************************************************************************/
  76. void IconListBox::SetHeight(HWND hwndDlg,
  77. LPMEASUREITEMSTRUCT lpm,
  78. int itemHeight) // defaults to 16
  79. {
  80. ASSERT(hwndDlg != NULL);
  81. ASSERT((int)lpm->CtlID == _nCtlID);
  82. _hwndDialog = hwndDlg;
  83. _hwndListBox = ::GetDlgItem(_hwndDialog, _nCtlID);
  84. // Determine if this is a combo box
  85. char szClass[32];
  86. GetClassName(_hwndListBox,szClass,sizeof(szClass));
  87. if (::lstrcmpi(szClass,"combobox") == 0 )
  88. _fCombo = TRUE;
  89. // Create the background brushes used for filling listbox entries...
  90. _hbrSelected = ::CreateSolidBrush(_colSel);
  91. _hbrUnselected = ::CreateSolidBrush(_colUnsel);
  92. // Calculate how to centre the text vertically in the listbox item.
  93. TEXTMETRIC tm;
  94. HDC hDC = ::GetDC(hwndDlg);
  95. GetTextMetrics(hDC, &tm);
  96. // Set the only lpm entry that matters
  97. // allow larger height if passed in - but at least large enough
  98. // to fit font.
  99. lpm->itemHeight = max( itemHeight, tm.tmHeight + tm.tmExternalLeading );
  100. _nTextOffset = tm.tmExternalLeading / 2 + 1;
  101. ::ReleaseDC(hwndDlg, hDC);
  102. }
  103. /****************************************************************************
  104. IconListBox::DrawItem
  105. This function MUST be called in response to the WM_DRAWITEM message.
  106. It takes care of drawing listbox items in selected or unselected state.
  107. Drawing and undrawing the focus rectangle takes advantage of the fact
  108. that DrawFocusRect uses an XOR pen, and Windows is nice enough to assume
  109. this in the order of the ODA_FOCUS messages.
  110. ****************************************************************************/
  111. void IconListBox::DrawItem(LPDRAWITEMSTRUCT lpd)
  112. {
  113. ASSERT(_hwndDialog != NULL); // make sure SetHeight has been called
  114. char string[MAXSTRINGLEN];
  115. BOOL bSelected = (lpd->itemState & ODS_SELECTED);
  116. GetString(lpd->itemID, string);
  117. // fill entire rectangle with background color
  118. ::FillRect(lpd->hDC, &(lpd->rcItem),
  119. bSelected ? _hbrSelected : _hbrUnselected);
  120. // Look for registered icon to display, and paint it if found
  121. for (int id = 0; id < _cIcons; id++)
  122. if (_aIcons[id].nID == (int) lpd->itemData)
  123. break;
  124. if (id != _cIcons) // if we found a bitmap to display
  125. {
  126. HDC hdcMem = ::CreateCompatibleDC(lpd->hDC);
  127. HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hdcMem,
  128. bSelected ? _aIcons[id].hbmSelected : _aIcons[id].hbmUnselected);
  129. // draw bitmap ICONSPACE pixels from left and centred vertically
  130. int x = lpd->rcItem.left + ICONSPACE;
  131. int y = ((lpd->rcItem.bottom - lpd->rcItem.top) - _iconHeight) / 2;
  132. y += lpd->rcItem.top;
  133. ::BitBlt(lpd->hDC, x, y, _iconWidth, _iconHeight, hdcMem,
  134. _aIcons[id].x, _aIcons[id].y, SRCCOPY);
  135. ::SelectObject(hdcMem, hOldBitmap);
  136. ::DeleteDC(hdcMem);
  137. }
  138. if (lpd->itemState & ODS_FOCUS)
  139. ::DrawFocusRect(lpd->hDC, &(lpd->rcItem));
  140. lpd->rcItem.left += (_iconWidth + (2 * ICONSPACE));
  141. // Paint string
  142. ::SetTextColor(lpd->hDC, bSelected ? _colSelText : _colUnselText);
  143. ::SetBkColor(lpd->hDC, bSelected ? _colSel : _colUnsel);
  144. (lpd->rcItem.top) += _nTextOffset;
  145. if (_cTabs == 0) // if no tabs registered
  146. {
  147. ::DrawText(lpd->hDC, string, lstrlen(string), &(lpd->rcItem),
  148. DT_LEFT | DT_EXPANDTABS);
  149. }
  150. else
  151. {
  152. ::TabbedTextOut(lpd->hDC, lpd->rcItem.left, lpd->rcItem.top,
  153. string, lstrlen(string), _cTabs, _aTabs, 0);
  154. }
  155. }
  156. /****************************************************************************
  157. IconListBox::RegisterIcons
  158. Icons must be registered before they can be referenced in AddString.
  159. Note that if you are using several icons from the same bitmap (with different
  160. x and y offsets) they must all have the same background color.
  161. ****************************************************************************/
  162. void IconListBox::RegisterIcon( int nIconID, // caller's code
  163. int nResID, // RC file id
  164. int x, int y, // top left corner
  165. COLORREF colTransparent) // def. bright green
  166. {
  167. ASSERT( _cIcons < MAXICONS );
  168. _aIcons[_cIcons].nID = nIconID;
  169. _aIcons[_cIcons].nResID = nResID;
  170. _aIcons[_cIcons].x = x;
  171. _aIcons[_cIcons].y = y;
  172. // Check to see if we already have bitmaps for this resource ID
  173. // (which may have different x and y offsets.)
  174. for (int i = 0; i < _cIcons; i++)
  175. {
  176. if (_aIcons[i].nResID == nResID)
  177. {
  178. _aIcons[_cIcons].hbmSelected = _aIcons[i].hbmSelected;
  179. _aIcons[_cIcons].hbmUnselected = _aIcons[i].hbmUnselected;
  180. _cIcons++;
  181. return;
  182. }
  183. }
  184. // Otherwise, create new selected and unselected bitmaps
  185. // Get pointer to DIB
  186. HRSRC h = ::FindResource(_hInst, MAKEINTRESOURCE(nResID), RT_BITMAP);
  187. if (h == NULL)
  188. return;
  189. HANDLE hRes = ::LoadResource(_hInst, h);
  190. if (hRes == NULL)
  191. return;
  192. LPBITMAPINFOHEADER lpInfo = (LPBITMAPINFOHEADER) LockResource(hRes);
  193. if (NULL == lpInfo)
  194. return;
  195. // Get pointers to start of color table, and start of actual bitmap bits
  196. // Note that we make a copy of the bitmap header info and the color
  197. // table. This is so applications that use iconlistbox can keep their
  198. // resource segments read only.
  199. LPBYTE lpBits = (LPBYTE)
  200. (lpInfo + 1) + (1 << (lpInfo->biBitCount)) * sizeof(RGBQUAD);
  201. int cbCopy = (int) (lpBits - (LPBYTE)lpInfo);
  202. BYTE *lpCopy = new BYTE[cbCopy];
  203. if (!lpCopy)
  204. return;
  205. memcpy(lpCopy, lpInfo, cbCopy);
  206. RGBQUAD FAR *lpRGBQ =
  207. (RGBQUAD FAR *) ((LPSTR)lpCopy + lpInfo->biSize);
  208. // Find transparent color in color table
  209. BOOL bFound = FALSE; // did we find a transparent match?
  210. int nColorTableSize = (int) (lpBits - (LPBYTE)lpRGBQ);
  211. nColorTableSize /= sizeof(RGBQUAD);
  212. for (i = 0; i < nColorTableSize; i++)
  213. {
  214. if (colTransparent ==
  215. RGB(lpRGBQ[i].rgbRed, lpRGBQ[i].rgbGreen, lpRGBQ[i].rgbBlue))
  216. {
  217. bFound = TRUE;
  218. break;
  219. }
  220. }
  221. // Replace the transparent color with the background for selected and
  222. // unselected entries. Use these to create selected and unselected
  223. // bitmaps, and restore color table.
  224. RGBQUAD rgbqTemp; // color table entry to replace
  225. HDC hDC = ::GetDC(_hwndDialog);
  226. if (bFound)
  227. {
  228. rgbqTemp = lpRGBQ[i];
  229. lpRGBQ[i].rgbRed = GetRValue(_colUnsel);
  230. lpRGBQ[i].rgbBlue = GetBValue(_colUnsel);
  231. lpRGBQ[i].rgbGreen = GetGValue(_colUnsel);
  232. }
  233. _aIcons[_cIcons].hbmUnselected = ::CreateDIBitmap(hDC,
  234. (LPBITMAPINFOHEADER)lpCopy, CBM_INIT, lpBits,
  235. (LPBITMAPINFO)lpCopy, DIB_RGB_COLORS);
  236. if (bFound)
  237. {
  238. lpRGBQ[i].rgbRed = GetRValue(_colSel);
  239. lpRGBQ[i].rgbBlue = GetBValue(_colSel);
  240. lpRGBQ[i].rgbGreen = GetGValue(_colSel);
  241. }
  242. _aIcons[_cIcons].hbmSelected = ::CreateDIBitmap(hDC,
  243. (LPBITMAPINFOHEADER)lpCopy, CBM_INIT, lpBits,
  244. (LPBITMAPINFO)lpCopy, DIB_RGB_COLORS);
  245. if (bFound)
  246. lpRGBQ[i] = rgbqTemp; // restore original color table entry
  247. ::ReleaseDC(_hwndDialog, hDC);
  248. ::FreeResource(hRes);
  249. delete [] lpCopy;
  250. _cIcons++;
  251. }
  252. /****************************************************************************
  253. IconListBox::SetTabStops
  254. Since this is an owner-draw listbox, we can't rely on LB_SETTABS.
  255. Instead, tabs are registered here and TabbedTextOut is used to display
  256. strings. Dialogbox units have to be converted to pixels.
  257. ****************************************************************************/
  258. void IconListBox::SetTabStops(int cTabs, const int *pTabs)
  259. {
  260. ASSERT(cTabs <= MAXTABS);
  261. int nSize = (int) LOWORD(GetDialogBaseUnits());
  262. for (int i = 0; i < cTabs; i++)
  263. _aTabs[i] = ((nSize * pTabs[i]) / 4);
  264. _cTabs = cTabs;
  265. }
  266. /****************************************************************************
  267. IconListBox::UpdateHorizontalExtent
  268. ****************************************************************************/
  269. int IconListBox::UpdateHorizontalExtent(int nIcon,const char *string)
  270. {
  271. ASSERT(_hwndDialog != NULL); // make sure SetHeight has been called
  272. if (!string)
  273. return 0;
  274. // Calculate width in pixels for given string, taking into account icon, spacing and tabs
  275. int iItemWidth = ICONSPACE + (_iconWidth + (2 * ICONSPACE));
  276. HDC hDC = ::GetDC(_hwndDialog);
  277. iItemWidth += LOWORD(GetTabbedTextExtent(hDC,string,::lstrlen(string),_cTabs, _aTabs));
  278. ::ReleaseDC(_hwndDialog, hDC);
  279. // Update maximum value
  280. _iCurrentMaxHorzExt = max(_iCurrentMaxHorzExt,iItemWidth);
  281. return (int)SendDlgItemMessage(_hwndDialog,_nCtlID,
  282. LB_SETHORIZONTALEXTENT,
  283. (WPARAM)_iCurrentMaxHorzExt,0L);
  284. }