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.

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