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.

2755 lines
81 KiB

  1. // report view stuff (details)
  2. #include "ctlspriv.h"
  3. #include "listview.h"
  4. #include <limits.h>
  5. #define LV_DETAILSPADDING 1
  6. #define LV_ICONINDENT 2
  7. void ListView_RGetRectsEx(LV* plv, int iItem, int iSubItem, LPRECT prcIcon, LPRECT prcLabel);
  8. int ListView_RXHitTest(LV* plv, int x);
  9. void ListView_RInitialize(LV* plv, BOOL fInval)
  10. {
  11. MEASUREITEMSTRUCT mi;
  12. if (plv && (plv->ci.style & LVS_OWNERDRAWFIXED))
  13. {
  14. int iOld = plv->cyItem;
  15. mi.CtlType = ODT_LISTVIEW;
  16. mi.CtlID = GetDlgCtrlID(plv->ci.hwnd);
  17. mi.itemHeight = plv->cyItem; // default
  18. SendMessage(plv->ci.hwndParent, WM_MEASUREITEM, mi.CtlID, (LPARAM)(MEASUREITEMSTRUCT *)&mi);
  19. plv->cyItem = max(mi.itemHeight, 1); // never let app set height=0 or we fault-o-rama!
  20. if (fInval && (iOld != plv->cyItem))
  21. {
  22. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  23. }
  24. }
  25. }
  26. DWORD ListView_RApproximateViewRect(LV* plv, int iCount, int iWidth, int iHeight)
  27. {
  28. RECT rc;
  29. ListView_RGetRects(plv, iCount, NULL, NULL, &rc, NULL);
  30. rc.bottom += plv->ptlRptOrigin.y;
  31. rc.right += plv->ptlRptOrigin.x;
  32. return MAKELONG(rc.right, rc.bottom);
  33. }
  34. void CCDrawRect(HDC hdc, int x, int y, int dx, int dy)
  35. {
  36. RECT rc;
  37. rc.left = x;
  38. rc.top = y;
  39. rc.right = x + dx;
  40. rc.bottom = y + dy;
  41. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  42. }
  43. void ListView_RAfterRedraw(LV* plv, HDC hdc)
  44. {
  45. if (plv->exStyle & LVS_EX_GRIDLINES && !plv->fGroupView)
  46. {
  47. int i;
  48. int x;
  49. COLORREF clrBk;
  50. clrBk = SetBkColor(hdc, g_clrBtnFace);
  51. x = -plv->ptlRptOrigin.x;
  52. for (i = 0 ; (i < plv->cCol) && (x < plv->sizeClient.cx); i++)
  53. {
  54. HD_ITEM hitem;
  55. hitem.mask = HDI_WIDTH;
  56. Header_GetItem(plv->hwndHdr,
  57. SendMessage(plv->hwndHdr, HDM_ORDERTOINDEX, i, 0),
  58. &hitem);
  59. x += hitem.cxy;
  60. if (x > 0)
  61. {
  62. CCDrawRect(hdc, x, 0, g_cxBorder, plv->sizeClient.cy);
  63. }
  64. }
  65. for (x = plv->yTop - 1; (x < plv->sizeClient.cy); x += plv->cyItem)
  66. {
  67. CCDrawRect(hdc, 0, x, plv->sizeClient.cx, g_cxBorder);
  68. }
  69. SetBkColor(hdc, clrBk);
  70. }
  71. }
  72. //
  73. // Internal function to Get the CXLabel, taking into account if the listview
  74. // has no item data and also if RECOMPUTE needs to happen.
  75. //
  76. SHORT ListView_RGetCXLabel(LV* plv, int i, LISTITEM* pitem,
  77. HDC hdc, BOOL fUseItem)
  78. {
  79. SHORT cxLabel = SRECOMPUTE;
  80. if (!ListView_IsOwnerData( plv ))
  81. {
  82. cxLabel = pitem->cxSingleLabel;
  83. }
  84. if (cxLabel == SRECOMPUTE)
  85. {
  86. LISTITEM item;
  87. if (!pitem)
  88. {
  89. ASSERT(!fUseItem)
  90. pitem = &item;
  91. fUseItem = FALSE;
  92. }
  93. ListView_IRecomputeLabelSize(plv, pitem, i, hdc, fUseItem);
  94. cxLabel = pitem->cxSingleLabel;
  95. }
  96. // add on the space around the label taken up by the select rect
  97. cxLabel += 2*g_cxLabelMargin;
  98. return(cxLabel);
  99. }
  100. #define SATURATEBYTE(percent, x) { if (x + (percent * 10 * (x)) / 1000 > 0xFF) { if (fAllowDesaturation) x -= (x) / 30; else x = 0xFF; } else x += (percent * 10 * (x)) / 1000; }
  101. COLORREF GetSortColor(int iPercent, COLORREF clr)
  102. {
  103. BOOL fAllowDesaturation;
  104. BYTE r, g, b;
  105. if (clr == 0) // Black huh?
  106. {
  107. return RGB(128,128,128);
  108. }
  109. // Doing this is less expensive than Luminance adjustment
  110. fAllowDesaturation = FALSE;
  111. r = GetRValue(clr);
  112. g = GetGValue(clr);
  113. b = GetBValue(clr);
  114. // If all colors are above positive saturation, allow a desaturation
  115. if (r > 0xF0 && g > 0xF0 && b > 0xF0)
  116. {
  117. fAllowDesaturation = TRUE;
  118. }
  119. SATURATEBYTE(iPercent, r);
  120. SATURATEBYTE(iPercent, g);
  121. SATURATEBYTE(iPercent, b);
  122. return RGB(r,g,b);
  123. }
  124. //
  125. // Returns FALSE if no more items to draw.
  126. //
  127. BOOL ListView_RDrawItem(PLVDRAWITEM plvdi)
  128. {
  129. BOOL fDrawFocusRect = FALSE;
  130. BOOL fSelected = FALSE;
  131. RECT rcIcon;
  132. RECT rcLabel;
  133. RECT rcBounds;
  134. RECT rcT;
  135. LV* plv = plvdi->plv;
  136. int iCol = 0;
  137. LVITEM item;
  138. HDITEM hitem;
  139. TCHAR ach[CCHLABELMAX];
  140. UINT fText = 0;
  141. UINT uSubItemFlags;
  142. int iIndex = 0;
  143. int xOffset = 0;
  144. int yOffset = 0;
  145. ListView_RGetRects(plv, (int)plvdi->nmcd.nmcd.dwItemSpec, NULL, NULL, &rcBounds, NULL);
  146. if (rcBounds.bottom <= plv->yTop)
  147. return TRUE;
  148. if (plvdi->prcClip)
  149. {
  150. if (rcBounds.top >= plvdi->prcClip->bottom)
  151. return plv->fGroupView; // no more items need painting, unless we are in group view.
  152. // In group view, we can have the items out of order.
  153. // Probably this condition won't happen very often...
  154. if (!IntersectRect(&rcT, &rcBounds, plvdi->prcClip))
  155. return TRUE;
  156. }
  157. // REVIEW: this would be faster if we did the GetClientRect
  158. // outside the loop.
  159. //
  160. if (rcBounds.top >= plv->sizeClient.cy)
  161. return plv->fGroupView; // See above comment about groupview.
  162. if (plvdi->lpptOrg)
  163. {
  164. xOffset = plvdi->lpptOrg->x - rcBounds.left;
  165. yOffset = plvdi->lpptOrg->y - rcBounds.top;
  166. OffsetRect(&rcBounds, xOffset, yOffset);
  167. }
  168. item.iItem = (int)plvdi->nmcd.nmcd.dwItemSpec;
  169. item.stateMask = LVIS_ALL;
  170. // for first ListView_OnGetItem call
  171. item.state = 0;
  172. if (plv->ci.style & LVS_OWNERDRAWFIXED)
  173. {
  174. goto SendOwnerDraw;
  175. }
  176. SetRectEmpty(&rcT);
  177. for (; iCol < plv->cCol; iCol++)
  178. {
  179. DWORD dwCustom = 0;
  180. UINT uImageFlags;
  181. COLORREF crBkSave = plv->clrBk;
  182. COLORREF clrTextBk;
  183. iIndex = (int) SendMessage(plv->hwndHdr, HDM_ORDERTOINDEX, iCol, 0);
  184. SendOwnerDraw:
  185. if (iIndex == 0)
  186. {
  187. item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
  188. }
  189. else
  190. {
  191. // Next time through, we only want text for subitems...
  192. item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
  193. }
  194. item.iImage = -1;
  195. item.iSubItem = iIndex;
  196. item.pszText = ach;
  197. item.cchTextMax = ARRAYSIZE(ach);
  198. ListView_OnGetItem(plv, &item);
  199. uSubItemFlags = plvdi->flags;
  200. if (iIndex == 0)
  201. {
  202. // if it's owner draw, send off a message and return.
  203. // do this after we've collected state information above though
  204. if (plv->ci.style & LVS_OWNERDRAWFIXED)
  205. {
  206. DRAWITEMSTRUCT di = {0};
  207. di.CtlType = ODT_LISTVIEW;
  208. di.CtlID = GetDlgCtrlID(plv->ci.hwnd);
  209. di.itemID = (int)plvdi->nmcd.nmcd.dwItemSpec;
  210. di.itemAction = ODA_DRAWENTIRE;
  211. di.hwndItem = plv->ci.hwnd;
  212. di.hDC = plvdi->nmcd.nmcd.hdc;
  213. di.rcItem = rcBounds;
  214. if (plvdi->pitem)
  215. di.itemData = plvdi->pitem->lParam;
  216. if (item.state & LVIS_FOCUSED)
  217. {
  218. di.itemState |= ODS_FOCUS;
  219. }
  220. if (item.state & LVIS_SELECTED)
  221. {
  222. di.itemState |= ODS_SELECTED;
  223. }
  224. SendMessage(plv->ci.hwndParent, WM_DRAWITEM, di.CtlID,
  225. (LPARAM)(DRAWITEMSTRUCT *)&di);
  226. return TRUE;
  227. }
  228. }
  229. hitem.mask = HDI_WIDTH | HDI_FORMAT;
  230. Header_GetItem(plv->hwndHdr, iIndex, &hitem);
  231. // first get the rects...
  232. ListView_RGetRectsEx(plv, (int)plvdi->nmcd.nmcd.dwItemSpec, iIndex, &rcIcon, &rcLabel);
  233. OffsetRect(&rcIcon, xOffset, yOffset);
  234. OffsetRect(&rcLabel, xOffset, yOffset);
  235. if (plvdi->dwCustom & CDRF_NOTIFYSUBITEMDRAW)
  236. {
  237. RECT rcTemp;
  238. UINT uItemStateOld = plvdi->nmcd.nmcd.uItemState, uItemStateNew;
  239. SendMessage(plv->hwndHdr, HDM_GETITEMRECT, iIndex, (LPARAM)&rcTemp);
  240. plvdi->nmcd.nmcd.rc.left = rcTemp.left;
  241. plvdi->nmcd.nmcd.rc.right = rcTemp.right;
  242. plvdi->nmcd.iSubItem = iIndex;
  243. dwCustom = CICustomDrawNotify(&plvdi->plv->ci, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &plvdi->nmcd.nmcd);
  244. uItemStateNew = plvdi->nmcd.nmcd.uItemState;
  245. plvdi->nmcd.nmcd.uItemState = uItemStateOld;
  246. if (dwCustom & CDRF_SKIPDEFAULT)
  247. continue;
  248. uSubItemFlags &= ~(LVDI_FOCUS | LVDI_SELECTED | LVDI_SELECTNOFOCUS | LVDI_HOTSELECTED);
  249. if (uItemStateNew & CDIS_FOCUS)
  250. uSubItemFlags |= LVDI_FOCUS;
  251. if (uItemStateNew & CDIS_SELECTED)
  252. {
  253. if (plvdi->plv->flags & LVF_FOCUSED)
  254. uSubItemFlags |= LVDI_SELECTED;
  255. else
  256. uSubItemFlags |= LVDI_SELECTNOFOCUS;
  257. if (plvdi->plv->iHot == (int)plvdi->nmcd.nmcd.dwItemSpec &&
  258. ((plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  259. (plv->exStyle & LVS_EX_TWOCLICKACTIVATE)))
  260. {
  261. uSubItemFlags |= LVDI_HOTSELECTED;
  262. }
  263. }
  264. }
  265. if (iIndex != 0)
  266. {
  267. // for right now, add this in because the get rects for
  268. // non 0 doesn't account for the icon (yet)
  269. if (item.iImage != -1)
  270. rcLabel.left += plv->cxSmIcon + LV_ICONINDENT;
  271. }
  272. uImageFlags = uSubItemFlags;
  273. fText = ListView_GetTextSelectionFlags(plv, &item, uSubItemFlags);
  274. fSelected = fText & (SHDT_SELECTED | SHDT_SELECTNOFOCUS);
  275. clrTextBk = plvdi->nmcd.clrTextBk;
  276. if (plv->pImgCtx || ListView_IsWatermarked(plv))
  277. {
  278. clrTextBk = CLR_NONE;
  279. }
  280. if (iIndex == plv->iLastColSort &&
  281. !(plv->pImgCtx && plv->fImgCtxComplete) &&
  282. !plv->fGroupView)
  283. {
  284. plv->clrBk = GetSortColor(10, plv->clrBk);
  285. clrTextBk = plv->clrBk;
  286. }
  287. if (item.iImage == -1)
  288. {
  289. if (iIndex != 0)
  290. {
  291. // just use ListView_DrawImage to get the fText
  292. uImageFlags |= LVDI_NOIMAGE;
  293. }
  294. }
  295. else if (ListView_FullRowSelect(plv) &&
  296. (fSelected || !(plv->pImgCtx && plv->fImgCtxComplete))) // Don't do this unless we are selected or we don't have an image
  297. {
  298. int iLeft = rcIcon.left;
  299. int iRight = rcIcon.right;
  300. if (iIndex == 0)
  301. {
  302. rcIcon.left -= plv->cxState + LV_ICONTOSTATEOFFSET(plv) + g_cxEdge;
  303. }
  304. rcIcon.right = rcLabel.right;
  305. FillRectClr(plvdi->nmcd.nmcd.hdc, &rcIcon, plv->clrBk);
  306. rcIcon.left = iLeft;
  307. rcIcon.right = iRight;
  308. }
  309. ListView_DrawImageEx(plv, &item, plvdi->nmcd.nmcd.hdc,
  310. rcIcon.left, rcIcon.top, plv->clrBk, uSubItemFlags, rcLabel.right);
  311. if (ListView_FullRowSelect(plv) && (uSubItemFlags & LVDI_FOCUS))
  312. {
  313. // if we're doing a full row selection, collect the union
  314. // of the labels for the focus rect
  315. UnionRect(&rcT, &rcT, &rcLabel);
  316. }
  317. if (item.pszText)
  318. {
  319. int xLabelRight = rcLabel.right;
  320. UINT textflags;
  321. // give all but the first columns extra margins so
  322. // left and right justified things don't stick together
  323. textflags = (iIndex == 0) ? SHDT_ELLIPSES : SHDT_ELLIPSES | SHDT_EXTRAMARGIN;
  324. // rectangle limited to the size of the string
  325. textflags |= fText;
  326. if ((!ListView_FullRowSelect(plv)) &&
  327. ((fText & (SHDT_SELECTED | SHDT_SELECTNOFOCUS)) || (item.state & LVIS_FOCUSED)))
  328. {
  329. int cxLabel;
  330. // if selected or focused, the rectangle is more
  331. // meaningful and should correspond to the string
  332. //
  333. if (iIndex == 0)
  334. {
  335. LISTITEM litem;
  336. LISTITEM *pitem = plvdi->pitem;
  337. if (!pitem)
  338. {
  339. pitem = &litem;
  340. litem.pszText = item.pszText;
  341. }
  342. cxLabel = ListView_RGetCXLabel(plv, (int)plvdi->nmcd.nmcd.dwItemSpec, pitem, plvdi->nmcd.nmcd.hdc, TRUE);
  343. }
  344. else
  345. {
  346. // add g_cxLabelMargin * 6 because we use SHDT_EXTRAMARGIN
  347. // on iIndex != 0
  348. // and if you look inside shdrawtext, there are 6 cxlabelmargins added...
  349. cxLabel = ListView_OnGetStringWidth(plv, item.pszText, plvdi->nmcd.nmcd.hdc) + g_cxLabelMargin * 6;
  350. }
  351. if (rcLabel.right > rcLabel.left + cxLabel)
  352. {
  353. rcLabel.right = rcLabel.left + cxLabel;
  354. }
  355. }
  356. if ((iIndex != 0) || (plv->iEdit != (int)plvdi->nmcd.nmcd.dwItemSpec))
  357. {
  358. COLORREF clrText;
  359. HFONT hFontTemp = NULL;
  360. int cxEllipses;
  361. HRESULT hr = E_FAIL;
  362. clrText = plvdi->nmcd.clrText;
  363. if ((clrText == GetSysColor(COLOR_HOTLIGHT)) ||
  364. ((plv->exStyle & LVS_EX_UNDERLINEHOT) &&
  365. ((plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  366. ((plvdi->plv->exStyle & LVS_EX_TWOCLICKACTIVATE) &&
  367. ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec, LVIS_SELECTED)))))
  368. {
  369. if (iIndex != 0 && !ListView_FullRowSelect(plv))
  370. {
  371. hFontTemp = SelectFont(plvdi->nmcd.nmcd.hdc, plv->hfontLabel);
  372. if (hFontTemp != plv->hFontHot)
  373. {
  374. // they've overridden... leave it.
  375. SelectFont(plvdi->nmcd.nmcd.hdc, hFontTemp);
  376. hFontTemp = NULL;
  377. }
  378. clrText = plv->clrText;
  379. }
  380. }
  381. if ((textflags & SHDT_SELECTED) && (uSubItemFlags & LVDI_HOTSELECTED))
  382. textflags |= SHDT_HOTSELECTED;
  383. if( plv->dwExStyle & WS_EX_RTLREADING)
  384. {
  385. //
  386. // temp hack for the find.files to see if LtoR/RtoL mixing
  387. // works. if ok, we'll take this out and make that lv ownerdraw
  388. //
  389. if ((item.pszText[0] != '\xfd') && (item.pszText[lstrlen(item.pszText)-1] != '\xfd'))
  390. textflags |= SHDT_RTLREADING;
  391. }
  392. //
  393. // If the app customized the font, we need to get the new
  394. // ellipsis size. We could try to optimize not doing this
  395. // if ellipses aren't needed, but tough. That's what you
  396. // get if you use customdraw.
  397. //
  398. if ((plvdi->dwCustom | dwCustom) & CDRF_NEWFONT)
  399. {
  400. SIZE siz;
  401. GetTextExtentPoint(plvdi->nmcd.nmcd.hdc, c_szEllipses, CCHELLIPSES, &siz);
  402. cxEllipses = siz.cx;
  403. }
  404. else
  405. cxEllipses = plv->cxEllipses;
  406. SHDrawText(plvdi->nmcd.nmcd.hdc, item.pszText, &rcLabel,
  407. hitem.fmt & HDF_JUSTIFYMASK, textflags,
  408. plv->cyLabelChar, cxEllipses,
  409. clrText, clrTextBk);
  410. // draw a focus rect on the first column of a focus item
  411. if ((uSubItemFlags & LVDI_FOCUS) &&
  412. (item.state & LVIS_FOCUSED) &&
  413. !(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS))
  414. {
  415. if (ListView_FullRowSelect(plv))
  416. {
  417. fDrawFocusRect = TRUE;
  418. // if we're doing a full row selection, collect the union
  419. // of the labels for the focus rect
  420. UnionRect(&rcT, &rcT, &rcLabel);
  421. }
  422. else
  423. {
  424. DrawFocusRect(plvdi->nmcd.nmcd.hdc, &rcLabel);
  425. }
  426. }
  427. // restore the font
  428. if (hFontTemp)
  429. SelectFont(plvdi->nmcd.nmcd.hdc, hFontTemp);
  430. }
  431. }
  432. if (dwCustom & CDRF_NOTIFYPOSTPAINT)
  433. {
  434. CICustomDrawNotify(&plvdi->plv->ci, CDDS_SUBITEM | CDDS_ITEMPOSTPAINT, &plvdi->nmcd.nmcd);
  435. }
  436. plv->clrBk = crBkSave;
  437. }
  438. if (fDrawFocusRect)
  439. {
  440. DrawFocusRect(plvdi->nmcd.nmcd.hdc, &rcT);
  441. }
  442. return TRUE;
  443. }
  444. BOOL_PTR ListView_CreateHeader(LV* plv)
  445. {
  446. // enable drag drop always here... just fail the notify
  447. // if the bit in listview isn't set
  448. DWORD dwStyle = HDS_HORZ | WS_CHILD | HDS_DRAGDROP;
  449. if (plv->ci.style & LVS_NOCOLUMNHEADER)
  450. dwStyle |= HDS_HIDDEN;
  451. if (!(plv->ci.style & LVS_NOSORTHEADER))
  452. dwStyle |= HDS_BUTTONS;
  453. dwStyle |= HDS_FULLDRAG;
  454. plv->hwndHdr = CreateWindowEx(0L, c_szHeaderClass, // WC_HEADER,
  455. NULL, dwStyle, 0, 0, 0, 0, plv->ci.hwnd, (HMENU)LVID_HEADER, GetWindowInstance(plv->ci.hwnd), NULL);
  456. if (plv->hwndHdr)
  457. {
  458. FORWARD_WM_SETFONT(plv->hwndHdr, plv->hfontLabel, FALSE, SendMessage);
  459. if (plv->himlSmall)
  460. SendMessage(plv->hwndHdr, HDM_SETIMAGELIST, 0, (LPARAM)plv->himlSmall);
  461. }
  462. return (BOOL_PTR)plv->hwndHdr;
  463. }
  464. int ListView_OnInsertColumnA(LV* plv, int iCol, LV_COLUMNA * pcol)
  465. {
  466. LPWSTR pszW = NULL;
  467. LPSTR pszC = NULL;
  468. int iRet;
  469. //HACK ALERT -- this code assumes that LV_COLUMNA is exactly the same
  470. // as LV_COLUMNW except for the pointer to the string.
  471. ASSERT(sizeof(LV_COLUMNA) == sizeof(LV_COLUMNW));
  472. if (!pcol)
  473. return -1;
  474. if ((pcol->mask & LVCF_TEXT) && (pcol->pszText != NULL))
  475. {
  476. pszC = pcol->pszText;
  477. if ((pszW = ProduceWFromA(plv->ci.uiCodePage, pszC)) == NULL)
  478. {
  479. // NT's IE4 returned -1, so we keep doing it in IE5.
  480. return -1;
  481. }
  482. else
  483. {
  484. pcol->pszText = (LPSTR)pszW;
  485. }
  486. }
  487. iRet = ListView_OnInsertColumn(plv, iCol, (const LV_COLUMN*) pcol);
  488. if (pszW != NULL)
  489. {
  490. pcol->pszText = pszC;
  491. FreeProducedString(pszW);
  492. }
  493. return iRet;
  494. }
  495. int ListView_OnInsertColumn(LV* plv, int iCol, const LV_COLUMN* pcol)
  496. {
  497. int idpa = -1;
  498. HD_ITEM item;
  499. ASSERT(LVCFMT_LEFT == HDF_LEFT);
  500. ASSERT(LVCFMT_RIGHT == HDF_RIGHT);
  501. ASSERT(LVCFMT_CENTER == HDF_CENTER);
  502. if (iCol < 0 || !pcol)
  503. return -1;
  504. if (!plv->hwndHdr && !ListView_CreateHeader(plv))
  505. return -1;
  506. item.mask = (HDI_WIDTH | HDI_HEIGHT | HDI_FORMAT | HDI_LPARAM);
  507. if (pcol->mask & LVCF_IMAGE) {
  508. // do this only if this bit is set so that we don't fault on
  509. // old binaries
  510. item.iImage = pcol->iImage;
  511. item.mask |= HDI_IMAGE;
  512. }
  513. if (pcol->mask & LVCF_TEXT) {
  514. item.pszText = pcol->pszText;
  515. item.mask |= HDI_TEXT;
  516. }
  517. if (pcol->mask & LVCF_ORDER) {
  518. item.iOrder = pcol->iOrder;
  519. item.mask |= HDI_ORDER;
  520. }
  521. item.cxy = pcol->mask & LVCF_WIDTH ? pcol->cx : 10; // some random default
  522. item.fmt = ((pcol->mask & LVCF_FMT) && (iCol > 0)) ? pcol->fmt : LVCFMT_LEFT;
  523. item.hbm = NULL;
  524. item.lParam = pcol->mask & LVCF_SUBITEM ? pcol->iSubItem : 0;
  525. // Column 0 refers to the item list. If we've already added a
  526. // column, make sure there are plv->cCol - 1 subitem ptr slots
  527. // in hdpaSubItems...
  528. //
  529. if (plv->cCol > 0)
  530. {
  531. if (!plv->hdpaSubItems)
  532. {
  533. plv->hdpaSubItems = DPA_CreateEx(8, plv->hheap);
  534. if (!plv->hdpaSubItems)
  535. return -1;
  536. }
  537. // WARNING: the max(0, iCol-1) was min in Win95, which was
  538. // just wrong. hopefully(!) no one has relied on this brokeness
  539. // if so, we may have to version switch it.
  540. idpa = DPA_InsertPtr(plv->hdpaSubItems, max(0, iCol - 1), NULL);
  541. if (idpa == -1)
  542. return -1;
  543. }
  544. iCol = Header_InsertItem(plv->hwndHdr, iCol, &item);
  545. if (iCol == -1)
  546. {
  547. if (plv->hdpaSubItems && (idpa != -1))
  548. DPA_DeletePtr(plv->hdpaSubItems, idpa);
  549. return -1;
  550. }
  551. plv->xTotalColumnWidth = RECOMPUTE;
  552. plv->cCol++;
  553. ListView_UpdateScrollBars(plv);
  554. if (ListView_IsReportView(plv) && ListView_RedrawEnabled(plv)) {
  555. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  556. }
  557. return iCol;
  558. }
  559. int ListView_FreeColumnData(LPVOID d, LPVOID p)
  560. {
  561. PLISTSUBITEM plsi = (PLISTSUBITEM)d;
  562. ListView_FreeSubItem(plsi);
  563. return 1;
  564. }
  565. BOOL ListView_OnDeleteColumn(LV* plv, int iCol)
  566. {
  567. if (iCol < 0 || iCol >= plv->cCol) // validate column index
  568. {
  569. RIPMSG(0, "LVM_DELETECOLUMN: Invalid column index: %d", iCol);
  570. return FALSE;
  571. }
  572. if (plv->hdpaSubItems)
  573. {
  574. int iDeleteColumn = iCol; // This is the column we want to delete
  575. if (iCol == 0 && // Trying to delete column Zero?
  576. plv->cCol >= 2 && // Do we have two or more columns?
  577. !ListView_IsOwnerData(plv))
  578. {
  579. // if deleting column 0,
  580. // we have to do something a little special...
  581. // set all item 0 strings to what column 1 has and
  582. // delete column 1
  583. int i;
  584. int iCount = ListView_Count(plv);
  585. for (i = 0; i < iCount; i++)
  586. {
  587. LISTSUBITEM lsi;
  588. LVITEM lvi;
  589. ListView_GetSubItem(plv, i, 1, &lsi);
  590. lvi.iSubItem = 0;
  591. lvi.iItem = i;
  592. lvi.mask = LVIF_TEXT | LVIF_IMAGE;
  593. lvi.iImage = lsi.iImage;
  594. lvi.pszText = lsi.pszText;
  595. lvi.state = lsi.state;
  596. lvi.stateMask = 0xffffffff;
  597. ListView_OnSetItem(plv, &lvi);
  598. }
  599. iDeleteColumn = 1;
  600. }
  601. if (iDeleteColumn > 0)
  602. {
  603. HDPA hdpa = (HDPA)DPA_DeletePtr(plv->hdpaSubItems, iDeleteColumn - 1);
  604. DPA_DestroyCallback(hdpa, ListView_FreeColumnData, 0);
  605. }
  606. }
  607. if (!Header_DeleteItem(plv->hwndHdr, iCol))
  608. return FALSE;
  609. plv->cCol--;
  610. plv->xTotalColumnWidth = RECOMPUTE;
  611. ListView_UpdateScrollBars(plv);
  612. if (ListView_IsReportView(plv) && ListView_RedrawEnabled(plv))
  613. {
  614. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  615. }
  616. return TRUE;
  617. }
  618. int ListView_RGetColumnWidth(LV* plv, int iCol)
  619. {
  620. HD_ITEM item = {0};
  621. item.mask = HDI_WIDTH;
  622. Header_GetItem(plv->hwndHdr, iCol, &item);
  623. return item.cxy;
  624. }
  625. // The FakeCustomDraw functions are used when you want the customdraw client
  626. // to set up a HDC so you can do stuff like GetTextExtent.
  627. //
  628. // Usage:
  629. //
  630. // LVFAKEDRAW lvfd;
  631. // LV_ITEM item;
  632. // ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  633. // for each item you care about {
  634. // item.iItem = iItem;
  635. // item.iItem = iSubItem;
  636. // item.lParam = <item lParam>; // use ListView_OnGetItem to get it
  637. // ListView_BeginFakeItemDraw(&lvfd);
  638. // <party on the HDC in lvfd.nmcd.nmcd.hdc>
  639. // ListView_EndFakeItemDraw(&lvfd);
  640. // }
  641. // ListView_EndFakeCustomDraw(&lvfd);
  642. //
  643. void ListView_BeginFakeCustomDraw(LV* plv, PLVFAKEDRAW plvfd, LV_ITEM *pitem)
  644. {
  645. plvfd->nmcd.nmcd.hdc = GetDC(plv->ci.hwnd);
  646. plvfd->nmcd.nmcd.uItemState = 0;
  647. plvfd->nmcd.nmcd.dwItemSpec = 0;
  648. plvfd->nmcd.nmcd.lItemlParam = 0;
  649. plvfd->hfontPrev = SelectFont(plvfd->nmcd.nmcd.hdc, plv->hfontLabel);
  650. plvfd->nmcd.dwItemType = 0;
  651. //
  652. // Since we aren't actually painting anything, we pass an empty
  653. // paint rectangle. Gosh, I hope no app faults when it sees an
  654. // empty paint rectangle.
  655. //
  656. SetRectEmpty(&plvfd->nmcd.nmcd.rc);
  657. plvfd->plv = plv;
  658. plvfd->dwCustomPrev = plv->ci.dwCustom;
  659. plvfd->pitem = pitem;
  660. plv->ci.dwCustom = CIFakeCustomDrawNotify(&plv->ci, CDDS_PREPAINT, &plvfd->nmcd.nmcd);
  661. }
  662. DWORD ListView_BeginFakeItemDraw(PLVFAKEDRAW plvfd)
  663. {
  664. LV *plv = plvfd->plv;
  665. LV_ITEM *pitem;
  666. // Early-out: If client doesn't use CustomDraw, then stop immediately.
  667. if (!(plv->ci.dwCustom & CDRF_NOTIFYITEMDRAW))
  668. return CDRF_DODEFAULT;
  669. pitem = plvfd->pitem;
  670. // Note that if the client says CDRF_SKIPDEFAULT (i.e., is owner-draw)
  671. // we measure the item anyway, because that's what IE4 did.
  672. // Make sure we do have the lParam. Office will fault if you give
  673. // bogus lParams during customdraw callbacks.
  674. plvfd->nmcd.nmcd.dwItemSpec = pitem->iItem;
  675. if (ListView_IsOwnerData(plv))
  676. {
  677. // OwnerData always gets lItemlParam = 0
  678. ASSERT(plvfd->nmcd.nmcd.lItemlParam == 0); // should still be 0
  679. } else {
  680. ASSERT(pitem->mask & LVIF_PARAM);
  681. plvfd->nmcd.nmcd.lItemlParam = pitem->lParam;
  682. }
  683. if (!(plv->ci.dwCustom & CDRF_SKIPDEFAULT)) {
  684. plvfd->nmcd.iSubItem = 0;
  685. plvfd->dwCustomItem = CIFakeCustomDrawNotify(&plv->ci, CDDS_ITEMPREPAINT, &plvfd->nmcd.nmcd);
  686. } else {
  687. plvfd->dwCustomItem = CDRF_DODEFAULT;
  688. }
  689. //
  690. // Only report view supports sub-items.
  691. //
  692. if (!ListView_IsReportView(plv))
  693. plvfd->dwCustomItem &= ~CDRF_NOTIFYSUBITEMDRAW;
  694. if (plvfd->dwCustomItem & CDRF_NOTIFYSUBITEMDRAW) {
  695. plvfd->nmcd.iSubItem = pitem->iSubItem;
  696. plvfd->dwCustomSubItem = CIFakeCustomDrawNotify(&plv->ci, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &plvfd->nmcd.nmcd);
  697. } else {
  698. plvfd->dwCustomSubItem = CDRF_DODEFAULT;
  699. }
  700. return plvfd->dwCustomItem | plvfd->dwCustomSubItem;
  701. }
  702. void ListView_EndFakeItemDraw(PLVFAKEDRAW plvfd)
  703. {
  704. LV *plv = plvfd->plv;
  705. // Early-out: If client doesn't use CustomDraw, then stop immediately.
  706. if (!(plv->ci.dwCustom & CDRF_NOTIFYITEMDRAW))
  707. return;
  708. if (!(plvfd->dwCustomSubItem & CDRF_SKIPDEFAULT) &&
  709. (plvfd->dwCustomSubItem & CDRF_NOTIFYPOSTPAINT)) {
  710. ASSERT(plvfd->dwCustomItem & CDRF_NOTIFYSUBITEMDRAW);
  711. ASSERT(plvfd->nmcd.iSubItem == plvfd->pitem->iSubItem);
  712. CIFakeCustomDrawNotify(&plv->ci, CDDS_SUBITEM | CDDS_ITEMPOSTPAINT, &plvfd->nmcd.nmcd);
  713. }
  714. if ((plvfd->dwCustomItem | plvfd->dwCustomSubItem) & CDRF_NEWFONT) // App changed font, so
  715. SelectFont(plvfd->nmcd.nmcd.hdc, plv->hfontLabel); // restore default font
  716. if (!(plvfd->dwCustomItem & CDRF_SKIPDEFAULT) &&
  717. (plvfd->dwCustomItem & CDRF_NOTIFYPOSTPAINT)) {
  718. plvfd->nmcd.iSubItem = 0;
  719. CIFakeCustomDrawNotify(&plv->ci, CDDS_ITEMPOSTPAINT, &plvfd->nmcd.nmcd);
  720. }
  721. }
  722. void ListView_EndFakeCustomDraw(PLVFAKEDRAW plvfd)
  723. {
  724. LV *plv = plvfd->plv;
  725. // notify parent afterwards if they want us to
  726. if (!(plv->ci.dwCustom & CDRF_SKIPDEFAULT) &&
  727. plv->ci.dwCustom & CDRF_NOTIFYPOSTPAINT) {
  728. CIFakeCustomDrawNotify(&plv->ci, CDDS_POSTPAINT, &plvfd->nmcd.nmcd);
  729. }
  730. // Restore previous state
  731. plv->ci.dwCustom = plvfd->dwCustomPrev;
  732. SelectObject(plvfd->nmcd.nmcd.hdc, plvfd->hfontPrev);
  733. ReleaseDC(plv->ci.hwnd, plvfd->nmcd.nmcd.hdc);
  734. }
  735. BOOL hasVertScroll
  736. (
  737. LV* plv
  738. )
  739. {
  740. RECT rcClient;
  741. RECT rcBounds;
  742. int cColVis;
  743. BOOL fHorSB;
  744. // Get the horizontal bounds of the items.
  745. ListView_GetClientRect(plv, &rcClient, FALSE, NULL);
  746. ListView_RGetRects(plv, 0, NULL, NULL, &rcBounds, NULL);
  747. fHorSB = (rcBounds.right - rcBounds.left > rcClient.right);
  748. cColVis = (rcClient.bottom - plv->yTop -
  749. (fHorSB ? ListView_GetCyScrollbar(plv) : 0)) / plv->cyItem;
  750. // check to see if we need a vert scrollbar
  751. if ((int)cColVis < ListView_Count(plv))
  752. return(TRUE);
  753. else
  754. return(FALSE);
  755. }
  756. BOOL ListView_RSetColumnWidth(LV* plv, int iCol, int cx)
  757. {
  758. HD_ITEM item;
  759. HD_ITEM colitem;
  760. SIZE siz;
  761. LV_ITEM lviItem;
  762. int i;
  763. int ItemWidth = 0;
  764. int HeaderWidth = 0;
  765. TCHAR szLabel[CCHLABELMAX + 4]; // CCHLABLEMAX == MAX_PATH
  766. int iBegin;
  767. int iEnd;
  768. // Should we compute the width based on the widest string?
  769. // If we do, include the Width of the Label, and if this is the
  770. // Last column, set the width so the right side is at the list view's right edge
  771. if (cx <= LVSCW_AUTOSIZE)
  772. {
  773. LVFAKEDRAW lvfd; // in case client uses customdraw
  774. if (cx == LVSCW_AUTOSIZE_USEHEADER)
  775. {
  776. // Special Cases:
  777. // 1) There is only 1 column. Set the width to the width of the listview
  778. // 2) This is the rightmost column, set the width so the right edge of the
  779. // column coinsides with to right edge of the list view.
  780. if (plv->cCol == 1)
  781. {
  782. RECT rcClient;
  783. ListView_GetClientRect(plv, &rcClient, FALSE, NULL);
  784. HeaderWidth = rcClient.right - rcClient.left;
  785. }
  786. else if (iCol == (plv->cCol-1))
  787. {
  788. // REARCHITECT: This will only work if the listview as NOT
  789. // been previously horizontally scrolled
  790. RECT rcClient;
  791. RECT rcHeader;
  792. ListView_GetClientRect(plv, &rcClient, FALSE, NULL);
  793. if (!Header_GetItemRect(plv->hwndHdr, plv->cCol - 2, &rcHeader))
  794. rcHeader.right = 0;
  795. // Is if visible
  796. if (rcHeader.right < (rcClient.right-rcClient.left))
  797. {
  798. HeaderWidth = (rcClient.right-rcClient.left) - rcHeader.right;
  799. }
  800. }
  801. // If we have a header width, then is is one of these special ones, so
  802. // we need to account for a vert scroll bar since we are using Client values
  803. if (HeaderWidth && hasVertScroll(plv))
  804. {
  805. HeaderWidth -= g_cxVScroll;
  806. }
  807. // Get the Width of the label.
  808. // We assume that the app hasn't changed any attributes
  809. // of the header control - still has default font, margins, etc.
  810. colitem.mask = HDI_TEXT | HDI_FORMAT;
  811. colitem.pszText = szLabel;
  812. colitem.cchTextMax = ARRAYSIZE(szLabel);
  813. if (Header_GetItem(plv->hwndHdr, iCol, &colitem))
  814. {
  815. HTHEME hThemeHeader;
  816. HDC hdc = GetDC(plv->ci.hwnd);
  817. HFONT hfPrev = SelectFont(hdc, plv->hfontLabel);
  818. GetTextExtentPoint(hdc, colitem.pszText,
  819. lstrlen(colitem.pszText), &siz);
  820. siz.cx += 2 * (3 * g_cxLabelMargin); // phd->iTextMargin
  821. if (colitem.fmt & HDF_IMAGE)
  822. {
  823. siz.cx += plv->cxSmIcon;
  824. siz.cx += 2 * (3 * g_cxLabelMargin); // pdh->iBmMargin
  825. }
  826. hThemeHeader = OpenThemeData(plv->hwndHdr, L"Header");
  827. if (hThemeHeader)
  828. {
  829. RECT rc = {0, 0, siz.cx, siz.cy};
  830. GetThemeBackgroundExtent(hThemeHeader, hdc, HP_HEADERITEM, 0, &rc, &rc);
  831. siz.cx = RECTWIDTH(rc);
  832. siz.cy = RECTHEIGHT(rc);
  833. CloseThemeData(hThemeHeader);
  834. }
  835. HeaderWidth = max(HeaderWidth, siz.cx);
  836. SelectFont(hdc, hfPrev);
  837. ReleaseDC(plv->ci.hwnd, hdc);
  838. }
  839. }
  840. iBegin = 0;
  841. iEnd = ListView_Count( plv );
  842. //
  843. // Loop for each item in the view
  844. //
  845. if (ListView_IsOwnerData( plv ))
  846. {
  847. iBegin = (int)((plv->ptlRptOrigin.y - plv->yTop)
  848. / plv->cyItem);
  849. iEnd = (int)((plv->ptlRptOrigin.y + plv->sizeClient.cy - plv->yTop)
  850. / plv->cyItem) + 1;
  851. iBegin = max( 0, iBegin );
  852. iEnd = max(iEnd, iBegin + 1);
  853. iEnd = min( iEnd, ListView_Count( plv ) );
  854. ListView_NotifyCacheHint( plv, iBegin, iEnd-1 );
  855. }
  856. //
  857. // To obtain the widths of the strings, we have to pretend that
  858. // we are painting them, in case the custom-draw client wants
  859. // to play with fonts (e.g., Athena).
  860. //
  861. ListView_BeginFakeCustomDraw(plv, &lvfd, &lviItem);
  862. //
  863. // If column 0, then we also need to take indent into account.
  864. //
  865. lviItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  866. if (iCol == 0) {
  867. lviItem.mask |= LVIF_INDENT;
  868. }
  869. // Loop for each item in the List
  870. for (i = iBegin; i < iEnd; i++)
  871. {
  872. lviItem.iImage = -1;
  873. lviItem.iItem = i;
  874. lviItem.iSubItem = iCol;
  875. lviItem.pszText = szLabel;
  876. lviItem.cchTextMax = ARRAYSIZE(szLabel);
  877. lviItem.iIndent = 0;
  878. lviItem.stateMask = 0;
  879. ListView_OnGetItem(plv, &lviItem);
  880. // If there is a Text item, get its width
  881. if (lviItem.pszText || (lviItem.iImage != -1))
  882. {
  883. if (lviItem.pszText)
  884. {
  885. ListView_BeginFakeItemDraw(&lvfd);
  886. GetTextExtentPoint(lvfd.nmcd.nmcd.hdc, lviItem.pszText,
  887. lstrlen(lviItem.pszText), &siz);
  888. ListView_EndFakeItemDraw(&lvfd);
  889. }
  890. else
  891. {
  892. siz.cx = 0;
  893. }
  894. if (lviItem.iImage != -1)
  895. {
  896. siz.cx += plv->cxSmIcon + g_cxEdge + LV_ICONINDENT;
  897. }
  898. siz.cx += lviItem.iIndent * plv->cxSmIcon;
  899. ItemWidth = max(ItemWidth, siz.cx);
  900. }
  901. }
  902. ListView_EndFakeCustomDraw(&lvfd);
  903. // Adjust by a reasonable border amount.
  904. // If col 0, add 2*g_cxLabelMargin + g_szSmIcon.
  905. // Otherwise add 6*g_cxLabelMargin.
  906. // These amounts are based on Margins added automatically
  907. // to the ListView in ShDrawText.
  908. // REARCHITECT ListView Report format currently assumes and makes
  909. // room for a Small Icon.
  910. if (iCol == 0)
  911. {
  912. ItemWidth += plv->cxState + LV_ICONTOSTATEOFFSET(plv) + g_cxEdge;
  913. ItemWidth += 2*g_cxLabelMargin;
  914. }
  915. else
  916. {
  917. ItemWidth += 6*g_cxLabelMargin;
  918. }
  919. TraceMsg(TF_LISTVIEW, "ListView: HeaderWidth:%d ItemWidth:%d", HeaderWidth, ItemWidth);
  920. item.cxy = max(HeaderWidth, ItemWidth);
  921. }
  922. else
  923. {
  924. // Use supplied width
  925. item.cxy = cx;
  926. }
  927. plv->xTotalColumnWidth = RECOMPUTE;
  928. item.mask = HDI_WIDTH;
  929. return Header_SetItem(plv->hwndHdr, iCol, &item);
  930. }
  931. BOOL ListView_OnGetColumnA(LV* plv, int iCol, LV_COLUMNA* pcol)
  932. {
  933. LPWSTR pszW = NULL;
  934. LPSTR pszC = NULL;
  935. BOOL fRet;
  936. //HACK ALERT -- this code assumes that LV_COLUMNA is exactly the same
  937. // as LV_COLUMNW except for the pointer to the string.
  938. ASSERT(sizeof(LV_COLUMNA) == sizeof(LV_COLUMNW))
  939. if (!pcol) return FALSE;
  940. if ((pcol->mask & LVCF_TEXT) && (pcol->pszText != NULL))
  941. {
  942. pszC = pcol->pszText;
  943. pszW = LocalAlloc(LMEM_FIXED, pcol->cchTextMax * sizeof(WCHAR));
  944. if (pszW == NULL)
  945. return FALSE;
  946. pcol->pszText = (LPSTR)pszW;
  947. }
  948. fRet = ListView_OnGetColumn(plv, iCol, (LV_COLUMN*) pcol);
  949. if (pszW != NULL)
  950. {
  951. if (fRet && pcol->cchTextMax)
  952. ConvertWToAN(plv->ci.uiCodePage, pszC, pcol->cchTextMax, pszW, -1);
  953. pcol->pszText = pszC;
  954. LocalFree(pszW);
  955. }
  956. return fRet;
  957. }
  958. BOOL ListView_OnGetColumn(LV* plv, int iCol, LV_COLUMN* pcol)
  959. {
  960. HD_ITEM item;
  961. UINT mask;
  962. if (!pcol)
  963. {
  964. RIPMSG(0, "LVM_GETCOLUMN: Invalid pcol = NULL");
  965. return FALSE;
  966. }
  967. mask = pcol->mask;
  968. if (!mask)
  969. return TRUE;
  970. item.mask = HDI_FORMAT | HDI_WIDTH | HDI_LPARAM | HDI_ORDER | HDI_IMAGE;
  971. if (mask & LVCF_TEXT)
  972. {
  973. if (pcol->pszText)
  974. {
  975. item.mask |= HDI_TEXT;
  976. item.pszText = pcol->pszText;
  977. item.cchTextMax = pcol->cchTextMax;
  978. } else {
  979. // For compatibility reasons, we don't fail the call if they
  980. // pass NULL.
  981. RIPMSG(0, "LVM_GETCOLUMN: Invalid pcol->pszText = NULL");
  982. }
  983. }
  984. if (!Header_GetItem(plv->hwndHdr, iCol, &item))
  985. {
  986. RIPMSG(0, "LVM_GETCOLUMN: Invalid column number %d", iCol);
  987. return FALSE;
  988. }
  989. if (mask & LVCF_SUBITEM)
  990. pcol->iSubItem = (int)item.lParam;
  991. if (mask & LVCF_ORDER)
  992. pcol->iOrder = (int)item.iOrder;
  993. if (mask & LVCF_IMAGE)
  994. pcol->iImage = item.iImage;
  995. if (mask & LVCF_FMT)
  996. pcol->fmt = item.fmt;
  997. if (mask & LVCF_WIDTH)
  998. pcol->cx = item.cxy;
  999. return TRUE;
  1000. }
  1001. BOOL ListView_OnSetColumnA(LV* plv, int iCol, LV_COLUMNA* pcol)
  1002. {
  1003. LPWSTR pszW = NULL;
  1004. LPSTR pszC = NULL;
  1005. BOOL fRet;
  1006. //HACK ALERT -- this code assumes that LV_COLUMNA is exactly the same
  1007. // as LV_COLUMNW except for the pointer to the string.
  1008. ASSERT(sizeof(LV_COLUMNA) == sizeof(LV_COLUMNW));
  1009. if (!pcol) return FALSE;
  1010. if ((pcol->mask & LVCF_TEXT) && (pcol->pszText != NULL))
  1011. {
  1012. pszC = pcol->pszText;
  1013. if ((pszW = ProduceWFromA(plv->ci.uiCodePage, pszC)) == NULL)
  1014. return FALSE;
  1015. pcol->pszText = (LPSTR)pszW;
  1016. }
  1017. fRet = ListView_OnSetColumn(plv, iCol, (const LV_COLUMN*) pcol);
  1018. if (pszW != NULL) {
  1019. pcol->pszText = pszC;
  1020. FreeProducedString(pszW);
  1021. }
  1022. return fRet;
  1023. }
  1024. BOOL ListView_OnSetColumn(LV* plv, int iCol, const LV_COLUMN* pcol)
  1025. {
  1026. HD_ITEM item;
  1027. UINT mask;
  1028. if (!pcol) return FALSE;
  1029. mask = pcol->mask;
  1030. if (!mask)
  1031. return TRUE;
  1032. item.mask = 0;
  1033. if (mask & LVCF_SUBITEM)
  1034. {
  1035. item.mask |= HDI_LPARAM;
  1036. item.lParam = iCol;
  1037. }
  1038. if (mask & LVCF_FMT)
  1039. {
  1040. item.mask |= HDI_FORMAT;
  1041. item.fmt = (pcol->fmt | HDF_STRING);
  1042. }
  1043. if (mask & LVCF_WIDTH)
  1044. {
  1045. item.mask |= HDI_WIDTH;
  1046. item.cxy = pcol->cx;
  1047. }
  1048. if (mask & LVCF_TEXT)
  1049. {
  1050. RIPMSG(pcol->pszText != NULL, "LVM_SETCOLUMN: LV_COLUMN.pszText should not be NULL");
  1051. item.mask |= HDI_TEXT;
  1052. item.pszText = pcol->pszText;
  1053. item.cchTextMax = 0;
  1054. }
  1055. if (mask & LVCF_IMAGE)
  1056. {
  1057. item.mask |= HDI_IMAGE;
  1058. item.iImage = pcol->iImage;
  1059. }
  1060. if (mask & LVCF_ORDER)
  1061. {
  1062. item.mask |= HDI_ORDER;
  1063. item.iOrder = pcol->iOrder;
  1064. }
  1065. plv->xTotalColumnWidth = RECOMPUTE;
  1066. return Header_SetItem(plv->hwndHdr, iCol, &item);
  1067. }
  1068. BOOL ListView_SetSubItem(LV* plv, const LV_ITEM* plvi)
  1069. {
  1070. LISTSUBITEM lsi;
  1071. BOOL fChanged = FALSE;
  1072. int i;
  1073. int idpa;
  1074. HDPA hdpa;
  1075. if (plvi->mask & ~(LVIF_DI_SETITEM | LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))
  1076. {
  1077. RIPMSG(0, "ListView: Invalid mask: %04x", plvi->mask);
  1078. return FALSE;
  1079. }
  1080. if (!(plvi->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)))
  1081. return TRUE;
  1082. i = plvi->iItem;
  1083. if (!ListView_IsValidItemNumber(plv, i))
  1084. {
  1085. RIPMSG(0, "LVM_SETITEM: Invalid iItem: %d", plvi->iItem);
  1086. return FALSE;
  1087. }
  1088. // sub item indices are 1-based...
  1089. //
  1090. idpa = plvi->iSubItem - 1;
  1091. if (idpa < 0 || idpa >= plv->cCol - 1)
  1092. {
  1093. RIPMSG(0, "LVM_SETITEM: Invalid iSubItem: %d", plvi->iSubItem);
  1094. return FALSE;
  1095. }
  1096. hdpa = ListView_GetSubItemDPA(plv, idpa);
  1097. if (!hdpa)
  1098. {
  1099. hdpa = DPA_CreateEx(LV_HDPA_GROW, plv->hheap);
  1100. if (!hdpa)
  1101. return FALSE;
  1102. DPA_SetPtr(plv->hdpaSubItems, idpa, (void*)hdpa);
  1103. }
  1104. ListView_GetSubItem(plv, i, plvi->iSubItem, &lsi);
  1105. if (plvi->mask & LVIF_TEXT) {
  1106. if (lsi.pszText != plvi->pszText) {
  1107. Str_Set(&lsi.pszText, plvi->pszText);
  1108. fChanged = TRUE;
  1109. }
  1110. }
  1111. if (plvi->mask & LVIF_IMAGE) {
  1112. if (plvi->iImage != lsi.iImage) {
  1113. lsi.iImage = (short) plvi->iImage;
  1114. fChanged = TRUE;
  1115. }
  1116. }
  1117. if (plvi->mask & LVIF_STATE) {
  1118. DWORD dwChange;
  1119. dwChange = (lsi.state ^ plvi->state ) & plvi->stateMask;
  1120. if (dwChange) {
  1121. lsi.state ^= dwChange;
  1122. fChanged = TRUE;
  1123. }
  1124. }
  1125. if (fChanged) {
  1126. PLISTSUBITEM plsiReal = DPA_GetPtr(hdpa, i);
  1127. if (!plsiReal) {
  1128. plsiReal = LocalAlloc(LPTR, sizeof(LISTSUBITEM));
  1129. if (!plsiReal) {
  1130. // fail! bail out
  1131. return FALSE;
  1132. }
  1133. }
  1134. *plsiReal = lsi;
  1135. if (!DPA_SetPtr(hdpa, i, (void*)plsiReal)) {
  1136. ListView_FreeSubItem(plsiReal);
  1137. return FALSE;
  1138. }
  1139. }
  1140. // all's well... let's invalidate this
  1141. if (ListView_IsReportView(plv)) {
  1142. RECT rc;
  1143. ListView_RGetRectsEx(plv, plvi->iItem, plvi->iSubItem, NULL, &rc);
  1144. RedrawWindow(plv->ci.hwnd, &rc, NULL, RDW_ERASE | RDW_INVALIDATE);
  1145. }
  1146. else if (ListView_IsTileView(plv))
  1147. {
  1148. LISTITEM *pitem = ListView_GetItemPtr(plv, i);
  1149. if (pitem)
  1150. {
  1151. ListView_SetSRecompute(pitem);
  1152. // For tile view, we need to recompute the item
  1153. plv->rcView.left = RECOMPUTE;
  1154. if (plv->iItemDrawing != i)
  1155. ListView_InvalidateItemEx(plv, i, FALSE, RDW_ERASE | RDW_INVALIDATE, LVIF_TEXT);
  1156. }
  1157. }
  1158. return TRUE;
  1159. }
  1160. int ListView_RDestroyColumn(LPVOID d, LPVOID p)
  1161. {
  1162. HDPA hdpa = (HDPA)d;
  1163. DPA_DestroyCallback(hdpa, ListView_FreeColumnData, 0);
  1164. return 1;
  1165. }
  1166. void ListView_RDestroy(LV* plv)
  1167. {
  1168. DPA_DestroyCallback(plv->hdpaSubItems, ListView_RDestroyColumn, 0);
  1169. plv->hdpaSubItems = NULL;
  1170. }
  1171. VOID ListView_RHeaderTrack(LV* plv, HD_NOTIFY * pnm)
  1172. {
  1173. // We want to update to show where the column header will be.
  1174. HDC hdc;
  1175. RECT rcBounds;
  1176. // Statics needed from call to call
  1177. static int s_xLast = -32767;
  1178. hdc = GetDC(plv->ci.hwnd);
  1179. if (hdc == NULL)
  1180. return;
  1181. //
  1182. // First undraw the last marker we drew.
  1183. //
  1184. if (s_xLast > 0)
  1185. {
  1186. PatBlt(hdc, s_xLast, plv->yTop, g_cxBorder, plv->sizeClient.cy - plv->yTop, PATINVERT);
  1187. }
  1188. if (pnm->hdr.code == HDN_ENDTRACK)
  1189. {
  1190. s_xLast = -32767; // Some large negative number...
  1191. }
  1192. else
  1193. {
  1194. RECT rc;
  1195. //
  1196. // First we need to calculate the X location of the column
  1197. // To do this, we will need to know where this column begins
  1198. // Note: We need the bounding rects to help us know the origin.
  1199. ListView_GetRects(plv, 0, QUERY_DEFAULT, NULL, NULL, &rcBounds, NULL);
  1200. if (!Header_GetItemRect(plv->hwndHdr, pnm->iItem, &rc)) {
  1201. rc.left = 0;
  1202. }
  1203. rcBounds.left += rc.left;
  1204. // Draw the new line...
  1205. s_xLast = rcBounds.left + pnm->pitem->cxy;
  1206. PatBlt(hdc, s_xLast, plv->yTop, g_cxBorder, plv->sizeClient.cy - plv->yTop, PATINVERT);
  1207. }
  1208. ReleaseDC(plv->ci.hwnd, hdc);
  1209. }
  1210. // try to use scrollwindow to adjust the columns rather than erasing
  1211. // and redrawing.
  1212. void ListView_AdjustColumn(LV * plv, int iWidth)
  1213. {
  1214. int x;
  1215. RECT rcClip;
  1216. int dx = iWidth - plv->iSelOldWidth;
  1217. if (iWidth == plv->iSelOldWidth)
  1218. return;
  1219. // find the x coord of the left side of the iCol
  1220. // use rcClip as a temporary...
  1221. if (!Header_GetItemRect(plv->hwndHdr, plv->iSelCol, &rcClip)) {
  1222. x = 0;
  1223. } else {
  1224. x = rcClip.left;
  1225. }
  1226. x -= plv->ptlRptOrigin.x;
  1227. // compute the area to the right of the adjusted column
  1228. GetWindowRect(plv->hwndHdr, &rcClip);
  1229. rcClip.left = x;
  1230. rcClip.top = RECTHEIGHT(rcClip);
  1231. rcClip.right = plv->sizeClient.cx;
  1232. rcClip.bottom = plv->sizeClient.cy;
  1233. if (plv->fGroupView || ListView_IsWatermarkedBackground(plv) || ListView_IsWatermarked(plv))
  1234. {
  1235. plv->xTotalColumnWidth = RECOMPUTE;
  1236. ListView_UpdateScrollBars(plv);
  1237. RedrawWindow(plv->ci.hwnd, NULL, NULL,
  1238. RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
  1239. }
  1240. else if ((plv->pImgCtx == NULL) &&
  1241. (plv->clrBk != CLR_NONE) &&
  1242. (plv->clrTextBk != CLR_NONE))
  1243. {
  1244. //
  1245. // We have a solid color background,
  1246. // so we can smooth scroll the right side columns.
  1247. //
  1248. SMOOTHSCROLLINFO si = {0};
  1249. si.cbSize = sizeof(si),
  1250. si.hwnd = plv->ci.hwnd,
  1251. si.dx = dx,
  1252. si.lprcClip = &rcClip;
  1253. si.fuScroll = SW_ERASE | SW_INVALIDATE,
  1254. rcClip.left += min(plv->iSelOldWidth, iWidth);
  1255. SmoothScrollWindow(&si);
  1256. // if we shrunk, invalidate the right most edge because
  1257. // there might be junk there
  1258. if (iWidth < plv->iSelOldWidth)
  1259. {
  1260. rcClip.right = rcClip.left + g_cxEdge;
  1261. InvalidateRect(plv->ci.hwnd, &rcClip, TRUE);
  1262. }
  1263. plv->xTotalColumnWidth = RECOMPUTE;
  1264. // adjust clipping rect to only redraw the adjusted column
  1265. rcClip.left = x;
  1266. rcClip.right = max(rcClip.left, x+iWidth);
  1267. // Make the rectangle origin-based because ListView_UpdateScrollBars
  1268. // may scroll us around.
  1269. OffsetRect(&rcClip, plv->ptlRptOrigin.x, plv->ptlRptOrigin.y);
  1270. ListView_UpdateScrollBars(plv);
  1271. // Okay, now convert it back to client coordinates
  1272. OffsetRect(&rcClip, -plv->ptlRptOrigin.x, -plv->ptlRptOrigin.y);
  1273. // call update because scrollwindowex might have erased the right
  1274. // we don't want this invalidate to then enlarge the region
  1275. // and end up erasing everything.
  1276. UpdateWindow(plv->ci.hwnd);
  1277. RedrawWindow(plv->ci.hwnd, &rcClip, NULL,
  1278. RDW_INVALIDATE | RDW_UPDATENOW);
  1279. }
  1280. else
  1281. {
  1282. //
  1283. // We don't have a solid color background,
  1284. // erase and redraw the adjusted column and
  1285. // everything to the right (sigh).
  1286. //
  1287. plv->xTotalColumnWidth = RECOMPUTE;
  1288. ListView_UpdateScrollBars(plv);
  1289. rcClip.left = x;
  1290. RedrawWindow(plv->ci.hwnd, &rcClip, NULL,
  1291. RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
  1292. }
  1293. }
  1294. BOOL ListView_ForwardHeaderNotify(LV* plv, HD_NOTIFY *pnm)
  1295. {
  1296. return BOOLFROMPTR(SendNotifyEx(plv->ci.hwndParent, pnm->hdr.hwndFrom, pnm->hdr.code,
  1297. (NMHDR *)pnm, plv->ci.bUnicode));
  1298. }
  1299. LRESULT ListView_HeaderNotify(LV* plv, HD_NOTIFY *pnm)
  1300. {
  1301. LRESULT lres = 0;
  1302. switch (pnm->hdr.code)
  1303. {
  1304. case HDN_BEGINDRAG:
  1305. if (!(plv->exStyle & LVS_EX_HEADERDRAGDROP))
  1306. return TRUE;
  1307. return ListView_ForwardHeaderNotify(plv, pnm);
  1308. case HDN_ENDDRAG:
  1309. if (pnm->pitem->iOrder != -1) {
  1310. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1311. return ListView_ForwardHeaderNotify(plv, pnm);
  1312. }
  1313. goto DoDefault;
  1314. case HDN_ITEMCHANGING:
  1315. if (pnm->pitem->mask & HDI_WIDTH) {
  1316. HD_ITEM hitem;
  1317. hitem.mask = HDI_WIDTH;
  1318. Header_GetItem(plv->hwndHdr, pnm->iItem, &hitem);
  1319. plv->iSelCol = pnm->iItem;
  1320. plv->iSelOldWidth = hitem.cxy;
  1321. TraceMsg(TF_LISTVIEW, "HDN_ITEMCHANGING %d %d", hitem.cxy, pnm->pitem->cxy);
  1322. return ListView_ForwardHeaderNotify(plv, pnm);
  1323. }
  1324. else if (pnm->pitem->mask & HDI_FILTER) {
  1325. return ListView_ForwardHeaderNotify(plv, pnm);
  1326. }
  1327. goto DoDefault;
  1328. case HDN_ITEMCHANGED:
  1329. if (pnm->pitem->mask & HDI_WIDTH)
  1330. {
  1331. ListView_DismissEdit(plv, FALSE);
  1332. if (pnm->iItem == plv->iSelCol) {
  1333. // Must do this even if there are no items, since
  1334. // we have to redo the scrollbar, and the client
  1335. // may have custom-drawn gridlines or something.
  1336. ListView_AdjustColumn(plv, pnm->pitem->cxy);
  1337. } else {
  1338. // sanity check. we got confused, so redraw all
  1339. RedrawWindow(plv->ci.hwnd, NULL, NULL,
  1340. RDW_ERASE | RDW_INVALIDATE);
  1341. }
  1342. plv->iSelCol = -1;
  1343. lres = ListView_ForwardHeaderNotify(plv, pnm);
  1344. }
  1345. else if (pnm->pitem->mask & HDI_FILTER) {
  1346. lres = ListView_ForwardHeaderNotify(plv, pnm);
  1347. } else
  1348. goto DoDefault;
  1349. break;
  1350. case HDN_ITEMCLICK:
  1351. {
  1352. //
  1353. // Need to pass this and other HDN_ notifications back to
  1354. // parent. Should we simply pass up the HDN notifications
  1355. // or should we define equivlent LVN_ notifications...
  1356. //
  1357. // Pass column number in iSubItem, not iItem...
  1358. //
  1359. NMHEADER* pnmH = (NMHEADER*)pnm;
  1360. ListView_DismissEdit(plv, FALSE);
  1361. ListView_Notify(plv, -1, pnm->iItem, LVN_COLUMNCLICK);
  1362. lres = ListView_ForwardHeaderNotify(plv, pnm);
  1363. SetFocus(plv->ci.hwnd);
  1364. }
  1365. break;
  1366. case HDN_TRACK:
  1367. case HDN_ENDTRACK:
  1368. ListView_DismissEdit(plv, FALSE);
  1369. ListView_RHeaderTrack(plv, pnm);
  1370. lres = ListView_ForwardHeaderNotify(plv, pnm);
  1371. SetFocus(plv->ci.hwnd);
  1372. break;
  1373. case HDN_DIVIDERDBLCLICK:
  1374. ListView_DismissEdit(plv, FALSE);
  1375. ListView_RSetColumnWidth(plv, pnm->iItem, -1);
  1376. lres = ListView_ForwardHeaderNotify(plv, pnm);
  1377. SetFocus(plv->ci.hwnd);
  1378. break;
  1379. case HDN_FILTERCHANGE:
  1380. case HDN_FILTERBTNCLICK:
  1381. return ListView_ForwardHeaderNotify(plv, pnm);
  1382. case NM_RCLICK:
  1383. return (UINT)SendNotifyEx(plv->ci.hwndParent, plv->hwndHdr, NM_RCLICK, NULL, plv->ci.bUnicode);
  1384. default:
  1385. DoDefault:
  1386. return ListView_ForwardHeaderNotify(plv, pnm);
  1387. break;
  1388. }
  1389. // in v < 5 we always returned 0
  1390. // but for newer clients we'd like to have them deal with the notify
  1391. return lres;
  1392. }
  1393. int ListView_RYHitTest(LV* plv, int cy)
  1394. {
  1395. if (plv->fGroupView)
  1396. {
  1397. int iHit;
  1398. for (iHit = 0; iHit < ListView_Count(plv); iHit++)
  1399. {
  1400. RECT rc;
  1401. ListView_GetRects(plv, iHit, QUERY_DEFAULT, NULL, NULL, &rc, NULL);
  1402. if (cy >= rc.top && cy < rc.bottom)
  1403. return iHit;
  1404. }
  1405. }
  1406. else
  1407. return (cy + plv->ptlRptOrigin.y - plv->yTop) / plv->cyItem;
  1408. return -1;
  1409. }
  1410. /*----------------------------------------------------------------
  1411. ** Check for a hit in a report view.
  1412. **
  1413. ** a hit only counts if it's on the icon or the string in the first
  1414. ** column. so we gotta figure out what this means exactly. yuck.
  1415. **
  1416. ** BONUS FEATURE: If piSubItem is non-NULL, then we also hit-test
  1417. ** against subitems. But if we find nothing, we return iSubItem = 0
  1418. ** for compatibility with the other hit-test functions.
  1419. **----------------------------------------------------------------*/
  1420. int ListView_RItemHitTest(LV* plv, int x, int y, UINT* pflags, int *piSubItem)
  1421. {
  1422. int iHit;
  1423. int i, iSub;
  1424. UINT flags;
  1425. RECT rcLabel;
  1426. RECT rcIcon;
  1427. if (piSubItem)
  1428. *piSubItem = 0;
  1429. if (plv->rcView.left == RECOMPUTE && plv->fGroupView)
  1430. ListView_Recompute(plv);
  1431. flags = LVHT_NOWHERE;
  1432. iHit = -1;
  1433. i = ListView_RYHitTest(plv, y);
  1434. if (ListView_IsValidItemNumber(plv, i))
  1435. {
  1436. if (plv->ci.style & LVS_OWNERDRAWFIXED)
  1437. {
  1438. flags = LVHT_ONITEM;
  1439. iHit = i;
  1440. }
  1441. else
  1442. {
  1443. RECT rcSelect;
  1444. ListView_GetRects(plv, i, QUERY_DEFAULT, &rcIcon, &rcLabel, NULL, &rcSelect);
  1445. // is the hit in the first column?
  1446. if ((x < rcIcon.left - g_cxEdge) && x > (rcIcon.left - plv->cxState - LV_ICONTOSTATEOFFSET(plv)))
  1447. {
  1448. iHit = i;
  1449. flags = LVHT_ONITEMSTATEICON;
  1450. }
  1451. else if ((x >= rcIcon.left) && (x < rcIcon.right))
  1452. {
  1453. iHit = i;
  1454. flags = LVHT_ONITEMICON;
  1455. }
  1456. else if (x >= rcLabel.left && (x < rcSelect.right))
  1457. {
  1458. iHit = i;
  1459. flags = LVHT_ONITEMLABEL;
  1460. if (ListView_FullRowSelect(plv)) {
  1461. // this is kinda funky... in full row select mode
  1462. // we're only really on the label if x is <= rcLabel.left + cxLabel
  1463. // because GetRects returns a label rect of the full column width
  1464. // and rcSelect has the full row in FullRowSelect mode
  1465. // (it has the label only width in non-fullrow select mode.
  1466. //
  1467. // go figure..
  1468. //
  1469. int cxLabel;
  1470. LISTITEM* pitem = NULL;
  1471. if (!ListView_IsOwnerData( plv ))
  1472. {
  1473. pitem = ListView_FastGetItemPtr(plv, i);
  1474. }
  1475. cxLabel = ListView_RGetCXLabel(plv, i, pitem, NULL, FALSE);
  1476. if (x >= min(rcLabel.left + cxLabel, rcLabel.right)) {
  1477. if (!piSubItem)
  1478. flags = LVHT_ONITEM;
  1479. else
  1480. goto CheckSubItem;
  1481. }
  1482. }
  1483. } else if (x < rcSelect.right && ListView_FullRowSelect(plv)) {
  1484. // we can fall into this case if columns have been re-ordered
  1485. iHit = i;
  1486. flags = LVHT_ONITEM;
  1487. } else if (piSubItem) {
  1488. CheckSubItem:
  1489. iSub = ListView_RXHitTest(plv, x);
  1490. if (iSub >= 0) {
  1491. iHit = i;
  1492. *piSubItem = iSub;
  1493. // Flags still say LVHT_NOWHERE
  1494. }
  1495. }
  1496. }
  1497. }
  1498. *pflags = flags;
  1499. return iHit;
  1500. }
  1501. void ListView_GetSubItem(LV* plv, int i, int iSubItem, PLISTSUBITEM plsi)
  1502. {
  1503. HDPA hdpa;
  1504. PLISTSUBITEM plsiSrc = NULL;
  1505. ASSERT( !ListView_IsOwnerData( plv ));
  1506. // Sub items are indexed starting at 1...
  1507. //
  1508. RIPMSG(iSubItem > 0 && iSubItem < plv->cCol, "ListView: Invalid iSubItem: %d", iSubItem);
  1509. #ifdef DEBUG
  1510. // Avoid the assert in DPA_GetPtr if somebdy tries to get a subitem
  1511. // when no columns have been added. We already RIP'd above.
  1512. hdpa = plv->cCol ? ListView_GetSubItemDPA(plv, iSubItem - 1) : NULL;
  1513. #else
  1514. hdpa = ListView_GetSubItemDPA(plv, iSubItem - 1);
  1515. #endif
  1516. if (hdpa) {
  1517. plsiSrc = DPA_GetPtr(hdpa, i);
  1518. }
  1519. if (plsiSrc) {
  1520. *plsi = *plsiSrc;
  1521. } else {
  1522. // item data exists.. give defaults
  1523. plsi->pszText = LPSTR_TEXTCALLBACK;
  1524. plsi->iImage = I_IMAGECALLBACK;
  1525. plsi->state = 0;
  1526. }
  1527. }
  1528. // this will return the rect of a subitem as requested.
  1529. void ListView_RGetRectsEx(LV* plv, int iItem, int iSubItem, LPRECT prcIcon, LPRECT prcLabel)
  1530. {
  1531. int x;
  1532. int y;
  1533. LONG ly;
  1534. RECT rcLabel;
  1535. RECT rcIcon;
  1536. RECT rcHeader;
  1537. if (iSubItem == 0)
  1538. {
  1539. ListView_RGetRects(plv, iItem, prcIcon, prcLabel, NULL, NULL);
  1540. return;
  1541. }
  1542. ly = (LONG)iItem * plv->cyItem - plv->ptlRptOrigin.y + plv->yTop;
  1543. // otherwise it's just the header's column right and left and the item's height
  1544. if (plv->fGroupView && ListView_Count(plv) > 0)
  1545. {
  1546. LISTITEM* pitem = ListView_FastGetItemPtr(plv, iItem);
  1547. if (pitem && LISTITEM_HASGROUP(pitem))
  1548. {
  1549. ly = pitem->pt.y - plv->ptlRptOrigin.y + plv->yTop;
  1550. }
  1551. }
  1552. x = - (int)plv->ptlRptOrigin.x;
  1553. //
  1554. // Need to check for y overflow into rectangle structure
  1555. // if so we need to return something reasonable...
  1556. // For now will simply set it to the max or min that will fit...
  1557. //
  1558. if (ly >= (INT_MAX - plv->cyItem))
  1559. y = INT_MAX - plv->cyItem;
  1560. else if ( ly < INT_MIN)
  1561. y = INT_MIN;
  1562. else
  1563. y = (int)ly;
  1564. ASSERT(iSubItem < plv->cCol);
  1565. Header_GetItemRect(plv->hwndHdr, iSubItem, &rcHeader);
  1566. rcLabel.left = x + rcHeader.left;
  1567. rcLabel.right = x + rcHeader.right;
  1568. rcLabel.top = y;
  1569. rcLabel.bottom = rcLabel.top + plv->cyItem;
  1570. rcIcon = rcLabel;
  1571. rcIcon.right = rcIcon.left + plv->cxSmIcon;
  1572. if (SELECTOROF(prcIcon))
  1573. *prcIcon = rcIcon;
  1574. if (SELECTOROF(prcLabel))
  1575. *prcLabel = rcLabel;
  1576. }
  1577. int ListView_RGetTotalColumnWidth(LV* plv)
  1578. {
  1579. if (plv->xTotalColumnWidth == RECOMPUTE)
  1580. {
  1581. plv->xTotalColumnWidth = 0;
  1582. if (plv->cCol)
  1583. {
  1584. RECT rcLabel;
  1585. int iIndex;
  1586. // find the right edge of the last ordered item to get the total column width
  1587. iIndex = (int) SendMessage(plv->hwndHdr, HDM_ORDERTOINDEX, plv->cCol - 1, 0);
  1588. Header_GetItemRect(plv->hwndHdr, iIndex, &rcLabel);
  1589. plv->xTotalColumnWidth = rcLabel.right;
  1590. }
  1591. }
  1592. return plv->xTotalColumnWidth;
  1593. }
  1594. // get the rects for report view
  1595. void ListView_RGetRects(LV* plv, int iItem, RECT* prcIcon,
  1596. RECT* prcLabel, RECT* prcBounds, RECT* prcSelectBounds)
  1597. {
  1598. RECT rcIcon;
  1599. RECT rcLabel;
  1600. int x;
  1601. int y;
  1602. int cItems = ListView_Count(plv);
  1603. LONG ly = 0;
  1604. LVITEM lvitem;
  1605. BOOL fItemSpecific = (prcIcon || prcLabel || prcSelectBounds);
  1606. // If the item being asked for exceeds array bounds, use old calculation method
  1607. // This isn't a problem because listview typically is asking for bounds, or invalidation rects.
  1608. if (plv->fGroupView && iItem >= 0 && iItem < cItems)
  1609. {
  1610. LISTITEM* pitem = ListView_FastGetItemPtr(plv, iItem);
  1611. if (pitem && LISTITEM_HASGROUP(pitem))
  1612. {
  1613. ly = pitem->pt.y - plv->ptlRptOrigin.y + plv->yTop;
  1614. }
  1615. }
  1616. else
  1617. {
  1618. ly = (LONG)iItem * plv->cyItem - plv->ptlRptOrigin.y + plv->yTop;
  1619. }
  1620. x = - (int)plv->ptlRptOrigin.x;
  1621. //
  1622. // Need to check for y overflow into rectangle structure
  1623. // if so we need to return something reasonable...
  1624. // For now will simply set it to the max or min that will fit...
  1625. //
  1626. if (ly >= (INT_MAX - plv->cyItem))
  1627. y = INT_MAX - plv->cyItem;
  1628. else
  1629. y = (int)ly;
  1630. if (ListView_Count(plv) && fItemSpecific)
  1631. {
  1632. // move this over by the indent level as well
  1633. lvitem.mask = LVIF_INDENT;
  1634. lvitem.iItem = iItem;
  1635. lvitem.iSubItem = 0;
  1636. ListView_OnGetItem(plv, &lvitem);
  1637. }
  1638. else
  1639. {
  1640. lvitem.iIndent = 0;
  1641. }
  1642. rcIcon.left = x + plv->cxState + LV_ICONTOSTATEOFFSET(plv) + (lvitem.iIndent * plv->cxSmIcon) + g_cxEdge + LV_ICONINDENT;
  1643. rcIcon.right = rcIcon.left + plv->cxSmIcon;
  1644. rcIcon.top = y;
  1645. rcIcon.bottom = rcIcon.top + plv->cyItem;
  1646. rcLabel.left = rcIcon.right;
  1647. rcLabel.top = rcIcon.top;
  1648. rcLabel.bottom = rcIcon.bottom;
  1649. //
  1650. // The label is assumed to be the first column.
  1651. //
  1652. rcLabel.right = x;
  1653. if (plv->cCol > 0 && fItemSpecific)
  1654. {
  1655. RECT rc;
  1656. Header_GetItemRect(plv->hwndHdr, 0, &rc);
  1657. rcLabel.right = x + rc.right;
  1658. rcLabel.left += rc.left;
  1659. rcIcon.left += rc.left;
  1660. rcIcon.right += rc.left;
  1661. }
  1662. if (SELECTOROF(prcIcon))
  1663. *prcIcon = rcIcon;
  1664. // Save away the label bounds.
  1665. if (SELECTOROF(prcLabel))
  1666. {
  1667. *prcLabel = rcLabel;
  1668. }
  1669. // See if they also want the Selection bounds of the item
  1670. if (prcSelectBounds)
  1671. {
  1672. if (ListView_FullRowSelect(plv))
  1673. {
  1674. prcSelectBounds->left = x;
  1675. prcSelectBounds->top = y;
  1676. prcSelectBounds->bottom = rcLabel.bottom;
  1677. prcSelectBounds->right = prcSelectBounds->left + ListView_RGetTotalColumnWidth(plv);
  1678. }
  1679. else
  1680. {
  1681. int cxLabel;
  1682. LISTITEM* pitem = NULL;
  1683. if (!ListView_IsOwnerData( plv ))
  1684. {
  1685. pitem = ListView_FastGetItemPtr(plv, iItem);
  1686. }
  1687. cxLabel = ListView_RGetCXLabel(plv, iItem, pitem, NULL, FALSE);
  1688. *prcSelectBounds = rcIcon;
  1689. prcSelectBounds->right = rcLabel.left + cxLabel;
  1690. if (prcSelectBounds->right > rcLabel.right)
  1691. prcSelectBounds->right = rcLabel.right;
  1692. }
  1693. }
  1694. // And also the Total bounds
  1695. //
  1696. // and now for the complete bounds...
  1697. //
  1698. if (SELECTOROF(prcBounds))
  1699. {
  1700. prcBounds->left = x;
  1701. prcBounds->top = y;
  1702. prcBounds->bottom = rcLabel.bottom;
  1703. prcBounds->right = prcBounds->left + ListView_RGetTotalColumnWidth(plv);
  1704. }
  1705. }
  1706. BOOL ListView_OnGetSubItemRect(LV* plv, int iItem, LPRECT lprc)
  1707. {
  1708. LPRECT pRects[LVIR_MAX];
  1709. RECT rcTemp;
  1710. int iSubItem;
  1711. int iCode;
  1712. if (!lprc)
  1713. return FALSE;
  1714. iSubItem = lprc->top;
  1715. iCode = lprc->left;
  1716. if (iSubItem == 0)
  1717. {
  1718. return ListView_OnGetItemRect(plv, iItem, lprc);
  1719. }
  1720. if (!ListView_IsReportView(plv) ||
  1721. (iCode != LVIR_BOUNDS && iCode != LVIR_ICON && iCode != LVIR_LABEL))
  1722. {
  1723. return FALSE;
  1724. }
  1725. pRects[0] = NULL;
  1726. pRects[1] = &rcTemp; // LVIR_ICON
  1727. pRects[2] = &rcTemp; // LVIR_LABEL
  1728. pRects[3] = NULL;
  1729. if (iCode != LVIR_BOUNDS)
  1730. {
  1731. pRects[iCode] = lprc;
  1732. }
  1733. else
  1734. {
  1735. // choose either
  1736. pRects[LVIR_ICON] = lprc;
  1737. }
  1738. ListView_RGetRectsEx(plv, iItem, iSubItem,
  1739. pRects[LVIR_ICON], pRects[LVIR_LABEL]);
  1740. if (iCode == LVIR_BOUNDS)
  1741. {
  1742. UnionRect(lprc, lprc, &rcTemp);
  1743. }
  1744. return TRUE;
  1745. }
  1746. int ListView_RXHitTest(LV* plv, int x)
  1747. {
  1748. int iSubItem;
  1749. for (iSubItem = plv->cCol - 1; iSubItem >= 0; iSubItem--)
  1750. {
  1751. RECT rc;
  1752. // see if its in this rect,
  1753. if (!Header_GetItemRect(plv->hwndHdr, iSubItem, &rc))
  1754. return -1;
  1755. OffsetRect(&rc, -plv->ptlRptOrigin.x, 0);
  1756. if (rc.left <= x && x < rc.right)
  1757. {
  1758. break;
  1759. }
  1760. }
  1761. return iSubItem;
  1762. }
  1763. int ListView_OnSubItemHitTest(LV* plv, LPLVHITTESTINFO plvhti)
  1764. {
  1765. int i = -1;
  1766. int iSubItem = 0;
  1767. UINT uFlags = LVHT_NOWHERE;
  1768. if (!plvhti)
  1769. {
  1770. return -1;
  1771. }
  1772. if (ListView_IsReportView(plv))
  1773. {
  1774. iSubItem = ListView_RXHitTest(plv, plvhti->pt.x);
  1775. if (iSubItem == -1)
  1776. {
  1777. goto Bail;
  1778. }
  1779. }
  1780. if (iSubItem == 0)
  1781. {
  1782. // if we're in column 0, just hand it off to the old stuff
  1783. ListView_OnHitTest(plv, plvhti);
  1784. plvhti->iSubItem = 0;
  1785. return plvhti->iItem;
  1786. }
  1787. if (!ListView_IsReportView(plv))
  1788. {
  1789. goto Bail;
  1790. }
  1791. i = ListView_RYHitTest(plv, plvhti->pt.y);
  1792. if (i < ListView_Count(plv))
  1793. {
  1794. RECT rcIcon, rcLabel;
  1795. if (i != -1)
  1796. {
  1797. ListView_RGetRectsEx(plv, i, iSubItem, &rcIcon, &rcLabel);
  1798. if (plvhti->pt.x >= rcIcon.left && plvhti->pt.x <= rcIcon.right)
  1799. {
  1800. uFlags = LVHT_ONITEMICON;
  1801. }
  1802. else if (plvhti->pt.x >= rcLabel.left && plvhti->pt.x <= rcLabel.right)
  1803. {
  1804. uFlags = LVHT_ONITEMLABEL;
  1805. }
  1806. else
  1807. uFlags = LVHT_ONITEM;
  1808. }
  1809. }
  1810. else
  1811. {
  1812. i = -1;
  1813. }
  1814. Bail:
  1815. plvhti->iItem = i;
  1816. plvhti->iSubItem = iSubItem;
  1817. plvhti->flags = uFlags;
  1818. return plvhti->iItem;
  1819. }
  1820. // See whether entire string will fit in *prc; if not, compute number of chars
  1821. // that will fit, including ellipses. Returns length of string in *pcchDraw.
  1822. //
  1823. BOOL ListView_NeedsEllipses(HDC hdc, LPCTSTR pszText, RECT* prc, int* pcchDraw, int cxEllipses)
  1824. {
  1825. int cchText;
  1826. int cxRect;
  1827. int ichMin, ichMax, ichMid;
  1828. SIZE siz;
  1829. cxRect = prc->right - prc->left;
  1830. cchText = lstrlen(pszText);
  1831. if (cchText == 0)
  1832. {
  1833. *pcchDraw = cchText;
  1834. return FALSE;
  1835. }
  1836. GetTextExtentPoint(hdc, pszText, cchText, &siz);
  1837. if (siz.cx <= cxRect)
  1838. {
  1839. *pcchDraw = cchText;
  1840. return FALSE;
  1841. }
  1842. cxRect -= cxEllipses;
  1843. // If no room for ellipses, always show first character.
  1844. //
  1845. ichMax = 1;
  1846. if (cxRect > 0)
  1847. {
  1848. // Binary search to find character that will fit
  1849. ichMin = 0;
  1850. ichMax = cchText;
  1851. while (ichMin < ichMax)
  1852. {
  1853. // Be sure to round up, to make sure we make progress in
  1854. // the loop if ichMax == ichMin + 1.
  1855. //
  1856. ichMid = (ichMin + ichMax + 1) / 2;
  1857. GetTextExtentPoint(hdc, &pszText[ichMin], ichMid - ichMin, &siz);
  1858. if (siz.cx < cxRect)
  1859. {
  1860. ichMin = ichMid;
  1861. cxRect -= siz.cx;
  1862. }
  1863. else if (siz.cx > cxRect)
  1864. {
  1865. ichMax = ichMid - 1;
  1866. }
  1867. else
  1868. {
  1869. // Exact match up up to ichMid: just exit.
  1870. //
  1871. ichMax = ichMid;
  1872. break;
  1873. }
  1874. }
  1875. // Make sure we always show at least the first character...
  1876. //
  1877. if (ichMax < 1)
  1878. ichMax = 1;
  1879. }
  1880. *pcchDraw = ichMax;
  1881. return TRUE;
  1882. }
  1883. // in lvicon
  1884. DWORD ListView_GetClientRect(LV* plv, RECT* prcClient, BOOL fSubScroll, RECT *prcViewRect);
  1885. void ListView_RUpdateScrollBars(LV* plv)
  1886. {
  1887. HD_LAYOUT layout;
  1888. RECT rcClient;
  1889. RECT rcBounds;
  1890. WINDOWPOS wpos;
  1891. int cColVis, cyColVis, iNewPos, iyDelta = 0, ixDelta = 0;
  1892. BOOL fHorSB, fReupdate = FALSE;
  1893. SCROLLINFO si;
  1894. int iMin, iMax, iScreen, iPos;
  1895. ListView_GetClientRect(plv, &rcClient, FALSE, NULL);
  1896. if (!plv->hwndHdr)
  1897. ListView_CreateHeader(plv);
  1898. if (!plv->hwndHdr)
  1899. TraceMsg(TF_WARNING, "ListView_RUpdateScrollBars could not create hwndHdr");
  1900. layout.pwpos = &wpos;
  1901. // For now lets try to handle scrolling the header by setting
  1902. // its window pos.
  1903. rcClient.left -= (int)plv->ptlRptOrigin.x;
  1904. layout.prc = &rcClient;
  1905. Header_Layout(plv->hwndHdr, &layout);
  1906. rcClient.left += (int)plv->ptlRptOrigin.x; // Move it back over!
  1907. SetWindowPos(plv->hwndHdr, wpos.hwndInsertAfter, wpos.x, wpos.y,
  1908. wpos.cx, wpos.cy, wpos.flags | SWP_SHOWWINDOW);
  1909. // Get the horizontal bounds of the items.
  1910. ListView_RGetRects(plv, 0, NULL, NULL, &rcBounds, NULL);
  1911. plv->yTop = rcClient.top;
  1912. if (plv->fGroupView)
  1913. {
  1914. RECT rcView;
  1915. ListView_GetClientRect(plv, &rcClient, TRUE, &rcView);
  1916. iMin = 0;
  1917. iMax = RECTHEIGHT(rcView) - 1;
  1918. iScreen = RECTHEIGHT(rcClient);
  1919. iPos = rcClient.top - rcView.top;
  1920. }
  1921. else
  1922. {
  1923. // fHorSB = Do I need a horizontal scrollbar?
  1924. // cyColVis = number of pixels per screenful
  1925. fHorSB = (rcBounds.right - rcBounds.left > rcClient.right); // First guess.
  1926. cyColVis = rcClient.bottom - rcClient.top -
  1927. (fHorSB ? ListView_GetCyScrollbar(plv) : 0);
  1928. // If screen can't fit the entire listview...
  1929. if (cyColVis < ListView_Count(plv) * plv->cyItem)
  1930. {
  1931. //then we're going to have a vertical scrollbar.. make sure our horizontal count is correct
  1932. rcClient.right -= ListView_GetCxScrollbar(plv);
  1933. if (!fHorSB)
  1934. {
  1935. // if we previously thought we weren't going to have a scrollbar, we could be wrong..
  1936. // since the vertical bar shrunk our area
  1937. fHorSB = (rcBounds.right - rcBounds.left > rcClient.right); // First guess.
  1938. cyColVis = rcClient.bottom - rcClient.top -
  1939. (fHorSB ? ListView_GetCyScrollbar(plv) : 0);
  1940. }
  1941. }
  1942. // cColVis = number of completely visible items per screenful
  1943. cColVis = cyColVis / plv->cyItem;
  1944. iMin = 0;
  1945. iMax = ListView_Count(plv) - 1;
  1946. iScreen = cColVis;
  1947. iPos = (int)(plv->ptlRptOrigin.y / plv->cyItem);
  1948. }
  1949. si.cbSize = sizeof(SCROLLINFO);
  1950. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  1951. si.nPos = iPos;
  1952. si.nPage = iScreen;
  1953. si.nMin = iMin;
  1954. si.nMax = iMax;
  1955. ListView_SetScrollInfo(plv, SB_VERT, &si, TRUE);
  1956. // make sure our position and page doesn't hang over max
  1957. if ((si.nPos > (int)si.nMax - (int)si.nPage + 1) && si.nPos > 0)
  1958. {
  1959. iNewPos = (int)si.nMax - (int)si.nPage + 1;
  1960. if (iNewPos < 0) iNewPos = 0;
  1961. if (iNewPos != si.nPos)
  1962. {
  1963. iyDelta = iNewPos - (int)si.nPos;
  1964. fReupdate = TRUE;
  1965. }
  1966. }
  1967. si.nPos = (int)plv->ptlRptOrigin.x;
  1968. si.nPage = rcClient.right - rcClient.left;
  1969. // We need to subtract 1 here because nMax is 0 based, and nPage is the actual
  1970. // number of page pixels. So, if nPage and nMax are the same we will get a
  1971. // horz scroll, since there is 1 more pixel than the page can show, but... rcBounds
  1972. // is like rcRect, and is the actual number of pixels for the whole thing, so
  1973. // we need to set nMax so that: nMax - 0 == rcBounds.right - rcBound.left
  1974. si.nMax = rcBounds.right - rcBounds.left - 1;
  1975. ListView_SetScrollInfo(plv, SB_HORZ, &si, TRUE);
  1976. // SWP_FRAMECHANGED redraws the background if the client
  1977. // area has changed (taking into account scrollbars and
  1978. // the Header window). SetScrollInfo does this automatically
  1979. // when it creates a scrollbar - we do it ourselves when
  1980. // there is no scrollbar.
  1981. if ((UINT)si.nPage > (UINT)si.nMax &&
  1982. ((plv->pImgCtx && plv->fImgCtxComplete) || plv->hbmBkImage))
  1983. SetWindowPos(plv->ci.hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
  1984. // make sure our position and page doesn't hang over max
  1985. if ((si.nPos + (LONG)si.nPage - 1 > si.nMax) && si.nPos > 0)
  1986. {
  1987. iNewPos = (int)si.nMax - (int)si.nPage + 1;
  1988. if (iNewPos < 0) iNewPos = 0;
  1989. if (iNewPos != si.nPos)
  1990. {
  1991. ixDelta = iNewPos - (int)si.nPos;
  1992. fReupdate = TRUE;
  1993. }
  1994. }
  1995. if (fReupdate)
  1996. {
  1997. // we shouldn't recurse because the second time through, si.nPos >0
  1998. ListView_RScroll2(plv, ixDelta, iyDelta, 0);
  1999. ListView_RUpdateScrollBars(plv);
  2000. TraceMsg(TF_LISTVIEW, "LISTVIEW: ERROR: We had to recurse!");
  2001. }
  2002. }
  2003. //
  2004. // We need a smoothscroll callback so our background image draws
  2005. // at the correct origin. If we don't have a background image,
  2006. // then this work is superfluous but not harmful either.
  2007. //
  2008. int CALLBACK ListView_RScroll2_SmoothScroll(
  2009. HWND hwnd,
  2010. int dx,
  2011. int dy,
  2012. CONST RECT *prcScroll,
  2013. CONST RECT *prcClip,
  2014. HRGN hrgnUpdate,
  2015. LPRECT prcUpdate,
  2016. UINT flags)
  2017. {
  2018. LV* plv = ListView_GetPtr(hwnd);
  2019. if (plv)
  2020. {
  2021. plv->ptlRptOrigin.x -= dx;
  2022. plv->ptlRptOrigin.y -= dy;
  2023. }
  2024. // Now do what SmoothScrollWindow would've done if we weren't
  2025. // a callback
  2026. if (ListView_IsWatermarkedBackground(plv) ||
  2027. ListView_IsWatermarked(plv))
  2028. {
  2029. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2030. return TRUE;
  2031. }
  2032. else
  2033. return ScrollWindowEx(hwnd, dx, dy, prcScroll, prcClip, hrgnUpdate, prcUpdate, flags);
  2034. }
  2035. void ListView_RScroll2(LV* plv, int dx, int dy, UINT uSmooth)
  2036. {
  2037. LONG ldy;
  2038. if (dx | dy)
  2039. {
  2040. RECT rc;
  2041. GetClientRect(plv->ci.hwnd, &rc);
  2042. rc.top = plv->yTop;
  2043. // We can not do a simple multiply here as we may run into
  2044. // a case where this will overflow an int..
  2045. if (plv->fGroupView)
  2046. {
  2047. ldy = (LONG)dy;
  2048. }
  2049. else
  2050. {
  2051. ldy = (LONG)dy * plv->cyItem;
  2052. }
  2053. // handle case where dy is large (greater than int...)
  2054. if ((ldy > rc.bottom) || (ldy < -rc.bottom))
  2055. {
  2056. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2057. plv->ptlRptOrigin.x += dx;
  2058. plv->ptlRptOrigin.y += ldy;
  2059. }
  2060. else
  2061. {
  2062. SMOOTHSCROLLINFO si;
  2063. si.cbSize = sizeof(si);
  2064. si.fMask = SSIF_SCROLLPROC;
  2065. si.hwnd = plv->ci.hwnd;
  2066. si.dx = -dx;
  2067. si.dy = (int)-ldy;
  2068. si.lprcSrc = NULL;
  2069. si.lprcClip = &rc;
  2070. si.hrgnUpdate = NULL;
  2071. si.lprcUpdate = NULL;
  2072. si.fuScroll =SW_INVALIDATE | SW_ERASE | uSmooth;
  2073. si.pfnScrollProc = ListView_RScroll2_SmoothScroll;
  2074. SmoothScrollWindow(&si);
  2075. /// this causes horrible flicker/repaint on deletes.
  2076. // if this is a problem with UI scrolling, we'll have to pass through a
  2077. // flag when to use this
  2078. ///UpdateWindow(plv->ci.hwnd);
  2079. }
  2080. // if Horizontal scrolling, we should update the location of the
  2081. // left hand edge of the window...
  2082. //
  2083. if (dx != 0)
  2084. {
  2085. RECT rcHdr;
  2086. GetWindowRect(plv->hwndHdr, &rcHdr);
  2087. MapWindowRect(HWND_DESKTOP, plv->ci.hwnd, &rcHdr);
  2088. SetWindowPos(plv->hwndHdr, NULL, rcHdr.left - dx, rcHdr.top,
  2089. rcHdr.right - rcHdr.left + dx,
  2090. rcHdr.bottom - rcHdr.top,
  2091. SWP_NOZORDER | SWP_NOACTIVATE);
  2092. }
  2093. }
  2094. }
  2095. //-------------------------------------------------------------------
  2096. // Make sure that specified item is visible for report view.
  2097. // Must handle Large number of items...
  2098. BOOL ListView_ROnEnsureVisible(LV* plv, int iItem, BOOL fPartialOK)
  2099. {
  2100. LONG dy;
  2101. LONG yTop;
  2102. LONG lyTop;
  2103. yTop = plv->yTop;
  2104. // lyTop = where our item is right now
  2105. if (plv->fGroupView)
  2106. {
  2107. LISTITEM* pitem = ListView_GetItemPtr(plv, iItem);
  2108. RECT rcBounds;
  2109. ListView_RGetRects(plv, iItem, NULL, NULL, &rcBounds, NULL);
  2110. if (pitem)
  2111. {
  2112. LISTGROUP* pgrp = ListView_FindFirstVisibleGroup(plv);
  2113. if (pitem->pGroup == pgrp && pgrp)
  2114. {
  2115. rcBounds.top -= max(pgrp->cyTitle + 6, plv->rcBorder.top) + plv->paddingTop;
  2116. }
  2117. }
  2118. lyTop = rcBounds.top;
  2119. }
  2120. else
  2121. {
  2122. lyTop = (LONG)iItem * plv->cyItem - plv->ptlRptOrigin.y + plv->yTop;
  2123. }
  2124. // If visible below yTop and our bottom is visible above client bottom,
  2125. // then we're happy.
  2126. if ((lyTop >= (LONG)yTop) &&
  2127. ((lyTop + plv->cyItem) <= (LONG)plv->sizeClient.cy))
  2128. return(TRUE); // we are visible
  2129. dy = lyTop - yTop;
  2130. if (dy >= 0)
  2131. {
  2132. // dy = how many pixels we need to scroll to come into view
  2133. dy = lyTop + plv->cyItem - plv->sizeClient.cy;
  2134. if (dy < 0)
  2135. dy = 0;
  2136. }
  2137. if (dy)
  2138. {
  2139. int iRound = ((dy > 0) ? 1 : -1) * (plv->cyItem - 1);
  2140. if (!plv->fGroupView)
  2141. {
  2142. // Now convert into the number of items to scroll...
  2143. // Groupview uses pixels not items, so this calculation is not needed in groupview.
  2144. dy = (dy + iRound) / plv->cyItem;
  2145. }
  2146. ListView_RScroll2(plv, 0, (int)dy, 0);
  2147. if (ListView_RedrawEnabled(plv))
  2148. {
  2149. ListView_UpdateScrollBars(plv);
  2150. }
  2151. else
  2152. {
  2153. ListView_DeleteHrgnInval(plv);
  2154. plv->hrgnInval = (HRGN)ENTIRE_REGION;
  2155. plv->flags |= LVF_ERASE;
  2156. }
  2157. }
  2158. return TRUE;
  2159. }
  2160. int ListView_RGetScrollUnitsPerLine(LV* plv, UINT sb)
  2161. {
  2162. int cLine;
  2163. if (sb == SB_VERT)
  2164. {
  2165. if (plv->fGroupView)
  2166. {
  2167. cLine = plv->cyItem;
  2168. }
  2169. else
  2170. {
  2171. cLine = 1;
  2172. }
  2173. }
  2174. else
  2175. {
  2176. cLine = plv->cxLabelChar;
  2177. }
  2178. return cLine;
  2179. }
  2180. void ListView_ROnScroll(LV* plv, UINT code, int posNew, UINT sb)
  2181. {
  2182. int cLine = ListView_RGetScrollUnitsPerLine(plv, sb);
  2183. ListView_ComOnScroll(plv, code, posNew, sb, cLine, -1);
  2184. }
  2185. BOOL ListView_RRecomputeEx(LV* plv, HDPA hdpaSort, int iFrom, BOOL fForce)
  2186. {
  2187. if (plv->fGroupView && plv->hdpaGroups)
  2188. {
  2189. LISTGROUP* pgrp;
  2190. int cGroups;
  2191. int iAccumulatedHeight = 0;
  2192. int i;
  2193. int cItems = ListView_Count(plv);
  2194. int iGroupItem;
  2195. LISTITEM* pitem;
  2196. for (iGroupItem = 0; iGroupItem < cItems; iGroupItem++)
  2197. {
  2198. LV_ITEM item = {0};
  2199. pitem = ListView_FastGetItemPtr(plv, iGroupItem);
  2200. if (!pitem)
  2201. break;
  2202. item.iItem = iGroupItem;
  2203. item.lParam = pitem->lParam;
  2204. if (!LISTITEM_HASASKEDFORGROUP(pitem))
  2205. {
  2206. item.mask = LVIF_GROUPID;
  2207. ListView_OnGetItem(plv, &item);
  2208. }
  2209. }
  2210. if (iFrom > 0)
  2211. {
  2212. LISTGROUP* pgrpPrev = DPA_FastGetPtr(plv->hdpaGroups, iFrom - 1);
  2213. iAccumulatedHeight = pgrpPrev->rc.bottom + plv->paddingBottom;
  2214. }
  2215. // Need to do this afterwards because we may have added groups in the above block
  2216. cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  2217. for (i = iFrom; i < cGroups; i++)
  2218. {
  2219. pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  2220. if (!pgrp) // Huh?
  2221. break;
  2222. cItems = DPA_GetPtrCount(pgrp->hdpa);
  2223. if (cItems == 0)
  2224. {
  2225. SetRect(&pgrp->rc, 0, 0, 0, 0);
  2226. }
  2227. else
  2228. {
  2229. int iGroupItem;
  2230. RECT rcBoundsPrev = {0};
  2231. if (pgrp->pszHeader && (pgrp->cyTitle == 0 || fForce))
  2232. {
  2233. RECT rc = {0, 0, 1000, 0};
  2234. HDC hdc = GetDC(plv->ci.hwnd);
  2235. HFONT hfontOld = SelectObject(hdc, plv->hfontGroup);
  2236. DrawText(hdc, pgrp->pszHeader, -1, &rc, DT_LV | DT_CALCRECT);
  2237. SelectObject(hdc, hfontOld);
  2238. ReleaseDC(plv->ci.hwnd, hdc);
  2239. pgrp->cyTitle = RECTHEIGHT(rc);
  2240. }
  2241. iAccumulatedHeight += max(plv->rcBorder.top, pgrp->cyTitle + 6) + plv->paddingTop;
  2242. SetRect(&pgrp->rc, plv->rcBorder.left, iAccumulatedHeight,
  2243. plv->sizeClient.cx - plv->rcBorder.right, iAccumulatedHeight + cItems * (plv->cyItem + LV_DETAILSPADDING) + plv->paddingBottom);
  2244. iAccumulatedHeight += RECTHEIGHT(pgrp->rc);
  2245. for (iGroupItem = 0; iGroupItem < cItems; iGroupItem++)
  2246. {
  2247. LISTITEM* pitem = DPA_FastGetPtr(pgrp->hdpa, iGroupItem);
  2248. if (!pitem)
  2249. break;
  2250. pitem->pt.x = 0;
  2251. pitem->pt.y = pgrp->rc.top + iGroupItem * (plv->cyItem + LV_DETAILSPADDING);
  2252. }
  2253. }
  2254. }
  2255. SetRectEmpty(&plv->rcView);
  2256. // Find the first group with an item in it.
  2257. for (i = 0; i < cGroups; i++)
  2258. {
  2259. pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  2260. if (DPA_GetPtrCount(pgrp->hdpa) > 0)
  2261. {
  2262. plv->rcView.top = pgrp->rc.top - max(plv->rcBorder.top, pgrp->cyTitle + 6) - plv->paddingTop;
  2263. plv->rcView.left = pgrp->rc.left - plv->rcBorder.left - plv->paddingLeft;
  2264. break;
  2265. }
  2266. }
  2267. for (i = cGroups - 1; i >= 0; i--)
  2268. {
  2269. pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  2270. if (DPA_GetPtrCount(pgrp->hdpa))
  2271. {
  2272. plv->rcView.bottom = pgrp->rc.bottom + plv->rcBorder.bottom + plv->paddingBottom;
  2273. break;
  2274. }
  2275. }
  2276. ListView_UpdateScrollBars(plv);
  2277. }
  2278. return TRUE;
  2279. }