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.

1332 lines
42 KiB

  1. #include "ctlspriv.h"
  2. #include "treeview.h"
  3. #include "image.h"
  4. extern void TruncateString(char *sz, int cch);
  5. void NEAR TV_GetBackgroundBrush(PTREE pTree, HDC hdc)
  6. {
  7. if (pTree->clrBk == (COLORREF)-1) {
  8. if (pTree->ci.style & WS_DISABLED)
  9. pTree->hbrBk = FORWARD_WM_CTLCOLORSTATIC(pTree->ci.hwndParent, hdc, pTree->ci.hwnd, SendMessage);
  10. else
  11. pTree->hbrBk = FORWARD_WM_CTLCOLOREDIT(pTree->ci.hwndParent, hdc, pTree->ci.hwnd, SendMessage);
  12. }
  13. }
  14. // ----------------------------------------------------------------------------
  15. //
  16. // Draws a horizontal or vertical dotted line from the given (x,y) location
  17. // for the given length (c).
  18. //
  19. // ----------------------------------------------------------------------------
  20. void NEAR TV_DrawDottedLine(HDC hdc, int x, int y, int c, BOOL fVert)
  21. {
  22. while (c > 0)
  23. {
  24. PatBlt(hdc, x, y, 1, 1, PATCOPY);
  25. if (fVert)
  26. y += 2;
  27. else
  28. x += 2;
  29. c -= 2;
  30. }
  31. }
  32. // ----------------------------------------------------------------------------
  33. //
  34. // Draws a plus or minus sign centered around the given (x,y) location and
  35. // extending out from that location the given distance (c).
  36. //
  37. // ----------------------------------------------------------------------------
  38. void NEAR TV_DrawPlusMinus(HDC hdc, int x, int y, int c, HBRUSH hbrSign, HBRUSH hbrBox, HBRUSH hbrBk, BOOL fPlus)
  39. {
  40. int n;
  41. int p = (c * 7) / 10;
  42. n = p * 2 + 1;
  43. SelectObject(hdc, hbrSign);
  44. if (p >= 5)
  45. {
  46. PatBlt(hdc, x - p, y - 1, n, 3, PATCOPY);
  47. if (fPlus)
  48. PatBlt(hdc, x - 1, y - p, 3, n, PATCOPY);
  49. SelectObject(hdc, hbrBk);
  50. p--;
  51. n -= 2;
  52. }
  53. PatBlt(hdc, x - p, y, n, 1, PATCOPY);
  54. if (fPlus)
  55. PatBlt(hdc, x, y - p, 1, n, PATCOPY);
  56. n = c * 2 + 1;
  57. SelectObject(hdc, hbrBox);
  58. PatBlt(hdc, x - c, y - c, n, 1, PATCOPY);
  59. PatBlt(hdc, x - c, y - c, 1, n, PATCOPY);
  60. PatBlt(hdc, x - c, y + c, n, 1, PATCOPY);
  61. PatBlt(hdc, x + c, y - c, 1, n, PATCOPY);
  62. }
  63. // ----------------------------------------------------------------------------
  64. //
  65. // Create the bitmaps for the indent area of the tree as follows
  66. // if fHasLines && fHasButtons --> 7 bitmaps
  67. // if fHasLines && !fHasButtons --> 3 bitmaps
  68. // if !fHasLines && fHasButtons --> 2 bitmaps
  69. //
  70. // sets hStartBmp, hBmp, hdcBits
  71. //
  72. // If "has lines" then there are three basic bitmaps.
  73. //
  74. // | | |
  75. // | +--- +---
  76. // | |
  77. //
  78. // (The plan vertical line does not get buttons.)
  79. //
  80. // Otherwise, there are no lines, so the basic bitmaps are blank.
  81. //
  82. // If "has buttons", then the basic bitmaps are augmented with buttons.
  83. //
  84. // [+] [-]
  85. //
  86. // And if you have "lines at root", you get
  87. //
  88. // __
  89. //
  90. //
  91. // And if you have "lines at root" with "has buttons", then you also get
  92. //
  93. // --[+] --[-]
  94. //
  95. // So, there are twelve image types. Here they are, with the code names
  96. // written underneath.
  97. //
  98. // | | | | | | |
  99. // | +--- +--- [+]-- [+]-- [-]-- [-]--
  100. // | | | |
  101. //
  102. // "|" "|-" "L" "|-+" "L+" "|--" "L-"
  103. //
  104. // --- [+]-- [-]-- [+] [-]
  105. //
  106. // ".-" ".-+" ".--" "+" "-"
  107. //
  108. // And the master table of which styles get which images.
  109. //
  110. //
  111. // LINES BTNS ROOT | |- L |-+ L+ |-- L- .- .-+ .-- + -
  112. //
  113. // x 0 1
  114. // x 0 1 2 3
  115. // x 0 1 2 3
  116. // x x 0 1 2 3 4 5 6
  117. // x x 0 1 2 3
  118. // x x x 0 1 2 3 4 5 6 7 8 9
  119. //
  120. // ----------------------------------------------------------------------------
  121. void NEAR TV_CreateIndentBmps(PTREE pTree)
  122. {
  123. int cnt;
  124. RECT rc;
  125. HBRUSH hbrOld;
  126. int xMid, yMid;
  127. int x, c;
  128. HBITMAP hBmpOld;
  129. HBRUSH hbrLine;
  130. HBRUSH hbrText;
  131. HDC hdc;
  132. if (pTree->fRedraw)
  133. InvalidateRect(pTree->ci.hwnd, NULL, TRUE);
  134. if (pTree->ci.style & TVS_HASLINES)
  135. {
  136. if (pTree->ci.style & TVS_HASBUTTONS)
  137. cnt = 7; // | |- L |-+ L+ |-- L-
  138. else
  139. cnt = 3; // | |- L
  140. if (pTree->ci.style & TVS_LINESATROOT) {
  141. if (pTree->ci.style & TVS_HASBUTTONS)
  142. cnt += 3; // - -+ --
  143. else
  144. cnt += 1; // -
  145. }
  146. }
  147. else if (pTree->ci.style & TVS_HASBUTTONS)
  148. cnt = 2;
  149. else
  150. return;
  151. if (!pTree->hdcBits)
  152. pTree->hdcBits = CreateCompatibleDC(NULL);
  153. hdc = pTree->hdcBits;
  154. // Get a new background brush, just like an Edit does.
  155. TV_GetBackgroundBrush(pTree, hdc);
  156. hBmpOld = pTree->hBmp;
  157. pTree->hBmp = CreateColorBitmap(cnt * pTree->cxIndent, pTree->cyItem);
  158. if (hBmpOld) {
  159. SelectObject(hdc, pTree->hBmp);
  160. DeleteObject(hBmpOld);
  161. } else
  162. pTree->hStartBmp = SelectObject(hdc, pTree->hBmp);
  163. if (pTree->clrLine != CLR_DEFAULT)
  164. hbrLine = CreateSolidBrush(pTree->clrLine);
  165. else
  166. hbrLine = g_hbrGrayText;
  167. if (pTree->clrText != (COLORREF)-1)
  168. hbrText = CreateSolidBrush(pTree->clrText);
  169. else
  170. hbrText = g_hbrWindowText;
  171. hbrOld = SelectObject(hdc, hbrLine);
  172. rc.top = 0;
  173. rc.left = 0;
  174. rc.right = cnt * pTree->cxIndent;
  175. rc.bottom = pTree->cyItem;
  176. FillRect(hdc, &rc, pTree->hbrBk);
  177. x = 0;
  178. if (pTree->hImageList)
  179. xMid = (pTree->cxImage - MAGIC_INDENT) / 2;
  180. else
  181. xMid = pTree->cxIndent / 2;
  182. yMid = ((pTree->cyItem / 2) + 1) & ~1;
  183. c = (min(xMid, yMid)) / 2;
  184. if (pTree->ci.style & TVS_HASLINES)
  185. {
  186. TV_DrawDottedLine(hdc, x + xMid, 0, pTree->cyItem, TRUE);
  187. x += pTree->cxIndent;
  188. TV_DrawDottedLine(hdc, x + xMid, 0, pTree->cyItem, TRUE);
  189. TV_DrawDottedLine(hdc, x + xMid, yMid, pTree->cxIndent - xMid, FALSE);
  190. x += pTree->cxIndent;
  191. TV_DrawDottedLine(hdc, x + xMid, 0, yMid, TRUE);
  192. TV_DrawDottedLine(hdc, x + xMid, yMid, pTree->cxIndent - xMid, FALSE);
  193. x += pTree->cxIndent;
  194. }
  195. if (pTree->ci.style & TVS_HASBUTTONS)
  196. {
  197. BOOL fPlus = TRUE;
  198. x += xMid;
  199. doDrawPlusMinus:
  200. TV_DrawPlusMinus(hdc, x, yMid, c, hbrText, hbrLine, pTree->hbrBk, fPlus);
  201. if (pTree->ci.style & TVS_HASLINES)
  202. {
  203. TV_DrawDottedLine(hdc, x, 0, yMid - c, TRUE);
  204. TV_DrawDottedLine(hdc, x + c, yMid, pTree->cxIndent - xMid - c, FALSE);
  205. TV_DrawDottedLine(hdc, x, yMid + c, yMid - c, TRUE);
  206. x += pTree->cxIndent;
  207. TV_DrawPlusMinus(hdc, x, yMid, c, hbrText, hbrLine, pTree->hbrBk, fPlus);
  208. TV_DrawDottedLine(hdc, x, 0, yMid - c, TRUE);
  209. TV_DrawDottedLine(hdc, x + c, yMid, pTree->cxIndent - xMid - c, FALSE);
  210. }
  211. x += pTree->cxIndent;
  212. if (fPlus)
  213. {
  214. fPlus = FALSE;
  215. goto doDrawPlusMinus;
  216. }
  217. x -= xMid;
  218. }
  219. if (pTree->ci.style & TVS_LINESATROOT) {
  220. // -
  221. TV_DrawDottedLine(hdc, x + xMid, yMid, pTree->cxIndent - xMid, FALSE);
  222. x += pTree->cxIndent;
  223. if (pTree->ci.style & TVS_HASBUTTONS) {
  224. x += xMid;
  225. TV_DrawPlusMinus(hdc, x, yMid, c, hbrText, hbrLine, pTree->hbrBk, TRUE);
  226. TV_DrawDottedLine(hdc, x + c, yMid, pTree->cxIndent - xMid - c, FALSE);
  227. x += pTree->cxIndent;
  228. TV_DrawPlusMinus(hdc, x, yMid, c, hbrText, hbrLine, pTree->hbrBk, FALSE);
  229. TV_DrawDottedLine(hdc, x + c, yMid, pTree->cxIndent - xMid - c, FALSE);
  230. // uncomment if there's more to be added
  231. //x += pTree->cxIndent - xMid;
  232. }
  233. }
  234. if (hbrOld)
  235. SelectObject(pTree->hdcBits, hbrOld);
  236. if (pTree->clrLine != CLR_DEFAULT)
  237. DeleteObject(hbrLine);
  238. if (pTree->clrText != (COLORREF)-1)
  239. DeleteObject(hbrText);
  240. }
  241. // ----------------------------------------------------------------------------
  242. //
  243. // fills in a TVITEM structure based by coying data from the item or
  244. // by calling the callback to get it.
  245. //
  246. // in:
  247. // hItem item to get TVITEM struct for
  248. // mask which bits of the TVITEM struct you want (TVIF_ flags)
  249. // out:
  250. // lpItem TVITEM filled in
  251. //
  252. // ----------------------------------------------------------------------------
  253. void NEAR TV_GetItem(PTREE pTree, HTREEITEM hItem, UINT mask, LPTVITEMEX lpItem)
  254. {
  255. TV_DISPINFO nm;
  256. if (!hItem || !lpItem)
  257. return;
  258. DBG_ValidateTreeItem(hItem, FALSE);
  259. nm.item.mask = 0;
  260. // We need to check the mask to see if lpItem->pszText is valid
  261. // And even then, it might not be, so be paranoid
  262. if ((mask & TVIF_TEXT) && lpItem->pszText && lpItem->cchTextMax) {
  263. if (hItem->lpstr == LPSTR_TEXTCALLBACK) {
  264. nm.item.mask |= TVIF_TEXT;
  265. // caller had to fill in pszText and cchTextMax with valid data
  266. nm.item.pszText = lpItem->pszText;
  267. nm.item.cchTextMax = lpItem->cchTextMax;
  268. nm.item.pszText[0] = 0;
  269. } else {
  270. ASSERT(hItem->lpstr);
  271. // we could do this but this is dangerous (when responding
  272. // to TVM_GETITEM we would be giving the app a pointer to our data)
  273. // lpItem->pszText = hItem->lpstr;
  274. lstrcpyn(lpItem->pszText, hItem->lpstr, lpItem->cchTextMax);
  275. #ifndef UNICODE
  276. // only call truncate string if the source string is larger than the dest buffer
  277. // this is to deal with corel draw who passes in a bogus cchTextMax value
  278. //
  279. // We used to always call TruncateString when cchTextMax is MAX_PATH, but
  280. // McAfee Virus program (QFE1381) passes MAX_PATH with a smaller than MAX_PATH buffer
  281. // so we must always check the string length first. They luck out
  282. // and lstrlen(hItem->lpstr) is smaller than max path so we don't truncate.
  283. //
  284. if (lstrlen(hItem->lpstr) >= lpItem->cchTextMax) {
  285. // takes care of broken dbcs sequence, note lstrcpyn puts nul at
  286. // cchTextMax-1 if exceeded
  287. TruncateString(lpItem->pszText, lpItem->cchTextMax);
  288. }
  289. #endif
  290. }
  291. }
  292. if (mask & TVIF_IMAGE) {
  293. if (hItem->iImage == (WORD)I_IMAGECALLBACK)
  294. nm.item.mask |= TVIF_IMAGE;
  295. else
  296. lpItem->iImage = hItem->iImage;
  297. }
  298. if (mask & TVIF_SELECTEDIMAGE) {
  299. if (hItem->iSelectedImage == (WORD)I_IMAGECALLBACK)
  300. nm.item.mask |= TVIF_SELECTEDIMAGE;
  301. else
  302. lpItem->iSelectedImage = hItem->iSelectedImage;
  303. }
  304. if (mask & TVIF_INTEGRAL) {
  305. lpItem->iIntegral = hItem->iIntegral;
  306. }
  307. if (mask & TVIF_CHILDREN) {
  308. switch (hItem->fKids) {
  309. case KIDS_COMPUTE:
  310. lpItem->cChildren = hItem->hKids ? 1 : 0;// the actual count doesn't matter
  311. break;
  312. case KIDS_FORCE_YES:
  313. lpItem->cChildren = 1;// the actual count doesn't matter
  314. break;
  315. case KIDS_FORCE_NO:
  316. lpItem->cChildren = 0;
  317. break;
  318. case KIDS_CALLBACK:
  319. nm.item.mask |= TVIF_CHILDREN;
  320. break;
  321. }
  322. }
  323. // copy out constant parameters (and prepare for callback)
  324. // IE4 and IE5.0 did this unconditionally
  325. lpItem->state = nm.item.state = hItem->state;
  326. //
  327. // NOTICE! We do not set TVIF_STATE nm.item.mask and we do not
  328. // check for TVIF_STATE in the "any items need to be filled in
  329. // by callback?" test a few lines below. This is necessary for
  330. // backwards compat. IE5 and earlier did not call the app back
  331. // if the only thing you asked for was TVIF_STATE. You can't
  332. // change this behavior unless you guard it with a version check, or
  333. // apps will break. (They'll get callbacks when they didn't used to.)
  334. // Besides, nobody knows that they can customize the state, so it's
  335. // not like we're missing out on anything.
  336. //
  337. #ifdef DEBUG_TEST_BOLD
  338. if ((((int)hItem) / 100) % 2)
  339. lpItem->state |= TVIS_BOLD;
  340. if (!pTree->hFontBold)
  341. TV_CreateBoldFont(pTree);
  342. #endif
  343. lpItem->lParam = nm.item.lParam = hItem->lParam;
  344. // any items need to be filled in by callback?
  345. if (nm.item.mask & (TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN)) {
  346. nm.item.hItem = hItem;
  347. CCSendNotify(&pTree->ci, TVN_GETDISPINFO, &nm.hdr);
  348. // copy out things that may have been filled in on the callback
  349. if (nm.item.mask & TVIF_CHILDREN)
  350. lpItem->cChildren = nm.item.cChildren;
  351. if (nm.item.mask & TVIF_IMAGE)
  352. lpItem->iImage = nm.item.iImage;
  353. if (nm.item.mask & TVIF_SELECTEDIMAGE)
  354. lpItem->iSelectedImage = nm.item.iSelectedImage;
  355. // callback may have redirected pszText to point into its own buffer
  356. if (nm.item.mask & TVIF_TEXT)
  357. {
  358. if (mask & TVIF_TEXT) // did the *original* mask specify TVIF_TEXT?
  359. lpItem->pszText = CCReturnDispInfoText(nm.item.pszText, lpItem->pszText, lpItem->cchTextMax);
  360. else
  361. lpItem->pszText = nm.item.pszText; // do what we used to do
  362. }
  363. if (nm.item.mask & TVIF_STATE) {
  364. lpItem->state = (nm.item.state & nm.item.stateMask) | (lpItem->state & ~nm.item.stateMask);
  365. if ((lpItem->state & TVIS_BOLD) && !pTree->hFontBold)
  366. TV_CreateBoldFont(pTree);
  367. }
  368. if (nm.item.mask & TVIF_DI_SETITEM) {
  369. if (nm.item.mask & TVIF_TEXT)
  370. if (nm.item.pszText) {
  371. ASSERT(hItem->lpstr == LPSTR_TEXTCALLBACK);
  372. Str_Set(&hItem->lpstr, nm.item.pszText);
  373. }
  374. if (nm.item.mask & TVIF_STATE) {
  375. // if the bold bit changed, then the width changed
  376. if ((hItem->state ^ lpItem->state) & TVIS_BOLD)
  377. hItem->iWidth = 0;
  378. hItem->state = (WORD) lpItem->state;
  379. }
  380. if (nm.item.mask & TVIF_IMAGE)
  381. hItem->iImage = (WORD) lpItem->iImage;
  382. if (nm.item.mask & TVIF_SELECTEDIMAGE)
  383. hItem->iSelectedImage = (WORD) lpItem->iSelectedImage;
  384. if (nm.item.mask & TVIF_CHILDREN) {
  385. switch(nm.item.cChildren) {
  386. case I_CHILDRENCALLBACK:
  387. hItem->fKids = KIDS_CALLBACK;
  388. break;
  389. case 0:
  390. hItem->fKids = KIDS_FORCE_NO;
  391. break;
  392. default:
  393. hItem->fKids = KIDS_FORCE_YES;
  394. break;
  395. }
  396. }
  397. }
  398. }
  399. }
  400. // ----------------------------------------------------------------------------
  401. //
  402. // Draws the given item starting at the given (x,y) and extending down and to
  403. // the right.
  404. //
  405. // ----------------------------------------------------------------------------
  406. BOOL NEAR TV_ShouldItemDrawBlue(PTREE pTree, TVITEMEX *ti, UINT flags)
  407. {
  408. return ( (ti->state & TVIS_DROPHILITED) ||
  409. (!pTree->hDropTarget &&
  410. !(flags & TVDI_GRAYCTL) &&
  411. (ti->state & TVIS_SELECTED) &&
  412. pTree->fFocus));
  413. }
  414. #define TV_ShouldItemDrawDisabled(pTree, pti, flags) (flags & TVDI_GRAYCTL)
  415. //
  416. // Caution: Depending on the user's color scheme, a Gray item may
  417. // end up looking Blue if Gray would otherwise be invisible. So make
  418. // sure that there are other cues that the user can use to tell whether
  419. // the item is "Really Blue" or "Gray masquerading as Blue".
  420. //
  421. // For example, you might get both is if the treeview is
  422. // participating in drag/drop while it is not the active window,
  423. // because the selected item gets "Gray masquerading as Blue" and
  424. // the drop target gets "Really Blue". But we special-case that
  425. // and turn off the selection while we are worrying about drag/drop,
  426. // so there is no confusion after all.
  427. //
  428. BOOL TV_ShouldItemDrawGray(PTREE pTree, TVITEMEX *pti, UINT flags)
  429. {
  430. return ((flags & TVDI_GRAYCTL) ||
  431. (!pTree->hDropTarget &&
  432. ((pti->state & TVIS_SELECTED) &&
  433. (!pTree->fFocus && (pTree->ci.style & TVS_SHOWSELALWAYS)) )));
  434. }
  435. //
  436. // Draw a descender line for the item. It is the caller's job to
  437. // draw the appropriate glyph at level 0.
  438. //
  439. void
  440. TV_DrawDescender(PTREE pTree, HDC hdc, int x, int y, HTREEITEM hItem)
  441. {
  442. int i;
  443. for (i = 1; i < hItem->iIntegral; i++)
  444. BitBlt(hdc, x, y + i * pTree->cyItem, pTree->cxIndent, pTree->cyItem, pTree->hdcBits, 0, 0, SRCCOPY);
  445. }
  446. //
  447. // Erase any previous descender line for the item.
  448. //
  449. void
  450. TV_EraseDescender(PTREE pTree, HDC hdc, int x, int y, HTREEITEM hItem)
  451. {
  452. RECT rc;
  453. rc.left = x;
  454. rc.right = x + pTree->cxIndent;
  455. rc.top = y + pTree->cyItem;
  456. rc.bottom = y + hItem->iIntegral * pTree->cyItem;
  457. FillRect(hdc, &rc, pTree->hbrBk);
  458. }
  459. //
  460. // Draw (or erase) descenders for siblings and children.
  461. //
  462. void TV_DrawKinDescender(PTREE pTree, HDC hdc, int x, int y, HTREEITEM hItem, UINT state)
  463. {
  464. if (hItem->hNext) // Connect to next sibling
  465. TV_DrawDescender(pTree, hdc, x, y, hItem);
  466. else
  467. TV_EraseDescender(pTree, hdc, x, y, hItem);
  468. // If any bonus images, then need to connect the image to the kids.
  469. if (pTree->himlState || pTree->hImageList) {
  470. if (state & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) // Connect to expanded kids
  471. TV_DrawDescender(pTree, hdc, x + pTree->cxIndent, y, hItem);
  472. else
  473. TV_EraseDescender(pTree, hdc, x + pTree->cxIndent, y, hItem);
  474. }
  475. }
  476. void NEAR TV_DrawItem(PTREE pTree, HTREEITEM hItem, HDC hdc, int x, int y, UINT flags)
  477. {
  478. UINT cxIndent = pTree->cxIndent;
  479. COLORREF rgbOldBack = 0, rgbOldText;
  480. COLORREF clrBk = CLR_DEFAULT;
  481. RECT rc;
  482. int iBack, iText;
  483. HTREEITEM hItemSave = hItem;
  484. LPTSTR lpstr;
  485. int cch;
  486. UINT etoFlags = ETO_OPAQUE | ETO_CLIPPED;
  487. TVITEMEX ti;
  488. TCHAR szTemp[MAX_PATH];
  489. int iState = 0;
  490. HFONT hFont; //$BOLD
  491. DWORD dwRet;
  492. NMTVCUSTOMDRAW nmcd;
  493. BOOL fItemFocused = ((pTree->fFocus) && (hItem == pTree->hCaret));
  494. DWORD clrTextTemp, clrTextBkTemp;
  495. BOOL fSelectedIcon = FALSE;
  496. rc.top = y;
  497. rc.bottom = rc.top + (pTree->cyItem * hItem->iIntegral);
  498. rc.left = 0;
  499. rc.right = pTree->cxWnd;
  500. if (flags & TVDI_ERASE) {
  501. // Opaque the whole item
  502. FillRect(hdc, &rc, pTree->hbrBk);
  503. }
  504. // make sure the callbacks don't invalidate this item
  505. pTree->hItemPainting = hItem;
  506. ti.pszText = szTemp;
  507. ti.cchTextMax = ARRAYSIZE(szTemp);
  508. ti.stateMask = TVIS_OVERLAYMASK | TVIS_CUT | TVIS_BOLD; //$BOLD
  509. TV_GetItem(pTree, hItem, TVIF_IMAGE | TVIF_STATE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_CHILDREN | TVIF_PARAM, &ti);
  510. pTree->hItemPainting = NULL;
  511. ////////////////
  512. // set up the HDC
  513. if (TV_ShouldItemDrawBlue(pTree,&ti,flags)) {
  514. // selected
  515. iBack = COLOR_HIGHLIGHT;
  516. iText = COLOR_HIGHLIGHTTEXT;
  517. } else if (TV_ShouldItemDrawDisabled(pTree, &pti, flags)) {
  518. iBack = COLOR_3DFACE;
  519. iText = COLOR_GRAYTEXT;
  520. } else if (TV_ShouldItemDrawGray(pTree, &ti, flags)) {
  521. // On some color schemes, the BTNFACE color equals the WINDOW color,
  522. // and our gray comes out invisible. In such case, change from gray
  523. // to blue so you can see it at all.
  524. if (GetSysColor(COLOR_WINDOW) != GetSysColor(COLOR_BTNFACE))
  525. {
  526. iBack = COLOR_BTNFACE;
  527. iText = COLOR_BTNTEXT;
  528. }
  529. else
  530. {
  531. iBack = COLOR_HIGHLIGHT;
  532. iText = COLOR_HIGHLIGHTTEXT;
  533. }
  534. } else {
  535. // not selected
  536. iBack = COLOR_WINDOW;
  537. iText = COLOR_WINDOWTEXT;
  538. if (hItem == pTree->hHot) {
  539. iText = COLOR_HOTLIGHT;
  540. }
  541. }
  542. if (iBack == COLOR_WINDOW && (pTree->clrBk != (COLORREF)-1))
  543. nmcd.clrTextBk = clrTextBkTemp = pTree->clrBk;
  544. else
  545. nmcd.clrTextBk = clrTextBkTemp = GetSysColor(iBack);
  546. if (iText == COLOR_WINDOWTEXT && (pTree->clrText != (COLORREF)-1))
  547. nmcd.clrText = clrTextTemp = pTree->clrText;
  548. else
  549. nmcd.clrText = clrTextTemp = GetSysColor(iText);
  550. // if forcing black and transparent, do so. dc's BkMode should
  551. // already be set to TRANSPARENT by caller
  552. if (flags & TVDI_TRANSTEXT)
  553. {
  554. nmcd.clrText = clrTextTemp = 0x000000;
  555. etoFlags = 0; // don't opaque nothin'
  556. }
  557. rgbOldBack = SetBkColor(hdc, nmcd.clrTextBk);
  558. rgbOldText = SetTextColor(hdc, nmcd.clrText);
  559. #ifdef WINDOWS_ME
  560. if (pTree->ci.style & TVS_RTLREADING)
  561. etoFlags |= ETO_RTLREADING;
  562. #endif
  563. // Figure out which font to use.
  564. if (ti.state & TVIS_BOLD) {
  565. hFont = pTree->hFontBold;
  566. if (hItem == pTree->hHot) {
  567. hFont = CCGetHotFont(pTree->hFontBold, &pTree->hFontBoldHot);
  568. }
  569. } else {
  570. hFont = pTree->hFont;
  571. if (hItem == pTree->hHot) {
  572. hFont = CCGetHotFont(pTree->hFont, &pTree->hFontHot);
  573. }
  574. }
  575. hFont = SelectObject(hdc, hFont);
  576. // End HDC setup
  577. ////////////////
  578. // notify on custom draw then do it!
  579. nmcd.nmcd.hdc = hdc;
  580. nmcd.nmcd.dwItemSpec = (DWORD_PTR)hItem;
  581. nmcd.nmcd.uItemState = 0;
  582. nmcd.nmcd.rc = rc;
  583. if (flags & TVDI_NOTREE)
  584. nmcd.iLevel = 0;
  585. else
  586. nmcd.iLevel = hItem->iLevel;
  587. if (ti.state & TVIS_SELECTED) {
  588. fSelectedIcon = TRUE;
  589. if (pTree->fFocus || (pTree->ci.style & TVS_SHOWSELALWAYS))
  590. nmcd.nmcd.uItemState |= CDIS_SELECTED;
  591. }
  592. if (fItemFocused)
  593. nmcd.nmcd.uItemState |= CDIS_FOCUS;
  594. if (hItem == pTree->hHot)
  595. nmcd.nmcd.uItemState |= CDIS_HOT;
  596. #ifdef KEYBOARDCUES
  597. #if 0
  598. // BUGBUG: Custom draw stuff for UISTATE (stephstm)
  599. if (CCGetUIState(&(pTree->ci), KC_TBD))
  600. nmcd.nmcd.uItemState |= CDIS_SHOWKEYBOARDCUES;
  601. #endif
  602. #endif
  603. nmcd.nmcd.lItemlParam = ti.lParam;
  604. dwRet = CICustomDrawNotify(&pTree->ci, CDDS_ITEMPREPAINT, &nmcd.nmcd);
  605. if (dwRet & CDRF_SKIPDEFAULT)
  606. return;
  607. fItemFocused = (nmcd.nmcd.uItemState & CDIS_FOCUS);
  608. if (nmcd.nmcd.uItemState & CDIS_SELECTED)
  609. ti.state |= TVIS_SELECTED;
  610. else {
  611. ti.state &= ~TVIS_SELECTED;
  612. }
  613. if (nmcd.clrTextBk != clrTextBkTemp)
  614. SetBkColor(hdc, nmcd.clrTextBk);
  615. if (nmcd.clrText != clrTextTemp)
  616. SetTextColor(hdc, nmcd.clrText);
  617. if (pTree->ci.style & TVS_FULLROWSELECT &&
  618. !(flags & TVDI_TRANSTEXT))
  619. {
  620. FillRectClr(hdc, &nmcd.nmcd.rc, GetBkColor(hdc));
  621. etoFlags |= ETO_OPAQUE;
  622. clrBk = CLR_NONE;
  623. }
  624. if (!(flags & TVDI_NOTREE)) {
  625. if ((pTree->ci.style & (TVS_HASLINES | TVS_HASBUTTONS)) &&
  626. (pTree->ci.style & TVS_LINESATROOT))
  627. // Make room for the "plus" at the front of the tree
  628. x += cxIndent;
  629. }
  630. // deal with margin, etc.
  631. x += (pTree->cxBorder + (nmcd.iLevel * cxIndent));
  632. y += pTree->cyBorder;
  633. // draw image
  634. if ((!(flags & TVDI_NOTREE) && !(dwRet & TVCDRF_NOIMAGES)) || (flags & TVDI_FORCEIMAGE))
  635. {
  636. int dx, dy; // to clip the images within the borders.
  637. COLORREF clrImage = CLR_HILIGHT;
  638. COLORREF clrBkImage = clrBk;
  639. if (flags & TVDI_NOBK)
  640. {
  641. clrBkImage = CLR_NONE;
  642. }
  643. if (pTree->himlState) {
  644. iState = TV_StateIndex(&ti);
  645. // go figure. in the treeview, 0 for the state image index
  646. // means draw nothing... the 0th item is unused.
  647. // the listview is 0 based and uses the 0th item.
  648. if (iState) {
  649. dx = min(pTree->cxState, pTree->cxMax - pTree->cxBorder - x);
  650. dy = min(pTree->cyState, pTree->cyItem - (2 * pTree->cyBorder));
  651. ImageList_DrawEx(pTree->himlState, iState, hdc, x,
  652. y + max(pTree->cyItem - pTree->cyState, 0), dx, dy, clrBk, CLR_DEFAULT, ILD_NORMAL);
  653. x += pTree->cxState;
  654. }
  655. }
  656. if (pTree->hImageList) {
  657. UINT fStyle = 0;
  658. int i = (fSelectedIcon) ? ti.iSelectedImage : ti.iImage;
  659. if (ti.state & TVIS_CUT) {
  660. fStyle |= ILD_BLEND50;
  661. clrImage = ImageList_GetBkColor(pTree->hImageList);
  662. }
  663. dx = min(pTree->cxImage - MAGIC_INDENT, pTree->cxMax - pTree->cxBorder - x);
  664. dy = min(pTree->cyImage, pTree->cyItem - (2 * pTree->cyBorder));
  665. ImageList_DrawEx(pTree->hImageList, i, hdc,
  666. x, y + (max(pTree->cyItem - pTree->cyImage, 0) / 2), dx, dy,
  667. clrBkImage, clrImage,
  668. fStyle | (ti.state & TVIS_OVERLAYMASK));
  669. }
  670. }
  671. if (pTree->hImageList) {
  672. // even if not drawing image, draw text in right place
  673. x += pTree->cxImage;
  674. }
  675. // draw text
  676. lpstr = ti.pszText;
  677. cch = lstrlen(lpstr);
  678. if (!hItem->iWidth || (hItem->lpstr == LPSTR_TEXTCALLBACK))
  679. {
  680. TV_ComputeItemWidth(pTree, hItem, hdc); //$BOLD
  681. }
  682. rc.left = x;
  683. rc.top = y + pTree->cyBorder;
  684. rc.right = min((x + hItem->iWidth),
  685. (pTree->cxMax - pTree->cxBorder));
  686. rc.bottom-= pTree->cyBorder;
  687. // Draw the text, unless it's the one we are editing
  688. if (pTree->htiEdit != hItem || !IsWindow(pTree->hwndEdit) || !IsWindowVisible(pTree->hwndEdit))
  689. {
  690. ExtTextOut(hdc, x + g_cxLabelMargin, y + ((pTree->cyItem - pTree->cyText) / 2) + g_cyBorder,
  691. etoFlags, &rc, lpstr, cch, NULL);
  692. // Draw the focus rect, if appropriate.
  693. if (pTree->fFocus && (fItemFocused) &&
  694. !(pTree->ci.style & TVS_FULLROWSELECT) &&
  695. !(flags & (TVDI_TRANSTEXT | TVDI_GRAYCTL))
  696. #ifdef KEYBOARDCUES
  697. && !(CCGetUIState(&(pTree->ci)) & UISF_HIDEFOCUS)
  698. #endif
  699. )
  700. DrawFocusRect(hdc, &rc);
  701. }
  702. SetBkColor(hdc, rgbOldBack);
  703. SetTextColor(hdc, rgbOldText);
  704. // Restore the original font. //$BOLD
  705. SelectObject(hdc, hFont); //$BOLD
  706. // Notice that we should have opaque'd the rest of the line above if no tree
  707. if (!(flags & TVDI_NOTREE))
  708. {
  709. int dx, dy;
  710. if (pTree->hImageList)
  711. x -= pTree->cxImage;
  712. if (iState)
  713. x -= pTree->cxState;
  714. if (pTree->ci.style & TVS_HASLINES)
  715. {
  716. int i;
  717. x -= cxIndent;
  718. if (nmcd.iLevel-- || (pTree->ci.style & TVS_LINESATROOT))
  719. {
  720. // HACK: Special case the first root
  721. // We will draw a "last" sibling button upside down
  722. if (nmcd.iLevel == -1 && hItem == hItem->hParent->hKids)
  723. {
  724. if (hItem->hNext) {
  725. i = 2; // "L"
  726. if (ti.cChildren && (pTree->ci.style & TVS_HASBUTTONS))
  727. {
  728. i += 2; // "L+"
  729. if ((ti.state & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == TVIS_EXPANDED)
  730. i += 2; // "L-"
  731. }
  732. dx = min((int)cxIndent, pTree->cxMax - pTree->cxBorder - x);
  733. dy = pTree->cyItem - (2 * pTree->cyBorder);
  734. StretchBlt(hdc, x, y + pTree->cyItem, cxIndent, -pTree->cyItem, pTree->hdcBits
  735. , i * cxIndent, 0, dx, dy, SRCCOPY);
  736. i = -1;
  737. }
  738. else
  739. {
  740. // first root no siblings
  741. // if there's no other item, draw just the button if button mode,
  742. if (pTree->ci.style & TVS_HASBUTTONS)
  743. {
  744. if (ti.cChildren) {
  745. // hasbuttons, has lines, lines at root
  746. i = ((ti.state & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == TVIS_EXPANDED) ?
  747. 9 : 8; // ".--" : ".-+"
  748. } else {
  749. i = 7; // ".-"
  750. }
  751. }
  752. else
  753. {
  754. i = 3; // ".-"
  755. }
  756. }
  757. }
  758. else
  759. {
  760. i = (hItem->hNext) ? 1 : 2; // "|-" (rep) : "L"
  761. if (ti.cChildren && (pTree->ci.style & TVS_HASBUTTONS))
  762. {
  763. i += 2; // "|-+" (rep) : "L+"
  764. if ((ti.state & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == TVIS_EXPANDED)
  765. i += 2; // "|--" (rep) : "L-"
  766. }
  767. }
  768. if (hItem->iIntegral > 1)
  769. TV_DrawKinDescender(pTree, hdc, x, y, hItem, ti.state);
  770. if (i != -1)
  771. {
  772. dx = min((int)cxIndent, pTree->cxMax - pTree->cxBorder - x);
  773. dy = pTree->cyItem - (2 * pTree->cyBorder);
  774. if ((dx > 0) && (dy > 0))
  775. BitBlt(hdc, x, y, dx, dy, pTree->hdcBits
  776. , i * cxIndent, 0, SRCCOPY);
  777. }
  778. while ((--nmcd.iLevel >= 0) || ((pTree->ci.style & TVS_LINESATROOT) && nmcd.iLevel >= -1))
  779. {
  780. hItem = hItem->hParent;
  781. x -= cxIndent;
  782. if (hItem->hNext)
  783. {
  784. dx = min((int)cxIndent, (pTree->cxMax - pTree->cxBorder - x));
  785. dy = min(pTree->cyItem, pTree->cyWnd - pTree->cyBorder - y);
  786. if ((dx > 0) && (dy > 0))
  787. BitBlt(hdc, x, y, dx, dy, pTree->hdcBits, 0, 0, SRCCOPY);
  788. TV_DrawDescender(pTree, hdc, x, y, hItemSave);
  789. }
  790. }
  791. }
  792. }
  793. else
  794. { // no lines
  795. if ((pTree->ci.style & TVS_HASBUTTONS) && (nmcd.iLevel || pTree->ci.style & TVS_LINESATROOT)
  796. && ti.cChildren)
  797. {
  798. int i = (ti.state & TVIS_EXPANDED) ? cxIndent : 0;
  799. x -= cxIndent;
  800. dx = min((int)cxIndent, pTree->cxMax - pTree->cxBorder - x);
  801. dy = min(pTree->cyItem, pTree->cyWnd - pTree->cyBorder - y);
  802. if ((dx > 0) && (dy > 0))
  803. BitBlt(hdc, x, y, dx, dy, pTree->hdcBits, i, 0, SRCCOPY);
  804. }
  805. }
  806. }
  807. if (dwRet & CDRF_NOTIFYPOSTPAINT) {
  808. nmcd.nmcd.dwItemSpec = (DWORD_PTR)hItemSave;
  809. CICustomDrawNotify(&pTree->ci, CDDS_ITEMPOSTPAINT, &nmcd.nmcd);
  810. }
  811. }
  812. #define INSERTMARKSIZE 6
  813. BOOL TV_GetInsertMarkRect(PTREE pTree, LPRECT prc)
  814. {
  815. ASSERT(pTree);
  816. if(pTree->htiInsert && TV_GetItemRect(pTree, pTree->htiInsert, prc, TRUE))
  817. {
  818. if (pTree->fInsertAfter)
  819. prc->top = prc->bottom;
  820. else
  821. prc->bottom = prc->top;
  822. prc->top -= INSERTMARKSIZE/2;
  823. prc->bottom += INSERTMARKSIZE/2 + 1;
  824. prc->right = pTree->cxWnd - INSERTMARKSIZE; // should always go all the way to right with pad.
  825. prc->left -= pTree->cxImage;
  826. return TRUE;
  827. }
  828. return FALSE;
  829. }
  830. // this is implemented in toolbar.c, but we should be able to use
  831. // as well as long as we always set fHorizMode to FALSE
  832. void PASCAL DrawInsertMark(HDC hdc, LPRECT prc, BOOL fHorizMode, COLORREF clr);
  833. __inline COLORREF TV_GetInsertMarkColor(PTREE pTree)
  834. {
  835. if (pTree->clrim == CLR_DEFAULT)
  836. return g_clrWindowText;
  837. else
  838. return pTree->clrim;
  839. }
  840. void NEAR TV_DrawTree(PTREE pTree, HDC hdc, BOOL fErase, LPRECT lprc)
  841. {
  842. int x;
  843. int iStart, iCnt;
  844. UINT uFlags;
  845. RECT rc;
  846. NMCUSTOMDRAW nmcd;
  847. if (!pTree->fRedraw)
  848. return;
  849. if (pTree->ci.style & TVS_CHECKBOXES)
  850. if (!pTree->himlState)
  851. TV_InitCheckBoxes(pTree);
  852. x = -pTree->xPos;
  853. TV_GetBackgroundBrush(pTree, hdc);
  854. rc = *lprc;
  855. #ifdef MAINWIN
  856. if (lprc->top <= 0) { /* fix microsoft BUG */
  857. iStart = 0;
  858. } else {
  859. iStart = lprc->top / pTree->cyItem;
  860. }
  861. #else
  862. iStart = lprc->top / pTree->cyItem;
  863. #endif
  864. if (pTree->cItems && pTree->hTop) {
  865. ASSERT(ITEM_VISIBLE(pTree->hTop));
  866. iCnt = pTree->cShowing - pTree->hTop->iShownIndex;
  867. } else {
  868. iCnt = 0; // Nothing to draw
  869. }
  870. nmcd.hdc = hdc;
  871. /// not implemented yet
  872. //if (ptb->ci.hwnd == GetFocus())
  873. //nmcd.uItemState = CDIS_FOCUS;
  874. //else
  875. nmcd.uItemState = 0;
  876. nmcd.lItemlParam = 0;
  877. nmcd.rc = rc;
  878. pTree->ci.dwCustom = CICustomDrawNotify(&pTree->ci, CDDS_PREPAINT, &nmcd);
  879. if (!(pTree->ci.dwCustom & CDRF_SKIPDEFAULT)) {
  880. if (iStart < iCnt)
  881. {
  882. HTREEITEM hItem;
  883. HFONT hOldFont;
  884. RECT rcT;
  885. int y = 0;
  886. for (hItem = pTree->hTop; hItem; ) {
  887. if (iStart > hItem->iIntegral) {
  888. iStart -= hItem->iIntegral;
  889. y += hItem->iIntegral * pTree->cyItem;
  890. hItem = TV_GetNextVisItem(hItem);
  891. } else
  892. break;
  893. }
  894. hOldFont = pTree->hFont ? SelectObject(hdc, pTree->hFont) : NULL;
  895. // TVDI_* for all items
  896. uFlags = (pTree->ci.style & WS_DISABLED) ? TVDI_GRAYCTL : 0;
  897. if (fErase)
  898. uFlags |= TVDI_ERASE;
  899. // loop from the first visible item until either all visible items are
  900. // drawn or there are no more items to draw
  901. for ( ; hItem && y < lprc->bottom; hItem = TV_GetNextVisItem(hItem))
  902. {
  903. TV_DrawItem(pTree, hItem, hdc, x, y, uFlags);
  904. y += pTree->cyItem * hItem->iIntegral;
  905. }
  906. //
  907. // handle drawing the InsertMark next to this item.
  908. //
  909. if(TV_GetInsertMarkRect(pTree, &rcT))
  910. DrawInsertMark(hdc, &rcT, FALSE, TV_GetInsertMarkColor(pTree));
  911. if (hOldFont)
  912. SelectObject(hdc, hOldFont);
  913. rc.top = y;
  914. }
  915. if (fErase)
  916. // Opaque out everything we have not drawn explicitly
  917. FillRect(hdc, &rc, pTree->hbrBk);
  918. // notify parent afterwards if they want us to
  919. if (pTree->ci.dwCustom & CDRF_NOTIFYPOSTPAINT) {
  920. CICustomDrawNotify(&pTree->ci, CDDS_POSTPAINT, &nmcd);
  921. }
  922. }
  923. }
  924. // ----------------------------------------------------------------------------
  925. //
  926. // Set up for paint, call DrawTree, and clean up after paint.
  927. //
  928. // ----------------------------------------------------------------------------
  929. void NEAR TV_Paint(PTREE pTree, HDC hdc)
  930. {
  931. PAINTSTRUCT ps;
  932. if (hdc)
  933. {
  934. // hdc != 0 indicates a subclassed paint -- use the hdc passed in
  935. SetRect(&ps.rcPaint, 0, 0, pTree->cxWnd, pTree->cyWnd);
  936. TV_DrawTree(pTree, hdc, TRUE, &ps.rcPaint);
  937. }
  938. else
  939. {
  940. BeginPaint(pTree->ci.hwnd, &ps);
  941. TV_DrawTree(pTree, ps.hdc, ps.fErase, &ps.rcPaint);
  942. EndPaint(pTree->ci.hwnd, &ps);
  943. }
  944. }
  945. // ----------------------------------------------------------------------------
  946. // Create an imagelist to be used for dragging.
  947. //
  948. // 1) create mask and image bitmap matching the select bounds size
  949. // 2) draw the text to both bitmaps (in black for now)
  950. // 3) create an imagelist with these bitmaps
  951. // 4) make a dithered copy of the image onto the new imagelist
  952. // ----------------------------------------------------------------------------
  953. HIMAGELIST NEAR TV_CreateDragImage(PTREE pTree, HTREEITEM hItem)
  954. {
  955. HDC hdcMem = NULL;
  956. HBITMAP hbmImage = NULL;
  957. HBITMAP hbmMask = NULL;
  958. HBITMAP hbmOld;
  959. HIMAGELIST himl = NULL;
  960. BOOL bMirroredWnd = (pTree->ci.dwExStyle&RTL_MIRRORED_WINDOW);
  961. int dx, dy;
  962. int iSrc;
  963. TVITEMEX ti;
  964. if (!pTree->hImageList)
  965. return NULL;
  966. if (hItem == NULL)
  967. hItem = pTree->htiDrag;
  968. if (hItem == NULL)
  969. return NULL;
  970. // BUGBUG??? we know it's already been drawn, so is iWidth valid???
  971. dx = hItem->iWidth + pTree->cxImage;
  972. dy = pTree->cyItem;
  973. if (!(hdcMem = CreateCompatibleDC(NULL)))
  974. goto CDI_Exit;
  975. if (!(hbmImage = CreateColorBitmap(dx, dy)))
  976. goto CDI_Exit;
  977. if (!(hbmMask = CreateMonoBitmap(dx, dy)))
  978. goto CDI_Exit;
  979. //
  980. // Mirror the memory DC so that the transition from
  981. // mirrored(memDC)->non-mirrored(imagelist DCs)->mirrored(screenDC)
  982. // is consistent. [samera]
  983. //
  984. if (bMirroredWnd) {
  985. SET_DC_RTL_MIRRORED(hdcMem);
  986. }
  987. // prepare for drawing the item
  988. if (pTree->hFont)
  989. SelectObject(hdcMem, pTree->hFont);
  990. SetBkMode(hdcMem, TRANSPARENT);
  991. /*
  992. ** draw the text to both bitmaps
  993. */
  994. hbmOld = SelectObject(hdcMem, hbmImage);
  995. // fill image with black for transparency
  996. PatBlt(hdcMem, 0, 0, dx, dy, BLACKNESS);
  997. TV_DrawItem(pTree, hItem, hdcMem, 0, 0,
  998. TVDI_NOIMAGE | TVDI_NOTREE | TVDI_TRANSTEXT);
  999. //
  1000. // If the header is RTL mirrored, then
  1001. // mirror the Memory DC, so that when copying back
  1002. // we don't get any image-flipping. [samera]
  1003. //
  1004. if (bMirroredWnd)
  1005. MirrorBitmapInDC(hdcMem, hbmImage);
  1006. SelectObject(hdcMem, hbmMask);
  1007. // fill mask with white for transparency
  1008. PatBlt(hdcMem, 0, 0, dx, dy, WHITENESS);
  1009. TV_DrawItem(pTree, hItem, hdcMem, 0, 0,
  1010. TVDI_NOIMAGE | TVDI_NOTREE | TVDI_TRANSTEXT);
  1011. //
  1012. // If the header is RTL mirrored, then
  1013. // mirror the Memory DC, so that when copying back
  1014. // we don't get any image-flipping. [samera]
  1015. //
  1016. if (bMirroredWnd)
  1017. MirrorBitmapInDC(hdcMem, hbmMask);
  1018. // unselect objects that we used
  1019. SelectObject(hdcMem, hbmOld);
  1020. SelectObject(hdcMem, g_hfontSystem);
  1021. /*
  1022. ** make an image list that for now only has the text
  1023. */
  1024. //
  1025. // BUGBUG: To fix a pri-1 M7 bug, we create a shared image list.
  1026. //
  1027. if (!(himl = ImageList_Create(dx, dy, ILC_MASK, 1, 0)))
  1028. goto CDI_Exit;
  1029. ImageList_SetBkColor(himl, CLR_NONE);
  1030. ImageList_Add(himl, hbmImage, hbmMask);
  1031. /*
  1032. ** make a dithered copy of the image part onto our bitmaps
  1033. ** (need both bitmap and mask to be dithered)
  1034. */
  1035. TV_GetItem(pTree, hItem, TVIF_IMAGE, &ti);
  1036. iSrc = ti.iImage;
  1037. ImageList_CopyDitherImage(himl, 0, 0, (pTree->cyItem - pTree->cyImage) / 2,
  1038. pTree->hImageList, iSrc, ((pTree->ci.dwExStyle & dwExStyleRTLMirrorWnd) ? ILD_MIRROR : 0L) | (hItem->state & TVIS_OVERLAYMASK));
  1039. CDI_Exit:
  1040. if (hdcMem)
  1041. DeleteObject(hdcMem);
  1042. if (hbmImage)
  1043. DeleteObject(hbmImage);
  1044. if (hbmMask)
  1045. DeleteObject(hbmMask);
  1046. return himl;
  1047. }
  1048. #define COLORKEY RGB(0xF4, 0x0, 0x0)
  1049. LRESULT TV_GenerateDragImage(PTREE pTree, SHDRAGIMAGE* pshdi)
  1050. {
  1051. LRESULT lRet = 0;
  1052. HBITMAP hbmpOld = NULL;
  1053. HTREEITEM hItem = pTree->htiDrag;
  1054. RECT rc;
  1055. HDC hdcDragImage;
  1056. if (hItem == NULL)
  1057. return FALSE;
  1058. hdcDragImage = CreateCompatibleDC(NULL);
  1059. if (!hdcDragImage)
  1060. return 0;
  1061. // After this rc contains the bounds of all the items in Client Coordinates.
  1062. //
  1063. // Mirror the the DC, if the listview is mirrored.
  1064. //
  1065. if (pTree->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  1066. {
  1067. SET_DC_RTL_MIRRORED(hdcDragImage);
  1068. }
  1069. TV_GetItemRect(pTree, hItem, &rc, TRUE);
  1070. // Subtract off the image...
  1071. rc.left -= pTree->cxImage;
  1072. pshdi->sizeDragImage.cx = RECTWIDTH(rc);
  1073. pshdi->sizeDragImage.cy = RECTHEIGHT(rc);
  1074. pshdi->hbmpDragImage = CreateBitmap( pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy,
  1075. GetDeviceCaps(hdcDragImage, PLANES), GetDeviceCaps(hdcDragImage, BITSPIXEL),
  1076. NULL);
  1077. if (pshdi->hbmpDragImage)
  1078. {
  1079. COLORREF clrBkSave;
  1080. RECT rcImage = {0, 0, pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy};
  1081. hbmpOld = SelectObject(hdcDragImage, pshdi->hbmpDragImage);
  1082. pshdi->crColorKey = COLORKEY;
  1083. FillRectClr(hdcDragImage, &rcImage, pshdi->crColorKey);
  1084. // Calculate the offset... The cursor should be in the bitmap rect.
  1085. if (pTree->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  1086. pshdi->ptOffset.x = rc.right - pTree->ptCapture.x;
  1087. else
  1088. pshdi->ptOffset.x = pTree->ptCapture.x - rc.left;
  1089. pshdi->ptOffset.y = pTree->ptCapture.y - rc.top;
  1090. clrBkSave = pTree->clrBk;
  1091. pTree->clrBk = COLORKEY;
  1092. TV_DrawItem(pTree, hItem, hdcDragImage, 0, 0,
  1093. TVDI_NOTREE | TVDI_TRANSTEXT | TVDI_FORCEIMAGE | TVDI_NOBK);
  1094. pTree->clrBk = clrBkSave;
  1095. SelectObject(hdcDragImage, hbmpOld);
  1096. DeleteDC(hdcDragImage);
  1097. // We're passing back the created HBMP.
  1098. return 1;
  1099. }
  1100. return lRet;
  1101. }