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.

4581 lines
144 KiB

  1. // large icon view stuff
  2. #include "ctlspriv.h"
  3. #include "listview.h"
  4. #if defined(FE_IME) || !defined(WINNT)
  5. static TCHAR const szIMECompPos[]=TEXT("IMECompPos");
  6. #endif
  7. __inline int ICONCXLABEL(LV *plv, LISTITEM *pitem)
  8. {
  9. if (plv->ci.style & LVS_NOLABELWRAP) {
  10. ASSERT(pitem->cxSingleLabel == pitem->cxMultiLabel);
  11. }
  12. return pitem->cxMultiLabel;
  13. }
  14. int LV_GetNewColWidth(LV* plv, int iFirst, int iLast);
  15. void LV_AdjustViewRectOnMove(LV* plv, LISTITEM *pitem, int x, int y);
  16. void ListView_RecalcRegion(LV *plv, BOOL fForce, BOOL fRedraw);
  17. void ListView_ArrangeOrSnapToGrid(LV *plv);
  18. extern BOOL g_fSlowMachine;
  19. void _GetCurrentItemSize(LV* plv, int * pcx, int *pcy)
  20. {
  21. if (ListView_IsSmallView(plv))
  22. {
  23. *pcx = plv->cxItem;
  24. *pcy = plv->cyItem;
  25. }
  26. else if (ListView_IsTileView(plv))
  27. {
  28. *pcx = plv->sizeTile.cx;
  29. *pcy = plv->sizeTile.cy;
  30. }
  31. else
  32. {
  33. *pcx = plv->cxIconSpacing;
  34. *pcy = plv->cyIconSpacing;
  35. }
  36. }
  37. BOOL ListView_IDrawItem(PLVDRAWITEM plvdi)
  38. {
  39. RECT rcIcon;
  40. RECT rcLabel;
  41. RECT rcBounds;
  42. RECT rcBiasedBounds;
  43. RECT rcT;
  44. TCHAR ach[CCHLABELMAX];
  45. LV_ITEM item;
  46. int i = (int) plvdi->nmcd.nmcd.dwItemSpec;
  47. LV* plv = plvdi->plv;
  48. LISTITEM* pitem;
  49. BOOL fUnfolded;
  50. if (ListView_IsOwnerData(plv))
  51. {
  52. LISTITEM litem;
  53. // moved here to reduce call backs in OWNERDATA case
  54. item.iItem = i;
  55. item.iSubItem = 0;
  56. item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
  57. item.stateMask = LVIS_ALL;
  58. item.pszText = ach;
  59. item.cchTextMax = ARRAYSIZE(ach);
  60. ListView_OnGetItem(plv, &item);
  61. litem.pszText = item.pszText;
  62. ListView_GetRectsOwnerData(plv, i, &rcIcon, &rcLabel, &rcBounds, NULL, &litem);
  63. pitem = NULL;
  64. }
  65. else
  66. {
  67. pitem = ListView_GetItemPtr(plv, i);
  68. // NOTE this will do a GetItem LVIF_TEXT iff needed
  69. ListView_GetRects(plv, i, QUERY_DEFAULT, &rcIcon, &rcLabel, &rcBounds, NULL);
  70. }
  71. fUnfolded = FALSE;
  72. if ( (plvdi->flags & LVDI_UNFOLDED) || ListView_IsItemUnfolded(plv, i))
  73. {
  74. ListView_UnfoldRects(plv, i, &rcIcon, &rcLabel, &rcBounds, NULL );
  75. fUnfolded = TRUE;
  76. }
  77. rcBiasedBounds = rcBounds;
  78. if (ListView_IsBorderSelect(plv))
  79. InflateRect(&rcBiasedBounds, BORDERSELECT_THICKNESS, BORDERSELECT_THICKNESS);
  80. if (!plvdi->prcClip || IntersectRect(&rcT, &rcBiasedBounds, plvdi->prcClip))
  81. {
  82. RECT rcIconReal;
  83. UINT fText;
  84. COLORREF clrIconBk = plv->clrBk;
  85. if (!ListView_IsOwnerData(plv))
  86. {
  87. item.iItem = i;
  88. item.iSubItem = 0;
  89. item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
  90. item.stateMask = LVIS_ALL;
  91. item.pszText = ach;
  92. item.cchTextMax = ARRAYSIZE(ach);
  93. ListView_OnGetItem(plv, &item);
  94. // Make sure the listview hasn't been altered during
  95. // the callback to get the item info
  96. if (pitem != ListView_GetItemPtr(plv, i))
  97. return FALSE;
  98. }
  99. if (plvdi->lpptOrg)
  100. {
  101. OffsetRect(&rcIcon, plvdi->lpptOrg->x - rcBounds.left,
  102. plvdi->lpptOrg->y - rcBounds.top);
  103. OffsetRect(&rcLabel, plvdi->lpptOrg->x - rcBounds.left,
  104. plvdi->lpptOrg->y - rcBounds.top);
  105. OffsetRect(&rcBounds, plvdi->lpptOrg->x - rcBounds.left,
  106. plvdi->lpptOrg->y - rcBounds.top);
  107. }
  108. fText = ListView_GetTextSelectionFlags(plv, &item, plvdi->flags);
  109. if (ListView_IsIconView(plv))
  110. {
  111. rcIcon.left += ListView_GetIconBufferX(plv);
  112. rcIcon.top += ListView_GetIconBufferY(plv);
  113. fText = ListView_DrawImageEx(plv, &item, plvdi->nmcd.nmcd.hdc,
  114. rcIcon.left, rcIcon.top, clrIconBk, plvdi->flags, -1);
  115. SetRect(&rcIconReal, rcIcon.left, rcIcon.top, rcIcon.left + plv->cxIcon, rcIcon.top + plv->cyIcon);
  116. if (ListView_IsBorderSelect(plv))
  117. {
  118. int cp = 1;
  119. COLORREF clrOutline = plv->clrOutline;
  120. if (fText & SHDT_SELECTED || fText & SHDT_SELECTNOFOCUS)
  121. {
  122. clrOutline = (fText & SHDT_SELECTED)?g_clrHighlight:g_clrBtnFace;
  123. cp = BORDERSELECT_THICKNESS;
  124. InflateRect(&rcIconReal, cp, cp);
  125. }
  126. SHOutlineRectThickness(plvdi->nmcd.nmcd.hdc, &rcIconReal, clrOutline, g_clrBtnFace, cp);
  127. }
  128. // If linebreaking needs to happen, then use SHDT_DRAWTEXT.
  129. // Otherwise, use our (hopefully faster) internal SHDT_ELLIPSES
  130. if (rcLabel.bottom - rcLabel.top > plv->cyLabelChar)
  131. fText |= SHDT_DRAWTEXT;
  132. else
  133. fText |= SHDT_ELLIPSES;
  134. // We only use DT_NOFULLWIDTHCHARBREAK on Korean(949) Memphis and NT5
  135. if (949 == g_uiACP)
  136. fText |= SHDT_NODBCSBREAK;
  137. }
  138. else
  139. {
  140. SetRect(&rcIconReal, rcIcon.left, rcIcon.top, rcIcon.left + plv->cxIcon, rcIcon.top + plv->cyIcon);
  141. fText = ListView_DrawImageEx(plv, &item, plvdi->nmcd.nmcd.hdc,
  142. rcIcon.left, rcIcon.top, clrIconBk, plvdi->flags, -1);
  143. }
  144. if (ListView_HideLabels(plv) &&
  145. (plvdi->flags & LVDI_FOCUS) &&
  146. (item.state & LVIS_FOCUSED) &&
  147. !(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS))
  148. {
  149. DrawFocusRect(plvdi->nmcd.nmcd.hdc, &rcIconReal);
  150. }
  151. // Don't draw label if it's being edited...
  152. // or if it is hidden due to the HideLabels style.
  153. //
  154. if ((plv->iEdit != i) && !ListView_HideLabels(plv))
  155. {
  156. HRESULT hr = E_FAIL;
  157. COLORREF clrTextBk = plvdi->nmcd.clrTextBk;
  158. // If multiline label, then we need to use DrawText
  159. if (rcLabel.bottom - rcLabel.top > plv->cyLabelChar)
  160. {
  161. fText |= SHDT_DRAWTEXT;
  162. // If the text is folded, we need to clip and add ellipses
  163. if (!fUnfolded)
  164. fText |= SHDT_CLIPPED | SHDT_DTELLIPSIS;
  165. if ( ListView_IsOwnerData(plv) )
  166. {
  167. // If owner data, we have no z-order and if long names they will over lap each
  168. // other, better to truncate for now...
  169. if (ListView_IsSmallView(plv))
  170. fText |= SHDT_ELLIPSES;
  171. }
  172. }
  173. else
  174. fText |= SHDT_ELLIPSES;
  175. if (plvdi->flags & LVDI_TRANSTEXT)
  176. fText |= SHDT_TRANSPARENT;
  177. if ((fText & SHDT_SELECTED) && (plvdi->flags & LVDI_HOTSELECTED))
  178. fText |= SHDT_HOTSELECTED;
  179. if (item.pszText && (*item.pszText))
  180. {
  181. if (plv->pImgCtx || ListView_IsWatermarked(plv))
  182. clrTextBk = CLR_NONE;
  183. if(plv->dwExStyle & WS_EX_RTLREADING)
  184. fText |= SHDT_RTLREADING;
  185. if ((plv->clrBk == CLR_NONE) &&
  186. !(fText & (SHDT_SELECTED | SHDT_HOTSELECTED | SHDT_SELECTNOFOCUS)) && // And we're not selected
  187. !(plv->flags & LVF_DRAGIMAGE) && // And this is not during dragdrop.
  188. !(plv->exStyle & LVS_EX_REGIONAL) && // No need for regional.
  189. plv->fListviewShadowText) // and enabled
  190. {
  191. fText |= SHDT_SHADOWTEXT;
  192. }
  193. SHDrawText(plvdi->nmcd.nmcd.hdc, item.pszText, &rcLabel, LVCFMT_LEFT, fText,
  194. plv->cyLabelChar, plv->cxEllipses,
  195. plvdi->nmcd.clrText, clrTextBk);
  196. if ((plvdi->flags & LVDI_FOCUS) &&
  197. (item.state & LVIS_FOCUSED) &&
  198. !(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS))
  199. {
  200. rcLabel.top -= g_cyCompensateInternalLeading;
  201. DrawFocusRect(plvdi->nmcd.nmcd.hdc, &rcLabel);
  202. }
  203. }
  204. }
  205. }
  206. return TRUE;
  207. }
  208. void ListView_RefoldLabelRect(LV* plv, RECT *prcLabel, LISTITEM *pitem)
  209. {
  210. int bottom = pitem->cyUnfoldedLabel;
  211. bottom = min(bottom, pitem->cyFoldedLabel);
  212. bottom = min(bottom, CLIP_HEIGHT);
  213. prcLabel->bottom = prcLabel->top + bottom;
  214. }
  215. ULONGLONG _GetDistanceToRect(LV* plv, RECT *prcSlot, int x, int y)
  216. {
  217. int xSlotCenter = prcSlot->left + RECTWIDTH(*prcSlot) / 2;
  218. int ySlotCenter = prcSlot->top + RECTHEIGHT(*prcSlot) / 2;
  219. LONGLONG dx = (LONGLONG)(x - xSlotCenter);
  220. LONGLONG dy = (LONGLONG)(y - ySlotCenter);
  221. return (ULONGLONG)(dx * dx) + (ULONGLONG)(dy * dy);
  222. }
  223. // prcSlot returned in Listview Coordinates
  224. void ListView_CalcItemSlotAndRect(LV* plv, LISTITEM* pitem, int* piSlot, RECT* prcSlot)
  225. {
  226. int cxScreen, cyScreen, cSlots, iHit;
  227. POINT pt;
  228. // Determine which slot this item is in by calculating the hit slot for the
  229. // item's x,y position.
  230. short iWorkArea = (plv->nWorkAreas > 0) ? pitem->iWorkArea : -1;
  231. cSlots = ListView_GetSlotCountEx(plv, TRUE, iWorkArea, &cxScreen, &cyScreen);
  232. // Adjust point by current workarea location.
  233. if (iWorkArea >= 0)
  234. {
  235. pt.x = pitem->pt.x - plv->prcWorkAreas[iWorkArea].left;
  236. pt.y = pitem->pt.y - plv->prcWorkAreas[iWorkArea].top;
  237. }
  238. else
  239. {
  240. pt = pitem->pt;
  241. }
  242. iHit = ListView_CalcHitSlot(plv, pt, cSlots, cxScreen, cyScreen);
  243. if (piSlot)
  244. *piSlot = iHit;
  245. if (prcSlot)
  246. ListView_CalcSlotRect(plv, pitem, iHit, cSlots, FALSE, cxScreen, cyScreen, prcSlot);
  247. }
  248. int ListView_FindItemInSlot(LV* plv, short iWorkArea, int iSlotToFind)
  249. {
  250. int iItemFound = -1;
  251. int cItems;
  252. cItems = ListView_Count(plv);
  253. if (cItems == 0 || !ListView_IsRearrangeableView(plv) || plv->hdpaZOrder == NULL || ListView_IsOwnerData( plv ))
  254. {
  255. // nothing to check
  256. }
  257. else
  258. {
  259. int i;
  260. for (i = 0; i < cItems; i++)
  261. {
  262. LISTITEM* pitem = ListView_GetItemPtr(plv, i);
  263. // Only consider items in this workarea.
  264. if (pitem && ((iWorkArea == -1) || (pitem->iWorkArea == iWorkArea)))
  265. {
  266. int iSlot;
  267. ListView_CalcItemSlotAndRect(plv, pitem, &iSlot, NULL);
  268. if (iSlot == iSlotToFind)
  269. {
  270. iItemFound = i;
  271. break;
  272. }
  273. }
  274. }
  275. }
  276. return iItemFound;
  277. }
  278. BOOL ListView_OnInsertMarkHitTest(LV* plv, int x, int y, LPLVINSERTMARK plvim)
  279. {
  280. POINT pt = {x + plv->ptOrigin.x, y + plv->ptOrigin.y};
  281. short iWorkArea = -1;
  282. int cItems;
  283. if (plvim->cbSize != sizeof(LVINSERTMARK))
  284. return FALSE;
  285. if (plv->nWorkAreas)
  286. {
  287. ListView_FindWorkArea(plv, pt, &iWorkArea);
  288. }
  289. cItems = ListView_Count(plv);
  290. if (cItems == 0 || !ListView_IsRearrangeableView(plv) || plv->hdpaZOrder == NULL || ListView_IsOwnerData( plv ))
  291. {
  292. plvim->dwFlags = 0;
  293. plvim->iItem = -1;
  294. }
  295. else
  296. {
  297. ULONGLONG uClosestDistance = (ULONGLONG)-1; // MAX INT
  298. LISTITEM* pClosestItem = NULL;
  299. int iClosestItem = -1;
  300. int iClosestSlot = -1;
  301. RECT rcClosestSlot;
  302. int i;
  303. for (i = 0; i < cItems; i++)
  304. {
  305. // Only consider items in this workarea.
  306. LISTITEM* pitem = ListView_GetItemPtr(plv, i);
  307. if (pitem && ((iWorkArea == -1) || (pitem->iWorkArea == iWorkArea)))
  308. {
  309. int iSlot;
  310. RECT rcSlot;
  311. ListView_CalcItemSlotAndRect(plv, pitem, &iSlot, &rcSlot);
  312. if (PtInRect(&rcSlot, pt))
  313. {
  314. // Hit it. This is as close as we can get.
  315. pClosestItem = pitem;
  316. iClosestItem = i;
  317. iClosestSlot = iSlot;
  318. rcClosestSlot = rcSlot;
  319. break;
  320. }
  321. else
  322. {
  323. // Keep track of closest item in this workarea, in case none are hit.
  324. ULONGLONG uDistance = _GetDistanceToRect(plv, &rcSlot, pt.x, pt.y);
  325. if (uDistance < uClosestDistance)
  326. {
  327. pClosestItem = pitem;
  328. iClosestItem = i;
  329. iClosestSlot = iSlot;
  330. rcClosestSlot = rcSlot;
  331. uClosestDistance = uDistance;
  332. }
  333. }
  334. }
  335. }
  336. if (pClosestItem)
  337. {
  338. BOOL fVert = !((plv->ci.style & LVS_ALIGNMASK) == LVS_ALIGNTOP); // what about lvs_alignbottom?
  339. int iDragSlot = -1;
  340. // For the drag source case, we need the drag slot to compare against
  341. if (-1 != plv->iDrag)
  342. {
  343. LISTITEM* pitemDrag = ListView_GetItemPtr(plv, plv->iDrag);
  344. if (pitemDrag)
  345. ListView_CalcItemSlotAndRect(plv, pitemDrag, &iDragSlot, NULL);
  346. }
  347. // Now that we have the item, calculate before/after
  348. if (fVert)
  349. plvim->dwFlags = (pt.y > (rcClosestSlot.top + (RECTHEIGHT(rcClosestSlot))/2)) ? LVIM_AFTER : 0;
  350. else
  351. plvim->dwFlags = (pt.x > (rcClosestSlot.left + (RECTWIDTH(rcClosestSlot))/2)) ? LVIM_AFTER : 0;
  352. plvim->iItem = iClosestItem;
  353. // If this is the drag source (or right next to it) then ignore the hit.
  354. if (-1 != iDragSlot &&
  355. ((iDragSlot==iClosestSlot) ||
  356. (iDragSlot==(iClosestSlot-1) && !(plvim->dwFlags & LVIM_AFTER)) ||
  357. (iDragSlot==(iClosestSlot+1) && (plvim->dwFlags & LVIM_AFTER))))
  358. {
  359. plvim->dwFlags = 0;
  360. plvim->iItem = -1;
  361. }
  362. else if ((plv->ci.style & LVS_AUTOARRANGE) && !(plv->exStyle & LVS_EX_SINGLEROW) && !fVert) // auto arrange needs to tweak some beginning/end-of-line cases
  363. {
  364. RECT rcViewWorkArea;
  365. if (-1 != iWorkArea)
  366. {
  367. rcViewWorkArea = plv->prcWorkAreas[iWorkArea];
  368. }
  369. else
  370. {
  371. if (plv->rcView.left == RECOMPUTE)
  372. ListView_Recompute(plv);
  373. rcViewWorkArea = plv->rcView;
  374. }
  375. if ((-1 != iDragSlot) && (iClosestSlot > iDragSlot) && !(plvim->dwFlags & LVIM_AFTER))
  376. {
  377. // We're after our drag source, if we're at the beginning of a line
  378. // then the insert mark is actually at the end of the previous line.
  379. if (rcClosestSlot.left - RECTWIDTH(rcClosestSlot)/2 < rcViewWorkArea.left)
  380. {
  381. int iItemPrev = ListView_FindItemInSlot(plv, iWorkArea, iClosestSlot-1);
  382. if (-1 != iItemPrev)
  383. {
  384. plvim->dwFlags = LVIM_AFTER;
  385. plvim->iItem = iItemPrev;
  386. }
  387. }
  388. }
  389. else if (((-1 == iDragSlot) || (iClosestSlot < iDragSlot)) && (plvim->dwFlags & LVIM_AFTER))
  390. {
  391. // We're before our drag source (or there is no drag source), if we're at end of a line
  392. // then the insert mark is actually at the beginning of the next line.
  393. if (rcClosestSlot.right + RECTWIDTH(rcClosestSlot)/2 > rcViewWorkArea.right)
  394. {
  395. int iItemNext = ListView_FindItemInSlot(plv, iWorkArea, iClosestSlot+1);
  396. if (-1 != iItemNext)
  397. {
  398. plvim->dwFlags = 0;
  399. plvim->iItem = iItemNext;
  400. }
  401. }
  402. }
  403. }
  404. }
  405. else
  406. {
  407. // No insert mark.
  408. plvim->dwFlags = 0;
  409. plvim->iItem = -1;
  410. }
  411. }
  412. return TRUE;
  413. }
  414. int ListView_IItemHitTest(LV* plv, int x, int y, UINT* pflags, int *piSubItem)
  415. {
  416. int iHit;
  417. UINT flags;
  418. POINT pt;
  419. RECT rcLabel = {0};
  420. RECT rcIcon = {0};
  421. RECT rcState = {0};
  422. if (piSubItem)
  423. *piSubItem = 0;
  424. // Map window-relative coordinates to view-relative coords...
  425. //
  426. pt.x = x + plv->ptOrigin.x;
  427. pt.y = y + plv->ptOrigin.y;
  428. // If there are any uncomputed items, recompute them now.
  429. //
  430. if (plv->rcView.left == RECOMPUTE)
  431. ListView_Recompute(plv);
  432. flags = 0;
  433. if (ListView_IsOwnerData( plv ))
  434. {
  435. int cSlots;
  436. POINT ptWnd;
  437. LISTITEM item;
  438. int iWidth = 0, iHeight = 0;
  439. cSlots = ListView_GetSlotCount( plv, TRUE, &iWidth, &iHeight );
  440. iHit = ListView_CalcHitSlot( plv, pt, cSlots, iWidth, iHeight );
  441. if (iHit < ListView_Count(plv))
  442. {
  443. ListView_IGetRectsOwnerData( plv, iHit, &rcIcon, &rcLabel, &item, FALSE );
  444. ptWnd.x = x;
  445. ptWnd.y = y;
  446. if (PtInRect(&rcIcon, ptWnd))
  447. {
  448. flags = LVHT_ONITEMICON;
  449. }
  450. else if (PtInRect(&rcLabel, ptWnd) && !ListView_HideLabels(plv))
  451. {
  452. flags = LVHT_ONITEMLABEL;
  453. }
  454. }
  455. }
  456. else
  457. {
  458. for (iHit = 0; (iHit < ListView_Count(plv)); iHit++)
  459. {
  460. LISTITEM* pitem = ListView_FastGetZItemPtr(plv, iHit);
  461. POINT ptItem;
  462. RECT rcBounds; // Only used if ListView_IsBorderSelect
  463. ptItem.x = pitem->pt.x;
  464. ptItem.y = pitem->pt.y;
  465. rcIcon.top = ptItem.y - g_cyIconMargin;
  466. rcLabel.top = ptItem.y + plv->cyIcon + g_cyLabelSpace;
  467. rcLabel.bottom = rcLabel.top + pitem->cyUnfoldedLabel;
  468. if ( !ListView_IsItemUnfoldedPtr(plv, pitem) )
  469. ListView_RefoldLabelRect(plv, &rcLabel, pitem);
  470. // Quick, easy rejection test...
  471. //
  472. if (pt.y < rcIcon.top || pt.y >= rcLabel.bottom)
  473. continue;
  474. rcIcon.left = ptItem.x - g_cxIconMargin;
  475. rcIcon.right = ptItem.x + plv->cxIcon + g_cxIconMargin;
  476. // We need to make sure there is no gap between the icon and label
  477. rcIcon.bottom = rcLabel.top;
  478. if (ListView_IsSimpleSelect(plv) &&
  479. (ListView_IsIconView(plv) || ListView_IsTileView(plv)))
  480. {
  481. rcState.top = rcIcon.top;
  482. rcState.right = rcIcon.right - ((RECTWIDTH(rcIcon) -plv->cxIcon) / 2);
  483. rcState.left = rcState.right - plv->cxState;
  484. rcState.bottom = rcState.top + plv->cyState;
  485. }
  486. else
  487. {
  488. rcState.bottom = ptItem.y + plv->cyIcon;
  489. rcState.right = ptItem.x;
  490. rcState.top = rcState.bottom - plv->cyState;
  491. rcState.left = rcState.right - plv->cxState;
  492. }
  493. if (ListView_HideLabels(plv))
  494. {
  495. CopyRect(&rcBounds, &rcIcon);
  496. }
  497. else
  498. {
  499. rcLabel.left = ptItem.x + (plv->cxIcon / 2) - (ICONCXLABEL(plv, pitem) / 2);
  500. rcLabel.right = rcLabel.left + ICONCXLABEL(plv, pitem);
  501. }
  502. if (plv->cxState && PtInRect(&rcState, pt))
  503. {
  504. flags = LVHT_ONITEMSTATEICON;
  505. }
  506. else if (PtInRect(&rcIcon, pt))
  507. {
  508. flags = LVHT_ONITEMICON;
  509. if (pt.x < rcIcon.left + RECTWIDTH(rcIcon)/10)
  510. flags |= LVHT_ONLEFTSIDEOFICON;
  511. else if (pt.x >= rcIcon.right - RECTWIDTH(rcIcon)/10)
  512. flags |= LVHT_ONRIGHTSIDEOFICON;
  513. }
  514. else if (PtInRect(&rcLabel, pt))
  515. {
  516. flags = LVHT_ONITEMLABEL;
  517. }
  518. else if (ListView_IsBorderSelect(plv) &&
  519. (pitem->state & LVIS_SELECTED) &&
  520. PtInRect(&rcBounds, pt))
  521. {
  522. flags = LVHT_ONITEMICON;
  523. if (pt.x < rcBounds.left + RECTWIDTH(rcBounds)/10)
  524. flags |= LVHT_ONLEFTSIDEOFICON;
  525. else if (pt.x >= rcBounds.right - RECTWIDTH(rcBounds)/10)
  526. flags |= LVHT_ONRIGHTSIDEOFICON;
  527. }
  528. if (flags)
  529. break;
  530. }
  531. }
  532. if (flags == 0)
  533. {
  534. flags = LVHT_NOWHERE;
  535. iHit = -1;
  536. }
  537. else
  538. {
  539. if (!ListView_IsOwnerData( plv ))
  540. {
  541. iHit = DPA_GetPtrIndex(plv->hdpa, ListView_FastGetZItemPtr(plv, iHit));
  542. }
  543. }
  544. *pflags = flags;
  545. return iHit;
  546. }
  547. // REARCHITECT raymondc
  548. // need to pass HDC here isnce it's sometimes called from the paint loop
  549. // This returns rects in Window Coordinates
  550. void ListView_IGetRectsOwnerData( LV* plv,
  551. int iItem,
  552. RECT* prcIcon,
  553. RECT* prcLabel,
  554. LISTITEM* pitem,
  555. BOOL fUsepitem )
  556. {
  557. int itemIconXLabel;
  558. int cxIconMargin;
  559. int cSlots;
  560. // calculate x, y from iItem
  561. cSlots = ListView_GetSlotCount( plv, TRUE, NULL, NULL );
  562. pitem->iWorkArea = 0; // OwnerData doesn't support workareas
  563. ListView_SetIconPos( plv, pitem, iItem, cSlots );
  564. // calculate lable sizes from iItem
  565. ListView_IRecomputeLabelSize( plv, pitem, iItem, NULL, fUsepitem);
  566. if (plv->ci.style & LVS_NOLABELWRAP)
  567. {
  568. // use single label
  569. itemIconXLabel = pitem->cxSingleLabel;
  570. }
  571. else
  572. {
  573. // use multilabel
  574. itemIconXLabel = pitem->cxMultiLabel;
  575. }
  576. cxIconMargin = ListView_GetIconBufferX(plv);
  577. prcIcon->left = pitem->pt.x - cxIconMargin - plv->ptOrigin.x;
  578. prcIcon->right = prcIcon->left + plv->cxIcon + 2 * cxIconMargin;
  579. prcIcon->top = pitem->pt.y - g_cyIconMargin - plv->ptOrigin.y;
  580. prcIcon->bottom = prcIcon->top + plv->cyIcon + 2 * g_cyIconMargin;
  581. prcLabel->left = pitem->pt.x + (plv->cxIcon / 2) - (itemIconXLabel / 2) - plv->ptOrigin.x;
  582. prcLabel->right = prcLabel->left + itemIconXLabel;
  583. prcLabel->top = pitem->pt.y + plv->cyIcon + g_cyLabelSpace - plv->ptOrigin.y;
  584. prcLabel->bottom = prcLabel->top + pitem->cyUnfoldedLabel;
  585. if ( !ListView_IsItemUnfolded(plv, iItem) )
  586. ListView_RefoldLabelRect(plv, prcLabel, pitem);
  587. }
  588. // out:
  589. // prcIcon icon bounds including icon margin area
  590. // This returns rects in Window Coordinates
  591. void ListView_IGetRects(LV* plv, LISTITEM* pitem, UINT fQueryLabelRects, RECT* prcIcon, RECT* prcLabel, LPRECT prcBounds)
  592. {
  593. int cxIconMargin;
  594. ASSERT( !ListView_IsOwnerData( plv ) );
  595. if (pitem->pt.x == RECOMPUTE)
  596. {
  597. ListView_Recompute(plv);
  598. }
  599. if (pitem->pt.x == RECOMPUTE)
  600. {
  601. RECT rcZero = {0};
  602. *prcIcon = *prcLabel = rcZero;
  603. return;
  604. }
  605. cxIconMargin = ListView_GetIconBufferX(plv);
  606. prcIcon->left = pitem->pt.x - cxIconMargin - plv->ptOrigin.x;
  607. prcIcon->right = prcIcon->left + plv->cxIcon + 2 * cxIconMargin;
  608. prcIcon->top = pitem->pt.y - g_cyIconMargin - plv->ptOrigin.y;
  609. prcIcon->bottom = prcIcon->top + plv->cyIcon + 2 * g_cyIconMargin;
  610. prcLabel->left = pitem->pt.x + (plv->cxIcon / 2) - (ICONCXLABEL(plv, pitem) / 2) - plv->ptOrigin.x;
  611. prcLabel->right = prcLabel->left + ICONCXLABEL(plv, pitem);
  612. prcLabel->top = pitem->pt.y + plv->cyIcon + g_cyLabelSpace - plv->ptOrigin.y;
  613. prcLabel->bottom = prcLabel->top + pitem->cyUnfoldedLabel;
  614. if (IsQueryFolded(fQueryLabelRects) ||
  615. (!ListView_IsItemUnfoldedPtr(plv, pitem) && !IsQueryUnfolded(fQueryLabelRects)))
  616. {
  617. ListView_RefoldLabelRect(plv, prcLabel, pitem);
  618. }
  619. }
  620. // fWithoutScrollbars==FALSE means that we assume more items are on the screen than will fit, so we'll have a scrollbar.
  621. int ListView_GetSlotCountEx(LV* plv, BOOL fWithoutScrollbars, int iWorkArea, int *piWidth, int *piHeight)
  622. {
  623. int cxScreen;
  624. int cyScreen;
  625. int dxItem;
  626. int dyItem;
  627. int iSlots;
  628. int iSlotsX;
  629. int iSlotsY;
  630. // film strip mode
  631. if (ListView_SingleRow(plv))
  632. {
  633. if(piWidth)
  634. *piWidth = plv->sizeClient.cx;
  635. if(piHeight)
  636. *piHeight = plv->sizeClient.cy;
  637. return MAXINT;
  638. }
  639. // Always assume we have a scrollbar when in group view,
  640. // since our iTotalSlots calculation at the bottom will be wrong in this mode...
  641. if (plv->fGroupView)
  642. fWithoutScrollbars = FALSE;
  643. // Always use the current client window size to determine
  644. //
  645. if ((iWorkArea >= 0 ) && (plv->nWorkAreas > 0))
  646. {
  647. ASSERT(iWorkArea < plv->nWorkAreas);
  648. cxScreen = RECTWIDTH(plv->prcWorkAreas[iWorkArea]);
  649. cyScreen = RECTHEIGHT(plv->prcWorkAreas[iWorkArea]);
  650. }
  651. else
  652. {
  653. if (plv->fGroupView)
  654. {
  655. cxScreen = plv->sizeClient.cx - plv->rcBorder.left - plv->rcBorder.right - plv->paddingRight - plv->paddingLeft;
  656. cyScreen = plv->sizeClient.cy - plv->rcBorder.bottom - plv->rcBorder.top - plv->paddingBottom - plv->paddingTop;
  657. }
  658. else
  659. {
  660. RECT rcClientNoScrollBars;
  661. ListView_GetClientRect(plv, &rcClientNoScrollBars, FALSE, NULL);
  662. cxScreen = RECTWIDTH(rcClientNoScrollBars);
  663. cyScreen = RECTHEIGHT(rcClientNoScrollBars);
  664. if (ListView_IsIScrollView(plv) && !(plv->ci.style & LVS_NOSCROLL))
  665. {
  666. cxScreen = cxScreen - plv->rcViewMargin.left - plv->rcViewMargin.right;
  667. cyScreen = cyScreen - plv->rcViewMargin.top - plv->rcViewMargin.bottom;
  668. }
  669. }
  670. if (cxScreen < 0)
  671. cxScreen = 0;
  672. if (cyScreen < 0)
  673. cyScreen = 0;
  674. }
  675. // If we're assuming the scrollbars are there, shrink width/height as appropriate
  676. if (!fWithoutScrollbars && !(plv->ci.style & LVS_NOSCROLL))
  677. {
  678. switch (plv->ci.style & LVS_ALIGNMASK)
  679. {
  680. case LVS_ALIGNBOTTOM:
  681. case LVS_ALIGNTOP:
  682. cxScreen -= ListView_GetCxScrollbar(plv);
  683. break;
  684. case LVS_ALIGNRIGHT:
  685. default:
  686. case LVS_ALIGNLEFT:
  687. cyScreen -= ListView_GetCyScrollbar(plv);
  688. break;
  689. }
  690. }
  691. _GetCurrentItemSize(plv, &dxItem, &dyItem);
  692. if (!dxItem)
  693. dxItem = 1;
  694. if (!dyItem)
  695. dyItem = 1;
  696. iSlotsX = max(1, (cxScreen) / dxItem);
  697. iSlotsY = max(1, (cyScreen) / dyItem);
  698. // Lets see which direction the view states
  699. switch (plv->ci.style & LVS_ALIGNMASK)
  700. {
  701. case LVS_ALIGNBOTTOM:
  702. //The number of slots are the same as ALIGNTOP;
  703. //So, intentional fall through...
  704. case LVS_ALIGNTOP:
  705. iSlots = iSlotsX;
  706. break;
  707. case LVS_ALIGNRIGHT:
  708. // The number of slots are the same as ALIGNLEFT;
  709. // So, intentional fall through...
  710. default:
  711. case LVS_ALIGNLEFT:
  712. iSlots = iSlotsY;
  713. break;
  714. }
  715. if(piWidth)
  716. *piWidth = cxScreen;
  717. if(piHeight)
  718. *piHeight = cyScreen;
  719. // if we don't have enough slots total on the screen, we're going to have
  720. // a scrollbar, so recompute with the scrollbars on
  721. if (fWithoutScrollbars)
  722. {
  723. int iTotalSlots = (iSlotsX * iSlotsY);
  724. if (iTotalSlots < ListView_Count(plv))
  725. {
  726. iSlots = ListView_GetSlotCountEx(plv, FALSE, iWorkArea, piWidth, piHeight);
  727. }
  728. }
  729. return iSlots;
  730. }
  731. int ListView_GetSlotCount(LV* plv, BOOL fWithoutScrollbars, int *piWidth, int *piHeight)
  732. {
  733. // Make sure this function does exactly the same thing as when
  734. // we had no workareas
  735. return ListView_GetSlotCountEx(plv, fWithoutScrollbars, -1, piWidth, piHeight);
  736. }
  737. // get the pixel row (or col in left align) of pitem
  738. int LV_GetItemPixelRow(LV* plv, LISTITEM* pitem)
  739. {
  740. DWORD dwAlignment = plv->ci.style & LVS_ALIGNMASK;
  741. if((dwAlignment == LVS_ALIGNLEFT) || (dwAlignment == LVS_ALIGNRIGHT))
  742. return pitem->pt.x;
  743. else
  744. return pitem->pt.y;
  745. }
  746. // get the pixel row (or col in left align) of the lowest item
  747. int LV_GetMaxPlacedItem(LV* plv)
  748. {
  749. int i;
  750. int iMaxPlacedItem = 0;
  751. for (i = 0; i < ListView_Count(plv); i++)
  752. {
  753. LISTITEM* pitem = ListView_FastGetItemPtr(plv, i);
  754. if (pitem->pt.y != RECOMPUTE)
  755. {
  756. int iRow = LV_GetItemPixelRow(plv, pitem);
  757. // if the current item is "below" (on right if it's left aligned)
  758. // the lowest placed item, we can start appending
  759. if (!i || iRow > iMaxPlacedItem)
  760. iMaxPlacedItem = iRow;
  761. }
  762. }
  763. return iMaxPlacedItem;;
  764. }
  765. // Get the buffer around an item for rcView calculations and slot offsets
  766. int ListView_GetIconBufferX(LV* plv)
  767. {
  768. if (ListView_IsIconView(plv))
  769. {
  770. return (plv->cxIconSpacing - plv->cxIcon) / 2;
  771. }
  772. else if (ListView_IsTileView(plv))
  773. return g_cxLabelMargin;
  774. else
  775. return 0;
  776. }
  777. int ListView_GetIconBufferY(LV* plv)
  778. {
  779. if (ListView_IsIconView(plv))
  780. return g_cyIconOffset;
  781. else if (ListView_IsTileView(plv))
  782. return g_cyIconMargin;
  783. else
  784. return 0;
  785. }
  786. void ListView_AddViewRectBuffer(LV* plv, RECT* prcView)
  787. {
  788. if (ListView_IsIconView(plv))
  789. {
  790. // we now grow the label size a bit, so we already have the g_cxEdge added/removed
  791. }
  792. else
  793. {
  794. prcView->right += g_cxEdge;
  795. prcView->bottom += g_cyEdge;
  796. }
  797. }
  798. // Calculate rcView, returned in Listview Coordinates
  799. // Returns FALSE if rcView is not calculatable and fNoRecalc is specified
  800. BOOL ListView_ICalcViewRect(LV* plv, BOOL fNoRecalc, RECT* prcView)
  801. {
  802. int i;
  803. ASSERT(ListView_IsIScrollView(plv) && !ListView_IsOwnerData(plv) && !(plv->fGroupView && plv->hdpaGroups));
  804. SetRectEmpty(prcView);
  805. for (i = 0; i < ListView_Count(plv); i++)
  806. {
  807. RECT rcIcon;
  808. RECT rcLabel;
  809. RECT rcItem;
  810. if (fNoRecalc)
  811. {
  812. LISTITEM *pitem = ListView_FastGetItemPtr(plv, i);
  813. if (pitem->pt.x == RECOMPUTE)
  814. {
  815. return FALSE;
  816. }
  817. }
  818. ListView_GetRects(plv, i, QUERY_RCVIEW|QUERY_UNFOLDED, &rcIcon, &rcLabel, &rcItem, NULL);
  819. UnionRect(prcView, prcView, &rcItem);
  820. }
  821. if (!IsRectEmpty(prcView))
  822. {
  823. // Convert to listview coordinates
  824. OffsetRect(prcView, plv->ptOrigin.x, plv->ptOrigin.y);
  825. // Grow it a bit
  826. ListView_AddViewRectBuffer(plv, prcView);
  827. }
  828. return TRUE;
  829. }
  830. BOOL ListView_FixIScrollPositions(LV* plv, BOOL fNoScrollbarUpdate, RECT* prcClient)
  831. {
  832. BOOL fRet = FALSE;
  833. // it's possible for the below ListView_GetClientRect() to force a recalc of rcView
  834. // which can come back to this function. Nothing bad happens, but there's no
  835. // need to do fix the scroll positions until we unwind.
  836. if (!plv->fInFixIScrollPositions)
  837. {
  838. plv->fInFixIScrollPositions = TRUE;
  839. // First, where rcClient is smaller than rcView:
  840. // * rcView.left <= ptOrigin.x <= ptOrigin.x+rcClient.right <= rcView.right
  841. // Second, where rcClient is larger than rcView (no scrollbars visible):
  842. // * ptOrigin.x <= rcView.left <= rcView.right <= ptOrigin.x+rcClient.right
  843. if (!(plv->ci.style & LVS_NOSCROLL))
  844. {
  845. POINT pt = plv->ptOrigin;
  846. RECT rcClient;
  847. if (prcClient)
  848. rcClient = *prcClient; // can be passed in to avoid calling the below function a second time
  849. else
  850. ListView_GetClientRect(plv, &rcClient, TRUE, FALSE);
  851. ASSERT(plv->rcView.left != RECOMPUTE);
  852. if (RECTWIDTH(rcClient) < RECTWIDTH(plv->rcView))
  853. {
  854. if (plv->ptOrigin.x < plv->rcView.left)
  855. plv->ptOrigin.x = plv->rcView.left;
  856. else if (plv->ptOrigin.x > plv->rcView.right - RECTWIDTH(rcClient))
  857. plv->ptOrigin.x = plv->rcView.right - RECTWIDTH(rcClient);
  858. }
  859. else
  860. {
  861. if (plv->rcView.left < plv->ptOrigin.x)
  862. plv->ptOrigin.x = plv->rcView.left;
  863. else if (plv->rcView.right - RECTWIDTH(rcClient) > plv->ptOrigin.x)
  864. plv->ptOrigin.x = plv->rcView.right - RECTWIDTH(rcClient);
  865. }
  866. if (RECTHEIGHT(rcClient) < RECTHEIGHT(plv->rcView))
  867. {
  868. if (plv->ptOrigin.y < plv->rcView.top)
  869. plv->ptOrigin.y = plv->rcView.top;
  870. else if (plv->ptOrigin.y > plv->rcView.bottom - RECTHEIGHT(rcClient))
  871. plv->ptOrigin.y = plv->rcView.bottom - RECTHEIGHT(rcClient);
  872. }
  873. else
  874. {
  875. if (plv->rcView.top < plv->ptOrigin.y)
  876. plv->ptOrigin.y = plv->rcView.top;
  877. else if (plv->rcView.bottom - RECTHEIGHT(rcClient) > plv->ptOrigin.y)
  878. plv->ptOrigin.y = plv->rcView.bottom - RECTHEIGHT(rcClient);
  879. }
  880. fRet = (pt.x != plv->ptOrigin.x) || (pt.y != plv->ptOrigin.y);
  881. }
  882. plv->fInFixIScrollPositions = FALSE;
  883. if (fRet)
  884. {
  885. // Something moved, we need to invalidate
  886. ListView_InvalidateWindow(plv);
  887. if (!fNoScrollbarUpdate)
  888. ListView_UpdateScrollBars(plv);
  889. }
  890. }
  891. return fRet;
  892. }
  893. // Go through and recompute any icon positions and optionally
  894. // icon label dimensions.
  895. //
  896. // This function also recomputes the view bounds rectangle.
  897. //
  898. // The algorithm is to simply search the list for any items needing
  899. // recomputation. For icon positions, we scan possible icon slots
  900. // and check to see if any already-positioned icon intersects the slot.
  901. // If not, the slot is free. As an optimization, we start scanning
  902. // icon slots from the previous slot we found.
  903. //
  904. BOOL ListView_IRecomputeEx(LV* plv, HDPA hdpaSort, int iFrom, BOOL fForce)
  905. {
  906. int i;
  907. int cGroups = 0;
  908. int cWorkAreaSlots[LV_MAX_WORKAREAS];
  909. BOOL fUpdateSB;
  910. // if all the items are unplaced, we can just keep appending
  911. BOOL fAppendAtEnd = (((UINT)ListView_Count(plv)) == plv->uUnplaced);
  912. int iFree;
  913. int iWidestGroup = 0;
  914. BOOL fRet = FALSE;
  915. if (hdpaSort == NULL)
  916. hdpaSort = plv->hdpa;
  917. plv->uUnplaced = 0;
  918. if (!ListView_IsSlotView(plv))
  919. return FALSE;
  920. if (plv->flags & LVF_INRECOMPUTE)
  921. {
  922. return FALSE;
  923. }
  924. plv->flags |= LVF_INRECOMPUTE;
  925. plv->cSlots = ListView_GetSlotCount(plv, FALSE, NULL, NULL);
  926. if (plv->nWorkAreas > 0)
  927. for (i = 0; i < plv->nWorkAreas; i++)
  928. cWorkAreaSlots[i] = ListView_GetSlotCountEx(plv, FALSE, i, NULL, NULL);
  929. // Scan all items for RECOMPUTE, and recompute slot if needed.
  930. //
  931. fUpdateSB = (plv->rcView.left == RECOMPUTE);
  932. if (!ListView_IsOwnerData( plv ))
  933. {
  934. LVFAKEDRAW lvfd; // in case client uses customdraw
  935. LV_ITEM item; // in case client uses customdraw
  936. int iMaxPlacedItem = RECOMPUTE;
  937. item.mask = LVIF_PARAM;
  938. item.iSubItem = 0;
  939. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  940. if (!fAppendAtEnd)
  941. iMaxPlacedItem = LV_GetMaxPlacedItem(plv);
  942. if (plv->fGroupView && plv->hdpaGroups)
  943. {
  944. int iAccumulatedHeight = 0;
  945. int cItems = ListView_Count(plv);
  946. int iGroupItem;
  947. LISTITEM* pitem;
  948. for (iGroupItem = 0; iGroupItem < cItems; iGroupItem++)
  949. {
  950. pitem = ListView_FastGetItemPtr(plv, iGroupItem);
  951. if (!pitem)
  952. break;
  953. if (pitem->cyFoldedLabel == SRECOMPUTE || fForce)
  954. {
  955. // Get the item lParam only if we need it for customdraw
  956. item.iItem = iGroupItem;
  957. item.lParam = pitem->lParam;
  958. if (!LISTITEM_HASASKEDFORGROUP(pitem))
  959. {
  960. item.mask = LVIF_GROUPID;
  961. ListView_OnGetItem(plv, &item);
  962. }
  963. _ListView_RecomputeLabelSize(plv, pitem, iGroupItem, NULL, FALSE);
  964. }
  965. }
  966. if (iFrom > 0)
  967. {
  968. LISTGROUP* pgrpPrev = DPA_FastGetPtr(plv->hdpaGroups, iFrom - 1);
  969. iAccumulatedHeight = pgrpPrev->rc.bottom + plv->paddingBottom;
  970. }
  971. cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  972. for (i = iFrom; i < cGroups; i++)
  973. {
  974. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  975. if (!pgrp) // Huh?
  976. break;
  977. cItems = DPA_GetPtrCount(pgrp->hdpa);
  978. if (cItems == 0)
  979. {
  980. SetRect(&pgrp->rc, 0, 0, 0, 0);
  981. }
  982. else
  983. {
  984. RECT rcBoundsPrev = {0};
  985. iFree = 0;
  986. if (pgrp->pszHeader && (pgrp->cyTitle == 0 || fForce))
  987. {
  988. RECT rc = {0, 0, 1000, 0};
  989. HDC hdc = GetDC(plv->ci.hwnd);
  990. HFONT hfontOld = SelectObject(hdc, plv->hfontGroup);
  991. DrawText(hdc, pgrp->pszHeader, -1, &rc, DT_LV | DT_CALCRECT);
  992. SelectObject(hdc, hfontOld);
  993. ReleaseDC(plv->ci.hwnd, hdc);
  994. pgrp->cyTitle = RECTHEIGHT(rc);
  995. }
  996. iAccumulatedHeight += LISTGROUP_HEIGHT(plv, pgrp);
  997. SetRect(&pgrp->rc, plv->rcBorder.left + plv->paddingLeft, iAccumulatedHeight,
  998. plv->sizeClient.cx - plv->rcBorder.right - plv->paddingRight, 0);
  999. for (iGroupItem = 0; iGroupItem < cItems; iGroupItem++)
  1000. {
  1001. pitem = DPA_FastGetPtr(pgrp->hdpa, iGroupItem);
  1002. if (!pitem)
  1003. break;
  1004. if (iGroupItem > 0)
  1005. {
  1006. RECT rcBounds;
  1007. ListView_SetIconPos(plv, pitem, iFree, plv->cSlots);
  1008. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem, QUERY_DEFAULT,
  1009. NULL, NULL, &rcBounds, NULL);
  1010. if (IntersectRect(&rcBounds, &rcBounds, &rcBoundsPrev))
  1011. iFree++;
  1012. }
  1013. if (ListView_SetIconPos(plv, pitem, iFree, plv->cSlots))
  1014. fRet = TRUE;
  1015. if (!fUpdateSB && LV_IsItemOnViewEdge(plv, pitem))
  1016. fUpdateSB = TRUE;
  1017. if (iFree == 0)
  1018. {
  1019. int cx, cy;
  1020. _GetCurrentItemSize(plv, &cx, &cy);
  1021. iAccumulatedHeight += cy;
  1022. iWidestGroup = max(iWidestGroup, cx);
  1023. }
  1024. else if (iFree % plv->cSlots == 0)
  1025. {
  1026. int cx, cy;
  1027. _GetCurrentItemSize(plv, &cx, &cy);
  1028. iAccumulatedHeight += cy;
  1029. iWidestGroup = max(iWidestGroup, plv->cSlots * cx);
  1030. }
  1031. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem, QUERY_DEFAULT,
  1032. NULL, NULL, &rcBoundsPrev, NULL);
  1033. iFree++;
  1034. }
  1035. pgrp->rc.bottom = iAccumulatedHeight;
  1036. iAccumulatedHeight += plv->rcBorder.bottom + plv->paddingBottom;
  1037. }
  1038. }
  1039. // Now iterate through the items and Reset the position of items that aren't associated with a group
  1040. for (i = 0; i < ListView_Count(plv); i++)
  1041. {
  1042. LISTITEM* pitem = ListView_FastGetItemPtr(plv, i);
  1043. if (pitem->pGroup == NULL)
  1044. {
  1045. pitem->pt.x = 0;
  1046. pitem->pt.y = 0;
  1047. }
  1048. }
  1049. }
  1050. else
  1051. {
  1052. // Must keep in local variable because ListView_SetIconPos will keep
  1053. // invalidating the iFreeSlot cache while we're looping
  1054. if (fForce)
  1055. plv->iFreeSlot = -1;
  1056. iFree = plv->iFreeSlot;
  1057. for (i = 0; i < ListView_Count(plv); i++)
  1058. {
  1059. int cRealSlots;
  1060. LISTITEM* pitem = ListView_FastGetItemPtr(plv, i);
  1061. BOOL fRedraw = FALSE;
  1062. cRealSlots = (plv->nWorkAreas > 0) ? cWorkAreaSlots[pitem->iWorkArea] : plv->cSlots;
  1063. if (pitem->pt.y == RECOMPUTE || fForce)
  1064. {
  1065. if (pitem->cyFoldedLabel == SRECOMPUTE || fForce)
  1066. {
  1067. // Get the item lParam only if we need it for customdraw
  1068. item.iItem = i;
  1069. item.lParam = pitem->lParam;
  1070. _ListView_RecomputeLabelSize(plv, pitem, i, NULL, FALSE);
  1071. }
  1072. if (i < ListView_Count(plv)) // Recompute could have nuked some items.
  1073. {
  1074. if (!fForce)
  1075. {
  1076. // (dli) This function gets a new icon postion and then goes
  1077. // through the whole set of items to see if that position is occupied
  1078. // should let it know in the multi-workarea case, it only needs to go
  1079. // through those who are in the same workarea.
  1080. // This is okay for now because we cannot have too many items on the
  1081. // desktop.
  1082. if (lvfd.nmcd.nmcd.hdc)
  1083. {
  1084. int iWidth = 0, iHeight = 0;
  1085. DWORD dwAlignment = (plv->ci.style & LVS_ALIGNMASK);
  1086. // If we are right or bottom aligned, then..
  1087. // ....we want to get the width and height to be passed to FindFreeSlot.
  1088. if ((dwAlignment == LVS_ALIGNRIGHT) || (dwAlignment == LVS_ALIGNBOTTOM))
  1089. ListView_GetSlotCountEx(plv, FALSE, pitem->iWorkArea, &iWidth, &iHeight);
  1090. iFree = ListView_FindFreeSlot(plv, i, iFree + 1, cRealSlots, QUERY_FOLDED, &fUpdateSB, &fAppendAtEnd, lvfd.nmcd.nmcd.hdc, iWidth, iHeight);
  1091. }
  1092. ASSERT(iFree != -1);
  1093. }
  1094. else
  1095. {
  1096. iFree++;
  1097. }
  1098. // If this slot is frozen, then move this item to the next slot!
  1099. if ((plv->iFrozenSlot != -1) && (plv->iFrozenSlot == iFree))
  1100. {
  1101. iFree++; // Skip the frozen slot!
  1102. }
  1103. if (ListView_SetIconPos(plv, pitem, iFree, cRealSlots))
  1104. fRet = TRUE;
  1105. if (!fAppendAtEnd)
  1106. {
  1107. //// optimization. each time we calc a new free slot, we iterate through all the items to see
  1108. // if any of the freely placed items collide with this.
  1109. // fAppendAtEnd indicates that iFree is beyond any freely placed item
  1110. //
  1111. // if the current item is "below" (on right if it's left aligned)
  1112. // the lowest placed item, we can start appending
  1113. if (LV_GetItemPixelRow(plv, pitem) > iMaxPlacedItem)
  1114. fAppendAtEnd = TRUE;
  1115. }
  1116. if (!fUpdateSB && LV_IsItemOnViewEdge(plv, pitem))
  1117. fUpdateSB = TRUE;
  1118. fRedraw = TRUE;
  1119. }
  1120. }
  1121. if (fRedraw)
  1122. {
  1123. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
  1124. }
  1125. // If the item that we just positioned is marked a frozen item...
  1126. if ((pitem == plv->pFrozenItem) && (iFree >= 0))
  1127. {
  1128. //... then we need to ignore that in free slot calculations.
  1129. iFree--;
  1130. }
  1131. }
  1132. plv->iFreeSlot = iFree;
  1133. }
  1134. ListView_EndFakeCustomDraw(&lvfd);
  1135. }
  1136. // If we changed something, recompute the view rectangle
  1137. // and then update the scroll bars.
  1138. //
  1139. if (fUpdateSB || plv->rcView.left == RECOMPUTE || fForce)
  1140. {
  1141. // NOTE: No infinite recursion results because we're setting
  1142. // plv->rcView.left != RECOMPUTE
  1143. //
  1144. SetRectEmpty(&plv->rcView);
  1145. if (plv->fGroupView && plv->hdpaGroups && !ListView_IsOwnerData( plv ))
  1146. {
  1147. LISTGROUP* pgrp;
  1148. int iGroup;
  1149. // Find the first group with an item in it...
  1150. for (iGroup = 0; iGroup < cGroups; iGroup++)
  1151. {
  1152. pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  1153. if (DPA_GetPtrCount(pgrp->hdpa) > 0)
  1154. {
  1155. plv->rcView.top = pgrp->rc.top - max(plv->rcBorder.top, pgrp->cyTitle + 6) - plv->paddingTop;
  1156. plv->rcView.left = pgrp->rc.left - plv->rcBorder.left - plv->paddingLeft;
  1157. break;
  1158. }
  1159. }
  1160. // ...and the last group with an item in it
  1161. for (iGroup = cGroups - 1; iGroup >= 0; iGroup--)
  1162. {
  1163. pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  1164. if (DPA_GetPtrCount(pgrp->hdpa))
  1165. {
  1166. plv->rcView.bottom = pgrp->rc.bottom + plv->rcBorder.bottom + plv->paddingBottom;
  1167. break;
  1168. }
  1169. }
  1170. plv->rcView.right = iWidestGroup;
  1171. }
  1172. else
  1173. {
  1174. if (ListView_IsOwnerData( plv ))
  1175. {
  1176. TraceMsg(TF_GENERAL, "************ LV: Expensive update! ******* ");
  1177. if (ListView_Count( plv ) > 0)
  1178. {
  1179. RECT rcLast;
  1180. RECT rcItem;
  1181. int iSlots;
  1182. int iItem = ListView_Count( plv ) - 1;
  1183. ListView_GetRects( plv, 0, QUERY_DEFAULT, NULL, NULL, &plv->rcView, NULL );
  1184. ListView_GetRects( plv, iItem, QUERY_DEFAULT, NULL, NULL, &rcLast, NULL );
  1185. plv->rcView.right = rcLast.right;
  1186. plv->rcView.bottom = rcLast.bottom;
  1187. //
  1188. // calc how far back in the list to check
  1189. //
  1190. iSlots = plv->cSlots + 2;
  1191. // REVIEW: This cache hint notification causes a spurious
  1192. // hint, since this happens often but is always the last items
  1193. // available. Should this hint be done at all and this information
  1194. // be cached local to the control?
  1195. ListView_NotifyCacheHint( plv, max( 0, iItem - iSlots), iItem );
  1196. // move backwards from last item until either rc.right or
  1197. // rc.left is greater than the last, then use that value.
  1198. // Note: This code makes very little assumptions about the ordering
  1199. // done. We should be careful as multiple line text fields could
  1200. // mess us up.
  1201. for( iItem--;
  1202. (iSlots > 0) && (iItem >= 0);
  1203. iSlots--, iItem--)
  1204. {
  1205. RECT rcIcon;
  1206. RECT rcLabel;
  1207. ListView_GetRects( plv, iItem, QUERY_RCVIEW|QUERY_UNFOLDED, &rcIcon, &rcLabel, &rcItem, NULL );
  1208. if (rcItem.right > rcLast.right)
  1209. {
  1210. plv->rcView.right = rcItem.right;
  1211. }
  1212. if (rcItem.bottom > rcLast.bottom)
  1213. {
  1214. plv->rcView.bottom = rcItem.bottom;
  1215. }
  1216. }
  1217. // The above calculations were done in Window coordinates, convert to Listview coordinates
  1218. OffsetRect(&plv->rcView, plv->ptOrigin.x, plv->ptOrigin.y);
  1219. }
  1220. }
  1221. else
  1222. {
  1223. ListView_ICalcViewRect(plv, FALSE, &plv->rcView);
  1224. }
  1225. }
  1226. //TraceMsg(DM_TRACE, "RECOMPUTE: rcView %x %x %x %x", plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
  1227. //TraceMsg(DM_TRACE, "Origin %x %x", plv->ptOrigin.x, plv->ptOrigin.y);
  1228. ListView_UpdateScrollBars(plv);
  1229. }
  1230. ListView_RecalcRegion(plv, FALSE, TRUE);
  1231. // Now state we are out of the recompute...
  1232. plv->flags &= ~LVF_INRECOMPUTE;
  1233. ASSERT(ListView_ValidatercView(plv, &plv->rcView, TRUE));
  1234. ASSERT(ListView_ValidateScrollPositions(plv, NULL));
  1235. return fRet;
  1236. }
  1237. //This function finds out the nearest work area where the given item must fall.
  1238. int NearestWorkArea(LV *plv, int x, int y, int cxItem, int cyItem, int iCurrentWorkArea)
  1239. {
  1240. int iIndex = 0;
  1241. POINT ptItemCenter = {x + (cxItem/2), y + (cyItem/2)}; //Get the center of the item.
  1242. if(plv->nWorkAreas <= 0) //If this is a single monitor system...
  1243. return -1; //... then return -1 to indicate that.
  1244. if(plv->nWorkAreas == 1)
  1245. return 0; //There is only one workarea; so, return it's index.
  1246. //Find the work-area where the center of the icon falls.
  1247. iIndex = iCurrentWorkArea; // This point is most likely to be in the current work area.
  1248. do // So, start with that work area.
  1249. {
  1250. if(PtInRect(&plv->prcWorkAreas[iIndex], ptItemCenter))
  1251. return iIndex;
  1252. if(++iIndex == plv->nWorkAreas) //If we have reached the end,...
  1253. iIndex = 0; // ...start from the begining.
  1254. // If we have completed looking at all workareas...
  1255. // ....quit the loop!
  1256. } while (iIndex != iCurrentWorkArea);
  1257. return iCurrentWorkArea; //If it does not fall into any of the work areas, then use the current one.
  1258. }
  1259. // This function modifies *px, *py to to be in slot-like position -- it doesn't actually gurantee it's in a real slot
  1260. // (NOTE: I tried to assert that this way of calculating slots matched with ListView_CalcSlotRect, but the latter
  1261. // function guarantee's it's in a real slot and this can not. That assert flushed out real bugs in callers of listview,
  1262. // but it unfortunately hits valid cases as well.)
  1263. void NearestSlot(LV *plv, LISTITEM* pitem, int *px, int *py, int cxItem, int cyItem, LPRECT prcWork)
  1264. {
  1265. DWORD dwAlignment = plv->ci.style & LVS_ALIGNMASK;
  1266. int iWidth = 0;
  1267. int iHeight = 0;
  1268. int x = *px;
  1269. int y = *py;
  1270. if (prcWork != NULL)
  1271. {
  1272. x = x - prcWork->left;
  1273. y = y - prcWork->top;
  1274. }
  1275. //Get the x with respect to the top right corner.
  1276. if (dwAlignment == LVS_ALIGNRIGHT)
  1277. {
  1278. x = (iWidth = (prcWork? RECTWIDTH(*prcWork) : plv->sizeClient.cx)) - x - cxItem;
  1279. }
  1280. else if (dwAlignment == LVS_ALIGNBOTTOM) //Get y with respect to the bottom left corner.
  1281. {
  1282. y = (iHeight = (prcWork? RECTHEIGHT(*prcWork) : plv->sizeClient.cy)) - y - cyItem;
  1283. }
  1284. #if 0
  1285. // The above iWidth/iHeight calculations are incorrect in some cases, but I don't think that matters...
  1286. // We might want to consider replacing the above code with ListView_GetSlotCountEx...
  1287. if (TRUE)
  1288. {
  1289. int iWidth2, iHeight2;
  1290. ListView_GetSlotCountEx(plv, TRUE, prcWork ? pitem->iWorkArea : -1, &iWidth2, &iHeight2);
  1291. if (dwAlignment == LVS_ALIGNRIGHT)
  1292. {
  1293. ASSERT(iWidth == iWidth2);
  1294. }
  1295. else if (dwAlignment == LVS_ALIGNBOTTOM)
  1296. {
  1297. ASSERT(iHeight == iHeight2);
  1298. }
  1299. }
  1300. #endif
  1301. // Calculate the center point
  1302. if (x < 0)
  1303. x -= cxItem/2;
  1304. else
  1305. x += cxItem/2;
  1306. if (y < 0)
  1307. y -= cyItem/2;
  1308. else
  1309. y += cyItem/2;
  1310. // Find the new x,y point
  1311. x = x - (x % cxItem);
  1312. y = y - (y % cyItem);
  1313. // Get x and y with respect to the top left corner again.
  1314. if (dwAlignment == LVS_ALIGNRIGHT)
  1315. x = iWidth - x - cxItem;
  1316. else if (dwAlignment == LVS_ALIGNBOTTOM)
  1317. y = iHeight - y - cyItem;
  1318. if (prcWork != NULL)
  1319. {
  1320. x = x + prcWork->left;
  1321. y = y + prcWork->top;
  1322. }
  1323. *px = x;
  1324. *py = y;
  1325. }
  1326. //-------------------------------------------------------------------
  1327. //
  1328. //-------------------------------------------------------------------
  1329. void ListView_CalcMinMaxIndex( LV* plv, PRECT prcBounding, int* iMin, int* iMax )
  1330. {
  1331. POINT pt;
  1332. int cSlots;
  1333. int iWidth = 0, iHeight = 0;
  1334. cSlots = ListView_GetSlotCount( plv, TRUE, &iWidth, &iHeight );
  1335. pt.x = prcBounding->left + plv->ptOrigin.x;
  1336. pt.y = prcBounding->top + plv->ptOrigin.y;
  1337. *iMin = ListView_CalcHitSlot( plv, pt, cSlots, iWidth, iHeight );
  1338. pt.x = prcBounding->right + plv->ptOrigin.x;
  1339. pt.y = prcBounding->bottom + plv->ptOrigin.y;
  1340. *iMax = ListView_CalcHitSlot( plv, pt, cSlots, iWidth, iHeight ) + 1;
  1341. }
  1342. //-------------------------------------------------------------------
  1343. //
  1344. // Function: ListView_CalcHitSlot
  1345. //
  1346. // Summary: Given a point (relative to complete icon view), calculate
  1347. // which slot that point is closest to.
  1348. //
  1349. // Arguments:
  1350. // plv [in] - List view to work with
  1351. // pt [in] - location to check with
  1352. // cslot [in] - number of slots wide the current view is
  1353. //
  1354. // Notes: This does not guarentee that the point is hitting the item
  1355. // located at that slot. That should be checked by comparing rects.
  1356. //
  1357. // History:
  1358. // Nov-1-1994 MikeMi Added to improve Ownerdata hit testing
  1359. // July-11-2000 Sankar Added iWidth and iHeight parameters to support Right alignment.
  1360. //
  1361. //-------------------------------------------------------------------
  1362. int ListView_CalcHitSlot( LV* plv, POINT pt, int cSlot, int iWidth, int iHeight)
  1363. {
  1364. int cxItem;
  1365. int cyItem;
  1366. int iSlot = 0;
  1367. ASSERT(plv);
  1368. if (cSlot < 1)
  1369. cSlot = 1;
  1370. _GetCurrentItemSize(plv, &cxItem, &cyItem);
  1371. // Lets see which direction the view states
  1372. switch (plv->ci.style & LVS_ALIGNMASK)
  1373. {
  1374. case LVS_ALIGNBOTTOM:
  1375. iSlot = min (pt.x / cxItem, cSlot - 1) + ((iHeight - pt.y) / cyItem) * cSlot;
  1376. break;
  1377. case LVS_ALIGNTOP:
  1378. iSlot = min (pt.x / cxItem, cSlot - 1) + (pt.y / cyItem) * cSlot;
  1379. break;
  1380. case LVS_ALIGNLEFT:
  1381. iSlot = (pt.x / cxItem) * cSlot + min (pt.y / cyItem, cSlot - 1);
  1382. break;
  1383. case LVS_ALIGNRIGHT:
  1384. iSlot = ((iWidth - pt.x) / cxItem) * cSlot + min (pt.y / cyItem, cSlot - 1);
  1385. break;
  1386. }
  1387. return( iSlot );
  1388. }
  1389. DWORD ListView_IApproximateViewRect(LV* plv, int iCount, int iWidth, int iHeight)
  1390. {
  1391. int cxSave = plv->sizeClient.cx;
  1392. int cySave = plv->sizeClient.cy;
  1393. int cxItem;
  1394. int cyItem;
  1395. int cCols;
  1396. int cRows;
  1397. plv->sizeClient.cx = iWidth;
  1398. plv->sizeClient.cy = iHeight;
  1399. cCols = ListView_GetSlotCount(plv, TRUE, NULL, NULL);
  1400. plv->sizeClient.cx = cxSave;
  1401. plv->sizeClient.cy = cySave;
  1402. cCols = min(cCols, iCount);
  1403. if (cCols == 0)
  1404. cCols = 1;
  1405. cRows = (iCount + cCols - 1) / cCols;
  1406. if (plv->ci.style & (LVS_ALIGNLEFT | LVS_ALIGNRIGHT)) {
  1407. int c;
  1408. c = cCols;
  1409. cCols = cRows;
  1410. cRows = c;
  1411. }
  1412. _GetCurrentItemSize(plv, &cxItem, &cyItem);
  1413. iWidth = cCols * cxItem;
  1414. iHeight = cRows * cyItem;
  1415. return MAKELONG(iWidth + g_cxEdge, iHeight + g_cyEdge);
  1416. }
  1417. // if fBias is specified, slot rect is returned in Window Coordinates
  1418. // otherwise, slot rect is returned in Listview Coordinates
  1419. void ListView_CalcSlotRect(LV* plv, LISTITEM *pItem, int iSlot, int cSlot, BOOL fBias, int iWidth, int iHeight, LPRECT lprc)
  1420. {
  1421. int cxItem, cyItem;
  1422. ASSERT(plv);
  1423. if (cSlot < 1)
  1424. cSlot = 1;
  1425. _GetCurrentItemSize(plv, &cxItem, &cyItem);
  1426. // Lets see which direction the view states
  1427. switch (plv->ci.style & LVS_ALIGNMASK)
  1428. {
  1429. case LVS_ALIGNBOTTOM:
  1430. lprc->left = (iSlot % cSlot) * cxItem;
  1431. lprc->top = iHeight - ((iSlot / cSlot)+1) * cyItem;
  1432. break;
  1433. case LVS_ALIGNTOP:
  1434. lprc->left = (iSlot % cSlot) * cxItem;
  1435. lprc->top = (iSlot / cSlot) * cyItem;
  1436. break;
  1437. case LVS_ALIGNRIGHT:
  1438. lprc->top = (iSlot % cSlot) * cyItem;
  1439. lprc->left = iWidth - (((iSlot / cSlot)+1) * cxItem);
  1440. break;
  1441. case LVS_ALIGNLEFT:
  1442. lprc->top = (iSlot % cSlot) * cyItem;
  1443. lprc->left = (iSlot / cSlot) * cxItem;
  1444. break;
  1445. }
  1446. if (fBias)
  1447. {
  1448. lprc->left -= plv->ptOrigin.x;
  1449. lprc->top -= plv->ptOrigin.y;
  1450. }
  1451. lprc->bottom = lprc->top + cyItem;
  1452. lprc->right = lprc->left + cxItem;
  1453. // Multi-Workarea case offset from the workarea coordinate to the whole
  1454. // listview coordinate.
  1455. if (plv->nWorkAreas > 0)
  1456. {
  1457. ASSERT(pItem);
  1458. ASSERT(pItem->iWorkArea < plv->nWorkAreas);
  1459. OffsetRect(lprc, plv->prcWorkAreas[pItem->iWorkArea].left, plv->prcWorkAreas[pItem->iWorkArea].top);
  1460. }
  1461. if (plv->fGroupView)
  1462. {
  1463. if (pItem &&
  1464. LISTITEM_HASGROUP(pItem))
  1465. {
  1466. OffsetRect(lprc, pItem->pGroup->rc.left, pItem->pGroup->rc.top);
  1467. }
  1468. }
  1469. }
  1470. // Intersect this rectangle with all items in this listview except myself,
  1471. // this will determine if this rectangle overlays any icons.
  1472. BOOL ListView_IsCleanRect(LV * plv, RECT * prc, int iExcept, UINT fQueryLabelRects, BOOL * pfUpdate, HDC hdc)
  1473. {
  1474. int j;
  1475. RECT rc;
  1476. int cItems = ListView_Count(plv);
  1477. for (j = cItems; j-- > 0; )
  1478. {
  1479. if (j == iExcept)
  1480. continue;
  1481. else
  1482. {
  1483. LISTITEM* pitem = ListView_FastGetItemPtr(plv, j);
  1484. if (pitem->pt.y != RECOMPUTE)
  1485. {
  1486. // If the dimensions aren't computed, then do it now.
  1487. //
  1488. if (pitem->cyFoldedLabel == SRECOMPUTE)
  1489. {
  1490. _ListView_RecomputeLabelSize(plv, pitem, j, hdc, FALSE);
  1491. // Ensure that the item gets redrawn...
  1492. //
  1493. ListView_InvalidateItem(plv, j, FALSE, RDW_INVALIDATE | RDW_ERASE);
  1494. // Set flag indicating that scroll bars need to be
  1495. // adjusted.
  1496. //
  1497. if (LV_IsItemOnViewEdge(plv, pitem))
  1498. *pfUpdate = TRUE;
  1499. }
  1500. ListView_GetRects(plv, j, fQueryLabelRects, NULL, NULL, &rc, NULL);
  1501. if (IntersectRect(&rc, &rc, prc))
  1502. return FALSE;
  1503. }
  1504. }
  1505. }
  1506. return TRUE;
  1507. }
  1508. // Find an icon slot that doesn't intersect an icon.
  1509. // Start search for free slot from slot i.
  1510. //
  1511. int ListView_FindFreeSlot(LV* plv, int iItem, int i, int cSlot, UINT fQueryLabelRects, BOOL* pfUpdate,
  1512. BOOL *pfAppend, HDC hdc, int iWidth, int iHeight)
  1513. {
  1514. RECT rcSlot;
  1515. RECT rcItem;
  1516. RECT rc;
  1517. LISTITEM * pItemLooking = ListView_FastGetItemPtr(plv, iItem);
  1518. ASSERT(!ListView_IsOwnerData( plv ));
  1519. // Horrible N-squared algorithm:
  1520. // enumerate each slot and see if any items intersect it.
  1521. //
  1522. // REVIEW: This is really slow with long lists (e.g., 1000)
  1523. //
  1524. //
  1525. // If the Append at end is set, we should be able to simply get the
  1526. // rectangle of the i-1 element and check against it instead of
  1527. // looking at every other item...
  1528. //
  1529. if (*pfAppend)
  1530. {
  1531. int iPrev = iItem - 1;
  1532. // Be careful about going off the end of the list. (i is a slot
  1533. // number not an item index).
  1534. if (plv->nWorkAreas > 0)
  1535. {
  1536. while (iPrev >= 0)
  1537. {
  1538. LISTITEM * pPrev = ListView_FastGetItemPtr(plv, iPrev);
  1539. if ((pPrev->iWorkArea == pItemLooking->iWorkArea) && (plv->pFrozenItem != pPrev))
  1540. break;
  1541. iPrev--;
  1542. }
  1543. }
  1544. else
  1545. {
  1546. while (iPrev >= 0)
  1547. {
  1548. LISTITEM * pPrev = ListView_FastGetItemPtr(plv, iPrev);
  1549. if (plv->pFrozenItem != pPrev)
  1550. break;
  1551. iPrev--;
  1552. }
  1553. }
  1554. if (iPrev >= 0)
  1555. ListView_GetRects(plv, iPrev, fQueryLabelRects, NULL, NULL, &rcItem, NULL);
  1556. else
  1557. SetRect(&rcItem, 0, 0, 0, 0);
  1558. }
  1559. for ( ; ; i++)
  1560. {
  1561. // Compute view-relative slot rectangle...
  1562. //
  1563. ListView_CalcSlotRect(plv, pItemLooking, i, cSlot, TRUE, iWidth, iHeight, &rcSlot);
  1564. if (*pfAppend)
  1565. {
  1566. if (!IntersectRect(&rc, &rcItem, &rcSlot)) {
  1567. return i; // Found a free slot...
  1568. }
  1569. }
  1570. if (ListView_IsCleanRect(plv, &rcSlot, iItem, fQueryLabelRects, pfUpdate, hdc))
  1571. break;
  1572. }
  1573. return i;
  1574. }
  1575. // Recompute an item's label size (cxLabel/cyLabel). For speed, this function
  1576. // is passed a DC to use for text measurement.
  1577. //
  1578. // If hdc is NULL, then this function will create and initialize a temporary
  1579. // DC, then destroy it. If hdc is non-NULL, then it is assumed to have
  1580. // the correct font already selected into it.
  1581. //
  1582. // fUsepitem means not to use the text of the item. Instead, use the text
  1583. // pointed to by the pitem structure. This is used in two cases.
  1584. //
  1585. // - Ownerdata, because we don't have a real pitem.
  1586. // - Regulardata, where we already found the pitem text (as an optimizatin)
  1587. //
  1588. void ListView_IRecomputeLabelSize(LV* plv, LISTITEM* pitem, int i, HDC hdc, BOOL fUsepitem)
  1589. {
  1590. TCHAR szLabel[CCHLABELMAX + 4];
  1591. TCHAR szLabelFolded[ARRAYSIZE(szLabel) + CCHELLIPSES + CCHELLIPSES];
  1592. int cchLabel;
  1593. RECT rcSingle = {0};
  1594. RECT rcFolded = {0};
  1595. RECT rcUnfolded = {0};
  1596. LVFAKEDRAW lvfd;
  1597. LV_ITEM item;
  1598. ASSERT(plv);
  1599. // the following will use the passed in pitem text instead of calling
  1600. // GetItem. This would be two consecutive calls otherwise, in some cases.
  1601. //
  1602. if (fUsepitem && (pitem->pszText != LPSTR_TEXTCALLBACK))
  1603. {
  1604. Str_GetPtr0(pitem->pszText, szLabel, ARRAYSIZE(szLabel));
  1605. item.lParam = pitem->lParam;
  1606. }
  1607. else
  1608. {
  1609. item.mask = LVIF_TEXT | LVIF_PARAM;
  1610. item.iItem = i;
  1611. item.iSubItem = 0;
  1612. item.pszText = szLabel;
  1613. item.cchTextMax = ARRAYSIZE(szLabel);
  1614. item.stateMask = 0;
  1615. szLabel[0] = TEXT('\0'); // In case the OnGetItem fails
  1616. ListView_OnGetItem(plv, &item);
  1617. if (!item.pszText)
  1618. {
  1619. SetRectEmpty(&rcSingle);
  1620. rcFolded = rcSingle;
  1621. rcUnfolded = rcSingle;
  1622. goto Exit;
  1623. }
  1624. if (item.pszText != szLabel)
  1625. lstrcpyn(szLabel, item.pszText, ARRAYSIZE(szLabel));
  1626. }
  1627. cchLabel = lstrlen(szLabel);
  1628. rcUnfolded.left = rcUnfolded.top = rcUnfolded.bottom = 0;
  1629. rcUnfolded.right = plv->cxIconSpacing - g_cxLabelMargin * 2;
  1630. rcSingle = rcUnfolded;
  1631. rcFolded = rcUnfolded;
  1632. if (cchLabel > 0)
  1633. {
  1634. UINT flags;
  1635. lvfd.nmcd.nmcd.hdc = NULL;
  1636. if (!hdc)
  1637. { // Set up fake customdraw
  1638. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  1639. ListView_BeginFakeItemDraw(&lvfd);
  1640. }
  1641. else
  1642. {
  1643. lvfd.nmcd.nmcd.hdc = hdc; // Use the one the app gave us
  1644. }
  1645. if (lvfd.nmcd.nmcd.hdc != NULL)
  1646. {
  1647. int align;
  1648. if (plv->dwExStyle & WS_EX_RTLREADING)
  1649. {
  1650. align = GetTextAlign(lvfd.nmcd.nmcd.hdc);
  1651. SetTextAlign(lvfd.nmcd.nmcd.hdc, align | TA_RTLREADING);
  1652. }
  1653. DrawText(lvfd.nmcd.nmcd.hdc, szLabel, cchLabel, &rcSingle, (DT_LV | DT_CALCRECT));
  1654. if (plv->ci.style & LVS_NOLABELWRAP)
  1655. {
  1656. flags = DT_LV | DT_CALCRECT;
  1657. }
  1658. else
  1659. {
  1660. flags = DT_LVWRAP | DT_CALCRECT;
  1661. // We only use DT_NOFULLWIDTHCHARBREAK on Korean(949) Memphis and NT5
  1662. if (949 == g_uiACP)
  1663. flags |= DT_NOFULLWIDTHCHARBREAK;
  1664. }
  1665. DrawText(lvfd.nmcd.nmcd.hdc, szLabel, cchLabel, &rcUnfolded, flags);
  1666. //
  1667. // DrawText with DT_MODIFYSTRING is quirky when you enable
  1668. // word ellipses. Once it finds anything that requires ellipses,
  1669. // it stops and doesn't return anything else (even if those other
  1670. // things got displayed).
  1671. //
  1672. lstrcpy(szLabelFolded, szLabel);
  1673. DrawText(lvfd.nmcd.nmcd.hdc, szLabelFolded, cchLabel, &rcFolded, flags | DT_WORD_ELLIPSIS | DT_MODIFYSTRING);
  1674. // If we had to ellipsify, but you can't tell from looking at the
  1675. // rcFolded.bottom and rcUnfolded.bottom, then tweak rcFolded.bottom
  1676. // so the unfoldifier knows that unfolding is worthwhile.
  1677. if (rcFolded.bottom == rcUnfolded.bottom &&
  1678. lstrcmp(szLabel, szLabelFolded))
  1679. {
  1680. // The actual value isn't important, as long as it's greater
  1681. // than rcUnfolded.bottom and CLIP_HEIGHT. We take advantage
  1682. // of the fact that CLIP_HEIGHT is only two lines, so the only
  1683. // problem case is where you have a two-line item and only the
  1684. // first line is ellipsified.
  1685. rcFolded.bottom++;
  1686. }
  1687. if (plv->dwExStyle & WS_EX_RTLREADING)
  1688. {
  1689. SetTextAlign(lvfd.nmcd.nmcd.hdc, align);
  1690. }
  1691. if (!hdc)
  1692. {
  1693. // Clean up fake customdraw
  1694. ListView_EndFakeItemDraw(&lvfd);
  1695. ListView_EndFakeCustomDraw(&lvfd);
  1696. }
  1697. }
  1698. }
  1699. else
  1700. {
  1701. rcFolded.bottom = rcUnfolded.bottom = rcUnfolded.top + plv->cyLabelChar;
  1702. }
  1703. Exit:
  1704. if (pitem)
  1705. {
  1706. int cyEdge;
  1707. pitem->cxSingleLabel = (short)((rcSingle.right - rcSingle.left) + 2 * g_cxLabelMargin);
  1708. pitem->cxMultiLabel = (short)((rcUnfolded.right - rcUnfolded.left) + 2 * g_cxLabelMargin);
  1709. cyEdge = (plv->ci.style & LVS_NOLABELWRAP) ? 0 : g_cyEdge;
  1710. pitem->cyFoldedLabel = (short)((rcFolded.bottom - rcFolded.top) + cyEdge);
  1711. pitem->cyUnfoldedLabel = (short)((rcUnfolded.bottom - rcUnfolded.top) + cyEdge);
  1712. }
  1713. }
  1714. // Set up an icon slot position. Returns FALSE if position didn't change.
  1715. //
  1716. BOOL ListView_SetIconPos(LV* plv, LISTITEM* pitem, int iSlot, int cSlot)
  1717. {
  1718. RECT rc;
  1719. int iWidth = 0, iHeight = 0;
  1720. DWORD dwAlignment;
  1721. ASSERT(plv);
  1722. // We need to compute iWidth and iHeight only if right or bottom aligned.
  1723. dwAlignment = (plv->ci.style & LVS_ALIGNMASK);
  1724. if ((dwAlignment == LVS_ALIGNRIGHT) || (dwAlignment == LVS_ALIGNBOTTOM))
  1725. ListView_GetSlotCountEx(plv, FALSE, pitem->iWorkArea, &iWidth, &iHeight);
  1726. ListView_CalcSlotRect(plv, pitem, iSlot, cSlot, FALSE, iWidth, iHeight, &rc);
  1727. // Offset into the slot so the item will draw at the right place
  1728. rc.left += ListView_GetIconBufferX(plv);
  1729. rc.top += ListView_GetIconBufferY(plv);
  1730. if (rc.left != pitem->pt.x || rc.top != pitem->pt.y)
  1731. {
  1732. LV_AdjustViewRectOnMove(plv, pitem, rc.left, rc.top);
  1733. return TRUE;
  1734. }
  1735. return FALSE;
  1736. }
  1737. // returns rcView in window coordinates
  1738. void ListView_GetViewRect2(LV* plv, RECT* prcView, int cx, int cy)
  1739. {
  1740. if (plv->rcView.left == RECOMPUTE)
  1741. ListView_Recompute(plv);
  1742. *prcView = plv->rcView;
  1743. //
  1744. // Offsets for scrolling.
  1745. //
  1746. if (ListView_IsReportView(plv))
  1747. {
  1748. OffsetRect(prcView, -plv->ptlRptOrigin.x, -plv->ptlRptOrigin.y);
  1749. }
  1750. else
  1751. {
  1752. OffsetRect(prcView, -plv->ptOrigin.x, -plv->ptOrigin.y);
  1753. }
  1754. // desktop's snaptogrid code and defview's "position in lower right corner"
  1755. // assume rcView includes the entire rcClient... The below violates the
  1756. // scrolling rules, so only do this for noscroll listview.
  1757. if (ListView_IsSlotView(plv) && (plv->ci.style & LVS_NOSCROLL))
  1758. {
  1759. RECT rc;
  1760. rc.left = 0;
  1761. rc.top = 0;
  1762. rc.right = cx;
  1763. rc.bottom = cy;
  1764. UnionRect(prcView, prcView, &rc);
  1765. }
  1766. }
  1767. BOOL ListView_OnGetViewRect(LV* plv, RECT* prcView)
  1768. {
  1769. BOOL fRet = FALSE;
  1770. if (prcView)
  1771. {
  1772. ListView_GetViewRect2(plv, prcView, plv->sizeClient.cx, plv->sizeClient.cy);
  1773. fRet = TRUE;
  1774. }
  1775. return fRet;
  1776. }
  1777. // RECTs returned in window coordinates
  1778. DWORD ListView_GetStyleAndClientRectGivenViewRect(LV* plv, RECT *prcViewRect, RECT* prcClient)
  1779. {
  1780. RECT rcClient;
  1781. DWORD style;
  1782. // do this instead of the #else below because
  1783. // in new versus old apps, you may need to add in g_c?Border because of
  1784. // the one pixel overlap...
  1785. GetWindowRect(plv->ci.hwnd, &rcClient);
  1786. if (GetWindowLong(plv->ci.hwnd, GWL_EXSTYLE) & (WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE))
  1787. {
  1788. rcClient.right -= 2 * g_cxEdge;
  1789. rcClient.bottom -= 2 * g_cyEdge;
  1790. }
  1791. rcClient.right -= rcClient.left;
  1792. rcClient.bottom -= rcClient.top;
  1793. if (rcClient.right < 0)
  1794. rcClient.right = 0;
  1795. if (rcClient.bottom < 0)
  1796. rcClient.bottom = 0;
  1797. rcClient.top = rcClient.left = 0;
  1798. style = 0L;
  1799. if (prcViewRect)
  1800. {
  1801. ASSERT(!ListView_IsIScrollView(plv) || ListView_ValidatercView(plv, &plv->rcView, FALSE));
  1802. if ((rcClient.left < rcClient.right) && (rcClient.top < rcClient.bottom))
  1803. {
  1804. RECT rcView = *prcViewRect;
  1805. // IScrollViews ensure scrollpositions based on rectwidth/height,
  1806. // so we can use this current-scroll-position-independant method:
  1807. if (ListView_IsIScrollView(plv))
  1808. {
  1809. do
  1810. {
  1811. if (!(style & WS_HSCROLL) &&
  1812. (RECTWIDTH(rcClient) < RECTWIDTH(rcView)))
  1813. {
  1814. style |= WS_HSCROLL;
  1815. rcClient.bottom -= ListView_GetCyScrollbar(plv);
  1816. }
  1817. if (!(style & WS_VSCROLL) &&
  1818. (RECTHEIGHT(rcClient) < RECTHEIGHT(rcView)))
  1819. {
  1820. style |= WS_VSCROLL;
  1821. rcClient.right -= ListView_GetCxScrollbar(plv);
  1822. }
  1823. }
  1824. while (!(style & WS_HSCROLL) && (RECTWIDTH(rcClient) < RECTWIDTH(rcView)));
  1825. }
  1826. else
  1827. {
  1828. do
  1829. {
  1830. if (!(style & WS_HSCROLL) &&
  1831. (rcView.left < rcClient.left || rcView.right > rcClient.right))
  1832. {
  1833. style |= WS_HSCROLL;
  1834. rcClient.bottom -= ListView_GetCyScrollbar(plv);
  1835. }
  1836. if (!(style & WS_VSCROLL) &&
  1837. (rcView.top < rcClient.top || rcView.bottom > rcClient.bottom))
  1838. {
  1839. style |= WS_VSCROLL;
  1840. rcClient.right -= ListView_GetCxScrollbar(plv);
  1841. }
  1842. }
  1843. while (!(style & WS_HSCROLL) && rcView.right > rcClient.right);
  1844. }
  1845. }
  1846. }
  1847. *prcClient = rcClient;
  1848. return style;
  1849. }
  1850. // prcViewRect used only if fSubScroll is TRUE
  1851. // RECTs returned in window coordinates
  1852. DWORD ListView_GetClientRect(LV* plv, RECT* prcClient, BOOL fSubScroll, RECT *prcViewRect)
  1853. {
  1854. RECT rcView;
  1855. if (fSubScroll)
  1856. {
  1857. ListView_GetViewRect2(plv, &rcView, 0, 0);
  1858. if (prcViewRect)
  1859. *prcViewRect = rcView;
  1860. else
  1861. prcViewRect = &rcView;
  1862. }
  1863. else
  1864. {
  1865. prcViewRect = NULL;
  1866. }
  1867. return ListView_GetStyleAndClientRectGivenViewRect(plv, prcViewRect, prcClient);
  1868. }
  1869. // Note: pitem->iWorkArea must be properly set when calling this. It gets set
  1870. // in LV_AdjustViewRectOnMove().
  1871. int CALLBACK ArrangeIconCompare(LISTITEM* pitem1, LISTITEM* pitem2, LPARAM lParam)
  1872. {
  1873. int v1, v2;
  1874. int iDirection = 1; //Assume "normal" direction
  1875. POINT pt1 = {pitem1->pt.x, pitem1->pt.y};
  1876. POINT pt2 = {pitem2->pt.x, pitem2->pt.y};
  1877. // REVIEW: lParam can be 0 and we fault ... bug in caller, but we might want to be robust here.
  1878. LV* plv = (LV*)lParam;
  1879. int cx, cy;
  1880. // Are these guys in the same workarea? Normalize with respect to topleft of workarea
  1881. if (plv->nWorkAreas)
  1882. {
  1883. if (pitem1->iWorkArea == pitem2->iWorkArea)
  1884. {
  1885. RECT *prcWorkArea = &plv->prcWorkAreas[pitem1->iWorkArea];
  1886. pt1.x -= prcWorkArea->left;
  1887. pt2.x -= prcWorkArea->left;
  1888. pt1.y -= prcWorkArea->top;
  1889. pt2.y -= prcWorkArea->top;
  1890. }
  1891. }
  1892. _GetCurrentItemSize(plv, &cx, &cy);
  1893. switch((WORD)(plv->ci.style & LVS_ALIGNMASK))
  1894. {
  1895. case LVS_ALIGNRIGHT:
  1896. iDirection = -1; //Right alignment results in abonormal direction.
  1897. //Intentional fall through....
  1898. case LVS_ALIGNLEFT:
  1899. // Vertical arrangement
  1900. v1 = pt1.x / cx;
  1901. v2 = pt2.x / cx;
  1902. if (v1 > v2)
  1903. return iDirection;
  1904. else if (v1 < v2)
  1905. return -iDirection;
  1906. else
  1907. {
  1908. if (pt1.y > pt2.y)
  1909. return 1;
  1910. else if (pt1.y < pt2.y)
  1911. return -1;
  1912. }
  1913. break;
  1914. case LVS_ALIGNBOTTOM:
  1915. iDirection = -1; //Bottom alignment results in abnormal direction.
  1916. //Intentional fall through....
  1917. case LVS_ALIGNTOP:
  1918. v1 = pt1.y / cy;
  1919. v2 = pt2.y / cy;
  1920. if (v1 > v2)
  1921. return iDirection;
  1922. else if (v1 < v2)
  1923. return -iDirection;
  1924. else
  1925. {
  1926. if (pt1.x > pt2.x)
  1927. return 1;
  1928. else if (pt1.x < pt2.x)
  1929. return -1;
  1930. }
  1931. break;
  1932. }
  1933. return 0;
  1934. }
  1935. void ListView_CalcBounds(LV* plv, UINT fQueryLabelRects, RECT *prcIcon, RECT *prcLabel, RECT *prcBounds)
  1936. {
  1937. if ( ListView_HideLabels(plv) )
  1938. {
  1939. *prcBounds = *prcIcon;
  1940. }
  1941. else
  1942. {
  1943. UnionRect(prcBounds, prcIcon, prcLabel);
  1944. if (IsQueryrcView(fQueryLabelRects))
  1945. {
  1946. if (ListView_IsIScrollView(plv))
  1947. {
  1948. RECT rcLabel = *prcLabel;
  1949. prcBounds->left -= plv->rcViewMargin.left;
  1950. prcBounds->top -= plv->rcViewMargin.top;
  1951. prcBounds->right += plv->rcViewMargin.right;
  1952. prcBounds->bottom += plv->rcViewMargin.bottom;
  1953. // If no rcViewMargin is set, then we should make sure the label text
  1954. // doesn't actually hit the edge of the screen...
  1955. InflateRect(&rcLabel, g_cxEdge, g_cyEdge);
  1956. UnionRect(prcBounds, prcBounds, &rcLabel);
  1957. }
  1958. }
  1959. }
  1960. }
  1961. // This returns rects in Window Coordinates
  1962. // fQueryLabelRects determins how prcBounds and prcLabel are returned
  1963. void _ListView_GetRectsFromItem(LV* plv, BOOL bSmallIconView,
  1964. LISTITEM *pitem, UINT fQueryLabelRects,
  1965. LPRECT prcIcon, LPRECT prcLabel, LPRECT prcBounds, LPRECT prcSelectBounds)
  1966. {
  1967. RECT rcIcon;
  1968. RECT rcLabel;
  1969. if (!prcIcon)
  1970. prcIcon = &rcIcon;
  1971. if (!prcLabel)
  1972. prcLabel = &rcLabel;
  1973. // Test for NULL item passed in
  1974. if (pitem)
  1975. {
  1976. // This routine is called during ListView_Recompute(), while
  1977. // plv->rcView.left may still be == RECOMPUTE. So, we can't
  1978. // test that to see if recomputation is needed.
  1979. //
  1980. if (pitem->pt.y == RECOMPUTE || pitem->cyFoldedLabel == SRECOMPUTE)
  1981. {
  1982. ListView_Recompute(plv);
  1983. }
  1984. if (bSmallIconView)
  1985. {
  1986. ListView_SGetRects(plv, pitem, prcIcon, prcLabel, prcBounds);
  1987. }
  1988. else if (ListView_IsTileView(plv))
  1989. {
  1990. ListView_TGetRects(plv, pitem, prcIcon, prcLabel, prcBounds);
  1991. }
  1992. else
  1993. {
  1994. ListView_IGetRects(plv, pitem, fQueryLabelRects, prcIcon, prcLabel, prcBounds);
  1995. }
  1996. if (prcBounds)
  1997. {
  1998. ListView_CalcBounds(plv, fQueryLabelRects, prcIcon, prcLabel, prcBounds);
  1999. if (!(ListView_IsSimpleSelect(plv) && (ListView_IsIconView(plv) || ListView_IsTileView(plv))) &&
  2000. plv->himlState && (LV_StateImageValue(pitem)))
  2001. {
  2002. prcBounds->left -= plv->cxState + LV_ICONTOSTATECX;
  2003. }
  2004. }
  2005. }
  2006. else
  2007. {
  2008. SetRectEmpty(prcIcon);
  2009. *prcLabel = *prcIcon;
  2010. if (prcBounds)
  2011. *prcBounds = *prcIcon;
  2012. }
  2013. if (prcSelectBounds)
  2014. {
  2015. if ( ListView_HideLabels(plv) )
  2016. *prcSelectBounds = *prcIcon;
  2017. else
  2018. UnionRect(prcSelectBounds, prcIcon, prcLabel);
  2019. if (!(ListView_IsSimpleSelect(plv) &&
  2020. (ListView_IsIconView(plv) || ListView_IsTileView(plv)))
  2021. && plv->himlState && (LV_StateImageValue(pitem)))
  2022. {
  2023. prcSelectBounds->left -= plv->cxState + LV_ICONTOSTATECX;
  2024. }
  2025. }
  2026. }
  2027. void _ListView_InvalidateItemPtr(LV* plv, BOOL bSmallIcon, LISTITEM *pitem, UINT fRedraw)
  2028. {
  2029. RECT rcBounds;
  2030. ASSERT( !ListView_IsOwnerData( plv ));
  2031. _ListView_GetRectsFromItem(plv, bSmallIcon, pitem, QUERY_DEFAULT, NULL, NULL, &rcBounds, NULL);
  2032. ListView_DebugDrawInvalidRegion(plv, &rcBounds, NULL);
  2033. RedrawWindow(plv->ci.hwnd, &rcBounds, NULL, fRedraw);
  2034. }
  2035. //
  2036. // return TRUE if things still overlap
  2037. // this only happens if we tried to unstack things, and there was NOSCROLL set and
  2038. // items tried to go off the deep end
  2039. //
  2040. // NOTE: This function is written such that the order of icons in hdpaSort is still valid
  2041. // even after unstacking some icons. This is very important because this function gets
  2042. // called twice (one for each direction) and we need to make sure the sort order does not
  2043. // change between those two calls.
  2044. //
  2045. BOOL ListView_IUnstackOverlaps(LV* plv, HDPA hdpaSort, int iDirection, int xMargin, int yMargin, BOOL *pfIconsUnstacked)
  2046. {
  2047. int i;
  2048. int iCount;
  2049. BOOL bSmallIconView = ListView_IsSmallView(plv);
  2050. RECT rcItem, rcItem2, rcTemp;
  2051. int cxItem, cyItem;
  2052. LISTITEM* pitem;
  2053. LISTITEM* pitem2;
  2054. int iStartIndex, iEndIndex;
  2055. BOOL fAdjustY;
  2056. int iNextPrevCol = 1;
  2057. int iNextPrevRow = 1;
  2058. int iSlots;
  2059. int iCurWorkArea;
  2060. RECT rcCurWorkArea;
  2061. BOOL fRet = FALSE;
  2062. ASSERT( !ListView_IsOwnerData( plv ) );
  2063. _GetCurrentItemSize(plv, &cxItem, &cyItem);
  2064. iCount = ListView_Count(plv);
  2065. //
  2066. // Get the direction in which we need to move the icons.
  2067. //
  2068. if(iDirection == 1)
  2069. {
  2070. iStartIndex = 0; //We are starting with icon "0"...
  2071. iEndIndex = iCount - 1; //...and moving towards the last icon.
  2072. }
  2073. else
  2074. {
  2075. ASSERT(iDirection == -1);
  2076. iStartIndex = iCount - 1; //We are starting with the last icon...
  2077. iEndIndex = 0; //..and moving towards the "0"th icon.
  2078. }
  2079. //
  2080. // Look at the alignment of the icons to decide if we need to move them up/down or
  2081. // left/right.
  2082. //
  2083. switch (plv->ci.style & LVS_ALIGNMASK)
  2084. {
  2085. case LVS_ALIGNBOTTOM:
  2086. iNextPrevRow = -1;
  2087. //Intentional fall-through!
  2088. case LVS_ALIGNTOP:
  2089. fAdjustY = FALSE;
  2090. break;
  2091. case LVS_ALIGNRIGHT:
  2092. iNextPrevCol = -1;
  2093. //Intentional fall-through!
  2094. case LVS_ALIGNLEFT:
  2095. default:
  2096. fAdjustY = TRUE;
  2097. break;
  2098. }
  2099. *pfIconsUnstacked = FALSE;
  2100. // Give an unusual value to iCurWorkArea so that we will be forced to compute the
  2101. // rcCurWorkArea when we go through the loop the first time.
  2102. iCurWorkArea = -2;
  2103. // finally, unstack any overlaps
  2104. for (i = iStartIndex ; i != (iEndIndex + iDirection) ; i += iDirection)
  2105. {
  2106. int j;
  2107. pitem = DPA_GetPtr(hdpaSort, i);
  2108. if (bSmallIconView)
  2109. {
  2110. _ListView_GetRectsFromItem(plv, bSmallIconView, pitem, QUERY_FOLDED, NULL, NULL, &rcItem, NULL);
  2111. }
  2112. // move all the items that overlap with pitem
  2113. for (j = i+iDirection ; j != (iEndIndex + iDirection); j += iDirection)
  2114. {
  2115. POINT ptOldPos;
  2116. pitem2 = DPA_GetPtr(hdpaSort, j);
  2117. ptOldPos = pitem2->pt;
  2118. //If an item is being newly added, ignore that item from participating
  2119. //in the Unstacking. Otherwise, it results in all items being shuffled
  2120. //around un-necessarrily!
  2121. if((pitem2->pt.x == RECOMPUTE) || (pitem2->pt.y == RECOMPUTE))
  2122. break; //break out of the loop!
  2123. //
  2124. //Check if pitem and pitem2 overlap; If so, move pitem2 to the next position.
  2125. //
  2126. if (bSmallIconView)
  2127. {
  2128. // for small icons, we need to do an intersect rect
  2129. _ListView_GetRectsFromItem(plv, bSmallIconView, pitem2, QUERY_FOLDED, NULL, NULL, &rcItem2, NULL);
  2130. if (IntersectRect(&rcTemp, &rcItem, &rcItem2))
  2131. {
  2132. // yes, it intersects. move it out
  2133. *pfIconsUnstacked = TRUE;
  2134. _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
  2135. do
  2136. {
  2137. if(fAdjustY)
  2138. pitem2->pt.y += (cyItem * iDirection);
  2139. else
  2140. pitem2->pt.x += (cxItem * iDirection);
  2141. } while (PtInRect(&rcItem, pitem2->pt));
  2142. }
  2143. else
  2144. {
  2145. // pitem and pitem2 do not overlap...!
  2146. break; //...break out of the loop!
  2147. }
  2148. }
  2149. else
  2150. {
  2151. // for large icons, just find the ones that share the x,y;
  2152. if (pitem2->pt.x == pitem->pt.x && pitem2->pt.y == pitem->pt.y)
  2153. {
  2154. *pfIconsUnstacked = TRUE;
  2155. _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
  2156. if(fAdjustY)
  2157. pitem2->pt.y += (cyItem * iDirection);
  2158. else
  2159. pitem2->pt.x += (cxItem * iDirection);
  2160. }
  2161. else
  2162. {
  2163. // pitem and pitem2 do not overlap...!
  2164. break; //...break out of the loop!
  2165. }
  2166. }
  2167. //
  2168. // Now we know that pitem2 overlapped with pitem and therefore pitem2 had been
  2169. // moved to the "next" possible slot!
  2170. //
  2171. // If scrolling is possible, then we don't have to do anything else. But, if
  2172. // NOSCROLL style is there, we need to check if the icon falls outside the
  2173. // client area and if so move it within.
  2174. //
  2175. if (plv->ci.style & LVS_NOSCROLL)
  2176. {
  2177. //Since our list of icons are sorted based on their positions, the work
  2178. //area change occurs only infrequently.
  2179. if(iCurWorkArea != pitem2->iWorkArea)
  2180. {
  2181. iCurWorkArea = pitem2->iWorkArea;
  2182. if((iCurWorkArea == -1) || (plv->prcWorkAreas == NULL) || (plv->nWorkAreas < 1))
  2183. {
  2184. rcCurWorkArea.left = rcCurWorkArea.top = 0;
  2185. rcCurWorkArea.right = plv->sizeClient.cx;
  2186. rcCurWorkArea.bottom = plv->sizeClient.cy;
  2187. }
  2188. else
  2189. {
  2190. ASSERT(plv->nWorkAreas >= 1);
  2191. rcCurWorkArea = plv->prcWorkAreas[iCurWorkArea];
  2192. }
  2193. //Get the number of slots per row/column based on the alignment style!
  2194. iSlots = ListView_GetSlotCountEx(plv, TRUE, iCurWorkArea, NULL, NULL);
  2195. }
  2196. //No scrolling possible. So, check if the icon lies outside the client area.
  2197. if(fAdjustY)
  2198. {
  2199. if(iDirection == 1)
  2200. {
  2201. //Has it moved below the bottom edge?
  2202. if(pitem2->pt.y > (rcCurWorkArea.bottom - (cyItem/2)))
  2203. {
  2204. //Then, move the item to the next/prev column.
  2205. pitem2->pt.x += iNextPrevCol*cxItem;
  2206. pitem2->pt.y = rcCurWorkArea.top + yMargin;
  2207. *pfIconsUnstacked = TRUE; // while not "unstacked", they did move
  2208. }
  2209. }
  2210. else
  2211. {
  2212. ASSERT(iDirection == -1);
  2213. //Has it moved above the top edge?
  2214. if(pitem2->pt.y < rcCurWorkArea.top)
  2215. {
  2216. //Then, move it to the next/prev column.
  2217. pitem2->pt.x -= iNextPrevCol*cxItem;
  2218. pitem2->pt.y = rcCurWorkArea.top + yMargin + (iSlots - 1)*cyItem;
  2219. *pfIconsUnstacked = TRUE; // while not "unstacked", they did move
  2220. }
  2221. }
  2222. }
  2223. else
  2224. {
  2225. if(iDirection == 1)
  2226. {
  2227. //Has it been moved to the right of the right-edge?
  2228. if(pitem2->pt.x > (rcCurWorkArea.right - (cxItem/2)))
  2229. {
  2230. //Then move the item to the next/prev row.
  2231. pitem2->pt.x = rcCurWorkArea.left + xMargin;
  2232. pitem2->pt.y += iNextPrevRow*cyItem;
  2233. *pfIconsUnstacked = TRUE; // while not "unstacked", they did move
  2234. }
  2235. }
  2236. else
  2237. {
  2238. ASSERT(iDirection == -1);
  2239. //Has is moved to the left of the left-edge?
  2240. if(pitem2->pt.x < rcCurWorkArea.left)
  2241. {
  2242. //Then move the item to the prev/next row.
  2243. pitem2->pt.x = rcCurWorkArea.left + xMargin + (iSlots - 1)*cxItem;
  2244. pitem2->pt.y -= iNextPrevRow*cyItem;
  2245. *pfIconsUnstacked = TRUE; // while not "unstacked", they did move
  2246. }
  2247. }
  2248. }
  2249. // Inspite of all the above adjustments, if it still falls outside the
  2250. // client, then move it back to where it was!
  2251. if (pitem2->pt.x < rcCurWorkArea.left || pitem2->pt.y < rcCurWorkArea.top ||
  2252. pitem2->pt.x > (rcCurWorkArea.right - (cxItem/2))||
  2253. pitem2->pt.y > (rcCurWorkArea.bottom - (cyItem/2)))
  2254. {
  2255. pitem2->pt = ptOldPos;
  2256. fRet = TRUE; // TRUE = >Icons are still overlapped at the corner.
  2257. //When this happens, we have reached the top-left corner or
  2258. //the bottom-right corner of one work area (depending on the direction
  2259. //and alignment)
  2260. //Once we reach a corner, we can't return immediately because there
  2261. //could be icons in other work-areas that need to be unstacked.
  2262. //So, return only if we are working with a single work area.
  2263. if(plv->nWorkAreas <= 1)
  2264. {
  2265. if (*pfIconsUnstacked)
  2266. plv->rcView.left = RECOMPUTE;
  2267. return(fRet);
  2268. }
  2269. }
  2270. }
  2271. // invalidate the new position as well
  2272. _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
  2273. }
  2274. }
  2275. // NOTE: the above code should call LV_AdjustViewRectOnMove instead
  2276. // of modifying item's points directly, but this is the easier fix. This is
  2277. // also not a perf hit, since it's uncommon for items to be stacked.
  2278. if (*pfIconsUnstacked)
  2279. plv->rcView.left = RECOMPUTE;
  2280. return fRet;
  2281. }
  2282. BOOL ListView_SnapToGrid(LV* plv, HDPA hdpaSort)
  2283. {
  2284. // this algorithm can't fit in the structure of the other
  2285. // arrange loop without becoming n^2 or worse.
  2286. // this algorithm is order n.
  2287. // iterate through and snap to the nearest grid.
  2288. // iterate through and push aside overlaps.
  2289. int i;
  2290. int iCount;
  2291. int x,y;
  2292. LISTITEM* pitem;
  2293. int cxItem, cyItem;
  2294. RECT rcClient = {0, 0, plv->sizeClient.cx, plv->sizeClient.cy};
  2295. int xMargin;
  2296. int yMargin;
  2297. BOOL fIconsMoved = FALSE; //Has any icon moved to goto the nearest slot?
  2298. BOOL fIconsUnstacked = FALSE; //Did we unstack any icons?
  2299. ASSERT( !ListView_IsOwnerData( plv ) );
  2300. _GetCurrentItemSize(plv, &cxItem, &cyItem);
  2301. xMargin = ListView_GetIconBufferX(plv);
  2302. yMargin = ListView_GetIconBufferY(plv);
  2303. iCount = ListView_Count(plv);
  2304. // first snap to nearest grid
  2305. for (i = 0; i < iCount; i++)
  2306. {
  2307. int iWorkArea = 0;
  2308. LPRECT prcCurWorkArea;
  2309. pitem = DPA_GetPtr(hdpaSort, i);
  2310. x = pitem->pt.x;
  2311. y = pitem->pt.y;
  2312. //If an item is being newly added, ignore that item from participating
  2313. //in the snap-to-grid. Otherwise, it results in all items being shuffled
  2314. //around un-necessarrily!
  2315. if ((x == RECOMPUTE) || (y == RECOMPUTE))
  2316. continue;
  2317. x -= xMargin;
  2318. y -= yMargin;
  2319. //Let's find the nearest work area (where this icon should fall)
  2320. iWorkArea = NearestWorkArea(plv, x, y, cxItem, cyItem, pitem->iWorkArea);
  2321. if(iWorkArea == -1)
  2322. {
  2323. prcCurWorkArea = &rcClient;
  2324. }
  2325. else
  2326. {
  2327. prcCurWorkArea = &plv->prcWorkAreas[iWorkArea];
  2328. pitem->iWorkArea = (short)iWorkArea;
  2329. }
  2330. NearestSlot(plv, pitem, &x,&y, cxItem, cyItem, prcCurWorkArea);
  2331. x += xMargin;
  2332. y += yMargin;
  2333. if (x != pitem->pt.x || y != pitem->pt.y)
  2334. {
  2335. fIconsMoved = TRUE;
  2336. _ListView_InvalidateItemPtr(plv, ListView_IsSmallView(plv), pitem, RDW_INVALIDATE| RDW_ERASE);
  2337. if (plv->ci.style & LVS_NOSCROLL)
  2338. {
  2339. // if it's marked noscroll, make sure it's still on the client region
  2340. while (x > (prcCurWorkArea->right - cxItem + xMargin))
  2341. x -= cxItem;
  2342. while (x < 0)
  2343. x += cxItem;
  2344. while (y > (prcCurWorkArea->bottom - cyItem + yMargin))
  2345. y -= cyItem;
  2346. while (y < 0)
  2347. y += cyItem;
  2348. }
  2349. LV_AdjustViewRectOnMove(plv, pitem, x, y);
  2350. _ListView_InvalidateItemPtr(plv, ListView_IsSmallView(plv), pitem, RDW_INVALIDATE| RDW_ERASE);
  2351. }
  2352. }
  2353. // now resort the dpa
  2354. if (!DPA_Sort(hdpaSort, ArrangeIconCompare, (LPARAM)plv))
  2355. return FALSE;
  2356. // go in one direction, if there are still overlaps, go in the other
  2357. // direction as well
  2358. if (ListView_IUnstackOverlaps(plv, hdpaSort, 1, xMargin, yMargin, &fIconsUnstacked))
  2359. {
  2360. //The sorting already done by DPA_Sort is still valid!
  2361. BOOL fIconsUnstackedSecondTime = FALSE;
  2362. ListView_IUnstackOverlaps(plv, hdpaSort, -1, xMargin, yMargin, &fIconsUnstackedSecondTime);
  2363. fIconsUnstacked |= fIconsUnstackedSecondTime;
  2364. }
  2365. // If something moved, make sure the scrollbars are correct
  2366. if ((fIconsMoved || fIconsUnstacked))
  2367. {
  2368. ListView_UpdateScrollBars(plv);
  2369. }
  2370. return FALSE;
  2371. }
  2372. BOOL ListView_OnArrange(LV* plv, UINT style)
  2373. {
  2374. HDPA hdpaSort = NULL;
  2375. if (!ListView_IsAutoArrangeView(plv))
  2376. {
  2377. return FALSE;
  2378. }
  2379. if (ListView_IsOwnerData( plv ))
  2380. {
  2381. if ( style & (LVA_SNAPTOGRID | LVA_SORTASCENDING | LVA_SORTDESCENDING) )
  2382. {
  2383. RIPMSG(0, "LVM_ARRANGE: Cannot combine LVA_SNAPTOGRID or LVA_SORTxxx with owner-data");
  2384. return( FALSE );
  2385. }
  2386. }
  2387. if (!ListView_IsOwnerData( plv ))
  2388. {
  2389. // we clone plv->hdpa so we don't blow away indices that
  2390. // apps have saved away.
  2391. // we sort here to make the nested for loop below more bearable.
  2392. hdpaSort = DPA_Clone(plv->hdpa, NULL);
  2393. if (!hdpaSort)
  2394. return FALSE;
  2395. }
  2396. // Give every item a new position...
  2397. if (ListView_IsOwnerData( plv ))
  2398. {
  2399. ListView_CommonArrange(plv, style, NULL);
  2400. }
  2401. else
  2402. {
  2403. if (!DPA_Sort(hdpaSort, ArrangeIconCompare, (LPARAM)plv))
  2404. return FALSE;
  2405. ListView_CommonArrange(plv, style, hdpaSort);
  2406. DPA_Destroy(hdpaSort);
  2407. }
  2408. NotifyWinEvent(EVENT_OBJECT_REORDER, plv->ci.hwnd, OBJID_CLIENT, 0);
  2409. return TRUE;
  2410. }
  2411. BOOL ListView_CommonArrangeGroup(LV* plv, int cSlots, HDPA hdpa, int iWorkArea, int cWorkAreaSlots[])
  2412. {
  2413. int iItem;
  2414. BOOL fItemMoved = FALSE;
  2415. int iSlot = 0;
  2416. // For each group, we start as slot zero.
  2417. for (iItem = 0; iItem < DPA_GetPtrCount(hdpa); iItem++)
  2418. {
  2419. int cRealSlots;
  2420. LISTITEM* pitem = DPA_GetPtr(hdpa, iItem);
  2421. // In the multi-workarea case, if this item is not in our workarea, skip it.
  2422. if (pitem->iWorkArea != iWorkArea)
  2423. continue;
  2424. // Ignore frozen items.
  2425. if (pitem == plv->pFrozenItem)
  2426. continue;
  2427. cRealSlots = (plv->nWorkAreas > 0) ? cWorkAreaSlots[pitem->iWorkArea] : cSlots;
  2428. fItemMoved |= ListView_SetIconPos(plv, pitem, iSlot++, cRealSlots);
  2429. }
  2430. return fItemMoved;
  2431. }
  2432. void ListView_InvalidateWindow(LV* plv)
  2433. {
  2434. if (ListView_RedrawEnabled(plv))
  2435. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  2436. else
  2437. {
  2438. ListView_DeleteHrgnInval(plv);
  2439. plv->hrgnInval = (HRGN)ENTIRE_REGION;
  2440. plv->flags |= LVF_ERASE;
  2441. }
  2442. }
  2443. // Arrange the icons given a sorted hdpa, and arrange them in the sub workareas
  2444. BOOL ListView_CommonArrangeEx(LV* plv, UINT style, HDPA hdpaSort, int iWorkArea)
  2445. {
  2446. if (!ListView_IsOwnerData( plv ))
  2447. {
  2448. BOOL fItemMoved = FALSE;
  2449. BOOL fScrolled = FALSE;
  2450. // We're going to call FixIScrollPositions at the end of this, so turn off
  2451. // scroll-validation while we re-arrange the world
  2452. ASSERT(!plv->fInFixIScrollPositions);
  2453. plv->fInFixIScrollPositions = TRUE;
  2454. if (style == LVA_SNAPTOGRID && !plv->fGroupView)
  2455. {
  2456. // ListView_SnapToGrid() has been made multi-mon aware. This needs to be called
  2457. // just once and it snaps to grid all icons in ALL work areas. Since
  2458. // ListView_CommonArrangeEx() gets called for every work area, we want to avoid
  2459. // un-necessary calls to ListView_SnapToGrid(). So, we call it just once for
  2460. // the first work area.
  2461. if (iWorkArea < 1) // For iWorkArea = 0 or -1.
  2462. {
  2463. fItemMoved |= ListView_SnapToGrid(plv, hdpaSort);
  2464. }
  2465. }
  2466. else
  2467. {
  2468. int cSlots;
  2469. int cWorkAreaSlots[LV_MAX_WORKAREAS];
  2470. if (plv->nWorkAreas > 0)
  2471. {
  2472. int i;
  2473. for (i = 0; i < plv->nWorkAreas; i++)
  2474. cWorkAreaSlots[i] = ListView_GetSlotCountEx(plv, TRUE, i, NULL, NULL);
  2475. }
  2476. else
  2477. cSlots = ListView_GetSlotCount(plv, TRUE, NULL, NULL);
  2478. if (plv->fGroupView && plv->hdpaGroups)
  2479. {
  2480. int iGroup;
  2481. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  2482. for (iGroup = 0; iGroup < cGroups; iGroup++)
  2483. {
  2484. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  2485. fItemMoved |= ListView_CommonArrangeGroup(plv, cSlots, pgrp->hdpa, iWorkArea, cWorkAreaSlots);
  2486. }
  2487. if (fItemMoved)
  2488. {
  2489. ListView_IRecomputeEx(plv, NULL, 0, FALSE);
  2490. }
  2491. }
  2492. else
  2493. {
  2494. fItemMoved |= ListView_CommonArrangeGroup(plv, cSlots, hdpaSort, iWorkArea, cWorkAreaSlots);
  2495. }
  2496. }
  2497. plv->fInFixIScrollPositions = FALSE;
  2498. // We might have to adjust the scroll positions to match the new rcView
  2499. if (ListView_IsIScrollView(plv) && !(plv->ci.style & LVS_NOSCROLL))
  2500. {
  2501. RECT rcClient;
  2502. POINT pt;
  2503. fScrolled |= ListView_FixIScrollPositions(plv, FALSE, NULL);
  2504. // Find the auto arrange origin
  2505. ListView_GetClientRect(plv, &rcClient, TRUE, FALSE);
  2506. if ((plv->ci.style & LVS_ALIGNMASK) == LVS_ALIGNRIGHT)
  2507. pt.x = plv->rcView.right - RECTWIDTH(rcClient);
  2508. else
  2509. pt.x = plv->rcView.left;
  2510. if ((plv->ci.style & LVS_ALIGNMASK) == LVS_ALIGNBOTTOM)
  2511. pt.y = plv->rcView.bottom - RECTHEIGHT(rcClient);
  2512. else
  2513. pt.y = plv->rcView.top;
  2514. // If rcView is smaller than rcClient, peg it to the correct side
  2515. if (RECTWIDTH(rcClient) > RECTWIDTH(plv->rcView))
  2516. {
  2517. if (plv->ptOrigin.x != pt.x)
  2518. {
  2519. plv->ptOrigin.x = pt.x;
  2520. fScrolled = TRUE;
  2521. }
  2522. }
  2523. if (RECTHEIGHT(rcClient) > RECTHEIGHT(plv->rcView))
  2524. {
  2525. if (plv->ptOrigin.y != pt.y)
  2526. {
  2527. plv->ptOrigin.y = pt.y;
  2528. fScrolled = TRUE;
  2529. }
  2530. }
  2531. ASSERT(ListView_ValidateScrollPositions(plv, &rcClient));
  2532. }
  2533. if (fItemMoved || fScrolled)
  2534. {
  2535. int iItem;
  2536. // We might as well invalidate the entire window to make sure...
  2537. ListView_InvalidateWindow(plv);
  2538. // ensure important items are visible
  2539. iItem = (plv->iFocus >= 0) ? plv->iFocus : ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  2540. if (ListView_RedrawEnabled(plv))
  2541. ListView_UpdateScrollBars(plv);
  2542. if (iItem >= 0)
  2543. ListView_OnEnsureVisible(plv, iItem, FALSE);
  2544. }
  2545. }
  2546. return TRUE;
  2547. }
  2548. // this arranges the icon given a sorted hdpa.
  2549. // Arrange the workareas one by one in the multi-workarea case.
  2550. BOOL ListView_CommonArrange(LV* plv, UINT style, HDPA hdpaSort)
  2551. {
  2552. if (plv->nWorkAreas < 1)
  2553. {
  2554. if (plv->exStyle & LVS_EX_MULTIWORKAREAS)
  2555. return TRUE;
  2556. else
  2557. return ListView_CommonArrangeEx(plv, style, hdpaSort, 0);
  2558. }
  2559. else
  2560. {
  2561. int i;
  2562. for (i = 0; i < plv->nWorkAreas; i++)
  2563. ListView_CommonArrangeEx(plv, style, hdpaSort, i);
  2564. return TRUE;
  2565. }
  2566. }
  2567. void ListView_IUpdateScrollBars(LV* plv)
  2568. {
  2569. // nothing to update if we're in the middle of fixing them up...
  2570. if (!plv->fInFixIScrollPositions)
  2571. {
  2572. RECT rcClient;
  2573. RECT rcView;
  2574. DWORD style;
  2575. DWORD styleOld;
  2576. SCROLLINFO si;
  2577. styleOld = ListView_GetWindowStyle(plv);
  2578. style = ListView_GetClientRect(plv, &rcClient, TRUE, &rcView);
  2579. if (ListView_FixIScrollPositions(plv, TRUE, &rcClient))
  2580. {
  2581. RECT rcClient2, rcView2;
  2582. DWORD style2 = ListView_GetClientRect(plv, &rcClient2, TRUE, &rcView2);
  2583. #ifdef DEBUG
  2584. // Now that ListView_GetClientRect is scroll-position-independent, fixing the scroll
  2585. // positions should have no effect on the size of rcClient and it's style
  2586. //
  2587. ASSERT(style2 == style);
  2588. ASSERT(RECTWIDTH(rcClient)==RECTWIDTH(rcClient2) && RECTHEIGHT(rcClient)==RECTHEIGHT(rcClient2));
  2589. ASSERT(RECTWIDTH(rcView)==RECTWIDTH(rcView2) && RECTHEIGHT(rcView)==RECTHEIGHT(rcView2));
  2590. #endif
  2591. rcClient = rcClient2;
  2592. rcView = rcView2;
  2593. }
  2594. si.cbSize = sizeof(SCROLLINFO);
  2595. if (style & WS_HSCROLL)
  2596. {
  2597. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  2598. si.nMin = 0;
  2599. si.nMax = rcView.right - rcView.left - 1;
  2600. si.nPage = rcClient.right - rcClient.left;
  2601. si.nPos = rcClient.left - rcView.left;
  2602. // ListView_FixIScrollPositions() ensures that our scroll positions are correct:
  2603. ASSERT(si.nMax >= (int)si.nPage); // otherwise why is WS_HSCROLL set?
  2604. ASSERT(si.nPos >= 0); // rcClient.left isn't left of rcView.left
  2605. ASSERT(si.nPos + (int)si.nPage <= si.nMax + 1); // rcClient.right isn't right of rcView.right
  2606. ListView_SetScrollInfo(plv, SB_HORZ, &si, TRUE);
  2607. }
  2608. else if (styleOld & WS_HSCROLL)
  2609. {
  2610. ListView_SetScrollRange(plv, SB_HORZ, 0, 0, TRUE);
  2611. }
  2612. if (style & WS_VSCROLL)
  2613. {
  2614. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  2615. si.nMin = 0;
  2616. si.nMax = rcView.bottom - rcView.top - 1;
  2617. si.nPage = rcClient.bottom - rcClient.top;
  2618. si.nPos = rcClient.top - rcView.top;
  2619. // ListView_FixIScrollPositions() ensures that our scroll positions are correct:
  2620. ASSERT(si.nMax >= (int)si.nPage); // otherwise why is WS_VSCROLL set?
  2621. ASSERT(si.nPos >= 0); // rcClient.top isn't above rcView.top
  2622. ASSERT(si.nPos + (int)si.nPage <= si.nMax + 1); // rcClient.bottom isn't below of rcView.bottom
  2623. ListView_SetScrollInfo(plv, SB_VERT, &si, TRUE);
  2624. }
  2625. else if (styleOld & WS_VSCROLL)
  2626. {
  2627. ListView_SetScrollRange(plv, SB_VERT, 0, 0, TRUE);
  2628. }
  2629. }
  2630. }
  2631. void ListView_ComOnScroll(LV* plv, UINT code, int posNew, int sb,
  2632. int cLine, int cPage)
  2633. {
  2634. int pos;
  2635. SCROLLINFO si;
  2636. BOOL fVert = (sb == SB_VERT);
  2637. UINT uSmooth = SSW_EX_UPDATEATEACHSTEP;
  2638. si.cbSize = sizeof(SCROLLINFO);
  2639. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  2640. if (!ListView_GetScrollInfo(plv, sb, &si)) {
  2641. return;
  2642. }
  2643. if (cPage != -1)
  2644. si.nPage = cPage;
  2645. if (si.nPage)
  2646. si.nMax -= (si.nPage - 1);
  2647. ASSERT(si.nMax >= si.nMin);
  2648. if (si.nMax < si.nMin)
  2649. si.nMax = si.nMin;
  2650. pos = (int)si.nPos; // current position
  2651. switch (code)
  2652. {
  2653. case SB_LEFT:
  2654. si.nPos = si.nMin;
  2655. break;
  2656. case SB_RIGHT:
  2657. si.nPos = si.nMax;
  2658. break;
  2659. case SB_PAGELEFT:
  2660. si.nPos = max(si.nMin, si.nPos - (int)si.nPage);
  2661. break;
  2662. case SB_LINELEFT:
  2663. si.nPos = max(si.nMin, si.nPos - cLine);
  2664. break;
  2665. case SB_PAGERIGHT:
  2666. si.nPos = min(si.nMax, si.nPos + (int)si.nPage);
  2667. break;
  2668. case SB_LINERIGHT:
  2669. si.nPos = min(si.nMax, si.nPos + cLine);
  2670. break;
  2671. case SB_THUMBTRACK:
  2672. si.nPos = posNew;
  2673. uSmooth = SSW_EX_IMMEDIATE;
  2674. break;
  2675. case SB_ENDSCROLL:
  2676. // When scroll bar tracking is over, ensure scroll bars
  2677. // are properly updated...
  2678. //
  2679. ListView_UpdateScrollBars(plv);
  2680. return;
  2681. default:
  2682. return;
  2683. }
  2684. if (plv->iScrollCount >= SMOOTHSCROLLLIMIT)
  2685. uSmooth = SSW_EX_IMMEDIATE;
  2686. si.fMask = SIF_POS;
  2687. si.nPos = ListView_SetScrollInfo(plv, sb, &si, TRUE);
  2688. if (pos != si.nPos)
  2689. {
  2690. int delta = (int)si.nPos - pos;
  2691. int dx = 0, dy = 0;
  2692. if (fVert)
  2693. dy = delta;
  2694. else
  2695. dx = delta;
  2696. ListView_SendScrollNotify(plv, TRUE, dx, dy);
  2697. _ListView_Scroll2(plv, dx, dy, uSmooth);
  2698. ListView_SendScrollNotify(plv, FALSE, dx, dy);
  2699. UpdateWindow(plv->ci.hwnd);
  2700. }
  2701. }
  2702. //
  2703. // We need a smoothscroll callback so our background image draws
  2704. // at the correct origin. If we don't have a background image,
  2705. // then this work is superfluous but not harmful either.
  2706. //
  2707. int CALLBACK ListView_IScroll2_SmoothScroll(
  2708. HWND hwnd,
  2709. int dx,
  2710. int dy,
  2711. CONST RECT *prcScroll,
  2712. CONST RECT *prcClip ,
  2713. HRGN hrgnUpdate,
  2714. LPRECT prcUpdate,
  2715. UINT flags)
  2716. {
  2717. LV* plv = ListView_GetPtr(hwnd);
  2718. if (plv)
  2719. {
  2720. plv->ptOrigin.x -= dx;
  2721. plv->ptOrigin.y -= dy;
  2722. }
  2723. // Now do what SmoothScrollWindow would've done if we weren't
  2724. // a callback
  2725. if (ListView_IsWatermarkedBackground(plv) ||
  2726. ListView_IsWatermarked(plv))
  2727. {
  2728. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2729. return TRUE;
  2730. }
  2731. else
  2732. return ScrollWindowEx(hwnd, dx, dy, prcScroll, prcClip, hrgnUpdate, prcUpdate, flags);
  2733. }
  2734. void ListView_IScroll2(LV* plv, int dx, int dy, UINT uSmooth)
  2735. {
  2736. if (dx | dy)
  2737. {
  2738. if ((plv->clrBk == CLR_NONE) && (plv->pImgCtx == NULL))
  2739. {
  2740. plv->ptOrigin.x += dx;
  2741. plv->ptOrigin.y += dy;
  2742. LVSeeThruScroll(plv, NULL);
  2743. }
  2744. else
  2745. {
  2746. SMOOTHSCROLLINFO si;
  2747. si.cbSize = sizeof(si);
  2748. si.fMask = SSIF_SCROLLPROC;
  2749. si.hwnd = plv->ci.hwnd;
  2750. si.dx = -dx;
  2751. si.dy = -dy;
  2752. si.lprcSrc = NULL;
  2753. si.lprcClip = NULL;
  2754. si.hrgnUpdate = NULL;
  2755. si.lprcUpdate = NULL;
  2756. si.fuScroll = uSmooth | SW_INVALIDATE | SW_ERASE | SSW_EX_UPDATEATEACHSTEP;
  2757. si.pfnScrollProc = ListView_IScroll2_SmoothScroll;
  2758. SmoothScrollWindow(&si);
  2759. }
  2760. }
  2761. }
  2762. void ListView_IOnScroll(LV* plv, UINT code, int posNew, UINT sb)
  2763. {
  2764. int cLine;
  2765. if (sb == SB_VERT)
  2766. {
  2767. cLine = plv->cyIconSpacing / 2;
  2768. }
  2769. else
  2770. {
  2771. cLine = plv->cxIconSpacing / 2;
  2772. }
  2773. ListView_ComOnScroll(plv, code, posNew, sb, cLine, -1);
  2774. }
  2775. int ListView_IGetScrollUnitsPerLine(LV* plv, UINT sb)
  2776. {
  2777. int cLine;
  2778. if (sb == SB_VERT)
  2779. {
  2780. cLine = plv->cyIconSpacing / 2;
  2781. }
  2782. else
  2783. {
  2784. cLine = plv->cxIconSpacing / 2;
  2785. }
  2786. return cLine;
  2787. }
  2788. // NOTE: there is very similar code in the treeview
  2789. //
  2790. // Totally disgusting hack in order to catch VK_RETURN
  2791. // before edit control gets it.
  2792. //
  2793. LRESULT CALLBACK ListView_EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  2794. {
  2795. LV* plv = ListView_GetPtr(GetParent(hwnd));
  2796. LRESULT lret;
  2797. ASSERT(plv);
  2798. #if defined(FE_IME) || !defined(WINNT)
  2799. if ( (g_fDBCSInputEnabled) && LOWORD(GetKeyboardLayout(0L)) == 0x0411 )
  2800. {
  2801. // The following code adds IME awareness to the
  2802. // listview's label editing. Currently just for Japanese.
  2803. //
  2804. DWORD dwGcs;
  2805. if (msg==WM_SIZE)
  2806. {
  2807. // If it's given the size, tell it to an IME.
  2808. ListView_SizeIME(hwnd);
  2809. }
  2810. else if (msg == EM_SETLIMITTEXT )
  2811. {
  2812. if (wParam < 13)
  2813. plv->flags |= LVF_DONTDRAWCOMP;
  2814. else
  2815. plv->flags &= ~LVF_DONTDRAWCOMP;
  2816. }
  2817. // Give up to draw IME composition by ourselves in case
  2818. // we're working on SFN. Win95d-5709
  2819. else if (!(plv->flags & LVF_DONTDRAWCOMP ))
  2820. {
  2821. switch (msg)
  2822. {
  2823. case WM_IME_STARTCOMPOSITION:
  2824. case WM_IME_ENDCOMPOSITION:
  2825. return 0L;
  2826. case WM_IME_COMPOSITION:
  2827. // If lParam has no data available bit, it implies
  2828. // canceling composition.
  2829. // ListView_InsertComposition() tries to get composition
  2830. // string w/ GCS_COMPSTR then remove it from edit control if
  2831. // nothing is available.
  2832. //
  2833. if ( !lParam )
  2834. dwGcs = GCS_COMPSTR;
  2835. else
  2836. dwGcs = (DWORD) lParam;
  2837. ListView_InsertComposition(hwnd, wParam, dwGcs, plv);
  2838. return 0L;
  2839. case WM_PAINT:
  2840. lret=CallWindowProc(plv->pfnEditWndProc, hwnd, msg, wParam, lParam);
  2841. ListView_PaintComposition(hwnd,plv);
  2842. return lret;
  2843. case WM_IME_SETCONTEXT:
  2844. // We draw composition string.
  2845. //
  2846. lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  2847. break;
  2848. default:
  2849. // the other messages should simply be processed
  2850. // in this subclass procedure.
  2851. break;
  2852. }
  2853. }
  2854. }
  2855. #endif FE_IME
  2856. switch (msg)
  2857. {
  2858. case WM_SETTEXT:
  2859. SetWindowID(hwnd, 1);
  2860. break;
  2861. case WM_KEYDOWN:
  2862. switch (wParam)
  2863. {
  2864. case VK_RETURN:
  2865. ListView_DismissEdit(plv, FALSE);
  2866. return 0L;
  2867. case VK_ESCAPE:
  2868. ListView_DismissEdit(plv, TRUE);
  2869. return 0L;
  2870. }
  2871. break;
  2872. case WM_CHAR:
  2873. switch (wParam)
  2874. {
  2875. case VK_RETURN:
  2876. // Eat the character, so edit control wont beep!
  2877. return 0L;
  2878. }
  2879. break;
  2880. case WM_GETDLGCODE:
  2881. return DLGC_WANTALLKEYS | DLGC_HASSETSEL; /* editing name, no dialog handling right now */
  2882. }
  2883. return CallWindowProc(plv->pfnEditWndProc, hwnd, msg, wParam, lParam);
  2884. }
  2885. // Helper routine for SetEditSize
  2886. void ListView_ChangeEditRectForRegion(LV* plv, LPRECT lprc)
  2887. {
  2888. LISTITEM* pitem = ListView_GetItemPtr(plv, plv->iEdit);
  2889. ASSERT(!ListView_IsOwnerData(plv));
  2890. ASSERT(ListView_IsIconView(plv));
  2891. if (!EqualRect((CONST RECT *)&pitem->rcTextRgn, (CONST RECT *)lprc))
  2892. {
  2893. // RecalcRegion knows to use rcTextRgn in the case where iEdit != -1,
  2894. // so set it up before calling through.
  2895. CopyRect(&pitem->rcTextRgn, (CONST RECT *)lprc);
  2896. ListView_RecalcRegion(plv, TRUE, TRUE);
  2897. // Invalidate the entire Edit and force a repaint from the listview
  2898. // on down to make sure we don't leave turds...
  2899. InvalidateRect(plv->hwndEdit, NULL, TRUE);
  2900. UpdateWindow(plv->ci.hwnd);
  2901. }
  2902. }
  2903. void ListView_SetEditSize(LV* plv)
  2904. {
  2905. RECT rcLabel;
  2906. UINT seips;
  2907. if (!((plv->iEdit >= 0) && (plv->iEdit < ListView_Count(plv))))
  2908. {
  2909. ListView_DismissEdit(plv, TRUE); // cancel edits
  2910. return;
  2911. }
  2912. ListView_GetRects(plv, plv->iEdit, QUERY_DEFAULT, NULL, &rcLabel, NULL, NULL);
  2913. // OffsetRect(&rc, rcLabel.left + g_cxLabelMargin + g_cxBorder,
  2914. // (rcLabel.bottom + rcLabel.top - rc.bottom) / 2 + g_cyBorder);
  2915. // OffsetRect(&rc, rcLabel.left + g_cxLabelMargin , rcLabel.top);
  2916. // get the text bounding rect
  2917. if (ListView_IsIconView(plv))
  2918. {
  2919. // We should not adjust y-positoin in case of the icon view.
  2920. InflateRect(&rcLabel, -g_cxLabelMargin, -g_cyBorder);
  2921. }
  2922. else
  2923. {
  2924. // Special case for single-line & centered
  2925. InflateRect(&rcLabel, -g_cxLabelMargin - g_cxBorder, (-(rcLabel.bottom - rcLabel.top - plv->cyLabelChar) / 2) - g_cyBorder);
  2926. }
  2927. seips = 0;
  2928. if (ListView_IsIconView(plv) && !(plv->ci.style & LVS_NOLABELWRAP))
  2929. seips |= SEIPS_WRAP;
  2930. #ifdef DEBUG
  2931. if (plv->ci.style & LVS_NOSCROLL)
  2932. seips |= SEIPS_NOSCROLL;
  2933. #endif
  2934. SetEditInPlaceSize(plv->hwndEdit, &rcLabel, plv->hfontLabel, seips);
  2935. if (plv->exStyle & LVS_EX_REGIONAL)
  2936. ListView_ChangeEditRectForRegion(plv, &rcLabel);
  2937. }
  2938. // to avoid eating too much stack
  2939. void ListView_DoOnEditLabel(LV *plv, int i, LPTSTR pszInitial)
  2940. {
  2941. TCHAR szLabel[CCHLABELMAX];
  2942. LV_ITEM item;
  2943. item.mask = LVIF_TEXT;
  2944. item.iItem = i;
  2945. item.iSubItem = 0;
  2946. item.pszText = szLabel;
  2947. item.cchTextMax = ARRAYSIZE(szLabel);
  2948. ListView_OnGetItem(plv, &item);
  2949. if (!item.pszText)
  2950. return;
  2951. // Make sure the edited item has the focus.
  2952. if (plv->iFocus != i)
  2953. ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
  2954. // Make sure the item is fully visible
  2955. ListView_OnEnsureVisible(plv, i, FALSE); // fPartialOK == FALSE
  2956. // Must subtract one from ARRAYSIZE(szLabel) because Edit_LimitText doesn't include
  2957. // the terminating NULL
  2958. plv->hwndEdit = CreateEditInPlaceWindow(plv->ci.hwnd,
  2959. pszInitial? pszInitial : item.pszText, ARRAYSIZE(szLabel) - 1,
  2960. ListView_IsIconView(plv) ?
  2961. (WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD | ES_CENTER | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL) :
  2962. (WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD | ES_LEFT | ES_AUTOHSCROLL), plv->hfontLabel);
  2963. if (plv->hwndEdit)
  2964. {
  2965. LISTITEM* pitem;
  2966. LV_DISPINFO nm;
  2967. // We create the edit window but have not shown it. Ask the owner
  2968. // if they are interested or not.
  2969. // If we passed in initial text set the ID to be dirty...
  2970. if (pszInitial)
  2971. SetWindowID(plv->hwndEdit, 1);
  2972. nm.item.mask = LVIF_PARAM;
  2973. nm.item.iItem = i;
  2974. nm.item.iSubItem = 0;
  2975. if (!ListView_IsOwnerData( plv ))
  2976. {
  2977. if (!(pitem = ListView_GetItemPtr(plv, i)))
  2978. {
  2979. DestroyWindow(plv->hwndEdit);
  2980. plv->hwndEdit = NULL;
  2981. return;
  2982. }
  2983. nm.item.lParam = pitem->lParam;
  2984. }
  2985. else
  2986. nm.item.lParam = (LPARAM)0;
  2987. plv->iEdit = i;
  2988. // if they have LVS_EDITLABELS but return non-FALSE here, stop!
  2989. if ((BOOL)CCSendNotify(&plv->ci, LVN_BEGINLABELEDIT, &nm.hdr))
  2990. {
  2991. plv->iEdit = -1;
  2992. DestroyWindow(plv->hwndEdit);
  2993. plv->hwndEdit = NULL;
  2994. }
  2995. }
  2996. }
  2997. void RescrollEditWindow(HWND hwndEdit)
  2998. {
  2999. Edit_SetSel(hwndEdit, -1, -1); // move to the end
  3000. Edit_SetSel(hwndEdit, 0, -1); // select all text
  3001. }
  3002. HWND ListView_OnEditLabel(LV* plv, int i, LPTSTR pszInitialText)
  3003. {
  3004. // this eats stack
  3005. ListView_DismissEdit(plv, FALSE);
  3006. if (!(plv->ci.style & LVS_EDITLABELS) || (GetFocus() != plv->ci.hwnd) ||
  3007. (i == -1))
  3008. return(NULL); // Does not support this.
  3009. ListView_DoOnEditLabel(plv, i, pszInitialText);
  3010. if (plv->hwndEdit) {
  3011. plv->pfnEditWndProc = SubclassWindow(plv->hwndEdit, ListView_EditWndProc);
  3012. #if defined(FE_IME) || !defined(WINNT)
  3013. if (g_fDBCSInputEnabled) {
  3014. if (SendMessage(plv->hwndEdit, EM_GETLIMITTEXT, (WPARAM)0, (LPARAM)0)<13)
  3015. {
  3016. plv->flags |= LVF_DONTDRAWCOMP;
  3017. }
  3018. }
  3019. #endif
  3020. ListView_SetEditSize(plv);
  3021. // Show the window and set focus to it. Do this after setting the
  3022. // size so we don't get flicker.
  3023. SetFocus(plv->hwndEdit);
  3024. ShowWindow(plv->hwndEdit, SW_SHOW);
  3025. ListView_InvalidateItem(plv, i, TRUE, RDW_INVALIDATE | RDW_ERASE);
  3026. RescrollEditWindow(plv->hwndEdit);
  3027. /* Due to a bizzare twist of fate, a certain mix of resolution / font size / icon
  3028. / spacing results in being able to see the previous label behind the edit control
  3029. / we have just created. Therefore to overcome this problem we ensure that this
  3030. / label is erased.
  3031. /
  3032. / As the label is not painted when we have an edit control we just invalidate the
  3033. / area and the background will be painted. As the window is a child of the list view
  3034. / we should not see any flicker within it. */
  3035. if ( ListView_IsIconView( plv ) && !ListView_HideLabels(plv))
  3036. {
  3037. RECT rcLabel;
  3038. ListView_GetRects( plv, i, QUERY_UNFOLDED, NULL, &rcLabel, NULL, NULL );
  3039. InvalidateRect( plv->ci.hwnd, &rcLabel, TRUE );
  3040. UpdateWindow( plv->ci.hwnd );
  3041. }
  3042. }
  3043. return plv->hwndEdit;
  3044. }
  3045. BOOL ListView_DismissEdit(LV* plv, BOOL fCancel)
  3046. {
  3047. LISTITEM* pitem = NULL;
  3048. BOOL fOkToContinue = TRUE;
  3049. HWND hwndEdit = plv->hwndEdit;
  3050. HWND hwnd = plv->ci.hwnd;
  3051. int iEdit;
  3052. LV_DISPINFO nm;
  3053. TCHAR szLabel[CCHLABELMAX];
  3054. #if defined(FE_IME) || !defined(WINNT)
  3055. HIMC himc;
  3056. #endif
  3057. if (plv->fNoDismissEdit)
  3058. return FALSE;
  3059. if (!hwndEdit) {
  3060. // Also make sure there are no pending edits...
  3061. ListView_CancelPendingEdit(plv);
  3062. return TRUE; // It is OK to process as normal...
  3063. }
  3064. // If the window is not visible, we are probably in the process
  3065. // of being destroyed, so assume that we are being destroyed
  3066. if (!IsWindowVisible(plv->ci.hwnd))
  3067. fCancel = TRUE;
  3068. //
  3069. // We are using the Window ID of the control as a BOOL to
  3070. // state if it is dirty or not.
  3071. switch (GetWindowID(hwndEdit)) {
  3072. case 0:
  3073. // The edit control is not dirty so act like cancel.
  3074. fCancel = TRUE;
  3075. // Fall through to set window so we will not recurse!
  3076. case 1:
  3077. // The edit control is dirty so continue.
  3078. SetWindowID(hwndEdit, 2); // Don't recurse
  3079. break;
  3080. case 2:
  3081. // We are in the process of processing an update now, bail out
  3082. return TRUE;
  3083. }
  3084. // Bug#94345: this will fail if the program deleted the items out
  3085. // from underneath us (while we are waiting for the edit timer).
  3086. // make delete item invalidate our edit item
  3087. // We uncouple the edit control and hwnd out from under this as
  3088. // to allow code that process the LVN_ENDLABELEDIT to reenter
  3089. // editing mode if an error happens.
  3090. iEdit = plv->iEdit;
  3091. do
  3092. {
  3093. if (ListView_IsOwnerData( plv ))
  3094. {
  3095. if (!((iEdit >= 0) && (iEdit < plv->cTotalItems)))
  3096. {
  3097. break;
  3098. }
  3099. nm.item.lParam = 0;
  3100. }
  3101. else
  3102. {
  3103. pitem = ListView_GetItemPtr(plv, iEdit);
  3104. ASSERT(pitem);
  3105. if (pitem == NULL)
  3106. {
  3107. break;
  3108. }
  3109. nm.item.lParam = pitem->lParam;
  3110. }
  3111. nm.item.iItem = iEdit;
  3112. nm.item.iSubItem = 0;
  3113. nm.item.cchTextMax = 0;
  3114. nm.item.mask = 0;
  3115. if (fCancel)
  3116. nm.item.pszText = NULL;
  3117. else
  3118. {
  3119. Edit_GetText(hwndEdit, szLabel, ARRAYSIZE(szLabel));
  3120. nm.item.pszText = szLabel;
  3121. nm.item.mask |= LVIF_TEXT;
  3122. nm.item.cchTextMax = ARRAYSIZE(szLabel);
  3123. }
  3124. //
  3125. // Notify the parent that we the label editing has completed.
  3126. // We will use the LV_DISPINFO structure to return the new
  3127. // label in. The parent still has the old text available by
  3128. // calling the GetItemText function.
  3129. //
  3130. fOkToContinue = (BOOL)CCSendNotify(&plv->ci, LVN_ENDLABELEDIT, &nm.hdr);
  3131. if (!IsWindow(hwnd)) {
  3132. return FALSE;
  3133. }
  3134. if (fOkToContinue && !fCancel)
  3135. {
  3136. //
  3137. // If the item has the text set as CALLBACK, we will let the
  3138. // ower know that they are supposed to set the item text in
  3139. // their own data structures. Else we will simply update the
  3140. // text in the actual view.
  3141. //
  3142. if (!ListView_IsOwnerData( plv ) &&
  3143. (pitem->pszText != LPSTR_TEXTCALLBACK))
  3144. {
  3145. // Set the item text (everything's set up in nm.item)
  3146. //
  3147. nm.item.mask = LVIF_TEXT;
  3148. ListView_OnSetItem(plv, &nm.item);
  3149. }
  3150. else
  3151. {
  3152. CCSendNotify(&plv->ci, LVN_SETDISPINFO, &nm.hdr);
  3153. // Also we will assume that our cached size is invalid...
  3154. plv->rcView.left = RECOMPUTE;
  3155. if (!ListView_IsOwnerData( plv ))
  3156. {
  3157. ListView_SetSRecompute(pitem);
  3158. }
  3159. }
  3160. }
  3161. #if defined(FE_IME) || !defined(WINNT)
  3162. if (g_fDBCSInputEnabled) {
  3163. if (LOWORD(GetKeyboardLayout(0L)) == 0x0411 && (himc = ImmGetContext(hwndEdit)))
  3164. {
  3165. ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0L);
  3166. ImmReleaseContext(hwndEdit, himc);
  3167. }
  3168. }
  3169. #endif
  3170. // redraw
  3171. ListView_InvalidateItem(plv, iEdit, FALSE, RDW_INVALIDATE | RDW_ERASE);
  3172. } while (FALSE);
  3173. // If the hwnedit is still us clear out the variables
  3174. if (hwndEdit == plv->hwndEdit)
  3175. {
  3176. plv->iEdit = -1;
  3177. plv->hwndEdit = NULL; // avoid being reentered
  3178. }
  3179. DestroyWindow(hwndEdit);
  3180. // We've to recalc the region because the edit in place window has
  3181. // added stuff to the region that we don't know how to remove
  3182. // safely.
  3183. ListView_RecalcRegion(plv, TRUE, TRUE);
  3184. return fOkToContinue;
  3185. }
  3186. HWND CreateEditInPlaceWindow(HWND hwnd, LPCTSTR lpText, int cbText, LONG style, HFONT hFont)
  3187. {
  3188. HWND hwndEdit;
  3189. // Create the window with some nonzero size so margins work properly
  3190. // The caller will do a SetEditInPlaceSize to set the real size
  3191. // But make sure the width is huge so when an app calls SetWindowText,
  3192. // USER won't try to scroll the window.
  3193. hwndEdit = CreateWindowEx(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_RTLREADING,
  3194. TEXT("EDIT"), lpText, style,
  3195. 0, 0, 16384, 20, hwnd, NULL, HINST_THISDLL, NULL);
  3196. if (hwndEdit) {
  3197. Edit_LimitText(hwndEdit, cbText);
  3198. Edit_SetSel(hwndEdit, 0, 0); // move to the beginning
  3199. FORWARD_WM_SETFONT(hwndEdit, hFont, FALSE, SendMessage);
  3200. }
  3201. return hwndEdit;
  3202. }
  3203. // in:
  3204. // hwndEdit edit control to position in client coords of parent window
  3205. // prc bonding rect of the text, used to position everthing
  3206. // hFont font being used
  3207. // flags
  3208. // SEIPS_WRAP if this is a wrapped type (multiline) edit
  3209. // SEIPS_NOSCROLL if the parent control does not have scrollbars
  3210. //
  3211. // The SEIPS_NOSCROLL flag is used only in DEBUG. Normally, the item
  3212. // being edited should have been scrolled into view, but if the parent
  3213. // doesn't have scrollbars, then clearly that's not possible, so we
  3214. // shouldn't ASSERT in that case.
  3215. //
  3216. // Notes:
  3217. // The top-left corner of the bouding rectangle must be the position
  3218. // the client uses to draw text. We adjust the edit field rectangle
  3219. // appropriately.
  3220. //
  3221. void SetEditInPlaceSize(HWND hwndEdit, RECT *prc, HFONT hFont, UINT seips)
  3222. {
  3223. RECT rc, rcClient, rcFormat;
  3224. TCHAR szLabel[CCHLABELMAX + 1];
  3225. int cchLabel, cxIconTextWidth;
  3226. HDC hdc;
  3227. HWND hwndParent = GetParent(hwndEdit);
  3228. UINT flags;
  3229. cchLabel = Edit_GetText(hwndEdit, szLabel, ARRAYSIZE(szLabel));
  3230. if (szLabel[0] == 0)
  3231. {
  3232. lstrcpy(szLabel, c_szSpace);
  3233. cchLabel = 1;
  3234. }
  3235. hdc = GetDC(hwndParent);
  3236. SelectFont(hdc, hFont);
  3237. cxIconTextWidth = g_cxIconSpacing - g_cxLabelMargin * 2;
  3238. rc.left = rc.top = rc.bottom = 0;
  3239. rc.right = cxIconTextWidth; // for DT_LVWRAP
  3240. // REVIEW: we might want to include DT_EDITCONTROL in our DT_LVWRAP
  3241. if (seips & SEIPS_WRAP)
  3242. {
  3243. flags = DT_LVWRAP | DT_CALCRECT;
  3244. // We only use DT_NOFULLWIDTHCHARBREAK on Korean(949) Memphis and NT5
  3245. if (949 == g_uiACP)
  3246. flags |= DT_NOFULLWIDTHCHARBREAK;
  3247. }
  3248. else
  3249. flags = DT_LV | DT_CALCRECT;
  3250. // If the string is NULL display a rectangle that is visible.
  3251. DrawText(hdc, szLabel, cchLabel, &rc, flags);
  3252. // Minimum text box size is 1/4 icon spacing size
  3253. if (rc.right < g_cxIconSpacing / 4)
  3254. rc.right = g_cxIconSpacing / 4;
  3255. // position the text rect based on the text rect passed in
  3256. // if wrapping, center the edit control around the text mid point
  3257. OffsetRect(&rc,
  3258. (seips & SEIPS_WRAP) ? prc->left + ((prc->right - prc->left) - (rc.right - rc.left)) / 2 : prc->left,
  3259. (seips & SEIPS_WRAP) ? prc->top : prc->top + ((prc->bottom - prc->top) - (rc.bottom - rc.top)) / 2 );
  3260. // give a little space to ease the editing of this thing
  3261. if (!(seips & SEIPS_WRAP))
  3262. rc.right += g_cxLabelMargin * 4;
  3263. rc.right += g_cyEdge; // try to leave a little more for dual blanks
  3264. ReleaseDC(hwndParent, hdc);
  3265. GetClientRect(hwndParent, &rcClient);
  3266. IntersectRect(&rc, &rc, &rcClient);
  3267. //
  3268. // Inflate it after the clipping, because it's ok to hide border.
  3269. //
  3270. // EM_GETRECT already takes EM_GETMARGINS into account, so don't use both.
  3271. SendMessage(hwndEdit, EM_GETRECT, 0, (LPARAM)(LPRECT)&rcFormat);
  3272. // Turn the margins inside-out so we can AdjustWindowRect on them.
  3273. rcFormat.top = -rcFormat.top;
  3274. rcFormat.left = -rcFormat.left;
  3275. AdjustWindowRectEx(&rcFormat, GetWindowStyle(hwndEdit), FALSE,
  3276. GetWindowExStyle(hwndEdit));
  3277. InflateRect(&rc, -rcFormat.left, -rcFormat.top);
  3278. HideCaret(hwndEdit);
  3279. SetWindowPos(hwndEdit, NULL, rc.left, rc.top,
  3280. rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
  3281. CopyRect(prc, (CONST RECT *)&rc);
  3282. InvalidateRect(hwndEdit, NULL, TRUE);
  3283. ShowCaret(hwndEdit);
  3284. }
  3285. // draw three pixel wide border for border selection.
  3286. void ListView_DrawBorderSel(HIMAGELIST himl, HWND hwnd, HDC hdc, int x,int y, COLORREF clr)
  3287. {
  3288. int dx, dy;
  3289. RECT rc;
  3290. COLORREF clrSave = SetBkColor(hdc, clr);
  3291. ImageList_GetIconSize(himl, &dx, &dy);
  3292. //left
  3293. rc.left = x - 4; // 1 pixel seperation + 3 pixel width.
  3294. rc.top = y - 4;
  3295. rc.right = x - 1;
  3296. rc.bottom = y + dy + 4;
  3297. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  3298. //top
  3299. rc.left = rc.right;
  3300. rc.right = rc.left + dx + 2;
  3301. rc.bottom = rc.top + 3;
  3302. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  3303. //right
  3304. rc.left = rc.right;
  3305. rc.right = rc.left + 3;
  3306. rc.bottom = rc.top + dy + 8; // 2*3 pixel borders + 2*1 pixel seperation = 8
  3307. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  3308. // bottom
  3309. rc.top = rc.bottom - 3;
  3310. rc.right = rc.left;
  3311. rc.left = rc.right - dx - 2;
  3312. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  3313. SetBkColor(hdc, clrSave);
  3314. return;
  3315. }
  3316. UINT ListView_GetTextSelectionFlags(LV* plv, LV_ITEM *pitem, UINT fDraw)
  3317. {
  3318. UINT fText = SHDT_DESELECTED;
  3319. // the item can have one of 4 states, for 3 looks:
  3320. // normal simple drawing
  3321. // selected, no focus light image highlight, no text hi
  3322. // selected w/ focus highlight image & text
  3323. // drop highlighting highlight image & text
  3324. if ((pitem->state & LVIS_DROPHILITED) ||
  3325. (fDraw & LVDI_SELECTED && (pitem->state & LVIS_SELECTED)) )
  3326. {
  3327. fText = SHDT_SELECTED;
  3328. }
  3329. if (fDraw & LVDI_SELECTNOFOCUS && (pitem->state & LVIS_SELECTED))
  3330. {
  3331. fText = SHDT_SELECTNOFOCUS;
  3332. }
  3333. return fText;
  3334. }
  3335. //
  3336. // If xMax >= 0, then the image will not be drawn past the x-coordinate
  3337. // specified by xMax. This is used only during report view drawing, where
  3338. // we have to clip against our column width.
  3339. //
  3340. UINT ListView_DrawImageEx2(LV* plv, LV_ITEM* pitem, HDC hdc, int x, int y, COLORREF crBk, UINT fDraw, int xMax, int iIconEffect, int iFrame)
  3341. {
  3342. BOOL fBorderSel = ListView_IsBorderSelect(plv);
  3343. UINT fImage;
  3344. COLORREF clr = 0;
  3345. HIMAGELIST himl;
  3346. int cxIcon;
  3347. UINT fText = ListView_GetTextSelectionFlags(plv, pitem, fDraw);
  3348. DWORD fState = iIconEffect;
  3349. fImage = (pitem->state & LVIS_OVERLAYMASK);
  3350. if (plv->flags & LVF_DRAGIMAGE)
  3351. {
  3352. fImage |= ILD_PRESERVEALPHA;
  3353. }
  3354. if (ListView_IsIconView(plv) || ListView_IsTileView(plv))
  3355. {
  3356. himl = plv->himl;
  3357. cxIcon = plv->cxIcon;
  3358. }
  3359. else
  3360. {
  3361. himl = plv->himlSmall;
  3362. cxIcon = plv->cxSmIcon;
  3363. }
  3364. if (!(plv->flags & LVF_DRAGIMAGE))
  3365. {
  3366. // the item can have one of 4 states, for 3 looks:
  3367. // normal simple drawing
  3368. // selected, no focus light image highlight, no text hi
  3369. // selected w/ focus highlight image & text
  3370. // drop highlighting highlight image & text
  3371. if ((pitem->state & LVIS_DROPHILITED) ||
  3372. ((fDraw & LVDI_SELECTED) && (pitem->state & LVIS_SELECTED)))
  3373. {
  3374. fText = SHDT_SELECTED;
  3375. if (!fBorderSel) // do not effect color of icon on borderselect.
  3376. {
  3377. fImage |= ILD_BLEND50;
  3378. clr = CLR_HILIGHT;
  3379. }
  3380. }
  3381. if (pitem->state & LVIS_CUT)
  3382. {
  3383. fImage |= ILD_BLEND50;
  3384. clr = plv->clrBk;
  3385. }
  3386. // Affects only allowed if double buffering
  3387. if (ListView_IsDoubleBuffer(plv))
  3388. {
  3389. if ((pitem->state & LVIS_GLOW || (fDraw & LVDI_GLOW)) && !(fDraw & LVDI_NOEFFECTS))
  3390. {
  3391. crBk = CLR_NONE;
  3392. fState |= ILS_GLOW;
  3393. }
  3394. if (fDraw & LVDI_SHADOW && !(fDraw & LVDI_NOEFFECTS))
  3395. {
  3396. crBk = CLR_NONE;
  3397. fState |= ILS_SHADOW;
  3398. }
  3399. }
  3400. }
  3401. if (!(fDraw & LVDI_NOIMAGE))
  3402. {
  3403. if (himl)
  3404. {
  3405. if (plv->pImgCtx || ListView_IsWatermarked(plv) || ((plv->exStyle & LVS_EX_REGIONAL) && !g_fSlowMachine))
  3406. {
  3407. crBk = CLR_NONE;
  3408. }
  3409. if (xMax >= 0)
  3410. cxIcon = min(cxIcon, xMax - x);
  3411. if (cxIcon > 0)
  3412. {
  3413. IMAGELISTDRAWPARAMS imldp;
  3414. DWORD dwFrame = iFrame;
  3415. imldp.cbSize = sizeof(imldp);
  3416. imldp.himl = himl;
  3417. imldp.i = pitem->iImage;
  3418. imldp.hdcDst = hdc;
  3419. imldp.x = x;
  3420. imldp.y = y;
  3421. imldp.cx = CCIsHighDPI()?0:cxIcon;
  3422. imldp.cy = 0;
  3423. imldp.xBitmap= 0;
  3424. imldp.yBitmap= 0;
  3425. imldp.rgbBk = crBk;
  3426. imldp.rgbFg = clr;
  3427. imldp.fStyle = fImage;
  3428. imldp.fState = fState;
  3429. imldp.Frame = dwFrame;
  3430. if (ListView_IsDPIScaled(plv))
  3431. imldp.fStyle |= ILD_DPISCALE;
  3432. ImageList_DrawIndirect(&imldp);
  3433. }
  3434. }
  3435. if (plv->himlState)
  3436. {
  3437. if (LV_StateImageValue(pitem) &&
  3438. (pitem->iSubItem == 0 || plv->exStyle & LVS_EX_SUBITEMIMAGES)
  3439. )
  3440. {
  3441. int iState = LV_StateImageIndex(pitem);
  3442. int dyImage = 0;
  3443. int xDraw = x - plv->cxState - LV_ICONTOSTATECX;
  3444. // if we are not rendering checks boxes with toggle select
  3445. // then lets render the state image the old way.
  3446. if (ListView_IsSimpleSelect(plv) &&
  3447. (ListView_IsIconView(plv) || ListView_IsTileView(plv)))
  3448. {
  3449. xDraw = x+cxIcon -plv->cxState; // align top right
  3450. dyImage = 0;
  3451. }
  3452. else
  3453. {
  3454. if (himl)
  3455. {
  3456. if (ListView_IsIconView(plv))
  3457. dyImage = plv->cyIcon - plv->cyState;
  3458. else if (ListView_IsTileView(plv))
  3459. dyImage = (plv->sizeTile.cy - plv->cyState) / 2; //Center vertically
  3460. else // assume small icon
  3461. dyImage = plv->cySmIcon - plv->cyState;
  3462. }
  3463. }
  3464. cxIcon = plv->cxState;
  3465. if (xMax >= 0)
  3466. {
  3467. cxIcon = min(cxIcon, xMax - xDraw);
  3468. }
  3469. if (cxIcon > 0)
  3470. {
  3471. IMAGELISTDRAWPARAMS imldp;
  3472. imldp.cbSize = sizeof(imldp);
  3473. imldp.himl = plv->himlState;
  3474. imldp.i = iState;
  3475. imldp.hdcDst = hdc;
  3476. imldp.x = xDraw;
  3477. imldp.y = y + dyImage;
  3478. imldp.cx = CCIsHighDPI()?0:cxIcon;
  3479. imldp.cy = 0;
  3480. imldp.xBitmap= 0;
  3481. imldp.yBitmap= 0;
  3482. imldp.rgbBk = crBk;
  3483. imldp.rgbFg = clr;
  3484. imldp.fStyle = fImage;
  3485. imldp.fState = fState;
  3486. imldp.Frame = 0;
  3487. if (ListView_IsDPIScaled(plv))
  3488. imldp.fStyle |= ILD_DPISCALE;
  3489. ImageList_DrawIndirect(&imldp);
  3490. }
  3491. }
  3492. }
  3493. }
  3494. return fText;
  3495. }
  3496. UINT ListView_DrawImageEx(LV* plv, LV_ITEM* pitem, HDC hdc, int x, int y, COLORREF crBk, UINT fDraw, int xMax)
  3497. {
  3498. return ListView_DrawImageEx2(plv, pitem, hdc, x, y, crBk, fDraw, xMax, ILD_NORMAL, 0);
  3499. }
  3500. #if defined(FE_IME) || !defined(WINNT)
  3501. void ListView_SizeIME(HWND hwnd)
  3502. {
  3503. HIMC himc;
  3504. #ifdef _WIN32
  3505. CANDIDATEFORM candf;
  3506. #else
  3507. CANDIDATEFORM16 candf;
  3508. #endif
  3509. RECT rc;
  3510. // If this subclass procedure is being called with WM_SIZE,
  3511. // This routine sets the rectangle to an IME.
  3512. GetClientRect(hwnd, &rc);
  3513. // Candidate stuff
  3514. candf.dwIndex = 0; // Bogus assumption for Japanese IME.
  3515. candf.dwStyle = CFS_EXCLUDE;
  3516. candf.ptCurrentPos.x = rc.left;
  3517. candf.ptCurrentPos.y = rc.bottom;
  3518. candf.rcArea = rc;
  3519. if (himc=ImmGetContext(hwnd))
  3520. {
  3521. ImmSetCandidateWindow(himc, &candf);
  3522. ImmReleaseContext(hwnd, himc);
  3523. }
  3524. }
  3525. #ifndef UNICODE
  3526. LPSTR DoDBCSBoundary(LPTSTR lpsz, int *lpcchMax)
  3527. {
  3528. int i = 0;
  3529. while (i < *lpcchMax && *lpsz)
  3530. {
  3531. i++;
  3532. if (IsDBCSLeadByte(*lpsz))
  3533. {
  3534. if (i >= *lpcchMax)
  3535. {
  3536. --i; // Wrap up without the last leadbyte.
  3537. break;
  3538. }
  3539. i++;
  3540. lpsz+= 2;
  3541. }
  3542. else
  3543. lpsz++;
  3544. }
  3545. *lpcchMax = i;
  3546. return lpsz;
  3547. }
  3548. #endif
  3549. void DrawCompositionLine(HWND hwnd, HDC hdc, HFONT hfont, LPTSTR lpszComp, LPBYTE lpszAttr, int ichCompStart, int ichCompEnd, int ichStart)
  3550. {
  3551. PTSTR pszCompStr;
  3552. int ichSt,ichEnd;
  3553. DWORD dwPos;
  3554. BYTE bAttr;
  3555. HFONT hfontOld;
  3556. int fnPen;
  3557. HPEN hPen;
  3558. COLORREF crDrawText;
  3559. COLORREF crDrawBack;
  3560. COLORREF crOldText;
  3561. COLORREF crOldBk;
  3562. while (ichCompStart < ichCompEnd)
  3563. {
  3564. // Get the fragment to draw
  3565. //
  3566. // ichCompStart,ichCompEnd -- index at Edit Control
  3567. // ichSt,ichEnd -- index at lpszComp
  3568. ichEnd = ichSt = ichCompStart - ichStart;
  3569. bAttr = lpszAttr[ichSt];
  3570. while (ichEnd < ichCompEnd - ichStart)
  3571. {
  3572. if (bAttr == lpszAttr[ichEnd])
  3573. ichEnd++;
  3574. else
  3575. break;
  3576. }
  3577. pszCompStr = (PTSTR)LocalAlloc(LPTR, sizeof(TCHAR)*(ichEnd - ichSt + 1 + 1) ); // 1 for NULL.
  3578. if (pszCompStr)
  3579. {
  3580. lstrcpyn(pszCompStr, &lpszComp[ichSt], ichEnd-ichSt+1);
  3581. pszCompStr[ichEnd-ichSt] = '\0';
  3582. }
  3583. // Attribute stuff
  3584. switch (bAttr)
  3585. {
  3586. case ATTR_INPUT:
  3587. fnPen = PS_DOT;
  3588. crDrawText = g_clrWindowText;
  3589. crDrawBack = g_clrWindow;
  3590. break;
  3591. case ATTR_TARGET_CONVERTED:
  3592. case ATTR_TARGET_NOTCONVERTED:
  3593. fnPen = PS_DOT;
  3594. crDrawText = g_clrHighlightText;
  3595. crDrawBack = g_clrHighlight;
  3596. break;
  3597. case ATTR_CONVERTED:
  3598. fnPen = PS_SOLID;
  3599. crDrawText = g_clrWindowText;
  3600. crDrawBack = g_clrWindow;
  3601. break;
  3602. }
  3603. crOldText = SetTextColor(hdc, crDrawText);
  3604. crOldBk = SetBkColor(hdc, crDrawBack);
  3605. hfontOld= SelectObject(hdc, hfont);
  3606. // Get the start position of composition
  3607. //
  3608. dwPos = (DWORD) SendMessage(hwnd, EM_POSFROMCHAR, ichCompStart, 0);
  3609. // Draw it.
  3610. TextOut(hdc, GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), pszCompStr, ichEnd-ichSt);
  3611. #ifndef DONT_UNDERLINE
  3612. // Underline
  3613. hPen = CreatePen(fnPen, 1, crDrawText);
  3614. if( hPen ) {
  3615. HPEN hpenOld = SelectObject( hdc, hPen );
  3616. int iOldBk = SetBkMode( hdc, TRANSPARENT );
  3617. SIZE size;
  3618. GetTextExtentPoint(hdc, pszCompStr, ichEnd-ichSt, &size);
  3619. MoveToEx( hdc, GET_X_LPARAM(dwPos), size.cy + GET_Y_LPARAM(dwPos)-1, NULL);
  3620. LineTo( hdc, size.cx + GET_X_LPARAM(dwPos), size.cy + GET_Y_LPARAM(dwPos)-1 );
  3621. SetBkMode( hdc, iOldBk );
  3622. if( hpenOld ) SelectObject( hdc, hpenOld );
  3623. DeleteObject( hPen );
  3624. }
  3625. #endif
  3626. if (hfontOld)
  3627. SelectObject(hdc, hfontOld);
  3628. SetTextColor(hdc, crOldText);
  3629. SetBkColor(hdc, crOldBk);
  3630. LocalFree((HLOCAL)pszCompStr);
  3631. //Next fragment
  3632. //
  3633. ichCompStart += ichEnd-ichSt;
  3634. }
  3635. }
  3636. void ListView_InsertComposition(HWND hwnd, WPARAM wParam, LPARAM lParam, LV *plv)
  3637. {
  3638. PSTR pszCompStr;
  3639. int cbComp = 0;
  3640. int cbCompNew;
  3641. int cchMax;
  3642. int cchText;
  3643. DWORD dwSel;
  3644. HIMC himc = (HIMC)0;
  3645. // To prevent recursion..
  3646. if (plv->flags & LVF_INSERTINGCOMP)
  3647. {
  3648. return;
  3649. }
  3650. plv->flags |= LVF_INSERTINGCOMP;
  3651. // Don't want to redraw edit during inserting.
  3652. //
  3653. SendMessage(hwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
  3654. // If we have RESULT STR, put it to EC first.
  3655. if (himc = ImmGetContext(hwnd))
  3656. {
  3657. #ifdef WIN32
  3658. if (!(dwSel = PtrToUlong(GetProp(hwnd, szIMECompPos))))
  3659. dwSel = Edit_GetSel(hwnd);
  3660. // Becaues we don't setsel after inserting composition
  3661. // in win32 case.
  3662. Edit_SetSel(hwnd, GET_X_LPARAM(dwSel), GET_Y_LPARAM(dwSel));
  3663. #endif
  3664. if (lParam&GCS_RESULTSTR)
  3665. {
  3666. // ImmGetCompositionString() returns length of buffer in bytes,
  3667. // not in # of character
  3668. cbComp = (int)ImmGetCompositionString(himc, GCS_RESULTSTR, NULL, 0);
  3669. pszCompStr = (PSTR)LocalAlloc(LPTR, cbComp + sizeof(TCHAR));
  3670. if (pszCompStr)
  3671. {
  3672. ImmGetCompositionString(himc, GCS_RESULTSTR, (PSTR)pszCompStr, cbComp+sizeof(TCHAR));
  3673. // With ImmGetCompositionStringW, cbComp is # of bytes copied
  3674. // character position must be calculated by cbComp / sizeof(TCHAR)
  3675. //
  3676. *(TCHAR *)(&pszCompStr[cbComp]) = TEXT('\0');
  3677. Edit_ReplaceSel(hwnd, (LPTSTR)pszCompStr);
  3678. LocalFree((HLOCAL)pszCompStr);
  3679. }
  3680. #ifdef WIN32
  3681. // There's no longer selection
  3682. //
  3683. RemoveProp(hwnd, szIMECompPos);
  3684. // Get current cursor pos so that the subsequent composition
  3685. // handling will do the right thing.
  3686. //
  3687. dwSel = Edit_GetSel(hwnd);
  3688. #endif
  3689. }
  3690. if (lParam & GCS_COMPSTR)
  3691. {
  3692. // ImmGetCompositionString() returns length of buffer in bytes,
  3693. // not in # of character
  3694. //
  3695. cbComp = (int)ImmGetCompositionString(himc, GCS_COMPSTR, NULL, 0);
  3696. pszCompStr = (PSTR)LocalAlloc(LPTR, cbComp + sizeof(TCHAR));
  3697. if (pszCompStr)
  3698. {
  3699. ImmGetCompositionString(himc, GCS_COMPSTR, pszCompStr, cbComp+sizeof(TCHAR));
  3700. // Get position of the current selection
  3701. //
  3702. #ifndef WIN32
  3703. dwSel = Edit_GetSel(hwnd);
  3704. #endif
  3705. cchMax = (int)SendMessage(hwnd, EM_GETLIMITTEXT, 0, 0);
  3706. cchText = Edit_GetTextLength(hwnd);
  3707. // Cut the composition string if it exceeds limit.
  3708. //
  3709. cbCompNew = min((UINT)cbComp,
  3710. sizeof(TCHAR)*(cchMax-(cchText-(HIWORD(dwSel)-LOWORD(dwSel)))));
  3711. // wrap up the DBCS at the end of string
  3712. //
  3713. if (cbCompNew < cbComp)
  3714. {
  3715. #ifndef UNICODE
  3716. DoDBCSBoundary((LPSTR)pszCompStr, (int *)&cbCompNew);
  3717. #endif
  3718. *(TCHAR *)(&pszCompStr[cbCompNew]) = TEXT('\0');
  3719. // Reset composition string if we cut it.
  3720. ImmSetCompositionString(himc, SCS_SETSTR, pszCompStr, cbCompNew, NULL, 0);
  3721. cbComp = cbCompNew;
  3722. }
  3723. *(TCHAR *)(&pszCompStr[cbComp]) = TEXT('\0');
  3724. // Replace the current selection with composition string.
  3725. //
  3726. Edit_ReplaceSel(hwnd, (LPTSTR)pszCompStr);
  3727. LocalFree((HLOCAL)pszCompStr);
  3728. }
  3729. // Mark the composition string so that we can replace it again
  3730. // for the next time.
  3731. //
  3732. #ifdef WIN32
  3733. // Don't setsel to avoid flicking
  3734. if (cbComp)
  3735. {
  3736. dwSel = MAKELONG(LOWORD(dwSel),LOWORD(dwSel)+cbComp/sizeof(TCHAR));
  3737. SetProp(hwnd, szIMECompPos, IntToPtr(dwSel));
  3738. }
  3739. else
  3740. RemoveProp(hwnd, szIMECompPos);
  3741. #else
  3742. // Still use SETSEL for 16bit.
  3743. if (cbComp)
  3744. Edit_SetSel(hwnd, LOWORD(dwSel), LOWORD(dwSel)+cbComp);
  3745. #endif
  3746. }
  3747. ImmReleaseContext(hwnd, himc);
  3748. }
  3749. SendMessage(hwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
  3750. //
  3751. // We want to update the size of label edit just once at
  3752. // each WM_IME_COMPOSITION processing. ReplaceSel causes several EN_UPDATE
  3753. // and it causes ugly flicking too.
  3754. //
  3755. RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT|RDW_INVALIDATE);
  3756. SetWindowID(plv->hwndEdit, 1);
  3757. ListView_SetEditSize(plv);
  3758. plv->flags &= ~LVF_INSERTINGCOMP;
  3759. }
  3760. void ListView_PaintComposition(HWND hwnd, LV * plv)
  3761. {
  3762. BYTE szCompStr[CCHLABELMAX + 1];
  3763. BYTE szCompAttr[CCHLABELMAX + 1];
  3764. int cchLine, ichLineStart;
  3765. int cbComp = 0;
  3766. int cchComp;
  3767. int nLine;
  3768. int ichCompStart, ichCompEnd;
  3769. DWORD dwSel;
  3770. int cchMax, cchText;
  3771. HIMC himc = (HIMC)0;
  3772. HDC hdc;
  3773. if (plv->flags & LVF_INSERTINGCOMP)
  3774. {
  3775. // This is the case that ImmSetCompositionString() generates
  3776. // WM_IME_COMPOSITION. We're not ready to paint composition here.
  3777. return;
  3778. }
  3779. if (himc = ImmGetContext(hwnd))
  3780. {
  3781. cbComp=(UINT)ImmGetCompositionString(himc, GCS_COMPSTR, szCompStr, sizeof(szCompStr));
  3782. ImmGetCompositionString(himc, GCS_COMPATTR, szCompAttr, sizeof(szCompStr));
  3783. ImmReleaseContext(hwnd, himc);
  3784. }
  3785. if (cbComp)
  3786. {
  3787. // Get the position of current selection
  3788. //
  3789. #ifdef WIN32
  3790. if (!(dwSel = PtrToUlong(GetProp(hwnd, szIMECompPos))))
  3791. dwSel = 0L;
  3792. #else
  3793. dwSel = Edit_GetSel(hwnd);
  3794. #endif
  3795. cchMax = (int)SendMessage(hwnd, EM_GETLIMITTEXT, 0, 0);
  3796. cchText = Edit_GetTextLength(hwnd);
  3797. cbComp = min((UINT)cbComp, sizeof(TCHAR)*(cchMax-(cchText-(HIWORD(dwSel)-LOWORD(dwSel)))));
  3798. #ifndef UNICODE
  3799. DoDBCSBoundary((LPTSTR)szCompStr, (int *)&cbComp);
  3800. #endif
  3801. *(TCHAR *)(&szCompStr[cbComp]) = TEXT('\0');
  3802. /////////////////////////////////////////////////
  3803. // //
  3804. // Draw composition string over the sel string.//
  3805. // //
  3806. /////////////////////////////////////////////////
  3807. hdc = GetDC(hwnd);
  3808. ichCompStart = LOWORD(dwSel);
  3809. cchComp = cbComp/sizeof(TCHAR);
  3810. while (ichCompStart < (int)LOWORD(dwSel) + cchComp)
  3811. {
  3812. // Get line from each start pos.
  3813. //
  3814. nLine = Edit_LineFromChar(hwnd, ichCompStart);
  3815. ichLineStart = Edit_LineIndex(hwnd, nLine);
  3816. cchLine= Edit_LineLength(hwnd, ichLineStart);
  3817. // See if composition string is longer than this line.
  3818. //
  3819. if(ichLineStart+cchLine > (int)LOWORD(dwSel)+cchComp)
  3820. ichCompEnd = LOWORD(dwSel)+cchComp;
  3821. else
  3822. {
  3823. // Yes, the composition string is longer.
  3824. // Take the begining of the next line as next start.
  3825. //
  3826. if (ichLineStart+cchLine > ichCompStart)
  3827. ichCompEnd = ichLineStart+cchLine;
  3828. else
  3829. {
  3830. // If the starting position is not proceeding,
  3831. // let's get out of here.
  3832. break;
  3833. }
  3834. }
  3835. // Draw the line
  3836. //
  3837. DrawCompositionLine(hwnd, hdc, plv->hfontLabel, (LPTSTR)szCompStr, szCompAttr, ichCompStart, ichCompEnd, LOWORD(dwSel));
  3838. ichCompStart = ichCompEnd;
  3839. }
  3840. ReleaseDC(hwnd, hdc);
  3841. // We don't want to repaint the window.
  3842. ValidateRect(hwnd, NULL);
  3843. }
  3844. }
  3845. #endif FE_IME