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.

2450 lines
72 KiB

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