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.

1302 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. }
  270. else
  271. {
  272. ASSERT(hItem->lpstr);
  273. // we could do this but this is dangerous (when responding
  274. // to TVM_GETITEM we would be giving the app a pointer to our data)
  275. // lpItem->pszText = hItem->lpstr;
  276. StringCchCopy(lpItem->pszText, lpItem->cchTextMax, hItem->lpstr);
  277. }
  278. }
  279. if (mask & TVIF_IMAGE) {
  280. if (hItem->iImage == (WORD)I_IMAGECALLBACK)
  281. nm.item.mask |= TVIF_IMAGE;
  282. else
  283. lpItem->iImage = hItem->iImage;
  284. }
  285. if (mask & TVIF_SELECTEDIMAGE) {
  286. if (hItem->iSelectedImage == (WORD)I_IMAGECALLBACK)
  287. nm.item.mask |= TVIF_SELECTEDIMAGE;
  288. else
  289. lpItem->iSelectedImage = hItem->iSelectedImage;
  290. }
  291. if (mask & TVIF_INTEGRAL) {
  292. lpItem->iIntegral = hItem->iIntegral;
  293. }
  294. if (mask & TVIF_CHILDREN) {
  295. switch (hItem->fKids) {
  296. case KIDS_COMPUTE:
  297. lpItem->cChildren = hItem->hKids ? 1 : 0;// the actual count doesn't matter
  298. break;
  299. case KIDS_FORCE_YES:
  300. lpItem->cChildren = 1;// the actual count doesn't matter
  301. break;
  302. case KIDS_FORCE_NO:
  303. lpItem->cChildren = 0;
  304. break;
  305. case KIDS_CALLBACK:
  306. nm.item.mask |= TVIF_CHILDREN;
  307. break;
  308. }
  309. }
  310. // copy out constant parameters (and prepare for callback)
  311. // IE4 and IE5.0 did this unconditionally
  312. lpItem->state = nm.item.state = hItem->state;
  313. //
  314. // NOTICE! We do not set TVIF_STATE nm.item.mask and we do not
  315. // check for TVIF_STATE in the "any items need to be filled in
  316. // by callback?" test a few lines below. This is necessary for
  317. // backwards compat. IE5 and earlier did not call the app back
  318. // if the only thing you asked for was TVIF_STATE. You can't
  319. // change this behavior unless you guard it with a version check, or
  320. // apps will break. (They'll get callbacks when they didn't used to.)
  321. // Besides, nobody knows that they can customize the state, so it's
  322. // not like we're missing out on anything.
  323. //
  324. #ifdef DEBUG_TEST_BOLD
  325. if ((((int)hItem) / 100) % 2)
  326. lpItem->state |= TVIS_BOLD;
  327. if (!pTree->hFontBold)
  328. TV_CreateBoldFont(pTree);
  329. #endif
  330. lpItem->lParam = nm.item.lParam = hItem->lParam;
  331. // any items need to be filled in by callback?
  332. if (nm.item.mask & (TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN)) {
  333. nm.item.hItem = hItem;
  334. CCSendNotify(&pTree->ci, TVN_GETDISPINFO, &nm.hdr);
  335. // copy out things that may have been filled in on the callback
  336. if (nm.item.mask & TVIF_CHILDREN)
  337. lpItem->cChildren = nm.item.cChildren;
  338. if (nm.item.mask & TVIF_IMAGE)
  339. lpItem->iImage = nm.item.iImage;
  340. if (nm.item.mask & TVIF_SELECTEDIMAGE)
  341. lpItem->iSelectedImage = nm.item.iSelectedImage;
  342. // callback may have redirected pszText to point into its own buffer
  343. if (nm.item.mask & TVIF_TEXT)
  344. {
  345. if (mask & TVIF_TEXT) // did the *original* mask specify TVIF_TEXT?
  346. lpItem->pszText = CCReturnDispInfoText(nm.item.pszText, lpItem->pszText, lpItem->cchTextMax);
  347. else
  348. lpItem->pszText = nm.item.pszText; // do what we used to do
  349. }
  350. if (nm.item.mask & TVIF_STATE) {
  351. lpItem->state = (nm.item.state & nm.item.stateMask) | (lpItem->state & ~nm.item.stateMask);
  352. if ((lpItem->state & TVIS_BOLD) && !pTree->hFontBold)
  353. TV_CreateBoldFont(pTree);
  354. }
  355. if (nm.item.mask & TVIF_DI_SETITEM) {
  356. if (nm.item.mask & TVIF_TEXT)
  357. if (nm.item.pszText) {
  358. ASSERT(hItem->lpstr == LPSTR_TEXTCALLBACK);
  359. Str_Set(&hItem->lpstr, nm.item.pszText);
  360. }
  361. if (nm.item.mask & TVIF_STATE) {
  362. // if the bold bit changed, then the width changed
  363. if ((hItem->state ^ lpItem->state) & TVIS_BOLD)
  364. hItem->iWidth = 0;
  365. hItem->state = (WORD) lpItem->state;
  366. }
  367. if (nm.item.mask & TVIF_IMAGE)
  368. hItem->iImage = (WORD) lpItem->iImage;
  369. if (nm.item.mask & TVIF_SELECTEDIMAGE)
  370. hItem->iSelectedImage = (WORD) lpItem->iSelectedImage;
  371. if (nm.item.mask & TVIF_CHILDREN) {
  372. switch(nm.item.cChildren) {
  373. case I_CHILDRENCALLBACK:
  374. hItem->fKids = KIDS_CALLBACK;
  375. break;
  376. case 0:
  377. hItem->fKids = KIDS_FORCE_NO;
  378. break;
  379. default:
  380. hItem->fKids = KIDS_FORCE_YES;
  381. break;
  382. }
  383. }
  384. }
  385. }
  386. }
  387. // ----------------------------------------------------------------------------
  388. //
  389. // Draws the given item starting at the given (x,y) and extending down and to
  390. // the right.
  391. //
  392. // ----------------------------------------------------------------------------
  393. BOOL NEAR TV_ShouldItemDrawBlue(PTREE pTree, TVITEMEX *ti, UINT flags)
  394. {
  395. return ( (ti->state & TVIS_DROPHILITED) ||
  396. (!pTree->hDropTarget &&
  397. !(flags & TVDI_GRAYCTL) &&
  398. (ti->state & TVIS_SELECTED) &&
  399. pTree->fFocus));
  400. }
  401. #define TV_ShouldItemDrawDisabled(pTree, pti, flags) (flags & TVDI_GRAYCTL)
  402. //
  403. // Caution: Depending on the user's color scheme, a Gray item may
  404. // end up looking Blue if Gray would otherwise be invisible. So make
  405. // sure that there are other cues that the user can use to tell whether
  406. // the item is "Really Blue" or "Gray masquerading as Blue".
  407. //
  408. // For example, you might get both is if the treeview is
  409. // participating in drag/drop while it is not the active window,
  410. // because the selected item gets "Gray masquerading as Blue" and
  411. // the drop target gets "Really Blue". But we special-case that
  412. // and turn off the selection while we are worrying about drag/drop,
  413. // so there is no confusion after all.
  414. //
  415. BOOL TV_ShouldItemDrawGray(PTREE pTree, TVITEMEX *pti, UINT flags)
  416. {
  417. return ((flags & TVDI_GRAYCTL) ||
  418. (!pTree->hDropTarget &&
  419. ((pti->state & TVIS_SELECTED) &&
  420. (!pTree->fFocus && (pTree->ci.style & TVS_SHOWSELALWAYS)) )));
  421. }
  422. //
  423. // Draw a descender line for the item. It is the caller's job to
  424. // draw the appropriate glyph at level 0.
  425. //
  426. void
  427. TV_DrawDescender(PTREE pTree, HDC hdc, int x, int y, HTREEITEM hItem)
  428. {
  429. int i;
  430. for (i = 1; i < hItem->iIntegral; i++)
  431. BitBlt(hdc, x, y + i * pTree->cyItem, pTree->cxIndent, pTree->cyItem, pTree->hdcBits, 0, 0, SRCCOPY);
  432. }
  433. //
  434. // Erase any previous descender line for the item.
  435. //
  436. void
  437. TV_EraseDescender(PTREE pTree, HDC hdc, int x, int y, HTREEITEM hItem)
  438. {
  439. RECT rc;
  440. rc.left = x;
  441. rc.right = x + pTree->cxIndent;
  442. rc.top = y + pTree->cyItem;
  443. rc.bottom = y + hItem->iIntegral * pTree->cyItem;
  444. FillRect(hdc, &rc, pTree->hbrBk);
  445. }
  446. //
  447. // Draw (or erase) descenders for siblings and children.
  448. //
  449. void TV_DrawKinDescender(PTREE pTree, HDC hdc, int x, int y, HTREEITEM hItem, UINT state)
  450. {
  451. if (hItem->hNext) // Connect to next sibling
  452. TV_DrawDescender(pTree, hdc, x, y, hItem);
  453. else
  454. TV_EraseDescender(pTree, hdc, x, y, hItem);
  455. // If any bonus images, then need to connect the image to the kids.
  456. if (pTree->himlState || pTree->hImageList) {
  457. if (state & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) // Connect to expanded kids
  458. TV_DrawDescender(pTree, hdc, x + pTree->cxIndent, y, hItem);
  459. else
  460. TV_EraseDescender(pTree, hdc, x + pTree->cxIndent, y, hItem);
  461. }
  462. }
  463. void NEAR TV_DrawItem(PTREE pTree, HTREEITEM hItem, HDC hdc, int x, int y, UINT flags)
  464. {
  465. UINT cxIndent = pTree->cxIndent;
  466. COLORREF rgbOldBack = 0, rgbOldText;
  467. COLORREF clrBk = CLR_DEFAULT;
  468. RECT rc;
  469. int iBack, iText;
  470. HTREEITEM hItemSave = hItem;
  471. LPTSTR lpstr;
  472. int cch;
  473. UINT etoFlags = ETO_OPAQUE | ETO_CLIPPED;
  474. TVITEMEX ti;
  475. TCHAR szTemp[MAX_PATH];
  476. int iState = 0;
  477. HFONT hFont; //$BOLD
  478. DWORD dwRet;
  479. NMTVCUSTOMDRAW nmcd;
  480. BOOL fItemFocused = ((pTree->fFocus) && (hItem == pTree->hCaret));
  481. DWORD clrTextTemp, clrTextBkTemp;
  482. BOOL fSelectedIcon = FALSE;
  483. rc.top = y;
  484. rc.bottom = rc.top + (pTree->cyItem * hItem->iIntegral);
  485. rc.left = 0;
  486. rc.right = pTree->cxWnd;
  487. if (flags & TVDI_ERASE) {
  488. // Opaque the whole item
  489. FillRect(hdc, &rc, pTree->hbrBk);
  490. }
  491. // make sure the callbacks don't invalidate this item
  492. pTree->hItemPainting = hItem;
  493. ti.pszText = szTemp;
  494. ti.cchTextMax = ARRAYSIZE(szTemp);
  495. ti.stateMask = TVIS_OVERLAYMASK | TVIS_CUT | TVIS_BOLD; //$BOLD
  496. TV_GetItem(pTree, hItem, TVIF_IMAGE | TVIF_STATE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_CHILDREN | TVIF_PARAM, &ti);
  497. pTree->hItemPainting = NULL;
  498. ////////////////
  499. // set up the HDC
  500. if (TV_ShouldItemDrawBlue(pTree,&ti,flags)) {
  501. // selected
  502. iBack = COLOR_HIGHLIGHT;
  503. iText = COLOR_HIGHLIGHTTEXT;
  504. } else if (TV_ShouldItemDrawDisabled(pTree, &pti, flags)) {
  505. iBack = COLOR_3DFACE;
  506. iText = COLOR_GRAYTEXT;
  507. } else if (TV_ShouldItemDrawGray(pTree, &ti, flags)) {
  508. // On some color schemes, the BTNFACE color equals the WINDOW color,
  509. // and our gray comes out invisible. In such case, change from gray
  510. // to blue so you can see it at all.
  511. if (GetSysColor(COLOR_WINDOW) != GetSysColor(COLOR_BTNFACE))
  512. {
  513. iBack = COLOR_BTNFACE;
  514. iText = COLOR_BTNTEXT;
  515. }
  516. else
  517. {
  518. iBack = COLOR_HIGHLIGHT;
  519. iText = COLOR_HIGHLIGHTTEXT;
  520. }
  521. } else {
  522. // not selected
  523. iBack = COLOR_WINDOW;
  524. iText = COLOR_WINDOWTEXT;
  525. if (hItem == pTree->hHot) {
  526. iText = COLOR_HOTLIGHT;
  527. }
  528. }
  529. if (iBack == COLOR_WINDOW && (pTree->clrBk != (COLORREF)-1))
  530. nmcd.clrTextBk = clrTextBkTemp = pTree->clrBk;
  531. else
  532. nmcd.clrTextBk = clrTextBkTemp = GetSysColor(iBack);
  533. if (iText == COLOR_WINDOWTEXT && (pTree->clrText != (COLORREF)-1))
  534. nmcd.clrText = clrTextTemp = pTree->clrText;
  535. else
  536. nmcd.clrText = clrTextTemp = GetSysColor(iText);
  537. // if forcing black and transparent, do so. dc's BkMode should
  538. // already be set to TRANSPARENT by caller
  539. if (flags & TVDI_TRANSTEXT)
  540. {
  541. nmcd.clrText = clrTextTemp = 0x000000;
  542. etoFlags = 0; // don't opaque nothin'
  543. }
  544. rgbOldBack = SetBkColor(hdc, nmcd.clrTextBk);
  545. rgbOldText = SetTextColor(hdc, nmcd.clrText);
  546. if (pTree->ci.style & TVS_RTLREADING)
  547. {
  548. etoFlags |= ETO_RTLREADING;
  549. }
  550. // Figure out which font to use.
  551. if (ti.state & TVIS_BOLD) {
  552. hFont = pTree->hFontBold;
  553. if (hItem == pTree->hHot) {
  554. hFont = CCGetHotFont(pTree->hFontBold, &pTree->hFontBoldHot);
  555. }
  556. } else {
  557. hFont = pTree->hFont;
  558. if (hItem == pTree->hHot) {
  559. hFont = CCGetHotFont(pTree->hFont, &pTree->hFontHot);
  560. }
  561. }
  562. hFont = SelectObject(hdc, hFont);
  563. // End HDC setup
  564. ////////////////
  565. // notify on custom draw then do it!
  566. nmcd.nmcd.hdc = hdc;
  567. nmcd.nmcd.dwItemSpec = (DWORD_PTR)hItem;
  568. nmcd.nmcd.uItemState = 0;
  569. nmcd.nmcd.rc = rc;
  570. if (flags & TVDI_NOTREE)
  571. nmcd.iLevel = 0;
  572. else
  573. nmcd.iLevel = hItem->iLevel;
  574. if (ti.state & TVIS_SELECTED) {
  575. fSelectedIcon = TRUE;
  576. if (pTree->fFocus || (pTree->ci.style & TVS_SHOWSELALWAYS))
  577. nmcd.nmcd.uItemState |= CDIS_SELECTED;
  578. }
  579. if (fItemFocused)
  580. nmcd.nmcd.uItemState |= CDIS_FOCUS;
  581. if (hItem == pTree->hHot)
  582. nmcd.nmcd.uItemState |= CDIS_HOT;
  583. nmcd.nmcd.lItemlParam = ti.lParam;
  584. dwRet = CICustomDrawNotify(&pTree->ci, CDDS_ITEMPREPAINT, &nmcd.nmcd);
  585. if (dwRet & CDRF_SKIPDEFAULT)
  586. return;
  587. fItemFocused = (nmcd.nmcd.uItemState & CDIS_FOCUS);
  588. if (nmcd.nmcd.uItemState & CDIS_SELECTED)
  589. ti.state |= TVIS_SELECTED;
  590. else {
  591. ti.state &= ~TVIS_SELECTED;
  592. }
  593. if (nmcd.clrTextBk != clrTextBkTemp)
  594. SetBkColor(hdc, nmcd.clrTextBk);
  595. if (nmcd.clrText != clrTextTemp)
  596. SetTextColor(hdc, nmcd.clrText);
  597. if (pTree->ci.style & TVS_FULLROWSELECT &&
  598. !(flags & TVDI_TRANSTEXT))
  599. {
  600. FillRectClr(hdc, &nmcd.nmcd.rc, GetBkColor(hdc));
  601. etoFlags |= ETO_OPAQUE;
  602. clrBk = CLR_NONE;
  603. }
  604. if (!(flags & TVDI_NOTREE)) {
  605. if ((pTree->ci.style & (TVS_HASLINES | TVS_HASBUTTONS)) &&
  606. (pTree->ci.style & TVS_LINESATROOT))
  607. // Make room for the "plus" at the front of the tree
  608. x += cxIndent;
  609. }
  610. // deal with margin, etc.
  611. x += (pTree->cxBorder + (nmcd.iLevel * cxIndent));
  612. y += pTree->cyBorder;
  613. // draw image
  614. if ((!(flags & TVDI_NOTREE) && !(dwRet & TVCDRF_NOIMAGES)) || (flags & TVDI_FORCEIMAGE))
  615. {
  616. int dx, dy; // to clip the images within the borders.
  617. COLORREF clrImage = CLR_HILIGHT;
  618. COLORREF clrBkImage = clrBk;
  619. if (flags & TVDI_NOBK)
  620. {
  621. clrBkImage = CLR_NONE;
  622. }
  623. if (pTree->himlState) {
  624. iState = TV_StateIndex(&ti);
  625. // go figure. in the treeview, 0 for the state image index
  626. // means draw nothing... the 0th item is unused.
  627. // the listview is 0 based and uses the 0th item.
  628. if (iState) {
  629. dx = min(pTree->cxState, pTree->cxMax - pTree->cxBorder - x);
  630. dy = min(pTree->cyState, pTree->cyItem - (2 * pTree->cyBorder));
  631. ImageList_DrawEx(pTree->himlState, iState, hdc, x,
  632. y + max(pTree->cyItem - pTree->cyState, 0), dx, dy, clrBk, CLR_DEFAULT, ILD_NORMAL);
  633. x += pTree->cxState;
  634. }
  635. }
  636. if (pTree->hImageList) {
  637. UINT fStyle = 0;
  638. int i = (fSelectedIcon) ? ti.iSelectedImage : ti.iImage;
  639. if (ti.state & TVIS_CUT) {
  640. fStyle |= ILD_BLEND50;
  641. clrImage = ImageList_GetBkColor(pTree->hImageList);
  642. }
  643. dx = min(pTree->cxImage - MAGIC_INDENT, pTree->cxMax - pTree->cxBorder - x);
  644. dy = min(pTree->cyImage, pTree->cyItem - (2 * pTree->cyBorder));
  645. ImageList_DrawEx(pTree->hImageList, i, hdc,
  646. x, y + (max(pTree->cyItem - pTree->cyImage, 0) / 2), dx, dy,
  647. clrBkImage, clrImage,
  648. fStyle | (ti.state & TVIS_OVERLAYMASK));
  649. }
  650. }
  651. if (pTree->hImageList) {
  652. // even if not drawing image, draw text in right place
  653. x += pTree->cxImage;
  654. }
  655. // draw text
  656. lpstr = ti.pszText;
  657. cch = lstrlen(lpstr);
  658. if (!hItem->iWidth || (hItem->lpstr == LPSTR_TEXTCALLBACK))
  659. {
  660. TV_ComputeItemWidth(pTree, hItem, hdc); //$BOLD
  661. }
  662. rc.left = x;
  663. rc.top = y + pTree->cyBorder;
  664. rc.right = min((x + hItem->iWidth),
  665. (pTree->cxMax - pTree->cxBorder));
  666. rc.bottom-= pTree->cyBorder;
  667. // Draw the text, unless it's the one we are editing
  668. if (pTree->htiEdit != hItem || !IsWindow(pTree->hwndEdit) || !IsWindowVisible(pTree->hwndEdit))
  669. {
  670. ExtTextOut(hdc, x + g_cxLabelMargin, y + ((pTree->cyItem - pTree->cyText) / 2) + g_cyBorder,
  671. etoFlags, &rc, lpstr, cch, NULL);
  672. // Draw the focus rect, if appropriate.
  673. if (pTree->fFocus && (fItemFocused) &&
  674. !(pTree->ci.style & TVS_FULLROWSELECT) &&
  675. !(flags & (TVDI_TRANSTEXT | TVDI_GRAYCTL))
  676. && !(CCGetUIState(&(pTree->ci)) & UISF_HIDEFOCUS)
  677. )
  678. DrawFocusRect(hdc, &rc);
  679. }
  680. SetBkColor(hdc, rgbOldBack);
  681. SetTextColor(hdc, rgbOldText);
  682. // Restore the original font. //$BOLD
  683. SelectObject(hdc, hFont); //$BOLD
  684. // Notice that we should have opaque'd the rest of the line above if no tree
  685. if (!(flags & TVDI_NOTREE))
  686. {
  687. int dx, dy;
  688. if (pTree->hImageList)
  689. x -= pTree->cxImage;
  690. if (iState)
  691. x -= pTree->cxState;
  692. if (pTree->ci.style & TVS_HASLINES)
  693. {
  694. int i;
  695. x -= cxIndent;
  696. if (nmcd.iLevel-- || (pTree->ci.style & TVS_LINESATROOT))
  697. {
  698. // HACK: Special case the first root
  699. // We will draw a "last" sibling button upside down
  700. if (nmcd.iLevel == -1 && hItem == hItem->hParent->hKids)
  701. {
  702. if (hItem->hNext) {
  703. i = 2; // "L"
  704. if (ti.cChildren && (pTree->ci.style & TVS_HASBUTTONS))
  705. {
  706. i += 2; // "L+"
  707. if ((ti.state & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == TVIS_EXPANDED)
  708. i += 2; // "L-"
  709. }
  710. dx = min((int)cxIndent, pTree->cxMax - pTree->cxBorder - x);
  711. dy = pTree->cyItem - (2 * pTree->cyBorder);
  712. StretchBlt(hdc, x, y + pTree->cyItem, cxIndent, -pTree->cyItem, pTree->hdcBits
  713. , i * cxIndent, 0, dx, dy, SRCCOPY);
  714. i = -1;
  715. }
  716. else
  717. {
  718. // first root no siblings
  719. // if there's no other item, draw just the button if button mode,
  720. if (pTree->ci.style & TVS_HASBUTTONS)
  721. {
  722. if (ti.cChildren) {
  723. // hasbuttons, has lines, lines at root
  724. i = ((ti.state & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == TVIS_EXPANDED) ?
  725. 9 : 8; // ".--" : ".-+"
  726. } else {
  727. i = 7; // ".-"
  728. }
  729. }
  730. else
  731. {
  732. i = 3; // ".-"
  733. }
  734. }
  735. }
  736. else
  737. {
  738. i = (hItem->hNext) ? 1 : 2; // "|-" (rep) : "L"
  739. if (ti.cChildren && (pTree->ci.style & TVS_HASBUTTONS))
  740. {
  741. i += 2; // "|-+" (rep) : "L+"
  742. if ((ti.state & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == TVIS_EXPANDED)
  743. i += 2; // "|--" (rep) : "L-"
  744. }
  745. }
  746. if (hItem->iIntegral > 1)
  747. TV_DrawKinDescender(pTree, hdc, x, y, hItem, ti.state);
  748. if (i != -1)
  749. {
  750. dx = min((int)cxIndent, pTree->cxMax - pTree->cxBorder - x);
  751. dy = pTree->cyItem - (2 * pTree->cyBorder);
  752. if ((dx > 0) && (dy > 0))
  753. BitBlt(hdc, x, y, dx, dy, pTree->hdcBits
  754. , i * cxIndent, 0, SRCCOPY);
  755. }
  756. while ((--nmcd.iLevel >= 0) || ((pTree->ci.style & TVS_LINESATROOT) && nmcd.iLevel >= -1))
  757. {
  758. hItem = hItem->hParent;
  759. x -= cxIndent;
  760. if (hItem->hNext)
  761. {
  762. dx = min((int)cxIndent, (pTree->cxMax - pTree->cxBorder - x));
  763. dy = min(pTree->cyItem, pTree->cyWnd - pTree->cyBorder - y);
  764. if ((dx > 0) && (dy > 0))
  765. BitBlt(hdc, x, y, dx, dy, pTree->hdcBits, 0, 0, SRCCOPY);
  766. TV_DrawDescender(pTree, hdc, x, y, hItemSave);
  767. }
  768. }
  769. }
  770. }
  771. else
  772. { // no lines
  773. if ((pTree->ci.style & TVS_HASBUTTONS) && (nmcd.iLevel || pTree->ci.style & TVS_LINESATROOT)
  774. && ti.cChildren)
  775. {
  776. int i = (ti.state & TVIS_EXPANDED) ? cxIndent : 0;
  777. x -= cxIndent;
  778. dx = min((int)cxIndent, pTree->cxMax - pTree->cxBorder - x);
  779. dy = min(pTree->cyItem, pTree->cyWnd - pTree->cyBorder - y);
  780. if ((dx > 0) && (dy > 0))
  781. BitBlt(hdc, x, y, dx, dy, pTree->hdcBits, i, 0, SRCCOPY);
  782. }
  783. }
  784. }
  785. if (dwRet & CDRF_NOTIFYPOSTPAINT) {
  786. nmcd.nmcd.dwItemSpec = (DWORD_PTR)hItemSave;
  787. CICustomDrawNotify(&pTree->ci, CDDS_ITEMPOSTPAINT, &nmcd.nmcd);
  788. }
  789. }
  790. #define INSERTMARKSIZE 6
  791. BOOL TV_GetInsertMarkRect(PTREE pTree, LPRECT prc)
  792. {
  793. ASSERT(pTree);
  794. if(pTree->htiInsert && TV_GetItemRect(pTree, pTree->htiInsert, prc, TRUE))
  795. {
  796. if (pTree->fInsertAfter)
  797. prc->top = prc->bottom;
  798. else
  799. prc->bottom = prc->top;
  800. prc->top -= INSERTMARKSIZE/2;
  801. prc->bottom += INSERTMARKSIZE/2 + 1;
  802. prc->right = pTree->cxWnd - INSERTMARKSIZE; // should always go all the way to right with pad.
  803. prc->left -= pTree->cxImage;
  804. return TRUE;
  805. }
  806. return FALSE;
  807. }
  808. // this is implemented in toolbar.c, but we should be able to use
  809. // as well as long as we always set fHorizMode to FALSE
  810. void PASCAL DrawInsertMark(HDC hdc, LPRECT prc, BOOL fHorizMode, COLORREF clr);
  811. __inline COLORREF TV_GetInsertMarkColor(PTREE pTree)
  812. {
  813. if (pTree->clrim == CLR_DEFAULT)
  814. return g_clrWindowText;
  815. else
  816. return pTree->clrim;
  817. }
  818. void NEAR TV_DrawTree(PTREE pTree, HDC hdc, BOOL fErase, LPRECT lprc)
  819. {
  820. int x;
  821. int iStart, iCnt;
  822. UINT uFlags;
  823. RECT rc;
  824. NMCUSTOMDRAW nmcd;
  825. if (!pTree->fRedraw)
  826. return;
  827. if (pTree->ci.style & TVS_CHECKBOXES)
  828. if (!pTree->himlState)
  829. TV_InitCheckBoxes(pTree);
  830. x = -pTree->xPos;
  831. TV_GetBackgroundBrush(pTree, hdc);
  832. rc = *lprc;
  833. iStart = lprc->top / pTree->cyItem;
  834. if (pTree->cItems && pTree->hTop) {
  835. ASSERT(ITEM_VISIBLE(pTree->hTop));
  836. iCnt = pTree->cShowing - pTree->hTop->iShownIndex;
  837. } else {
  838. iCnt = 0; // Nothing to draw
  839. }
  840. nmcd.hdc = hdc;
  841. /// not implemented yet
  842. //if (ptb->ci.hwnd == GetFocus())
  843. //nmcd.uItemState = CDIS_FOCUS;
  844. //else
  845. nmcd.uItemState = 0;
  846. nmcd.lItemlParam = 0;
  847. nmcd.rc = rc;
  848. pTree->ci.dwCustom = CICustomDrawNotify(&pTree->ci, CDDS_PREPAINT, &nmcd);
  849. if (!(pTree->ci.dwCustom & CDRF_SKIPDEFAULT)) {
  850. if (iStart < iCnt)
  851. {
  852. HTREEITEM hItem;
  853. HFONT hOldFont;
  854. RECT rcT;
  855. int y = 0;
  856. for (hItem = pTree->hTop; hItem; ) {
  857. if (iStart > hItem->iIntegral) {
  858. iStart -= hItem->iIntegral;
  859. y += hItem->iIntegral * pTree->cyItem;
  860. hItem = TV_GetNextVisItem(hItem);
  861. } else
  862. break;
  863. }
  864. hOldFont = pTree->hFont ? SelectObject(hdc, pTree->hFont) : NULL;
  865. // TVDI_* for all items
  866. uFlags = (pTree->ci.style & WS_DISABLED) ? TVDI_GRAYCTL : 0;
  867. if (fErase)
  868. uFlags |= TVDI_ERASE;
  869. // loop from the first visible item until either all visible items are
  870. // drawn or there are no more items to draw
  871. for ( ; hItem && y < lprc->bottom; hItem = TV_GetNextVisItem(hItem))
  872. {
  873. TV_DrawItem(pTree, hItem, hdc, x, y, uFlags);
  874. y += pTree->cyItem * hItem->iIntegral;
  875. }
  876. //
  877. // handle drawing the InsertMark next to this item.
  878. //
  879. if(TV_GetInsertMarkRect(pTree, &rcT))
  880. DrawInsertMark(hdc, &rcT, FALSE, TV_GetInsertMarkColor(pTree));
  881. if (hOldFont)
  882. SelectObject(hdc, hOldFont);
  883. rc.top = y;
  884. }
  885. if (fErase)
  886. // Opaque out everything we have not drawn explicitly
  887. FillRect(hdc, &rc, pTree->hbrBk);
  888. // notify parent afterwards if they want us to
  889. if (pTree->ci.dwCustom & CDRF_NOTIFYPOSTPAINT) {
  890. CICustomDrawNotify(&pTree->ci, CDDS_POSTPAINT, &nmcd);
  891. }
  892. }
  893. }
  894. // ----------------------------------------------------------------------------
  895. //
  896. // Set up for paint, call DrawTree, and clean up after paint.
  897. //
  898. // ----------------------------------------------------------------------------
  899. void NEAR TV_Paint(PTREE pTree, HDC hdc)
  900. {
  901. PAINTSTRUCT ps;
  902. if (hdc)
  903. {
  904. // hdc != 0 indicates a subclassed paint -- use the hdc passed in
  905. SetRect(&ps.rcPaint, 0, 0, pTree->cxWnd, pTree->cyWnd);
  906. TV_DrawTree(pTree, hdc, TRUE, &ps.rcPaint);
  907. }
  908. else
  909. {
  910. BeginPaint(pTree->ci.hwnd, &ps);
  911. TV_DrawTree(pTree, ps.hdc, ps.fErase, &ps.rcPaint);
  912. EndPaint(pTree->ci.hwnd, &ps);
  913. }
  914. }
  915. // ----------------------------------------------------------------------------
  916. // Create an imagelist to be used for dragging.
  917. //
  918. // 1) create mask and image bitmap matching the select bounds size
  919. // 2) draw the text to both bitmaps (in black for now)
  920. // 3) create an imagelist with these bitmaps
  921. // 4) make a dithered copy of the image onto the new imagelist
  922. // ----------------------------------------------------------------------------
  923. HIMAGELIST NEAR TV_CreateDragImage(PTREE pTree, HTREEITEM hItem)
  924. {
  925. HDC hdcMem = NULL;
  926. HBITMAP hbmImage = NULL;
  927. HBITMAP hbmMask = NULL;
  928. HBITMAP hbmOld;
  929. HIMAGELIST himl = NULL;
  930. BOOL bMirroredWnd = (pTree->ci.dwExStyle&RTL_MIRRORED_WINDOW);
  931. int dx, dy;
  932. int iSrc;
  933. TVITEMEX ti;
  934. if (!pTree->hImageList)
  935. return NULL;
  936. if (hItem == NULL)
  937. hItem = pTree->htiDrag;
  938. if (hItem == NULL)
  939. return NULL;
  940. // BUGBUG??? we know it's already been drawn, so is iWidth valid???
  941. dx = hItem->iWidth + pTree->cxImage;
  942. dy = pTree->cyItem;
  943. if (!(hdcMem = CreateCompatibleDC(NULL)))
  944. goto CDI_Exit;
  945. if (!(hbmImage = CreateColorBitmap(dx, dy)))
  946. goto CDI_Exit;
  947. if (!(hbmMask = CreateMonoBitmap(dx, dy)))
  948. goto CDI_Exit;
  949. //
  950. // Mirror the memory DC so that the transition from
  951. // mirrored(memDC)->non-mirrored(imagelist DCs)->mirrored(screenDC)
  952. // is consistent. [samera]
  953. //
  954. if (bMirroredWnd) {
  955. SET_DC_RTL_MIRRORED(hdcMem);
  956. }
  957. // prepare for drawing the item
  958. if (pTree->hFont)
  959. SelectObject(hdcMem, pTree->hFont);
  960. SetBkMode(hdcMem, TRANSPARENT);
  961. /*
  962. ** draw the text to both bitmaps
  963. */
  964. hbmOld = SelectObject(hdcMem, hbmImage);
  965. // fill image with black for transparency
  966. PatBlt(hdcMem, 0, 0, dx, dy, BLACKNESS);
  967. TV_DrawItem(pTree, hItem, hdcMem, 0, 0,
  968. TVDI_NOIMAGE | TVDI_NOTREE | TVDI_TRANSTEXT);
  969. //
  970. // If the header is RTL mirrored, then
  971. // mirror the Memory DC, so that when copying back
  972. // we don't get any image-flipping. [samera]
  973. //
  974. if (bMirroredWnd)
  975. MirrorBitmapInDC(hdcMem, hbmImage);
  976. SelectObject(hdcMem, hbmMask);
  977. // fill mask with white for transparency
  978. PatBlt(hdcMem, 0, 0, dx, dy, WHITENESS);
  979. TV_DrawItem(pTree, hItem, hdcMem, 0, 0,
  980. TVDI_NOIMAGE | TVDI_NOTREE | TVDI_TRANSTEXT);
  981. //
  982. // If the header is RTL mirrored, then
  983. // mirror the Memory DC, so that when copying back
  984. // we don't get any image-flipping. [samera]
  985. //
  986. if (bMirroredWnd)
  987. MirrorBitmapInDC(hdcMem, hbmMask);
  988. // unselect objects that we used
  989. SelectObject(hdcMem, hbmOld);
  990. SelectObject(hdcMem, g_hfontSystem);
  991. /*
  992. ** make an image list that for now only has the text
  993. */
  994. //
  995. // BUGBUG: To fix a pri-1 M7 bug, we create a shared image list.
  996. //
  997. if (!(himl = ImageList_Create(dx, dy, ILC_MASK, 1, 0)))
  998. goto CDI_Exit;
  999. ImageList_SetBkColor(himl, CLR_NONE);
  1000. ImageList_Add(himl, hbmImage, hbmMask);
  1001. /*
  1002. ** make a dithered copy of the image part onto our bitmaps
  1003. ** (need both bitmap and mask to be dithered)
  1004. */
  1005. TV_GetItem(pTree, hItem, TVIF_IMAGE, &ti);
  1006. iSrc = ti.iImage;
  1007. ImageList_CopyDitherImage(himl, 0, 0, (pTree->cyItem - pTree->cyImage) / 2,
  1008. pTree->hImageList, iSrc, ((pTree->ci.dwExStyle & dwExStyleRTLMirrorWnd) ? ILD_MIRROR : 0L) | (hItem->state & TVIS_OVERLAYMASK));
  1009. CDI_Exit:
  1010. if (hdcMem)
  1011. DeleteObject(hdcMem);
  1012. if (hbmImage)
  1013. DeleteObject(hbmImage);
  1014. if (hbmMask)
  1015. DeleteObject(hbmMask);
  1016. return himl;
  1017. }
  1018. #define COLORKEY RGB(0xF4, 0x0, 0x0)
  1019. LRESULT TV_GenerateDragImage(PTREE pTree, SHDRAGIMAGE* pshdi)
  1020. {
  1021. LRESULT lRet = 0;
  1022. HBITMAP hbmpOld = NULL;
  1023. HTREEITEM hItem = pTree->htiDrag;
  1024. RECT rc;
  1025. HDC hdcDragImage;
  1026. if (hItem == NULL)
  1027. return FALSE;
  1028. hdcDragImage = CreateCompatibleDC(NULL);
  1029. if (!hdcDragImage)
  1030. return 0;
  1031. // After this rc contains the bounds of all the items in Client Coordinates.
  1032. //
  1033. // Mirror the the DC, if the listview is mirrored.
  1034. //
  1035. if (pTree->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  1036. {
  1037. SET_DC_RTL_MIRRORED(hdcDragImage);
  1038. }
  1039. TV_GetItemRect(pTree, hItem, &rc, TRUE);
  1040. // Subtract off the image...
  1041. rc.left -= pTree->cxImage;
  1042. pshdi->sizeDragImage.cx = RECTWIDTH(rc);
  1043. pshdi->sizeDragImage.cy = RECTHEIGHT(rc);
  1044. pshdi->hbmpDragImage = CreateBitmap( pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy,
  1045. GetDeviceCaps(hdcDragImage, PLANES), GetDeviceCaps(hdcDragImage, BITSPIXEL),
  1046. NULL);
  1047. if (pshdi->hbmpDragImage)
  1048. {
  1049. COLORREF clrBkSave;
  1050. RECT rcImage = {0, 0, pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy};
  1051. hbmpOld = SelectObject(hdcDragImage, pshdi->hbmpDragImage);
  1052. pshdi->crColorKey = COLORKEY;
  1053. FillRectClr(hdcDragImage, &rcImage, pshdi->crColorKey);
  1054. // Calculate the offset... The cursor should be in the bitmap rect.
  1055. if (pTree->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  1056. pshdi->ptOffset.x = rc.right - pTree->ptCapture.x;
  1057. else
  1058. pshdi->ptOffset.x = pTree->ptCapture.x - rc.left;
  1059. pshdi->ptOffset.y = pTree->ptCapture.y - rc.top;
  1060. clrBkSave = pTree->clrBk;
  1061. pTree->clrBk = COLORKEY;
  1062. TV_DrawItem(pTree, hItem, hdcDragImage, 0, 0,
  1063. TVDI_NOTREE | TVDI_TRANSTEXT | TVDI_FORCEIMAGE | TVDI_NOBK);
  1064. pTree->clrBk = clrBkSave;
  1065. SelectObject(hdcDragImage, hbmpOld);
  1066. DeleteDC(hdcDragImage);
  1067. // We're passing back the created HBMP.
  1068. return 1;
  1069. }
  1070. return lRet;
  1071. }