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.

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