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.

626 lines
17 KiB

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