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.

3353 lines
103 KiB

  1. // large icon view stuff
  2. #include "ctlspriv.h"
  3. #include "listview.h"
  4. #if defined(FE_IME)
  5. static TCHAR const szIMECompPos[]=TEXT("IMECompPos");
  6. #endif
  7. __inline int ICONCXLABEL(LV *plv, LISTITEM *pitem)
  8. {
  9. if (plv->ci.style & LVS_NOLABELWRAP) {
  10. ASSERT(pitem->cxSingleLabel == pitem->cxMultiLabel);
  11. }
  12. return pitem->cxMultiLabel;
  13. }
  14. int LV_GetNewColWidth(LV* plv, int iFirst, int iLast);
  15. void LV_AdjustViewRectOnMove(LV* plv, LISTITEM *pitem, int x, int y);
  16. UINT LV_IsItemOnViewEdge(LV* plv, LISTITEM *pitem);
  17. void ListView_RecalcRegion(LV *plv, BOOL fForce, BOOL fRedraw);
  18. extern BOOL g_fSlowMachine;
  19. BOOL ListView_IDrawItem(PLVDRAWITEM plvdi)
  20. {
  21. RECT rcIcon;
  22. RECT rcLabel;
  23. RECT rcBounds;
  24. RECT rcT;
  25. TCHAR ach[CCHLABELMAX];
  26. LV_ITEM item;
  27. int i = (int) plvdi->nmcd.nmcd.dwItemSpec;
  28. LV* plv = plvdi->plv;
  29. LISTITEM FAR* pitem;
  30. BOOL fUnfolded;
  31. if (ListView_IsOwnerData(plv))
  32. {
  33. LISTITEM litem;
  34. // moved here to reduce call backs in OWNERDATA case
  35. item.iItem = i;
  36. item.iSubItem = 0;
  37. item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
  38. item.stateMask = LVIS_ALL;
  39. item.pszText = ach;
  40. item.cchTextMax = ARRAYSIZE(ach);
  41. ListView_OnGetItem(plv, &item);
  42. litem.pszText = item.pszText;
  43. ListView_GetRectsOwnerData(plv, i, &rcIcon, &rcLabel, &rcBounds, NULL, &litem);
  44. pitem = NULL;
  45. }
  46. else
  47. {
  48. pitem = ListView_GetItemPtr(plv, i);
  49. // NOTE this will do a GetItem LVIF_TEXT iff needed
  50. ListView_GetRects(plv, i, &rcIcon, &rcLabel, &rcBounds, NULL);
  51. }
  52. fUnfolded = FALSE;
  53. if ( (plvdi->flags & LVDI_UNFOLDED) || ListView_IsItemUnfolded(plv, i))
  54. {
  55. ListView_UnfoldRects(plv, i, &rcIcon, &rcLabel, &rcBounds, NULL );
  56. fUnfolded = TRUE;
  57. }
  58. if (!plvdi->prcClip || IntersectRect(&rcT, &rcBounds, plvdi->prcClip))
  59. {
  60. UINT fText;
  61. if (!ListView_IsOwnerData(plv))
  62. {
  63. item.iItem = i;
  64. item.iSubItem = 0;
  65. item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE;
  66. item.stateMask = LVIS_ALL;
  67. item.pszText = ach;
  68. item.cchTextMax = ARRAYSIZE(ach);
  69. ListView_OnGetItem(plv, &item);
  70. // Make sure the listview hasn't been altered during
  71. // the callback to get the item info
  72. if (pitem != ListView_GetItemPtr(plv, i))
  73. return FALSE;
  74. }
  75. if (plvdi->lpptOrg)
  76. {
  77. OffsetRect(&rcIcon, plvdi->lpptOrg->x - rcBounds.left,
  78. plvdi->lpptOrg->y - rcBounds.top);
  79. OffsetRect(&rcLabel, plvdi->lpptOrg->x - rcBounds.left,
  80. plvdi->lpptOrg->y - rcBounds.top);
  81. }
  82. if (ListView_IsIconView(plv))
  83. {
  84. fText = ListView_DrawImage(plv, &item, plvdi->nmcd.nmcd.hdc,
  85. rcIcon.left + g_cxIconMargin, rcIcon.top + g_cyIconMargin, plvdi->flags);
  86. // If linebreaking needs to happen, then use SHDT_DRAWTEXT.
  87. // Otherwise, use our (hopefully faster) internal SHDT_ELLIPSES
  88. if (rcLabel.bottom - rcLabel.top > plv->cyLabelChar)
  89. fText |= SHDT_DRAWTEXT;
  90. else
  91. fText |= SHDT_ELLIPSES;
  92. // We only use DT_NOFULLWIDTHCHARBREAK on Korean(949) Memphis and NT5
  93. if (949 == g_uiACP && (g_bRunOnNT5 || g_bRunOnMemphis))
  94. fText |= SHDT_NODBCSBREAK;
  95. }
  96. else
  97. {
  98. fText = ListView_DrawImage(plv, &item, plvdi->nmcd.nmcd.hdc,
  99. rcIcon.left, rcIcon.top, plvdi->flags);
  100. }
  101. // Don't draw label if it's being edited...
  102. //
  103. if (plv->iEdit != i)
  104. {
  105. // If multiline label, then we need to use DrawText
  106. if (rcLabel.bottom - rcLabel.top > plv->cyLabelChar)
  107. {
  108. fText |= SHDT_DRAWTEXT;
  109. // If the text is folded, we need to clip and add ellipses
  110. if (!fUnfolded)
  111. fText |= SHDT_CLIPPED | SHDT_DTELLIPSIS;
  112. if ( ListView_IsOwnerData(plv) )
  113. {
  114. // If owner data, we have no z-order and if long names they will over lap each
  115. // other, better to truncate for now...
  116. if (ListView_IsSmallView(plv))
  117. fText |= SHDT_ELLIPSES;
  118. }
  119. }
  120. else
  121. fText |= SHDT_ELLIPSES;
  122. if (plvdi->flags & LVDI_TRANSTEXT)
  123. fText |= SHDT_TRANSPARENT;
  124. if ((fText & SHDT_SELECTED) && (plvdi->flags & LVDI_HOTSELECTED))
  125. fText |= SHDT_HOTSELECTED;
  126. if (item.pszText && (*item.pszText))
  127. {
  128. if(plv->dwExStyle & WS_EX_RTLREADING)
  129. {
  130. fText |= SHDT_RTLREADING;
  131. }
  132. SHDrawText(plvdi->nmcd.nmcd.hdc, item.pszText, &rcLabel, LVCFMT_LEFT, fText,
  133. plv->cyLabelChar, plv->cxEllipses,
  134. plvdi->nmcd.clrText, plvdi->nmcd.clrTextBk);
  135. if ((plvdi->flags & LVDI_FOCUS) && (item.state & LVIS_FOCUSED)
  136. && !(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS)
  137. )
  138. {
  139. DrawFocusRect(plvdi->nmcd.nmcd.hdc, &rcLabel);
  140. }
  141. }
  142. }
  143. }
  144. return TRUE;
  145. }
  146. void ListView_RefoldLabelRect(LV* plv, RECT *prcLabel, LISTITEM *pitem)
  147. {
  148. int bottom = pitem->cyUnfoldedLabel;
  149. bottom = min(bottom, pitem->cyFoldedLabel);
  150. bottom = min(bottom, CLIP_HEIGHT);
  151. prcLabel->bottom = prcLabel->top + bottom;
  152. }
  153. int NEAR ListView_IItemHitTest(LV* plv, int x, int y, UINT FAR* pflags, int *piSubItem)
  154. {
  155. int iHit;
  156. UINT flags;
  157. POINT pt;
  158. RECT rcLabel;
  159. RECT rcIcon;
  160. RECT rcState;
  161. if (piSubItem)
  162. *piSubItem = 0;
  163. // Map window-relative coordinates to view-relative coords...
  164. //
  165. pt.x = x + plv->ptOrigin.x;
  166. pt.y = y + plv->ptOrigin.y;
  167. // If there are any uncomputed items, recompute them now.
  168. //
  169. if (plv->rcView.left == RECOMPUTE)
  170. ListView_Recompute(plv);
  171. flags = 0;
  172. if (ListView_IsOwnerData( plv ))
  173. {
  174. int cSlots;
  175. POINT ptWnd;
  176. LISTITEM item;
  177. cSlots = ListView_GetSlotCount( plv, TRUE );
  178. iHit = ListView_CalcHitSlot( plv, pt, cSlots );
  179. if (iHit < ListView_Count(plv))
  180. {
  181. ListView_IGetRectsOwnerData( plv, iHit, &rcIcon, &rcLabel, &item, FALSE );
  182. ptWnd.x = x;
  183. ptWnd.y = y;
  184. if (PtInRect(&rcIcon, ptWnd))
  185. {
  186. flags = LVHT_ONITEMICON;
  187. }
  188. else if (PtInRect(&rcLabel, ptWnd))
  189. {
  190. flags = LVHT_ONITEMLABEL;
  191. }
  192. }
  193. }
  194. else
  195. {
  196. for (iHit = 0; (iHit < ListView_Count(plv)); iHit++)
  197. {
  198. LISTITEM FAR* pitem = ListView_FastGetZItemPtr(plv, iHit);
  199. POINT ptItem;
  200. ptItem.x = pitem->pt.x;
  201. ptItem.y = pitem->pt.y;
  202. rcIcon.top = ptItem.y - g_cyIconMargin;
  203. rcLabel.top = ptItem.y + plv->cyIcon + g_cyLabelSpace;
  204. rcLabel.bottom = rcLabel.top + pitem->cyUnfoldedLabel;
  205. if ( !ListView_IsItemUnfoldedPtr(plv, pitem) )
  206. ListView_RefoldLabelRect(plv, &rcLabel, pitem);
  207. // Quick, easy rejection test...
  208. //
  209. if (pt.y < rcIcon.top || pt.y >= rcLabel.bottom)
  210. continue;
  211. rcIcon.left = ptItem.x - g_cxIconMargin;
  212. rcIcon.right = ptItem.x + plv->cxIcon + g_cxIconMargin;
  213. // We need to make sure there is no gap between the icon and label
  214. rcIcon.bottom = rcLabel.top;
  215. rcState.bottom = ptItem.y + plv->cyIcon;
  216. rcState.right = ptItem.x;
  217. rcState.top = rcState.bottom - plv->cyState;
  218. rcState.left = rcState.right - plv->cxState;
  219. rcLabel.left = ptItem.x + (plv->cxIcon / 2) - (ICONCXLABEL(plv, pitem) / 2);
  220. rcLabel.right = rcLabel.left + ICONCXLABEL(plv, pitem);
  221. if (plv->cxState && PtInRect(&rcState, pt))
  222. {
  223. flags = LVHT_ONITEMSTATEICON;
  224. }
  225. else if (PtInRect(&rcIcon, pt))
  226. {
  227. flags = LVHT_ONITEMICON;
  228. }
  229. else if (PtInRect(&rcLabel, pt))
  230. {
  231. flags = LVHT_ONITEMLABEL;
  232. }
  233. if (flags)
  234. break;
  235. }
  236. }
  237. if (flags == 0)
  238. {
  239. flags = LVHT_NOWHERE;
  240. iHit = -1;
  241. }
  242. else
  243. {
  244. if (!ListView_IsOwnerData( plv ))
  245. {
  246. iHit = DPA_GetPtrIndex(plv->hdpa, ListView_FastGetZItemPtr(plv, iHit));
  247. }
  248. }
  249. *pflags = flags;
  250. return iHit;
  251. }
  252. // BUGBUG raymondc
  253. // need to pass HDC here isnce it's sometimes called from the paint loop
  254. void NEAR ListView_IGetRectsOwnerData( LV* plv,
  255. int iItem,
  256. RECT FAR* prcIcon,
  257. RECT FAR* prcLabel,
  258. LISTITEM* pitem,
  259. BOOL fUsepitem )
  260. {
  261. int itemIconXLabel;
  262. int cSlots;
  263. // calculate x, y from iItem
  264. cSlots = ListView_GetSlotCount( plv, TRUE );
  265. pitem->iWorkArea = 0; // OwnerData doesn't support workareas
  266. ListView_SetIconPos( plv, pitem, iItem, cSlots );
  267. // calculate lable sizes from iItem
  268. ListView_RecomputeLabelSize( plv, pitem, iItem, NULL, fUsepitem);
  269. if (plv->ci.style & LVS_NOLABELWRAP)
  270. {
  271. // use single label
  272. itemIconXLabel = pitem->cxSingleLabel;
  273. }
  274. else
  275. {
  276. // use multilabel
  277. itemIconXLabel = pitem->cxMultiLabel;
  278. }
  279. prcIcon->left = pitem->pt.x - g_cxIconMargin - plv->ptOrigin.x;
  280. prcIcon->right = prcIcon->left + plv->cxIcon + 2 * g_cxIconMargin;
  281. prcIcon->top = pitem->pt.y - g_cyIconMargin - plv->ptOrigin.y;
  282. prcIcon->bottom = prcIcon->top + plv->cyIcon + 2 * g_cyIconMargin;
  283. prcLabel->left = pitem->pt.x + (plv->cxIcon / 2) - (itemIconXLabel / 2) - plv->ptOrigin.x;
  284. prcLabel->right = prcLabel->left + itemIconXLabel;
  285. prcLabel->top = pitem->pt.y + plv->cyIcon + g_cyLabelSpace - plv->ptOrigin.y;
  286. prcLabel->bottom = prcLabel->top + pitem->cyUnfoldedLabel;
  287. if ( !ListView_IsItemUnfolded(plv, iItem) )
  288. ListView_RefoldLabelRect(plv, prcLabel, pitem);
  289. }
  290. // out:
  291. // prcIcon icon bounds including icon margin area
  292. void NEAR ListView_IGetRects(LV* plv, LISTITEM FAR* pitem, RECT FAR* prcIcon, RECT FAR* prcLabel, LPRECT prcBounds)
  293. {
  294. int cxIconMargin;
  295. ASSERT( !ListView_IsOwnerData( plv ) );
  296. if (pitem->pt.x == RECOMPUTE) {
  297. ListView_Recompute(plv);
  298. }
  299. if (ListView_IsIconView(plv) && ((plv->cxIconSpacing - plv->cxIcon) < (2 * g_cxIconMargin)))
  300. cxIconMargin = (plv->cxIconSpacing - plv->cxIcon) / 2;
  301. else
  302. cxIconMargin = g_cxIconMargin;
  303. prcIcon->left = pitem->pt.x - cxIconMargin - plv->ptOrigin.x;
  304. prcIcon->right = prcIcon->left + plv->cxIcon + 2 * cxIconMargin;
  305. prcIcon->top = pitem->pt.y - g_cyIconMargin - plv->ptOrigin.y;
  306. prcIcon->bottom = prcIcon->top + plv->cyIcon + 2 * g_cyIconMargin;
  307. prcLabel->left = pitem->pt.x + (plv->cxIcon / 2) - (ICONCXLABEL(plv, pitem) / 2) - plv->ptOrigin.x;
  308. prcLabel->right = prcLabel->left + ICONCXLABEL(plv, pitem);
  309. prcLabel->top = pitem->pt.y + plv->cyIcon + g_cyLabelSpace - plv->ptOrigin.y;
  310. prcLabel->bottom = prcLabel->top + pitem->cyUnfoldedLabel;
  311. if ( !ListView_IsItemUnfoldedPtr(plv, pitem) )
  312. ListView_RefoldLabelRect(plv, prcLabel, pitem);
  313. }
  314. int NEAR ListView_GetSlotCountEx(LV* plv, BOOL fWithoutScrollbars, int iWorkArea)
  315. {
  316. int cxScreen;
  317. int cyScreen;
  318. int dxItem;
  319. int dyItem;
  320. int iSlots = 1;
  321. BOOL fCheckWithScroll = FALSE;
  322. DWORD style = 0;
  323. // Always use the current client window size to determine
  324. //
  325. // REVIEW: Should we exclude any vertical scroll bar that may
  326. // exist when computing this? progman.exe does not.
  327. //
  328. if ((iWorkArea >= 0 ) && (plv->nWorkAreas > 0))
  329. {
  330. ASSERT(iWorkArea < plv->nWorkAreas);
  331. cxScreen = RECTWIDTH(plv->prcWorkAreas[iWorkArea]);
  332. cyScreen = RECTHEIGHT(plv->prcWorkAreas[iWorkArea]);
  333. }
  334. else
  335. {
  336. cxScreen = plv->sizeClient.cx;
  337. cyScreen = plv->sizeClient.cy;
  338. }
  339. if (fWithoutScrollbars)
  340. {
  341. style = ListView_GetWindowStyle(plv);
  342. if (style & WS_VSCROLL)
  343. {
  344. cxScreen += ListView_GetCxScrollbar(plv);
  345. }
  346. if (style & WS_HSCROLL)
  347. {
  348. cyScreen += ListView_GetCyScrollbar(plv);
  349. }
  350. }
  351. if (ListView_IsSmallView(plv))
  352. dxItem = plv->cxItem;
  353. else
  354. dxItem = lv_cxIconSpacing;
  355. if (ListView_IsSmallView(plv))
  356. dyItem = plv->cyItem;
  357. else
  358. dyItem = lv_cyIconSpacing;
  359. if (!dxItem)
  360. dxItem = 1;
  361. if (!dyItem)
  362. dyItem = 1;
  363. // Lets see which direction the view states
  364. switch (plv->ci.style & LVS_ALIGNMASK)
  365. {
  366. case LVS_ALIGNBOTTOM:
  367. case LVS_ALIGNTOP:
  368. iSlots = max(1, (cxScreen) / dxItem);
  369. fCheckWithScroll = (BOOL)(style & WS_VSCROLL);
  370. break;
  371. case LVS_ALIGNRIGHT:
  372. case LVS_ALIGNLEFT:
  373. iSlots = max(1, (cyScreen) / dyItem);
  374. fCheckWithScroll = (BOOL)(style & WS_HSCROLL);
  375. break;
  376. default:
  377. ASSERT(0);
  378. return 1;
  379. }
  380. // if we don't have enough slots total on the screen, we're going to have
  381. // a scrollbar, so recompute with the scrollbars on
  382. if (fWithoutScrollbars && fCheckWithScroll) {
  383. int iTotalSlots = (dxItem * dyItem);
  384. if (iTotalSlots < ListView_Count(plv)) {
  385. iSlots = ListView_GetSlotCountEx(plv, FALSE, iWorkArea);
  386. }
  387. }
  388. return iSlots;
  389. }
  390. int NEAR ListView_GetSlotCount(LV* plv, BOOL fWithoutScrollbars)
  391. {
  392. // Make sure this function does exactly the same thing as when
  393. // we had no workareas
  394. return ListView_GetSlotCountEx(plv, fWithoutScrollbars, -1);
  395. }
  396. // get the pixel row (or col in left align) of pitem
  397. int LV_GetItemPixelRow(LV* plv, LISTITEM* pitem)
  398. {
  399. if ((plv->ci.style & LVS_ALIGNMASK) == LVS_ALIGNLEFT) {
  400. return pitem->pt.x;
  401. } else {
  402. return pitem->pt.y;
  403. }
  404. }
  405. // get the pixel row (or col in left align) of the lowest item
  406. int LV_GetMaxPlacedItem(LV* plv)
  407. {
  408. int i;
  409. int iMaxPlacedItem = 0;
  410. for (i = 0; i < ListView_Count(plv); i++) {
  411. LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, i);
  412. if (pitem->pt.y != RECOMPUTE) {
  413. int iRow = LV_GetItemPixelRow(plv, pitem);
  414. // if the current item is "below" (on right if it's left aligned)
  415. // the lowest placed item, we can start appending
  416. if (!i || iRow > iMaxPlacedItem)
  417. iMaxPlacedItem = iRow;
  418. }
  419. }
  420. return iMaxPlacedItem;;
  421. }
  422. // Go through and recompute any icon positions and optionally
  423. // icon label dimensions.
  424. //
  425. // This function also recomputes the view bounds rectangle.
  426. //
  427. // The algorithm is to simply search the list for any items needing
  428. // recomputation. For icon positions, we scan possible icon slots
  429. // and check to see if any already-positioned icon intersects the slot.
  430. // If not, the slot is free. As an optimization, we start scanning
  431. // icon slots from the previous slot we found.
  432. //
  433. void NEAR ListView_Recompute(LV* plv)
  434. {
  435. int i;
  436. int cSlots;
  437. int cWorkAreaSlots[LV_MAX_WORKAREAS];
  438. BOOL fUpdateSB;
  439. // if all the items are unplaced, we can just keep appending
  440. BOOL fAppendAtEnd = (((UINT)ListView_Count(plv)) == plv->uUnplaced);
  441. int iFree;
  442. plv->uUnplaced = 0;
  443. if (!(ListView_IsIconView(plv) || ListView_IsSmallView(plv)))
  444. return;
  445. if (plv->flags & LVF_INRECOMPUTE)
  446. {
  447. return;
  448. }
  449. plv->flags |= LVF_INRECOMPUTE;
  450. cSlots = ListView_GetSlotCount(plv, FALSE);
  451. if (plv->nWorkAreas > 0)
  452. for (i = 0; i < plv->nWorkAreas; i++)
  453. cWorkAreaSlots[i] = ListView_GetSlotCountEx(plv, FALSE, i);
  454. // Scan all items for RECOMPUTE, and recompute slot if needed.
  455. //
  456. fUpdateSB = (plv->rcView.left == RECOMPUTE);
  457. if (!ListView_IsOwnerData( plv ))
  458. {
  459. LVFAKEDRAW lvfd; // in case client uses customdraw
  460. LV_ITEM item; // in case client uses customdraw
  461. int iMaxPlacedItem = RECOMPUTE;
  462. item.mask = LVIF_PARAM;
  463. item.iSubItem = 0;
  464. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  465. if (!fAppendAtEnd)
  466. iMaxPlacedItem = LV_GetMaxPlacedItem(plv);
  467. // Must keep in local variable because ListView_SetIconPos will keep
  468. // invalidating the iFreeSlot cache while we're looping
  469. iFree = plv->iFreeSlot;
  470. for (i = 0; i < ListView_Count(plv); i++)
  471. {
  472. int cRealSlots;
  473. LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, i);
  474. BOOL fRedraw = FALSE;
  475. cRealSlots = (plv->nWorkAreas > 0) ? cWorkAreaSlots[pitem->iWorkArea] : cSlots;
  476. if (pitem->pt.y == RECOMPUTE)
  477. {
  478. if (pitem->cyFoldedLabel == SRECOMPUTE)
  479. {
  480. // Get the item lParam only if we need it for customdraw
  481. item.iItem = i;
  482. item.lParam = pitem->lParam;
  483. ListView_BeginFakeItemDraw(&lvfd);
  484. ListView_RecomputeLabelSize(plv, pitem, i, lvfd.nmcd.nmcd.hdc, FALSE);
  485. ListView_EndFakeItemDraw(&lvfd);
  486. }
  487. // BUGBUG: (dli) This function gets a new icon postion and then goes
  488. // through the whole set of items to see if that position is occupied
  489. // should let it know in the multi-workarea case, it only needs to go
  490. // through those who are in the same workarea.
  491. // This is okay for now because we cannot have too many items on the
  492. // desktop.
  493. iFree = ListView_FindFreeSlot(plv, i, iFree + 1, cRealSlots, &fUpdateSB, &fAppendAtEnd, lvfd.nmcd.nmcd.hdc);
  494. ASSERT(iFree != -1);
  495. ListView_SetIconPos(plv, pitem, iFree, cRealSlots);
  496. if (!fAppendAtEnd) {
  497. //// optimization. each time we calc a new free slot, we iterate through all the items to see
  498. // if any of the freely placed items collide with this.
  499. // fAppendAtEnd indicates that iFree is beyond any freely placed item
  500. //
  501. // if the current item is "below" (on right if it's left aligned)
  502. // the lowest placed item, we can start appending
  503. if (LV_GetItemPixelRow(plv, pitem) > iMaxPlacedItem)
  504. fAppendAtEnd = TRUE;
  505. }
  506. if (!fUpdateSB && LV_IsItemOnViewEdge(plv, pitem))
  507. fUpdateSB = TRUE;
  508. fRedraw = TRUE;
  509. }
  510. if (fRedraw)
  511. {
  512. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
  513. }
  514. }
  515. plv->iFreeSlot = iFree;
  516. ListView_EndFakeCustomDraw(&lvfd);
  517. }
  518. // If we changed something, recompute the view rectangle
  519. // and then update the scroll bars.
  520. //
  521. if (fUpdateSB || plv->rcView.left == RECOMPUTE )
  522. {
  523. TraceMsg(TF_GENERAL, "************ LV: Expensive update! ******* ");
  524. // NOTE: No infinite recursion results because we're setting
  525. // plv->rcView.left != RECOMPUTE
  526. //
  527. SetRectEmpty(&plv->rcView);
  528. if (ListView_IsOwnerData( plv ))
  529. {
  530. if (ListView_Count( plv ) > 0)
  531. {
  532. RECT rcLast;
  533. RECT rcItem;
  534. int iSlots;
  535. int iItem = ListView_Count( plv ) - 1;
  536. ListView_GetRects( plv, 0, NULL, NULL, &plv->rcView, NULL );
  537. ListView_GetRects( plv, iItem, NULL, NULL, &rcLast, NULL );
  538. plv->rcView.right = rcLast.right;
  539. plv->rcView.bottom = rcLast.bottom;
  540. //
  541. // calc how far back in the list to check
  542. //
  543. iSlots = cSlots + 2;
  544. // REVIEW: This cache hint notification causes a spurious
  545. // hint, since this happens often but is always the last items
  546. // available. Should this hint be done at all and this information
  547. // be cached local to the control?
  548. ListView_NotifyCacheHint( plv, max( 0, iItem - iSlots), iItem );
  549. // move backwards from last item until either rc.right or
  550. // rc.left is greater than the last, then use that value.
  551. // Note: This code makes very little assumptions about the ordering
  552. // done. We should be careful as multiple line text fields could
  553. // mess us up.
  554. for( iItem--;
  555. (iSlots > 0) && (iItem >= 0);
  556. iSlots--, iItem--)
  557. {
  558. RECT rcIcon;
  559. RECT rcLabel;
  560. ListView_GetRects( plv, iItem, &rcIcon, &rcLabel, &rcItem, NULL );
  561. ListView_UnfoldRects( plv, iItem, &rcIcon, &rcLabel, &rcItem, NULL );
  562. if (rcItem.right > rcLast.right)
  563. {
  564. plv->rcView.right = rcItem.right;
  565. }
  566. if (rcItem.bottom > rcLast.bottom)
  567. {
  568. plv->rcView.bottom = rcItem.bottom;
  569. }
  570. }
  571. }
  572. }
  573. else
  574. {
  575. for (i = 0; i < ListView_Count(plv); i++)
  576. {
  577. RECT rcIcon;
  578. RECT rcLabel;
  579. RECT rcItem;
  580. ListView_GetRects(plv, i, &rcIcon, &rcLabel, &rcItem, NULL);
  581. ListView_UnfoldRects(plv, i, &rcIcon, &rcLabel, &rcItem, NULL);
  582. UnionRect(&plv->rcView, &plv->rcView, &rcItem);
  583. }
  584. }
  585. // add a little space at the edges so that we don't bump text
  586. // completely to the end of the window
  587. plv->rcView.bottom += g_cyEdge;
  588. plv->rcView.right += g_cxEdge;
  589. OffsetRect(&plv->rcView, plv->ptOrigin.x, plv->ptOrigin.y);
  590. //TraceMsg(DM_TRACE, "RECOMPUTE: rcView %x %x %x %x", plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
  591. //TraceMsg(DM_TRACE, "Origin %x %x", plv->ptOrigin.x, plv->ptOrigin.y);
  592. ListView_UpdateScrollBars(plv);
  593. }
  594. ListView_RecalcRegion(plv, FALSE, TRUE);
  595. // Now state we are out of the recompute...
  596. plv->flags &= ~LVF_INRECOMPUTE;
  597. }
  598. void NEAR PASCAL NearestSlot(int FAR *x, int FAR *y, int cxItem, int cyItem, LPRECT prcWork)
  599. {
  600. if (prcWork != NULL)
  601. {
  602. *x = *x - prcWork->left;
  603. *y = *y - prcWork->top;
  604. }
  605. if (*x < 0)
  606. *x -= cxItem/2;
  607. else
  608. *x += cxItem/2;
  609. if (*y < 0)
  610. *y -= cyItem/2;
  611. else
  612. *y += cyItem/2;
  613. *x = *x - (*x % cxItem);
  614. *y = *y - (*y % cyItem);
  615. if (prcWork != NULL)
  616. {
  617. *x = *x + prcWork->left;
  618. *y = *y + prcWork->top;
  619. }
  620. }
  621. //-------------------------------------------------------------------
  622. //
  623. //-------------------------------------------------------------------
  624. void ListView_CalcMinMaxIndex( LV* plv, PRECT prcBounding, int* iMin, int* iMax )
  625. {
  626. POINT pt;
  627. int cSlots;
  628. cSlots = ListView_GetSlotCount( plv, TRUE );
  629. pt.x = prcBounding->left + plv->ptOrigin.x;
  630. pt.y = prcBounding->top + plv->ptOrigin.y;
  631. *iMin = ListView_CalcHitSlot( plv, pt, cSlots );
  632. pt.x = prcBounding->right + plv->ptOrigin.x;
  633. pt.y = prcBounding->bottom + plv->ptOrigin.y;
  634. *iMax = ListView_CalcHitSlot( plv, pt, cSlots ) + 1;
  635. }
  636. //-------------------------------------------------------------------
  637. //
  638. // Function: ListView_CalcHitSlot
  639. //
  640. // Summary: Given a point (relative to complete icon view), calculate
  641. // which slot that point is closest to.
  642. //
  643. // Arguments:
  644. // plv [in] - List view to work with
  645. // pt [in] - location to check with
  646. // cslot [in] - number of slots wide the current view is
  647. //
  648. // Notes: This does not guarentee that the point is hitting the item
  649. // located at that slot. That should be checked by comparing rects.
  650. //
  651. // History:
  652. // Nov-1-1994 MikeMi Added to improve Ownerdata hit testing
  653. //
  654. //-------------------------------------------------------------------
  655. int ListView_CalcHitSlot( LV* plv, POINT pt, int cSlot )
  656. {
  657. int cxItem;
  658. int cyItem;
  659. int iSlot = 0;
  660. ASSERT(plv);
  661. if (cSlot < 1)
  662. cSlot = 1;
  663. if (ListView_IsSmallView(plv))
  664. {
  665. cxItem = plv->cxItem;
  666. cyItem = plv->cyItem;
  667. }
  668. else
  669. {
  670. cxItem = lv_cxIconSpacing;
  671. cyItem = lv_cyIconSpacing;
  672. }
  673. // Lets see which direction the view states
  674. switch (plv->ci.style & LVS_ALIGNMASK)
  675. {
  676. case LVS_ALIGNBOTTOM:
  677. // Assert False (Change default in shell2d.. to ALIGN_TOP)
  678. case LVS_ALIGNTOP:
  679. iSlot = (pt.x / cxItem) + (pt.y / cyItem) * cSlot;
  680. break;
  681. case LVS_ALIGNLEFT:
  682. iSlot = (pt.x / cxItem) * cSlot + (pt.y / cyItem);
  683. break;
  684. case LVS_ALIGNRIGHT:
  685. ASSERT(FALSE); // Not implemented yet...
  686. break;
  687. }
  688. return( iSlot );
  689. }
  690. void _GetCurrentItemSize(LV* plv, int * pcx, int *pcy)
  691. {
  692. if (ListView_IsSmallView(plv))
  693. {
  694. *pcx = plv->cxItem;
  695. *pcy = plv->cyItem;
  696. }
  697. else
  698. {
  699. *pcx = lv_cxIconSpacing;
  700. *pcy = lv_cyIconSpacing;
  701. }
  702. }
  703. DWORD ListView_IApproximateViewRect(LV* plv, int iCount, int iWidth, int iHeight)
  704. {
  705. int cxSave = plv->sizeClient.cx;
  706. int cySave = plv->sizeClient.cy;
  707. int cxItem;
  708. int cyItem;
  709. int cCols;
  710. int cRows;
  711. plv->sizeClient.cx = iWidth;
  712. plv->sizeClient.cy = iHeight;
  713. cCols = ListView_GetSlotCount(plv, TRUE);
  714. plv->sizeClient.cx = cxSave;
  715. plv->sizeClient.cy = cySave;
  716. cCols = min(cCols, iCount);
  717. if (cCols == 0)
  718. cCols = 1;
  719. cRows = (iCount + cCols - 1) / cCols;
  720. if (plv->ci.style & (LVS_ALIGNLEFT | LVS_ALIGNRIGHT)) {
  721. int c;
  722. c = cCols;
  723. cCols = cRows;
  724. cRows = c;
  725. }
  726. _GetCurrentItemSize(plv, &cxItem, &cyItem);
  727. iWidth = cCols * cxItem;
  728. iHeight = cRows * cyItem;
  729. return MAKELONG(iWidth + g_cxEdge, iHeight + g_cyEdge);
  730. }
  731. void NEAR _CalcSlotRect(LV* plv, LISTITEM *pItem, int iSlot, int cSlot, BOOL fBias, LPRECT lprc)
  732. {
  733. int cxItem, cyItem;
  734. ASSERT(plv);
  735. if (cSlot < 1)
  736. cSlot = 1;
  737. _GetCurrentItemSize(plv, &cxItem, &cyItem);
  738. // Lets see which direction the view states
  739. switch (plv->ci.style & LVS_ALIGNMASK)
  740. {
  741. case LVS_ALIGNBOTTOM:
  742. // Assert False (Change default in shell2d.. to ALIGN_TOP)
  743. case LVS_ALIGNTOP:
  744. lprc->left = (iSlot % cSlot) * cxItem;
  745. lprc->top = (iSlot / cSlot) * cyItem;
  746. break;
  747. case LVS_ALIGNRIGHT:
  748. RIPMSG(0, "LVM_ARRANGE: Invalid listview icon arrangement style");
  749. // ASSERT(FALSE); // Not implemented yet...
  750. // fall through, use LVS_ALIGNLEFT instead
  751. case LVS_ALIGNLEFT:
  752. lprc->top = (iSlot % cSlot) * cyItem;
  753. lprc->left = (iSlot / cSlot) * cxItem;
  754. break;
  755. }
  756. if (fBias)
  757. {
  758. lprc->left -= plv->ptOrigin.x;
  759. lprc->top -= plv->ptOrigin.y;
  760. }
  761. lprc->bottom = lprc->top + cyItem;
  762. lprc->right = lprc->left + cxItem;
  763. // Multi-Workarea case offset from the workarea coordinate to the whole
  764. // listview coordinate.
  765. if (plv->nWorkAreas > 0)
  766. {
  767. ASSERT(pItem);
  768. ASSERT(pItem->iWorkArea < plv->nWorkAreas);
  769. OffsetRect(lprc, plv->prcWorkAreas[pItem->iWorkArea].left, plv->prcWorkAreas[pItem->iWorkArea].top);
  770. }
  771. }
  772. // Intersect this rectangle with all items in this listview except myself,
  773. // this will determine if this rectangle overlays any icons.
  774. BOOL NEAR ListView_IsCleanRect(LV * plv, RECT * prc, int iExcept, BOOL * pfUpdate, HDC hdc)
  775. {
  776. int j;
  777. RECT rc;
  778. int cItems = ListView_Count(plv);
  779. for (j = cItems; j-- > 0; )
  780. {
  781. if (j == iExcept)
  782. continue;
  783. else
  784. {
  785. LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, j);
  786. if (pitem->pt.y != RECOMPUTE)
  787. {
  788. // If the dimensions aren't computed, then do it now.
  789. //
  790. if (pitem->cyFoldedLabel == SRECOMPUTE)
  791. {
  792. ListView_RecomputeLabelSize(plv, pitem, j, hdc, FALSE);
  793. // Ensure that the item gets redrawn...
  794. //
  795. ListView_InvalidateItem(plv, j, FALSE, RDW_INVALIDATE | RDW_ERASE);
  796. // Set flag indicating that scroll bars need to be
  797. // adjusted.
  798. //
  799. if (LV_IsItemOnViewEdge(plv, pitem))
  800. *pfUpdate = TRUE;
  801. }
  802. ListView_GetRects(plv, j, NULL, NULL, &rc, NULL);
  803. if (IntersectRect(&rc, &rc, prc))
  804. return FALSE;
  805. }
  806. }
  807. }
  808. return TRUE;
  809. }
  810. // Find an icon slot that doesn't intersect an icon.
  811. // Start search for free slot from slot i.
  812. //
  813. int NEAR ListView_FindFreeSlot(LV* plv, int iItem, int i, int cSlot, BOOL FAR* pfUpdate,
  814. BOOL FAR *pfAppend, HDC hdc)
  815. {
  816. RECT rcSlot;
  817. RECT rcItem;
  818. RECT rc;
  819. LISTITEM FAR * pItemLooking = ListView_FastGetItemPtr(plv, iItem);
  820. ASSERT(!ListView_IsOwnerData( plv ));
  821. // Horrible N-squared algorithm:
  822. // enumerate each slot and see if any items intersect it.
  823. //
  824. // REVIEW: This is really slow with long lists (e.g., 1000)
  825. //
  826. //
  827. // If the Append at end is set, we should be able to simply get the
  828. // rectangle of the i-1 element and check against it instead of
  829. // looking at every other item...
  830. //
  831. if (*pfAppend)
  832. {
  833. int iPrev = iItem - 1;
  834. // Be carefull about going of the end of the list. (i is a slot
  835. // number not an item index).
  836. if (plv->nWorkAreas > 0)
  837. {
  838. while (iPrev >= 0)
  839. {
  840. LISTITEM FAR * pPrev = ListView_FastGetItemPtr(plv, iPrev);
  841. if (pPrev->iWorkArea == pItemLooking->iWorkArea)
  842. break;
  843. iPrev--;
  844. }
  845. }
  846. if (iPrev >= 0)
  847. ListView_GetRects(plv, iPrev, NULL, NULL, &rcItem, NULL);
  848. else
  849. SetRect(&rcItem, 0, 0, 0, 0);
  850. }
  851. for ( ; ; i++)
  852. {
  853. // Compute view-relative slot rectangle...
  854. //
  855. _CalcSlotRect(plv, pItemLooking, i, cSlot, TRUE, &rcSlot);
  856. if (*pfAppend)
  857. {
  858. if (!IntersectRect(&rc, &rcItem, &rcSlot)) {
  859. return i; // Found a free slot...
  860. }
  861. }
  862. if (ListView_IsCleanRect(plv, &rcSlot, iItem, pfUpdate, hdc))
  863. break;
  864. }
  865. return i;
  866. }
  867. // Recompute an item's label size (cxLabel/cyLabel). For speed, this function
  868. // is passed a DC to use for text measurement.
  869. //
  870. // If hdc is NULL, then this function will create and initialize a temporary
  871. // DC, then destroy it. If hdc is non-NULL, then it is assumed to have
  872. // the correct font already selected into it.
  873. //
  874. // fUsepitem means not to use the text of the item. Instead, use the text
  875. // pointed to by the pitem structure. This is used in two cases.
  876. //
  877. // - Ownerdata, because we don't have a real pitem.
  878. // - Regulardata, where we already found the pitem text (as an optimizatin)
  879. //
  880. void NEAR ListView_RecomputeLabelSize(LV* plv, LISTITEM FAR* pitem, int i, HDC hdc, BOOL fUsepitem)
  881. {
  882. TCHAR szLabel[CCHLABELMAX + 4];
  883. TCHAR szLabelFolded[ARRAYSIZE(szLabel) + CCHELLIPSES + CCHELLIPSES];
  884. int cchLabel;
  885. RECT rcSingle, rcFolded, rcUnfolded;
  886. LVFAKEDRAW lvfd;
  887. LV_ITEM item;
  888. ASSERT(plv);
  889. // the following will use the passed in pitem text instead of calling
  890. // GetItem. This would be two consecutive calls otherwise, in some cases.
  891. //
  892. if (fUsepitem && (pitem->pszText != LPSTR_TEXTCALLBACK))
  893. {
  894. Str_GetPtr0(pitem->pszText, szLabel, ARRAYSIZE(szLabel));
  895. item.lParam = pitem->lParam;
  896. }
  897. else
  898. {
  899. item.mask = LVIF_TEXT | LVIF_PARAM;
  900. item.iItem = i;
  901. item.iSubItem = 0;
  902. item.pszText = szLabel;
  903. item.cchTextMax = ARRAYSIZE(szLabel);
  904. item.stateMask = 0;
  905. szLabel[0] = TEXT('\0'); // In case the OnGetItem fails
  906. ListView_OnGetItem(plv, &item);
  907. if (!item.pszText)
  908. {
  909. SetRectEmpty(&rcSingle);
  910. rcFolded = rcSingle;
  911. rcUnfolded = rcSingle;
  912. goto Exit;
  913. }
  914. if (item.pszText != szLabel)
  915. {
  916. StringCchCopy(szLabel, ARRAYSIZE(szLabel), item.pszText);
  917. }
  918. }
  919. cchLabel = lstrlen(szLabel);
  920. rcUnfolded.left = rcUnfolded.top = rcUnfolded.bottom = 0;
  921. rcUnfolded.right = lv_cxIconSpacing - g_cxLabelMargin * 2;
  922. rcSingle = rcUnfolded;
  923. rcFolded = rcUnfolded;
  924. if (cchLabel > 0)
  925. {
  926. UINT flags;
  927. if (!hdc) { // Set up fake customdraw
  928. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  929. ListView_BeginFakeItemDraw(&lvfd);
  930. } else
  931. lvfd.nmcd.nmcd.hdc = hdc; // Use the one the app gave us
  932. DrawText(lvfd.nmcd.nmcd.hdc, szLabel, cchLabel, &rcSingle, (DT_LV | DT_CALCRECT));
  933. if (plv->ci.style & LVS_NOLABELWRAP) {
  934. flags = DT_LV | DT_CALCRECT;
  935. } else {
  936. flags = DT_LVWRAP | DT_CALCRECT;
  937. // We only use DT_NOFULLWIDTHCHARBREAK on Korean(949) Memphis and NT5
  938. if (949 == g_uiACP && (g_bRunOnNT5 || g_bRunOnMemphis))
  939. flags |= DT_NOFULLWIDTHCHARBREAK;
  940. }
  941. DrawText(lvfd.nmcd.nmcd.hdc, szLabel, cchLabel, &rcUnfolded, flags);
  942. //
  943. // DrawText with DT_MODIFYSTRING is quirky when you enable
  944. // word ellipses. Once it finds anything that requires ellipses,
  945. // it stops and doesn't return anything else (even if those other
  946. // things got displayed).
  947. //
  948. StringCchCopy(szLabelFolded, ARRAYSIZE(szLabelFolded), szLabel);
  949. DrawText(lvfd.nmcd.nmcd.hdc, szLabelFolded, cchLabel, &rcFolded, flags | DT_WORD_ELLIPSIS | DT_MODIFYSTRING);
  950. // If we had to ellipsify, but you can't tell from looking at the
  951. // rcFolded.bottom and rcUnfolded.bottom, then tweak rcFolded.bottom
  952. // so the unfoldifier knows that unfolding is worthwhile.
  953. if (rcFolded.bottom == rcUnfolded.bottom &&
  954. lstrcmp(szLabel, szLabelFolded))
  955. {
  956. // The actual value isn't important, as long as it's greater
  957. // than rcUnfolded.bottom and CLIP_HEIGHT. We take advantage
  958. // of the fact that CLIP_HEIGHT is only two lines, so the only
  959. // problem case is where you have a two-line item and only the
  960. // first line is ellipsified.
  961. rcFolded.bottom++;
  962. }
  963. if (!hdc) { // Clean up fake customdraw
  964. ListView_EndFakeItemDraw(&lvfd);
  965. ListView_EndFakeCustomDraw(&lvfd);
  966. }
  967. }
  968. else
  969. {
  970. rcFolded.bottom = rcUnfolded.bottom = rcUnfolded.top + plv->cyLabelChar;
  971. }
  972. Exit:
  973. if (pitem) {
  974. int cyEdge;
  975. pitem->cxSingleLabel = (short)((rcSingle.right - rcSingle.left) + 2 * g_cxLabelMargin);
  976. pitem->cxMultiLabel = (short)((rcUnfolded.right - rcUnfolded.left) + 2 * g_cxLabelMargin);
  977. cyEdge = (plv->ci.style & LVS_NOLABELWRAP) ? 0 : g_cyEdge;
  978. pitem->cyFoldedLabel = (short)((rcFolded.bottom - rcFolded.top) + cyEdge);
  979. pitem->cyUnfoldedLabel = (short)((rcUnfolded.bottom - rcUnfolded.top) + cyEdge);
  980. }
  981. }
  982. // Set up an icon slot position. Returns FALSE if position didn't change.
  983. //
  984. BOOL NEAR ListView_SetIconPos(LV* plv, LISTITEM FAR* pitem, int iSlot, int cSlot)
  985. {
  986. RECT rc;
  987. ASSERT(plv);
  988. //
  989. // Sort of a hack, this internal function return TRUE if small icon.
  990. _CalcSlotRect(plv, pitem, iSlot, cSlot, FALSE, &rc);
  991. if (ListView_IsIconView(plv))
  992. {
  993. rc.left += ((lv_cxIconSpacing - plv->cxIcon) / 2);
  994. rc.top += g_cyIconOffset;
  995. }
  996. if (rc.left != pitem->pt.x || rc.top != pitem->pt.y)
  997. {
  998. LV_AdjustViewRectOnMove(plv, pitem, rc.left, rc.top);
  999. return TRUE;
  1000. }
  1001. return FALSE;
  1002. }
  1003. void NEAR ListView_GetViewRect2(LV* plv, RECT FAR* prcView, int cx, int cy)
  1004. {
  1005. if (plv->rcView.left == RECOMPUTE)
  1006. ListView_Recompute(plv);
  1007. *prcView = plv->rcView;
  1008. //
  1009. // Offsets for scrolling.
  1010. //
  1011. OffsetRect(prcView, -plv->ptOrigin.x, -plv->ptOrigin.y);
  1012. if (ListView_IsIconView(plv) || ListView_IsSmallView(plv))
  1013. {
  1014. // don't do that funky half-re-origining thing.
  1015. RECT rc;
  1016. rc.left = 0;
  1017. rc.top = 0;
  1018. rc.right = cx;
  1019. rc.bottom = cy;
  1020. UnionRect(prcView, prcView, &rc);
  1021. }
  1022. }
  1023. // prcViewRect used only if fSubScroll is TRUE
  1024. DWORD NEAR ListView_GetClientRect(LV* plv, RECT FAR* prcClient, BOOL fSubScroll, RECT FAR *prcViewRect)
  1025. {
  1026. RECT rcClient;
  1027. RECT rcView;
  1028. DWORD style;
  1029. #if 1
  1030. // do this instead of the #else below because
  1031. // in new versus old apps, you may need to add in g_c?Border because of
  1032. // the one pixel overlap...
  1033. GetWindowRect(plv->ci.hwnd, &rcClient);
  1034. if (GetWindowLong(plv->ci.hwnd, GWL_EXSTYLE) & (WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE)) {
  1035. rcClient.right -= 2 * g_cxEdge;
  1036. rcClient.bottom -= 2 * g_cyEdge;
  1037. }
  1038. rcClient.right -= rcClient.left;
  1039. rcClient.bottom -= rcClient.top;
  1040. if (rcClient.right < 0)
  1041. rcClient.right = 0;
  1042. if (rcClient.bottom < 0)
  1043. rcClient.bottom = 0;
  1044. rcClient.top = rcClient.left = 0;
  1045. #else
  1046. style = ListView_GetWindowStyle(plv);
  1047. GetClientRect(plv->ci.hwnd, &rcClient);
  1048. if (style & WS_VSCROLL)
  1049. rcClient.right += ListView_GetCxScrollbar(plv);
  1050. if (style & WS_HSCROLL)
  1051. rcClient.bottom += ListView_GetCyScrollbar(plv);
  1052. #endif
  1053. style = 0L;
  1054. if (fSubScroll)
  1055. {
  1056. ListView_GetViewRect2(plv, &rcView, 0, 0);
  1057. if ((rcClient.left < rcClient.right) && (rcClient.top < rcClient.bottom))
  1058. {
  1059. do
  1060. {
  1061. if (!(style & WS_HSCROLL) &&
  1062. (rcView.left < rcClient.left || rcView.right > rcClient.right))
  1063. {
  1064. style |= WS_HSCROLL;
  1065. rcClient.bottom -= ListView_GetCyScrollbar(plv); // BUGBUG what if not SB yet?
  1066. }
  1067. if (!(style & WS_VSCROLL) &&
  1068. (rcView.top < rcClient.top || rcView.bottom > rcClient.bottom))
  1069. {
  1070. style |= WS_VSCROLL;
  1071. rcClient.right -= ListView_GetCxScrollbar(plv);
  1072. }
  1073. }
  1074. while (!(style & WS_HSCROLL) && rcView.right > rcClient.right);
  1075. }
  1076. if (prcViewRect)
  1077. *prcViewRect = rcView;
  1078. }
  1079. *prcClient = rcClient;
  1080. return style;
  1081. }
  1082. int CALLBACK ArrangeIconCompare(LISTITEM FAR* pitem1, LISTITEM FAR* pitem2, LPARAM lParam)
  1083. {
  1084. int v1, v2;
  1085. // REVIEW: lParam can be 0 and we fault ... bug in caller, but we might want to be robust here.
  1086. if (HIWORD(lParam))
  1087. {
  1088. // Vertical arrange
  1089. v1 = pitem1->pt.x / GET_X_LPARAM(lParam);
  1090. v2 = pitem2->pt.x / GET_X_LPARAM(lParam);
  1091. if (v1 > v2)
  1092. return 1;
  1093. else if (v1 < v2)
  1094. return -1;
  1095. else
  1096. {
  1097. int y1 = pitem1->pt.y;
  1098. int y2 = pitem2->pt.y;
  1099. if (y1 > y2)
  1100. return 1;
  1101. else if (y1 < y2)
  1102. return -1;
  1103. }
  1104. }
  1105. else
  1106. {
  1107. v1 = pitem1->pt.y / (int)lParam;
  1108. v2 = pitem2->pt.y / (int)lParam;
  1109. if (v1 > v2)
  1110. return 1;
  1111. else if (v1 < v2)
  1112. return -1;
  1113. else
  1114. {
  1115. int x1 = pitem1->pt.x;
  1116. int x2 = pitem2->pt.x;
  1117. if (x1 > x2)
  1118. return 1;
  1119. else if (x1 < x2)
  1120. return -1;
  1121. }
  1122. }
  1123. return 0;
  1124. }
  1125. void NEAR PASCAL _ListView_GetRectsFromItem(LV* plv, BOOL bSmallIconView,
  1126. LISTITEM FAR *pitem,
  1127. LPRECT prcIcon, LPRECT prcLabel, LPRECT prcBounds, LPRECT prcSelectBounds)
  1128. {
  1129. RECT rcIcon;
  1130. RECT rcLabel;
  1131. if (!prcIcon)
  1132. prcIcon = &rcIcon;
  1133. if (!prcLabel)
  1134. prcLabel = &rcLabel;
  1135. // Test for NULL item passed in
  1136. if (pitem)
  1137. {
  1138. // This routine is called during ListView_Recompute(), while
  1139. // plv->rcView.left may still be == RECOMPUTE. So, we can't
  1140. // test that to see if recomputation is needed.
  1141. //
  1142. if (pitem->pt.y == RECOMPUTE || pitem->cyFoldedLabel == SRECOMPUTE)
  1143. ListView_Recompute(plv);
  1144. if (bSmallIconView)
  1145. ListView_SGetRects(plv, pitem, prcIcon, prcLabel, prcBounds);
  1146. else
  1147. // ListView_IGetRects already refolds as necessary
  1148. ListView_IGetRects(plv, pitem, prcIcon, prcLabel, prcBounds);
  1149. if (prcBounds)
  1150. {
  1151. UnionRect(prcBounds, prcIcon, prcLabel);
  1152. if (plv->himlState && (LV_StateImageValue(pitem)))
  1153. {
  1154. prcBounds->left -= plv->cxState;
  1155. }
  1156. }
  1157. } else {
  1158. SetRectEmpty(prcIcon);
  1159. *prcLabel = *prcIcon;
  1160. if (prcBounds)
  1161. *prcBounds = *prcIcon;
  1162. }
  1163. if (prcSelectBounds)
  1164. {
  1165. UnionRect(prcSelectBounds, prcIcon, prcLabel);
  1166. }
  1167. }
  1168. void NEAR _ListView_InvalidateItemPtr(LV* plv, BOOL bSmallIcon, LISTITEM FAR *pitem, UINT fRedraw)
  1169. {
  1170. RECT rcBounds;
  1171. ASSERT( !ListView_IsOwnerData( plv ));
  1172. _ListView_GetRectsFromItem(plv, bSmallIcon, pitem, NULL, NULL, &rcBounds, NULL);
  1173. RedrawWindow(plv->ci.hwnd, &rcBounds, NULL, fRedraw);
  1174. }
  1175. // return TRUE if things still overlap
  1176. // this only happens if we tried to unstack things, and there was NOSCROLL set and
  1177. // items tried to go off the deep end
  1178. BOOL NEAR PASCAL ListView_IUnstackOverlaps(LV* plv, HDPA hdpaSort, int iDirection)
  1179. {
  1180. BOOL fRet = FALSE;
  1181. int i;
  1182. int iCount;
  1183. BOOL bSmallIconView;
  1184. RECT rcItem, rcItem2, rcTemp;
  1185. int cxItem, cyItem;
  1186. LISTITEM FAR* pitem;
  1187. LISTITEM FAR* pitem2;
  1188. ASSERT( !ListView_IsOwnerData( plv ) );
  1189. if (bSmallIconView = ListView_IsSmallView(plv))
  1190. {
  1191. cxItem = plv->cxItem;
  1192. cyItem = plv->cyItem;
  1193. }
  1194. else
  1195. {
  1196. cxItem = lv_cxIconSpacing;
  1197. cyItem = lv_cyIconSpacing;
  1198. }
  1199. iCount = ListView_Count(plv);
  1200. // finally, unstack any overlaps
  1201. for (i = 0 ; i < iCount ; i++) {
  1202. int j;
  1203. pitem = DPA_GetPtr(hdpaSort, i);
  1204. if (bSmallIconView) {
  1205. _ListView_GetRectsFromItem(plv, bSmallIconView, pitem, NULL, NULL, &rcItem, NULL);
  1206. }
  1207. // move all the items that overlap with us
  1208. for (j = i+1 ; j < iCount; j++) {
  1209. POINT ptOldPos;
  1210. pitem2 = DPA_GetPtr(hdpaSort, j);
  1211. ptOldPos = pitem2->pt;
  1212. if (bSmallIconView) {
  1213. // for small icons, we need to do an intersect rect
  1214. _ListView_GetRectsFromItem(plv, bSmallIconView, pitem2, NULL, NULL, &rcItem2, NULL);
  1215. if (IntersectRect(&rcTemp, &rcItem, &rcItem2)) {
  1216. // yes, it intersects. move it out
  1217. _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
  1218. do {
  1219. pitem2->pt.x += (cxItem * iDirection);
  1220. } while (PtInRect(&rcItem, pitem2->pt));
  1221. } else {
  1222. // no more intersect!
  1223. break;
  1224. }
  1225. } else {
  1226. // for large icons, just find the ones that share the x,y;
  1227. if (pitem2->pt.x == pitem->pt.x && pitem2->pt.y == pitem->pt.y) {
  1228. _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
  1229. pitem2->pt.x += (cxItem * iDirection);
  1230. } else {
  1231. // no more intersect!
  1232. break;
  1233. }
  1234. }
  1235. if (plv->ci.style & LVS_NOSCROLL) {
  1236. if (pitem2->pt.x < 0 || pitem2->pt.y < 0 ||
  1237. pitem2->pt.x > (plv->sizeClient.cx - (cxItem/2))||
  1238. pitem2->pt.y > (plv->sizeClient.cy - (cyItem/2))) {
  1239. pitem2->pt = ptOldPos;
  1240. fRet = TRUE;
  1241. }
  1242. }
  1243. // invalidate the new position as well
  1244. _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
  1245. }
  1246. }
  1247. return fRet;
  1248. }
  1249. BOOL NEAR PASCAL ListView_SnapToGrid(LV* plv, HDPA hdpaSort)
  1250. {
  1251. // this algorithm can't fit in the structure of the other
  1252. // arrange loop without becoming n^2 or worse.
  1253. // this algorithm is order n.
  1254. // iterate through and snap to the nearest grid.
  1255. // iterate through and push aside overlaps.
  1256. int i;
  1257. int iCount;
  1258. LPARAM xySpacing;
  1259. int x,y;
  1260. LISTITEM FAR* pitem;
  1261. BOOL bSmallIconView;
  1262. int cxItem, cyItem;
  1263. ASSERT( !ListView_IsOwnerData( plv ) );
  1264. if (bSmallIconView = ListView_IsSmallView(plv))
  1265. {
  1266. cxItem = plv->cxItem;
  1267. cyItem = plv->cyItem;
  1268. }
  1269. else
  1270. {
  1271. cxItem = lv_cxIconSpacing;
  1272. cyItem = lv_cyIconSpacing;
  1273. }
  1274. iCount = ListView_Count(plv);
  1275. // first snap to nearest grid
  1276. for (i = 0; i < iCount; i++) {
  1277. pitem = DPA_GetPtr(hdpaSort, i);
  1278. x = pitem->pt.x;
  1279. y = pitem->pt.y;
  1280. if (!bSmallIconView) {
  1281. x -= ((lv_cxIconSpacing - plv->cxIcon) / 2);
  1282. y -= g_cyIconOffset;
  1283. }
  1284. NearestSlot(&x,&y, cxItem, cyItem, (plv->nWorkAreas > 0) ? &(plv->prcWorkAreas[pitem->iWorkArea]) : NULL);
  1285. if (!bSmallIconView) {
  1286. x += ((lv_cxIconSpacing - plv->cxIcon) / 2);
  1287. y += g_cyIconOffset;
  1288. }
  1289. if (x != pitem->pt.x || y != pitem->pt.y) {
  1290. _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem, RDW_INVALIDATE| RDW_ERASE);
  1291. if ((plv->ci.style & LVS_NOSCROLL) && (plv->nWorkAreas == 0)) {
  1292. // if it's marked noscroll, make sure it's still on the client region
  1293. while (x >= (plv->sizeClient.cx - (cxItem/2)))
  1294. x -= cxItem;
  1295. while (x < 0)
  1296. x += cxItem;
  1297. while (y >= (plv->sizeClient.cy - (cyItem/2)))
  1298. y -= cyItem;
  1299. while (y < 0)
  1300. y += cyItem;
  1301. }
  1302. pitem->pt.x = x;
  1303. pitem->pt.y = y;
  1304. plv->iFreeSlot = -1; // The "free slot" cache is no good once an item moves
  1305. _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem, RDW_INVALIDATE| RDW_ERASE);
  1306. }
  1307. }
  1308. // now resort the dpa
  1309. switch (plv->ci.style & LVS_ALIGNMASK)
  1310. {
  1311. case LVS_ALIGNLEFT:
  1312. case LVS_ALIGNRIGHT:
  1313. xySpacing = MAKELONG(bSmallIconView ? plv->cxItem : lv_cxIconSpacing, TRUE);
  1314. break;
  1315. default:
  1316. xySpacing = MAKELONG(bSmallIconView ? plv->cyItem : lv_cyIconSpacing, FALSE);
  1317. }
  1318. if (!DPA_Sort(hdpaSort, ArrangeIconCompare, xySpacing))
  1319. return FALSE;
  1320. // go in one direction, if there are still overlaps, go in the other
  1321. // direction as well
  1322. if (ListView_IUnstackOverlaps(plv, hdpaSort, 1))
  1323. ListView_IUnstackOverlaps(plv, hdpaSort, -1);
  1324. return FALSE;
  1325. }
  1326. BOOL NEAR ListView_OnArrange(LV* plv, UINT style)
  1327. {
  1328. BOOL bSmallIconView;
  1329. LPARAM xySpacing;
  1330. HDPA hdpaSort = NULL;
  1331. bSmallIconView = ListView_IsSmallView(plv);
  1332. if (!bSmallIconView && !ListView_IsIconView(plv)) {
  1333. return FALSE;
  1334. }
  1335. if (ListView_IsOwnerData( plv ))
  1336. {
  1337. if ( style & (LVA_SNAPTOGRID | LVA_SORTASCENDING | LVA_SORTDESCENDING) )
  1338. {
  1339. RIPMSG(0, "LVM_ARRANGE: Cannot combine LVA_SNAPTOGRID or LVA_SORTxxx with owner-data");
  1340. return( FALSE );
  1341. }
  1342. }
  1343. // Make sure our items have positions and their text rectangles
  1344. // caluculated
  1345. if (plv->rcView.left == RECOMPUTE)
  1346. ListView_Recompute(plv);
  1347. if (!ListView_IsOwnerData( plv ))
  1348. {
  1349. // we clone plv->hdpa so we don't blow away indices that
  1350. // apps have saved away.
  1351. // we sort here to make the nested for loop below more bearable.
  1352. hdpaSort = DPA_Clone(plv->hdpa, NULL);
  1353. if (!hdpaSort)
  1354. return FALSE;
  1355. }
  1356. switch (plv->ci.style & LVS_ALIGNMASK)
  1357. {
  1358. case LVS_ALIGNLEFT:
  1359. case LVS_ALIGNRIGHT:
  1360. xySpacing = MAKELONG(bSmallIconView ? plv->cxItem : lv_cxIconSpacing, TRUE);
  1361. break;
  1362. default:
  1363. xySpacing = MAKELONG(bSmallIconView ? plv->cyItem : lv_cyIconSpacing, FALSE);
  1364. }
  1365. if (ListView_IsOwnerData( plv ))
  1366. {
  1367. ListView_CommonArrange(plv, style, NULL);
  1368. }
  1369. else
  1370. {
  1371. if (!DPA_Sort(hdpaSort, ArrangeIconCompare, xySpacing))
  1372. return FALSE;
  1373. ListView_CommonArrange(plv, style, hdpaSort);
  1374. DPA_Destroy(hdpaSort);
  1375. }
  1376. MyNotifyWinEvent(EVENT_OBJECT_REORDER, plv->ci.hwnd, OBJID_CLIENT, 0);
  1377. return TRUE;
  1378. }
  1379. // Arrange the icons given a sorted hdpa, and arrange them in the sub workareas
  1380. BOOL NEAR ListView_CommonArrangeEx(LV* plv, UINT style, HDPA hdpaSort, int iWorkArea)
  1381. {
  1382. int iSlot;
  1383. int iItem;
  1384. int cSlots;
  1385. int cWorkAreaSlots[LV_MAX_WORKAREAS];
  1386. BOOL fItemMoved;
  1387. RECT rcLastItem;
  1388. RECT rcSlot;
  1389. RECT rcT;
  1390. BOOL bSmallIconView;
  1391. BOOL bIconView;
  1392. int xMin = 0;
  1393. bSmallIconView = ListView_IsSmallView(plv);
  1394. bIconView = ListView_IsIconView(plv);
  1395. //
  1396. // when this is an autoarrange, then we dont need to worry about
  1397. // scrolling the origin, because we are going to arrange everything
  1398. // around the positive side of the origin
  1399. //
  1400. if (LVA_DEFAULT == style && (plv->ci.style & LVS_AUTOARRANGE))
  1401. {
  1402. if (plv->ptOrigin.x < 0)
  1403. plv->ptOrigin.x = 0;
  1404. if (plv->ptOrigin.y < 0)
  1405. plv->ptOrigin.y = 0;
  1406. }
  1407. // REVIEW, this causes a repaint if we are scrollled
  1408. // we can probably avoid this some how
  1409. fItemMoved = (plv->ptOrigin.x != 0) || (plv->ptOrigin.y != 0);
  1410. if (!ListView_IsOwnerData( plv ))
  1411. {
  1412. if (style == LVA_SNAPTOGRID) {
  1413. // (dli) This function is fitting all the icons into just one rectangle,
  1414. // namely sizeClient. We need to make it multi-workarea aware if we want
  1415. // multi-workarea for the general case (i.e. other than just the desktop)
  1416. // This is never called in the desktop case.
  1417. fItemMoved |= ListView_SnapToGrid(plv, hdpaSort);
  1418. } else {
  1419. if (plv->nWorkAreas > 0)
  1420. {
  1421. int i;
  1422. for (i = 0; i < plv->nWorkAreas; i++)
  1423. cWorkAreaSlots[i] = ListView_GetSlotCountEx(plv, TRUE, i);
  1424. }
  1425. else
  1426. cSlots = ListView_GetSlotCount(plv, TRUE);
  1427. SetRectEmpty(&rcLastItem);
  1428. // manipulate only the sorted version of the item list below!
  1429. iSlot = 0;
  1430. for (iItem = 0; iItem < ListView_Count(plv); iItem++)
  1431. {
  1432. int cRealSlots;
  1433. RECT rcIcon, rcLabel;
  1434. LISTITEM FAR* pitem = DPA_GetPtr(hdpaSort, iItem);
  1435. // (dli) In the multi-workarea case, if this item is not in our
  1436. // workarea, skip it.
  1437. if (pitem->iWorkArea != iWorkArea)
  1438. continue;
  1439. cRealSlots = (plv->nWorkAreas > 0) ? cWorkAreaSlots[pitem->iWorkArea] : cSlots;
  1440. if (bSmallIconView || bIconView)
  1441. {
  1442. for ( ; ; )
  1443. {
  1444. _CalcSlotRect(plv, pitem, iSlot, cRealSlots, FALSE, &rcSlot);
  1445. if (!IntersectRect(&rcT, &rcSlot, &rcLastItem))
  1446. break;
  1447. iSlot++;
  1448. }
  1449. }
  1450. fItemMoved |= ListView_SetIconPos(plv, pitem, iSlot++, cRealSlots);
  1451. // do this instead of ListView_GetRects() because we need
  1452. // to use the pitem from the sorted hdpa, not the ones in *plv
  1453. _ListView_GetRectsFromItem(plv, bSmallIconView, pitem, &rcIcon, &rcLabel, &rcLastItem, NULL);
  1454. // f-n above will return unfolded rects if there are any, we must make sure
  1455. // we use folded ones for slot allocations
  1456. if (bIconView)
  1457. {
  1458. if (ListView_IsItemUnfoldedPtr(plv, pitem))
  1459. {
  1460. ListView_RefoldLabelRect(plv, &rcLabel, pitem);
  1461. UnionRect(&rcLastItem, &rcIcon, &rcLabel);
  1462. if (plv->himlState && (LV_StateImageValue(pitem)))
  1463. rcLastItem.left -= plv->cxState;
  1464. }
  1465. }
  1466. //
  1467. // Keep track of the minimum x as we don't want negative values
  1468. // when we finish.
  1469. if (rcLastItem.left < xMin)
  1470. xMin = rcLastItem.left;
  1471. }
  1472. //
  1473. // See if we need to scroll the items over to make sure that all of the
  1474. // no items are hanging off the left hand side.
  1475. //
  1476. if (xMin < 0)
  1477. {
  1478. for (iItem = 0; iItem < ListView_Count(plv); iItem++)
  1479. {
  1480. LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, iItem);
  1481. pitem->pt.x -= xMin; // scroll them over
  1482. }
  1483. plv->rcView.left = RECOMPUTE; // need to recompute.
  1484. fItemMoved = TRUE;
  1485. }
  1486. }
  1487. }
  1488. //
  1489. // We might as well invalidate the entire window to make sure...
  1490. if (fItemMoved) {
  1491. if (ListView_RedrawEnabled(plv))
  1492. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  1493. else {
  1494. ListView_DeleteHrgnInval(plv);
  1495. plv->hrgnInval = (HRGN)ENTIRE_REGION;
  1496. plv->flags |= LVF_ERASE;
  1497. }
  1498. // ensure important items are visible
  1499. iItem = (plv->iFocus >= 0) ? plv->iFocus : ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  1500. if (iItem >= 0)
  1501. ListView_OnEnsureVisible(plv, iItem, FALSE);
  1502. if (ListView_RedrawEnabled(plv))
  1503. ListView_UpdateScrollBars(plv);
  1504. }
  1505. return TRUE;
  1506. }
  1507. // this arranges the icon given a sorted hdpa.
  1508. // Arrange the workareas one by one in the multi-workarea case.
  1509. BOOL NEAR ListView_CommonArrange(LV* plv, UINT style, HDPA hdpaSort)
  1510. {
  1511. if (plv->nWorkAreas < 1)
  1512. {
  1513. if (plv->exStyle & LVS_EX_MULTIWORKAREAS)
  1514. return TRUE;
  1515. else
  1516. return ListView_CommonArrangeEx(plv, style, hdpaSort, 0);
  1517. }
  1518. else
  1519. {
  1520. int i;
  1521. for (i = 0; i < plv->nWorkAreas; i++)
  1522. ListView_CommonArrangeEx(plv, style, hdpaSort, i);
  1523. return TRUE;
  1524. }
  1525. }
  1526. void NEAR ListView_IUpdateScrollBars(LV* plv)
  1527. {
  1528. RECT rcClient;
  1529. RECT rcView;
  1530. DWORD style;
  1531. DWORD styleOld;
  1532. SCROLLINFO si;
  1533. int ixDelta = 0, iyDelta = 0;
  1534. int iNewPos;
  1535. BOOL fReupdate = FALSE;
  1536. styleOld = ListView_GetWindowStyle(plv);
  1537. style = ListView_GetClientRect(plv, &rcClient, TRUE, &rcView);
  1538. // Grow scrolling rect to origin if necessary.
  1539. if (rcView.left > 0)
  1540. {
  1541. rcView.left = 0;
  1542. }
  1543. if (rcView.top > 0)
  1544. {
  1545. rcView.top = 0;
  1546. }
  1547. //TraceMsg(TF_LISTVIEW, "ListView_GetClientRect %x %x %x %x", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
  1548. //TraceMsg(TF_LISTVIEW, "ListView_GetViewRect2 %x %x %x %x", rcView.left, rcView.top, rcView.right, rcView.bottom);
  1549. //TraceMsg(TF_LISTVIEW, "rcView %x %x %x %x", plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
  1550. //TraceMsg(TF_LISTVIEW, "Origin %x %x", plv->ptOrigin.x, plv->ptOrigin.y);
  1551. si.cbSize = sizeof(SCROLLINFO);
  1552. if (style & WS_HSCROLL)
  1553. {
  1554. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  1555. si.nMin = 0;
  1556. si.nMax = rcView.right - rcView.left - 1;
  1557. //TraceMsg(TF_LISTVIEW, "si.nMax rcView.right - rcView.left - 1 %x", si.nMax);
  1558. si.nPage = rcClient.right - rcClient.left;
  1559. //TraceMsg(TF_LISTVIEW, "si.nPage %x", si.nPage);
  1560. si.nPos = rcClient.left - rcView.left;
  1561. if (si.nPos < 0)
  1562. {
  1563. // with the new rcView calculations, I don't think
  1564. // rcView.left is ever larger than rcClient.left. msq
  1565. ASSERT(0);
  1566. si.nPos = 0;
  1567. }
  1568. //TraceMsg(TF_LISTVIEW, "si.nPos %x", si.nPos);
  1569. ListView_SetScrollInfo(plv, SB_HORZ, &si, TRUE);
  1570. // make sure our position and page doesn't hang over max
  1571. if ((si.nPos + (LONG)si.nPage - 1 > si.nMax) && si.nPos > 0) {
  1572. iNewPos = (int)si.nMax - (int)si.nPage + 1;
  1573. if (iNewPos < 0) iNewPos = 0;
  1574. if (iNewPos != si.nPos) {
  1575. ixDelta = iNewPos - (int)si.nPos;
  1576. fReupdate = TRUE;
  1577. }
  1578. }
  1579. }
  1580. else if (styleOld & WS_HSCROLL)
  1581. {
  1582. ListView_SetScrollRange(plv, SB_HORZ, 0, 0, TRUE);
  1583. }
  1584. if (style & WS_VSCROLL)
  1585. {
  1586. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  1587. si.nMin = 0;
  1588. si.nMax = rcView.bottom - rcView.top - 1;
  1589. si.nPage = rcClient.bottom - rcClient.top;
  1590. si.nPos = rcClient.top - rcView.top;
  1591. if (si.nPos < 0)
  1592. {
  1593. // with the new rcView calculations, I don't think
  1594. // rcView.top is ever larger than rcClient.top. msq
  1595. ASSERT(0);
  1596. si.nPos = 0;
  1597. }
  1598. ListView_SetScrollInfo(plv, SB_VERT, &si, TRUE);
  1599. // make sure our position and page doesn't hang over max
  1600. if ((si.nPos + (LONG)si.nPage - 1 > si.nMax) && si.nPos > 0) {
  1601. iNewPos = (int)si.nMax - (int)si.nPage + 1;
  1602. if (iNewPos < 0) iNewPos = 0;
  1603. if (iNewPos != si.nPos) {
  1604. iyDelta = iNewPos - (int)si.nPos;
  1605. fReupdate = TRUE;
  1606. }
  1607. }
  1608. }
  1609. else if (styleOld & WS_VSCROLL)
  1610. {
  1611. ListView_SetScrollRange(plv, SB_VERT, 0, 0, TRUE);
  1612. }
  1613. if (fReupdate)
  1614. {
  1615. // we shouldn't recurse because the second time through, si.nPos >0
  1616. ListView_IScroll2(plv, ixDelta, iyDelta, 0);
  1617. ListView_IUpdateScrollBars(plv);
  1618. TraceMsg(TF_WARNING, "LISTVIEW: ERROR: We had to recurse!");
  1619. }
  1620. }
  1621. void FAR PASCAL ListView_ComOnScroll(LV* plv, UINT code, int posNew, int sb,
  1622. int cLine, int cPage)
  1623. {
  1624. int pos;
  1625. SCROLLINFO si;
  1626. BOOL fVert = (sb == SB_VERT);
  1627. UINT uSmooth = 0;
  1628. si.cbSize = sizeof(SCROLLINFO);
  1629. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  1630. if (!ListView_GetScrollInfo(plv, sb, &si)) {
  1631. return;
  1632. }
  1633. if (cPage != -1)
  1634. si.nPage = cPage;
  1635. si.nMax -= (si.nPage - 1);
  1636. if (si.nMax < si.nMin)
  1637. si.nMax = si.nMin;
  1638. pos = (int)si.nPos; // current position
  1639. switch (code)
  1640. {
  1641. case SB_LEFT:
  1642. si.nPos = si.nMin;
  1643. break;
  1644. case SB_RIGHT:
  1645. si.nPos = si.nMax;
  1646. break;
  1647. case SB_PAGELEFT:
  1648. si.nPos -= si.nPage;
  1649. break;
  1650. case SB_LINELEFT:
  1651. si.nPos -= cLine;
  1652. break;
  1653. case SB_PAGERIGHT:
  1654. si.nPos += si.nPage;
  1655. break;
  1656. case SB_LINERIGHT:
  1657. si.nPos += cLine;
  1658. break;
  1659. case SB_THUMBTRACK:
  1660. si.nPos = posNew;
  1661. uSmooth = SSW_EX_IMMEDIATE;
  1662. break;
  1663. case SB_ENDSCROLL:
  1664. // When scroll bar tracking is over, ensure scroll bars
  1665. // are properly updated...
  1666. //
  1667. ListView_UpdateScrollBars(plv);
  1668. return;
  1669. default:
  1670. return;
  1671. }
  1672. if (plv->iScrollCount >= SMOOTHSCROLLLIMIT)
  1673. uSmooth = SSW_EX_IMMEDIATE;
  1674. si.fMask = SIF_POS;
  1675. si.nPos = ListView_SetScrollInfo(plv, sb, &si, TRUE);
  1676. if (pos != si.nPos)
  1677. {
  1678. int delta = (int)si.nPos - pos;
  1679. int dx = 0, dy = 0;
  1680. if (fVert)
  1681. dy = delta;
  1682. else
  1683. dx = delta;
  1684. _ListView_Scroll2(plv, dx, dy, uSmooth);
  1685. UpdateWindow(plv->ci.hwnd);
  1686. }
  1687. }
  1688. //
  1689. // We need a smoothscroll callback so our background image draws
  1690. // at the correct origin. If we don't have a background image,
  1691. // then this work is superfluous but not harmful either.
  1692. //
  1693. int CALLBACK ListView_IScroll2_SmoothScroll(
  1694. HWND hwnd,
  1695. int dx,
  1696. int dy,
  1697. CONST RECT *prcScroll,
  1698. CONST RECT *prcClip ,
  1699. HRGN hrgnUpdate,
  1700. LPRECT prcUpdate,
  1701. UINT flags)
  1702. {
  1703. LV* plv = ListView_GetPtr(hwnd);
  1704. if (plv)
  1705. {
  1706. plv->ptOrigin.x -= dx;
  1707. plv->ptOrigin.y -= dy;
  1708. }
  1709. // Now do what SmoothScrollWindow would've done if we weren't
  1710. // a callback
  1711. return ScrollWindowEx(hwnd, dx, dy, prcScroll, prcClip, hrgnUpdate, prcUpdate, flags);
  1712. }
  1713. void FAR PASCAL ListView_IScroll2(LV* plv, int dx, int dy, UINT uSmooth)
  1714. {
  1715. if (dx | dy)
  1716. {
  1717. if ((plv->clrBk == CLR_NONE) && (plv->pImgCtx == NULL))
  1718. {
  1719. plv->ptOrigin.x += dx;
  1720. plv->ptOrigin.y += dy;
  1721. LVSeeThruScroll(plv, NULL);
  1722. }
  1723. else
  1724. {
  1725. SMOOTHSCROLLINFO si;
  1726. si.cbSize = sizeof(si);
  1727. si.fMask = SSIF_SCROLLPROC;
  1728. si.hwnd = plv->ci.hwnd;
  1729. si.dx = -dx;
  1730. si.dy = -dy;
  1731. si.lprcSrc = NULL;
  1732. si.lprcClip = NULL;
  1733. si.hrgnUpdate = NULL;
  1734. si.lprcUpdate = NULL;
  1735. si.fuScroll = uSmooth | SW_INVALIDATE | SW_ERASE;
  1736. si.pfnScrollProc = ListView_IScroll2_SmoothScroll;
  1737. SmoothScrollWindow(&si);
  1738. }
  1739. }
  1740. }
  1741. void NEAR ListView_IOnScroll(LV* plv, UINT code, int posNew, UINT sb)
  1742. {
  1743. int cLine;
  1744. if (sb == SB_VERT)
  1745. {
  1746. cLine = lv_cyIconSpacing / 2;
  1747. }
  1748. else
  1749. {
  1750. cLine = lv_cxIconSpacing / 2;
  1751. }
  1752. ListView_ComOnScroll(plv, code, posNew, sb,
  1753. cLine, -1);
  1754. }
  1755. int NEAR ListView_IGetScrollUnitsPerLine(LV* plv, UINT sb)
  1756. {
  1757. int cLine;
  1758. if (sb == SB_VERT)
  1759. {
  1760. cLine = lv_cyIconSpacing / 2;
  1761. }
  1762. else
  1763. {
  1764. cLine = lv_cxIconSpacing / 2;
  1765. }
  1766. return cLine;
  1767. }
  1768. // NOTE: there is very similar code in the treeview
  1769. //
  1770. // Totally disgusting hack in order to catch VK_RETURN
  1771. // before edit control gets it.
  1772. //
  1773. LRESULT CALLBACK ListView_EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1774. {
  1775. LV* plv = ListView_GetPtr(GetParent(hwnd));
  1776. LRESULT lret;
  1777. ASSERT(plv);
  1778. #if defined(FE_IME)
  1779. if ( (g_fDBCSInputEnabled) && LOWORD(GetKeyboardLayout(0L)) == 0x0411 )
  1780. {
  1781. // The following code adds IME awareness to the
  1782. // listview's label editing. Currently just for Japanese.
  1783. //
  1784. DWORD dwGcs;
  1785. if (msg==WM_SIZE)
  1786. {
  1787. // If it's given the size, tell it to an IME.
  1788. ListView_SizeIME(hwnd);
  1789. }
  1790. else if (msg == EM_SETLIMITTEXT )
  1791. {
  1792. if (wParam < 13)
  1793. plv->flags |= LVF_DONTDRAWCOMP;
  1794. else
  1795. plv->flags &= ~LVF_DONTDRAWCOMP;
  1796. }
  1797. // Give up to draw IME composition by ourselves in case
  1798. // we're working on SFN. Win95d-5709
  1799. else if (!(plv->flags & LVF_DONTDRAWCOMP ))
  1800. {
  1801. switch (msg)
  1802. {
  1803. case WM_IME_STARTCOMPOSITION:
  1804. case WM_IME_ENDCOMPOSITION:
  1805. return 0L;
  1806. case WM_IME_COMPOSITION:
  1807. // If lParam has no data available bit, it implies
  1808. // canceling composition.
  1809. // ListView_InsertComposition() tries to get composition
  1810. // string w/ GCS_COMPSTR then remove it from edit control if
  1811. // nothing is available.
  1812. //
  1813. if ( !lParam )
  1814. dwGcs = GCS_COMPSTR;
  1815. else
  1816. dwGcs = (DWORD) lParam;
  1817. ListView_InsertComposition(hwnd, wParam, dwGcs, plv);
  1818. return 0L;
  1819. case WM_PAINT:
  1820. lret=CallWindowProc(plv->pfnEditWndProc, hwnd, msg, wParam, lParam);
  1821. ListView_PaintComposition(hwnd,plv);
  1822. return lret;
  1823. case WM_IME_SETCONTEXT:
  1824. // We draw composition string.
  1825. //
  1826. lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  1827. break;
  1828. default:
  1829. // the other messages should simply be processed
  1830. // in this subclass procedure.
  1831. break;
  1832. }
  1833. }
  1834. }
  1835. #endif FE_IME
  1836. switch (msg)
  1837. {
  1838. case WM_SETTEXT:
  1839. SetWindowID(hwnd, 1);
  1840. break;
  1841. case WM_KEYDOWN:
  1842. switch (wParam)
  1843. {
  1844. case VK_RETURN:
  1845. ListView_DismissEdit(plv, FALSE);
  1846. return 0L;
  1847. case VK_ESCAPE:
  1848. ListView_DismissEdit(plv, TRUE);
  1849. return 0L;
  1850. }
  1851. break;
  1852. case WM_CHAR:
  1853. switch (wParam)
  1854. {
  1855. case VK_RETURN:
  1856. // Eat the character, so edit control wont beep!
  1857. return 0L;
  1858. }
  1859. break;
  1860. case WM_GETDLGCODE:
  1861. return DLGC_WANTALLKEYS | DLGC_HASSETSEL; /* editing name, no dialog handling right now */
  1862. }
  1863. return CallWindowProc(plv->pfnEditWndProc, hwnd, msg, wParam, lParam);
  1864. }
  1865. // Helper routine for SetEditSize
  1866. void ListView_ChangeEditRectForRegion(LV* plv, LPRECT lprc)
  1867. {
  1868. LISTITEM FAR* pitem = ListView_GetItemPtr(plv, plv->iEdit);
  1869. ASSERT(!ListView_IsOwnerData(plv));
  1870. ASSERT(ListView_IsIconView(plv));
  1871. if (!EqualRect((CONST RECT *)&pitem->rcTextRgn, (CONST RECT *)lprc)) {
  1872. // RecalcRegion knows to use rcTextRgn in the case where iEdit != -1,
  1873. // so set it up before calling through.
  1874. CopyRect(&pitem->rcTextRgn, (CONST RECT *)lprc);
  1875. ListView_RecalcRegion(plv, TRUE, TRUE);
  1876. // Invalidate the entire Edit and force a repaint from the listview
  1877. // on down to make sure we don't leave turds...
  1878. InvalidateRect(plv->hwndEdit, NULL, TRUE);
  1879. UpdateWindow(plv->ci.hwnd);
  1880. }
  1881. }
  1882. // BUGBUG: very similar routine in treeview
  1883. void NEAR ListView_SetEditSize(LV* plv)
  1884. {
  1885. RECT rcLabel;
  1886. UINT seips;
  1887. if (!((plv->iEdit >= 0) && (plv->iEdit < ListView_Count(plv))))
  1888. {
  1889. ListView_DismissEdit(plv, TRUE); // cancel edits
  1890. return;
  1891. }
  1892. ListView_GetRects(plv, plv->iEdit, NULL, &rcLabel, NULL, NULL);
  1893. // OffsetRect(&rc, rcLabel.left + g_cxLabelMargin + g_cxBorder,
  1894. // (rcLabel.bottom + rcLabel.top - rc.bottom) / 2 + g_cyBorder);
  1895. // OffsetRect(&rc, rcLabel.left + g_cxLabelMargin , rcLabel.top);
  1896. // get the text bounding rect
  1897. if (ListView_IsIconView(plv))
  1898. {
  1899. // We should not adjust y-positoin in case of the icon view.
  1900. InflateRect(&rcLabel, -g_cxLabelMargin, -g_cyBorder);
  1901. }
  1902. else
  1903. {
  1904. // Special case for single-line & centered
  1905. InflateRect(&rcLabel, -g_cxLabelMargin - g_cxBorder, (-(rcLabel.bottom - rcLabel.top - plv->cyLabelChar) / 2) - g_cyBorder);
  1906. }
  1907. seips = 0;
  1908. if (ListView_IsIconView(plv) && !(plv->ci.style & LVS_NOLABELWRAP))
  1909. seips |= SEIPS_WRAP;
  1910. #ifdef DEBUG
  1911. if (plv->ci.style & LVS_NOSCROLL)
  1912. seips |= SEIPS_NOSCROLL;
  1913. #endif
  1914. SetEditInPlaceSize(plv->hwndEdit, &rcLabel, plv->hfontLabel, seips);
  1915. if (plv->exStyle & LVS_EX_REGIONAL)
  1916. ListView_ChangeEditRectForRegion(plv, &rcLabel);
  1917. }
  1918. // to avoid eating too much stack
  1919. void NEAR ListView_DoOnEditLabel(LV *plv, int i, LPTSTR pszInitial)
  1920. {
  1921. TCHAR szLabel[CCHLABELMAX];
  1922. LV_ITEM item;
  1923. item.mask = LVIF_TEXT;
  1924. item.iItem = i;
  1925. item.iSubItem = 0;
  1926. item.pszText = szLabel;
  1927. item.cchTextMax = ARRAYSIZE(szLabel);
  1928. ListView_OnGetItem(plv, &item);
  1929. if (!item.pszText)
  1930. return;
  1931. // Make sure the edited item has the focus.
  1932. if (plv->iFocus != i)
  1933. ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
  1934. // Make sure the item is fully visible
  1935. ListView_OnEnsureVisible(plv, i, FALSE); // fPartialOK == FALSE
  1936. // Must subtract one from ARRAYSIZE(szLabel) because Edit_LimitText doesn't include
  1937. // the terminating NULL
  1938. plv->hwndEdit = CreateEditInPlaceWindow(plv->ci.hwnd,
  1939. pszInitial? pszInitial : item.pszText, ARRAYSIZE(szLabel) - 1,
  1940. ListView_IsIconView(plv) ?
  1941. (WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD | ES_CENTER | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL) :
  1942. (WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD | ES_LEFT | ES_AUTOHSCROLL), plv->hfontLabel);
  1943. if (plv->hwndEdit)
  1944. {
  1945. LISTITEM FAR* pitem;
  1946. LV_DISPINFO nm;
  1947. // We create the edit window but have not shown it. Ask the owner
  1948. // if they are interested or not.
  1949. // If we passed in initial text set the ID to be dirty...
  1950. if (pszInitial)
  1951. SetWindowID(plv->hwndEdit, 1);
  1952. nm.item.mask = LVIF_PARAM;
  1953. nm.item.iItem = i;
  1954. nm.item.iSubItem = 0;
  1955. if (!ListView_IsOwnerData( plv ))
  1956. {
  1957. if (!(pitem = ListView_GetItemPtr(plv, i)))
  1958. {
  1959. DestroyWindow(plv->hwndEdit);
  1960. plv->hwndEdit = NULL;
  1961. return;
  1962. }
  1963. nm.item.lParam = pitem->lParam;
  1964. }
  1965. else
  1966. nm.item.lParam = (LPARAM)0;
  1967. plv->iEdit = i;
  1968. // if they have LVS_EDITLABELS but return non-FALSE here, stop!
  1969. if ((BOOL)CCSendNotify(&plv->ci, LVN_BEGINLABELEDIT, &nm.hdr))
  1970. {
  1971. plv->iEdit = -1;
  1972. DestroyWindow(plv->hwndEdit);
  1973. plv->hwndEdit = NULL;
  1974. }
  1975. }
  1976. }
  1977. void FAR PASCAL RescrollEditWindow(HWND hwndEdit)
  1978. {
  1979. Edit_SetSel(hwndEdit, -1, -1); // move to the end
  1980. Edit_SetSel(hwndEdit, 0, -1); // select all text
  1981. }
  1982. // BUGBUG: very similar code in treeview.c
  1983. HWND NEAR ListView_OnEditLabel(LV* plv, int i, LPTSTR pszInitialText)
  1984. {
  1985. // this eats stack
  1986. ListView_DismissEdit(plv, FALSE);
  1987. if (!(plv->ci.style & LVS_EDITLABELS) || (GetFocus() != plv->ci.hwnd) ||
  1988. (i == -1))
  1989. return(NULL); // Does not support this.
  1990. ListView_DoOnEditLabel(plv, i, pszInitialText);
  1991. if (plv->hwndEdit) {
  1992. plv->pfnEditWndProc = SubclassWindow(plv->hwndEdit, ListView_EditWndProc);
  1993. #if defined(FE_IME)
  1994. if (g_fDBCSInputEnabled) {
  1995. if (SendMessage(plv->hwndEdit, EM_GETLIMITTEXT, (WPARAM)0, (LPARAM)0)<13)
  1996. {
  1997. plv->flags |= LVF_DONTDRAWCOMP;
  1998. }
  1999. }
  2000. #endif
  2001. ListView_SetEditSize(plv);
  2002. // Show the window and set focus to it. Do this after setting the
  2003. // size so we don't get flicker.
  2004. SetFocus(plv->hwndEdit);
  2005. ShowWindow(plv->hwndEdit, SW_SHOW);
  2006. ListView_InvalidateItem(plv, i, TRUE, RDW_INVALIDATE | RDW_ERASE);
  2007. RescrollEditWindow(plv->hwndEdit);
  2008. /* Due to a bizzare twist of fate, a certain mix of resolution / font size / icon
  2009. / spacing results in being able to see the previous label behind the edit control
  2010. / we have just created. Therefore to overcome this problem we ensure that this
  2011. / label is erased.
  2012. /
  2013. / As the label is not painted when we have an edit control we just invalidate the
  2014. / area and the background will be painted. As the window is a child of the list view
  2015. / we should not see any flicker within it. */
  2016. if ( ListView_IsIconView( plv ) )
  2017. {
  2018. RECT rcLabel;
  2019. ListView_GetRects( plv, i, NULL, &rcLabel, NULL, NULL );
  2020. ListView_UnfoldRects( plv, i, NULL, &rcLabel, NULL, NULL );
  2021. InvalidateRect( plv->ci.hwnd, &rcLabel, TRUE );
  2022. UpdateWindow( plv->ci.hwnd );
  2023. }
  2024. }
  2025. return plv->hwndEdit;
  2026. }
  2027. // BUGBUG: very similar code in treeview.c
  2028. BOOL NEAR ListView_DismissEdit(LV* plv, BOOL fCancel)
  2029. {
  2030. LISTITEM FAR* pitem = NULL;
  2031. BOOL fOkToContinue = TRUE;
  2032. HWND hwndEdit = plv->hwndEdit;
  2033. HWND hwnd = plv->ci.hwnd;
  2034. int iEdit;
  2035. LV_DISPINFO nm;
  2036. TCHAR szLabel[CCHLABELMAX];
  2037. #if defined(FE_IME)
  2038. HIMC himc;
  2039. #endif
  2040. if (plv->fNoDismissEdit)
  2041. return FALSE;
  2042. if (!hwndEdit) {
  2043. // Also make sure there are no pending edits...
  2044. ListView_CancelPendingEdit(plv);
  2045. return TRUE; // It is OK to process as normal...
  2046. }
  2047. // If the window is not visible, we are probably in the process
  2048. // of being destroyed, so assume that we are being destroyed
  2049. if (!IsWindowVisible(plv->ci.hwnd))
  2050. fCancel = TRUE;
  2051. //
  2052. // We are using the Window ID of the control as a BOOL to
  2053. // state if it is dirty or not.
  2054. switch (GetWindowID(hwndEdit)) {
  2055. case 0:
  2056. // The edit control is not dirty so act like cancel.
  2057. fCancel = TRUE;
  2058. // Fall through to set window so we will not recurse!
  2059. case 1:
  2060. // The edit control is dirty so continue.
  2061. SetWindowID(hwndEdit, 2); // Don't recurse
  2062. break;
  2063. case 2:
  2064. // We are in the process of processing an update now, bail out
  2065. return TRUE;
  2066. }
  2067. // BUGBUG: this will fail if the program deleted the items out
  2068. // from underneath us (while we are waiting for the edit timer).
  2069. // make delete item invalidate our edit item
  2070. // We uncouple the edit control and hwnd out from under this as
  2071. // to allow code that process the LVN_ENDLABELEDIT to reenter
  2072. // editing mode if an error happens.
  2073. iEdit = plv->iEdit;
  2074. do
  2075. {
  2076. if (ListView_IsOwnerData( plv ))
  2077. {
  2078. if (!((iEdit >= 0) && (iEdit < plv->cTotalItems)))
  2079. {
  2080. break;
  2081. }
  2082. nm.item.lParam = 0;
  2083. }
  2084. else
  2085. {
  2086. pitem = ListView_GetItemPtr(plv, iEdit);
  2087. ASSERT(pitem);
  2088. if (pitem == NULL)
  2089. {
  2090. break;
  2091. }
  2092. nm.item.lParam = pitem->lParam;
  2093. }
  2094. nm.item.iItem = iEdit;
  2095. nm.item.iSubItem = 0;
  2096. nm.item.cchTextMax = 0;
  2097. nm.item.mask = 0;
  2098. if (fCancel)
  2099. nm.item.pszText = NULL;
  2100. else
  2101. {
  2102. Edit_GetText(hwndEdit, szLabel, ARRAYSIZE(szLabel));
  2103. nm.item.pszText = szLabel;
  2104. nm.item.mask |= LVIF_TEXT;
  2105. nm.item.cchTextMax = ARRAYSIZE(szLabel);
  2106. }
  2107. //
  2108. // Notify the parent that we the label editing has completed.
  2109. // We will use the LV_DISPINFO structure to return the new
  2110. // label in. The parent still has the old text available by
  2111. // calling the GetItemText function.
  2112. //
  2113. fOkToContinue = (BOOL)CCSendNotify(&plv->ci, LVN_ENDLABELEDIT, &nm.hdr);
  2114. if (!IsWindow(hwnd)) {
  2115. return FALSE;
  2116. }
  2117. if (fOkToContinue && !fCancel)
  2118. {
  2119. //
  2120. // If the item has the text set as CALLBACK, we will let the
  2121. // ower know that they are supposed to set the item text in
  2122. // their own data structures. Else we will simply update the
  2123. // text in the actual view.
  2124. //
  2125. if (!ListView_IsOwnerData( plv ) &&
  2126. (pitem->pszText != LPSTR_TEXTCALLBACK))
  2127. {
  2128. // Set the item text (everything's set up in nm.item)
  2129. //
  2130. nm.item.mask = LVIF_TEXT;
  2131. ListView_OnSetItem(plv, &nm.item);
  2132. }
  2133. else
  2134. {
  2135. CCSendNotify(&plv->ci, LVN_SETDISPINFO, &nm.hdr);
  2136. // Also we will assume that our cached size is invalid...
  2137. plv->rcView.left = RECOMPUTE;
  2138. if (!ListView_IsOwnerData( plv ))
  2139. {
  2140. ListView_SetSRecompute(pitem);
  2141. }
  2142. }
  2143. }
  2144. #if defined(FE_IME)
  2145. if (g_fDBCSInputEnabled) {
  2146. if (LOWORD(GetKeyboardLayout(0L)) == 0x0411 && (himc = ImmGetContext(hwndEdit)))
  2147. {
  2148. ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0L);
  2149. ImmReleaseContext(hwndEdit, himc);
  2150. }
  2151. }
  2152. #endif
  2153. // redraw
  2154. ListView_InvalidateItem(plv, iEdit, FALSE, RDW_INVALIDATE | RDW_ERASE);
  2155. } while (FALSE);
  2156. // If the hwnedit is still us clear out the variables
  2157. if (hwndEdit == plv->hwndEdit)
  2158. {
  2159. plv->iEdit = -1;
  2160. plv->hwndEdit = NULL; // avoid being reentered
  2161. }
  2162. DestroyWindow(hwndEdit);
  2163. // We've to recalc the region because the edit in place window has
  2164. // added stuff to the region that we don't know how to remove
  2165. // safely.
  2166. ListView_RecalcRegion(plv, TRUE, TRUE);
  2167. return fOkToContinue;
  2168. }
  2169. //
  2170. // This function will scall the icon positions that are stored in the
  2171. // item structures between large and small icon view.
  2172. //
  2173. void NEAR ListView_ScaleIconPositions(LV* plv, BOOL fSmallIconView)
  2174. {
  2175. int cxItem, cyItem;
  2176. HWND hwnd;
  2177. int i;
  2178. if (fSmallIconView)
  2179. {
  2180. if (plv->flags & LVF_ICONPOSSML)
  2181. return; // Already done
  2182. }
  2183. else
  2184. {
  2185. if ((plv->flags & LVF_ICONPOSSML) == 0)
  2186. return; // dito
  2187. }
  2188. // Last but not least update our bit!
  2189. plv->flags ^= LVF_ICONPOSSML;
  2190. cxItem = plv->cxItem;
  2191. cyItem = plv->cyItem;
  2192. hwnd = plv->ci.hwnd;
  2193. // We will now loop through all of the items and update their coordinats
  2194. // We will update th position directly into the view instead of calling
  2195. // SetItemPosition as to not do 5000 invalidates and messages...
  2196. if (!ListView_IsOwnerData( plv ))
  2197. {
  2198. for (i = 0; i < ListView_Count(plv); i++)
  2199. {
  2200. LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, i);
  2201. if (pitem->pt.y != RECOMPUTE) {
  2202. if (fSmallIconView)
  2203. {
  2204. pitem->pt.x = MulDiv(pitem->pt.x - g_cxIconOffset, cxItem, lv_cxIconSpacing);
  2205. pitem->pt.y = MulDiv(pitem->pt.y - g_cyIconOffset, cyItem, lv_cyIconSpacing);
  2206. }
  2207. else
  2208. {
  2209. pitem->pt.x = MulDiv(pitem->pt.x, lv_cxIconSpacing, cxItem) + g_cxIconOffset;
  2210. pitem->pt.y = MulDiv(pitem->pt.y, lv_cyIconSpacing, cyItem) + g_cyIconOffset;
  2211. }
  2212. }
  2213. }
  2214. plv->iFreeSlot = -1; // The "free slot" cache is no good once an item moves
  2215. if (plv->ci.style & LVS_AUTOARRANGE)
  2216. {
  2217. ListView_ISetColumnWidth(plv, 0,
  2218. LV_GetNewColWidth(plv, 0, ListView_Count(plv)-1), FALSE);
  2219. // If autoarrange is turned on, the arrange function will do
  2220. // everything that is needed.
  2221. ListView_OnArrange(plv, LVA_DEFAULT);
  2222. return;
  2223. }
  2224. }
  2225. plv->rcView.left = RECOMPUTE;
  2226. //
  2227. // Also scale the origin
  2228. //
  2229. if (fSmallIconView)
  2230. {
  2231. plv->ptOrigin.x = MulDiv(plv->ptOrigin.x, cxItem, lv_cxIconSpacing);
  2232. plv->ptOrigin.y = MulDiv(plv->ptOrigin.y, cyItem, lv_cyIconSpacing);
  2233. }
  2234. else
  2235. {
  2236. plv->ptOrigin.x = MulDiv(plv->ptOrigin.x, lv_cxIconSpacing, cxItem);
  2237. plv->ptOrigin.y = MulDiv(plv->ptOrigin.y, lv_cyIconSpacing, cyItem);
  2238. }
  2239. // Make sure it fully redraws correctly
  2240. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  2241. }
  2242. HWND FAR PASCAL CreateEditInPlaceWindow(HWND hwnd, LPCTSTR lpText, int cbText, LONG style, HFONT hFont)
  2243. {
  2244. HWND hwndEdit;
  2245. // Create the window with some nonzero size so margins work properly
  2246. // The caller will do a SetEditInPlaceSize to set the real size
  2247. // But make sure the width is huge so when an app calls SetWindowText,
  2248. // USER won't try to scroll the window.
  2249. hwndEdit = CreateWindowEx(GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_RTLREADING,
  2250. TEXT("EDIT"), lpText, style,
  2251. 0, 0, 16384, 20, hwnd, NULL, HINST_THISDLL, NULL);
  2252. if (hwndEdit) {
  2253. Edit_LimitText(hwndEdit, cbText);
  2254. Edit_SetSel(hwndEdit, 0, 0); // move to the beginning
  2255. FORWARD_WM_SETFONT(hwndEdit, hFont, FALSE, SendMessage);
  2256. }
  2257. return hwndEdit;
  2258. }
  2259. // BUGBUG: very similar routine in treeview
  2260. // in:
  2261. // hwndEdit edit control to position in client coords of parent window
  2262. // prc bonding rect of the text, used to position everthing
  2263. // hFont font being used
  2264. // flags
  2265. // SEIPS_WRAP if this is a wrapped type (multiline) edit
  2266. // SEIPS_NOSCROLL if the parent control does not have scrollbars
  2267. //
  2268. // The SEIPS_NOSCROLL flag is used only in DEBUG. Normally, the item
  2269. // being edited should have been scrolled into view, but if the parent
  2270. // doesn't have scrollbars, then clearly that's not possible, so we
  2271. // shouldn't ASSERT in that case.
  2272. //
  2273. // Notes:
  2274. // The top-left corner of the bouding rectangle must be the position
  2275. // the client uses to draw text. We adjust the edit field rectangle
  2276. // appropriately.
  2277. //
  2278. void FAR PASCAL SetEditInPlaceSize(HWND hwndEdit, RECT FAR *prc, HFONT hFont, UINT seips)
  2279. {
  2280. RECT rc, rcClient, rcFormat;
  2281. TCHAR szLabel[CCHLABELMAX + 1];
  2282. int cchLabel, cxIconTextWidth;
  2283. HDC hdc;
  2284. HWND hwndParent = GetParent(hwndEdit);
  2285. UINT flags;
  2286. cchLabel = Edit_GetText(hwndEdit, szLabel, ARRAYSIZE(szLabel));
  2287. if (szLabel[0] == 0)
  2288. {
  2289. StringCchCopy(szLabel, ARRAYSIZE(szLabel), c_szSpace);
  2290. cchLabel = 1;
  2291. }
  2292. hdc = GetDC(hwndParent);
  2293. SelectFont(hdc, hFont);
  2294. cxIconTextWidth = g_cxIconSpacing - g_cxLabelMargin * 2;
  2295. rc.left = rc.top = rc.bottom = 0;
  2296. rc.right = cxIconTextWidth; // for DT_LVWRAP
  2297. // REVIEW: we might want to include DT_EDITCONTROL in our DT_LVWRAP
  2298. if (seips & SEIPS_WRAP)
  2299. {
  2300. flags = DT_LVWRAP | DT_CALCRECT;
  2301. // We only use DT_NOFULLWIDTHCHARBREAK on Korean(949) Memphis and NT5
  2302. if (949 == g_uiACP && (g_bRunOnNT5 || g_bRunOnMemphis))
  2303. flags |= DT_NOFULLWIDTHCHARBREAK;
  2304. }
  2305. else
  2306. flags = DT_LV | DT_CALCRECT;
  2307. // If the string is NULL display a rectangle that is visible.
  2308. DrawText(hdc, szLabel, cchLabel, &rc, flags);
  2309. // Minimum text box size is 1/4 icon spacing size
  2310. if (rc.right < g_cxIconSpacing / 4)
  2311. rc.right = g_cxIconSpacing / 4;
  2312. // position the text rect based on the text rect passed in
  2313. // if wrapping, center the edit control around the text mid point
  2314. OffsetRect(&rc,
  2315. (seips & SEIPS_WRAP) ? prc->left + ((prc->right - prc->left) - (rc.right - rc.left)) / 2 : prc->left,
  2316. (seips & SEIPS_WRAP) ? prc->top : prc->top + ((prc->bottom - prc->top) - (rc.bottom - rc.top)) / 2 );
  2317. // give a little space to ease the editing of this thing
  2318. if (!(seips & SEIPS_WRAP))
  2319. rc.right += g_cxLabelMargin * 4;
  2320. rc.right += g_cyEdge; // try to leave a little more for dual blanks
  2321. #ifdef DEBUG
  2322. //DrawFocusRect(hdc, &rc);
  2323. #endif
  2324. ReleaseDC(hwndParent, hdc);
  2325. //
  2326. // #5688: We need to make it sure that the whole edit window is
  2327. // always visible. We should not extend it to the outside of
  2328. // the parent window.
  2329. //
  2330. {
  2331. BOOL fSuccess;
  2332. GetClientRect(hwndParent, &rcClient);
  2333. fSuccess = IntersectRect(&rc, &rc, &rcClient);
  2334. ASSERT(fSuccess || IsRectEmpty(&rcClient) || (seips & SEIPS_NOSCROLL));
  2335. }
  2336. //
  2337. // Inflate it after the clipping, because it's ok to hide border.
  2338. //
  2339. // EM_GETRECT already takes EM_GETMARGINS into account, so don't use both.
  2340. SendMessage(hwndEdit, EM_GETRECT, 0, (LPARAM)(LPRECT)&rcFormat);
  2341. // Turn the margins inside-out so we can AdjustWindowRect on them.
  2342. rcFormat.top = -rcFormat.top;
  2343. rcFormat.left = -rcFormat.left;
  2344. AdjustWindowRectEx(&rcFormat, GetWindowStyle(hwndEdit), FALSE,
  2345. GetWindowExStyle(hwndEdit));
  2346. InflateRect(&rc, -rcFormat.left, -rcFormat.top);
  2347. HideCaret(hwndEdit);
  2348. SetWindowPos(hwndEdit, NULL, rc.left, rc.top,
  2349. rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
  2350. CopyRect(prc, (CONST RECT *)&rc);
  2351. InvalidateRect(hwndEdit, NULL, TRUE);
  2352. ShowCaret(hwndEdit);
  2353. }
  2354. // draw three pixel wide border for border selection.
  2355. void NEAR PASCAL ListView_DrawBorderSel(HIMAGELIST himl, HWND hwnd, HDC hdc, int x,int y, COLORREF clr)
  2356. {
  2357. int dx, dy;
  2358. RECT rc;
  2359. COLORREF clrSave = SetBkColor(hdc, clr);
  2360. ImageList_GetIconSize(himl, &dx, &dy);
  2361. //left
  2362. rc.left = x - 4; // 1 pixel seperation + 3 pixel width.
  2363. rc.top = y - 4;
  2364. rc.right = x - 1;
  2365. rc.bottom = y + dy + 4;
  2366. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  2367. //top
  2368. rc.left = rc.right;
  2369. rc.right = rc.left + dx + 2;
  2370. rc.bottom = rc.top + 3;
  2371. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  2372. //right
  2373. rc.left = rc.right;
  2374. rc.right = rc.left + 3;
  2375. rc.bottom = rc.top + dy + 8; // 2*3 pixel borders + 2*1 pixel seperation = 8
  2376. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  2377. // bottom
  2378. rc.top = rc.bottom - 3;
  2379. rc.right = rc.left;
  2380. rc.left = rc.right - dx - 2;
  2381. ExtTextOut(hdc,0,0,ETO_OPAQUE,&rc,NULL,0,NULL);
  2382. SetBkColor(hdc, clrSave);
  2383. return;
  2384. }
  2385. //
  2386. // If xMax >= 0, then the image will not be drawn past the x-coordinate
  2387. // specified by xMax. This is used only during report view drawing, where
  2388. // we have to clip against our column width.
  2389. //
  2390. UINT NEAR PASCAL ListView_DrawImageEx(LV* plv, LV_ITEM FAR* pitem, HDC hdc, int x, int y, UINT fDraw, int xMax)
  2391. {
  2392. UINT fText = SHDT_DESELECTED;
  2393. UINT fImage = ILD_NORMAL;
  2394. COLORREF clr = 0;
  2395. HIMAGELIST himl;
  2396. BOOL fBorderSel = (plv->exStyle & LVS_EX_BORDERSELECT);
  2397. int cxIcon;
  2398. fImage = (pitem->state & LVIS_OVERLAYMASK);
  2399. fText = SHDT_DESELECTED;
  2400. if (ListView_IsIconView(plv)) {
  2401. himl = plv->himl;
  2402. cxIcon = plv->cxIcon;
  2403. } else {
  2404. himl = plv->himlSmall;
  2405. cxIcon = plv->cxSmIcon;
  2406. }
  2407. // the item can have one of 4 states, for 3 looks:
  2408. // normal simple drawing
  2409. // selected, no focus light image highlight, no text hi
  2410. // selected w/ focus highlight image & text
  2411. // drop highlighting highlight image & text
  2412. if ((pitem->state & LVIS_DROPHILITED) ||
  2413. ((fDraw & LVDI_SELECTED) && (pitem->state & LVIS_SELECTED)))
  2414. {
  2415. fText = SHDT_SELECTED;
  2416. if (!fBorderSel) // do not effect color of icon on borderselect.
  2417. {
  2418. fImage |= ILD_BLEND50;
  2419. clr = CLR_HILIGHT;
  2420. }
  2421. }
  2422. if ((fDraw & LVDI_SELECTNOFOCUS) && (pitem->state & LVIS_SELECTED)) {
  2423. fText = SHDT_SELECTNOFOCUS;
  2424. //fImage |= ILD_BLEND50;
  2425. //clr = GetSysColor(COLOR_3DFACE);
  2426. }
  2427. if (pitem->state & LVIS_CUT)
  2428. {
  2429. fImage |= ILD_BLEND50;
  2430. clr = plv->clrBk;
  2431. }
  2432. if (!(fDraw & LVDI_NOIMAGE))
  2433. {
  2434. if (himl) {
  2435. COLORREF clrBk;
  2436. if (plv->pImgCtx || ((plv->exStyle & LVS_EX_REGIONAL) && !g_fSlowMachine))
  2437. clrBk = CLR_NONE;
  2438. else
  2439. clrBk = plv->clrBk;
  2440. if (xMax >= 0)
  2441. cxIcon = min(cxIcon, xMax - x);
  2442. if (cxIcon > 0)
  2443. ImageList_DrawEx(himl, pitem->iImage, hdc, x, y, cxIcon, 0, clrBk, clr, fImage);
  2444. }
  2445. if (plv->himlState) {
  2446. if (LV_StateImageValue(pitem) &&
  2447. (pitem->iSubItem == 0 ||
  2448. plv->exStyle & LVS_EX_SUBITEMIMAGES)
  2449. ) {
  2450. int iState = LV_StateImageIndex(pitem);
  2451. int dyImage =
  2452. (himl) ?
  2453. ( (ListView_IsIconView(plv) ? plv->cyIcon : plv->cySmIcon) - plv->cyState)
  2454. : 0;
  2455. int xDraw = x-plv->cxState;
  2456. cxIcon = plv->cxState;
  2457. if (xMax >= 0)
  2458. cxIcon = min(cxIcon, xMax - xDraw);
  2459. if (cxIcon > 0)
  2460. ImageList_DrawEx(plv->himlState, iState, hdc,
  2461. xDraw,
  2462. y + dyImage,
  2463. cxIcon,
  2464. 0,
  2465. CLR_DEFAULT,
  2466. CLR_DEFAULT,
  2467. ILD_NORMAL);
  2468. }
  2469. }
  2470. // draw the border selection if appropriate.
  2471. if (fBorderSel && !(fText & SHDT_DESELECTED)) // selected, draw the selection rect.
  2472. {
  2473. COLORREF clrBorder = (fDraw & LVDI_HOTSELECTED)
  2474. ? GetSysColor(COLOR_HOTLIGHT) : g_clrHighlight;
  2475. ListView_DrawBorderSel(himl, plv->ci.hwnd, hdc, x, y, clrBorder);
  2476. }
  2477. else if (fBorderSel && (fText & SHDT_DESELECTED)) // erase possible selection rect.
  2478. ListView_DrawBorderSel(himl, plv->ci.hwnd, hdc, x, y, plv->clrBk);
  2479. }
  2480. return fText;
  2481. }
  2482. #if defined(FE_IME)
  2483. void NEAR PASCAL ListView_SizeIME(HWND hwnd)
  2484. {
  2485. HIMC himc;
  2486. CANDIDATEFORM candf;
  2487. RECT rc;
  2488. // If this subclass procedure is being called with WM_SIZE,
  2489. // This routine sets the rectangle to an IME.
  2490. GetClientRect(hwnd, &rc);
  2491. // Candidate stuff
  2492. candf.dwIndex = 0; // Bogus assumption for Japanese IME.
  2493. candf.dwStyle = CFS_EXCLUDE;
  2494. candf.ptCurrentPos.x = rc.left;
  2495. candf.ptCurrentPos.y = rc.bottom;
  2496. candf.rcArea = rc;
  2497. if (himc=ImmGetContext(hwnd))
  2498. {
  2499. ImmSetCandidateWindow(himc, &candf);
  2500. ImmReleaseContext(hwnd, himc);
  2501. }
  2502. }
  2503. void NEAR PASCAL DrawCompositionLine(HWND hwnd, HDC hdc, HFONT hfont, LPTSTR lpszComp, LPBYTE lpszAttr, int ichCompStart, int ichCompEnd, int ichStart)
  2504. {
  2505. PTSTR pszCompStr;
  2506. int ichSt,ichEnd;
  2507. DWORD dwPos;
  2508. BYTE bAttr;
  2509. HFONT hfontOld;
  2510. int fnPen;
  2511. HPEN hPen;
  2512. COLORREF crDrawText;
  2513. COLORREF crDrawBack;
  2514. COLORREF crOldText;
  2515. COLORREF crOldBk;
  2516. while (ichCompStart < ichCompEnd)
  2517. {
  2518. // Get the fragment to draw
  2519. //
  2520. // ichCompStart,ichCompEnd -- index at Edit Control
  2521. // ichSt,ichEnd -- index at lpszComp
  2522. ichEnd = ichSt = ichCompStart - ichStart;
  2523. bAttr = lpszAttr[ichSt];
  2524. while (ichEnd < ichCompEnd - ichStart)
  2525. {
  2526. if (bAttr == lpszAttr[ichEnd])
  2527. ichEnd++;
  2528. else
  2529. break;
  2530. }
  2531. pszCompStr = (PTSTR)LocalAlloc(LPTR, sizeof(TCHAR)*(ichEnd - ichSt + 1 + 1) ); // 1 for NULL.
  2532. if (pszCompStr)
  2533. {
  2534. StringCchCopy(pszCompStr, ichEnd-ichSt+1, &lpszComp[ichSt]);
  2535. pszCompStr[ichEnd-ichSt] = '\0';
  2536. }
  2537. // Attribute stuff
  2538. switch (bAttr)
  2539. {
  2540. case ATTR_INPUT:
  2541. fnPen = PS_DOT;
  2542. crDrawText = g_clrWindowText;
  2543. crDrawBack = g_clrWindow;
  2544. break;
  2545. case ATTR_TARGET_CONVERTED:
  2546. case ATTR_TARGET_NOTCONVERTED:
  2547. fnPen = PS_DOT;
  2548. crDrawText = g_clrHighlightText;
  2549. crDrawBack = g_clrHighlight;
  2550. break;
  2551. case ATTR_CONVERTED:
  2552. fnPen = PS_SOLID;
  2553. crDrawText = g_clrWindowText;
  2554. crDrawBack = g_clrWindow;
  2555. break;
  2556. }
  2557. crOldText = SetTextColor(hdc, crDrawText);
  2558. crOldBk = SetBkColor(hdc, crDrawBack);
  2559. hfontOld= SelectObject(hdc, hfont);
  2560. // Get the start position of composition
  2561. //
  2562. dwPos = (DWORD) SendMessage(hwnd, EM_POSFROMCHAR, ichCompStart, 0);
  2563. // Draw it.
  2564. TextOut(hdc, GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), pszCompStr, ichEnd-ichSt);
  2565. #ifndef DONT_UNDERLINE
  2566. // Underline
  2567. hPen = CreatePen(fnPen, 1, crDrawText);
  2568. if( hPen ) {
  2569. HPEN hpenOld = SelectObject( hdc, hPen );
  2570. int iOldBk = SetBkMode( hdc, TRANSPARENT );
  2571. SIZE size;
  2572. GetTextExtentPoint(hdc, pszCompStr, ichEnd-ichSt, &size);
  2573. MoveToEx( hdc, GET_X_LPARAM(dwPos), size.cy + GET_Y_LPARAM(dwPos)-1, NULL);
  2574. LineTo( hdc, size.cx + GET_X_LPARAM(dwPos), size.cy + GET_Y_LPARAM(dwPos)-1 );
  2575. SetBkMode( hdc, iOldBk );
  2576. if( hpenOld ) SelectObject( hdc, hpenOld );
  2577. DeleteObject( hPen );
  2578. }
  2579. #endif
  2580. if (hfontOld)
  2581. SelectObject(hdc, hfontOld);
  2582. SetTextColor(hdc, crOldText);
  2583. SetBkColor(hdc, crOldBk);
  2584. LocalFree((HLOCAL)pszCompStr);
  2585. //Next fragment
  2586. //
  2587. ichCompStart += ichEnd-ichSt;
  2588. }
  2589. }
  2590. void NEAR PASCAL ListView_InsertComposition(HWND hwnd, WPARAM wParam, LPARAM lParam, LV *plv)
  2591. {
  2592. PSTR pszCompStr;
  2593. int cbComp = 0;
  2594. int cbCompNew;
  2595. int cchMax;
  2596. int cchText;
  2597. DWORD dwSel;
  2598. HIMC himc = (HIMC)0;
  2599. // To prevent recursion..
  2600. if (plv->flags & LVF_INSERTINGCOMP)
  2601. {
  2602. return;
  2603. }
  2604. plv->flags |= LVF_INSERTINGCOMP;
  2605. // Don't want to redraw edit during inserting.
  2606. //
  2607. SendMessage(hwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
  2608. // If we have RESULT STR, put it to EC first.
  2609. if (himc = ImmGetContext(hwnd))
  2610. {
  2611. if (!(dwSel = PtrToUlong(GetProp(hwnd, szIMECompPos))))
  2612. dwSel = Edit_GetSel(hwnd);
  2613. // Becaues we don't setsel after inserting composition
  2614. // in win32 case.
  2615. Edit_SetSel(hwnd, GET_X_LPARAM(dwSel), GET_Y_LPARAM(dwSel));
  2616. if (lParam&GCS_RESULTSTR)
  2617. {
  2618. // ImmGetCompositionString() returns length of buffer in bytes,
  2619. // not in # of character
  2620. cbComp = (int)ImmGetCompositionString(himc, GCS_RESULTSTR, NULL, 0);
  2621. pszCompStr = (PSTR)LocalAlloc(LPTR, cbComp + sizeof(TCHAR));
  2622. if (pszCompStr)
  2623. {
  2624. ImmGetCompositionString(himc, GCS_RESULTSTR, (PSTR)pszCompStr, cbComp+sizeof(TCHAR));
  2625. // With ImmGetCompositionStringW, cbComp is # of bytes copied
  2626. // character position must be calculated by cbComp / sizeof(TCHAR)
  2627. //
  2628. *(TCHAR *)(&pszCompStr[cbComp]) = TEXT('\0');
  2629. Edit_ReplaceSel(hwnd, (LPTSTR)pszCompStr);
  2630. LocalFree((HLOCAL)pszCompStr);
  2631. }
  2632. // There's no longer selection
  2633. //
  2634. RemoveProp(hwnd, szIMECompPos);
  2635. // Get current cursor pos so that the subsequent composition
  2636. // handling will do the right thing.
  2637. //
  2638. dwSel = Edit_GetSel(hwnd);
  2639. }
  2640. if (lParam & GCS_COMPSTR)
  2641. {
  2642. // ImmGetCompositionString() returns length of buffer in bytes,
  2643. // not in # of character
  2644. //
  2645. cbComp = (int)ImmGetCompositionString(himc, GCS_COMPSTR, NULL, 0);
  2646. pszCompStr = (PSTR)LocalAlloc(LPTR, cbComp + sizeof(TCHAR));
  2647. if (pszCompStr)
  2648. {
  2649. ImmGetCompositionString(himc, GCS_COMPSTR, pszCompStr, cbComp+sizeof(TCHAR));
  2650. // Get position of the current selection
  2651. //
  2652. cchMax = (int)SendMessage(hwnd, EM_GETLIMITTEXT, 0, 0);
  2653. cchText = Edit_GetTextLength(hwnd);
  2654. // Cut the composition string if it exceeds limit.
  2655. //
  2656. cbCompNew = min((UINT)cbComp,
  2657. sizeof(TCHAR)*(cchMax-(cchText-(HIWORD(dwSel)-LOWORD(dwSel)))));
  2658. // wrap up the DBCS at the end of string
  2659. //
  2660. if (cbCompNew < cbComp)
  2661. {
  2662. *(TCHAR *)(&pszCompStr[cbCompNew]) = TEXT('\0');
  2663. // Reset composition string if we cut it.
  2664. ImmSetCompositionString(himc, SCS_SETSTR, pszCompStr, cbCompNew, NULL, 0);
  2665. cbComp = cbCompNew;
  2666. }
  2667. *(TCHAR *)(&pszCompStr[cbComp]) = TEXT('\0');
  2668. // Replace the current selection with composition string.
  2669. //
  2670. Edit_ReplaceSel(hwnd, (LPTSTR)pszCompStr);
  2671. LocalFree((HLOCAL)pszCompStr);
  2672. }
  2673. // Mark the composition string so that we can replace it again
  2674. // for the next time.
  2675. //
  2676. // Don't setsel to avoid flicking
  2677. if (cbComp)
  2678. {
  2679. dwSel = MAKELONG(LOWORD(dwSel),LOWORD(dwSel)+cbComp/sizeof(TCHAR));
  2680. SetProp(hwnd, szIMECompPos, IntToPtr(dwSel));
  2681. }
  2682. else
  2683. RemoveProp(hwnd, szIMECompPos);
  2684. }
  2685. ImmReleaseContext(hwnd, himc);
  2686. }
  2687. SendMessage(hwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
  2688. //
  2689. // We want to update the size of label edit just once at
  2690. // each WM_IME_COMPOSITION processing. ReplaceSel causes several EN_UPDATE
  2691. // and it causes ugly flicking too.
  2692. //
  2693. RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT|RDW_INVALIDATE);
  2694. SetWindowID(plv->hwndEdit, 1);
  2695. ListView_SetEditSize(plv);
  2696. plv->flags &= ~LVF_INSERTINGCOMP;
  2697. }
  2698. void NEAR PASCAL ListView_PaintComposition(HWND hwnd, LV * plv)
  2699. {
  2700. BYTE szCompStr[CCHLABELMAX + 1];
  2701. BYTE szCompAttr[CCHLABELMAX + 1];
  2702. int cchLine, ichLineStart;
  2703. int cbComp = 0;
  2704. int cchComp;
  2705. int nLine;
  2706. int ichCompStart, ichCompEnd;
  2707. DWORD dwSel;
  2708. int cchMax, cchText;
  2709. HIMC himc = (HIMC)0;
  2710. HDC hdc;
  2711. if (plv->flags & LVF_INSERTINGCOMP)
  2712. {
  2713. // This is the case that ImmSetCompositionString() generates
  2714. // WM_IME_COMPOSITION. We're not ready to paint composition here.
  2715. return;
  2716. }
  2717. if (himc = ImmGetContext(hwnd))
  2718. {
  2719. cbComp=(UINT)ImmGetCompositionString(himc, GCS_COMPSTR, szCompStr, sizeof(szCompStr));
  2720. ImmGetCompositionString(himc, GCS_COMPATTR, szCompAttr, sizeof(szCompStr));
  2721. ImmReleaseContext(hwnd, himc);
  2722. }
  2723. if (cbComp)
  2724. {
  2725. // Get the position of current selection
  2726. //
  2727. if (!(dwSel = PtrToUlong(GetProp(hwnd, szIMECompPos))))
  2728. dwSel = 0L;
  2729. cchMax = (int)SendMessage(hwnd, EM_GETLIMITTEXT, 0, 0);
  2730. cchText = Edit_GetTextLength(hwnd);
  2731. cbComp = min((UINT)cbComp, sizeof(TCHAR)*(cchMax-(cchText-(HIWORD(dwSel)-LOWORD(dwSel)))));
  2732. *(TCHAR *)(&szCompStr[cbComp]) = TEXT('\0');
  2733. /////////////////////////////////////////////////
  2734. // //
  2735. // Draw composition string over the sel string.//
  2736. // //
  2737. /////////////////////////////////////////////////
  2738. hdc = GetDC(hwnd);
  2739. ichCompStart = LOWORD(dwSel);
  2740. cchComp = cbComp/sizeof(TCHAR);
  2741. while (ichCompStart < (int)LOWORD(dwSel) + cchComp)
  2742. {
  2743. // Get line from each start pos.
  2744. //
  2745. nLine = Edit_LineFromChar(hwnd, ichCompStart);
  2746. ichLineStart = Edit_LineIndex(hwnd, nLine);
  2747. cchLine= Edit_LineLength(hwnd, ichLineStart);
  2748. // See if composition string is longer than this line.
  2749. //
  2750. if(ichLineStart+cchLine > (int)LOWORD(dwSel)+cchComp)
  2751. ichCompEnd = LOWORD(dwSel)+cchComp;
  2752. else
  2753. {
  2754. // Yes, the composition string is longer.
  2755. // Take the begining of the next line as next start.
  2756. //
  2757. if (ichLineStart+cchLine > ichCompStart)
  2758. ichCompEnd = ichLineStart+cchLine;
  2759. else
  2760. {
  2761. // If the starting position is not proceeding,
  2762. // let's get out of here.
  2763. break;
  2764. }
  2765. }
  2766. // Draw the line
  2767. //
  2768. DrawCompositionLine(hwnd, hdc, plv->hfontLabel, (LPTSTR)szCompStr, szCompAttr, ichCompStart, ichCompEnd, LOWORD(dwSel));
  2769. ichCompStart = ichCompEnd;
  2770. }
  2771. ReleaseDC(hwnd, hdc);
  2772. // We don't want to repaint the window.
  2773. ValidateRect(hwnd, NULL);
  2774. }
  2775. }
  2776. #endif FE_IME