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.

1075 lines
37 KiB

  1. // large icon view stuff
  2. #include "ctlspriv.h"
  3. #include "listview.h"
  4. void ListView_TRecomputeLabelSizeInternal(LV* plv, LISTITEM* pitem, int i, HDC hdc, BOOL fUsepitem);
  5. void ListView_TGetRectsInternal(LV* plv, LISTITEM* pitem, int i, RECT* prcIcon, RECT* prcLabel, LPRECT prcBounds);
  6. void ListView_TGetRectsOwnerDataInternal( LV* plv, int iItem, RECT* prcIcon, RECT* prcLabel, LISTITEM* pitem, BOOL fUsepitem );
  7. #define TILELABELRATIO 20
  8. #define _GetStateCX(plv) \
  9. ((plv->himlState && !ListView_IsSimpleSelect(plv)) ? plv->cxState:0)
  10. #define _GetStateCY(plv) \
  11. (plv->himlState ? plv->cyState:0)
  12. int _CalcDesiredIconHeight(LV* plv)
  13. {
  14. return max(plv->cyIcon, _GetStateCY(plv));
  15. }
  16. // Based on the icon height, and the number of columns showing
  17. int _CalcDesiredTileHeight(LV* plv, LISTITEM* pitem)
  18. {
  19. return 2 * g_cyIconMargin + max (_CalcDesiredIconHeight(plv), pitem->cyFoldedLabel);
  20. }
  21. #define LIGHTENBYTE(percent, x) { x += (255 - x) * percent / 100;}
  22. COLORREF GetBorderSelectColor(int iPercent, COLORREF clr)
  23. {
  24. //BOOL fAllowDesaturation;
  25. BYTE r, g, b;
  26. // Doing this is less expensive than Luminance adjustment
  27. //fAllowDesaturation = FALSE;
  28. r = GetRValue(clr);
  29. g = GetGValue(clr);
  30. b = GetBValue(clr);
  31. // If all colors are above positive saturation, allow a desaturation
  32. /*if (r > 0xF0 && g > 0xF0 && b > 0xF0)
  33. {
  34. fAllowDesaturation = TRUE;
  35. }*/
  36. LIGHTENBYTE(iPercent, r);
  37. LIGHTENBYTE(iPercent, g);
  38. LIGHTENBYTE(iPercent, b);
  39. return RGB(r,g,b);
  40. }
  41. void _InitTileColumnsEnum(PLVTILECOLUMNSENUM plvtce, LV* plv, UINT cColumns, UINT *puColumns, BOOL fOneLessLine)
  42. {
  43. int iSortedColumn = (plv->iLastColSort < plv->cCol) ? plv->iLastColSort : -1;
  44. if (cColumns == I_COLUMNSCALLBACK)
  45. {
  46. // We don't have column information yet.
  47. plvtce->iTotalSpecifiedColumns = 0;
  48. plvtce->iColumnsRemainingMax = 0;
  49. }
  50. else
  51. {
  52. int iSubtract = fOneLessLine ? 1 : 0;
  53. // The total number of columns that we can use in the puColumns array
  54. // (limited not just by cColumns, but also plv->cSubItems)
  55. plvtce->iTotalSpecifiedColumns = min(plv->cSubItems - iSubtract, (int)cColumns);
  56. // The total number of columns that we might use, including the sorted column,
  57. // which may or may not be included in puColumns. This is also limited
  58. // by plv->cSubItems
  59. plvtce->iColumnsRemainingMax = min(plv->cSubItems - iSubtract, (int)cColumns + ((iSortedColumn >= 0) ? 1 : 0));
  60. }
  61. plvtce->puSpecifiedColumns = puColumns; // Array of specified columns
  62. plvtce->iCurrentSpecifiedColumn = 0;
  63. plvtce->iSortedColumn = iSortedColumn; // Sorted column (-1 if none, 0 if name - in these cases we ignore)
  64. plvtce->bUsedSortedColumn = FALSE;
  65. }
  66. /*
  67. * This is just like Str_Set, but for tile columns instead of strings.
  68. * ppuColumns and pcColumns get set to puColumns and cColumns
  69. */
  70. BOOL Tile_Set(UINT **ppuColumns, UINT *pcColumns, UINT *puColumns, UINT cColumns)
  71. {
  72. if ((cColumns == I_COLUMNSCALLBACK) || (cColumns == 0) || (puColumns == NULL))
  73. {
  74. // We're setting the columns to zero, or callback
  75. // If there was already something there, free it.
  76. if ((*pcColumns != I_COLUMNSCALLBACK) && (*pcColumns != 0))
  77. {
  78. if (*ppuColumns)
  79. LocalFree(*ppuColumns);
  80. }
  81. *pcColumns = cColumns;
  82. *ppuColumns = NULL;
  83. }
  84. else
  85. {
  86. // We're providing a bunch of new columns
  87. UINT *puColumnsNew = *ppuColumns;
  88. if ((*pcColumns == I_COLUMNSCALLBACK) || (*pcColumns == 0))
  89. puColumnsNew = NULL; // There's nothing there to realloc.
  90. // Reallocate the block of columns
  91. puColumnsNew = CCLocalReAlloc(puColumnsNew, sizeof(UINT) * cColumns);
  92. if (!puColumnsNew)
  93. return FALSE;
  94. *pcColumns = cColumns;
  95. CopyMemory(puColumnsNew, puColumns, sizeof(UINT) * cColumns);
  96. *ppuColumns = puColumnsNew;
  97. }
  98. return TRUE;
  99. }
  100. BOOL ListView_TDrawItem(PLVDRAWITEM plvdi)
  101. {
  102. RECT rcIcon;
  103. RECT rcLabel;
  104. RECT rcBounds;
  105. RECT rcT;
  106. RECT rcFocus={0};
  107. TCHAR ach[CCHLABELMAX];
  108. LV_ITEM item = {0};
  109. int i = (int) plvdi->nmcd.nmcd.dwItemSpec;
  110. int iStateImageOffset;
  111. LV* plv = plvdi->plv;
  112. LISTITEM* pitem;
  113. LISTITEM litem;
  114. UINT auColumns[CCMAX_TILE_COLUMNS];
  115. COLORREF clrTextBk = plvdi->nmcd.clrTextBk;
  116. if (ListView_IsOwnerData(plv))
  117. {
  118. // moved here to reduce call backs in OWNERDATA case
  119. item.iItem = i;
  120. item.iSubItem = 0;
  121. item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE | LVIF_COLUMNS;
  122. item.stateMask = LVIS_ALL;
  123. item.pszText = ach;
  124. item.cchTextMax = ARRAYSIZE(ach);
  125. item.cColumns = ARRAYSIZE(auColumns);
  126. item.puColumns = auColumns;
  127. ListView_OnGetItem(plv, &item);
  128. litem.pszText = item.pszText;
  129. ListView_TGetRectsOwnerDataInternal(plv, i, &rcIcon, &rcLabel, &litem, TRUE);
  130. UnionRect(&rcBounds, &rcLabel, &rcIcon);
  131. pitem = NULL;
  132. }
  133. else
  134. {
  135. pitem = ListView_GetItemPtr(plv, i);
  136. if (pitem)
  137. {
  138. // NOTE this will do a GetItem LVIF_TEXT iff needed
  139. ListView_TGetRects(plv, pitem, &rcIcon, &rcLabel, &rcBounds);
  140. }
  141. }
  142. if (!plvdi->prcClip || IntersectRect(&rcT, &rcBounds, plvdi->prcClip))
  143. {
  144. UINT fText;
  145. if (!ListView_IsOwnerData(plv))
  146. {
  147. item.iItem = i;
  148. item.iSubItem = 0;
  149. item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE | LVIF_COLUMNS;
  150. item.stateMask = LVIS_ALL;
  151. item.pszText = ach;
  152. item.cchTextMax = ARRAYSIZE(ach);
  153. item.cColumns = ARRAYSIZE(auColumns);
  154. item.puColumns = auColumns;
  155. ListView_OnGetItem(plv, &item);
  156. // Make sure the listview hasn't been altered during
  157. // the callback to get the item info
  158. if (pitem != ListView_GetItemPtr(plv, i))
  159. {
  160. return FALSE;
  161. }
  162. // Call this again. The bounds may have changed - ListView_OnGetItem may have retrieved new
  163. // info via LVN_GETDISPINFO
  164. ListView_TGetRectsInternal(plv, pitem, i, &rcIcon, &rcLabel, &rcBounds);
  165. }
  166. if (plvdi->lpptOrg)
  167. {
  168. OffsetRect(&rcIcon, plvdi->lpptOrg->x - rcBounds.left,
  169. plvdi->lpptOrg->y - rcBounds.top);
  170. OffsetRect(&rcLabel, plvdi->lpptOrg->x - rcBounds.left,
  171. plvdi->lpptOrg->y - rcBounds.top);
  172. OffsetRect(&rcBounds, plvdi->lpptOrg->x - rcBounds.left,
  173. plvdi->lpptOrg->y - rcBounds.top);
  174. }
  175. fText = ListView_GetTextSelectionFlags(plv, &item, plvdi->flags);
  176. plvdi->nmcd.iSubItem = 0;
  177. if (plv->pImgCtx || plv->hbmpWatermark)
  178. {
  179. clrTextBk = CLR_NONE;
  180. }
  181. else
  182. {
  183. if (CLR_NONE != plvdi->nmcd.clrFace)
  184. FillRectClr(plvdi->nmcd.nmcd.hdc, &rcBounds, plvdi->nmcd.clrFace);
  185. }
  186. iStateImageOffset = _GetStateCX(plv);
  187. ListView_DrawImageEx2(plv, &item, plvdi->nmcd.nmcd.hdc,
  188. rcIcon.left + iStateImageOffset + g_cxLabelMargin,
  189. rcIcon.top + (rcIcon.bottom - rcIcon.top - _CalcDesiredIconHeight(plv))/2,
  190. plvdi->nmcd.clrFace,
  191. plvdi->flags, rcLabel.right, plvdi->nmcd.iIconEffect, plvdi->nmcd.iIconPhase);
  192. // Don't draw label if it's being edited...
  193. //
  194. if (plv->iEdit != i)
  195. {
  196. RECT rcLine = rcLabel;
  197. RECT rcDummy;
  198. BOOL fLineWrap;
  199. LISTSUBITEM lsi;
  200. TCHAR szBuffer[CCHLABELMAX];
  201. rcFocus = rcLabel;
  202. // Apply any margins
  203. rcLine.left += plv->rcTileLabelMargin.left;
  204. rcLine.top += plv->rcTileLabelMargin.top;
  205. rcLine.right -= plv->rcTileLabelMargin.right;
  206. rcLine.bottom -= plv->rcTileLabelMargin.bottom;
  207. // Center text lines vertically:
  208. rcLine.top += (rcLine.bottom - rcLine.top - (pitem ? pitem->cyFoldedLabel : litem.cyFoldedLabel))/2;
  209. rcFocus.top = rcLine.top;
  210. // Make sure the text is in szBuffer
  211. if (szBuffer != item.pszText)
  212. lstrcpyn(szBuffer, item.pszText, ARRAYSIZE(szBuffer));
  213. // Now get the bounds of the thing.
  214. lsi.pszText = szBuffer;
  215. lsi.iImage = -1;
  216. lsi.state = 0;
  217. fLineWrap = TCalculateSubItemRect(plv, NULL, &lsi, i, 0, plvdi->nmcd.nmcd.hdc, &rcDummy, NULL);
  218. rcLine.bottom = rcLine.top + lsi.sizeText.cy;// + ((fLineWrap) ? lsi.sizeText.cy : 0);
  219. fText |= SHDT_LEFT | SHDT_CLIPPED | SHDT_NOMARGIN; // Need line wrapping, potentially, so SHDT_DRAWTEXT. Need left alignment. Need to clip to rect.
  220. if (plvdi->flags & LVDI_TRANSTEXT)
  221. fText |= SHDT_TRANSPARENT;
  222. if ((fText & SHDT_SELECTED) && (plvdi->flags & LVDI_HOTSELECTED))
  223. fText |= SHDT_HOTSELECTED;
  224. if (plvdi->dwCustom & LVCDRF_NOSELECT)
  225. {
  226. fText &= ~(SHDT_HOTSELECTED | SHDT_SELECTED);
  227. }
  228. if (item.pszText && (*item.pszText))
  229. {
  230. if(plv->dwExStyle & WS_EX_RTLREADING)
  231. fText |= SHDT_RTLREADING;
  232. SHDrawText(plvdi->nmcd.nmcd.hdc, item.pszText, &rcLine, LVCFMT_LEFT, SHDT_DRAWTEXT | fText,
  233. RECTHEIGHT(rcLine), plv->cxEllipses,
  234. plvdi->nmcd.clrText, clrTextBk);
  235. }
  236. if (plv->cCol > 0)
  237. {
  238. int fItemText = fText;
  239. // Map CLR_DEFAULT to a real colorref before passing to GetSortColor.
  240. COLORREF clrSubItemText = GetSortColor(10,
  241. (plvdi->nmcd.clrText == CLR_DEFAULT) ? g_clrWindowText : plvdi->nmcd.clrText);
  242. int iSubItem;
  243. LVTILECOLUMNSENUM lvtce;
  244. _InitTileColumnsEnum(&lvtce, plv, item.cColumns, item.puColumns, fLineWrap);
  245. while (-1 != (iSubItem = _GetNextColumn(&lvtce)))
  246. {
  247. LVITEM lvi;
  248. lvi.mask = LVIF_TEXT;
  249. lvi.iItem = i;
  250. lvi.iSubItem = iSubItem;
  251. lvi.pszText = szBuffer;
  252. lvi.cchTextMax = ARRAYSIZE(szBuffer);
  253. if (ListView_IsOwnerData( plv ))
  254. lvi.lParam = 0L;
  255. else
  256. lvi.lParam = pitem->lParam;
  257. if (ListView_OnGetItem(plv, &lvi))
  258. {
  259. if (lvi.pszText)
  260. {
  261. // Make sure the text is in szBuffer
  262. if (szBuffer != lvi.pszText)
  263. lstrcpyn(szBuffer, lvi.pszText, ARRAYSIZE(szBuffer));
  264. // Now get the bounds of the thing.
  265. lsi.pszText = szBuffer;
  266. lsi.iImage = -1;
  267. lsi.state = 0;
  268. plvdi->nmcd.clrText = clrSubItemText;
  269. TCalculateSubItemRect(plv, NULL, &lsi, i, iSubItem, plvdi->nmcd.nmcd.hdc, &rcDummy, NULL);
  270. // Now we should have the size of the text.
  271. plvdi->nmcd.iSubItem = iSubItem;
  272. CICustomDrawNotify(&plvdi->plv->ci, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &plvdi->nmcd.nmcd);
  273. if (lsi.pszText != NULL && *lsi.pszText != 0)
  274. {
  275. rcLine.top = rcLine.bottom;
  276. rcLine.bottom = rcLine.top + lsi.sizeText.cy;
  277. SHDrawText(plvdi->nmcd.nmcd.hdc, lsi.pszText, &rcLine, LVCFMT_LEFT, fItemText | SHDT_ELLIPSES,
  278. RECTHEIGHT(rcLine), plv->cxEllipses,
  279. plvdi->nmcd.clrText, clrTextBk);
  280. }
  281. }
  282. }
  283. }
  284. }
  285. rcFocus.bottom = rcLine.bottom;
  286. }
  287. if ((plvdi->flags & LVDI_FOCUS) &&
  288. (item.state & LVIS_FOCUSED) &&
  289. !(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS))
  290. {
  291. rcFocus.top -= g_cyCompensateInternalLeading;
  292. rcFocus.bottom += g_cyCompensateInternalLeading;
  293. DrawFocusRect(plvdi->nmcd.nmcd.hdc, &rcFocus);
  294. }
  295. }
  296. return TRUE;
  297. }
  298. int ListView_TItemHitTest(LV* plv, int x, int y, UINT* pflags, int *piSubItem)
  299. {
  300. int iHit;
  301. UINT flags;
  302. POINT pt;
  303. RECT rcLabel;
  304. RECT rcIcon;
  305. int iStateImageOffset = 0;
  306. if (piSubItem)
  307. *piSubItem = 0;
  308. // Map window-relative coordinates to view-relative coords...
  309. //
  310. pt.x = x + plv->ptOrigin.x;
  311. pt.y = y + plv->ptOrigin.y;
  312. // If there are any uncomputed items, recompute them now.
  313. //
  314. if (plv->rcView.left == RECOMPUTE)
  315. ListView_Recompute(plv);
  316. flags = 0;
  317. if (ListView_IsOwnerData( plv ))
  318. {
  319. int cSlots;
  320. POINT ptWnd;
  321. LISTITEM item;
  322. int iWidth = 0, iHeight = 0;
  323. cSlots = ListView_GetSlotCount( plv, TRUE, &iWidth, &iHeight );
  324. iHit = ListView_CalcHitSlot( plv, pt, cSlots, iWidth, iHeight );
  325. if (iHit < ListView_Count(plv))
  326. {
  327. ListView_TGetRectsOwnerDataInternal( plv, iHit, &rcIcon, &rcLabel, &item, FALSE );
  328. ptWnd.x = x;
  329. ptWnd.y = y;
  330. if (PtInRect(&rcIcon, ptWnd))
  331. {
  332. flags = LVHT_ONITEMICON;
  333. }
  334. else if (PtInRect(&rcLabel, ptWnd))
  335. {
  336. flags = LVHT_ONITEMLABEL;
  337. }
  338. }
  339. }
  340. else
  341. {
  342. iStateImageOffset = _GetStateCX(plv);
  343. for (iHit = 0; (iHit < ListView_Count(plv)); iHit++)
  344. {
  345. LISTITEM* pitem = ListView_FastGetZItemPtr(plv, iHit);
  346. POINT ptItem;
  347. ptItem.x = pitem->pt.x;
  348. ptItem.y = pitem->pt.y;
  349. rcIcon.left = ptItem.x;
  350. rcIcon.right = rcIcon.left + plv->cxIcon + 3 * g_cxLabelMargin + iStateImageOffset;
  351. rcIcon.top = pitem->pt.y;
  352. rcIcon.bottom = rcIcon.top + plv->sizeTile.cy - 2 * g_cyIconMargin;
  353. rcLabel.left = rcIcon.right;
  354. if (pitem->cyUnfoldedLabel != SRECOMPUTE)
  355. {
  356. rcLabel.right = rcLabel.left + pitem->cxSingleLabel;
  357. }
  358. else
  359. {
  360. rcLabel.right = rcLabel.left + plv->sizeTile.cx - RECTWIDTH(rcIcon) - 2 * g_cxLabelMargin;
  361. }
  362. rcLabel.top = rcIcon.top;
  363. rcLabel.bottom = rcIcon.bottom;
  364. // Max out bottoms
  365. rcLabel.bottom = rcIcon.bottom = max(rcIcon.bottom, rcLabel.bottom);
  366. if (PtInRect(&rcIcon, pt))
  367. {
  368. flags = LVHT_ONITEMICON;
  369. }
  370. else if (PtInRect(&rcLabel, pt))
  371. {
  372. flags = LVHT_ONITEMLABEL;
  373. }
  374. if (flags)
  375. break;
  376. }
  377. }
  378. if (flags == 0)
  379. {
  380. flags = LVHT_NOWHERE;
  381. iHit = -1;
  382. }
  383. else
  384. {
  385. if (!ListView_IsOwnerData( plv ))
  386. {
  387. iHit = DPA_GetPtrIndex(plv->hdpa, ListView_FastGetZItemPtr(plv, iHit));
  388. }
  389. }
  390. *pflags = flags;
  391. return iHit;
  392. }
  393. // out:
  394. // prcIcon icon bounds including icon margin area
  395. void ListView_TGetRects(LV* plv, LISTITEM* pitem, RECT* prcIcon, RECT* prcLabel, LPRECT prcBounds)
  396. {
  397. RECT rcIcon;
  398. RECT rcLabel;
  399. int iStateImageOffset = 0;
  400. if (!prcLabel)
  401. prcLabel = &rcLabel;
  402. if (!prcIcon)
  403. prcIcon = &rcIcon;
  404. if (pitem->pt.x == RECOMPUTE)
  405. {
  406. ListView_Recompute(plv);
  407. }
  408. if (pitem->pt.x == RECOMPUTE)
  409. {
  410. RECT rcZero = {0};
  411. *prcIcon = *prcLabel = rcZero;
  412. return;
  413. }
  414. iStateImageOffset = _GetStateCX(plv);
  415. prcIcon->left = pitem->pt.x - plv->ptOrigin.x;
  416. prcIcon->right = prcIcon->left + plv->cxIcon + 3 * g_cxLabelMargin + iStateImageOffset;
  417. prcIcon->top = pitem->pt.y - plv->ptOrigin.y;
  418. prcIcon->bottom = prcIcon->top + plv->sizeTile.cy - 2 * g_cyIconMargin;
  419. prcLabel->left = prcIcon->right;
  420. prcLabel->right = pitem->pt.x - plv->ptOrigin.x + plv->sizeTile.cx - 2 * g_cxLabelMargin; //2 in tile, 1 on right. pitem->pt.x takes care of left margin
  421. prcLabel->top = prcIcon->top;
  422. prcLabel->bottom = prcIcon->bottom;
  423. if (prcBounds)
  424. {
  425. UnionRect(prcBounds, prcLabel, prcIcon);
  426. }
  427. }
  428. void ListView_TGetRectsOwnerData( LV* plv,
  429. int iItem,
  430. RECT* prcIcon,
  431. RECT* prcLabel,
  432. LISTITEM* pitem,
  433. BOOL fUsepitem )
  434. {
  435. int cSlots;
  436. RECT rcIcon;
  437. RECT rcLabel;
  438. int iStateImageOffset = 0;
  439. if (!prcLabel)
  440. prcLabel = &rcLabel;
  441. if (!prcIcon)
  442. prcIcon = &rcIcon;
  443. // calculate x, y from iItem
  444. cSlots = ListView_GetSlotCount( plv, TRUE, NULL, NULL );
  445. pitem->iWorkArea = 0; // OwnerData doesn't support workareas
  446. ListView_SetIconPos( plv, pitem, iItem, cSlots );
  447. // What else can we do?
  448. pitem->cColumns = 0;
  449. pitem->puColumns = NULL;
  450. // End What else can we do?
  451. iStateImageOffset = _GetStateCX(plv);
  452. prcIcon->left = pitem->pt.x - plv->ptOrigin.x;
  453. prcIcon->right = prcIcon->left + plv->cxIcon + 3 * g_cxLabelMargin + iStateImageOffset;
  454. prcIcon->top = pitem->pt.y - plv->ptOrigin.y;
  455. prcIcon->bottom = prcIcon->top + plv->sizeTile.cy - 2 * g_cyIconMargin;
  456. prcLabel->left = prcIcon->right;
  457. prcLabel->right = pitem->pt.x - plv->ptOrigin.x + plv->sizeTile.cx - 2 * g_cxLabelMargin;
  458. prcLabel->top = prcIcon->top;
  459. prcLabel->bottom = prcIcon->bottom;
  460. }
  461. // out:
  462. // prcIcon icon bounds including icon margin area
  463. void ListView_TGetRectsInternal(LV* plv, LISTITEM* pitem, int i, RECT* prcIcon, RECT* prcLabel, LPRECT prcBounds)
  464. {
  465. RECT rcIcon;
  466. RECT rcLabel;
  467. int iStateImageOffset = 0;
  468. if (!prcLabel)
  469. prcLabel = &rcLabel;
  470. if (!prcIcon)
  471. prcIcon = &rcIcon;
  472. if (pitem->pt.x == RECOMPUTE)
  473. {
  474. ListView_Recompute(plv);
  475. }
  476. if (pitem->pt.x == RECOMPUTE)
  477. {
  478. RECT rcZero = {0};
  479. *prcIcon = *prcLabel = rcZero;
  480. return;
  481. }
  482. if (pitem->cyUnfoldedLabel == SRECOMPUTE)
  483. {
  484. ListView_TRecomputeLabelSizeInternal(plv, pitem, i, NULL, FALSE);
  485. }
  486. iStateImageOffset = _GetStateCX(plv);
  487. prcIcon->left = pitem->pt.x - plv->ptOrigin.x;
  488. prcIcon->right = prcIcon->left + plv->cxIcon + 3 * g_cxLabelMargin + iStateImageOffset;
  489. prcIcon->top = pitem->pt.y - plv->ptOrigin.y;
  490. prcIcon->bottom = prcIcon->top + plv->sizeTile.cy - 2 * g_cyIconMargin;
  491. prcLabel->left = prcIcon->right;
  492. if (ListView_FullRowSelect(plv)) // full-row-select means full-tile-select
  493. {
  494. prcLabel->right = pitem->pt.x - plv->ptOrigin.x + plv->sizeTile.cx - 2 * g_cxLabelMargin; //2 in tile, 1 on right. pitem->pt.x takes care of left margin
  495. }
  496. else
  497. {
  498. prcLabel->right = prcLabel->left + pitem->cxSingleLabel;
  499. }
  500. prcLabel->top = prcIcon->top;
  501. prcLabel->bottom = prcIcon->bottom;
  502. if (prcBounds)
  503. {
  504. UnionRect(prcBounds, prcLabel, prcIcon);
  505. }
  506. }
  507. void ListView_TGetRectsOwnerDataInternal( LV* plv,
  508. int iItem,
  509. RECT* prcIcon,
  510. RECT* prcLabel,
  511. LISTITEM* pitem,
  512. BOOL fUsepitem )
  513. {
  514. int cSlots;
  515. RECT rcIcon;
  516. RECT rcLabel;
  517. int iStateImageOffset = 0;
  518. if (!prcLabel)
  519. prcLabel = &rcLabel;
  520. if (!prcIcon)
  521. prcIcon = &rcIcon;
  522. // calculate x, y from iItem
  523. cSlots = ListView_GetSlotCount( plv, TRUE, NULL, NULL );
  524. pitem->iWorkArea = 0; // OwnerData doesn't support workareas
  525. ListView_SetIconPos( plv, pitem, iItem, cSlots );
  526. // What else can we do?
  527. pitem->cColumns = 0;
  528. pitem->puColumns = NULL;
  529. // End What else can we do?
  530. // calculate lable sizes from iItem
  531. ListView_TRecomputeLabelSizeInternal( plv, pitem, iItem, NULL, fUsepitem);
  532. iStateImageOffset = _GetStateCX(plv);
  533. prcIcon->left = pitem->pt.x - plv->ptOrigin.x;
  534. prcIcon->right = prcIcon->left + plv->cxIcon + 3 * g_cxLabelMargin + iStateImageOffset;
  535. prcIcon->top = pitem->pt.y - plv->ptOrigin.y;
  536. prcIcon->bottom = prcIcon->top + plv->sizeTile.cy - 2 * g_cyIconMargin;
  537. prcLabel->left = prcIcon->right;
  538. prcLabel->right = pitem->pt.x - plv->ptOrigin.x + plv->sizeTile.cx - 2 * g_cxLabelMargin;
  539. prcLabel->top = prcIcon->top;
  540. prcLabel->bottom = prcIcon->bottom;
  541. }
  542. // Note: still need to add rcTileLabelMargin to this diagram
  543. //g_cxLabelMargin g_cxLabelMargin g_cxLabelMargin
  544. // __|__ __|___ __|__ __|___ __|__ __|__
  545. //| | | | | | | | |
  546. // *************************************************************************************************
  547. // * * ^ * * * * *
  548. // * * |-- cyIconMargin * * * * *
  549. // * * v * * * * *
  550. // * ******************************** * * * *
  551. // * * * * * * *
  552. // * * Icon * * * * *
  553. // * * Width: plv->cxIcon + * * * * *
  554. // * * plv->cxState * * * * *
  555. // * * * * * * *
  556. // * * Height: max(plv->cyIcon, * * * Space left for label * *
  557. // * * plv->cyState) * * * * *
  558. // * * * * * * *
  559. // * * * * * * *
  560. // * * * * * * *
  561. // * * * * * * *
  562. // * * * * * * *
  563. // * * * * * * *
  564. // * * * * * * *
  565. // * ******************************** * * * *
  566. // * * ^ * * * * *
  567. // * * |-- cyIconMargin * * * * *
  568. // * * v * * * * *
  569. // *************************************************************************************************
  570. //
  571. // The top and bottom margins of the tile are plv->cyIconMargin, the left and right are plv->cxLabelMargin
  572. // (as shown in the diagram)
  573. // Returns TRUE when iSubItem == 0, and the text wraps to a second line. FALSE otherwise.
  574. // When the return value is TRUE, the height returned in pitem->rcTxtRgn/plsi->sizeText is the height of two lines.
  575. BOOL TCalculateSubItemRect(LV* plv, LISTITEM *pitem, LISTSUBITEM* plsi, int i, int iSubItem, HDC hdc, RECT* prc, BOOL *pbUnfolded)
  576. {
  577. TCHAR szLabel[CCHLABELMAX + 4];
  578. RECT rcSubItem = {0};
  579. LVFAKEDRAW lvfd;
  580. LV_ITEM item;
  581. BOOL fLineWrap = FALSE;
  582. int cchLabel;
  583. if (pbUnfolded)
  584. {
  585. *pbUnfolded = TRUE;
  586. }
  587. // the following will use the passed in pitem text instead of calling
  588. // GetItem. This would be two consecutive calls otherwise, in some cases.
  589. //
  590. if (pitem && (pitem->pszText != LPSTR_TEXTCALLBACK))
  591. {
  592. Str_GetPtr0(pitem->pszText, szLabel, ARRAYSIZE(szLabel));
  593. item.lParam = pitem->lParam;
  594. }
  595. else if (plsi && (plsi->pszText != LPSTR_TEXTCALLBACK))
  596. {
  597. Str_GetPtr0(plsi->pszText, szLabel, ARRAYSIZE(szLabel));
  598. }
  599. else
  600. {
  601. item.mask = LVIF_TEXT | LVIF_PARAM;
  602. item.iItem = i;
  603. item.iSubItem = iSubItem;
  604. item.pszText = szLabel;
  605. item.cchTextMax = ARRAYSIZE(szLabel);
  606. item.stateMask = 0;
  607. szLabel[0] = TEXT('\0'); // In case the OnGetItem fails
  608. ListView_OnGetItem(plv, &item);
  609. if (!item.pszText)
  610. {
  611. SetRectEmpty(&rcSubItem);
  612. goto Exit;
  613. }
  614. if (item.pszText != szLabel)
  615. lstrcpyn(szLabel, item.pszText, ARRAYSIZE(szLabel));
  616. }
  617. cchLabel = lstrlen(szLabel);
  618. if (cchLabel > 0)
  619. {
  620. int cxRoomForLabel = plv->sizeTile.cx
  621. - 5 * g_cxLabelMargin
  622. - plv->cxIcon
  623. - _GetStateCX(plv)
  624. - plv->rcTileLabelMargin.left
  625. - plv->rcTileLabelMargin.right;
  626. int align;
  627. if (hdc)
  628. {
  629. lvfd.nmcd.nmcd.hdc = hdc; // Use the one the app gave us
  630. }
  631. else
  632. { // Set up fake customdraw
  633. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  634. lvfd.nmcd.nmcd.dwItemSpec = i;
  635. lvfd.nmcd.iSubItem = iSubItem;
  636. CIFakeCustomDrawNotify(&plv->ci, CDDS_ITEMPREPAINT | ((iSubItem != 0)?CDDS_SUBITEM:0), &lvfd.nmcd.nmcd);
  637. }
  638. if (plv->dwExStyle & WS_EX_RTLREADING)
  639. {
  640. align = GetTextAlign(lvfd.nmcd.nmcd.hdc);
  641. SetTextAlign(lvfd.nmcd.nmcd.hdc, align | TA_RTLREADING);
  642. }
  643. DrawText(lvfd.nmcd.nmcd.hdc, szLabel, cchLabel, &rcSubItem, (DT_LV | DT_CALCRECT));
  644. if ((iSubItem == 0) && (plv->cSubItems > 0))
  645. {
  646. // Sub Item zero can wrap to two lines (but only if there is room for a second line, i.e. if
  647. // cSubItems > 0. We need to pass this information (that we wrapped to a second
  648. // line, in addition to passing the rect height back) to the caller. The way we determine if we have
  649. // wrapped to a second line, is to call DrawText a second time with word wrapping enabled, and see if the
  650. // RECTHEIGHT is bigger.
  651. RECT rcSubItemWrapped = {0};
  652. LONG lLineHeight = RECTHEIGHT(rcSubItem);
  653. rcSubItemWrapped.right = cxRoomForLabel;
  654. DrawText(lvfd.nmcd.nmcd.hdc, szLabel, cchLabel, &rcSubItemWrapped, (DT_LVTILEWRAP | DT_CALCRECT | DT_WORD_ELLIPSIS));
  655. if (RECTHEIGHT(rcSubItemWrapped) > RECTHEIGHT(rcSubItem))
  656. {
  657. // We wrapped to multiple lines.
  658. fLineWrap = TRUE;
  659. // Don't let us go past two lines.
  660. if (RECTHEIGHT(rcSubItemWrapped) > 2 * RECTHEIGHT(rcSubItem))
  661. rcSubItemWrapped.bottom = rcSubItemWrapped.top + 2 * RECTHEIGHT(rcSubItem);
  662. rcSubItem = rcSubItemWrapped;
  663. }
  664. // Did we asked if we're folded?
  665. if (pbUnfolded)
  666. {
  667. // We need to call draw text again - this time without DT_WORD_ELLIPSES -
  668. // to determine if anything was actually truncated.
  669. RECT rcSubItemWrappedNoEllipse = {0};
  670. int cLines = fLineWrap ? 2 : 1;
  671. rcSubItemWrappedNoEllipse.right = cxRoomForLabel;
  672. DrawText(lvfd.nmcd.nmcd.hdc, szLabel, cchLabel, &rcSubItemWrappedNoEllipse, (DT_LVTILEWRAP | DT_CALCRECT));
  673. if (RECTHEIGHT(rcSubItemWrappedNoEllipse) > (cLines * lLineHeight))
  674. {
  675. *pbUnfolded = FALSE; // We're going to draw truncated.
  676. }
  677. }
  678. }
  679. else if (pbUnfolded)
  680. {
  681. // Are we truncated?
  682. *pbUnfolded = (RECTWIDTH(rcSubItem) <= cxRoomForLabel);
  683. }
  684. if (plv->dwExStyle & WS_EX_RTLREADING)
  685. {
  686. SetTextAlign(lvfd.nmcd.nmcd.hdc, align);
  687. }
  688. // rcSubItem was calculated w/o margins. Now add in margins.
  689. rcSubItem.left -= plv->rcTileLabelMargin.left;
  690. rcSubItem.right += plv->rcTileLabelMargin.right;
  691. // Top and bottom margins are left for the whole label - don't need to be applied here.
  692. if (!hdc)
  693. { // Clean up fake customdraw
  694. CIFakeCustomDrawNotify(&plv->ci, CDDS_ITEMPOSTPAINT | ((iSubItem != 0)?CDDS_SUBITEM:0), &lvfd.nmcd.nmcd);
  695. ListView_EndFakeCustomDraw(&lvfd);
  696. }
  697. }
  698. else
  699. {
  700. SetRectEmpty(&rcSubItem);
  701. }
  702. Exit:
  703. if (pitem)
  704. {
  705. pitem->rcTextRgn = rcSubItem;
  706. }
  707. else if (plsi)
  708. {
  709. plsi->sizeText.cx = RECTWIDTH(rcSubItem);
  710. plsi->sizeText.cy = RECTHEIGHT(rcSubItem);
  711. }
  712. if (prc)
  713. {
  714. if (rcSubItem.left < prc->left)
  715. prc->left = rcSubItem.left;
  716. if (rcSubItem.right > prc->right)
  717. prc->right = rcSubItem.right;
  718. prc->bottom += RECTHEIGHT(rcSubItem);
  719. }
  720. return fLineWrap;
  721. }
  722. void ListView_TRecomputeLabelSize(LV* plv, LISTITEM* pitem, int i, HDC hdc, BOOL fUsepitem)
  723. {
  724. if (pitem)
  725. {
  726. pitem->cxSingleLabel = 0;
  727. pitem->cxMultiLabel = 0;
  728. pitem->cyFoldedLabel = 0;
  729. pitem->cyUnfoldedLabel = SRECOMPUTE;
  730. }
  731. }
  732. void ListView_TRecomputeLabelSizeInternal(LV* plv, LISTITEM* pitem, int i, HDC hdc, BOOL fUsepitem)
  733. {
  734. RECT rcTotal = {0};
  735. LONG iLastBottom;
  736. LONG iLastDifference = 0; // last line's height
  737. BOOL fLineWrap; // Does the first line of the label wrap to the second?
  738. int iLabelLines = plv->cSubItems; // listview-wide number of lines per tile.
  739. LV_ITEM item; // What to use if pitem is not to be used.
  740. UINT cColumns = 0;
  741. UINT rguColumns[CCMAX_TILE_COLUMNS] = {0};
  742. UINT *puColumns = rguColumns;
  743. // Determine the number of columns to show
  744. if (fUsepitem && (pitem->cColumns != I_COLUMNSCALLBACK))
  745. {
  746. cColumns = pitem->cColumns;
  747. puColumns = pitem->puColumns;
  748. }
  749. else
  750. {
  751. item.mask = LVIF_COLUMNS;
  752. item.iItem = i;
  753. item.iSubItem = 0;
  754. item.stateMask = 0;
  755. item.cColumns = ARRAYSIZE(rguColumns);
  756. item.puColumns = rguColumns;
  757. if (ListView_OnGetItem(plv, &item))
  758. {
  759. cColumns = item.cColumns; // and puColumns = rguColumns
  760. }
  761. }
  762. iLastBottom = rcTotal.bottom;
  763. // The text of the item is determined in TCalculateSubItemRect.
  764. fLineWrap = TCalculateSubItemRect(plv, (fUsepitem ? pitem : NULL), NULL, i, 0, hdc, &rcTotal, NULL);
  765. iLastDifference = rcTotal.bottom - iLastBottom;
  766. if (fLineWrap)
  767. {
  768. iLabelLines--; // One less line for subitems.
  769. // iLastDifference should represent a single line... in this case, it represents two lines. Chop it in half.
  770. iLastDifference /= 2;
  771. }
  772. if (plv->cCol > 0)
  773. {
  774. int iSubItem;
  775. LVTILECOLUMNSENUM lvtce;
  776. _InitTileColumnsEnum(&lvtce, plv, cColumns, puColumns, fLineWrap);
  777. while (-1 != (iSubItem = _GetNextColumn(&lvtce)))
  778. {
  779. LISTSUBITEM* plsi;
  780. HDPA hdpa = ListView_IsOwnerData(plv) ? ListView_GetSubItemDPA(plv, iSubItem - 1) : NULL;
  781. if (hdpa)
  782. plsi = DPA_GetPtr(hdpa, i);
  783. else
  784. plsi = NULL;
  785. iLastBottom = rcTotal.bottom;
  786. TCalculateSubItemRect(plv, NULL, plsi, i, iSubItem, hdc, &rcTotal, NULL);
  787. iLabelLines--;
  788. }
  789. }
  790. // Add the top and bottom margins to rcTotal. Doesn't matter whether they're added to top or bottom,
  791. // since we only consider RECTHEIGHT
  792. rcTotal.bottom += (plv->rcTileLabelMargin.top + plv->rcTileLabelMargin.bottom);
  793. if (pitem)
  794. {
  795. int iStateImageOffset = _GetStateCX(plv);
  796. int cx = (plv->sizeTile.cx - 5 * g_cxLabelMargin - iStateImageOffset - plv->cxIcon);
  797. if (ListView_FullRowSelect(plv)) // full-row-select means full-tile-select
  798. {
  799. pitem->cxSingleLabel = pitem->cxMultiLabel = (short) cx;
  800. }
  801. else
  802. {
  803. if (cx > RECTWIDTH(rcTotal))
  804. cx = RECTWIDTH(rcTotal);
  805. pitem->cxSingleLabel = pitem->cxMultiLabel = (short) cx;
  806. }
  807. pitem->cyFoldedLabel = pitem->cyUnfoldedLabel = (short)RECTHEIGHT(rcTotal);
  808. }
  809. }
  810. /**
  811. * This function calculates the tilesize for listview, based on the following:
  812. * 1) Leave room for margins and padding
  813. * 2) Take into account imagelist and stateimage list.
  814. * 3) For the label portion, take into account
  815. * a) The number of tile columns (plv->cSubItems)
  816. * b) The height and width of a typical letter (leave space for 20 m's?)
  817. */
  818. void ListView_RecalcTileSize(LV* plv)
  819. {
  820. RECT rcItem = {0};
  821. int cLines;
  822. LVFAKEDRAW lvfd;
  823. LV_ITEM lvitem;
  824. if (plv->dwTileFlags == (LVTVIF_FIXEDHEIGHT | LVTVIF_FIXEDWIDTH))
  825. return; // Nothing to do.
  826. ListView_BeginFakeCustomDraw(plv, &lvfd, &lvitem);
  827. DrawText(lvfd.nmcd.nmcd.hdc, TEXT("m"), 1, &rcItem, (DT_LV | DT_CALCRECT));
  828. // REVIEW: Custom draw functionality needs to be tested.
  829. ListView_EndFakeCustomDraw(&lvfd);
  830. cLines = plv->cSubItems + 1; // +1 because cSubItems doesn't include the main label.
  831. if (!(plv->dwTileFlags & LVTVIF_FIXEDWIDTH))
  832. {
  833. // Here, we are attempting to determine a valid width for the tile, by assuming a typical number
  834. // of chars... and the size is based on TILELABELRATIO * the width of the letter 'm' in the current font.
  835. // This sucks. Without a genuine layout engine though, it is a difficult task. Other options include basing
  836. // the tile width on:
  837. // 1) some fraction of the client width
  838. // 2) the longest label we've got (like the LIST view currently does - this sucks)
  839. // 3) the height (via some ratio)
  840. // After some experimentation, TILELABELRATIO seems to look alright. (Note that a client can always
  841. // set tiles to be an explicit size too.)
  842. plv->sizeTile.cx = 4 * g_cxLabelMargin +
  843. _GetStateCX(plv) +
  844. plv->cxIcon +
  845. plv->rcTileLabelMargin.left +
  846. RECTWIDTH(rcItem) * TILELABELRATIO +
  847. plv->rcTileLabelMargin.right;
  848. }
  849. if (!(plv->dwTileFlags & LVTVIF_FIXEDHEIGHT))
  850. {
  851. int cyIcon = max(_GetStateCY(plv), plv->cyIcon);
  852. int cyText = plv->rcTileLabelMargin.top +
  853. RECTHEIGHT(rcItem) * cLines +
  854. plv->rcTileLabelMargin.bottom;
  855. plv->sizeTile.cy = 4 * g_cyIconMargin + max(cyIcon, cyText);
  856. }
  857. }
  858. /**
  859. * This gets the next tile column base on the LVTILECOLUMNSENUM struct.
  860. * We don't just directly use the column information in LVITEM/LISTITEM structs,
  861. * because we want to take the current sorted column into account. That column
  862. * automatically gets prepended to the columns that are displayed for each item.
  863. */
  864. int _GetNextColumn(PLVTILECOLUMNSENUM plvtce)
  865. {
  866. if (plvtce->iColumnsRemainingMax > 0)
  867. {
  868. plvtce->iColumnsRemainingMax--;
  869. if (plvtce->bUsedSortedColumn || (plvtce->iSortedColumn <= 0))
  870. {
  871. // We've already used the sorted column, or we've got no sorted column
  872. int iColumn;
  873. do
  874. {
  875. if (plvtce->iCurrentSpecifiedColumn >= plvtce->iTotalSpecifiedColumns)
  876. return -1;
  877. iColumn = plvtce->puSpecifiedColumns[plvtce->iCurrentSpecifiedColumn];
  878. plvtce->iCurrentSpecifiedColumn++;
  879. } while (iColumn == plvtce->iSortedColumn);
  880. return iColumn;
  881. }
  882. else
  883. {
  884. // We have a sorted column, and it has not been used - return it!
  885. plvtce->bUsedSortedColumn = TRUE;
  886. return plvtce->iSortedColumn;
  887. }
  888. }
  889. return -1;
  890. }