Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3484 lines
102 KiB

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