Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4467 lines
146 KiB

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