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.

549 lines
15 KiB

  1. // list view (small icons, multiple columns)
  2. #include "ctlspriv.h"
  3. #include "listview.h"
  4. BOOL ListView_LDrawItem(PLVDRAWITEM plvdi)
  5. {
  6. RECT rcIcon;
  7. RECT rcLabel;
  8. RECT rcBounds;
  9. RECT rcT;
  10. LV_ITEM item;
  11. TCHAR ach[CCHLABELMAX];
  12. LV* plv = plvdi->plv;
  13. int i = (int) plvdi->nmcd.nmcd.dwItemSpec;
  14. COLORREF clrTextBk = plvdi->nmcd.clrTextBk;
  15. if (plv->pImgCtx || ListView_IsWatermarked(plv))
  16. {
  17. clrTextBk = CLR_NONE;
  18. }
  19. // moved here to reduce call backs in OWNERDATA case
  20. //
  21. item.iItem = i;
  22. item.iSubItem = 0;
  23. item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
  24. item.stateMask = LVIS_ALL;
  25. item.pszText = ach;
  26. item.cchTextMax = ARRAYSIZE(ach);
  27. ListView_OnGetItem(plv, &item);
  28. ListView_LGetRects(plv, i, &rcIcon, &rcLabel, &rcBounds, NULL);
  29. if (!plvdi->prcClip || IntersectRect(&rcT, &rcBounds, plvdi->prcClip))
  30. {
  31. UINT fText;
  32. if (plvdi->lpptOrg)
  33. {
  34. OffsetRect(&rcIcon, plvdi->lpptOrg->x - rcBounds.left,
  35. plvdi->lpptOrg->y - rcBounds.top);
  36. OffsetRect(&rcLabel, plvdi->lpptOrg->x - rcBounds.left,
  37. plvdi->lpptOrg->y - rcBounds.top);
  38. }
  39. fText = ListView_DrawImage(plv, &item, plvdi->nmcd.nmcd.hdc,
  40. rcIcon.left, rcIcon.top, plvdi->flags) | SHDT_ELLIPSES;
  41. // Don't draw the label if it is being edited.
  42. if (plv->iEdit != i)
  43. {
  44. int ItemCxSingleLabel;
  45. UINT ItemState;
  46. if (ListView_IsOwnerData( plv ))
  47. {
  48. LISTITEM listitem;
  49. // calculate lable sizes from iItem
  50. listitem.pszText = ach;
  51. ListView_IRecomputeLabelSize( plv, &listitem, i, plvdi->nmcd.nmcd.hdc, TRUE );
  52. ItemCxSingleLabel = listitem.cxSingleLabel;
  53. ItemState = item.state;
  54. }
  55. else
  56. {
  57. ItemCxSingleLabel = plvdi->pitem->cxSingleLabel;
  58. ItemState = plvdi->pitem->state;
  59. }
  60. if (plvdi->flags & LVDI_TRANSTEXT)
  61. fText |= SHDT_TRANSPARENT;
  62. if (ItemCxSingleLabel == SRECOMPUTE)
  63. {
  64. ListView_IRecomputeLabelSize(plv, plvdi->pitem, i, plvdi->nmcd.nmcd.hdc, FALSE);
  65. ItemCxSingleLabel = plvdi->pitem->cxSingleLabel;
  66. }
  67. if (ItemCxSingleLabel < rcLabel.right - rcLabel.left)
  68. rcLabel.right = rcLabel.left + ItemCxSingleLabel;
  69. if ((fText & SHDT_SELECTED) && (plvdi->flags & LVDI_HOTSELECTED))
  70. fText |= SHDT_HOTSELECTED;
  71. #ifdef WINDOWS_ME
  72. if( plv->dwExStyle & WS_EX_RTLREADING)
  73. fText |= SHDT_RTLREADING;
  74. #endif
  75. SHDrawText(plvdi->nmcd.nmcd.hdc, item.pszText, &rcLabel, LVCFMT_LEFT, fText,
  76. plv->cyLabelChar, plv->cxEllipses,
  77. plvdi->nmcd.clrText, clrTextBk);
  78. if ((plvdi->flags & LVDI_FOCUS) && (ItemState & LVIS_FOCUSED) &&
  79. !(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS))
  80. {
  81. DrawFocusRect(plvdi->nmcd.nmcd.hdc, &rcLabel);
  82. }
  83. }
  84. }
  85. return TRUE;
  86. }
  87. DWORD ListView_LApproximateViewRect(LV* plv, int iCount, int iWidth, int iHeight)
  88. {
  89. int cxItem = plv->cxItem;
  90. int cyItem = plv->cyItem;
  91. int cCols;
  92. int cRows;
  93. cRows = iHeight / cyItem;
  94. cRows = min(cRows, iCount);
  95. if (cRows == 0)
  96. cRows = 1;
  97. cCols = (iCount + cRows - 1) / cRows;
  98. iWidth = cCols * cxItem;
  99. iHeight = cRows * cyItem;
  100. return MAKELONG(iWidth + g_cxEdge, iHeight + g_cyEdge);
  101. }
  102. int ListView_LItemHitTest(LV* plv, int x, int y, UINT* pflags, int *piSubItem)
  103. {
  104. int iHit;
  105. int i;
  106. int iCol;
  107. int xItem; //where is the x in relation to the item
  108. UINT flags;
  109. LISTITEM* pitem;
  110. if (piSubItem)
  111. *piSubItem = 0;
  112. flags = LVHT_NOWHERE;
  113. iHit = -1;
  114. i = y / plv->cyItem;
  115. if (i >= 0 && i < plv->cItemCol)
  116. {
  117. iCol = (x + plv->xOrigin) / plv->cxItem;
  118. i += iCol * plv->cItemCol;
  119. if (i >= 0 && i < ListView_Count(plv))
  120. {
  121. iHit = i;
  122. xItem = x + plv->xOrigin - iCol * plv->cxItem;
  123. if (xItem < plv->cxState) {
  124. flags = LVHT_ONITEMSTATEICON;
  125. } else if (xItem < (plv->cxState + plv->cxSmIcon)) {
  126. flags = LVHT_ONITEMICON;
  127. }
  128. else
  129. {
  130. int ItemCxSingleLabel;
  131. if (ListView_IsOwnerData( plv ))
  132. {
  133. LISTITEM item;
  134. // calculate lable sizes from iItem
  135. ListView_IRecomputeLabelSize( plv, &item, i, NULL, FALSE );
  136. ItemCxSingleLabel = item.cxSingleLabel;
  137. }
  138. else
  139. {
  140. pitem = ListView_FastGetItemPtr(plv, i);
  141. if (pitem->cxSingleLabel == SRECOMPUTE)
  142. {
  143. ListView_IRecomputeLabelSize(plv, pitem, i, NULL, FALSE);
  144. }
  145. ItemCxSingleLabel = pitem->cxSingleLabel;
  146. }
  147. if (xItem < (plv->cxSmIcon + plv->cxState + ItemCxSingleLabel))
  148. flags = LVHT_ONITEMLABEL;
  149. }
  150. }
  151. }
  152. *pflags = flags;
  153. return iHit;
  154. }
  155. void ListView_LGetRects(LV* plv, int i, RECT* prcIcon,
  156. RECT* prcLabel, RECT *prcBounds, RECT* prcSelectBounds)
  157. {
  158. RECT rcIcon;
  159. RECT rcLabel;
  160. int x, y;
  161. int cItemCol = plv->cItemCol;
  162. if (cItemCol == 0)
  163. {
  164. // Called before other data has been initialized so call
  165. // update scrollbars which should make sure that that
  166. // we have valid data...
  167. ListView_UpdateScrollBars(plv);
  168. // but it's possible that updatescrollbars did nothing because of
  169. // LVS_NOSCROLL or redraw
  170. // REARCHITECT raymondc v6.0: Get it right even if no redraw. Fix for v6.
  171. if (plv->cItemCol == 0)
  172. cItemCol = 1;
  173. else
  174. cItemCol = plv->cItemCol;
  175. }
  176. x = (i / cItemCol) * plv->cxItem;
  177. y = (i % cItemCol) * plv->cyItem;
  178. rcIcon.left = x - plv->xOrigin + (plv->cxState + LV_ICONTOSTATEOFFSET(plv));
  179. rcIcon.top = y;
  180. rcIcon.right = rcIcon.left + plv->cxSmIcon;
  181. rcIcon.bottom = rcIcon.top + plv->cyItem;
  182. if (prcIcon)
  183. *prcIcon = rcIcon;
  184. rcLabel.left = rcIcon.right;
  185. rcLabel.right = rcIcon.left + plv->cxItem - (plv->cxState + LV_ICONTOSTATEOFFSET(plv));
  186. rcLabel.top = rcIcon.top;
  187. rcLabel.bottom = rcIcon.bottom;
  188. if (prcLabel)
  189. *prcLabel = rcLabel;
  190. if (prcBounds)
  191. {
  192. *prcBounds = rcLabel;
  193. prcBounds->left = rcIcon.left - (plv->cxState + LV_ICONTOSTATEOFFSET(plv));
  194. }
  195. if (prcSelectBounds)
  196. {
  197. *prcSelectBounds = rcLabel;
  198. prcSelectBounds->left = rcIcon.left;
  199. }
  200. }
  201. void ListView_LUpdateScrollBars(LV* plv)
  202. {
  203. RECT rcClient;
  204. int cItemCol;
  205. int cCol;
  206. int cColVis;
  207. int nPosHold;
  208. SCROLLINFO si;
  209. ASSERT(plv);
  210. ListView_GetClientRect(plv, &rcClient, FALSE, NULL);
  211. cColVis = (rcClient.right - rcClient.left) / plv->cxItem;
  212. cItemCol = max(1, (rcClient.bottom - rcClient.top) / plv->cyItem);
  213. cCol = (ListView_Count(plv) + cItemCol - 1) / cItemCol;
  214. // Make the client area smaller as appropriate, and
  215. // recompute cCol to reflect scroll bar.
  216. //
  217. si.cbSize = sizeof(SCROLLINFO);
  218. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  219. si.nPage = cColVis;
  220. si.nMin = 0;
  221. rcClient.bottom -= ListView_GetCyScrollbar(plv);
  222. cItemCol = max(1, (rcClient.bottom - rcClient.top) / plv->cyItem);
  223. cCol = (ListView_Count(plv) + cItemCol - 1) / cItemCol;
  224. si.nPos = nPosHold = plv->xOrigin / plv->cxItem;
  225. si.nMax = cCol - 1;
  226. ListView_SetScrollInfo(plv, SB_HORZ, &si, TRUE);
  227. // SetScrollInfo changes si.nPos to 0 if si.nMax == 0 and si.nPos > 0.
  228. // That can prevent the list view items from scrolling into position if the
  229. // view goes from 1 column to zero.
  230. si.nPos = nPosHold;
  231. // Update number of visible lines...
  232. //
  233. if (plv->cItemCol != cItemCol)
  234. {
  235. plv->cItemCol = cItemCol;
  236. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  237. }
  238. // make sure our position and page doesn't hang over max
  239. if ((si.nPos + (LONG)si.nPage - 1 > si.nMax) && si.nPos > 0) {
  240. int iNewPos, iDelta;
  241. iNewPos = (int)si.nMax - (int)si.nPage + 1;
  242. if (iNewPos < 0) iNewPos = 0;
  243. if (iNewPos != si.nPos) {
  244. iDelta = iNewPos - (int)si.nPos;
  245. ListView_LScroll2(plv, iDelta, 0, 0);
  246. ListView_LUpdateScrollBars(plv);
  247. }
  248. }
  249. // never have the other scrollbar
  250. ListView_SetScrollRange(plv, SB_VERT, 0, 0, TRUE);
  251. }
  252. //
  253. // We need a smoothscroll callback so our background image draws
  254. // at the correct origin. If we don't have a background image,
  255. // then this work is superfluous but not harmful either.
  256. //
  257. int CALLBACK ListView_LScroll2_SmoothScroll(
  258. HWND hwnd,
  259. int dx,
  260. int dy,
  261. CONST RECT *prcScroll,
  262. CONST RECT *prcClip,
  263. HRGN hrgnUpdate,
  264. LPRECT prcUpdate,
  265. UINT flags)
  266. {
  267. LV* plv = ListView_GetPtr(hwnd);
  268. if (plv)
  269. {
  270. plv->xOrigin -= dx;
  271. }
  272. // Now do what SmoothScrollWindow would've done if we weren't
  273. // a callback
  274. if (ListView_IsWatermarkedBackground(plv) ||
  275. ListView_IsWatermarked(plv))
  276. {
  277. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  278. return TRUE;
  279. }
  280. else
  281. return ScrollWindowEx(hwnd, dx, dy, prcScroll, prcClip, hrgnUpdate, prcUpdate, flags);
  282. }
  283. void ListView_LScroll2(LV* plv, int dx, int dy, UINT uSmooth)
  284. {
  285. if (dx)
  286. {
  287. SMOOTHSCROLLINFO si;
  288. dx *= plv->cxItem;
  289. si.cbSize = sizeof(si);
  290. si.fMask = SSIF_SCROLLPROC;
  291. si.hwnd =plv->ci.hwnd ;
  292. si.dx =-dx ;
  293. si.dy = 0;
  294. si.lprcSrc = NULL;
  295. si.lprcClip = NULL;
  296. si.hrgnUpdate = NULL;
  297. si.lprcUpdate = NULL;
  298. si.fuScroll = SW_INVALIDATE | SW_ERASE | SSW_EX_UPDATEATEACHSTEP;
  299. si.pfnScrollProc = ListView_LScroll2_SmoothScroll;
  300. SmoothScrollWindow(&si);
  301. UpdateWindow(plv->ci.hwnd);
  302. }
  303. }
  304. void ListView_LOnScroll(LV* plv, UINT code, int posNew, UINT sb)
  305. {
  306. RECT rcClient;
  307. int cPage;
  308. if (plv->hwndEdit)
  309. ListView_DismissEdit(plv, FALSE);
  310. ListView_GetClientRect(plv, &rcClient, TRUE, NULL);
  311. cPage = (rcClient.right - rcClient.left) / plv->cxItem;
  312. ListView_ComOnScroll(plv, code, posNew, SB_HORZ, 1,
  313. cPage ? cPage : 1);
  314. }
  315. int ListView_LGetScrollUnitsPerLine(LV* plv, UINT sb)
  316. {
  317. return 1;
  318. }
  319. //------------------------------------------------------------------------------
  320. //
  321. // Function: ListView_LCalcViewItem
  322. //
  323. // Summary: This function will calculate which item slot is at the x, y location
  324. //
  325. // Arguments:
  326. // plv [in] - The list View to work with
  327. // x [in] - The x location
  328. // y [in] - The y location
  329. //
  330. // Returns: the valid slot the point was within.
  331. //
  332. // Notes:
  333. //
  334. // History:
  335. // Nov-3-94 MikeMi Created
  336. //
  337. //------------------------------------------------------------------------------
  338. int ListView_LCalcViewItem( LV* plv, int x, int y )
  339. {
  340. int iItem;
  341. int iRow = 0;
  342. int iCol = 0;
  343. ASSERT( plv );
  344. iRow = y / plv->cyItem;
  345. iRow = max( iRow, 0 );
  346. iRow = min( iRow, plv->cItemCol - 1 );
  347. iCol = (x + plv->xOrigin) / plv->cxItem;
  348. iItem = iRow + iCol * plv->cItemCol;
  349. iItem = max( iItem, 0 );
  350. iItem = min( iItem, ListView_Count(plv) - 1);
  351. return( iItem );
  352. }
  353. int LV_GetNewColWidth(LV* plv, int iFirst, int iLast)
  354. {
  355. int cxMaxLabel = 0;
  356. // Don't do anything if there are no items to measure
  357. if ((iFirst <= iLast) && (iLast < ListView_Count(plv)))
  358. {
  359. LVFAKEDRAW lvfd;
  360. LV_ITEM lvitem;
  361. LISTITEM item;
  362. if (ListView_IsOwnerData( plv ))
  363. {
  364. int iViewFirst;
  365. int iViewLast;
  366. iViewFirst = ListView_LCalcViewItem( plv, 1, 1 );
  367. iViewLast = ListView_LCalcViewItem( plv,
  368. plv->sizeClient.cx - 1,
  369. plv->sizeClient.cy - 1 );
  370. if ((iLast - iFirst) > (iViewLast - iViewFirst))
  371. {
  372. iFirst = max( iFirst, iViewFirst );
  373. iLast = min( iLast, iViewLast );
  374. }
  375. iLast = min( ListView_Count( plv ), iLast );
  376. iFirst = max( 0, iFirst );
  377. iLast = max( iLast, iFirst );
  378. ListView_NotifyCacheHint( plv, iFirst, iLast );
  379. }
  380. ListView_BeginFakeCustomDraw(plv, &lvfd, &lvitem);
  381. lvitem.iSubItem = 0;
  382. lvitem.mask = LVIF_PARAM;
  383. item.lParam = 0;
  384. while (iFirst <= iLast)
  385. {
  386. LISTITEM* pitem;
  387. if (ListView_IsOwnerData( plv ))
  388. {
  389. pitem = &item;
  390. pitem->cxSingleLabel = SRECOMPUTE;
  391. }
  392. else
  393. {
  394. pitem = ListView_FastGetItemPtr(plv, iFirst);
  395. }
  396. if (pitem->cxSingleLabel == SRECOMPUTE)
  397. {
  398. lvitem.iItem = iFirst;
  399. lvitem.lParam = pitem->lParam;
  400. ListView_BeginFakeItemDraw(&lvfd);
  401. ListView_IRecomputeLabelSize(plv, pitem, iFirst, lvfd.nmcd.nmcd.hdc, FALSE);
  402. ListView_EndFakeItemDraw(&lvfd);
  403. }
  404. if (pitem->cxSingleLabel > cxMaxLabel)
  405. cxMaxLabel = pitem->cxSingleLabel;
  406. iFirst++;
  407. }
  408. ListView_EndFakeCustomDraw(&lvfd);
  409. }
  410. // We have the max label width, see if this plus the rest of the slop will
  411. // cause us to want to resize.
  412. //
  413. cxMaxLabel += plv->cxSmIcon + g_cxIconMargin + plv->cxState + LV_ICONTOSTATEOFFSET(plv);
  414. if (cxMaxLabel > g_cxScreen)
  415. cxMaxLabel = g_cxScreen;
  416. return cxMaxLabel;
  417. }
  418. //------------------------------------------------------------------------------
  419. // This function will see if the size of column should be changed for the listview
  420. // It will check to see if the items between first and last exceed the current width
  421. // and if so will see if the columns are currently big enough. This wont happen
  422. // if we are not currently in listview or if the caller has set an explicit size.
  423. //
  424. // OWNERDATA CHANGE
  425. // This function is normally called with the complete list range,
  426. // This will has been changed to be called only with currently visible
  427. // to the user when in OWNERDATA mode. This will be much more effiencent.
  428. //
  429. BOOL ListView_MaybeResizeListColumns(LV* plv, int iFirst, int iLast)
  430. {
  431. HDC hdc = NULL;
  432. int cxMaxLabel;
  433. if (!ListView_IsListView(plv) || (plv->flags & LVF_COLSIZESET))
  434. return(FALSE);
  435. cxMaxLabel = LV_GetNewColWidth(plv, iFirst, iLast);
  436. // Now see if we should resize the columns...
  437. if (cxMaxLabel > plv->cxItem)
  438. {
  439. int iScroll = plv->xOrigin / plv->cxItem;
  440. TraceMsg(TF_LISTVIEW, "LV Resize Columns: %d", cxMaxLabel);
  441. ListView_ISetColumnWidth(plv, 0, cxMaxLabel, FALSE);
  442. plv->xOrigin = iScroll * plv->cxItem;
  443. return(TRUE);
  444. }
  445. return(FALSE);
  446. }