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.

14527 lines
435 KiB

  1. #include "ctlspriv.h"
  2. #include "listview.h"
  3. #include "image.h"
  4. #include <mlang.h>
  5. #include <inetreg.h>
  6. #include "uxthemep.h"
  7. #define __IOleControl_INTERFACE_DEFINED__ // There is a conflict with the IOleControl's def of CONTROLINFO
  8. #include "shlobj.h"
  9. #ifdef FULL_DEBUG
  10. #define LISTVIEW_VFX_DEFAULT TRUE
  11. #else
  12. #define LISTVIEW_VFX_DEFAULT FALSE
  13. #endif
  14. int LV_GetNewColWidth(LV* plv, int iFirst, int iLast);
  15. void ListView_RecalcTileSize(LV* plv);
  16. int ListView_ComputeCXItemSize(LV* plv);
  17. #define IE_SETTINGS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced")
  18. #define USE_DBL_CLICK_TIMER TEXT("UseDoubleClickTimer")
  19. int g_bUseDblClickTimer;
  20. #define LVMP_WINDOWPOSCHANGED (WM_USER + 1)
  21. HRESULT WINAPI UninitializeFlatSB(HWND hwnd);
  22. // the insert mark is 6 pixels wide
  23. #define INSERTMARKSIZE 6
  24. #define GROUPHEADER_PADDING 6
  25. #define GRADIENT_WIDTH 300
  26. #define COLORISLIGHT(clr) ((5*GetGValue((clr)) + 2*GetRValue((clr)) + GetBValue((clr))) > 8*128)
  27. void ListView_HandleMouse(LV* plv, BOOL fDoubleClick, int x, int y, UINT keyFlags, BOOL bMouseWheel);
  28. /// function table setup
  29. const PFNLISTVIEW_DRAWITEM pfnListView_DrawItem[5] =
  30. {
  31. ListView_IDrawItem,
  32. ListView_RDrawItem,
  33. ListView_IDrawItem,
  34. ListView_LDrawItem,
  35. ListView_TDrawItem,
  36. };
  37. void ListView_HandleStateIconClick(LV* plv, int iItem);
  38. DWORD ListView_IApproximateViewRect(LV* ,int, int, int);
  39. DWORD ListView_RApproximateViewRect(LV* ,int, int, int);
  40. DWORD ListView_LApproximateViewRect(LV* ,int, int, int);
  41. const PFNLISTVIEW_APPROXIMATEVIEWRECT pfnListView_ApproximateViewRect[5] =
  42. {
  43. ListView_IApproximateViewRect,
  44. ListView_RApproximateViewRect,
  45. ListView_IApproximateViewRect,
  46. ListView_LApproximateViewRect,
  47. ListView_IApproximateViewRect,
  48. };
  49. const PFNLISTVIEW_UPDATESCROLLBARS pfnListView_UpdateScrollBars[5] =
  50. {
  51. ListView_IUpdateScrollBars,
  52. ListView_RUpdateScrollBars,
  53. ListView_IUpdateScrollBars,
  54. ListView_LUpdateScrollBars,
  55. ListView_IUpdateScrollBars,
  56. };
  57. const PFNLISTVIEW_ITEMHITTEST pfnListView_ItemHitTest[5] =
  58. {
  59. ListView_IItemHitTest,
  60. ListView_RItemHitTest,
  61. ListView_SItemHitTest,
  62. ListView_LItemHitTest,
  63. ListView_TItemHitTest,
  64. };
  65. const PFNLISTVIEW_ONSCROLL pfnListView_OnScroll[5] =
  66. {
  67. ListView_IOnScroll,
  68. ListView_ROnScroll,
  69. ListView_IOnScroll,
  70. ListView_LOnScroll,
  71. ListView_IOnScroll,
  72. };
  73. const PFNLISTVIEW_SCROLL2 pfnListView_Scroll2[5] =
  74. {
  75. ListView_IScroll2,
  76. ListView_RScroll2,
  77. ListView_IScroll2,
  78. ListView_LScroll2,
  79. ListView_IScroll2,
  80. };
  81. const PFNLISTVIEW_GETSCROLLUNITSPERLINE pfnListView_GetScrollUnitsPerLine[5] =
  82. {
  83. ListView_IGetScrollUnitsPerLine,
  84. ListView_RGetScrollUnitsPerLine,
  85. ListView_IGetScrollUnitsPerLine,
  86. ListView_LGetScrollUnitsPerLine,
  87. ListView_IGetScrollUnitsPerLine,
  88. };
  89. void ListView_NULLRecomputeLabelSize(LV* plv, LISTITEM* pitem, int i, HDC hdc, BOOL fusepitem)
  90. {
  91. // Report and List view don't need a recompute
  92. }
  93. const PFNLISTVIEW_RECOMPUTELABELSIZE pfnListView_RecomputeLabelSize[5] =
  94. {
  95. ListView_IRecomputeLabelSize,
  96. ListView_IRecomputeLabelSize,
  97. ListView_IRecomputeLabelSize,
  98. ListView_IRecomputeLabelSize,
  99. ListView_TRecomputeLabelSize,
  100. };
  101. BOOL ListView_NULLRecomputeEx(LV* plv, HDPA hdpaSort, int iFrom, BOOL fForce)
  102. {
  103. return FALSE;
  104. }
  105. const PFNLISTVIEW_RECOMPUTEEX pfnListView_RecomputeEx[5] =
  106. {
  107. ListView_IRecomputeEx,
  108. ListView_RRecomputeEx,
  109. ListView_IRecomputeEx,
  110. ListView_NULLRecomputeEx,
  111. ListView_IRecomputeEx,
  112. };
  113. #ifdef DEBUG_PAINT
  114. void ListView_DebugDrawInvalidRegion(LV* plv, RECT* prc, HRGN hrgn)
  115. {
  116. HDC hdc;
  117. HBRUSH hbrush;
  118. int bkMode;
  119. static int s_iclr;
  120. static COLORREF s_aclr[] =
  121. {
  122. RGB(255, 0, 0), RGB(0, 255, 0),
  123. RGB(255, 255, 0), RGB(0, 255, 255),
  124. };
  125. s_iclr = (s_iclr + 1) % ARRAYSIZE(s_aclr);
  126. hdc = GetDC(plv->ci.hwnd);
  127. hbrush = CreateHatchBrush(HS_DIAGCROSS, s_aclr[s_iclr]);
  128. bkMode = SetBkMode(hdc, TRANSPARENT);
  129. if (prc)
  130. {
  131. FillRect(hdc, prc, hbrush);
  132. }
  133. else if (hrgn)
  134. {
  135. FillRgn(hdc, hrgn, hbrush);
  136. }
  137. DeleteObject((HGDIOBJ)hbrush);
  138. SetBkMode(hdc, bkMode);
  139. ReleaseDC(plv->ci.hwnd, hdc);
  140. Sleep(120);
  141. }
  142. BOOL ListView_DebugDrawInvalidItem(LV* plv, int iItem)
  143. {
  144. RECT rcLabel;
  145. RECT rcIcon;
  146. ListView_GetRects(plv, iItem, QUERY_DEFAULT,
  147. &rcIcon, &rcLabel, NULL, NULL);
  148. ListView_DebugDrawInvalidRegion(plv, &rcIcon, NULL);
  149. ListView_DebugDrawInvalidRegion(plv, &rcLabel, NULL);
  150. return TRUE;
  151. }
  152. void ListView_DebugDisplayClipRegion(LV* plv, RECT* prc, HRGN hrgn)
  153. {
  154. HDC hdc = GetDC(plv->ci.hwnd);
  155. if (prc)
  156. {
  157. InvertRect(hdc, prc);
  158. }
  159. else if (hrgn)
  160. {
  161. InvertRgn(hdc, hrgn);
  162. }
  163. Sleep(120);
  164. if (prc)
  165. {
  166. InvertRect(hdc, prc);
  167. }
  168. else if (hrgn)
  169. {
  170. InvertRgn(hdc, hrgn);
  171. }
  172. ReleaseDC(plv->ci.hwnd, hdc);
  173. }
  174. #else
  175. #define ListView_DebugDrawInvalidItem(plv, iItem) FALSE
  176. #endif
  177. // redefine to trace at most calls to ListView_SendChange
  178. #define DM_LVSENDCHANGE 0
  179. // penwin.h is messed up; define local stuff for now
  180. #define HN_BEGINDIALOG 40 // Lens/EditText/garbage detection dialog is about
  181. // to come up on this hedit/bedit
  182. #define HN_ENDDIALOG 41 // Lens/EditText/garbage detection dialog has
  183. // just been destroyed
  184. //---------------------------------------------------------
  185. // no way am I gonna make TWO function calls where I can do FOUR comparisons!
  186. //
  187. #define RECTS_IN_SIZE(sz, r2) (!RECTS_NOT_IN_SIZE(sz, r2))
  188. #define RECTS_NOT_IN_SIZE(sz, r2) (\
  189. ((sz).cx <= (r2).left) ||\
  190. (0 >= (r2).right) ||\
  191. ((sz).cy <= (r2).top) ||\
  192. (0 >= (r2).bottom))
  193. //---------------------------------------------------------
  194. void ListView_OnUpdate(LV* plv, int i);
  195. void ListView_OnDestroy(LV* plv);
  196. BOOL ListView_ValidateScrollParams(LV* plv, int * dx, int *dy);
  197. void ListView_ButtonSelect(LV* plv, int iItem, UINT keyFlags, BOOL bSelected);
  198. void ListView_DeselectAll(LV* plv, int iDontDeselect);
  199. void ListView_LRInvalidateBelow(LV* plv, int i, int fSmoothScroll);
  200. void ListView_IInvalidateBelow(LV* plv, int i);
  201. void ListView_InvalidateFoldedItem(LV* plv, int iItem, BOOL fSelectionOnly, UINT fRedraw);
  202. void ListView_ReleaseBkImage(LV *plv);
  203. void ListView_RecalcRegion(LV *plv, BOOL fForce, BOOL fRedraw);
  204. BOOL g_fSlowMachine = -1;
  205. BOOL ListView_Init(HINSTANCE hinst)
  206. {
  207. WNDCLASS wc;
  208. wc.lpfnWndProc = ListView_WndProc;
  209. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  210. wc.hIcon = NULL;
  211. wc.lpszMenuName = NULL;
  212. wc.hInstance = hinst;
  213. wc.lpszClassName = c_szListViewClass;
  214. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // NULL;
  215. wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
  216. wc.cbWndExtra = sizeof(LV*);
  217. wc.cbClsExtra = 0;
  218. if (!RegisterClass(&wc) && !GetClassInfo(hinst, c_szListViewClass, &wc))
  219. return FALSE;
  220. return TRUE;
  221. }
  222. // Cancel tracking tooltips which are activated by item focus via keyboard
  223. void ListView_CancelTipTrack(LV* plv)
  224. {
  225. // Make sure in tracking mode
  226. if (plv->hwndToolTips)
  227. {
  228. // Cancel any pending timer
  229. KillTimer(plv->ci.hwnd, IDT_TRACKINGTIP);
  230. if (ListView_IsKbdTipTracking(plv))
  231. {
  232. TOOLINFO ti = {0};
  233. // Mark as tracking nothing
  234. plv->iTracking = LVKTT_NOTRACK;
  235. // Reset tooltip to non-tracking
  236. ti.cbSize = sizeof(TOOLINFO);
  237. ti.hwnd = plv->ci.hwnd;
  238. SendMessage(plv->hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti);
  239. SendMessage(plv->hwndToolTips, TTM_TRACKACTIVATE, FALSE, (LPARAM)&ti);
  240. // Switch tooltip window back to non-tracking (manual) mode
  241. ti.uFlags &= ~TTF_TRACK;
  242. SendMessage(plv->hwndToolTips, TTM_SETTOOLINFO, 0, (LPARAM)&ti);
  243. }
  244. }
  245. }
  246. BOOL ListView_GetRegIASetting(BOOL *pb)
  247. {
  248. HKEY hkey;
  249. BOOL bRet = FALSE;
  250. BOOL bValue = TRUE;
  251. if (RegOpenKeyEx(HKEY_CURRENT_USER, IE_SETTINGS, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
  252. {
  253. DWORD dwType;
  254. DWORD dwValue;
  255. DWORD cbValue = sizeof(DWORD);
  256. if (RegQueryValueEx(hkey, (LPTSTR)USE_DBL_CLICK_TIMER, 0, &dwType, (LPBYTE)&dwValue, &cbValue) == ERROR_SUCCESS)
  257. {
  258. bValue = (BOOL)dwValue;
  259. bRet = TRUE;
  260. }
  261. RegCloseKey(hkey);
  262. }
  263. *pb = bValue;
  264. return bRet;
  265. }
  266. BOOL ListView_NotifyCacheHint(LV* plv, int iFrom, int iTo)
  267. {
  268. NM_CACHEHINT nm;
  269. if (iFrom <= iTo)
  270. {
  271. nm.iFrom = iFrom;
  272. nm.iTo = iTo;
  273. return !(BOOL)CCSendNotify(&plv->ci, LVN_ODCACHEHINT, &nm.hdr);
  274. }
  275. return FALSE;
  276. }
  277. void ListView_LazyCreateObjects(LV *plv, int iMin, int iMax)
  278. {
  279. for (; iMin < iMax; iMin++)
  280. NotifyWinEvent(EVENT_OBJECT_CREATE, plv->ci.hwnd, OBJID_CLIENT, 1 + iMin);
  281. }
  282. //
  283. // Owner-data causes MSAA lots of grief, because there is no way to tell
  284. // MSAA "I just created 25 million items". You have to tell it one at a
  285. // time. Instead of sending out 25 million "add item" notifications, we
  286. // just send them out as they scroll into view.
  287. //
  288. // plv->iMSAAMin and plv->iMSAAMax are the range of items we most
  289. // recently told MSAA about. MSAAMax is *exclusive*, just like RECTs.
  290. // It makes the math easier.
  291. //
  292. // We use iMSAAMin and iMSAAMax to avoid sending blatantly redundant
  293. // notifications, which would other happen very frequently.
  294. //
  295. void ListView_LazyCreateWinEvents(LV *plv, int iFrom, int iTo)
  296. {
  297. int iMin = iFrom;
  298. int iMax = iTo+1; // Convert from [From,To] to [Min,Max)
  299. //
  300. // If the incoming range is entirely contained within the existing
  301. // range, then there is nothing to do. This happens a lot.
  302. //
  303. if (iMin >= plv->iMSAAMin && iMax <= plv->iMSAAMax)
  304. return;
  305. //
  306. // If the incoming range is adjacent to or overlaps the low end
  307. // of the existing range... (This happens when scrolling backwards.)
  308. //
  309. if (iMin <= plv->iMSAAMin && iMax >= plv->iMSAAMin)
  310. {
  311. // Notify the low end.
  312. ListView_LazyCreateObjects(plv, iMin, plv->iMSAAMin);
  313. // Extend the list of things we've notified.
  314. plv->iMSAAMin = iMin;
  315. // Remove it from the things left to be notified.
  316. iMin = plv->iMSAAMax;
  317. }
  318. //
  319. // Now do the same thing to the top end.
  320. // (This happens when scrolling forwards.)
  321. //
  322. if (iMax >= plv->iMSAAMax && iMin <= plv->iMSAAMax)
  323. {
  324. // Notify the top end.
  325. ListView_LazyCreateObjects(plv, plv->iMSAAMax, iMax);
  326. // Extend the list of things we've notified.
  327. plv->iMSAAMax = iMax;
  328. // Remove it from the things left to be notified.
  329. iMax = plv->iMSAAMin;
  330. }
  331. //
  332. // If there are still things to be notified, then it means that the
  333. // incoming range isn't contiguous with the previous range, so throw
  334. // away the old range and just set it to the current range.
  335. // (This happens when you grab the scrollbar and jump to a completely
  336. // unrelated part of the listview.)
  337. //
  338. if (iMin < iMax)
  339. {
  340. plv->iMSAAMin = iMin;
  341. plv->iMSAAMax = iMax;
  342. ListView_LazyCreateObjects(plv, iMin, iMax);
  343. }
  344. }
  345. LRESULT ListView_RequestFindItem(LV* plv, CONST LV_FINDINFO* plvfi, int iStart)
  346. {
  347. NM_FINDITEM nm;
  348. nm.lvfi = *plvfi;
  349. nm.iStart = iStart;
  350. return CCSendNotify(&plv->ci, LVN_ODFINDITEM, &nm.hdr);
  351. }
  352. BOOL ListView_SendChange(LV* plv, int i, int iSubItem, int code, UINT oldState, UINT newState,
  353. UINT changed, LPARAM lParam)
  354. {
  355. NM_LISTVIEW nm;
  356. nm.iItem = i;
  357. nm.iSubItem = iSubItem;
  358. nm.uNewState = newState;
  359. nm.uOldState = oldState;
  360. nm.uChanged = changed;
  361. nm.ptAction.x = 0;
  362. nm.ptAction.y = 0;
  363. nm.lParam = lParam;
  364. return !CCSendNotify(&plv->ci, code, &nm.hdr);
  365. }
  366. void ListView_SendODChangeAndInvalidate(LV* plv, int iFrom, int iTo, UINT oldState,
  367. UINT newState)
  368. {
  369. NM_ODSTATECHANGE nm;
  370. nm.iFrom = iFrom;
  371. nm.iTo = iTo;
  372. nm.uNewState = newState;
  373. nm.uOldState = oldState;
  374. CCSendNotify(&plv->ci, LVN_ODSTATECHANGED, &nm.hdr);
  375. // Tell accessibility, "Selection changed in a complex way"
  376. NotifyWinEvent(EVENT_OBJECT_SELECTIONWITHIN, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  377. // considerable speed increase less than 100 to do this method
  378. // while over 100, the other method works faster
  379. if ((iTo - iFrom) > 100)
  380. {
  381. InvalidateRect(plv->ci.hwnd, NULL, FALSE);
  382. }
  383. else
  384. {
  385. while (iFrom <= iTo)
  386. {
  387. ListView_InvalidateItem(plv, iFrom, TRUE, RDW_INVALIDATE);
  388. iFrom++;
  389. }
  390. }
  391. }
  392. //
  393. // This function autoarranges or snaps to grid based on the style and ExStyle
  394. //
  395. // Note: AutoArrange overrides the snap-to-grid style.
  396. //
  397. void ListView_ArrangeOrSnapToGrid(LV *plv)
  398. {
  399. if (plv->ci.style & LVS_AUTOARRANGE)
  400. ListView_OnArrange(plv, LVA_DEFAULT);
  401. else if (plv->exStyle & LVS_EX_SNAPTOGRID)
  402. ListView_OnArrange(plv, LVA_SNAPTOGRID);
  403. }
  404. BOOL ListView_Notify(LV* plv, int i, int iSubItem, int code)
  405. {
  406. NM_LISTVIEW nm;
  407. nm.iItem = i;
  408. nm.iSubItem = iSubItem;
  409. nm.uNewState = nm.uOldState = 0;
  410. nm.uChanged = 0;
  411. nm.lParam = 0;
  412. if (!ListView_IsOwnerData(plv))
  413. {
  414. if (code == LVN_DELETEITEM)
  415. {
  416. LISTITEM * pItem = ListView_GetItemPtr(plv, i);
  417. if (pItem)
  418. nm.lParam = pItem->lParam;
  419. }
  420. }
  421. return (BOOL)CCSendNotify(&plv->ci, code, &nm.hdr);
  422. }
  423. BOOL ListView_GetEmptyText(LV* plv)
  424. {
  425. NMLVDISPINFO nm;
  426. BOOL ret;
  427. TCHAR szText[80];
  428. if (plv->fNoEmptyText)
  429. return FALSE;
  430. if (plv->pszEmptyText)
  431. return TRUE;
  432. // For each listview control, we will only send this notify
  433. // once if necessary.
  434. memset(&nm, 0, sizeof(NMLVDISPINFO));
  435. nm.item.mask = LVIF_TEXT;
  436. nm.item.cchTextMax = ARRAYSIZE(szText);
  437. nm.item.pszText = szText;
  438. szText[0] = TEXT('\0');
  439. ret = (BOOL)CCSendNotify(&plv->ci, LVN_GETEMPTYTEXT, &nm.hdr);
  440. if (ret)
  441. // save the text so we don't notify again.
  442. Str_Set(&plv->pszEmptyText, szText);
  443. else
  444. // set a flag so we don't notify again.
  445. plv->fNoEmptyText = TRUE;
  446. return ret;
  447. }
  448. void ListView_NotifyFocusEvent(LV *plv)
  449. {
  450. if (plv->iFocus != -1 && IsWindowVisible(plv->ci.hwnd) && GetFocus() == plv->ci.hwnd)
  451. NotifyWinEvent(EVENT_OBJECT_FOCUS, plv->ci.hwnd, OBJID_CLIENT,
  452. plv->iFocus+1);
  453. }
  454. //
  455. // Call this function when the listview has changed in a radical manner.
  456. // It notifies MSAA that "Whoa, things are completely different now."
  457. //
  458. void ListView_NotifyRecreate(LV *plv)
  459. {
  460. NotifyWinEvent(EVENT_OBJECT_DESTROY, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  461. NotifyWinEvent(EVENT_OBJECT_CREATE, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  462. plv->iMSAAMin = plv->iMSAAMax = 0;
  463. }
  464. int ListView_OnSetItemCount(LV *plv, int iItems, DWORD dwFlags)
  465. {
  466. BOOL frt = TRUE;
  467. // For compatability we assume 0 for flags implies old (Athena) type of functionality and
  468. // does a Invalidate all otherwise if low bit is set we try to be a bit smarter. First pass
  469. // If the first added item is visible invalidate all. Yes we can do better...
  470. if (ListView_IsOwnerData(plv))
  471. {
  472. int iItem;
  473. int cTotalItemsOld = plv->cTotalItems;
  474. BOOL fInvalidateAll = ((dwFlags & LVSICF_NOINVALIDATEALL) == 0);
  475. if ((iItems >= 0) && (iItems <= MAX_LISTVIEWITEMS))
  476. {
  477. plv->cTotalItems = iItems;
  478. // check focus
  479. if (plv->iFocus >= iItems)
  480. plv->iFocus = -1;
  481. if (plv->iDropHilite >= iItems)
  482. plv->iDropHilite = -1;
  483. // check mark
  484. if (plv->iMark >= iItems)
  485. plv->iMark = -1;
  486. // make sure no selections above number of items
  487. plv->plvrangeCut->lpVtbl->ExcludeRange(plv->plvrangeCut, iItems, SELRANGE_MAXVALUE);
  488. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, iItems, SELRANGE_MAXVALUE)))
  489. {
  490. SetLastError(ERROR_OUTOFMEMORY);
  491. return FALSE;
  492. }
  493. plv->rcView.left = RECOMPUTE; // recompute view rect
  494. if (ListView_IsAutoArrangeView(plv))
  495. {
  496. // Call off to the arrange function.
  497. ListView_OnArrange(plv, LVA_DEFAULT);
  498. if (!fInvalidateAll)
  499. {
  500. // Try to be smart and invalidate only what we need to.
  501. // Add a little logic to erase any message like no items found when
  502. // the view was previously empty...
  503. if (cTotalItemsOld < iItems)
  504. iItem = cTotalItemsOld;
  505. else
  506. iItem = iItems - 1; // Get the index
  507. if ((iItem >= 0) && (cTotalItemsOld > 0))
  508. ListView_IInvalidateBelow(plv, iItem);
  509. else
  510. fInvalidateAll = TRUE;
  511. }
  512. }
  513. else
  514. {
  515. ListView_Recompute(plv);
  516. // if we have empty text and old count was zero... then we should redraw all
  517. if (plv->pszEmptyText && (cTotalItemsOld == 0) && (iItems > 0))
  518. fInvalidateAll = TRUE;
  519. // Try to do smart invalidates...
  520. if (!fInvalidateAll)
  521. {
  522. // Try to be smart and invalidate only what we need to.
  523. if (cTotalItemsOld < iItems)
  524. iItem = cTotalItemsOld;
  525. else
  526. iItem = iItems - 1; // Get the index
  527. if (iItem >= 0)
  528. ListView_LRInvalidateBelow(plv, iItem, FALSE);
  529. }
  530. // We may try to resize the column
  531. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  532. // For compatability we assume 0 for flags implies old type
  533. // of functionality and scrolls the important item into view.
  534. // If second bit is set, we leave the scroll position alone.
  535. if ((dwFlags & LVSICF_NOSCROLL) == 0)
  536. {
  537. // what is the important item
  538. iItem = (plv->iFocus >= 0) ?
  539. plv->iFocus :
  540. ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  541. iItem = max(0, iItem);
  542. // make important item visable
  543. ListView_OnEnsureVisible(plv, iItem, FALSE);
  544. }
  545. }
  546. if (fInvalidateAll)
  547. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  548. ListView_UpdateScrollBars(plv);
  549. ListView_NotifyRecreate(plv);
  550. ListView_NotifyFocusEvent(plv);
  551. }
  552. else
  553. {
  554. frt = FALSE;
  555. }
  556. }
  557. else
  558. {
  559. if (plv->hdpaSubItems)
  560. {
  561. int iCol;
  562. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  563. {
  564. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  565. if (hdpa) // this is optional, call backs don't have them
  566. DPA_Grow(hdpa, iItems);
  567. }
  568. }
  569. DPA_Grow(plv->hdpa, iItems);
  570. DPA_Grow(plv->hdpaZOrder, iItems);
  571. }
  572. return frt;
  573. }
  574. VOID ListView_InvalidateTTLastHit(LV* plv, int iNewHit)
  575. {
  576. if (plv->iTTLastHit == iNewHit)
  577. {
  578. plv->iTTLastHit = -1;
  579. if (plv->pszTip && plv->pszTip != LPSTR_TEXTCALLBACK)
  580. {
  581. plv->pszTip[0] = 0;
  582. }
  583. }
  584. }
  585. typedef struct
  586. {
  587. LV *plv;
  588. BOOL fSortIndices;
  589. PFNLVCOMPARE pfnCompare;
  590. LPARAM lParam;
  591. BOOL bPassLP;
  592. } LVSortInfo;
  593. int CALLBACK ListView_SortCallback(void * dw1, void * dw2, LPARAM lParam)
  594. {
  595. LISTITEM *pitem1;
  596. LISTITEM *pitem2;
  597. LVSortInfo *pSortInfo = (LVSortInfo *)lParam;
  598. ASSERT(!ListView_IsOwnerData(pSortInfo->plv));
  599. // determine whether dw1 and dw2 are indices or the real items
  600. // and assign pitem? accordingly
  601. if (pSortInfo->fSortIndices)
  602. {
  603. pitem1 = ListView_GetItemPtr(pSortInfo->plv, PtrToUlong(dw1));
  604. pitem2 = ListView_GetItemPtr(pSortInfo->plv, PtrToUlong(dw2));
  605. }
  606. else
  607. {
  608. pitem1 = (LISTITEM *)dw1;
  609. pitem2 = (LISTITEM *)dw2;
  610. }
  611. if (!pSortInfo->pfnCompare)
  612. {
  613. // Treat NULL pszText like null string.
  614. LPCTSTR pszText1 = pitem1->pszText ? pitem1->pszText : c_szNULL;
  615. LPCTSTR pszText2 = pitem2->pszText ? pitem2->pszText : c_szNULL;
  616. // REARCHITECT: should allow callbacks in text
  617. if (pszText1 != LPSTR_TEXTCALLBACK &&
  618. pszText2 != LPSTR_TEXTCALLBACK)
  619. {
  620. return lstrcmpi(pitem1->pszText, pitem2->pszText);
  621. }
  622. RIPMSG(0, "LVM_SORTITEM(EX): Cannot combine NULL callback with LPSTR_TEXTCALLBACK");
  623. return -1;
  624. }
  625. else
  626. {
  627. if (pSortInfo->bPassLP)
  628. return pSortInfo->pfnCompare(pitem1->lParam, pitem2->lParam, pSortInfo->lParam);
  629. else
  630. {
  631. if (pSortInfo->fSortIndices)
  632. return pSortInfo->pfnCompare((LPARAM)dw1, (LPARAM)dw2, pSortInfo->lParam);
  633. else
  634. {
  635. // we want to sort by the indices, but all we've got are pointers to the items
  636. // and there is no way to get back from that pointer to an index
  637. RIPMSG(0, "LVM_SORTITEM(EX): Want to sort by indicies, but only have pointers");
  638. return -1;
  639. }
  640. }
  641. }
  642. RIPMSG(0, "LVM_SORTITEM(EX): Didn't seem to sort by anything");
  643. return -1;
  644. }
  645. LISTGROUP* ListView_FindGroupFromID(LV* plv, int iGroupId, int* piIndex)
  646. {
  647. if (plv->hdpaGroups)
  648. {
  649. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  650. int iGroup;
  651. for (iGroup = 0; iGroup < cGroups; iGroup++)
  652. {
  653. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  654. if (pgrp->iGroupId == iGroupId)
  655. {
  656. if (piIndex)
  657. *piIndex = iGroup;
  658. return pgrp;
  659. }
  660. }
  661. }
  662. return NULL;
  663. }
  664. BOOL ListView_VerifyGroupIdIsUnique(LV* plv, int iGroupId)
  665. {
  666. if (plv->hdpaGroups)
  667. {
  668. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  669. int iGroup;
  670. for (iGroup = 0; iGroup < cGroups; iGroup++)
  671. {
  672. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  673. if (pgrp->iGroupId == iGroupId)
  674. return FALSE;
  675. }
  676. }
  677. return TRUE;
  678. }
  679. int ListView_GroupIndexFromItem(LV* plv, LISTITEM* pitem)
  680. {
  681. if (LISTITEM_HASGROUP(pitem))
  682. {
  683. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  684. int iGroup;
  685. for (iGroup = 0; iGroup < cGroups; iGroup++)
  686. {
  687. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  688. if (pgrp == pitem->pGroup)
  689. {
  690. return iGroup;
  691. }
  692. }
  693. }
  694. return -1;
  695. }
  696. BOOL ListView_RemoveItemFromItsGroup(LV* plv, LISTITEM* pitem)
  697. {
  698. if (LISTITEM_HASGROUP(pitem))
  699. {
  700. LISTGROUP* pgrp = pitem->pGroup;
  701. int cItems = DPA_GetPtrCount(pgrp->hdpa);
  702. int iItem;
  703. for (iItem = 0; iItem < cItems; iItem++)
  704. {
  705. LISTITEM* pgitem = DPA_FastGetPtr(pgrp->hdpa, iItem);
  706. if (pgitem == pitem)
  707. {
  708. DPA_DeletePtr(pgrp->hdpa, iItem);
  709. return TRUE;
  710. }
  711. }
  712. }
  713. return FALSE;
  714. }
  715. BOOL ListView_FixupGroupsAfterSorting(LV *plv)
  716. {
  717. BOOL fRet = FALSE;
  718. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  719. int *rgiGroupIds = LocalAlloc(LPTR, sizeof(int) * cGroups);
  720. // rgi will be where we keep the index in each group as we add items to them
  721. int *rgi = LocalAlloc(LPTR, sizeof(int) * cGroups);
  722. if (rgiGroupIds && rgi)
  723. {
  724. int i;
  725. int iMax = DPA_GetPtrCount(plv->hdpa);
  726. // Save away the group IDs, and temporary replace them with straight indices
  727. for (i=0; i < cGroups; i++)
  728. {
  729. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  730. rgiGroupIds[i] = pgrp->iGroupId;
  731. pgrp->iGroupId = i;
  732. }
  733. // Now all the items are sorted, and all we need to do it put them back in their
  734. // respective groups is sorted order
  735. for (i=0; i < iMax;i++)
  736. {
  737. LISTITEM *pitem = ListView_FastGetItemPtr(plv, i);
  738. LISTGROUP* pgrp = LISTITEM_GROUP(pitem);
  739. if (pgrp)
  740. {
  741. ASSERT(pgrp->hdpa);
  742. DPA_SetPtr(pgrp->hdpa, rgi[pgrp->iGroupId]++, pitem);
  743. }
  744. }
  745. #if DEBUG
  746. // At this point, we should still have the proper number of items in each group!
  747. for (i=0; i < cGroups; i++)
  748. {
  749. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  750. ASSERT(DPA_GetPtrCount(pgrp->hdpa) == rgi[i]);
  751. }
  752. #endif
  753. // Restore the proper GroupIds now
  754. for (i=0; i < cGroups; i++)
  755. {
  756. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  757. pgrp->iGroupId = rgiGroupIds[i];
  758. }
  759. fRet = TRUE;
  760. }
  761. LocalFree(rgiGroupIds);
  762. LocalFree(rgi);
  763. return fRet;
  764. }
  765. BOOL ListView_SortAllColumns(LV* plv, LVSortInfo * psi)
  766. {
  767. BOOL fReturn;
  768. ASSERT(!ListView_IsOwnerData(plv));
  769. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  770. // don't do this optimization if we will need the indices to sort by
  771. if (psi->bPassLP &&
  772. ((!plv->hdpaSubItems) ||
  773. !DPA_GetPtrCount(plv->hdpaSubItems)))
  774. {
  775. psi->fSortIndices = FALSE;
  776. fReturn = DPA_Sort(plv->hdpa, ListView_SortCallback, (LPARAM)psi);
  777. }
  778. else
  779. {
  780. // if we need to sort several hdpa's, create one DPA of just indices
  781. // and sort that, then fix up all the dpa's
  782. // initialize the hdpa with indices
  783. HDPA hdpa = DPA_Clone(plv->hdpa, NULL);
  784. fReturn = FALSE;
  785. if (hdpa)
  786. {
  787. int i, iMax;
  788. void **ph;
  789. void **pNewIndices;
  790. ASSERT(DPA_GetPtrCount(plv->hdpa) == DPA_GetPtrCount(hdpa));
  791. ph = pNewIndices = DPA_GetPtrPtr(hdpa);
  792. iMax = DPA_GetPtrCount(hdpa);
  793. for (i = 0; i < iMax; ph++, i++)
  794. {
  795. *ph = IntToPtr(i);
  796. }
  797. psi->fSortIndices = TRUE;
  798. if (DPA_Sort(hdpa, ListView_SortCallback, (LPARAM)psi))
  799. {
  800. ph = LocalAlloc(LPTR, sizeof(void *) * iMax);
  801. if (ph)
  802. {
  803. int j;
  804. void **pSubItems;
  805. // we could get here because bPassLP is false, even if we don't have subitems
  806. if (plv->hdpaSubItems && DPA_GetPtrCount(plv->hdpaSubItems))
  807. {
  808. for (i = DPA_GetPtrCount(plv->hdpaSubItems) - 1; i >= 0; i--)
  809. {
  810. HDPA hdpaSubItem = ListView_GetSubItemDPA(plv, i);
  811. if (hdpaSubItem)
  812. {
  813. // make sure it's of the right size
  814. while (DPA_GetPtrCount(hdpaSubItem) < iMax)
  815. {
  816. if (DPA_InsertPtr(hdpaSubItem, iMax, NULL) == -1)
  817. goto Bail;
  818. }
  819. // actually copy across the dpa with the new indices
  820. pSubItems = DPA_GetPtrPtr(hdpaSubItem);
  821. for (j = 0; j < iMax; j++)
  822. {
  823. ph[j] = pSubItems[PtrToUlong(pNewIndices[j])];
  824. }
  825. // finally, copy it all back to the pSubItems;
  826. memcpy(pSubItems, ph, sizeof(void *) * iMax);
  827. }
  828. }
  829. }
  830. // now do the main hdpa
  831. pSubItems = DPA_GetPtrPtr(plv->hdpa);
  832. for (j = 0; j < iMax; j++)
  833. {
  834. ph[j] = pSubItems[PtrToUlong(pNewIndices[j])];
  835. }
  836. // finally, copy it all back to the pSubItems;
  837. memcpy(pSubItems, ph, sizeof(void *) * iMax);
  838. fReturn = TRUE;
  839. Bail:
  840. LocalFree(ph);
  841. }
  842. }
  843. DPA_Destroy(hdpa);
  844. }
  845. }
  846. if (fReturn && plv->fGroupView && plv->hdpaGroups && DPA_GetPtrCount(plv->hdpaGroups) > 0 && ListView_IsGroupedView(plv))
  847. {
  848. fReturn = ListView_FixupGroupsAfterSorting(plv);
  849. }
  850. return fReturn;
  851. }
  852. DWORD ListView_OnApproximateViewRect(LV* plv, int iCount, int iWidth, int iHeight)
  853. {
  854. if (iCount == -1)
  855. iCount = ListView_Count(plv);
  856. if (iWidth == -1)
  857. iWidth = plv->sizeClient.cx;
  858. if (iHeight == -1)
  859. iHeight = plv->sizeClient.cy;
  860. return _ListView_ApproximateViewRect(plv, iCount, iWidth, iHeight);
  861. }
  862. DWORD ListView_OnSetLVRangeObject(LV* plv, int iWhich, ILVRange *plvrange)
  863. {
  864. ILVRange **pplvrange;
  865. switch (iWhich)
  866. {
  867. case LVSR_SELECTION:
  868. pplvrange = &plv->plvrangeSel;
  869. break;
  870. case LVSR_CUT:
  871. pplvrange = &plv->plvrangeCut;
  872. break;
  873. default:
  874. return FALSE;
  875. }
  876. if (*pplvrange)
  877. {
  878. // Release the old one
  879. (*pplvrange)->lpVtbl->Release(*pplvrange);
  880. }
  881. *pplvrange = plvrange;
  882. // Hold onto the pointer...
  883. if (plvrange)
  884. plvrange->lpVtbl->AddRef(plvrange);
  885. return TRUE;
  886. }
  887. BOOL ListView_OnSortItems(LV *plv, LPARAM lParam, PFNLVCOMPARE pfnCompare, BOOL bPassLP)
  888. {
  889. LVSortInfo SortInfo;
  890. LISTITEM *pitemFocused;
  891. SortInfo.pfnCompare = pfnCompare;
  892. SortInfo.lParam = lParam;
  893. SortInfo.plv = plv;
  894. SortInfo.bPassLP = bPassLP;
  895. if (ListView_IsOwnerData(plv))
  896. {
  897. RIPMSG(0, "LVM_SORTITEMS: Invalid for owner-data listview");
  898. return FALSE;
  899. }
  900. ListView_DismissEdit(plv, TRUE); // cancel edits
  901. // we're going to mess with the indices, so stash away the pointer to the
  902. // focused item.
  903. if (plv->iFocus != -1)
  904. {
  905. pitemFocused = ListView_GetItemPtr(plv, plv->iFocus);
  906. }
  907. else
  908. pitemFocused = NULL;
  909. if (ListView_SortAllColumns(plv, &SortInfo))
  910. {
  911. // restore the focused item.
  912. if (pitemFocused)
  913. {
  914. int i;
  915. for (i = ListView_Count(plv) - 1; i >= 0 ; i--)
  916. {
  917. if (ListView_GetItemPtr(plv, i) == pitemFocused)
  918. {
  919. plv->iFocus = i;
  920. plv->iMark = i;
  921. }
  922. }
  923. }
  924. if (ListView_IsAutoArrangeView(plv))
  925. {
  926. ListView_CommonArrange(plv, LVA_DEFAULT, plv->hdpa);
  927. }
  928. else
  929. {
  930. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  931. }
  932. // The items in the view have moved around; let apps know
  933. NotifyWinEvent(EVENT_OBJECT_REORDER, plv->ci.hwnd, OBJID_CLIENT, 0);
  934. return TRUE;
  935. }
  936. return FALSE;
  937. }
  938. void ListView_EnableWindow(LV* plv, BOOL wParam)
  939. {
  940. if (wParam)
  941. {
  942. if (plv->ci.style & WS_DISABLED)
  943. {
  944. plv->ci.style &= ~WS_DISABLED; // enabled
  945. ListView_OnSetBkColor(plv, plv->clrBkSave);
  946. }
  947. }
  948. else
  949. {
  950. if (!(plv->ci.style & WS_DISABLED))
  951. {
  952. plv->clrBkSave = plv->clrBk;
  953. plv->ci.style |= WS_DISABLED; // disabled
  954. ListView_OnSetBkColor(plv, g_clrBtnFace);
  955. }
  956. }
  957. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  958. }
  959. BOOL ListView_IsItemVisibleI(LV* plv, int i)
  960. // Assumes parmss ok etc for speed. Called inside region calc code.
  961. {
  962. RECT rcBounds;
  963. // get bounding rect of item
  964. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, &rcBounds, NULL);
  965. // Should perf this up for multimonitor case where there are dead zones in work area...
  966. return RECTS_IN_SIZE(plv->sizeClient, rcBounds);
  967. }
  968. // Helper for ListView_RecalcRegion
  969. #define BitOn(lpbits, x, y, cx) (*((BYTE *)(lpbits + ((y * cx) + (x / 8)))) & (0x80 >> (x % 8)))
  970. void ListView_RecalcRegion(LV* plv, BOOL fForce, BOOL fRedraw)
  971. {
  972. HRGN hrgnUnion = NULL;
  973. HRGN hrgn = NULL;
  974. int i;
  975. HDC hdc = NULL;
  976. BYTE * lpBits = NULL;
  977. HBITMAP hbmp = NULL, hbmpOld = NULL;
  978. RECT rc, rcIcon = {0};
  979. LISTITEM * pitem;
  980. BITMAP bm;
  981. // Bail out if we don't need to do any work
  982. if (!(plv->exStyle & LVS_EX_REGIONAL) || !ListView_RedrawEnabled(plv) ||
  983. (plv->flags & LVF_INRECALCREGION))
  984. return;
  985. // To prevent recursion
  986. plv->flags |= LVF_INRECALCREGION;
  987. if ((ListView_Count(plv) > 0))
  988. {
  989. int cxIcon, cyIcon;
  990. int dxOffset, dyOffset;
  991. // Run through first to see if anything changed - bail if not!
  992. if (!fForce)
  993. {
  994. for (i = 0; i < ListView_Count(plv); i++)
  995. {
  996. pitem = ListView_FastGetItemPtr(plv, i);
  997. if (!ListView_IsItemVisibleI(plv, i))
  998. {
  999. if (pitem->hrgnIcon == (HANDLE)-1 || !pitem->hrgnIcon)
  1000. // Item was invisible and still is. Nothing changed.
  1001. continue;
  1002. if (pitem->hrgnIcon)
  1003. {
  1004. // Item was visible and now is invisible... Something
  1005. // changed.
  1006. pitem->ptRgn.x = RECOMPUTE;
  1007. pitem->ptRgn.y = RECOMPUTE;
  1008. DeleteObject(pitem->hrgnIcon);
  1009. pitem->hrgnIcon = NULL;
  1010. }
  1011. }
  1012. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, &rc, NULL, NULL);
  1013. // If the location of the icon or the text rectangle have
  1014. // changed, then we need to continue so that we can recalculate
  1015. // the region.
  1016. if ((pitem->pt.x != pitem->ptRgn.x) ||
  1017. (pitem->pt.y != pitem->ptRgn.y) ||
  1018. (!pitem->hrgnIcon) ||
  1019. !EqualRect((CONST RECT *)&pitem->rcTextRgn, (CONST RECT *)&rc))
  1020. goto changed;
  1021. }
  1022. // If we go through all the items and nothing changed, then
  1023. // we can return without doing any work!
  1024. ASSERT(i == ListView_Count(plv));
  1025. goto exit;
  1026. changed:;
  1027. }
  1028. // Figure out the dimensions of the Icon rectangle - assumes
  1029. // each Icon rectangle is the same size.
  1030. ListView_GetRects(plv, 0, QUERY_DEFAULT, &rcIcon, NULL, NULL, NULL);
  1031. // Center the icon in the rectangle
  1032. CCGetIconSize(&plv->ci, plv->himl, &cxIcon, &cyIcon);
  1033. dxOffset = (rcIcon.right - rcIcon.left - cxIcon) / 2;
  1034. dyOffset = (rcIcon.bottom - rcIcon.top - cyIcon) / 2;
  1035. cxIcon = rcIcon.right - rcIcon.left;
  1036. cyIcon = rcIcon.bottom - rcIcon.top;
  1037. if (!(hdc = CreateCompatibleDC(NULL)) ||
  1038. (!(hbmp = CreateBitmap(cxIcon, cyIcon, 1, 1, NULL))))
  1039. {
  1040. goto BailOut;
  1041. }
  1042. GetObject(hbmp, sizeof(bm), &bm);
  1043. if (!(lpBits = (BYTE *)GlobalAlloc(GPTR, bm.bmWidthBytes * bm.bmHeight)))
  1044. goto BailOut;
  1045. hbmpOld = SelectObject(hdc, hbmp);
  1046. PatBlt(hdc, 0, 0, cxIcon, cyIcon, WHITENESS);
  1047. if (hrgnUnion = CreateRectRgn(0, 0, 0, 0))
  1048. {
  1049. for (i = 0; i < ListView_Count(plv); i++)
  1050. {
  1051. int x, y, iResult;
  1052. BOOL fStarted = FALSE;
  1053. LPRECT lprc;
  1054. pitem = ListView_FastGetItemPtr(plv, i);
  1055. if (pitem->pt.y == RECOMPUTE)
  1056. continue;
  1057. if (!ListView_IsItemVisibleI(plv, i))
  1058. {
  1059. // ignore invisible items
  1060. if (pitem->hrgnIcon && pitem->hrgnIcon!=(HANDLE)-1)
  1061. {
  1062. pitem->ptRgn.x = RECOMPUTE;
  1063. pitem->ptRgn.y = RECOMPUTE;
  1064. DeleteObject(pitem->hrgnIcon);
  1065. pitem->hrgnIcon = (HANDLE)-1;
  1066. }
  1067. continue;
  1068. }
  1069. // Add the region for the icon text first
  1070. ListView_GetRects(plv, i, QUERY_DEFAULT, &rcIcon, &rc, NULL, NULL);
  1071. // If we're in edit mode always use rcTextRgn
  1072. if (i == plv->iEdit)
  1073. lprc = &pitem->rcTextRgn;
  1074. else
  1075. lprc = &rc;
  1076. if (!(hrgn = CreateRectRgnIndirect(lprc)))
  1077. goto Error;
  1078. iResult = CombineRgn(hrgnUnion, hrgn, hrgnUnion, RGN_OR);
  1079. DeleteObject(hrgn);
  1080. if (iResult == ERROR)
  1081. {
  1082. // Error case - out of memory. Just select in a NULL region.
  1083. Error:
  1084. DeleteObject(hrgnUnion);
  1085. hrgnUnion = NULL;
  1086. break;
  1087. }
  1088. // Succeeded, copy the rectangle to rcTextRgn so we
  1089. // can test against it in the future. Don't copy over
  1090. // it if we are in edit mode, the rectangle is used to
  1091. // store the edit window in that case.
  1092. if (plv->iEdit != i)
  1093. CopyRect(&pitem->rcTextRgn, (CONST RECT *)&rc);
  1094. // Now create a region for the icon mask - or use the cached one
  1095. if (!pitem->hrgnIcon || pitem->hrgnIcon == (HANDLE)-1)
  1096. {
  1097. // (pitem->pt.x != pitem->ptRgn.x) ||
  1098. // (pitem->pt.y != pitem->ptRgn.y))
  1099. HRGN hrgnIcon = NULL;
  1100. // On slow machines, we'll just wrap the icon with a rectangle. But on
  1101. // faster machines, we'll build a region that corresponds to the
  1102. // mask for the icon so it looks sweet.
  1103. if (g_fSlowMachine)
  1104. {
  1105. // Modify the rectangle slightly so it looks better
  1106. // Glue the icon and text rectangles together
  1107. rcIcon.bottom = rc.top;
  1108. // Shrink the width of the rectangle so it's only as big as the icon itself
  1109. InflateRect(&rcIcon, -dxOffset, 0);
  1110. hrgnIcon = CreateRectRgnIndirect(&rcIcon);
  1111. }
  1112. else
  1113. {
  1114. // If the image isn't around, get it now.
  1115. if (pitem->iImage == I_IMAGECALLBACK)
  1116. {
  1117. LV_ITEM item;
  1118. item.iItem = i;
  1119. item.iSubItem = 0;
  1120. item.mask = LVIF_IMAGE;
  1121. item.stateMask = LVIS_ALL;
  1122. item.pszText = NULL;
  1123. item.cchTextMax = 0;
  1124. // BOGUS - do we need to worry about our state
  1125. // getting messed up during the callback?
  1126. ListView_OnGetItem(plv, &item);
  1127. }
  1128. ImageList_Draw(plv->himl, pitem->iImage, hdc, 0, 0, ILD_MASK | (pitem->state & LVIS_OVERLAYMASK));
  1129. GetBitmapBits(hbmp, bm.bmWidthBytes * bm.bmHeight, (void *)lpBits);
  1130. for (y = 0; y < cyIcon; y++)
  1131. {
  1132. for (x = 0; x < cxIcon; x++)
  1133. {
  1134. if (!fStarted && !BitOn(lpBits, x, y, bm.bmWidthBytes))
  1135. {
  1136. rc.left = x;
  1137. rc.top = y;
  1138. rc.bottom = y + 1;
  1139. fStarted = TRUE;
  1140. if (x == (cxIcon - 1))
  1141. {
  1142. x++;
  1143. goto AddIt;
  1144. }
  1145. else
  1146. {
  1147. continue;
  1148. }
  1149. }
  1150. if (fStarted && BitOn(lpBits, x, y, bm.bmWidthBytes))
  1151. {
  1152. AddIt:
  1153. rc.right = x;
  1154. //
  1155. // Mirror the region so that the icons get displayed ok. [samera]
  1156. //
  1157. if (plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  1158. {
  1159. int iLeft = rc.left;
  1160. rc.left = (cxIcon - (rc.right+1));
  1161. rc.right = (cxIcon - (iLeft+1));
  1162. OffsetRect(&rc, rcIcon.left - dxOffset, rcIcon.top + dyOffset);
  1163. }
  1164. else
  1165. OffsetRect(&rc, rcIcon.left + dxOffset, rcIcon.top + dyOffset);
  1166. if (hrgn = CreateRectRgnIndirect(&rc))
  1167. {
  1168. if (hrgnIcon || (hrgnIcon = CreateRectRgn(0, 0, 0, 0)))
  1169. iResult = CombineRgn(hrgnIcon, hrgn, hrgnIcon, RGN_OR);
  1170. else
  1171. iResult = ERROR;
  1172. DeleteObject(hrgn);
  1173. }
  1174. if (!hrgn || (iResult == ERROR))
  1175. {
  1176. if (hrgnIcon)
  1177. DeleteObject(hrgnIcon);
  1178. goto Error;
  1179. }
  1180. fStarted = FALSE;
  1181. }
  1182. }
  1183. }
  1184. }
  1185. if (hrgnIcon)
  1186. {
  1187. // Cache it since it takes a long time to build it
  1188. if (pitem->hrgnIcon && pitem->hrgnIcon != (HANDLE)-1)
  1189. DeleteObject(pitem->hrgnIcon);
  1190. pitem->hrgnIcon = hrgnIcon;
  1191. pitem->ptRgn = pitem->pt;
  1192. // Add it to the accumulated window region
  1193. if (ERROR == CombineRgn(hrgnUnion, hrgnIcon, hrgnUnion, RGN_OR))
  1194. goto Error;
  1195. }
  1196. }
  1197. else
  1198. {
  1199. OffsetRgn(pitem->hrgnIcon, pitem->pt.x - pitem->ptRgn.x, pitem->pt.y - pitem->ptRgn.y);
  1200. pitem->ptRgn = pitem->pt;
  1201. if (ERROR == CombineRgn(hrgnUnion, pitem->hrgnIcon, hrgnUnion, RGN_OR))
  1202. goto Error;
  1203. }
  1204. }
  1205. }
  1206. }
  1207. BailOut:
  1208. if (lpBits)
  1209. GlobalFree((HGLOBAL)lpBits);
  1210. if (hbmp)
  1211. {
  1212. SelectObject(hdc, hbmpOld);
  1213. DeleteObject(hbmp);
  1214. }
  1215. if (hdc)
  1216. DeleteDC(hdc);
  1217. // Windows takes ownership of the region when we select it in to the window
  1218. SetWindowRgn(plv->ci.hwnd, hrgnUnion, fRedraw);
  1219. exit:
  1220. plv->flags &= ~LVF_INRECALCREGION;
  1221. }
  1222. HIMAGELIST CreateCheckBoxImagelist(HIMAGELIST himl, BOOL fTree, BOOL fUseColorKey, BOOL fMirror)
  1223. {
  1224. int cxImage, cyImage;
  1225. HBITMAP hbm;
  1226. HBITMAP hbmTemp;
  1227. COLORREF clrMask = CLR_DEFAULT;
  1228. HDC hdcDesk = GetDC(NULL);
  1229. HDC hdc;
  1230. RECT rc;
  1231. int nImages = fTree ? 3 : 2;
  1232. HTHEME hTheme;
  1233. DTBGOPTS dtbg = {sizeof(DTBGOPTS), DTBG_DRAWSOLID, 0,}; // tell drawthemebackground to preserve the alpha channel
  1234. if (!hdcDesk)
  1235. return NULL;
  1236. hdc = CreateCompatibleDC(hdcDesk);
  1237. ReleaseDC(NULL, hdcDesk);
  1238. if (!hdc)
  1239. return NULL;
  1240. hTheme = OpenThemeData(NULL, L"Button");
  1241. // Must protect against ImageList_GetIconSize failing in case app
  1242. // gave us a bad himl
  1243. if (himl && ImageList_GetIconSize(himl, &cxImage, &cyImage))
  1244. {
  1245. // cxImage and cyImage are okay
  1246. }
  1247. else
  1248. {
  1249. cxImage = g_cxSmIcon;
  1250. cyImage = g_cySmIcon;
  1251. }
  1252. himl = ImageList_Create(cxImage, cyImage, ILC_MASK | ILC_COLOR32, 0, nImages);
  1253. hbm = CreateColorBitmap(cxImage * nImages, cyImage);
  1254. // fill
  1255. hbmTemp = SelectObject(hdc, hbm);
  1256. rc.left = rc.top = 0;
  1257. rc.bottom = cyImage;
  1258. rc.right = cxImage * nImages;
  1259. if (!hTheme)
  1260. {
  1261. if (fUseColorKey)
  1262. {
  1263. clrMask = RGB(255,000,255); // magenta
  1264. if (clrMask == g_clrWindow)
  1265. clrMask = RGB(000,000,255); // blue
  1266. }
  1267. else
  1268. {
  1269. clrMask = g_clrWindow;
  1270. }
  1271. // Don't fill the image with the mask when themes are on. We want this to
  1272. // "Alpha blend to zero" or be clear. No transparent blt needed.
  1273. FillRectClr(hdc, &rc, clrMask);
  1274. }
  1275. rc.right = cxImage;
  1276. // now draw the real controls on
  1277. InflateRect(&rc, -g_cxEdge, -g_cyEdge);
  1278. rc.right++;
  1279. rc.bottom++;
  1280. if (fTree)
  1281. OffsetRect(&rc, cxImage, 0);
  1282. if (hTheme)
  1283. {
  1284. DrawThemeBackgroundEx(hTheme, hdc, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, &rc, &dtbg);
  1285. }
  1286. else
  1287. {
  1288. DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT |
  1289. (fUseColorKey? 0 : DFCS_TRANSPARENT));
  1290. }
  1291. OffsetRect(&rc, cxImage, 0);
  1292. // [msadek]; For the mirrored case, there is an off-by-one somewhere in MirrorIcon() or System API.
  1293. // Since I will not be touching MirrorIcon() by any mean and no chance to fix a system API,
  1294. // let's compensate for it here.
  1295. if (fMirror)
  1296. {
  1297. OffsetRect(&rc, -1, 0);
  1298. }
  1299. if (hTheme)
  1300. {
  1301. DrawThemeBackgroundEx(hTheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, &rc, &dtbg);
  1302. }
  1303. else
  1304. {
  1305. DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | DFCS_CHECKED |
  1306. (fUseColorKey? 0 : DFCS_TRANSPARENT));
  1307. }
  1308. SelectObject(hdc, hbmTemp);
  1309. if (fUseColorKey)
  1310. {
  1311. ImageList_AddMasked(himl, hbm, clrMask);
  1312. }
  1313. else
  1314. {
  1315. ImageList_Add(himl, hbm, NULL);
  1316. }
  1317. if (fMirror)
  1318. {
  1319. HICON hIcon = ImageList_ExtractIcon(0, himl, nImages-1);
  1320. MirrorIcon(&hIcon, NULL);
  1321. ImageList_ReplaceIcon(himl, nImages-1, hIcon);
  1322. }
  1323. DeleteDC(hdc);
  1324. DeleteObject(hbm);
  1325. if (hTheme)
  1326. CloseThemeData(hTheme);
  1327. return himl;
  1328. }
  1329. void ListView_InitCheckBoxes(LV* plv, BOOL fInitializeState)
  1330. {
  1331. HIMAGELIST himlCopy = (plv->himlSmall ? plv->himlSmall : plv->himl);
  1332. HIMAGELIST himl;
  1333. BOOL bMirror = FALSE;
  1334. // [msadek], CheckBoxed need not to be mirrored.
  1335. // mirroer it during imagelist creation time so that it displays correctly
  1336. himl = CreateCheckBoxImagelist(himlCopy, FALSE, TRUE, IS_WINDOW_RTL_MIRRORED(plv->ci.hwnd));
  1337. ImageList_SetBkColor(himl, IsUsingCleartype()? (plv->clrBk) : (CLR_NONE));
  1338. ListView_OnSetImageList(plv, himl, LVSIL_STATE);
  1339. if (fInitializeState)
  1340. ListView_OnSetItemState(plv, -1, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK);
  1341. }
  1342. void ListView_PopBubble(LV *plv)
  1343. {
  1344. if (plv->hwndToolTips)
  1345. SendMessage(plv->hwndToolTips, TTM_POP, 0, 0);
  1346. }
  1347. DWORD ListView_ExtendedStyleChange(LV* plv, DWORD dwNewStyle, DWORD dwExMask)
  1348. {
  1349. DWORD dwOldStyle = plv->exStyle;
  1350. // this will change the listview report size and painting algorithm
  1351. // because of the leading edge, so need to re-update scroll bars
  1352. // and repaint everything
  1353. if (ListView_IsReportView(plv))
  1354. {
  1355. ListView_RUpdateScrollBars(plv);
  1356. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1357. }
  1358. // Change of styles may also changes tooltip policy, so pop it
  1359. ListView_PopBubble(plv);
  1360. if (dwExMask)
  1361. dwNewStyle = (plv->exStyle & ~ dwExMask) | (dwNewStyle & dwExMask);
  1362. // Currently, LVS_EX_REGIONAL, LVS_EX_MULTIWORKAREAS, LVS_EX_HIDELABELS, and
  1363. // LVS_EX_SINGLEROW are only supported for large icon view
  1364. if (!ListView_IsIconView(plv))
  1365. {
  1366. dwNewStyle &= ~(LVS_EX_REGIONAL | LVS_EX_MULTIWORKAREAS | LVS_EX_HIDELABELS | LVS_EX_SINGLEROW);
  1367. }
  1368. // LVS_EX_REGIONAL and LVS_EX_SINGLEROW are not supported for ownerdata
  1369. if (ListView_IsOwnerData(plv))
  1370. {
  1371. dwNewStyle &= ~(LVS_EX_REGIONAL | LVS_EX_SINGLEROW);
  1372. }
  1373. plv->exStyle = dwNewStyle;
  1374. // do any invalidation or whatever is needed here.
  1375. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_HIDELABELS)
  1376. {
  1377. plv->rcView.left = RECOMPUTE;
  1378. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1379. }
  1380. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_GRIDLINES)
  1381. {
  1382. if (ListView_IsReportView(plv))
  1383. {
  1384. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1385. }
  1386. }
  1387. if ((dwOldStyle ^ dwNewStyle) & (LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD |
  1388. LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE |
  1389. LVS_EX_SUBITEMIMAGES | LVS_EX_SNAPTOGRID))
  1390. {
  1391. plv->rcView.left = RECOMPUTE;
  1392. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1393. }
  1394. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_CHECKBOXES)
  1395. {
  1396. if (dwNewStyle & LVS_EX_CHECKBOXES)
  1397. {
  1398. ListView_InitCheckBoxes(plv, TRUE);
  1399. }
  1400. else
  1401. {
  1402. // destroy the check boxes!
  1403. HIMAGELIST himl = ListView_OnSetImageList(plv, NULL, LVSIL_STATE);
  1404. if (himl)
  1405. ImageList_Destroy(himl);
  1406. }
  1407. }
  1408. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_FLATSB)
  1409. {
  1410. if (dwNewStyle & LVS_EX_FLATSB)
  1411. {
  1412. InitializeFlatSB(plv->ci.hwnd);
  1413. if (plv->hwndHdr)
  1414. {
  1415. SetWindowBits(plv->hwndHdr, GWL_STYLE, HDS_FLAT, HDS_FLAT);
  1416. }
  1417. }
  1418. else
  1419. {
  1420. if (plv->hwndHdr)
  1421. {
  1422. SetWindowBits(plv->hwndHdr, GWL_STYLE, HDS_FLAT, 0);
  1423. }
  1424. UninitializeFlatSB(plv->ci.hwnd);
  1425. }
  1426. }
  1427. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_REGIONAL)
  1428. {
  1429. g_fSlowMachine = FALSE;
  1430. if (dwNewStyle & LVS_EX_REGIONAL)
  1431. {
  1432. ListView_RecalcRegion(plv, TRUE, TRUE);
  1433. }
  1434. else
  1435. {
  1436. int i;
  1437. LISTITEM * pitem;
  1438. // Delete all the cached regions, then NULL out our selected region.
  1439. for (i = 0; i < ListView_Count(plv); i++)
  1440. {
  1441. pitem = ListView_FastGetItemPtr(plv, i);
  1442. if (pitem->hrgnIcon && pitem->hrgnIcon!=(HANDLE)-1)
  1443. {
  1444. DeleteObject(pitem->hrgnIcon);
  1445. }
  1446. pitem->hrgnIcon = NULL;
  1447. }
  1448. SetWindowRgn(plv->ci.hwnd, (HRGN)NULL, TRUE);
  1449. }
  1450. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1451. }
  1452. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_SINGLEROW)
  1453. {
  1454. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  1455. }
  1456. if (ListView_IsDoubleBuffer(plv))
  1457. {
  1458. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1459. }
  1460. return dwOldStyle;
  1461. }
  1462. // Bug#94368 raymondc v6.0: Doesn't detect WM_WINDOWPOSCHANGING as a way
  1463. // of being shown. NT5 defview has to hack around it pretty grossly.
  1464. // Fix for v6.0.
  1465. void LV_OnShowWindow(LV* plv, BOOL fShow)
  1466. {
  1467. if (fShow)
  1468. {
  1469. if (!(plv->flags & LVF_VISIBLE))
  1470. {
  1471. plv->flags |= LVF_VISIBLE;
  1472. if (plv->fGroupView)
  1473. _ListView_RecomputeEx(plv, NULL, 0, FALSE);
  1474. ListView_UpdateScrollBars(plv);
  1475. }
  1476. }
  1477. else
  1478. plv->flags &= ~LVF_VISIBLE;
  1479. }
  1480. LRESULT ListView_OnHelp(LV* plv, LPHELPINFO lpHelpInfo)
  1481. {
  1482. // If we're seeing WM_HELP because of our child header control, then
  1483. // munge the HELPINFO structure to use the ListView's control id.
  1484. // win\core\user\combo.c has similiar code to handle the child edit
  1485. // control of a combo box.
  1486. if ((lpHelpInfo != NULL) && (plv->wView == LV_VIEW_DETAILS) &&
  1487. (lpHelpInfo->iCtrlId == LVID_HEADER))
  1488. {
  1489. lpHelpInfo->hItemHandle = plv->ci.hwnd;
  1490. lpHelpInfo->iCtrlId = GetWindowID(plv->ci.hwnd);
  1491. // Shouldn't have to do this: USER would have filled in the appropriate
  1492. // context id by walking up the parent hwnd chain.
  1493. //lpHelpInfo->dwContextId = GetContextHelpId(hwnd);
  1494. }
  1495. return DefWindowProc(plv->ci.hwnd, WM_HELP, 0, (LPARAM)lpHelpInfo);
  1496. }
  1497. DWORD ListView_OnSetIconSpacing(LV* plv, LPARAM lParam)
  1498. {
  1499. DWORD dwOld = MAKELONG(plv->cxIconSpacing, plv->cyIconSpacing);
  1500. int cxIconSpacing, cyIconSpacing;
  1501. if (lParam == (LPARAM)-1)
  1502. {
  1503. // go back to using defaults
  1504. plv->flags &= ~LVF_ICONSPACESET;
  1505. cxIconSpacing = (plv->cxIcon + (g_cxIconSpacing - g_cxIcon));
  1506. cyIconSpacing = (plv->cyIcon + (g_cyIconSpacing - g_cyIcon));
  1507. }
  1508. else
  1509. {
  1510. if (LOWORD(lParam))
  1511. {
  1512. cxIconSpacing = LOWORD(lParam);
  1513. if (ListView_IsDPIScaled(plv))
  1514. CCDPIScaleX(&cxIconSpacing);
  1515. }
  1516. else
  1517. {
  1518. cxIconSpacing = plv->cxIconSpacing;
  1519. }
  1520. if (HIWORD(lParam))
  1521. {
  1522. cyIconSpacing = HIWORD(lParam);
  1523. if (ListView_IsDPIScaled(plv))
  1524. CCDPIScaleY(&cyIconSpacing);
  1525. }
  1526. else
  1527. {
  1528. cyIconSpacing = plv->cyIconSpacing;
  1529. }
  1530. plv->flags |= LVF_ICONSPACESET;
  1531. }
  1532. if ((cxIconSpacing != plv->cxIconSpacing) ||
  1533. (cyIconSpacing != plv->cyIconSpacing))
  1534. {
  1535. plv->cxIconSpacing = cxIconSpacing;
  1536. plv->cyIconSpacing = cyIconSpacing;
  1537. plv->rcView.left = RECOMPUTE;
  1538. // Recomputing is necessary except when snap-to-grid is toggled. Snap to grid assumes icon spacing
  1539. // is the grid, however this is the only style that makes this assumption.
  1540. if(!(plv->exStyle & LVS_EX_SNAPTOGRID))
  1541. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  1542. plv->iFreeSlot = -1;
  1543. }
  1544. return dwOld;
  1545. }
  1546. BOOL ListView_OnSetCursorMsg(LV* plv)
  1547. {
  1548. if (plv->exStyle & (LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE))
  1549. {
  1550. if (plv->iHot != -1)
  1551. {
  1552. if (((plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK)) ||
  1553. ListView_OnGetItemState(plv, plv->iHot, LVIS_SELECTED))
  1554. {
  1555. if (!plv->hCurHot)
  1556. {
  1557. plv->hCurHot = LoadCursor(NULL, IDC_HAND);
  1558. }
  1559. SetCursor(plv->hCurHot);
  1560. return TRUE;
  1561. }
  1562. }
  1563. }
  1564. return FALSE;
  1565. }
  1566. void ListView_OnSetHotItem(LV* plv, int iItem)
  1567. {
  1568. if (iItem != plv->iHot)
  1569. {
  1570. if ((plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  1571. (plv->exStyle & LVS_EX_TWOCLICKACTIVATE)) // We only change visuals for hot with Underline
  1572. {
  1573. BOOL fSelectOnly;
  1574. UINT uInvalidateFlags = RDW_INVALIDATE;
  1575. BOOL fBlended = FALSE;
  1576. // Check to see if the item we are making not is in a blended state
  1577. if (iItem != -1)
  1578. {
  1579. // Cut is blended so we need to erase...
  1580. fBlended = ListView_OnGetItemState(plv, iItem, LVIS_CUT);
  1581. if (!fBlended)
  1582. fBlended = ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  1583. }
  1584. // If we need to erase either one then we erase both.
  1585. if (plv->iHot != -1 && ListView_IsValidItemNumber(plv, plv->iHot) && !fBlended)
  1586. {
  1587. // Cut is blended so we need to erase...
  1588. fBlended = ListView_OnGetItemState(plv, plv->iHot, LVIS_CUT);
  1589. if (!fBlended)
  1590. fBlended = ListView_OnGetItemState(plv, plv->iHot, LVIS_SELECTED);
  1591. }
  1592. if (ImageList_GetFlags(plv->himl) & ILC_COLOR32)
  1593. fBlended = TRUE;
  1594. // Affects only apply if double buffering
  1595. if (ListView_IsDoubleBuffer(plv) ||
  1596. plv->fListviewShadowText ||
  1597. fBlended)
  1598. {
  1599. uInvalidateFlags |= RDW_ERASE;
  1600. }
  1601. fSelectOnly = ListView_FullRowSelect(plv);
  1602. ListView_InvalidateItemEx(plv, plv->iHot, fSelectOnly, uInvalidateFlags, LVIF_TEXT | LVIF_IMAGE);
  1603. ListView_InvalidateItemEx(plv, iItem, fSelectOnly, uInvalidateFlags, LVIF_TEXT | LVIF_IMAGE);
  1604. }
  1605. plv->iHot = iItem;
  1606. }
  1607. }
  1608. BOOL fShouldFirstClickActivate()
  1609. {
  1610. static BOOL fInited = FALSE;
  1611. static BOOL fActivate = TRUE;
  1612. if (!fInited)
  1613. {
  1614. long cb = 0;
  1615. if (RegQueryValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\NoFirstClickActivate"),
  1616. NULL, &cb) == ERROR_SUCCESS)
  1617. fActivate = FALSE;
  1618. fInited = TRUE;
  1619. }
  1620. return fActivate;
  1621. }
  1622. BOOL ChildOfDesktop(HWND hwnd)
  1623. {
  1624. return IsChild(GetShellWindow(), hwnd);
  1625. }
  1626. void ListView_OnMouseMove(LV* plv, int x, int y, UINT uFlags)
  1627. {
  1628. int iItem;
  1629. LV_HITTESTINFO ht;
  1630. NMLISTVIEW nm;
  1631. ht.pt.x = x;
  1632. ht.pt.y = y;
  1633. iItem = ListView_OnSubItemHitTest(plv, &ht);
  1634. if (ht.iSubItem != 0)
  1635. {
  1636. // if we're not in full row select,
  1637. // hitting on a subitem is like hitting on nowhere
  1638. // also, in win95, ownerdraw fixed effectively had full row select
  1639. if (!ListView_FullRowSelect(plv) &&
  1640. !(plv->ci.style & LVS_OWNERDRAWFIXED))
  1641. {
  1642. iItem = -1;
  1643. ht.flags = LVHT_NOWHERE;
  1644. }
  1645. }
  1646. if (ht.flags & LVHT_NOWHERE ||
  1647. ht.flags & LVHT_ONITEMSTATEICON)
  1648. {
  1649. iItem = -1; // this is possible in the list mode (sigh)
  1650. }
  1651. nm.iItem = iItem;
  1652. nm.iSubItem = ht.iSubItem;
  1653. nm.uChanged = 0;
  1654. nm.ptAction.x = x;
  1655. nm.ptAction.y = y;
  1656. if (!CCSendNotify(&plv->ci, LVN_HOTTRACK, &nm.hdr))
  1657. {
  1658. #ifdef DEBUG
  1659. if ((nm.iItem != -1) && nm.iSubItem != 0)
  1660. nm.iItem = -1;
  1661. #endif
  1662. ListView_OnSetHotItem(plv, nm.iItem);
  1663. // Ensure our cursor is correct now since the WM_SETCURSOR
  1664. // message was already generated for this mouse event.
  1665. ListView_OnSetCursorMsg(plv);
  1666. // this lets us know when we've left an item
  1667. // and can then reselect/toggle it on hover events
  1668. if (iItem != plv->iNoHover)
  1669. {
  1670. plv->iNoHover = -1;
  1671. }
  1672. }
  1673. }
  1674. BOOL EditBoxHasFocus()
  1675. {
  1676. HWND hwndFocus = GetFocus();
  1677. if (hwndFocus)
  1678. {
  1679. if (SendMessage(hwndFocus, WM_GETDLGCODE, 0, 0) & DLGC_HASSETSEL)
  1680. return TRUE;
  1681. }
  1682. return FALSE;
  1683. }
  1684. void ListView_OnMouseHover(LV* plv, int x, int y, UINT uFlags)
  1685. {
  1686. int iItem;
  1687. BOOL bSelected;
  1688. LV_HITTESTINFO ht;
  1689. BOOL fControl;
  1690. BOOL fShift;
  1691. BOOL fNotifyReturn = FALSE;
  1692. if (GetCapture() || !ChildOfActiveWindow(plv->ci.hwnd) ||
  1693. EditBoxHasFocus())
  1694. return; // ignore hover while editing or any captured (d/d) operation
  1695. if (CCSendNotify(&plv->ci, NM_HOVER, NULL))
  1696. {
  1697. return;
  1698. }
  1699. // REVIEW: right button implies no shift or control stuff
  1700. // Single selection style also implies no modifiers
  1701. //if (RIGHTBUTTON(keyFlags) || (plv->ci.style & LVS_SINGLESEL))
  1702. if ((plv->ci.style & LVS_SINGLESEL))
  1703. {
  1704. fControl = FALSE;
  1705. fShift = FALSE;
  1706. }
  1707. else
  1708. {
  1709. fControl = GetAsyncKeyState(VK_CONTROL) < 0;
  1710. fShift = GetAsyncKeyState(VK_SHIFT) < 0;
  1711. }
  1712. ht.pt.x = x;
  1713. ht.pt.y = y;
  1714. iItem = ListView_OnHitTest(plv, &ht);
  1715. if (iItem == -1 ||
  1716. iItem == plv->iNoHover)
  1717. return;
  1718. //before we hover select we launch any pending item
  1719. //this prevents clicking on one item and hover selecting other before
  1720. //the timer goes off which result in wrong item being launched
  1721. if (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickHappened && plv->fOneClickOK)
  1722. {
  1723. HWND hwnd = plv->ci.hwnd;
  1724. KillTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED);
  1725. plv->fOneClickHappened = FALSE;
  1726. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  1727. if (!IsWindow(hwnd))
  1728. return;
  1729. }
  1730. plv->iNoHover = iItem;
  1731. bSelected = ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  1732. if (ht.flags & (LVHT_ONITEMLABEL | LVHT_ONITEMICON))
  1733. {
  1734. UINT keyFlags = 0;
  1735. if (fShift)
  1736. keyFlags |= MK_SHIFT;
  1737. if (fControl)
  1738. keyFlags |= MK_CONTROL;
  1739. if (!bSelected)
  1740. {
  1741. // if it wasn't selected, we're about to select it... play
  1742. // a little ditty for us...
  1743. CCPlaySound(c_szSelect);
  1744. }
  1745. ListView_ButtonSelect(plv, iItem, keyFlags, bSelected);
  1746. if (fControl)
  1747. {
  1748. ListView_SetFocusSel(plv, iItem, !fShift, FALSE, !fShift);
  1749. }
  1750. if (!fShift)
  1751. plv->iMark = iItem;
  1752. ListView_OnSetCursorMsg(plv);
  1753. SetFocus(plv->ci.hwnd); // activate this window
  1754. }
  1755. }
  1756. BOOL EqualRects(LPRECT prcNew, LPRECT prcOld, int nRects)
  1757. {
  1758. int i;
  1759. for (i = 0; i < nRects; i++)
  1760. if (!EqualRect(&prcNew[i], &prcOld[i]))
  1761. return FALSE;
  1762. return TRUE;
  1763. }
  1764. BOOL ListView_FindWorkArea(LV * plv, POINT pt, short * piWorkArea)
  1765. {
  1766. int iWork;
  1767. for (iWork = 0; iWork < plv->nWorkAreas; iWork++)
  1768. {
  1769. if (PtInRect(&plv->prcWorkAreas[iWork], pt))
  1770. {
  1771. *piWorkArea = (short)iWork;
  1772. return TRUE;
  1773. }
  1774. }
  1775. // (dli) default case is the primary work area
  1776. *piWorkArea = 0;
  1777. return FALSE;
  1778. }
  1779. void ListView_BullyIconsOnWorkarea(LV * plv, HDPA hdpaLostItems)
  1780. {
  1781. int ihdpa;
  1782. int iFree = -1; // the last free slot number
  1783. LVFAKEDRAW lvfd;
  1784. LV_ITEM item;
  1785. // Caller should've filtered this case out
  1786. ASSERT(DPA_GetPtrCount(hdpaLostItems) > 0);
  1787. // Set up in case caller is customdraw
  1788. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  1789. item.mask = LVIF_PARAM;
  1790. item.iSubItem = 0;
  1791. // Go through my hdpa list of lost icons and try to place them within bound
  1792. for (ihdpa = 0; ihdpa < DPA_GetPtrCount(hdpaLostItems); ihdpa++)
  1793. {
  1794. POINT ptNew, pt;
  1795. RECT rcBound = {0};
  1796. int cxBound, cyBound;
  1797. int iWidth, iHeight;
  1798. int iItem;
  1799. LISTITEM * pitem;
  1800. iItem = PtrToUlong(DPA_GetPtr(hdpaLostItems, ihdpa));
  1801. pitem = ListView_FastGetItemPtr(plv, iItem);
  1802. pt = pitem->pt;
  1803. iWidth = RECTWIDTH(plv->prcWorkAreas[pitem->iWorkArea]);
  1804. iHeight = RECTHEIGHT(plv->prcWorkAreas[pitem->iWorkArea]);
  1805. ListView_GetRects(plv, iItem, QUERY_DEFAULT, NULL, NULL, &rcBound, NULL);
  1806. cxBound = RECTWIDTH(rcBound);
  1807. cyBound = RECTHEIGHT(rcBound);
  1808. pt.x -= plv->prcWorkAreas[pitem->iWorkArea].left;
  1809. pt.y -= plv->prcWorkAreas[pitem->iWorkArea].top;
  1810. if (pt.x < (-cxBound / 2))
  1811. {
  1812. ptNew.x = 0;
  1813. }
  1814. else if (pt.x > (iWidth - (cxBound / 2)))
  1815. {
  1816. ptNew.x = iWidth - cxBound;
  1817. }
  1818. else
  1819. ptNew.x = pt.x;
  1820. if (pt.y < (-cyBound/2))
  1821. {
  1822. ptNew.y = 0;
  1823. }
  1824. else if (pt.y > (iHeight - (cyBound / 2)))
  1825. {
  1826. ptNew.y = iHeight - cyBound;
  1827. }
  1828. else
  1829. ptNew.y = pt.y;
  1830. if ((ptNew.x != pt.x) || (ptNew.y != pt.y))
  1831. {
  1832. BOOL fUpdate;
  1833. RECT rcTest;
  1834. ptNew.x += plv->prcWorkAreas[pitem->iWorkArea].left;
  1835. ptNew.y += plv->prcWorkAreas[pitem->iWorkArea].top;
  1836. // See if the potential rectangle intersects other items.
  1837. rcTest.left = ptNew.x - plv->ptOrigin.x;
  1838. rcTest.top = ptNew.y - plv->ptOrigin.y;
  1839. rcTest.right = rcTest.left + cxBound;
  1840. rcTest.bottom = rcTest.top + cyBound;
  1841. item.iItem = iItem;
  1842. item.lParam = pitem->lParam;
  1843. ListView_BeginFakeItemDraw(&lvfd);
  1844. if (!ListView_IsCleanRect(plv, &rcTest, iItem, QUERY_DEFAULT, &fUpdate, lvfd.nmcd.nmcd.hdc))
  1845. {
  1846. // doh! We hit another item, let's try to find an available location
  1847. // for this item
  1848. BOOL fUpdateSB;
  1849. BOOL fAppendAtEnd = FALSE;
  1850. int iWidth, iHeight;
  1851. int cSlots = ListView_GetSlotCountEx(plv, FALSE, pitem->iWorkArea, &iWidth, &iHeight);
  1852. iFree = ListView_FindFreeSlot(plv, iItem, iFree + 1, cSlots, QUERY_DEFAULT, &fUpdateSB, &fAppendAtEnd, lvfd.nmcd.nmcd.hdc, iWidth, iHeight);
  1853. if (iFree == -1)
  1854. goto SetFirstGuess;
  1855. ListView_SetIconPos(plv, pitem, iFree, cSlots);
  1856. ListView_EndFakeItemDraw(&lvfd);
  1857. continue;
  1858. }
  1859. SetFirstGuess:
  1860. ListView_EndFakeItemDraw(&lvfd);
  1861. ListView_OnSetItemPosition(plv, iItem, ptNew.x, ptNew.y);
  1862. }
  1863. }
  1864. ListView_EndFakeCustomDraw(&lvfd);
  1865. }
  1866. #define DPA_LAST 0x7fffffff
  1867. //
  1868. // ListView_OnSetWorkAreas
  1869. //
  1870. // set the "work areas" for the list view.
  1871. // the "work areas" are a group of sub rectanges of the list view client rect
  1872. // where icons are aranged, and parked by default.
  1873. //
  1874. void ListView_OnSetWorkAreas(LV* plv, int nWorkAreas, LPRECT prc)
  1875. {
  1876. int iWork;
  1877. HDPA hdpaLostItems = NULL;
  1878. RECT rcOldWorkAreas[LV_MAX_WORKAREAS];
  1879. BOOL bAutoArrange = plv->ci.style & LVS_AUTOARRANGE;
  1880. int nOldWorkAreas = plv->nWorkAreas;
  1881. if (nOldWorkAreas > 0)
  1882. {
  1883. ASSERT(plv->prcWorkAreas != NULL);
  1884. memcpy(&rcOldWorkAreas[0], &plv->prcWorkAreas[0], sizeof(RECT) * nOldWorkAreas);
  1885. }
  1886. // for the mirrored case, the coordinates are reversed. IsRectEmpty() will always succeed
  1887. if (nWorkAreas == 0 || prc == NULL || ((IsRectEmpty(prc)) && !(plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)))
  1888. plv->nWorkAreas = 0;
  1889. else
  1890. {
  1891. plv->nWorkAreas = min(nWorkAreas, LV_MAX_WORKAREAS);
  1892. if (plv->prcWorkAreas == NULL)
  1893. plv->prcWorkAreas = (LPRECT)LocalAlloc(LPTR, sizeof(RECT) * LV_MAX_WORKAREAS);
  1894. if (plv->prcWorkAreas == NULL)
  1895. return;
  1896. //Should we check if they intersect? This problem is sort of
  1897. // solved (or made more confusing) by ListView_GetFreeSlot since it checks all of the icons for
  1898. // intersection instead of just the ones in the workarea.
  1899. for (iWork = 0; iWork < plv->nWorkAreas; iWork++)
  1900. CopyRect(&plv->prcWorkAreas[iWork], &prc[iWork]);
  1901. }
  1902. // We don't support workareas for owner-data because our icon placement
  1903. // algorithm (ListView_IGetRectsOwnerData) completely ignores workareas
  1904. // and just dumps the icons in a rectangular array starting at (0,0).
  1905. if (!ListView_IsOwnerData(plv) &&
  1906. plv->nWorkAreas > 0 &&
  1907. ((plv->nWorkAreas != nOldWorkAreas) ||
  1908. (!EqualRects(&plv->prcWorkAreas[0], &rcOldWorkAreas[0], nOldWorkAreas))))
  1909. {
  1910. int iItem;
  1911. LISTITEM * pitem;
  1912. //
  1913. // Subtle - ListView_Recompute cleans up all the RECOMPUTE icons,
  1914. // but in order to do that, it needs to have valid work area
  1915. // rectangles. So the call must happen after the CopyRect but before
  1916. // the loop that checks the icon positions.
  1917. //
  1918. ListView_Recompute(plv);
  1919. for (iItem = 0; iItem < ListView_Count(plv); iItem++)
  1920. {
  1921. pitem = ListView_FastGetItemPtr(plv, iItem);
  1922. if (pitem->pt.x == RECOMPUTE || pitem->pt.y == RECOMPUTE)
  1923. {
  1924. // ListView_Recompute should've fixed these if we were in
  1925. // an iconical view.
  1926. ASSERT(!(ListView_IsIconView(plv) || ListView_IsSmallView(plv)));
  1927. continue;
  1928. }
  1929. // Try to move me to the same location relative to the same workarea.
  1930. // This will give the cool shift effect when tools bars take the border areas.
  1931. // And we only want to do this for the workareas that changed
  1932. // Don't bully the icons on the workareas, Autoarrange will do the work for us
  1933. if (nOldWorkAreas > 0)
  1934. {
  1935. int iOldWorkArea;
  1936. iOldWorkArea = pitem->iWorkArea;
  1937. if (iOldWorkArea >= plv->nWorkAreas)
  1938. {
  1939. // My workarea is gone, put me on the primary workarea i.e. #0
  1940. pitem->iWorkArea = 0;
  1941. if (!bAutoArrange)
  1942. {
  1943. // If this item point location is already in the new primary workarea,
  1944. // move it out, and let ListView_BullyIconsOnWorkarea arrange it to the
  1945. // right place. NOTE: this could happen in the case the old secondary monitor
  1946. // is to the left of the old primary monitor, and user kills the secondary monitor
  1947. if (PtInRect(&plv->prcWorkAreas[0], pitem->pt))
  1948. {
  1949. pitem->pt.x = plv->prcWorkAreas[0].right + 1;
  1950. plv->iFreeSlot = -1; // an item moved -- old slot info is invalid
  1951. }
  1952. goto InsertLostItemsArray;
  1953. }
  1954. }
  1955. else if ((!bAutoArrange) && (!EqualRect(&plv->prcWorkAreas[iOldWorkArea], &rcOldWorkAreas[iOldWorkArea])))
  1956. {
  1957. RECT rcBound = {0};
  1958. POINT ptCenter;
  1959. pitem->pt.x += plv->prcWorkAreas[iOldWorkArea].left - rcOldWorkAreas[iOldWorkArea].left;
  1960. pitem->pt.y += plv->prcWorkAreas[iOldWorkArea].top - rcOldWorkAreas[iOldWorkArea].top;
  1961. // Use the center of this icon to determine whether it's out of bound
  1962. ListView_GetRects(plv, iItem, QUERY_DEFAULT, NULL, NULL, &rcBound, NULL);
  1963. ptCenter.x = pitem->pt.x + RECTWIDTH(rcBound) / 2;
  1964. ptCenter.y = pitem->pt.y + RECTHEIGHT(rcBound) / 2;
  1965. // If this shifted me out of bounds, register to be bullied on the workarea
  1966. if (!PtInRect(&plv->prcWorkAreas[iOldWorkArea], ptCenter))
  1967. {
  1968. InsertLostItemsArray:
  1969. if (!hdpaLostItems)
  1970. {
  1971. hdpaLostItems = DPA_Create(4);
  1972. }
  1973. if (hdpaLostItems)
  1974. DPA_InsertPtr(hdpaLostItems, DPA_LAST, IntToPtr(iItem));
  1975. }
  1976. }
  1977. }
  1978. else
  1979. {
  1980. // My first time in a multi-workarea system, so find out my workarea
  1981. if (!ListView_FindWorkArea(plv, pitem->pt, &(pitem->iWorkArea)) && !bAutoArrange)
  1982. goto InsertLostItemsArray;
  1983. }
  1984. if ((plv->exStyle & LVS_EX_REGIONAL) && (pitem->hrgnIcon))
  1985. {
  1986. if (pitem->hrgnIcon != (HANDLE)-1)
  1987. DeleteObject(pitem->hrgnIcon);
  1988. pitem->hrgnIcon = NULL;
  1989. }
  1990. }
  1991. if (hdpaLostItems)
  1992. {
  1993. ASSERT(!bAutoArrange);
  1994. if (DPA_GetPtrCount(hdpaLostItems) > 0)
  1995. ListView_BullyIconsOnWorkarea(plv, hdpaLostItems);
  1996. DPA_Destroy(hdpaLostItems);
  1997. }
  1998. if (plv->exStyle & LVS_EX_REGIONAL)
  1999. ListView_RecalcRegion(plv, TRUE, TRUE);
  2000. if (ListView_IsSmallView(plv) || ListView_IsIconView(plv))
  2001. ListView_ArrangeOrSnapToGrid(plv);
  2002. }
  2003. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  2004. }
  2005. void ListView_OnGetNumberOfWorkAreas(LV* plv, int * pnWorkAreas)
  2006. {
  2007. if (pnWorkAreas)
  2008. *pnWorkAreas = plv->nWorkAreas;
  2009. }
  2010. void ListView_OnGetWorkAreas(LV* plv, int nWorkAreas, LPRECT prc)
  2011. {
  2012. int i;
  2013. if (prc == NULL)
  2014. return;
  2015. for (i = 0; i < min(plv->nWorkAreas, nWorkAreas); i++)
  2016. {
  2017. if (i < plv->nWorkAreas)
  2018. CopyRect(&prc[i], &plv->prcWorkAreas[i]);
  2019. else
  2020. // Set the workareas to all zeros if we don't have it.
  2021. ZeroMemory(&prc[i], sizeof(RECT));
  2022. }
  2023. }
  2024. // test an item to see if it is unfolded (because it is focused)
  2025. BOOL ListView_IsItemUnfolded(LV *plv, int item)
  2026. {
  2027. return plv && (item >= 0) && ListView_IsIconView(plv) &&
  2028. (plv->flags & LVF_UNFOLDED) && (plv->iFocus == item);
  2029. }
  2030. BOOL ListView_IsItemUnfoldedPtr(LV *plv, LISTITEM *pitem)
  2031. {
  2032. return plv && pitem && ListView_IsIconView(plv) &&
  2033. (plv->flags & LVF_UNFOLDED) && (pitem->state & LVIS_FOCUSED);
  2034. }
  2035. // Returns TRUE if unfolding the item will be worthwhile
  2036. BOOL ListView_GetUnfoldedRect(LV* plv, int iItem, RECT *prc)
  2037. {
  2038. ListView_GetRects(plv, iItem, QUERY_DEFAULT, NULL, prc, NULL, NULL);
  2039. return ListView_UnfoldRects(plv, iItem, NULL, prc, NULL, NULL);
  2040. }
  2041. BOOL ListView_OnSetGroupInfoInternal(LV* plv, PLVGROUP plvgrp, LISTGROUP* pgrp)
  2042. {
  2043. if (plvgrp == NULL ||
  2044. plvgrp->cbSize < sizeof(LVGROUP))
  2045. {
  2046. return FALSE;
  2047. }
  2048. if (plvgrp->mask & LVGF_STATE)
  2049. {
  2050. if ((plvgrp->state & LVGS_MASK) != plvgrp->state)
  2051. return FALSE;
  2052. pgrp->state = plvgrp->state;
  2053. }
  2054. if (plvgrp->mask & LVGF_ALIGN)
  2055. {
  2056. if ((plvgrp->uAlign & LVGA_ALIGN_MASK) != plvgrp->uAlign)
  2057. return FALSE;
  2058. pgrp->uAlign = plvgrp->uAlign;
  2059. }
  2060. else
  2061. {
  2062. pgrp->uAlign = LVGA_HEADER_LEFT;
  2063. }
  2064. if (ListView_VerifyGroupIdIsUnique(plv, plvgrp->iGroupId))
  2065. {
  2066. pgrp->iGroupId = plvgrp->iGroupId;
  2067. }
  2068. else
  2069. {
  2070. return FALSE;
  2071. }
  2072. if (plvgrp->mask & LVGF_HEADER)
  2073. {
  2074. Str_SetPtr(&pgrp->pszHeader, plvgrp->pszHeader);
  2075. }
  2076. if (plvgrp->mask & LVGF_FOOTER)
  2077. {
  2078. Str_SetPtr(&pgrp->pszFooter, plvgrp->pszFooter);
  2079. }
  2080. // Update the group.
  2081. InvalidateRect(plv->ci.hwnd, &pgrp->rc, TRUE);
  2082. return TRUE;
  2083. }
  2084. int ListView_OnSetGroupInfo(LV* plv, int iGroupId, PLVGROUP plvgrp)
  2085. {
  2086. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, iGroupId, NULL);
  2087. if (pgrp)
  2088. {
  2089. ListView_OnSetGroupInfoInternal(plv, plvgrp, pgrp);
  2090. return iGroupId;
  2091. }
  2092. return -1;
  2093. }
  2094. int ListView_OnGetGroupInfo(LV* plv, int iGroupId, PLVGROUP plvgrp)
  2095. {
  2096. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, iGroupId, NULL);
  2097. if (plvgrp != NULL &&
  2098. plvgrp->cbSize >= sizeof(LVGROUP) &&
  2099. pgrp)
  2100. {
  2101. if (plvgrp->mask & LVGF_HEADER)
  2102. {
  2103. plvgrp->pszHeader = pgrp->pszHeader;
  2104. }
  2105. if (plvgrp->mask & LVGF_FOOTER)
  2106. {
  2107. plvgrp->pszFooter = pgrp->pszFooter;
  2108. }
  2109. if (plvgrp->mask & LVGF_STATE)
  2110. {
  2111. plvgrp->state = pgrp->state & plvgrp->stateMask;
  2112. }
  2113. if (plvgrp->mask & LVGF_ALIGN)
  2114. {
  2115. plvgrp->uAlign = pgrp->uAlign;
  2116. }
  2117. if (plvgrp->mask & LVGF_GROUPID)
  2118. {
  2119. plvgrp->iGroupId = pgrp->iGroupId;
  2120. }
  2121. return iGroupId;
  2122. }
  2123. return -1;
  2124. }
  2125. LISTGROUP* ListView_CreateGroup(LV* plv, PLVGROUP plvgrp)
  2126. {
  2127. LISTGROUP* pgrp;
  2128. // Validate Group
  2129. if (plvgrp == NULL ||
  2130. plvgrp->cbSize < sizeof(LVGROUP))
  2131. {
  2132. return NULL;
  2133. }
  2134. if (!(plvgrp->mask & LVGF_GROUPID))
  2135. {
  2136. // Have to have a group id...
  2137. return NULL;
  2138. }
  2139. pgrp = LocalAlloc(LPTR, sizeof(LISTGROUP));
  2140. if (pgrp)
  2141. {
  2142. if (!ListView_OnSetGroupInfoInternal(plv, plvgrp, pgrp))
  2143. {
  2144. LocalFree(pgrp);
  2145. return NULL;
  2146. }
  2147. pgrp->hdpa = DPA_Create(5);
  2148. SetRect(&pgrp->rc, 0, 0, 0, 0);
  2149. }
  2150. return pgrp;
  2151. }
  2152. void ListView_FreeGroupItem(LISTGROUP* pgrp)
  2153. {
  2154. DPA_Destroy(pgrp->hdpa);
  2155. Str_SetPtr(&pgrp->pszFooter, NULL);
  2156. Str_SetPtr(&pgrp->pszHeader, NULL);
  2157. LocalFree(pgrp);
  2158. }
  2159. LISTGROUP* ListView_FindFirstVisibleGroup(LV* plv)
  2160. {
  2161. LISTGROUP* pgrp = NULL;
  2162. int iGroup;
  2163. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  2164. // Find the first group with an item in it.
  2165. for (iGroup = 0; iGroup < cGroups; iGroup++)
  2166. {
  2167. pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  2168. if (DPA_GetPtrCount(pgrp->hdpa) > 0)
  2169. break;
  2170. }
  2171. return pgrp;
  2172. }
  2173. LRESULT ListView_OnInsertGroup(LV* plv, int iGroup, PLVGROUP plvgrp)
  2174. {
  2175. int iInsert = -1;
  2176. LISTGROUP* pgrp = ListView_CreateGroup(plv, plvgrp);
  2177. if (!pgrp)
  2178. {
  2179. return -1;
  2180. }
  2181. if (iGroup == -1)
  2182. {
  2183. iGroup = DA_LAST;
  2184. }
  2185. if (plv->hdpaGroups == NULL)
  2186. plv->hdpaGroups = DPA_Create(4);
  2187. if (plv->hdpaGroups)
  2188. iInsert = DPA_InsertPtr(plv->hdpaGroups, iGroup, pgrp);
  2189. if (iInsert == -1)
  2190. {
  2191. ListView_FreeGroupItem(pgrp);
  2192. }
  2193. plv->rcView.left = RECOMPUTE;
  2194. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2195. return iInsert;
  2196. }
  2197. LRESULT ListView_OnRemoveGroup(LV* plv, int iGroupId)
  2198. {
  2199. int iIndex;
  2200. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, iGroupId, &iIndex);
  2201. if (pgrp)
  2202. {
  2203. int cItems = DPA_GetPtrCount(pgrp->hdpa);
  2204. int iItem;
  2205. for (iItem = 0; iItem < cItems; iItem++)
  2206. {
  2207. LISTITEM* pitem = DPA_FastGetPtr(pgrp->hdpa, iItem);
  2208. if (pitem)
  2209. {
  2210. LISTITEM_SETHASNOTASKEDFORGROUP(pitem);
  2211. }
  2212. }
  2213. ListView_FreeGroupItem(pgrp);
  2214. DPA_DeletePtr(plv->hdpaGroups, iIndex);
  2215. plv->rcView.left = RECOMPUTE;
  2216. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2217. return iIndex;
  2218. }
  2219. return -1;
  2220. }
  2221. int CALLBACK DestroyGroups(void* pv, void* pvData)
  2222. {
  2223. LISTGROUP* pgrp = (LISTGROUP*)pv;
  2224. ListView_FreeGroupItem(pgrp);
  2225. return 1;
  2226. }
  2227. LRESULT ListView_OnRemoveAllGroups(LV* plv)
  2228. {
  2229. if (!ListView_IsOwnerData(plv) && plv->hdpaGroups)
  2230. {
  2231. int i;
  2232. int cItems = ListView_Count(plv);
  2233. plv->fGroupView = FALSE;
  2234. for (i = 0; i < cItems; i++)
  2235. {
  2236. LISTITEM* pitem = DPA_FastGetPtr(plv->hdpa, i);
  2237. if (pitem)
  2238. LISTITEM_SETHASNOTASKEDFORGROUP(pitem);
  2239. }
  2240. DPA_DestroyCallback(plv->hdpaGroups, DestroyGroups, NULL);
  2241. plv->hdpaGroups = NULL;
  2242. plv->rcView.left = RECOMPUTE;
  2243. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2244. }
  2245. return 1;
  2246. }
  2247. LRESULT ListView_OnSetGroupMetrics(LV* plv, PLVGROUPMETRICS pgm)
  2248. {
  2249. BOOL fRecompute = FALSE;
  2250. if (pgm->mask & LVGMF_BORDERSIZE)
  2251. {
  2252. plv->rcBorder.left = pgm->Left;
  2253. plv->rcBorder.top = pgm->Top;
  2254. plv->rcBorder.right = pgm->Right;
  2255. plv->rcBorder.bottom = pgm->Bottom;
  2256. fRecompute = TRUE;
  2257. }
  2258. if (pgm->mask & LVGMF_BORDERCOLOR)
  2259. {
  2260. plv->crTop = pgm->crTop;
  2261. plv->crLeft = pgm->crLeft;
  2262. plv->crRight = pgm->crRight;
  2263. plv->crBottom = pgm->crBottom;
  2264. }
  2265. if (pgm->mask & LVGMF_TEXTCOLOR)
  2266. {
  2267. plv->crHeader = pgm->crHeader;
  2268. plv->crFooter = pgm->crFooter;
  2269. }
  2270. if (fRecompute)
  2271. {
  2272. plv->rcView.left = RECOMPUTE;
  2273. ListView_Recompute(plv);
  2274. ListView_UpdateScrollBars(plv);
  2275. }
  2276. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2277. return 1;
  2278. }
  2279. LRESULT ListView_OnGetGroupMetrics(LV* plv, PLVGROUPMETRICS pgm)
  2280. {
  2281. if (pgm->mask & LVGMF_BORDERSIZE)
  2282. {
  2283. pgm->Left = plv->rcBorder.left;
  2284. pgm->Top = plv->rcBorder.top;
  2285. pgm->Right = plv->rcBorder.right;
  2286. pgm->Bottom = plv->rcBorder.bottom;
  2287. }
  2288. if (pgm->mask & LVGMF_BORDERCOLOR)
  2289. {
  2290. pgm->crTop = plv->crTop;
  2291. pgm->crLeft = plv->crLeft;
  2292. pgm->crRight = plv->crRight;
  2293. pgm->crBottom = plv->crBottom;
  2294. }
  2295. if (pgm->mask & LVGMF_TEXTCOLOR)
  2296. {
  2297. pgm->crHeader = plv->crHeader;
  2298. pgm->crFooter= plv->crFooter;
  2299. }
  2300. return 1;
  2301. }
  2302. typedef struct
  2303. {
  2304. PFNLVGROUPCOMPARE pfnCompare;
  2305. void * pvData;
  2306. } SORTGROUPDATA;
  2307. int CALLBACK pfnGroupSort(LPARAM one, LPARAM two, LPARAM pvData)
  2308. {
  2309. SORTGROUPDATA* psg = (SORTGROUPDATA*)pvData;
  2310. LISTGROUP* pgrp1 = (LISTGROUP*)one;
  2311. LISTGROUP* pgrp2 = (LISTGROUP*)two;
  2312. if (!one)
  2313. return 1;
  2314. if (!two)
  2315. return -1;
  2316. return psg->pfnCompare(pgrp1->iGroupId, pgrp2->iGroupId, psg->pvData);
  2317. }
  2318. LRESULT ListView_OnSortGroups(LV* plv, PFNLVGROUPCOMPARE pfnCompare, void * pvData)
  2319. {
  2320. if (plv->hdpaGroups)
  2321. {
  2322. SORTGROUPDATA sg;
  2323. sg.pfnCompare = pfnCompare;
  2324. sg.pvData = pvData;
  2325. DPA_Sort(plv->hdpaGroups, (PFNDPACOMPARE)pfnGroupSort, (LPARAM)&sg);
  2326. plv->rcView.left = RECOMPUTE;
  2327. ListView_Recompute(plv);
  2328. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2329. ListView_UpdateScrollBars(plv);
  2330. return 1;
  2331. }
  2332. return 0;
  2333. }
  2334. LRESULT ListView_OnInsertGroupSorted(LV* plv, LVINSERTGROUPSORTED* pinsert)
  2335. {
  2336. int iInsertIndex = -1;
  2337. SORTGROUPDATA sg;
  2338. LISTGROUP* pgrp = ListView_CreateGroup(plv, &pinsert->lvGroup);
  2339. sg.pfnCompare = pinsert->pfnGroupCompare;
  2340. sg.pvData = pinsert->pvData;
  2341. if (pgrp)
  2342. {
  2343. if (plv->hdpaGroups == NULL)
  2344. plv->hdpaGroups = DPA_Create(4);
  2345. if (plv->hdpaGroups)
  2346. {
  2347. iInsertIndex = DPA_SortedInsertPtr(plv->hdpaGroups, pgrp, 0, (PFNDPACOMPARE)pfnGroupSort,
  2348. (LPARAM)&sg, DPAS_INSERTAFTER, pgrp);
  2349. }
  2350. if (iInsertIndex == -1)
  2351. ListView_FreeGroupItem(pgrp);
  2352. plv->rcView.left = RECOMPUTE;
  2353. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2354. }
  2355. return iInsertIndex;
  2356. }
  2357. BOOL ListView_OnSetTileViewInfo(LV* plv, PLVTILEVIEWINFO pTileViewInfo)
  2358. {
  2359. BOOL bRecompute = FALSE;
  2360. if (!pTileViewInfo || (pTileViewInfo->cbSize != sizeof(LVTILEVIEWINFO)))
  2361. return FALSE;
  2362. if (pTileViewInfo->dwMask & LVTVIM_COLUMNS)
  2363. {
  2364. if (plv->cSubItems != pTileViewInfo->cLines)
  2365. {
  2366. bRecompute = TRUE;
  2367. plv->cSubItems = pTileViewInfo->cLines;
  2368. }
  2369. }
  2370. if (pTileViewInfo->dwMask & LVTVIM_TILESIZE)
  2371. {
  2372. DWORD dwTileFlags = pTileViewInfo->dwFlags & (LVTVIF_FIXEDHEIGHT | LVTVIF_FIXEDWIDTH);
  2373. if (plv->dwTileFlags != dwTileFlags)
  2374. {
  2375. plv->dwTileFlags = dwTileFlags;
  2376. bRecompute = TRUE;
  2377. }
  2378. if (ListView_IsDPIScaled(plv))
  2379. {
  2380. CCDPIScaleX(&pTileViewInfo->sizeTile.cx);
  2381. CCDPIScaleY(&pTileViewInfo->sizeTile.cy);
  2382. }
  2383. if ((plv->dwTileFlags & LVTVIF_FIXEDHEIGHT) &&
  2384. plv->sizeTile.cy != pTileViewInfo->sizeTile.cy)
  2385. {
  2386. plv->sizeTile.cy = pTileViewInfo->sizeTile.cy;
  2387. bRecompute = TRUE;
  2388. }
  2389. if ((plv->dwTileFlags & LVTVIF_FIXEDWIDTH) &&
  2390. plv->sizeTile.cx != pTileViewInfo->sizeTile.cx)
  2391. {
  2392. plv->sizeTile.cx = pTileViewInfo->sizeTile.cx;
  2393. bRecompute = TRUE;
  2394. }
  2395. }
  2396. if ((pTileViewInfo->dwMask & LVTVIM_LABELMARGIN) &&
  2397. !EqualRect(&plv->rcTileLabelMargin, &pTileViewInfo->rcLabelMargin))
  2398. {
  2399. plv->rcTileLabelMargin = pTileViewInfo->rcLabelMargin;
  2400. bRecompute = TRUE;
  2401. }
  2402. if (bRecompute)
  2403. {
  2404. ListView_RecalcTileSize(plv);
  2405. plv->rcView.left = RECOMPUTE;
  2406. ListView_Recompute(plv);
  2407. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2408. }
  2409. return TRUE;
  2410. }
  2411. BOOL ListView_OnGetTileViewInfo(LV* plv, PLVTILEVIEWINFO pTileViewInfo)
  2412. {
  2413. if (!pTileViewInfo || (pTileViewInfo->cbSize != sizeof(LVTILEVIEWINFO)))
  2414. return FALSE;
  2415. if (pTileViewInfo->dwMask & LVTVIM_COLUMNS)
  2416. {
  2417. pTileViewInfo->cLines = plv->cSubItems;
  2418. }
  2419. if (pTileViewInfo->dwMask & LVTVIM_TILESIZE)
  2420. {
  2421. pTileViewInfo->dwFlags = plv->dwTileFlags;
  2422. pTileViewInfo->sizeTile = plv->sizeTile;
  2423. }
  2424. if (pTileViewInfo->dwMask & LVTVIM_LABELMARGIN)
  2425. {
  2426. pTileViewInfo->rcLabelMargin = plv->rcTileLabelMargin;
  2427. }
  2428. return TRUE;
  2429. }
  2430. BOOL ListView_OnSetTileInfo(LV* plv, PLVTILEINFO pTileInfo)
  2431. {
  2432. LVITEM lvi;
  2433. if (!pTileInfo || (pTileInfo->cbSize != sizeof(LVTILEINFO)))
  2434. return FALSE;
  2435. lvi.mask = LVIF_COLUMNS;
  2436. lvi.cColumns = pTileInfo->cColumns;
  2437. lvi.puColumns = pTileInfo->puColumns;
  2438. lvi.iSubItem = 0;
  2439. lvi.iItem = pTileInfo->iItem;
  2440. return ListView_OnSetItem(plv, &lvi);
  2441. }
  2442. BOOL ListView_OnGetTileInfo(LV* plv, PLVTILEINFO pTileInfo)
  2443. {
  2444. LVITEM lvi;
  2445. if (!pTileInfo || (pTileInfo->cbSize != sizeof(LVTILEINFO)))
  2446. return FALSE;
  2447. lvi.mask = LVIF_COLUMNS;
  2448. lvi.iSubItem = 0;
  2449. lvi.iItem = pTileInfo->iItem;
  2450. lvi.cColumns = pTileInfo->cColumns;
  2451. lvi.puColumns = pTileInfo->puColumns;
  2452. if (ListView_OnGetItem(plv, &lvi))
  2453. {
  2454. pTileInfo->cColumns = lvi.cColumns;
  2455. return TRUE;
  2456. }
  2457. return FALSE;
  2458. }
  2459. LRESULT ListView_OnSetInsertMark(LV* plv, LPLVINSERTMARK plvim)
  2460. {
  2461. if (plvim->cbSize != sizeof(LVINSERTMARK))
  2462. return 0;
  2463. if (plvim->iItem != plv->iInsertItem ||
  2464. BOOLIFY(plv->fInsertAfter) != BOOLIFY(plvim->dwFlags & LVIM_AFTER))
  2465. {
  2466. if (plv->iInsertItem != -1)
  2467. ListView_InvalidateMark(plv);
  2468. plv->iInsertItem = plvim->iItem;
  2469. plv->fInsertAfter = BOOLIFY(plvim->dwFlags & LVIM_AFTER);
  2470. if (plv->iInsertItem != -1)
  2471. ListView_InvalidateMark(plv);
  2472. }
  2473. return 1;
  2474. }
  2475. LRESULT ListView_OnSetInfoTip(LV *plv, PLVSETINFOTIP plvSetInfoTip)
  2476. {
  2477. LPWSTR pszText = NULL;
  2478. LPWSTR pszProduced = NULL;
  2479. LRESULT lRet = 0;
  2480. // Check size and flags. MBZ for now.
  2481. if (plvSetInfoTip->cbSize == sizeof(LVSETINFOTIP) &&
  2482. plvSetInfoTip->dwFlags == 0 &&
  2483. plvSetInfoTip->pszText != NULL)
  2484. {
  2485. pszText = plvSetInfoTip->pszText;
  2486. // If we are still looking at the same item, then set its text, and pop up the tip.
  2487. if (plvSetInfoTip->iItem == plv->iTTLastHit && plvSetInfoTip->iSubItem == plv->iTTLastSubHit)
  2488. {
  2489. TCHAR szBuf[INFOTIPSIZE];
  2490. BOOL bItemUnfolded;
  2491. BOOL fInfoTip = FALSE;
  2492. szBuf[0] = 0;
  2493. // preload the default tip text for folded items.
  2494. bItemUnfolded = ListView_IsItemUnfolded2(plv, plv->iTTLastHit, plv->iTTLastSubHit, szBuf, ARRAYSIZE(szBuf));
  2495. if (ListView_IsInfoTip(plv) && plv->iTTLastSubHit == 0)
  2496. {
  2497. if (*pszText && lstrcmp(szBuf, pszText) != 0)
  2498. {
  2499. // App changed something - there is a real infotip
  2500. fInfoTip = TRUE;
  2501. }
  2502. }
  2503. else
  2504. {
  2505. pszText = szBuf;
  2506. }
  2507. //
  2508. // Set the margins now before the TTN_SHOW because it will be too late then.
  2509. //
  2510. // We want fat margins if we're an infotip, thin margins if we're an
  2511. // in-place tooltip.
  2512. //
  2513. if (fInfoTip)
  2514. {
  2515. static const RECT rcMargin = {4, 4, 4, 4};
  2516. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  2517. CCSetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  2518. plv->fPlaceTooltip = FALSE; // Set it to TRUE only if Unfolding tip is set
  2519. }
  2520. else
  2521. {
  2522. static const RECT rcMargin = {0, 0, 0, 0};
  2523. plv->fPlaceTooltip = TRUE;
  2524. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  2525. CCResetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  2526. }
  2527. Str_Set(&plv->pszTip, pszText);
  2528. // Re-display tooltip. If tracking, call tracking start code (same as timer code)
  2529. if (!ListView_IsKbdTipTracking(plv))
  2530. lRet = SendMessage(plv->hwndToolTips, TTM_POPUP, 0, 0);
  2531. else
  2532. ListView_OnTimer(plv, IDT_TRACKINGTIP);
  2533. }
  2534. if (pszProduced)
  2535. FreeProducedString(pszProduced);
  2536. }
  2537. return lRet;
  2538. }
  2539. LRESULT ListView_OnNotify(LV* plv, WPARAM wParam, LPNMHDR pnmh)
  2540. {
  2541. // we can't switch on the control ID because the tooltip is a WS_POPUP window
  2542. // and does not have a control ID. (header and tooltip both have 0 as ID)
  2543. if (plv->hwndHdr && (plv->hwndHdr == pnmh->hwndFrom))
  2544. {
  2545. // this is a notify for the header, deal with it as needed
  2546. return ListView_HeaderNotify(plv, (HD_NOTIFY *)pnmh);
  2547. }
  2548. else if (plv->hwndToolTips && (plv->hwndToolTips == pnmh->hwndFrom))
  2549. {
  2550. // implement unfolding the text for items as well as info tip support
  2551. switch (pnmh->code)
  2552. {
  2553. case TTN_NEEDTEXT:
  2554. {
  2555. POINT pt;
  2556. UINT uFlags;
  2557. int iNewHit;
  2558. int iNewSubHit;
  2559. NMTTDISPINFO *pttt = (NMTTDISPINFO *)pnmh;
  2560. // If keyboard tracking, do not hit test based on last cursor position
  2561. if (ListView_IsKbdTipTracking(plv))
  2562. {
  2563. RECT rcItem;
  2564. ListView_GetItemRect(plv->ci.hwnd, plv->iTracking, &rcItem, LVIR_LABEL);
  2565. pt.x = rcItem.left;
  2566. pt.y = rcItem.top;
  2567. }
  2568. else
  2569. GetMessagePosClient(plv->ci.hwnd, &pt);
  2570. iNewHit = _ListView_ItemHitTest(plv, pt.x, pt.y, &uFlags, &iNewSubHit);
  2571. if (iNewHit != plv->iTTLastHit || iNewSubHit != plv->iTTLastSubHit)
  2572. {
  2573. plv->fPlaceTooltip = FALSE; // Set it to TRUE only if Unfolding tip is set
  2574. Str_Set(&plv->pszTip, NULL); // clear the old tip
  2575. plv->iTTLastHit = iNewHit;
  2576. plv->iTTLastSubHit = iNewSubHit;
  2577. if ((iNewHit >= 0) && (plv->iEdit == -1))
  2578. {
  2579. TCHAR szBuf[INFOTIPSIZE], szBuf2[INFOTIPSIZE];
  2580. BOOL bItemUnfolded;
  2581. BOOL fInfoTip = FALSE;
  2582. LPTSTR pszTip = szBuf; // Use this one first
  2583. szBuf[0] = 0;
  2584. szBuf2[0] = 0;
  2585. // preload the tip text for folded items. this
  2586. // may be overridden by callback below
  2587. bItemUnfolded = ListView_IsItemUnfolded2(plv, plv->iTTLastHit, plv->iTTLastSubHit, szBuf, ARRAYSIZE(szBuf));
  2588. lstrcpyn(szBuf2, szBuf, ARRAYSIZE(szBuf2)); // Backup the unfolding text
  2589. if (ListView_IsInfoTip(plv) && iNewSubHit == 0)
  2590. {
  2591. NMLVGETINFOTIP git;
  2592. git.dwFlags = bItemUnfolded ? LVGIT_UNFOLDED : 0;
  2593. git.pszText = szBuf;
  2594. git.cchTextMax = ARRAYSIZE(szBuf);
  2595. git.iItem = plv->iTTLastHit;
  2596. git.iSubItem = 0;
  2597. git.lParam = 0;
  2598. // for folded items pszText is prepopulated with the
  2599. // item text, clients should append to this
  2600. CCSendNotify(&plv->ci, LVN_GETINFOTIP, &git.hdr);
  2601. if (*szBuf && lstrcmp(szBuf, szBuf2) != 0)
  2602. {
  2603. // App changed something - there is a real infotip
  2604. fInfoTip = TRUE;
  2605. }
  2606. }
  2607. //
  2608. // Set the margins now before the TTN_SHOW because it will be too late then.
  2609. //
  2610. // We want fat margins if we're an infotip, thin margins if we're an
  2611. // in-place tooltip.
  2612. //
  2613. if (fInfoTip)
  2614. {
  2615. static const RECT rcMargin = {4, 4, 4, 4};
  2616. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  2617. CCSetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  2618. }
  2619. else
  2620. {
  2621. static const RECT rcMargin = {0, 0, 0, 0};
  2622. plv->fPlaceTooltip = TRUE;
  2623. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  2624. CCResetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  2625. }
  2626. Str_Set(&plv->pszTip, pszTip);
  2627. }
  2628. }
  2629. pttt->lpszText = plv->pszTip; // here it is...
  2630. }
  2631. break;
  2632. // Handle custom draw as we want the tooltip painted as a multi-line that
  2633. // matches the formatting used by the list view.
  2634. case NM_CUSTOMDRAW:
  2635. {
  2636. LPNMTTCUSTOMDRAW pnm = (LPNMTTCUSTOMDRAW) pnmh;
  2637. if (plv->fPlaceTooltip &&
  2638. (pnm->nmcd.dwDrawStage == CDDS_PREPAINT ||
  2639. pnm->nmcd.dwDrawStage == CDDS_ITEMPREPAINT))
  2640. {
  2641. DWORD dwCustom = 0;
  2642. //
  2643. // Set up the customdraw DC to match the font of the LV item.
  2644. //
  2645. if (plv->iTTLastHit != -1)
  2646. {
  2647. LVFAKEDRAW lvfd;
  2648. LV_ITEM item;
  2649. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  2650. item.iItem = plv->iTTLastHit;
  2651. item.iSubItem = plv->iTTLastSubHit;
  2652. item.mask = LVIF_PARAM;
  2653. ListView_OnGetItem(plv, &item);
  2654. dwCustom = ListView_BeginFakeItemDraw(&lvfd);
  2655. // If client changed the font, then transfer the font
  2656. // from our private hdc into the tooltip's HDC. We use
  2657. // a private HDC because we only want to let the app change
  2658. // the font, not the colors or anything else.
  2659. if (dwCustom & CDRF_NEWFONT)
  2660. {
  2661. SelectObject(pnm->nmcd.hdc, GetCurrentObject(lvfd.nmcd.nmcd.hdc, OBJ_FONT));
  2662. }
  2663. ListView_EndFakeItemDraw(&lvfd);
  2664. ListView_EndFakeCustomDraw(&lvfd);
  2665. }
  2666. //
  2667. // The Large Icon tooltip needs to be drawn specially.
  2668. //
  2669. if (ListView_IsIconView(plv))
  2670. {
  2671. pnm->uDrawFlags &= ~(DT_SINGLELINE|DT_LEFT);
  2672. pnm->uDrawFlags |= DT_CENTER|DT_LVWRAP;
  2673. if (pnm->uDrawFlags & DT_CALCRECT)
  2674. {
  2675. pnm->nmcd.rc.right = pnm->nmcd.rc.left + (plv->cxIconSpacing - g_cxLabelMargin * 2);
  2676. pnm->nmcd.rc.bottom = pnm->nmcd.rc.top + 0x10000; // big number, no limit!
  2677. }
  2678. }
  2679. // Don't return other wacky flags to TT, since all we
  2680. // did was change the font (if even that)
  2681. return dwCustom & CDRF_NEWFONT;
  2682. }
  2683. }
  2684. break;
  2685. case TTN_SHOW:
  2686. if (plv->iTTLastHit != -1)
  2687. {
  2688. if (plv->fPlaceTooltip)
  2689. {
  2690. LPNMTTSHOWINFO psi = (LPNMTTSHOWINFO)pnmh;
  2691. RECT rcLabel;
  2692. // In case we're doing subitem hit-testing
  2693. rcLabel.top = plv->iTTLastSubHit;
  2694. rcLabel.left = LVIR_LABEL;
  2695. // reposition to allign with the text rect and
  2696. // set it to topmost
  2697. if (plv->iTTLastSubHit && ListView_OnGetSubItemRect(plv, plv->iTTLastHit, &rcLabel))
  2698. {
  2699. LV_ITEM item;
  2700. // we got the subitem rect. When we draw subitems, we give
  2701. // them SHDT_EXTRAMARGIN, so we have to also
  2702. rcLabel.left += g_cxLabelMargin * 3;
  2703. rcLabel.right -= g_cxLabelMargin * 3;
  2704. // And take the image into account, too.
  2705. // ListView_OnGetItem will worry about LVS_EX_SUBITEMIMAGES.
  2706. item.mask = LVIF_IMAGE;
  2707. item.iImage = -1;
  2708. item.iItem = plv->iTTLastHit;
  2709. item.iSubItem = plv->iTTLastSubHit;
  2710. ListView_OnGetItem(plv, &item);
  2711. if (item.iImage != -1)
  2712. rcLabel.left += plv->cxSmIcon;
  2713. }
  2714. else
  2715. { // a tip from subitem zero
  2716. ListView_GetUnfoldedRect(plv, plv->iTTLastHit, &rcLabel);
  2717. // SHDrawText actually leaves a g_cxLabelMargin margin
  2718. rcLabel.left += g_cxLabelMargin;
  2719. rcLabel.right -= g_cxLabelMargin;
  2720. }
  2721. // In report and list views, SHDrawText does vertical
  2722. // centering (without consulting the custom-draw client,
  2723. // even, so it just centers by a random amount).
  2724. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  2725. {
  2726. rcLabel.top += (rcLabel.bottom - rcLabel.top - plv->cyLabelChar) / 2;
  2727. }
  2728. SendMessage(plv->hwndToolTips, TTM_ADJUSTRECT, TRUE, (LPARAM)&rcLabel);
  2729. MapWindowRect(plv->ci.hwnd, HWND_DESKTOP, &rcLabel);
  2730. if (!ListView_IsIconView(plv))
  2731. {
  2732. // In non-large-icon view, the label size may be greater than the rect returned by ListView_GetUnfoldedRect.
  2733. // So don't specify the size
  2734. SetWindowPos(plv->hwndToolTips, HWND_TOP,
  2735. rcLabel.left, rcLabel.top,
  2736. 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW);
  2737. }
  2738. else
  2739. {
  2740. SetWindowPos(plv->hwndToolTips, HWND_TOP,
  2741. rcLabel.left, rcLabel.top,
  2742. (rcLabel.right - rcLabel.left), (rcLabel.bottom - rcLabel.top),
  2743. SWP_NOACTIVATE | SWP_HIDEWINDOW);
  2744. }
  2745. // This is an inplace tooltip, so disable animation.
  2746. psi->dwStyle |= TTS_NOANIMATE;
  2747. return TRUE;
  2748. }
  2749. else if (ListView_IsKbdTipTracking(plv)) // Size tip when keyboard tracking
  2750. {
  2751. RECT rc;
  2752. RECT rcTT;
  2753. RECT rcItem;
  2754. POINT ptTT;
  2755. POINT ptItem;
  2756. MONITORINFO mi = {0};
  2757. mi.cbSize = sizeof(MONITORINFO);
  2758. // Establish item screen position and size
  2759. ListView_GetItemRect(plv->ci.hwnd, plv->iTracking, &rcItem, LVIR_ICON);
  2760. ListView_GetItemRect(plv->ci.hwnd, plv->iTracking, &rc, LVIR_BOUNDS);
  2761. rcItem.top = rc.top;
  2762. rcItem.bottom = rc.bottom;
  2763. ptItem.x = rcItem.left;
  2764. ptItem.y = rcItem.top;
  2765. ClientToScreen(plv->ci.hwnd, &ptItem);
  2766. // Get tip rect
  2767. GetWindowRect(plv->hwndToolTips, &rcTT);
  2768. // Init tooltip position
  2769. ptTT.x = ptItem.x + RECTWIDTH(rcItem);
  2770. ptTT.y = ptItem.y + RECTHEIGHT(rcItem);
  2771. // Get screen info where tooltip is being displayed
  2772. GetMonitorInfo(MonitorFromPoint(ptTT, MONITOR_DEFAULTTONEAREST), &mi);
  2773. // Update tooltip position if it runs off the screen
  2774. if ((ptTT.x + RECTWIDTH(rcTT)) > mi.rcMonitor.right)
  2775. ptTT.x = (ptItem.x + g_cxIconMargin) - RECTWIDTH(rcTT);
  2776. if ((ptTT.y + RECTHEIGHT(rcTT)) > mi.rcMonitor.bottom)
  2777. ptTT.y = ptItem.y - RECTHEIGHT(rcTT);
  2778. SetWindowPos(plv->hwndToolTips, NULL, ptTT.x, ptTT.y, 0, 0, SWP_NOSIZE|SWP_NOACTIVATE);
  2779. return TRUE;
  2780. }
  2781. }
  2782. break;
  2783. }
  2784. }
  2785. return 0;
  2786. }
  2787. // Pass the focus to the given window, and then check to see if it exists.
  2788. // Passing focus can cause the window to be destroyed (by the Explorer
  2789. // when renaming).
  2790. BOOL ListView_SetFocus(HWND hwnd)
  2791. {
  2792. SetFocus(hwnd);
  2793. return IsWindow(hwnd);
  2794. }
  2795. void ListView_Realize(LV* plv, HDC hdcParam, BOOL fBackground, BOOL fForceRepaint)
  2796. {
  2797. if (plv->hpalHalftone)
  2798. {
  2799. HDC hdc = hdcParam ? hdcParam : GetDC(plv->ci.hwnd);
  2800. if (hdc)
  2801. {
  2802. BOOL fRepaint;
  2803. SelectPalette(hdc, plv->hpalHalftone, fBackground);
  2804. fRepaint = RealizePalette(hdc) || fForceRepaint;
  2805. if (!hdcParam)
  2806. ReleaseDC(plv->ci.hwnd, hdc);
  2807. if (fRepaint)
  2808. {
  2809. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2810. }
  2811. }
  2812. }
  2813. }
  2814. BOOL RectInRect(const RECT* prcOuter, const RECT* prcInner)
  2815. {
  2816. RECT rcDummy;
  2817. return IntersectRect(&rcDummy, prcOuter, prcInner);
  2818. }
  2819. LRESULT LVGenerateDragImage(LV* plv, SHDRAGIMAGE* pshdi)
  2820. {
  2821. LRESULT lRet = 0;
  2822. int iNumSelected = plv->nSelected;
  2823. int iIndex;
  2824. int iSelectedItem;
  2825. RECT rc = {0, 0, 0, 0};
  2826. RECT rcVisRect;
  2827. HBITMAP hbmpOld = NULL;
  2828. HDC hdcDragImage;
  2829. BOOL fBorderSelect = (plv->exStyle & LVS_EX_BORDERSELECT);
  2830. // First loop through can get the selection rect
  2831. if (ListView_IsOwnerData(plv))
  2832. {
  2833. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &iNumSelected);
  2834. }
  2835. if (iNumSelected == 0)
  2836. return FALSE;
  2837. plv->flags |= LVF_DRAGIMAGE;
  2838. GetClientRect(plv->ci.hwnd, &rcVisRect);
  2839. // Loop Through and calculate the enclosing rect.
  2840. for (iIndex = iNumSelected - 1, iSelectedItem = -1; iIndex >= 0; iIndex--)
  2841. {
  2842. iSelectedItem = ListView_OnGetNextItem(plv, iSelectedItem, LVNI_SELECTED);
  2843. if (iSelectedItem != -1)
  2844. {
  2845. RECT rcItemBounds;
  2846. // Make sure this is in the visible region
  2847. if (ListView_GetItemRect(plv->ci.hwnd, iSelectedItem, &rcItemBounds, LVIR_SELECTBOUNDS) &&
  2848. RectInRect(&rcVisRect, &rcItemBounds))
  2849. {
  2850. UnionRect(&rc, &rc, &rcItemBounds);
  2851. }
  2852. }
  2853. }
  2854. hdcDragImage = CreateCompatibleDC(NULL);
  2855. if (hdcDragImage)
  2856. {
  2857. RGBQUAD* prgbBits;
  2858. BITMAPINFO bi;
  2859. // Need to turn this off because it doesn't look good.
  2860. plv->exStyle &= ~LVS_EX_BORDERSELECT;
  2861. // After this rc contains the bounds of all the items in Client Coordinates.
  2862. //
  2863. // Mirror the the DC, if the listview is mirrored.
  2864. //
  2865. if (plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  2866. {
  2867. SET_DC_RTL_MIRRORED(hdcDragImage);
  2868. }
  2869. #define MAX_DRAG_RECT_WIDTH 300
  2870. #define MAX_DRAG_RECT_HEIGHT 300
  2871. // If this rect is too big, fix it.
  2872. if (RECTWIDTH(rc) > MAX_DRAG_RECT_WIDTH)
  2873. {
  2874. int iLeft = MAX_DRAG_RECT_WIDTH / 2;
  2875. int iRight = MAX_DRAG_RECT_WIDTH /2;
  2876. int iRectOriginalLeft = rc.left;
  2877. // Is the left boundry outside the visible rect?
  2878. if (rc.left < plv->ptCapture.x - iLeft)
  2879. {
  2880. // Yes, then we have to clip it.
  2881. rc.left = plv->ptCapture.x - iLeft;
  2882. }
  2883. else
  2884. {
  2885. // No? Well then shift the visible rect to the right, so that we have
  2886. // more room.
  2887. iRight += rc.left - (plv->ptCapture.x - iLeft);
  2888. }
  2889. // Is the right boundry outside the visible rect?
  2890. if (rc.right > plv->ptCapture.x + iRight)
  2891. {
  2892. // Yes, then we have to clip it.
  2893. rc.right = plv->ptCapture.x + iRight;
  2894. }
  2895. else
  2896. {
  2897. // No? Then try and add it to the left
  2898. if (rc.left > iRectOriginalLeft)
  2899. {
  2900. rc.left -= iRight - (rc.right - plv->ptCapture.x);
  2901. if (rc.left < iRectOriginalLeft)
  2902. rc.left = iRectOriginalLeft;
  2903. }
  2904. }
  2905. }
  2906. if (RECTHEIGHT(rc) > MAX_DRAG_RECT_HEIGHT)
  2907. {
  2908. // same for top and bottom:
  2909. // Is the top boundry outside the visible rect?
  2910. int iTop = MAX_DRAG_RECT_HEIGHT / 2;
  2911. int iBottom = MAX_DRAG_RECT_HEIGHT /2;
  2912. int iRectOriginalTop = rc.top;
  2913. if (rc.top < plv->ptCapture.y - iTop)
  2914. {
  2915. // Yes, then we have to clip it.
  2916. rc.top = plv->ptCapture.y - iTop;
  2917. }
  2918. else
  2919. {
  2920. // No? Well then shift the visible rect to the right, so that we have
  2921. // more room.
  2922. iBottom += rc.top - (plv->ptCapture.y - iTop);
  2923. }
  2924. // Is the right boundry outside the visible rect?
  2925. if (rc.bottom > plv->ptCapture.y + iBottom)
  2926. {
  2927. // Yes, then we have to clip it.
  2928. rc.bottom = plv->ptCapture.y + iBottom;
  2929. }
  2930. else
  2931. {
  2932. // No? Then try and add it to the top
  2933. if (rc.top > iRectOriginalTop)
  2934. {
  2935. rc.top -= iBottom - (rc.bottom - plv->ptCapture.y);
  2936. if (rc.top < iRectOriginalTop)
  2937. rc.top = iRectOriginalTop;
  2938. }
  2939. }
  2940. }
  2941. pshdi->sizeDragImage.cx = RECTWIDTH(rc) + 1;
  2942. pshdi->sizeDragImage.cy = RECTHEIGHT(rc) + 1;
  2943. bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
  2944. bi.bmiHeader.biWidth = pshdi->sizeDragImage.cx;
  2945. bi.bmiHeader.biHeight = pshdi->sizeDragImage.cy;
  2946. bi.bmiHeader.biPlanes = 1;
  2947. bi.bmiHeader.biBitCount = 32;
  2948. bi.bmiHeader.biCompression = BI_RGB;
  2949. pshdi->hbmpDragImage = CreateDIBSection(hdcDragImage, &bi, DIB_RGB_COLORS, &prgbBits, NULL, 0);
  2950. if (pshdi->hbmpDragImage)
  2951. {
  2952. int iTotal = bi.bmiHeader.biWidth * bi.bmiHeader.biHeight;
  2953. LVDRAWITEM lvdi;
  2954. int cItem;
  2955. RECT rcImage = {0, 0, pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy};
  2956. hbmpOld = SelectObject(hdcDragImage, pshdi->hbmpDragImage);
  2957. ZeroMemory(prgbBits, pshdi->sizeDragImage.cx * pshdi->sizeDragImage.cy);
  2958. pshdi->crColorKey = CLR_NONE;
  2959. // Calculate the offset... The cursor should be in the bitmap rect.
  2960. if (plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  2961. pshdi->ptOffset.x = rc.right - plv->ptCapture.x;
  2962. else
  2963. pshdi->ptOffset.x = plv->ptCapture.x - rc.left;
  2964. pshdi->ptOffset.y = plv->ptCapture.y - rc.top;
  2965. lvdi.prcClip = NULL;
  2966. lvdi.plv = plv;
  2967. lvdi.nmcd.nmcd.hdc = hdcDragImage;
  2968. lvdi.pitem = NULL;
  2969. cItem = ListView_Count(plv);
  2970. // Now loop through again for the paint cycle
  2971. for (iIndex = cItem - 1, iSelectedItem = -1; iIndex >= 0; iIndex--)
  2972. {
  2973. if (ListView_IsOwnerData(plv))
  2974. {
  2975. iSelectedItem++;
  2976. plv->plvrangeSel->lpVtbl->NextSelected(plv->plvrangeSel, iSelectedItem, &iSelectedItem);
  2977. }
  2978. else
  2979. {
  2980. LISTITEM* pitem;
  2981. iSelectedItem = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, iIndex);
  2982. pitem = ListView_FastGetItemPtr(plv, iSelectedItem);
  2983. if (!(pitem->state & LVIS_SELECTED))
  2984. iSelectedItem = -1;
  2985. }
  2986. if (iSelectedItem != -1)
  2987. {
  2988. int iOldItemDrawing;
  2989. COLORREF crSave;
  2990. POINT ptOrigin = {-rc.left, -rc.top}; //Offset the rects by...
  2991. RECT rcItemBounds;
  2992. RECT rcTemp;
  2993. iOldItemDrawing = plv->iItemDrawing;
  2994. plv->iItemDrawing = iSelectedItem;
  2995. lvdi.nmcd.nmcd.dwItemSpec = iSelectedItem;
  2996. ListView_GetRects(plv, iSelectedItem, QUERY_DEFAULT, NULL, NULL, &rcItemBounds, NULL);
  2997. // Make sure this is in the visible region
  2998. if (IntersectRect(&rcTemp, &rcVisRect, &rcItemBounds))
  2999. {
  3000. ptOrigin.x += rcItemBounds.left;
  3001. ptOrigin.y += rcItemBounds.top;
  3002. // these may get changed
  3003. lvdi.lpptOrg = &ptOrigin;
  3004. lvdi.flags = LVDI_NOEFFECTS;
  3005. lvdi.nmcd.clrText = plv->clrText;
  3006. lvdi.nmcd.clrTextBk = plv->clrTextBk;
  3007. lvdi.nmcd.clrFace = plv->clrBk;
  3008. lvdi.nmcd.iIconEffect = ILD_NORMAL;
  3009. lvdi.nmcd.iIconPhase = 0;
  3010. // Save the Background color!
  3011. crSave = plv->clrBk;
  3012. plv->clrBk = CLR_NONE; // None so that it "bleeds" into the alpha channel
  3013. ListView_DrawItem(&lvdi);
  3014. plv->clrBk = crSave;
  3015. }
  3016. plv->iItemDrawing = iOldItemDrawing;
  3017. }
  3018. }
  3019. for (iIndex = 0; iIndex < iTotal; iIndex++)
  3020. {
  3021. RGBQUAD* prgb = &prgbBits[iIndex];
  3022. if (prgb->rgbReserved == 0 &&
  3023. (prgb->rgbRed || prgb->rgbGreen || prgb->rgbBlue)) // Do we have color an no alpha?
  3024. {
  3025. prgb->rgbReserved = 0xFF;
  3026. }
  3027. }
  3028. SelectObject(hdcDragImage, hbmpOld);
  3029. DeleteDC(hdcDragImage);
  3030. // We're passing back the created HBMP.
  3031. lRet = 1;
  3032. }
  3033. if (fBorderSelect)
  3034. plv->exStyle |= LVS_EX_BORDERSELECT;
  3035. }
  3036. plv->flags &= ~LVF_DRAGIMAGE;
  3037. return lRet;
  3038. }
  3039. LRESULT ListView_OnEnableGroupView(LV* plv, BOOL fEnable)
  3040. {
  3041. if (plv->ci.style & LVS_OWNERDATA) // Not supported in ownerdata case.
  3042. return -1;
  3043. if (fEnable ^ plv->fGroupView)
  3044. {
  3045. if (fEnable)
  3046. {
  3047. // Turning on groupview, so nuke insertmark, because that's not allowed
  3048. // in group view
  3049. LVINSERTMARK lvim = {0};
  3050. lvim.cbSize = sizeof(LVINSERTMARK);
  3051. lvim.iItem = -1;
  3052. ListView_OnSetInsertMark(plv, &lvim);
  3053. }
  3054. plv->fGroupView = fEnable;
  3055. if (fEnable)
  3056. {
  3057. if (plv->hdpaGroups == NULL)
  3058. plv->hdpaGroups = DPA_Create(4);
  3059. if (plv->hdpaGroups == NULL)
  3060. return -1;
  3061. }
  3062. plv->rcView.left = RECOMPUTE;
  3063. SetWindowLongPtr(plv->ci.hwnd, GWL_STYLE, GetWindowLongPtr(plv->ci.hwnd, GWL_STYLE) | LVS_AUTOARRANGE);
  3064. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  3065. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3066. ListView_UpdateScrollBars(plv);
  3067. return 1;
  3068. }
  3069. return 0;
  3070. }
  3071. LRESULT ListView_SetViewType(LV* plv, WORD wView)
  3072. {
  3073. if (wView > LV_VIEW_MAX)
  3074. return -1;
  3075. else if (plv->wView != wView)
  3076. {
  3077. int i;
  3078. WORD wViewOld = plv->wView;
  3079. ListView_DismissEdit(plv, FALSE);
  3080. // (dli) Setting the small icon width here and only in the case when we go
  3081. // from large icon view to some other view because of three reasons:
  3082. // 1. According to chee, we want to set this before we change the style bit in
  3083. // plv or after we scale.
  3084. // 2. We don't want to do it after we scale because we want to set the width to
  3085. // the maximum value so that the items in this listview do not cover each other
  3086. // 3. we do it from large icon view because large icon view has fixed width for
  3087. // each item, small icon view width can be scaled.
  3088. if (wViewOld == LV_VIEW_ICON)
  3089. ListView_ISetColumnWidth(plv, 0, LV_GetNewColWidth(plv, 0, ListView_Count(plv)-1), FALSE);
  3090. if (wView == LV_VIEW_TILE)
  3091. {
  3092. ListView_RecalcTileSize(plv);
  3093. }
  3094. plv->wView = wView;
  3095. ListView_TypeChange(plv, wViewOld, BOOLIFY(plv->ci.style & LVS_OWNERDRAWFIXED));
  3096. // Else we would like to make the most important item to still
  3097. // be visible. So first we will look for a cursorered item
  3098. // if this fails, we will look for the first selected item,
  3099. // else we will simply ask for the first item (assuming the
  3100. // count > 0
  3101. //
  3102. // And make sure the scrollbars are up to date Note this
  3103. // also updates some variables that some views need
  3104. ListView_UpdateScrollBars(plv);
  3105. i = (plv->iFocus >= 0) ? plv->iFocus : ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  3106. if ((i == -1) && (ListView_Count(plv) > 0))
  3107. i = 0;
  3108. if (i != -1)
  3109. ListView_OnEnsureVisible(plv, i, TRUE);
  3110. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  3111. // Change of styles also changes tooltip policy, so pop it
  3112. ListView_PopBubble(plv);
  3113. }
  3114. return 1;
  3115. }
  3116. BOOL ListView_OnGetFrozenSlot(LV* plv, LPRECT pSlotRect)
  3117. {
  3118. int cSlots, iWidth = 0, iHeight = 0;
  3119. LISTITEM *pItem;
  3120. if((plv->iFrozenSlot == LV_NOFROZENSLOT) || !ListView_IsIconView(plv) || ListView_IsOwnerData(plv) || (pSlotRect == NULL)) //Supported only in Large Icon mode!
  3121. return FALSE;
  3122. cSlots = ListView_GetSlotCount(plv, TRUE, &iWidth, &iHeight);
  3123. //We need to have a valid pItem to pass to ListView_CalcSlotRect() function.
  3124. pItem = plv->pFrozenItem; //Try to use a frozen item, if present.
  3125. if(pItem == NULL)
  3126. pItem = ListView_GetItemPtr(plv, 0); //Or else, use the first item.
  3127. if(pItem == NULL) //If we couldn't get any pItem, then we can't call CalcSlotRect().
  3128. return FALSE; //... Hence, we have to return failure.
  3129. else
  3130. {
  3131. ListView_CalcSlotRect(plv, pItem, plv->iFrozenSlot, cSlots, FALSE,
  3132. iWidth, iHeight,pSlotRect);
  3133. return TRUE;
  3134. }
  3135. }
  3136. BOOL ListView_OnSetFrozenSlot(LV* plv, BOOL fFreeze, LPPOINT pPt)
  3137. {
  3138. if(!ListView_IsIconView(plv) || ListView_IsOwnerData(plv)) //Supported only in Large Icon mode!
  3139. return FALSE;
  3140. if(fFreeze)
  3141. {
  3142. //First, find the slot where the given point lies.
  3143. int cSlots, iWidth = 0, iHeight = 0;
  3144. cSlots = ListView_GetSlotCount(plv, TRUE, &iWidth, &iHeight);
  3145. plv->iFrozenSlot = ListView_CalcHitSlot(plv, *pPt, cSlots, iWidth, iHeight);
  3146. }
  3147. else
  3148. {
  3149. //Unfreeze a frozen slot.
  3150. plv->iFrozenSlot = LV_NOFROZENSLOT; //No slot is frozen.
  3151. }
  3152. return TRUE;
  3153. }
  3154. int ListView_OnGetFrozenItem(LV* plv)
  3155. {
  3156. int i;
  3157. LISTITEM *pItem;
  3158. if((plv->pFrozenItem == NULL) || !ListView_IsIconView(plv) || ListView_IsOwnerData(plv)) //Supported only in Large Icon mode!
  3159. return LV_NOFROZENITEM;
  3160. for(i = 0; i < ListView_Count(plv); i++)
  3161. {
  3162. pItem = ListView_GetItemPtr(plv, i);
  3163. if((pItem != NULL) && (pItem == plv->pFrozenItem))
  3164. return (i);
  3165. }
  3166. return LV_NOFROZENITEM;
  3167. }
  3168. BOOL ListView_OnSetFrozenItem(LV* plv, BOOL fFreeze, int iIndex)
  3169. {
  3170. LISTITEM *pitem;
  3171. if(!ListView_IsIconView(plv) || ListView_IsOwnerData(plv)) //Supported only in Large Icon mode!
  3172. return FALSE;
  3173. if(fFreeze)
  3174. {
  3175. //Freeze the given item.
  3176. pitem = ListView_GetItemPtr(plv, iIndex);
  3177. if(pitem == NULL)
  3178. return FALSE;
  3179. plv->pFrozenItem = pitem;
  3180. }
  3181. else
  3182. {
  3183. //Unfreeze the currently frozen item.
  3184. plv->pFrozenItem = NULL;
  3185. }
  3186. return TRUE;
  3187. }
  3188. // Item focus changed via the keyboard, start tracking tooltip timeout for keyboard nav popups
  3189. //
  3190. BOOL ListView_OnKeyboardSelected(LV* plv, int iNewFocus)
  3191. {
  3192. if (iNewFocus >= 0 && plv->hwndToolTips)
  3193. {
  3194. // Focus via the keyboard (already cancelled via entry into this function)
  3195. plv->iTracking = iNewFocus;
  3196. // Delay will be replaced with an SPI
  3197. SetTimer(plv->ci.hwnd, IDT_TRACKINGTIP, GetDoubleClickTime() * 2, NULL);
  3198. }
  3199. return TRUE;
  3200. }
  3201. LRESULT ListView_OnMapIndexToID(LV* plv, UINT iItem)
  3202. {
  3203. LISTITEM* pitem;
  3204. if (!ListView_IsValidItemNumber(plv, iItem) || ListView_IsOwnerData(plv))
  3205. {
  3206. return -1;
  3207. }
  3208. pitem = ListView_FastGetItemPtr(plv, iItem);
  3209. ASSERT (pitem);
  3210. return (LRESULT)pitem->dwId;
  3211. }
  3212. #ifdef DEBUG
  3213. UINT uAverageSeekCount = 0;
  3214. UINT uTotalSeeks = 0;
  3215. UINT uPerSeekCount = 0;
  3216. #endif
  3217. LRESULT ListView_OnMapIdToIndex(LV* plv, UINT Id)
  3218. {
  3219. DWORD dwRet = -1;
  3220. UINT cCounter = 0;
  3221. UINT cItems = ListView_Count(plv);
  3222. UINT i;
  3223. if (ListView_IsOwnerData(plv))
  3224. return -1;
  3225. if (plv->iLastId >= cItems)
  3226. plv->iLastId = 0;
  3227. DEBUG_CODE(uTotalSeeks++);
  3228. for (i = plv->iLastId; cCounter < cItems; cCounter++)
  3229. {
  3230. LISTITEM* pitem = ListView_FastGetItemPtr(plv, i);
  3231. if (pitem->dwId == Id)
  3232. {
  3233. if (plv->iLastId > i)
  3234. plv->iIncrement = -1;
  3235. else
  3236. plv->iIncrement = 1;
  3237. plv->iLastId = dwRet = i;
  3238. break;
  3239. }
  3240. DEBUG_CODE(uPerSeekCount++);
  3241. i += (DWORD)plv->iIncrement;
  3242. if (i == -1) // Wrapped around to "Less than zero"?
  3243. i = cItems - 1;
  3244. if (i >= cItems)
  3245. i = 0;
  3246. }
  3247. DEBUG_CODE(uAverageSeekCount = uPerSeekCount / uTotalSeeks);
  3248. return (LRESULT)dwRet;
  3249. }
  3250. void ListView_OnSize(LV* plv)
  3251. {
  3252. if (plv->hwndToolTips)
  3253. {
  3254. TOOLINFO ti;
  3255. if (ListView_IsLabelTip(plv))
  3256. {
  3257. // A truncated label may have been exposed or vice versa.
  3258. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  3259. }
  3260. ti.cbSize = sizeof(ti);
  3261. ti.hwnd = plv->ci.hwnd;
  3262. ti.uId = 0;
  3263. // Resize the tooltip control so that it covers the entire
  3264. // area of the window when its parent gets resized.
  3265. GetClientRect(plv->ci.hwnd, &ti.rect);
  3266. SendMessage(plv->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM) &ti);
  3267. }
  3268. // if we're supposed to center the image,
  3269. // we need to do a full redraw on each size
  3270. if ((plv->ulBkImageFlags & LVBKIF_SOURCE_MASK) &&
  3271. (plv->ulBkImageFlags & LVBKIF_STYLE_MASK) == LVBKIF_STYLE_NORMAL &&
  3272. (plv->xOffsetPercent || plv->yOffsetPercent))
  3273. {
  3274. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3275. }
  3276. }
  3277. BOOL ListView_OnSetViewMargins(LV* plv, RECT *prc)
  3278. {
  3279. if (!IsEqualRect(plv->rcViewMargin, *prc))
  3280. {
  3281. plv->rcViewMargin = *prc;
  3282. plv->rcView.left = RECOMPUTE;
  3283. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3284. }
  3285. return TRUE;
  3286. }
  3287. BOOL ListView_OnGetViewMargins(LV* plv, RECT *prc)
  3288. {
  3289. *prc = plv->rcViewMargin;
  3290. return TRUE;
  3291. }
  3292. LRESULT CALLBACK ListView_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3293. {
  3294. LV* plv = ListView_GetPtr(hwnd);
  3295. if (plv == NULL)
  3296. {
  3297. if (uMsg == WM_NCCREATE)
  3298. {
  3299. plv = (LV*)LocalAlloc(LPTR, sizeof(LV));
  3300. if (!plv)
  3301. {
  3302. TraceMsg(TF_ERROR, "ListView: Out of memory");
  3303. return 0L; // fail the window create
  3304. }
  3305. plv->ci.hwnd = hwnd;
  3306. plv->flags = LVF_REDRAW; // assume that redrawing enabled!
  3307. plv->iFocus = -1; // no focus
  3308. plv->iMark = -1;
  3309. plv->iSelCol = -1;
  3310. plv->iDropHilite = -1; // Assume no item has drop hilite...
  3311. plv->cyItem = plv->cyItemSave = 1; // never let these be zero, not even for a moment
  3312. plv->hTheme = OpenThemeData(hwnd, L"ListView");
  3313. plv->iInsertItem = -1; // No insert mark by default of course
  3314. plv->clrim = CLR_DEFAULT;
  3315. plv->iTracking = LVKTT_NOTRACK;
  3316. plv->hheap = GetProcessHeap();
  3317. plv->iFrozenSlot = LV_NOFROZENSLOT; //No slot is frozen to begin with!
  3318. plv->iIncrement = -1;
  3319. ListView_SetPtr(hwnd, plv);
  3320. }
  3321. goto DoDefault;
  3322. }
  3323. if ((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST))
  3324. {
  3325. if (plv->exStyle & (LVS_EX_TRACKSELECT|LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE))
  3326. {
  3327. TRACKMOUSEEVENT tme;
  3328. tme.cbSize = sizeof(tme);
  3329. tme.hwndTrack = plv->ci.hwnd;
  3330. tme.dwHoverTime = plv->dwHoverTime;
  3331. tme.dwFlags = TME_LEAVE | TME_HOVER | TME_QUERY;
  3332. // see what's set
  3333. TrackMouseEvent(&tme);
  3334. tme.dwFlags &= TME_HOVER | TME_LEAVE;
  3335. // set these bits if they aren't already set
  3336. tme.dwFlags ^= TME_LEAVE;
  3337. if (plv->exStyle & LVS_EX_TRACKSELECT)
  3338. {
  3339. tme.dwFlags ^= TME_HOVER;
  3340. }
  3341. tme.cbSize = sizeof(tme);
  3342. tme.hwndTrack = plv->ci.hwnd;
  3343. tme.dwHoverTime = plv->dwHoverTime;
  3344. // set it if there's anything to set
  3345. if (tme.dwFlags & (TME_HOVER | TME_LEAVE))
  3346. {
  3347. TrackMouseEvent(&tme);
  3348. }
  3349. }
  3350. }
  3351. if (uMsg == g_uDragImages)
  3352. {
  3353. return LVGenerateDragImage(plv, (SHDRAGIMAGE*)lParam);
  3354. }
  3355. switch (uMsg)
  3356. {
  3357. HANDLE_MSG(plv, WM_CREATE, ListView_OnCreate);
  3358. HANDLE_MSG(plv, WM_DESTROY, ListView_OnDestroy);
  3359. HANDLE_MSG(plv, WM_ERASEBKGND, ListView_OnEraseBkgnd);
  3360. HANDLE_MSG(plv, WM_COMMAND, ListView_OnCommand);
  3361. HANDLE_MSG(plv, WM_SETFOCUS, ListView_OnSetFocus);
  3362. HANDLE_MSG(plv, WM_KILLFOCUS, ListView_OnKillFocus);
  3363. HANDLE_MSG(plv, WM_HSCROLL, ListView_OnHScroll);
  3364. HANDLE_MSG(plv, WM_VSCROLL, ListView_OnVScroll);
  3365. HANDLE_MSG(plv, WM_GETDLGCODE, ListView_OnGetDlgCode);
  3366. HANDLE_MSG(plv, WM_SETFONT, ListView_OnSetFont);
  3367. HANDLE_MSG(plv, WM_GETFONT, ListView_OnGetFont);
  3368. HANDLE_MSG(plv, WM_TIMER, ListView_OnTimer);
  3369. HANDLE_MSG(plv, WM_SETREDRAW, ListView_OnSetRedraw);
  3370. HANDLE_MSG(plv, WM_NCDESTROY, ListView_OnNCDestroy);
  3371. case WM_SETCURSOR:
  3372. if (ListView_OnSetCursorMsg(plv))
  3373. return TRUE;
  3374. break;
  3375. case WM_PALETTECHANGED:
  3376. if ((HWND)wParam == hwnd)
  3377. break;
  3378. case WM_QUERYNEWPALETTE:
  3379. // Want to pass FALSE if WM_QUERYNEWPALETTE...
  3380. ListView_Realize(plv, NULL, uMsg == WM_PALETTECHANGED, uMsg == WM_PALETTECHANGED);
  3381. return TRUE;
  3382. case LVMP_WINDOWPOSCHANGED:
  3383. case WM_WINDOWPOSCHANGED:
  3384. HANDLE_WM_WINDOWPOSCHANGED(plv, wParam, lParam, ListView_OnWindowPosChanged);
  3385. break;
  3386. case WM_WINDOWPOSCHANGING:
  3387. {
  3388. WINDOWPOS* wp = (WINDOWPOS*)lParam;
  3389. if ((wp->flags & SWP_SHOWWINDOW)||
  3390. (wp->flags & SWP_HIDEWINDOW))
  3391. {
  3392. BOOL fShow = (wp->flags & SWP_SHOWWINDOW);
  3393. LV_OnShowWindow(plv, fShow);
  3394. }
  3395. if (ListView_IsWatermarked(plv))
  3396. {
  3397. RECT rc = {wp->x, wp->y, wp->x + wp->cx, wp->y + wp->y};
  3398. // Invalidate New.
  3399. rc.left = rc.right - plv->szWatermark.cx;
  3400. rc.top = rc.bottom - plv->szWatermark.cy;
  3401. InvalidateRect(plv->ci.hwnd, &rc, TRUE);
  3402. // and Old:
  3403. GetClientRect(plv->ci.hwnd, &rc);
  3404. rc.left = rc.right - plv->szWatermark.cx;
  3405. rc.top = rc.bottom - plv->szWatermark.cy;
  3406. InvalidateRect(plv->ci.hwnd, &rc, TRUE);
  3407. }
  3408. }
  3409. break;
  3410. case WM_MBUTTONDOWN:
  3411. if (ListView_SetFocus(hwnd) && plv->hwndToolTips)
  3412. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  3413. break;
  3414. case WM_LBUTTONDBLCLK:
  3415. case WM_RBUTTONDBLCLK:
  3416. // Cancel manual tip track on any mouse button down
  3417. ListView_CancelTipTrack(plv);
  3418. if (plv->hwndToolTips)
  3419. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  3420. ListView_OnButtonDown(plv, TRUE, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  3421. break;
  3422. case WM_LBUTTONDOWN:
  3423. case WM_RBUTTONDOWN:
  3424. // Cancel manual tip track on any mouse button down
  3425. ListView_CancelTipTrack(plv);
  3426. if (plv->hwndToolTips)
  3427. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  3428. ListView_OnButtonDown(plv, FALSE, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  3429. break;
  3430. case WM_LBUTTONUP:
  3431. case WM_RBUTTONUP:
  3432. case WM_MBUTTONUP:
  3433. case WM_NCMOUSEMOVE:
  3434. if (plv->hwndToolTips)
  3435. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  3436. break;
  3437. case WM_PRINTCLIENT:
  3438. case WM_PAINT:
  3439. ListView_OnPaint(plv, (HDC)wParam);
  3440. return 0;
  3441. case WM_NCPAINT:
  3442. {
  3443. if (plv->hTheme && plv->ci.dwExStyle & WS_EX_CLIENTEDGE)
  3444. {
  3445. HRGN hrgn = (wParam != 1) ? (HRGN)wParam : NULL;
  3446. if (CCDrawNonClientTheme(plv->hTheme, hwnd, hrgn, plv->hbrBk, 0, 0))
  3447. {
  3448. return 1;
  3449. }
  3450. }
  3451. }
  3452. break;
  3453. case WM_SHOWWINDOW:
  3454. LV_OnShowWindow(plv, BOOLFROMPTR(wParam));
  3455. break;
  3456. case WM_MOUSEHOVER:
  3457. ListView_OnMouseHover(plv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  3458. break;
  3459. case WM_MOUSELEAVE:
  3460. ListView_OnSetHotItem(plv, -1);
  3461. plv->iNoHover = -1;
  3462. break;
  3463. case WM_KEYUP:
  3464. plv->iScrollCount = 0;
  3465. break;
  3466. case WM_KEYDOWN:
  3467. HANDLE_WM_KEYDOWN(plv, wParam, lParam, ListView_OnKey);
  3468. break;
  3469. case WM_IME_COMPOSITION:
  3470. // Now only Korean version is interested in incremental search with composition string.
  3471. if (g_fDBCSInputEnabled)
  3472. {
  3473. if (((ULONG_PTR)GetKeyboardLayout(0L) & 0xF000FFFFL) == 0xE0000412L)
  3474. {
  3475. if (ListView_OnImeComposition(plv, wParam, lParam))
  3476. {
  3477. lParam &= ~GCS_RESULTSTR;
  3478. break;
  3479. }
  3480. else
  3481. return 0;
  3482. }
  3483. }
  3484. break;
  3485. case WM_CHAR:
  3486. if (plv->iPuntChar)
  3487. {
  3488. plv->iPuntChar--;
  3489. return TRUE;
  3490. }
  3491. else
  3492. {
  3493. return HANDLE_WM_CHAR(plv, wParam, lParam, ListView_OnChar);
  3494. }
  3495. case WM_WININICHANGE:
  3496. ListView_OnWinIniChange(plv, wParam, lParam);
  3497. break;
  3498. case WM_NOTIFYFORMAT:
  3499. return CIHandleNotifyFormat(&plv->ci, lParam);
  3500. case WM_ENABLE:
  3501. // HACK: we don't get WM_STYLECHANGE on EnableWindow()
  3502. ListView_EnableWindow(plv, BOOLFROMPTR(wParam));
  3503. break;
  3504. case WM_SYSCOLORCHANGE:
  3505. InitGlobalColors();
  3506. if (plv->ci.style & WS_DISABLED)
  3507. {
  3508. if (!(plv->flags & LVF_USERBKCLR))
  3509. plv->clrBkSave = g_clrWindow;
  3510. ListView_OnSetBkColor(plv, g_clrBtnFace);
  3511. }
  3512. else if (!(plv->flags & LVF_USERBKCLR))
  3513. {
  3514. ListView_OnSetBkColor(plv, g_clrWindow);
  3515. }
  3516. if (plv->exStyle & LVS_EX_CHECKBOXES)
  3517. {
  3518. ListView_InitCheckBoxes(plv, FALSE);
  3519. }
  3520. plv->crHeader = GetSysColor(COLOR_WINDOWTEXT);
  3521. plv->crTop = GetSysColor(COLOR_BTNFACE);
  3522. plv->crLeft = GetSysColor(COLOR_BTNFACE);
  3523. // 98/11/19 #249967 vtan: Always invalidate the list view
  3524. // rectangle so that the color change causes a refresh.
  3525. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3526. break;
  3527. // don't use HANDLE_MSG because this needs to go to the default handler
  3528. case WM_SYSKEYDOWN:
  3529. HANDLE_WM_SYSKEYDOWN(plv, wParam, lParam, ListView_OnKey);
  3530. break;
  3531. case WM_UPDATEUISTATE:
  3532. {
  3533. DWORD dwUIStateMask = MAKEWPARAM(0xFFFF, UISF_HIDEFOCUS);
  3534. // we care only about focus not accel, and redraw only if changed
  3535. if (CCOnUIState(&(plv->ci), WM_UPDATEUISTATE, wParam & dwUIStateMask, lParam))
  3536. {
  3537. if (plv->iFocus >= 0)
  3538. {
  3539. // an item has the focus, invalidate it
  3540. ListView_InvalidateItem(plv, plv->iFocus, FALSE, RDW_INVALIDATE | RDW_ERASE);
  3541. }
  3542. }
  3543. goto DoDefault;
  3544. }
  3545. case LVM_GETITEMA:
  3546. return (LRESULT)ListView_OnGetItemA(plv, (LV_ITEMA *)lParam);
  3547. case LVM_SETITEMA:
  3548. return (LRESULT)ListView_OnSetItemA(plv, (LV_ITEMA *)lParam);
  3549. case LVM_INSERTITEMA:
  3550. return (LRESULT)ListView_OnInsertItemA(plv, (LV_ITEMA *)lParam);
  3551. case LVM_FINDITEMA:
  3552. return (LRESULT)ListView_OnFindItemA(plv, (int)wParam, (LV_FINDINFOA *)lParam);
  3553. case LVM_GETSTRINGWIDTHA:
  3554. return (LRESULT)ListView_OnGetStringWidthA(plv, (LPCSTR)lParam, NULL);
  3555. case LVM_GETCOLUMNA:
  3556. return (LRESULT)ListView_OnGetColumnA(plv, (int)wParam, (LV_COLUMNA *)lParam);
  3557. case LVM_SETCOLUMNA:
  3558. return (LRESULT)ListView_OnSetColumnA(plv, (int)wParam, (LV_COLUMNA *)lParam);
  3559. case LVM_INSERTCOLUMNA:
  3560. return (LRESULT)ListView_OnInsertColumnA(plv, (int)wParam, (LV_COLUMNA *)lParam);
  3561. case LVM_GETITEMTEXTA:
  3562. return (LRESULT)ListView_OnGetItemTextA(plv, (int)wParam, (LV_ITEMA *)lParam);
  3563. case LVM_SETITEMTEXTA:
  3564. if (!lParam)
  3565. return FALSE;
  3566. return (LRESULT)ListView_OnSetItemTextA(plv, (int)wParam,
  3567. ((LV_ITEMA *)lParam)->iSubItem,
  3568. (LPCSTR)((LV_ITEMA *)lParam)->pszText);
  3569. case LVM_GETBKIMAGEA:
  3570. return (LRESULT)ListView_OnGetBkImageA(plv, (LPLVBKIMAGEA)lParam);
  3571. case LVM_SETBKIMAGEA:
  3572. return (LRESULT)ListView_OnSetBkImageA(plv, (LPLVBKIMAGEA)lParam);
  3573. case WM_STYLECHANGING:
  3574. ListView_OnStyleChanging(plv, (UINT)wParam, (LPSTYLESTRUCT)lParam);
  3575. return 0;
  3576. case WM_STYLECHANGED:
  3577. ListView_OnStyleChanged(plv, (UINT) wParam, (LPSTYLESTRUCT)lParam);
  3578. return 0L;
  3579. case WM_HELP:
  3580. return ListView_OnHelp(plv, (LPHELPINFO)lParam);
  3581. case LVM_GETIMAGELIST:
  3582. return (LRESULT)(UINT_PTR)(ListView_OnGetImageList(plv, (int)wParam));
  3583. case LVM_SETIMAGELIST:
  3584. return (LRESULT)(UINT_PTR)ListView_OnSetImageList(plv, (HIMAGELIST)lParam, (int)wParam);
  3585. case LVM_GETBKCOLOR:
  3586. return (LRESULT)(plv->ci.style & WS_DISABLED ? plv->clrBkSave : plv->clrBk);
  3587. case LVM_SETBKCOLOR:
  3588. plv->flags |= LVF_USERBKCLR;
  3589. if (plv->ci.style & WS_DISABLED)
  3590. {
  3591. plv->clrBkSave = (COLORREF)lParam;
  3592. return TRUE;
  3593. }
  3594. else
  3595. {
  3596. return (LRESULT)ListView_OnSetBkColor(plv, (COLORREF)lParam);
  3597. }
  3598. case LVM_GETTEXTCOLOR:
  3599. return (LRESULT)plv->clrText;
  3600. case LVM_SETTEXTCOLOR:
  3601. plv->clrText = (COLORREF)lParam;
  3602. return TRUE;
  3603. case LVM_GETTEXTBKCOLOR:
  3604. return (LRESULT)plv->clrTextBk;
  3605. case LVM_SETTEXTBKCOLOR:
  3606. plv->clrTextBk = (COLORREF)lParam;
  3607. return TRUE;
  3608. case LVM_GETHOTLIGHTCOLOR:
  3609. return (LRESULT)plv->clrHotlight;
  3610. case LVM_SETHOTLIGHTCOLOR:
  3611. plv->clrHotlight = (COLORREF)lParam;
  3612. return TRUE;
  3613. case LVM_GETITEMCOUNT:
  3614. if (ListView_IsOwnerData(plv))
  3615. return (LRESULT)plv->cTotalItems;
  3616. else if (!plv->hdpa)
  3617. return 0;
  3618. else
  3619. return (LRESULT)DPA_GetPtrCount(plv->hdpa);
  3620. break;
  3621. case LVM_GETITEM:
  3622. return (LRESULT)ListView_OnGetItem(plv, (LV_ITEM*)lParam);
  3623. case LVM_GETITEMSTATE:
  3624. return (LRESULT)ListView_OnGetItemState(plv, (int)wParam, (UINT)lParam);
  3625. case LVM_SETITEMSTATE:
  3626. if (!lParam)
  3627. return FALSE;
  3628. return (LRESULT)ListView_OnSetItemState(plv, (int)wParam,
  3629. ((LV_ITEM *)lParam)->state,
  3630. ((LV_ITEM *)lParam)->stateMask);
  3631. case LVM_SETITEMTEXT:
  3632. if (!lParam)
  3633. return FALSE;
  3634. return (LRESULT)ListView_OnSetItemText(plv, (int)wParam,
  3635. ((LV_ITEM *)lParam)->iSubItem,
  3636. (LPCTSTR)((LV_ITEM *)lParam)->pszText);
  3637. case LVM_GETITEMTEXT:
  3638. return (LRESULT)ListView_OnGetItemText(plv, (int)wParam, (LV_ITEM *)lParam);
  3639. case LVM_GETBKIMAGE:
  3640. return (LRESULT)ListView_OnGetBkImage(plv, (LPLVBKIMAGE)lParam);
  3641. case LVM_SETBKIMAGE:
  3642. return (LRESULT)ListView_OnSetBkImage(plv, (LPLVBKIMAGE)lParam);
  3643. case LVM_GETSELECTEDCOLUMN:
  3644. return plv->iLastColSort;
  3645. case LVM_SETSELECTEDCOLUMN:
  3646. plv->iLastColSort = (int) wParam;
  3647. if (ListView_IsTileView(plv))
  3648. {
  3649. // Tileview displays the selected column on the second line, if available. The second
  3650. // line might be blank w/o it. So when this changes, we need to recompute each tile.
  3651. if (!ListView_IsOwnerData(plv))
  3652. {
  3653. int i;
  3654. for (i = 0; i < ListView_Count(plv); i++)
  3655. {
  3656. LISTITEM *pitem = ListView_FastGetItemPtr(plv, i);
  3657. ListView_SetSRecompute(pitem);
  3658. }
  3659. }
  3660. plv->rcView.left = RECOMPUTE;
  3661. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3662. }
  3663. return 1;
  3664. case LVM_SETVIEW:
  3665. return ListView_SetViewType(plv, (WORD)wParam);
  3666. case LVM_GETVIEW:
  3667. return plv->wView;
  3668. case LVM_SETITEM:
  3669. return (LRESULT)ListView_OnSetItem(plv, (const LV_ITEM*)lParam);
  3670. case LVM_INSERTITEM:
  3671. return (LRESULT)ListView_OnInsertItem(plv, (const LV_ITEM*)lParam);
  3672. case LVM_DELETEITEM:
  3673. return (LRESULT)ListView_OnDeleteItem(plv, (int)wParam);
  3674. case LVM_UPDATE:
  3675. ListView_OnUpdate(plv, (int)wParam);
  3676. UpdateWindow(plv->ci.hwnd);
  3677. return TRUE;
  3678. case LVM_DELETEALLITEMS:
  3679. lParam = (LRESULT)ListView_OnDeleteAllItems(plv);
  3680. // Optimization: Instead of sending out a zillion EVENT_OBJECT_DESTROY's,
  3681. // we send out a destroy of ourselves followed by a fresh create.
  3682. // For compatibility with IE4, we still send out the REORDER notification.
  3683. NotifyWinEvent(EVENT_OBJECT_REORDER, hwnd, OBJID_CLIENT, 0);
  3684. ListView_NotifyRecreate(plv);
  3685. return lParam;
  3686. case LVM_GETITEMRECT:
  3687. return (LRESULT)ListView_OnGetItemRect(plv, (int)wParam, (RECT*)lParam);
  3688. case LVM_GETSUBITEMRECT:
  3689. return (LRESULT)ListView_OnGetSubItemRect(plv, (int)wParam, (LPRECT)lParam);
  3690. case LVM_SUBITEMHITTEST:
  3691. return (LRESULT)ListView_OnSubItemHitTest(plv, (LPLVHITTESTINFO)lParam);
  3692. case LVM_GETISEARCHSTRINGA:
  3693. if (GetFocus() == plv->ci.hwnd)
  3694. return (LRESULT)GetIncrementSearchStringA(&plv->is, plv->ci.uiCodePage, (LPSTR)lParam);
  3695. else
  3696. return 0;
  3697. case LVM_GETISEARCHSTRING:
  3698. if (GetFocus() == plv->ci.hwnd)
  3699. return (LRESULT)GetIncrementSearchString(&plv->is, (LPTSTR)lParam);
  3700. else
  3701. return 0;
  3702. case LVM_GETITEMSPACING:
  3703. if (wParam)
  3704. return MAKELONG(plv->cxItem, plv->cyItem);
  3705. else
  3706. return MAKELONG(plv->cxIconSpacing, plv->cyIconSpacing);
  3707. case LVM_GETNEXTITEM:
  3708. return (LRESULT)ListView_OnGetNextItem(plv, (int)wParam, (UINT)lParam);
  3709. case LVM_FINDITEM:
  3710. return (LRESULT)ListView_OnFindItem(plv, (int)wParam, (const LV_FINDINFO*)lParam);
  3711. case LVM_SETSELECTIONMARK:
  3712. {
  3713. int iOldMark = plv->iMark;
  3714. int iNewMark = (int)lParam;
  3715. if (iNewMark == -1 || ListView_IsValidItemNumber(plv, iNewMark))
  3716. {
  3717. plv->iMark = iNewMark;
  3718. }
  3719. return iOldMark;
  3720. }
  3721. case LVM_GETSELECTIONMARK:
  3722. return plv->iMark;
  3723. case LVM_GETITEMPOSITION:
  3724. return (LRESULT)ListView_OnGetItemPosition(plv, (int)wParam,
  3725. (POINT*)lParam);
  3726. case LVM_SETITEMPOSITION:
  3727. return (LRESULT)ListView_OnSetItemPosition(plv, (int)wParam,
  3728. GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  3729. case LVM_SETITEMPOSITION32:
  3730. if (!lParam)
  3731. return FALSE;
  3732. return (LRESULT)ListView_OnSetItemPosition(plv, (int)wParam,
  3733. ((LPPOINT)lParam)->x, ((LPPOINT)lParam)->y);
  3734. case LVM_SCROLL:
  3735. {
  3736. int dx = (int)wParam;
  3737. int dy = (int)lParam;
  3738. return (LRESULT)(ListView_ValidateScrollParams(plv, &dx, &dy) &&
  3739. ListView_OnScroll(plv, dx, dy));
  3740. }
  3741. case LVM_ENSUREVISIBLE:
  3742. return (LRESULT)ListView_OnEnsureVisible(plv, (int)wParam, BOOLFROMPTR(lParam));
  3743. case LVM_REDRAWITEMS:
  3744. return (LRESULT)ListView_OnRedrawItems(plv, (int)wParam, (int)lParam);
  3745. case LVM_ARRANGE:
  3746. return (LRESULT)ListView_OnArrange(plv, (UINT)wParam);
  3747. case LVM_GETEDITCONTROL:
  3748. return (LRESULT)(UINT_PTR)plv->hwndEdit;
  3749. case LVM_EDITLABELA:
  3750. {
  3751. LPWSTR lpEditString = NULL;
  3752. HWND hRet;
  3753. if (lParam)
  3754. {
  3755. lpEditString = ProduceWFromA(plv->ci.uiCodePage, (LPSTR)lParam);
  3756. }
  3757. hRet = ListView_OnEditLabel(plv, (int)wParam, lpEditString);
  3758. if (lpEditString)
  3759. {
  3760. FreeProducedString(lpEditString);
  3761. }
  3762. return (LRESULT)hRet;
  3763. }
  3764. case LVM_EDITLABEL:
  3765. return (LRESULT)(UINT_PTR)ListView_OnEditLabel(plv, (int)wParam, (LPTSTR)lParam);
  3766. case LVM_HITTEST:
  3767. return (LRESULT)ListView_OnHitTest(plv, (LV_HITTESTINFO*)lParam);
  3768. case LVM_GETSTRINGWIDTH:
  3769. return (LRESULT)ListView_OnGetStringWidth(plv, (LPCTSTR)lParam, NULL);
  3770. case LVM_GETCOLUMN:
  3771. return (LRESULT)ListView_OnGetColumn(plv, (int)wParam, (LV_COLUMN*)lParam);
  3772. case LVM_SETCOLUMN:
  3773. return (LRESULT)ListView_OnSetColumn(plv, (int)wParam, (const LV_COLUMN*)lParam);
  3774. case LVM_SETCOLUMNORDERARRAY:
  3775. return SendMessage(plv->hwndHdr, HDM_SETORDERARRAY, wParam, lParam);
  3776. case LVM_GETCOLUMNORDERARRAY:
  3777. return SendMessage(plv->hwndHdr, HDM_GETORDERARRAY, wParam, lParam);
  3778. case LVM_GETHEADER:
  3779. {
  3780. HWND hwndOld = plv->hwndHdr;
  3781. if (lParam && IsWindow((HWND)lParam))
  3782. {
  3783. plv->hwndHdr = (HWND)lParam;
  3784. }
  3785. return (LRESULT)hwndOld;
  3786. }
  3787. case LVM_INSERTCOLUMN:
  3788. return (LRESULT)ListView_OnInsertColumn(plv, (int)wParam, (const LV_COLUMN*)lParam);
  3789. case LVM_DELETECOLUMN:
  3790. return (LRESULT)ListView_OnDeleteColumn(plv, (int)wParam);
  3791. case LVM_CREATEDRAGIMAGE:
  3792. return (LRESULT)(UINT_PTR)ListView_OnCreateDragImage(plv, (int)wParam, (LPPOINT)lParam);
  3793. case LVMI_PLACEITEMS:
  3794. if (plv->uUnplaced)
  3795. {
  3796. ListView_Recompute(plv);
  3797. ListView_UpdateScrollBars(plv);
  3798. }
  3799. return 0;
  3800. case LVM_GETVIEWRECT:
  3801. return (LPARAM)ListView_OnGetViewRect(plv, (RECT*)lParam);
  3802. case LVM_GETCOLUMNWIDTH:
  3803. return (LPARAM)ListView_OnGetColumnWidth(plv, (int)wParam);
  3804. case LVM_SETCOLUMNWIDTH:
  3805. return (LPARAM)ListView_ISetColumnWidth(plv, (int)wParam,
  3806. GET_X_LPARAM(lParam), TRUE);
  3807. case LVM_SETCALLBACKMASK:
  3808. plv->stateCallbackMask = (UINT)wParam;
  3809. return (LPARAM)TRUE;
  3810. case LVM_GETCALLBACKMASK:
  3811. return (LPARAM)(UINT)plv->stateCallbackMask;
  3812. case LVM_GETTOPINDEX:
  3813. return (LPARAM)ListView_OnGetTopIndex(plv);
  3814. case LVM_GETCOUNTPERPAGE:
  3815. return (LPARAM)ListView_OnGetCountPerPage(plv);
  3816. case LVM_GETORIGIN:
  3817. return (LPARAM)ListView_OnGetOrigin(plv, (POINT*)lParam);
  3818. case LVM_SETITEMCOUNT:
  3819. return ListView_OnSetItemCount(plv, (int)wParam, (DWORD)lParam);
  3820. case LVM_GETSELECTEDCOUNT:
  3821. if (ListView_IsOwnerData(plv))
  3822. {
  3823. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  3824. }
  3825. return plv->nSelected;
  3826. case LVM_SORTITEMS:
  3827. return ListView_OnSortItems(plv, (LPARAM)wParam, (PFNLVCOMPARE)lParam, TRUE);
  3828. case LVM_SORTITEMSEX:
  3829. return ListView_OnSortItems(plv, (LPARAM)wParam, (PFNLVCOMPARE)lParam, FALSE);
  3830. case LVM_SETEXTENDEDLISTVIEWSTYLE:
  3831. return ListView_ExtendedStyleChange(plv, (DWORD) lParam, (DWORD) wParam);
  3832. case LVM_GETEXTENDEDLISTVIEWSTYLE:
  3833. return plv->exStyle;
  3834. case LVM_GETHOVERTIME:
  3835. return plv->dwHoverTime;
  3836. case LVM_SETHOVERTIME:
  3837. {
  3838. DWORD dwRet = plv->dwHoverTime;
  3839. plv->dwHoverTime = (DWORD)lParam;
  3840. return dwRet;
  3841. }
  3842. case LVM_GETTOOLTIPS:
  3843. return (LRESULT)plv->hwndToolTips;
  3844. case LVM_SETTOOLTIPS:
  3845. {
  3846. HWND hwndToolTips = plv->hwndToolTips;
  3847. plv->hwndToolTips = (HWND)wParam;
  3848. return (LRESULT)hwndToolTips;
  3849. }
  3850. case LVM_SETICONSPACING:
  3851. {
  3852. DWORD dwRet = ListView_OnSetIconSpacing(plv, lParam);
  3853. // rearrange as necessary
  3854. if (ListView_RedrawEnabled(plv) &&
  3855. (ListView_IsSmallView(plv) || ListView_IsIconView(plv)))
  3856. {
  3857. ListView_ArrangeOrSnapToGrid(plv);
  3858. }
  3859. return dwRet;
  3860. }
  3861. case LVM_SETHOTITEM:
  3862. {
  3863. int iOld = plv->iHot;
  3864. int iNew = (int)wParam;
  3865. if (iNew == -1 || ListView_IsValidItemNumber(plv, iNew))
  3866. {
  3867. ListView_OnSetHotItem(plv, (int)wParam);
  3868. }
  3869. return iOld;
  3870. }
  3871. case LVM_GETHOTITEM:
  3872. return plv->iHot;
  3873. // hCurHot is used iff LVS_EX_TRACKSELECT
  3874. case LVM_SETHOTCURSOR:
  3875. {
  3876. HCURSOR hCurOld = plv->hCurHot;
  3877. plv->hCurHot = (HCURSOR)lParam;
  3878. return (LRESULT)hCurOld;
  3879. }
  3880. case LVM_GETHOTCURSOR:
  3881. if (!plv->hCurHot)
  3882. {
  3883. plv->hCurHot = LoadCursor(NULL, IDC_HAND);
  3884. }
  3885. return (LRESULT)plv->hCurHot;
  3886. case LVM_APPROXIMATEVIEWRECT:
  3887. return ListView_OnApproximateViewRect(plv, (int)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  3888. case LVM_SETLVRANGEOBJECT:
  3889. return ListView_OnSetLVRangeObject(plv, (int)wParam, (ILVRange *)lParam);
  3890. case LVM_SETWORKAREAS:
  3891. ListView_OnSetWorkAreas(plv, (int)wParam, (RECT *)lParam);
  3892. return 0;
  3893. case LVM_GETWORKAREAS:
  3894. ListView_OnGetWorkAreas(plv, (int)wParam, (RECT *)lParam);
  3895. return 0;
  3896. case LVM_GETNUMBEROFWORKAREAS:
  3897. ListView_OnGetNumberOfWorkAreas(plv, (int *)lParam);
  3898. return 0;
  3899. case LVM_RESETEMPTYTEXT:
  3900. plv->fNoEmptyText = FALSE;
  3901. Str_Set(&plv->pszEmptyText, NULL);
  3902. if (ListView_Count(plv) == 0)
  3903. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3904. return 1;
  3905. case LVM_INSERTGROUPSORTED:
  3906. return ListView_OnInsertGroupSorted(plv, (LVINSERTGROUPSORTED*) wParam);
  3907. case LVM_SORTGROUPS:
  3908. return ListView_OnSortGroups(plv, (PFNLVGROUPCOMPARE)wParam, (void *)lParam);
  3909. case LVM_ENABLEGROUPVIEW:
  3910. return ListView_OnEnableGroupView(plv, (BOOL)wParam);
  3911. case LVM_ISGROUPVIEWENABLED:
  3912. return plv->fGroupView;
  3913. case LVM_INSERTGROUP:
  3914. return ListView_OnInsertGroup(plv, (int) wParam, (PLVGROUP)lParam);
  3915. case LVM_SETGROUPINFO:
  3916. return ListView_OnSetGroupInfo(plv, (int) wParam, (PLVGROUP)lParam);
  3917. case LVM_GETGROUPINFO:
  3918. return ListView_OnGetGroupInfo(plv, (int) wParam, (PLVGROUP)lParam);
  3919. case LVM_REMOVEGROUP:
  3920. return ListView_OnRemoveGroup(plv, (int) wParam);
  3921. case LVM_REMOVEALLGROUPS:
  3922. return ListView_OnRemoveAllGroups(plv);
  3923. case LVM_HASGROUP:
  3924. {
  3925. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, (int)wParam, NULL);
  3926. if (pgrp)
  3927. return 1;
  3928. else
  3929. return 0;
  3930. }
  3931. break;
  3932. case LVM_SETGROUPMETRICS:
  3933. return ListView_OnSetGroupMetrics(plv, (PLVGROUPMETRICS)lParam);
  3934. case LVM_GETGROUPMETRICS:
  3935. return ListView_OnGetGroupMetrics(plv, (PLVGROUPMETRICS)lParam);
  3936. case LVM_SETTILEVIEWINFO:
  3937. return ListView_OnSetTileViewInfo(plv, (PLVTILEVIEWINFO)lParam);
  3938. case LVM_GETTILEVIEWINFO:
  3939. return ListView_OnGetTileViewInfo(plv, (PLVTILEVIEWINFO)lParam);
  3940. case LVM_SETTILEINFO:
  3941. return ListView_OnSetTileInfo(plv, (PLVTILEINFO)lParam);
  3942. case LVM_GETTILEINFO:
  3943. return ListView_OnGetTileInfo(plv, (PLVTILEINFO)lParam);
  3944. case LVM_SETINSERTMARK:
  3945. if (ListView_IsRearrangeableView(plv) && (plv->ci.style & LVS_AUTOARRANGE) && !plv->fGroupView)
  3946. return ListView_OnSetInsertMark(plv, (LPLVINSERTMARK)lParam);
  3947. else
  3948. return FALSE;
  3949. case LVM_GETINSERTMARK:
  3950. {
  3951. LPLVINSERTMARK plvim = (LPLVINSERTMARK)lParam;
  3952. if (plvim->cbSize != sizeof(LVINSERTMARK))
  3953. return FALSE;
  3954. plvim->dwFlags = (plv->fInsertAfter ? LVIM_AFTER : 0) | LVIM_SETFROMINFO;
  3955. plvim->iItem = plv->iInsertItem;
  3956. return TRUE;
  3957. }
  3958. case LVM_GETINSERTMARKRECT:
  3959. {
  3960. return ListView_OnGetInsertMarkRect(plv, (LPRECT)lParam);
  3961. }
  3962. case LVM_SETINSERTMARKCOLOR:
  3963. {
  3964. LRESULT lres = (LRESULT)ListView_OnGetInsertMarkColor(plv);
  3965. plv->clrim = (COLORREF) lParam;
  3966. return lres;
  3967. }
  3968. case LVM_GETINSERTMARKCOLOR:
  3969. return ListView_OnGetInsertMarkColor(plv);
  3970. case LVM_INSERTMARKHITTEST:
  3971. {
  3972. LPPOINT ppt = (LPPOINT)wParam;
  3973. return ListView_OnInsertMarkHitTest(plv, ppt->x, ppt->y, (LPLVINSERTMARK)lParam);
  3974. }
  3975. case LVM_SETINFOTIP:
  3976. {
  3977. return ListView_OnSetInfoTip(plv, (PLVSETINFOTIP)lParam);
  3978. }
  3979. case LVM_SETOUTLINECOLOR:
  3980. {
  3981. LRESULT lres = (LRESULT)plv->clrOutline;
  3982. plv->clrOutline = (COLORREF) lParam;
  3983. return lres;
  3984. }
  3985. case LVM_GETOUTLINECOLOR:
  3986. return (LRESULT)plv->clrOutline;
  3987. case LVM_SETFROZENITEM:
  3988. return ListView_OnSetFrozenItem(plv, (BOOL) wParam, (int) lParam);
  3989. case LVM_GETFROZENITEM:
  3990. return ListView_OnGetFrozenItem(plv);
  3991. case LVM_SETFROZENSLOT:
  3992. return ListView_OnSetFrozenSlot(plv, (BOOL) wParam, (LPPOINT)lParam);
  3993. case LVM_GETFROZENSLOT:
  3994. return ListView_OnGetFrozenSlot(plv, (LPRECT)lParam);
  3995. case LVM_SETVIEWMARGINS:
  3996. return ListView_OnSetViewMargins(plv, (LPRECT)lParam);
  3997. case LVM_GETVIEWMARGINS:
  3998. return ListView_OnGetViewMargins(plv, (LPRECT)lParam);
  3999. case LVM_KEYBOARDSELECTED:
  4000. ListView_CancelTipTrack(plv);
  4001. return lParam == 0 ? ListView_OnKeyboardSelected(plv, (int)wParam) : FALSE;
  4002. case LVM_CANCELEDITLABEL:
  4003. ListView_DismissEdit(plv, FALSE);
  4004. return 1;
  4005. case LVM_MAPINDEXTOID:
  4006. return ListView_OnMapIndexToID(plv, (UINT)wParam);
  4007. case LVM_MAPIDTOINDEX:
  4008. return ListView_OnMapIdToIndex(plv, (UINT)wParam);
  4009. case LVM_ISITEMVISIBLE:
  4010. if (ListView_IsValidItemNumber(plv, (UINT)wParam))
  4011. {
  4012. return ListView_IsItemVisibleI(plv, (UINT)wParam);
  4013. }
  4014. else
  4015. {
  4016. return FALSE;
  4017. }
  4018. case WM_SIZE:
  4019. if (plv)
  4020. {
  4021. ListView_OnSize(plv);
  4022. }
  4023. break;
  4024. case WM_NOTIFY:
  4025. return ListView_OnNotify(plv, wParam, (LPNMHDR)lParam);
  4026. case WM_MOUSEMOVE:
  4027. // Cancel manual track if mouse moved
  4028. if (plv->lLastMMove != lParam)
  4029. {
  4030. ListView_CancelTipTrack(plv);
  4031. if (plv->hwndToolTips)
  4032. {
  4033. UINT uFlags;
  4034. int iHit, iSubHit;
  4035. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  4036. if (!ListView_IsKbdTipTracking(plv))
  4037. {
  4038. // check that we are still on the hit item, pop it!
  4039. iHit = _ListView_ItemHitTest(plv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &uFlags, &iSubHit);
  4040. if (iHit != plv->iTTLastHit || iSubHit != plv->iTTLastSubHit)
  4041. ListView_PopBubble(plv);
  4042. }
  4043. }
  4044. }
  4045. plv->lLastMMove = lParam;
  4046. ListView_OnMouseMove(plv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  4047. break;
  4048. case WM_GETOBJECT:
  4049. if (lParam == OBJID_QUERYCLASSNAMEIDX)
  4050. return MSAA_CLASSNAMEIDX_LISTVIEW;
  4051. break;
  4052. case WM_THEMECHANGED:
  4053. if (plv->hTheme)
  4054. CloseThemeData(plv->hTheme);
  4055. plv->hTheme = OpenThemeData(plv->ci.hwnd, L"ListView");
  4056. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  4057. break;
  4058. default:
  4059. {
  4060. LRESULT lres;
  4061. if (CCWndProc(&plv->ci, uMsg, wParam, lParam, &lres))
  4062. return lres;
  4063. }
  4064. if (uMsg == g_msgMSWheel)
  4065. {
  4066. DWORD dwStyle;
  4067. int sb;
  4068. SCROLLINFO si;
  4069. int cScrollUnitsPerLine;
  4070. int cPage;
  4071. int cLinesPerDetant;
  4072. int cDetants;
  4073. int dPos;
  4074. int iWheelDelta = (int)(short)HIWORD(wParam);
  4075. BOOL fScroll = !(wParam & (MK_SHIFT | MK_CONTROL));
  4076. BOOL fDataZoom = (BOOL) (wParam & MK_SHIFT);
  4077. // Update count of scroll amount
  4078. gcWheelDelta -= iWheelDelta;
  4079. cDetants = gcWheelDelta / WHEEL_DELTA;
  4080. if (cDetants != 0)
  4081. {
  4082. gcWheelDelta %= WHEEL_DELTA;
  4083. }
  4084. if (fScroll)
  4085. {
  4086. if (g_ucScrollLines > 0 &&
  4087. cDetants != 0 &&
  4088. ((WS_VSCROLL | WS_HSCROLL) & (dwStyle = ListView_GetWindowStyle(plv))))
  4089. {
  4090. sb = (dwStyle & WS_VSCROLL) ? SB_VERT : SB_HORZ;
  4091. // Get the scroll amount of one line
  4092. cScrollUnitsPerLine = _ListView_GetScrollUnitsPerLine(plv, sb);
  4093. ASSERT(cScrollUnitsPerLine > 0);
  4094. si.cbSize = sizeof(SCROLLINFO);
  4095. si.fMask = SIF_PAGE | SIF_POS;
  4096. if (!ListView_GetScrollInfo(plv, sb, &si))
  4097. return 1;
  4098. // The size of a page is at least one line, and
  4099. // leaves one line of overlap
  4100. cPage = (max(cScrollUnitsPerLine, (int)si.nPage - cScrollUnitsPerLine)) / cScrollUnitsPerLine;
  4101. // Don't scroll more than one page per detant
  4102. cLinesPerDetant = (int) min((ULONG) cPage, (ULONG) g_ucScrollLines);
  4103. dPos = cLinesPerDetant * cDetants * cScrollUnitsPerLine;
  4104. ListView_DismissEdit(plv, FALSE);
  4105. ListView_ComOnScroll(plv, SB_THUMBTRACK, si.nPos + dPos,
  4106. sb, cScrollUnitsPerLine, - 1);
  4107. ListView_UpdateScrollBars(plv);
  4108. // After scrolling, the tooltip might need to change
  4109. // so send the tooltip a fake mousemove message to force
  4110. // a recompute. We use WM_NCMOUSEMOVE since our lParam
  4111. // is in screen coordinates, not client coordinates.
  4112. ListView_PopBubble(plv);
  4113. RelayToToolTips(plv->hwndToolTips, plv->ci.hwnd,
  4114. WM_NCMOUSEMOVE, HTCLIENT, lParam);
  4115. }
  4116. return 1;
  4117. }
  4118. else if (fDataZoom)
  4119. {
  4120. LV_HITTESTINFO ht;
  4121. ht.pt.x = GET_X_LPARAM(lParam);
  4122. ht.pt.y = GET_Y_LPARAM(lParam);
  4123. ScreenToClient(hwnd, &(ht.pt));
  4124. // If we are rolling forward and we hit an item then navigate
  4125. // into that item (simulate dblclk which will open it). Otherwise
  4126. // just fall through so it isn't handled. In that case if we
  4127. // are being hosted in explorer it will do a backwards
  4128. // history navigation.
  4129. if ((iWheelDelta > 0) && (ListView_OnSubItemHitTest(plv, &ht) >= 0) &&
  4130. (ht.flags & LVHT_ONITEM) && cDetants != 0)
  4131. {
  4132. BYTE aKeyState[256];
  4133. // This is a bit yucky but when ListView_HandleMouse sends the
  4134. // notification to the listview owner we need to make sure that
  4135. // it doesn't think the shift key is down. Otherwise it may
  4136. // perform some "alternate" action but in this case we always
  4137. // want it to perform the default open action.
  4138. //
  4139. // Strip the high bit of VK_SHIFT so that the shift key is
  4140. // not down.
  4141. GetKeyboardState(aKeyState);
  4142. aKeyState[VK_SHIFT] &= 0x7f;
  4143. SetKeyboardState(aKeyState);
  4144. ListView_HandleMouse(plv, FALSE, ht.pt.x, ht.pt.y, 0, TRUE);
  4145. ListView_HandleMouse(plv, TRUE, ht.pt.x, ht.pt.y, 0, TRUE);
  4146. return 1;
  4147. }
  4148. // else fall through
  4149. }
  4150. }
  4151. break;
  4152. }
  4153. DoDefault:
  4154. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  4155. }
  4156. BOOL Listview_UpdateViewEffects(LV* plv)
  4157. {
  4158. BOOL fChanged = FALSE;
  4159. UINT fScroll = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewScrollOver"),
  4160. FALSE, // Don't ignore HKCU
  4161. LISTVIEW_VFX_DEFAULT); // Assume a fast enough machine
  4162. UINT fWatermark = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewWatermark"),
  4163. FALSE, // Don't ignore HKCU
  4164. LISTVIEW_VFX_DEFAULT); // Assume a fast enough machine
  4165. UINT fAlphaSelect = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewAlphaSelect"),
  4166. FALSE, // Don't ignore HKCU
  4167. LISTVIEW_VFX_DEFAULT); // Assume a fast enough machine
  4168. UINT fShadow = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewShadow"),
  4169. FALSE, // Don't ignore HKCU
  4170. LISTVIEW_VFX_DEFAULT); // Assume a fast enough machine
  4171. if (plv->fListviewAlphaSelect != fAlphaSelect ||
  4172. plv->fListviewShadowText != fShadow ||
  4173. plv->fListviewWatermarkBackgroundImages != fScroll ||
  4174. plv->fListviewEnableWatermark != fWatermark)
  4175. {
  4176. fChanged = TRUE;
  4177. }
  4178. plv->fListviewAlphaSelect = BOOLIFY(fAlphaSelect);
  4179. plv->fListviewShadowText = BOOLIFY(fShadow);
  4180. plv->fListviewWatermarkBackgroundImages = BOOLIFY(fScroll);
  4181. plv->fListviewEnableWatermark = BOOLIFY(fWatermark);
  4182. return fChanged;
  4183. }
  4184. void ListView_OnWinIniChange(LV* plv, WPARAM wParam, LPARAM lParam)
  4185. {
  4186. // REARCHITECT: will this also catch sysparametersinfo?
  4187. // we need a general way of handling this, not
  4188. // just relying on the listview.
  4189. InitGlobalMetrics(wParam);
  4190. if (Listview_UpdateViewEffects(plv))
  4191. {
  4192. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  4193. }
  4194. switch (wParam)
  4195. {
  4196. case 0:
  4197. case SPI_SETNONCLIENTMETRICS:
  4198. case SPI_SETICONTITLELOGFONT:
  4199. case SPI_SETICONMETRICS:
  4200. // If wParam is 0, only reload settings if lParam is 0 too. This catches the wild-card scenario
  4201. // (like the old plus tab which does WM_WININICHANGE, 0, 0) but allows us to ignore wParam = 0
  4202. // and lParam = lpszSectionName. Reduces unecessary flashing.
  4203. if (wParam || !lParam)
  4204. {
  4205. if (!(plv->flags & LVF_ICONSPACESET))
  4206. ListView_OnSetIconSpacing(plv, (LPARAM)-1);
  4207. if (plv->flags & LVF_FONTCREATED)
  4208. ListView_OnSetFont(plv, NULL, TRUE);
  4209. // Force a recalc of all the icon regions by stripping and
  4210. // then adding back the LVS_EX_REGIONAL bit.
  4211. if (plv->exStyle & LVS_EX_REGIONAL)
  4212. {
  4213. ListView_ExtendedStyleChange(plv, 0, LVS_EX_REGIONAL);
  4214. ListView_ExtendedStyleChange(plv, LVS_EX_REGIONAL, LVS_EX_REGIONAL);
  4215. }
  4216. }
  4217. break;
  4218. default:
  4219. break;
  4220. }
  4221. // If we are in an Iconic view and the user is in autoarrange mode,
  4222. // then we need to arrange the items.
  4223. //
  4224. if ((ListView_IsSmallView(plv) || ListView_IsIconView(plv)))
  4225. {
  4226. // Call off to the arrange function.
  4227. if (ListView_IsOwnerData(plv))
  4228. ListView_OnArrange(plv, LVA_DEFAULT);
  4229. else
  4230. ListView_ArrangeOrSnapToGrid(plv);
  4231. }
  4232. }
  4233. BOOL ListView_OnCreate(LV* plv, CREATESTRUCT* lpCreateStruct)
  4234. {
  4235. Listview_UpdateViewEffects(plv);
  4236. InitDitherBrush();
  4237. CIInitialize(&plv->ci, plv->ci.hwnd, lpCreateStruct);
  4238. plv->wView = (WORD)(plv->ci.style & LVS_TYPEMASK);
  4239. plv->dwExStyle = lpCreateStruct->dwExStyle;
  4240. if (plv->ci.style & WS_VISIBLE)
  4241. plv->flags |= LVF_VISIBLE;
  4242. ListView_GetRegIASetting(&g_bUseDblClickTimer);
  4243. if (ListView_IsOwnerData(plv))
  4244. {
  4245. // ownerdata initialization
  4246. plv->plvrangeSel = LVRange_Create();
  4247. if (NULL == plv->plvrangeSel)
  4248. goto error0;
  4249. plv->plvrangeCut = LVRange_Create();
  4250. if (NULL == plv->plvrangeCut)
  4251. goto error0;
  4252. }
  4253. else
  4254. {
  4255. ASSERT(plv->plvrangeSel == NULL);
  4256. ASSERT(plv->plvrangeCut == NULL);
  4257. plv->hdpa = DPA_CreateEx(LV_HDPA_GROW, plv->hheap);
  4258. if (!plv->hdpa)
  4259. goto error0;
  4260. plv->hdpaZOrder = DPA_CreateEx(LV_HDPA_GROW, plv->hheap);
  4261. if (!plv->hdpaZOrder)
  4262. goto error1;
  4263. }
  4264. ASSERT(plv->nWorkAreas == 0);
  4265. ASSERT(plv->prcWorkAreas == NULL);
  4266. ASSERT(plv->fIconsPositioned == FALSE);
  4267. plv->iNoHover = -1;
  4268. plv->dwHoverTime = HOVER_DEFAULT;
  4269. plv->iHot = -1;
  4270. plv->iEdit = -1;
  4271. plv->iFocus = -1;
  4272. plv->iDrag = -1;
  4273. plv->iTTLastHit = -1;
  4274. plv->iFreeSlot = -1;
  4275. plv->rcView.left = RECOMPUTE;
  4276. plv->iLastColSort = -1;
  4277. ASSERT(plv->sizeTile.cx == 0);
  4278. ASSERT(plv->sizeTile.cy == 0);
  4279. ASSERT(plv->dwTileFlags == 0);
  4280. plv->cSubItems = 1;
  4281. SetRect(&plv->rcBorder, 0, 12, 0, 0);
  4282. plv->crHeader = GetSysColor(COLOR_WINDOWTEXT);
  4283. plv->crTop = GetSysColor(COLOR_BTNFACE);
  4284. plv->crBottom = CLR_NONE;
  4285. plv->crLeft = CLR_NONE;
  4286. plv->crRight = CLR_NONE;
  4287. plv->paddingLeft = 12;
  4288. plv->paddingTop = 12;
  4289. plv->paddingRight = 0;
  4290. plv->paddingBottom = 12;
  4291. plv->szWatermark.cx = 200;
  4292. plv->szWatermark.cy = 200;
  4293. ASSERT(plv->iMSAAMin == plv->iMSAAMax);
  4294. plv->sizeClient.cx = lpCreateStruct->cx;
  4295. plv->sizeClient.cy = lpCreateStruct->cy;
  4296. // Setup flag to say if positions are in small or large view
  4297. if (ListView_IsSmallView(plv))
  4298. plv->flags |= LVF_ICONPOSSML;
  4299. // force calculation of listview metrics
  4300. ListView_OnSetFont(plv, NULL, FALSE);
  4301. plv->cxItem = ListView_ComputeCXItemSize(plv);
  4302. // if we're in ownerdraw report mode, the size got saved to cyItemSave
  4303. // at creation time, both need to have this
  4304. if ((plv->ci.style & LVS_OWNERDRAWFIXED) && ListView_IsReportView(plv))
  4305. plv->cyItem = plv->cyItemSave;
  4306. else
  4307. plv->cyItemSave = plv->cyItem;
  4308. ListView_OnSetIconSpacing(plv, (LPARAM)-1);
  4309. ListView_UpdateScrollBars(plv); // sets plv->cItemCol
  4310. plv->clrBk = CLR_NONE;
  4311. plv->clrText = CLR_DEFAULT;
  4312. plv->clrTextBk = CLR_DEFAULT;
  4313. plv->clrHotlight = CLR_DEFAULT;
  4314. plv->clrOutline = CLR_DEFAULT;
  4315. // create the bk brush, and set the imagelists colors if needed
  4316. ListView_OnSetBkColor(plv, g_clrWindow);
  4317. // Initialize report view fields
  4318. plv->xTotalColumnWidth = RECOMPUTE;
  4319. if (ListView_IsReportView(plv))
  4320. ListView_RInitialize(plv, FALSE);
  4321. if (plv->ci.style & WS_DISABLED)
  4322. {
  4323. plv->ci.style &= ~WS_DISABLED;
  4324. ListView_EnableWindow(plv, FALSE);
  4325. }
  4326. // tooltip for unfolding name lables
  4327. plv->hwndToolTips = CreateWindowEx(WS_EX_TRANSPARENT, TOOLTIPS_CLASS, NULL,
  4328. WS_POPUP|TTS_NOPREFIX, 0, 0, 0, 0,
  4329. NULL, NULL, g_hinst, NULL);
  4330. if (plv->hwndToolTips)
  4331. {
  4332. TOOLINFO ti;
  4333. ti.cbSize = sizeof(ti);
  4334. ti.uFlags = TTF_TRANSPARENT|TTF_ABSOLUTE;
  4335. ti.hwnd = plv->ci.hwnd;
  4336. ti.uId = 0;
  4337. ti.hinst = NULL;
  4338. ti.lpszText = LPSTR_TEXTCALLBACK;
  4339. GetClientRect(plv->ci.hwnd, &ti.rect);
  4340. SendMessage(plv->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM) &ti);
  4341. /* Ensure that the tooltips use the same font as the view */
  4342. FORWARD_WM_SETFONT(plv->hwndToolTips, plv->hfontLabel, FALSE, SendMessage);
  4343. }
  4344. SetTimer(plv->ci.hwnd, IDT_ONECLICKOK, GetDoubleClickTime(), NULL);
  4345. return TRUE;
  4346. error1:
  4347. DPA_Destroy(plv->hdpa);
  4348. plv->hdpa = NULL;
  4349. error0:
  4350. if (plv->plvrangeSel)
  4351. {
  4352. plv->plvrangeSel->lpVtbl->Release(plv->plvrangeSel);
  4353. plv->plvrangeSel = NULL;
  4354. }
  4355. if (plv->plvrangeCut)
  4356. {
  4357. plv->plvrangeCut->lpVtbl->Release(plv->plvrangeCut);
  4358. plv->plvrangeCut = NULL;
  4359. }
  4360. return FALSE;
  4361. }
  4362. void ListView_DeleteHrgnInval(LV* plv)
  4363. {
  4364. if (plv->hrgnInval && plv->hrgnInval != (HRGN)ENTIRE_REGION)
  4365. DeleteObject(plv->hrgnInval);
  4366. plv->hrgnInval = NULL;
  4367. }
  4368. void ListView_OnDestroy(LV* plv)
  4369. {
  4370. //
  4371. // The tooltip window may or may not exist at this point. It
  4372. // depends if the owning window of the tips is also being destroy.
  4373. // If so, then the tips are gone already.
  4374. //
  4375. if (IsWindow(plv->hwndToolTips))
  4376. DestroyWindow(plv->hwndToolTips);
  4377. if (plv->hCurHot)
  4378. DestroyCursor(plv->hCurHot);
  4379. plv->hwndToolTips = NULL;
  4380. Str_Set(&plv->pszTip, NULL);
  4381. Str_Set(&plv->pszEmptyText, NULL);
  4382. TerminateDitherBrush();
  4383. if (!ListView_IsOwnerData(plv))
  4384. {
  4385. // Make sure to notify the app
  4386. ListView_OnDeleteAllItems(plv);
  4387. }
  4388. if ((plv->flags & LVF_FONTCREATED) && plv->hfontLabel)
  4389. {
  4390. DeleteObject(plv->hfontLabel);
  4391. // plv->flags &= ~LVF_FONTCREATED;
  4392. // plv->hwfontLabel = NULL;
  4393. }
  4394. if (plv->hfontGroup)
  4395. {
  4396. DeleteObject(plv->hfontGroup);
  4397. }
  4398. if (plv->hFontHot)
  4399. {
  4400. DeleteObject(plv->hFontHot);
  4401. }
  4402. if (plv->hbmpWatermark)
  4403. {
  4404. DeleteObject(plv->hbmpWatermark);
  4405. }
  4406. ListView_DeleteHrgnInval(plv);
  4407. if (plv->prcWorkAreas)
  4408. {
  4409. // This assert is bogus: If the app created work areas then deleted
  4410. // them, nWorkAreas will be 0 but prcWorkAreas will be non-NULL.
  4411. // ASSERT(plv->nWorkAreas > 0);
  4412. LocalFree(plv->prcWorkAreas);
  4413. }
  4414. if (plv->hdpaGroups)
  4415. {
  4416. DPA_DestroyCallback(plv->hdpaGroups, DestroyGroups, NULL);
  4417. plv->hdpaGroups = NULL;
  4418. }
  4419. }
  4420. void ListView_OnNCDestroy(LV* plv)
  4421. {
  4422. if ((!(plv->ci.style & LVS_SHAREIMAGELISTS)) || ListView_CheckBoxes(plv))
  4423. {
  4424. if (plv->himlState &&
  4425. (plv->himlState != plv->himl) &&
  4426. (plv->himlState != plv->himlSmall))
  4427. {
  4428. ImageList_Destroy(plv->himlState);
  4429. }
  4430. }
  4431. if (!(plv->ci.style & LVS_SHAREIMAGELISTS))
  4432. {
  4433. if (plv->himl)
  4434. ImageList_Destroy(plv->himl);
  4435. if (plv->himlSmall)
  4436. ImageList_Destroy(plv->himlSmall);
  4437. }
  4438. if (ListView_IsOwnerData(plv))
  4439. {
  4440. if (plv->plvrangeSel)
  4441. {
  4442. plv->plvrangeSel->lpVtbl->Release(plv->plvrangeSel);
  4443. plv->plvrangeSel = NULL;
  4444. }
  4445. if (plv->plvrangeCut)
  4446. {
  4447. plv->plvrangeCut->lpVtbl->Release(plv->plvrangeCut);
  4448. plv->plvrangeCut = NULL;
  4449. }
  4450. plv->cTotalItems = 0;
  4451. }
  4452. ListView_ReleaseBkImage(plv);
  4453. if (plv->hbrBk)
  4454. DeleteBrush(plv->hbrBk);
  4455. if (plv->hdpa)
  4456. DPA_Destroy(plv->hdpa);
  4457. if (plv->hdpaZOrder)
  4458. DPA_Destroy(plv->hdpaZOrder);
  4459. ListView_RDestroy(plv);
  4460. IncrementSearchFree(&plv->is);
  4461. ListView_SetPtr(plv->ci.hwnd, NULL);
  4462. if (plv->hTheme)
  4463. CloseThemeData(plv->hTheme);
  4464. NearFree(plv);
  4465. }
  4466. // sets the background color for the listview
  4467. //
  4468. // this creats the brush for drawing the background as well
  4469. // as sets the imagelists background color if needed
  4470. BOOL ListView_OnSetBkColor(LV* plv, COLORREF clrBk)
  4471. {
  4472. if (plv->clrBk != clrBk)
  4473. {
  4474. if (plv->hbrBk)
  4475. {
  4476. DeleteBrush(plv->hbrBk);
  4477. plv->hbrBk = NULL;
  4478. }
  4479. if (clrBk != CLR_NONE)
  4480. {
  4481. plv->hbrBk = CreateSolidBrush(clrBk);
  4482. if (!plv->hbrBk)
  4483. return FALSE;
  4484. }
  4485. // don't mess with the imagelist color if things are shared
  4486. if (!(plv->ci.style & LVS_SHAREIMAGELISTS))
  4487. {
  4488. if (plv->himl)
  4489. ImageList_SetBkColor(plv->himl, clrBk);
  4490. if (plv->himlSmall)
  4491. ImageList_SetBkColor(plv->himlSmall, clrBk);
  4492. if (plv->himlState)
  4493. ImageList_SetBkColor(plv->himlState, clrBk);
  4494. }
  4495. plv->clrBk = clrBk;
  4496. }
  4497. return TRUE;
  4498. }
  4499. void InitBrushOrg(LV* plv, HDC hdc)
  4500. {
  4501. int x;
  4502. if (ListView_IsSmallView(plv) || ListView_IsIconView(plv))
  4503. {
  4504. x = plv->ptOrigin.x;
  4505. }
  4506. else if (ListView_IsListView(plv))
  4507. {
  4508. x = plv->xOrigin;
  4509. }
  4510. else
  4511. {
  4512. x = (int)plv->ptlRptOrigin.x;
  4513. }
  4514. SetBrushOrgEx(hdc, -x, 0, NULL);
  4515. }
  4516. void ListView_InvalidateRegion(LV* plv, HRGN hrgn)
  4517. {
  4518. if (hrgn)
  4519. {
  4520. if (plv->hrgnInval == NULL)
  4521. {
  4522. plv->hrgnInval = hrgn;
  4523. }
  4524. else
  4525. {
  4526. // union it in if the entire region isn't marked for invalidate
  4527. if (plv->hrgnInval != (HRGN)ENTIRE_REGION)
  4528. {
  4529. UnionRgn(plv->hrgnInval, plv->hrgnInval, hrgn);
  4530. }
  4531. DeleteObject(hrgn);
  4532. }
  4533. }
  4534. }
  4535. //
  4536. // Used when a watermark is the listview's background (detected via clrTextBk
  4537. // being CLR_NONE) to perform a flicker-free scroll of the client area, using
  4538. // an offscreen bitmap
  4539. //
  4540. // potential perf issue -- caching DC and/or bitmap instead of create/destroy
  4541. // on each call
  4542. //
  4543. // jeffbog 2/29/96
  4544. //
  4545. void LVSeeThruScroll(LV *plv, LPRECT lprcUpdate)
  4546. {
  4547. HDC hdcOff;
  4548. HBITMAP hbmpOff;
  4549. int x,y,cx,cy;
  4550. HDC hdc = GetDC(plv->ci.hwnd);
  4551. if (!lprcUpdate)
  4552. {
  4553. x = y = 0;
  4554. cx = plv->sizeClient.cx;
  4555. cy = plv->sizeClient.cy;
  4556. }
  4557. else
  4558. {
  4559. x = lprcUpdate->left;
  4560. y = lprcUpdate->top;
  4561. cx = lprcUpdate->right - x;
  4562. cy = lprcUpdate->bottom - y;
  4563. }
  4564. hdcOff = CreateCompatibleDC(hdc);
  4565. hbmpOff = CreateCompatibleBitmap(hdc, plv->sizeClient.cx, plv->sizeClient.cy);
  4566. SelectObject(hdcOff, hbmpOff);
  4567. SendMessage(plv->ci.hwnd, WM_PRINT, (WPARAM)hdcOff, PRF_CLIENT | PRF_ERASEBKGND);
  4568. BitBlt(hdc, x, y, cx, cy, hdcOff, x, y, SRCCOPY);
  4569. ReleaseDC(plv->ci.hwnd, hdc);
  4570. DeleteDC(hdcOff);
  4571. DeleteObject(hbmpOff);
  4572. }
  4573. void ListView_OnPaint(LV* plv, HDC hdc)
  4574. {
  4575. PAINTSTRUCT ps;
  4576. RECT rcUpdate;
  4577. HDC hPaintDC = hdc;
  4578. HDC hMemDC = NULL;
  4579. HBITMAP hMemBm = NULL;
  4580. HBITMAP hOldBm;
  4581. BOOL fInternDC = FALSE;
  4582. // Before handling WM_PAINT, go ensure everything's recomputed...
  4583. //
  4584. if (plv->rcView.left == RECOMPUTE)
  4585. ListView_Recompute(plv);
  4586. // If we're in report view, update the header window: it looks
  4587. // better this way...
  4588. //
  4589. if (ListView_IsReportView(plv) && plv->hwndHdr)
  4590. UpdateWindow(plv->hwndHdr);
  4591. // If nothing to do (i.e., we recieved a WM_PAINT because
  4592. // of an RDW_INTERNALPAINT, and we didn't invalidate anything)
  4593. // don't bother with the Begin/EndPaint.
  4594. //
  4595. if (hdc || GetUpdateRect(plv->ci.hwnd, &rcUpdate, FALSE))
  4596. {
  4597. if (!(plv->flags & LVF_VISIBLE))
  4598. {
  4599. plv->flags |= LVF_VISIBLE;
  4600. // We may try to resize the column
  4601. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  4602. ListView_UpdateScrollBars(plv);
  4603. }
  4604. // this needs to be done before the beginpaint because it clears
  4605. // out the update region
  4606. if (!(plv->flags & LVF_REDRAW))
  4607. {
  4608. // add this region to our local invalidate region
  4609. HRGN hrgn = CreateRectRgn(0, 0, 0,0);
  4610. if (hrgn)
  4611. {
  4612. // ok if GetUpdateRgn fails... then hrgn will still be
  4613. // and empty region..
  4614. GetUpdateRgn(plv->ci.hwnd, hrgn, FALSE);
  4615. ListView_InvalidateRegion(plv, hrgn);
  4616. }
  4617. }
  4618. // Get device context
  4619. if (!hdc)
  4620. {
  4621. hPaintDC = hdc = BeginPaint(plv->ci.hwnd, &ps);
  4622. fInternDC = TRUE;
  4623. }
  4624. else
  4625. {
  4626. GetClipBox(hdc, &ps.rcPaint);
  4627. }
  4628. // Skip painting if redrawing is not enabled but complete cycle (EndPaint)
  4629. if (ListView_RedrawEnabled(plv))
  4630. {
  4631. // Create memory surface and map rendering context if double buffering
  4632. if (ListView_IsDoubleBuffer(plv))
  4633. {
  4634. // Only make large enough for clipping region
  4635. hMemDC = CreateCompatibleDC(hdc);
  4636. if (hMemDC)
  4637. {
  4638. hMemBm = CreateCompatibleBitmap(hdc, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint));
  4639. if (hMemBm)
  4640. {
  4641. hOldBm = SelectObject(hMemDC, hMemBm);
  4642. // Offset painting to paint in region
  4643. OffsetWindowOrgEx(hMemDC, ps.rcPaint.left, ps.rcPaint.top, NULL);
  4644. }
  4645. else
  4646. {
  4647. DeleteDC(hMemDC);
  4648. hMemDC = NULL;
  4649. }
  4650. }
  4651. }
  4652. if (hMemDC)
  4653. {
  4654. // Use memory DC if it was created
  4655. hPaintDC = hMemDC;
  4656. }
  4657. if (hPaintDC)
  4658. {
  4659. // Setup brush offset for list view scrolling
  4660. InitBrushOrg(plv, hPaintDC);
  4661. ListView_DebugDisplayClipRegion(plv, &ps.rcPaint, NULL);
  4662. // Draw backround in this pass if double buffering, otherwise, it was handled in WM_ERASEBKGND
  4663. if (ListView_IsDoubleBuffer(plv))
  4664. {
  4665. // Add on buffer offset to scrolling offset
  4666. POINT ptBrOrg;
  4667. GetBrushOrgEx(hPaintDC, &ptBrOrg);
  4668. SetBrushOrgEx(hPaintDC, ptBrOrg.x - ps.rcPaint.left, ptBrOrg.y - ps.rcPaint.top, NULL);
  4669. ListView_DrawBackground(plv, hPaintDC, &ps.rcPaint);
  4670. }
  4671. // Draw foreground
  4672. ListView_Redraw(plv, hPaintDC, &ps.rcPaint);
  4673. // Complete double buffering by blitting and freeing off-screen objects
  4674. if (ListView_IsDoubleBuffer(plv) &&
  4675. hMemDC)
  4676. {
  4677. if (plv->flags & LVF_MARQUEE)
  4678. {
  4679. HDC h = CreateCompatibleDC(hMemDC);
  4680. if (h)
  4681. {
  4682. HBITMAP hbmp, hbmpOld;
  4683. BLENDFUNCTION bf = {0};
  4684. RECT rcInvalid;
  4685. RECT rcMarquee = {0, 0, RECTWIDTH(plv->rcMarquee), RECTHEIGHT(plv->rcMarquee)};
  4686. IntersectRect(&rcInvalid, &ps.rcPaint, &plv->rcMarquee);
  4687. if (!IsRectEmpty(&rcInvalid))
  4688. {
  4689. hbmp = CreateCompatibleBitmap(hMemDC, RECTWIDTH(rcInvalid), RECTHEIGHT(rcInvalid));
  4690. if (hbmp)
  4691. {
  4692. hbmpOld = SelectObject(h, hbmp);
  4693. FillRectClr(h, &rcMarquee, g_clrMenuHilight);
  4694. bf.BlendOp = AC_SRC_OVER;
  4695. bf.SourceConstantAlpha = 70;
  4696. GdiAlphaBlend(hMemDC, rcInvalid.left, rcInvalid.top, RECTWIDTH(rcInvalid), RECTHEIGHT(rcInvalid),
  4697. h, 0, 0, RECTWIDTH(rcInvalid), RECTHEIGHT(rcInvalid), bf);
  4698. SelectObject(h, hbmpOld);
  4699. DeleteObject(hbmp);
  4700. }
  4701. SHOutlineRect(hMemDC, &plv->rcMarquee, g_clrHighlight, g_clrHighlight);
  4702. }
  4703. DeleteDC(h);
  4704. }
  4705. }
  4706. BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint), hMemDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
  4707. SelectObject(hMemDC, hOldBm);
  4708. DeleteObject(hMemBm);
  4709. DeleteDC(hMemDC);
  4710. }
  4711. }
  4712. }
  4713. // Free DC if necessary
  4714. if (fInternDC)
  4715. EndPaint(plv->ci.hwnd, &ps);
  4716. }
  4717. }
  4718. void ListView_DrawSimpleBackground(LV *plv, HDC hdc, POINT* ppt, RECT *prcClip)
  4719. {
  4720. if (plv->clrBk != CLR_NONE)
  4721. {
  4722. //
  4723. // We just have a simple background color.
  4724. //
  4725. FillRect(hdc, prcClip, plv->hbrBk);
  4726. }
  4727. else
  4728. {
  4729. //
  4730. // Parent HWND draws the background for us.
  4731. //
  4732. POINT pt = {0,0}, ptOrig;
  4733. MapWindowPoints(plv->ci.hwnd, plv->ci.hwndParent, &pt, 1); //Map it to parent's co-ordinates
  4734. OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptOrig);
  4735. SendMessage(plv->ci.hwndParent, WM_ERASEBKGND, (WPARAM)hdc, (LPARAM)0); //Make the parent draw into child's DC
  4736. SetWindowOrgEx(hdc, ptOrig.x, ptOrig.y, NULL);
  4737. }
  4738. }
  4739. #define SATURATE(x, y) { int ___cTemp; ___cTemp = (x) + ((y + 1) * 100 * (x)) / 1000; if (___cTemp > 0xFF) ___cTemp = 0xFF; (x) = (BYTE)___cTemp; }
  4740. void SaturateDC(void * pvBitmapBits, int Amount, RECT* prcColumn, RECT* prcImage)
  4741. {
  4742. long x, y;
  4743. long uHeight = RECTHEIGHT(*prcImage);
  4744. long uWidth = RECTWIDTH(*prcImage);
  4745. ULONG* pul = (ULONG*)pvBitmapBits;
  4746. for (y = 0; y < uHeight ;y++)
  4747. {
  4748. for (x = 0; x < uWidth; x++)
  4749. {
  4750. if (x + prcImage->left >= prcColumn->left && x + prcImage->left <= prcColumn->right)
  4751. {
  4752. RGBQUAD* prgb = (RGBQUAD*)&pul[y * uWidth + x];
  4753. SATURATE(prgb->rgbRed, Amount);
  4754. SATURATE(prgb->rgbGreen, Amount);
  4755. SATURATE(prgb->rgbBlue, Amount);
  4756. }
  4757. }
  4758. }
  4759. }
  4760. void SaturateSortColumn(LV* plv, HDC hdc, void * pvBitmapBits, POINT* ppt, RECT* prcClip)
  4761. {
  4762. RECT rc;
  4763. RECT rcUpdate = *prcClip;
  4764. Header_GetItemRect(plv->hwndHdr, plv->iLastColSort, &rc);
  4765. OffsetRect(&rc, ppt->x, 0);
  4766. if (rcUpdate.left < rc.left)
  4767. rcUpdate.left = rc.left;
  4768. if (rcUpdate.right > rc.right)
  4769. rcUpdate.right = rc.right;
  4770. if (rcUpdate.left < rcUpdate.right ||
  4771. IntersectRect(&rc, &rcUpdate, prcClip))
  4772. {
  4773. SaturateDC(pvBitmapBits, 0, &rcUpdate, prcClip);
  4774. }
  4775. }
  4776. HDC PrepBackgroundDIBSection(HDC hdcDest, RECT* prc, void ** ppvBitmap, HBITMAP* phbmpOld)
  4777. {
  4778. HDC hdcRet = CreateCompatibleDC(hdcDest);
  4779. if (hdcRet)
  4780. {
  4781. HBITMAP hbmp;
  4782. BITMAPINFO bi = {0};
  4783. bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
  4784. bi.bmiHeader.biWidth = RECTWIDTH(*prc);
  4785. bi.bmiHeader.biHeight = RECTHEIGHT(*prc);
  4786. bi.bmiHeader.biPlanes = 1;
  4787. bi.bmiHeader.biBitCount = 32;
  4788. bi.bmiHeader.biCompression = BI_RGB;
  4789. hbmp = CreateDIBSection(hdcRet, &bi, DIB_RGB_COLORS, ppvBitmap, NULL, 0);
  4790. *phbmpOld = (HBITMAP)SelectObject(hdcRet, hbmp);
  4791. SetViewportOrgEx(hdcRet, -prc->left, -prc->top, NULL);
  4792. }
  4793. return hdcRet;
  4794. }
  4795. void CleanupBackgroundDIBSection(HDC hdc, HBITMAP hbmpOld)
  4796. {
  4797. if (hdc)
  4798. {
  4799. HBITMAP hbmp = SelectObject(hdc, hbmpOld);
  4800. if (hbmp)
  4801. DeleteObject(hbmp);
  4802. DeleteDC(hdc);
  4803. }
  4804. }
  4805. void ListView_DrawBackground(LV *plv, HDC hdc, RECT *prcClip)
  4806. {
  4807. HRGN hrgnClipSave;
  4808. RECT rcClip;
  4809. POINT ptBackOrg = {0};
  4810. //
  4811. // Compute ptBackOrg (aka scrolling offset), based on view style.
  4812. //
  4813. switch (plv->wView)
  4814. {
  4815. case LV_VIEW_LIST:
  4816. ptBackOrg.x = -plv->xOrigin;
  4817. ptBackOrg.y = 0;
  4818. break;
  4819. case LV_VIEW_DETAILS:
  4820. ptBackOrg.x = -plv->ptlRptOrigin.x;
  4821. ptBackOrg.y = -plv->ptlRptOrigin.y + plv->yTop;
  4822. break;
  4823. default:
  4824. ptBackOrg.x = -plv->ptOrigin.x;
  4825. ptBackOrg.y = -plv->ptOrigin.y;
  4826. break;
  4827. }
  4828. // Optimize the common/simple case
  4829. if (!(plv->pImgCtx && plv->fImgCtxComplete))
  4830. {
  4831. ListView_DrawSimpleBackground(plv, hdc, &ptBackOrg, prcClip);
  4832. if (ListView_IsWatermarked(plv))
  4833. {
  4834. HDC hdcMem = CreateCompatibleDC(hdc);
  4835. if (hdcMem)
  4836. {
  4837. HBITMAP hbmp = (HBITMAP)SelectObject(hdcMem, plv->hbmpWatermark);
  4838. RECT rcWatermark;
  4839. GetClientRect(plv->ci.hwnd, &rcWatermark);
  4840. rcWatermark.left = rcWatermark.right - plv->szWatermark.cx;
  4841. rcWatermark.top = rcWatermark.bottom - plv->szWatermark.cy;
  4842. BitBlt(hdc, rcWatermark.left, rcWatermark.top, plv->szWatermark.cx, plv->szWatermark.cy,
  4843. hdcMem, 0, 0, SRCCOPY);
  4844. SelectObject(hdcMem, hbmp);
  4845. DeleteDC(hdcMem);
  4846. }
  4847. }
  4848. if (plv->wView == LV_VIEW_DETAILS &&
  4849. plv->iLastColSort != -1 && !plv->fGroupView)
  4850. {
  4851. RECT rcUpdate = *prcClip;
  4852. RECT rc;
  4853. COLORREF cr;
  4854. Header_GetItemRect(plv->hwndHdr, plv->iLastColSort, &rc);
  4855. OffsetRect(&rc, ptBackOrg.x, 0);
  4856. if (rcUpdate.left < rc.left)
  4857. rcUpdate.left = rc.left;
  4858. if (rcUpdate.right > rc.right)
  4859. rcUpdate.right = rc.right;
  4860. cr = GetSortColor(10, plv->clrBk);
  4861. FillRectClr(hdc, &rcUpdate, cr);
  4862. }
  4863. return;
  4864. }
  4865. //
  4866. // Save the old clipping region,
  4867. // since we whack on it a lot.
  4868. //
  4869. hrgnClipSave = CreateRectRgnIndirect(prcClip);
  4870. if (hrgnClipSave)
  4871. {
  4872. if (GetClipRgn(hdc, hrgnClipSave) <= 0)
  4873. {
  4874. DeleteObject(hrgnClipSave);
  4875. hrgnClipSave = NULL;
  4876. }
  4877. }
  4878. //
  4879. // Clip the clipping region to the caller's rectangle,
  4880. // and save the final clipping rectangle in rcClip.
  4881. //
  4882. if (prcClip != NULL)
  4883. {
  4884. IntersectClipRect(hdc, prcClip->left, prcClip->top,
  4885. prcClip->right, prcClip->bottom);
  4886. }
  4887. GetClipBox(hdc, &rcClip);
  4888. if (plv->pImgCtx && plv->fImgCtxComplete)
  4889. {
  4890. RECT rcImage, rcClient;
  4891. ULONG ulState;
  4892. SIZE sizeImg;
  4893. ListView_Realize(plv, hdc, TRUE, FALSE);
  4894. switch (plv->ulBkImageFlags & LVBKIF_STYLE_MASK)
  4895. {
  4896. case LVBKIF_STYLE_TILE:
  4897. {
  4898. HDC hdcBackBuffer = hdc;
  4899. HBITMAP hbmpOld;
  4900. void * pvBits = NULL;
  4901. POINT ptBackTile = {0};
  4902. if (plv->wView == LV_VIEW_DETAILS &&
  4903. plv->iLastColSort != -1)
  4904. {
  4905. hdcBackBuffer = PrepBackgroundDIBSection(hdc, prcClip, &pvBits, &hbmpOld);
  4906. if (hdcBackBuffer == NULL)
  4907. hdcBackBuffer = hdc;
  4908. }
  4909. if (!plv->fListviewWatermarkBackgroundImages)
  4910. ptBackTile = ptBackOrg;
  4911. if (plv->ulBkImageFlags & LVBKIF_FLAG_TILEOFFSET)
  4912. {
  4913. // These offsets are in pixels, not percent (sorry)
  4914. ptBackTile.x -= plv->xOffsetPercent;
  4915. ptBackTile.y -= plv->yOffsetPercent;
  4916. }
  4917. IImgCtx_Tile(plv->pImgCtx, hdcBackBuffer, &ptBackTile, prcClip, NULL);
  4918. if (hdcBackBuffer != hdc)
  4919. {
  4920. SaturateSortColumn(plv, hdcBackBuffer, pvBits, &ptBackOrg, prcClip);
  4921. BitBlt(hdc, prcClip->left, prcClip->top, RECTWIDTH(*prcClip), RECTHEIGHT(*prcClip), hdcBackBuffer, prcClip->left, prcClip->top, SRCCOPY);
  4922. CleanupBackgroundDIBSection(hdcBackBuffer, hbmpOld);
  4923. }
  4924. }
  4925. ExcludeClipRect(hdc, prcClip->left, prcClip->top,
  4926. prcClip->right, prcClip->bottom);
  4927. break;
  4928. case LVBKIF_STYLE_NORMAL:
  4929. //
  4930. // Start with the base image.
  4931. //
  4932. IImgCtx_GetStateInfo(plv->pImgCtx, &ulState, &sizeImg, FALSE);
  4933. rcImage.left = 0;
  4934. rcImage.top = 0;
  4935. rcImage.right = sizeImg.cx;
  4936. rcImage.bottom = sizeImg.cy;
  4937. //
  4938. // Adjust for caller offsets.
  4939. //
  4940. GetClientRect(plv->ci.hwnd, &rcClient);
  4941. if (plv->xOffsetPercent)
  4942. {
  4943. LONG dx = plv->xOffsetPercent * (rcClient.right - sizeImg.cx) / 100;
  4944. rcImage.left += dx;
  4945. rcImage.right += dx;
  4946. }
  4947. if (plv->yOffsetPercent)
  4948. {
  4949. LONG dy = plv->yOffsetPercent * (rcClient.bottom - sizeImg.cy) / 100;
  4950. rcImage.top += dy;
  4951. rcImage.bottom += dy;
  4952. }
  4953. //
  4954. // Adjust for ptBackOrg (scrolling offset).
  4955. //
  4956. rcImage.left += ptBackOrg.x;
  4957. rcImage.top += ptBackOrg.y;
  4958. rcImage.right += ptBackOrg.x;
  4959. rcImage.bottom += ptBackOrg.y;
  4960. //
  4961. // Draw the image, if necessary.
  4962. //
  4963. if (RectVisible(hdc, &rcImage))
  4964. {
  4965. IImgCtx_Draw(plv->pImgCtx, hdc, &rcImage);
  4966. ExcludeClipRect(hdc, rcImage.left, rcImage.top,
  4967. rcImage.right, rcImage.bottom);
  4968. }
  4969. break;
  4970. }
  4971. }
  4972. //
  4973. // Now draw the rest of the background.
  4974. //
  4975. if (RectVisible(hdc, prcClip))
  4976. {
  4977. ListView_DrawSimpleBackground(plv, hdc, &ptBackOrg, prcClip);
  4978. }
  4979. //
  4980. // Restore old clipping region.
  4981. //
  4982. SelectClipRgn(hdc, hrgnClipSave);
  4983. if (hrgnClipSave)
  4984. {
  4985. DeleteObject(hrgnClipSave);
  4986. }
  4987. }
  4988. BOOL ListView_OnEraseBkgnd(LV *plv, HDC hdc)
  4989. {
  4990. // If redraw is turned off, still process erase bk
  4991. if (ListView_IsDoubleBuffer(plv) && (plv->flags & LVF_REDRAW))
  4992. {
  4993. // No erase, will happen in WM_PAINT handler (ListView_OnPaint)
  4994. return FALSE;
  4995. }
  4996. else
  4997. {
  4998. RECT rcClip;
  4999. //
  5000. // We draw our own background, erase with it.
  5001. //
  5002. GetClipBox(hdc, &rcClip);
  5003. ListView_DrawBackground(plv, hdc, &rcClip);
  5004. return TRUE;
  5005. }
  5006. }
  5007. void ListView_OnCommand(LV* plv, int id, HWND hwndCtl, UINT codeNotify)
  5008. {
  5009. if (hwndCtl == plv->hwndEdit)
  5010. {
  5011. switch (codeNotify)
  5012. {
  5013. case EN_UPDATE:
  5014. // We don't want flicker during replacing current selection
  5015. // as we use selection for IME composition.
  5016. //
  5017. if ((g_fDBCSInputEnabled) && (plv->flags & LVF_INSERTINGCOMP))
  5018. break;
  5019. // We will use the ID of the window as a Dirty flag...
  5020. if (IsWindowVisible(plv->hwndEdit))
  5021. {
  5022. SetWindowID(plv->hwndEdit, 1);
  5023. ListView_SetEditSize(plv);
  5024. }
  5025. break;
  5026. case EN_KILLFOCUS:
  5027. // We lost focus, so dismiss edit and save changes
  5028. // (Note that the owner might reject the change and restart
  5029. // edit mode, which traps the user. Owners need to give the
  5030. // user a way to get out.)
  5031. //
  5032. //
  5033. // Fix horrible undocumented hanging problem: LVN_ENDLABELEDIT
  5034. // is sent in response to EN_KILLFOCUS, which is send in response
  5035. // to WM_KILLFOCUS, and it is undocumented that you cannot display
  5036. // UI during WM_KILLFOCUS when a journal record hook is active,
  5037. // because the presence of a hook forces serialization of activation,
  5038. // and so when you put up UI, you generate activation changes, which
  5039. // get stuck because you haven't finished responding to the previous
  5040. // WM_KILLFOCUS message yet.
  5041. //
  5042. // See NT bug 414634.
  5043. //
  5044. if (InSendMessage())
  5045. ReplyMessage(0);
  5046. if (!ListView_DismissEdit(plv, FALSE))
  5047. return;
  5048. break;
  5049. case HN_BEGINDIALOG: // pen windows is bringing up a dialog
  5050. ASSERT(GetSystemMetrics(SM_PENWINDOWS)); // only on a pen system
  5051. plv->fNoDismissEdit = TRUE;
  5052. break;
  5053. case HN_ENDDIALOG: // pen windows has destroyed dialog
  5054. ASSERT(GetSystemMetrics(SM_PENWINDOWS)); // only on a pen system
  5055. plv->fNoDismissEdit = FALSE;
  5056. break;
  5057. }
  5058. // Forward edit control notifications up to parent
  5059. //
  5060. if (IsWindow(hwndCtl))
  5061. FORWARD_WM_COMMAND(plv->ci.hwndParent, id, hwndCtl, codeNotify, SendMessage);
  5062. }
  5063. }
  5064. void ListView_OnWindowPosChanged(LV* plv, const WINDOWPOS* lpwpos)
  5065. {
  5066. if (!lpwpos || !(lpwpos->flags & SWP_NOSIZE))
  5067. {
  5068. RECT rc;
  5069. int iOldSlots;
  5070. // Update scrollbars first, since ListView_OnEnsureVisible requires accurate scroll info
  5071. ListView_UpdateScrollBars(plv);
  5072. if (ListView_IsOwnerData(plv) &&
  5073. ListView_IsSlotView(plv))
  5074. {
  5075. iOldSlots = ListView_GetSlotCount(plv, TRUE, NULL, NULL);
  5076. }
  5077. GetClientRect(plv->ci.hwnd, &rc);
  5078. plv->sizeClient.cx = rc.right;
  5079. plv->sizeClient.cy = rc.bottom;
  5080. if (ListView_IsAutoArrangeView(plv))
  5081. {
  5082. // Call off to the arrange function.
  5083. ListView_ArrangeOrSnapToGrid(plv);
  5084. }
  5085. if (ListView_IsOwnerData(plv))
  5086. {
  5087. plv->rcView.left = RECOMPUTE;
  5088. ListView_Recompute(plv);
  5089. ListView_DismissEdit(plv, FALSE);
  5090. if (ListView_IsSlotView(plv))
  5091. {
  5092. // Uses the
  5093. int iNewSlots = ListView_GetSlotCount(plv, TRUE, NULL, NULL);
  5094. if ((iNewSlots != iOldSlots) && (ListView_Count(plv) > min(iNewSlots, iOldSlots)))
  5095. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  5096. }
  5097. }
  5098. ListView_RInitialize(plv, TRUE);
  5099. if (ListView_IsWatermarked(plv))
  5100. {
  5101. GetClientRect(plv->ci.hwnd, &rc);
  5102. rc.left = rc.right - plv->szWatermark.cx;
  5103. rc.top = rc.bottom - plv->szWatermark.cy;
  5104. InvalidateRect(plv->ci.hwnd, &rc, TRUE);
  5105. }
  5106. }
  5107. }
  5108. void ListView_InvalidateSelectedOrCutOwnerData(LV* plv, ILVRange *plvrangeSel)
  5109. {
  5110. UINT rdwFlags = RDW_INVALIDATE;
  5111. int cItem = ListView_Count(plv);
  5112. DWORD dwType = plv->wView;
  5113. int i;
  5114. RECT rcView;
  5115. ASSERT(ListView_IsOwnerData(plv));
  5116. ASSERT(plv);
  5117. GetClientRect(plv->ci.hwnd, &rcView);
  5118. if (plv->clrTextBk == CLR_NONE
  5119. || (plv->himl && (plv->clrBk != ImageList_GetBkColor(plv->himl))))
  5120. {
  5121. // always do an erase, otherwise the text background won't paint right
  5122. rdwFlags |= RDW_ERASE;
  5123. }
  5124. // calculate start of items and end of items visible on the view
  5125. //
  5126. switch (dwType)
  5127. {
  5128. case LV_VIEW_DETAILS:
  5129. i = ListView_RYHitTest(plv, rcView.top);
  5130. cItem = ListView_RYHitTest(plv, rcView.bottom) + 1;
  5131. break;
  5132. case LV_VIEW_LIST:
  5133. i = ListView_LCalcViewItem(plv, rcView.left, rcView.top);
  5134. cItem = ListView_LCalcViewItem(plv, rcView.right, rcView.bottom) + 1;
  5135. break;
  5136. default:
  5137. ListView_CalcMinMaxIndex(plv, &rcView, &i, &cItem);
  5138. break;
  5139. }
  5140. i = max(i, 0);
  5141. cItem = min(ListView_Count(plv), cItem);
  5142. if (cItem > i)
  5143. {
  5144. ListView_NotifyCacheHint(plv, i, cItem-1);
  5145. }
  5146. for (; i < cItem; i++)
  5147. {
  5148. if (plvrangeSel->lpVtbl->IsSelected(plvrangeSel, i) == S_OK)
  5149. {
  5150. ListView_InvalidateItem(plv, i, FALSE, rdwFlags);
  5151. }
  5152. }
  5153. }
  5154. void ListView_RedrawSelection(LV* plv)
  5155. {
  5156. if (ListView_IsOwnerData(plv))
  5157. {
  5158. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeSel);
  5159. }
  5160. else
  5161. {
  5162. int i = -1;
  5163. while ((i = ListView_OnGetNextItem(plv, i, LVNI_SELECTED)) != -1)
  5164. {
  5165. ListView_InvalidateItem(plv, i, TRUE, RDW_INVALIDATE | RDW_ERASE);
  5166. }
  5167. if (ListView_IsReportView(plv))
  5168. {
  5169. int iEnd = ListView_RYHitTest(plv, plv->sizeClient.cy) + 1;
  5170. iEnd = min(iEnd, ListView_Count(plv));
  5171. // if we're in report mode, sub items may have selection focus
  5172. for (i = ListView_RYHitTest(plv, 0); i < iEnd; i++)
  5173. {
  5174. int iCol;
  5175. for (iCol = 1; iCol < plv->cCol; iCol++)
  5176. {
  5177. LISTSUBITEM lsi;
  5178. ListView_GetSubItem(plv, i, iCol, &lsi);
  5179. if (lsi.state & LVIS_SELECTED)
  5180. {
  5181. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
  5182. }
  5183. break;
  5184. }
  5185. }
  5186. }
  5187. }
  5188. UpdateWindow(plv->ci.hwnd);
  5189. }
  5190. void ListView_OnSetFocus(LV* plv, HWND hwndOldFocus)
  5191. {
  5192. ASSERT(gcWheelDelta == 0);
  5193. // due to the way listview call SetFocus on themselves on buttondown,
  5194. // the window can get a strange sequence of focus messages: first
  5195. // set, then kill, and then set again. since these are not really
  5196. // focus changes, ignore them and only handle "real" cases.
  5197. //
  5198. // But still send out the accessibility notification because USER
  5199. // has already pushed focus back to the listview instead of to the
  5200. // focus item.
  5201. if (hwndOldFocus == plv->ci.hwnd)
  5202. {
  5203. ListView_NotifyFocusEvent(plv);
  5204. return;
  5205. }
  5206. plv->flags |= ListView_HideLabels(plv) ? LVF_FOCUSED : LVF_FOCUSED | LVF_UNFOLDED;
  5207. if (IsWindowVisible(plv->ci.hwnd))
  5208. {
  5209. if (plv->iFocus != -1)
  5210. {
  5211. ListView_InvalidateItem(plv, plv->iFocus, TRUE, RDW_INVALIDATE | RDW_ERASE);
  5212. ListView_NotifyFocusEvent(plv);
  5213. }
  5214. ListView_RedrawSelection(plv);
  5215. }
  5216. // Let the parent window know that we are getting the focus.
  5217. CCSendNotify(&plv->ci, NM_SETFOCUS, NULL);
  5218. }
  5219. void ListView_OnKillFocus(LV* plv, HWND hwndNewFocus)
  5220. {
  5221. // Reset wheel scroll amount
  5222. gcWheelDelta = 0;
  5223. // due to the way listview call SetFocus on themselves on buttondown,
  5224. // the window can get a strange sequence of focus messages: first
  5225. // set, then kill, and then set again. since these are not really
  5226. // focus changes, ignore them and only handle "real" cases.
  5227. if (!plv || hwndNewFocus == plv->ci.hwnd)
  5228. return;
  5229. ListView_CancelTipTrack(plv);
  5230. plv->flags &= ~(LVF_FOCUSED|LVF_UNFOLDED);
  5231. // Blow this off if we are not currently visible (being destroyed!)
  5232. if (IsWindowVisible(plv->ci.hwnd))
  5233. {
  5234. if (plv->iFocus != -1)
  5235. {
  5236. UINT fRedraw = RDW_INVALIDATE;
  5237. if (plv->clrTextBk == CLR_NONE || plv->fListviewShadowText)
  5238. fRedraw |= RDW_ERASE;
  5239. ListView_InvalidateFoldedItem(plv, plv->iFocus, TRUE, fRedraw);
  5240. }
  5241. ListView_RedrawSelection(plv);
  5242. }
  5243. // Let the parent window know that we are losing the focus.
  5244. CCSendNotify(&plv->ci, NM_KILLFOCUS, NULL);
  5245. IncrementSearchString(&plv->is, 0, NULL);
  5246. }
  5247. void ListView_DeselectAll(LV* plv, int iDontDeselect)
  5248. {
  5249. int i = -1;
  5250. int nSkipped = 0;
  5251. BOOL fWasSelected = FALSE;
  5252. if (iDontDeselect != -1)
  5253. {
  5254. if (ListView_OnGetItemState(plv, iDontDeselect, LVIS_SELECTED))
  5255. fWasSelected = TRUE;
  5256. }
  5257. if (ListView_IsOwnerData(plv))
  5258. {
  5259. // if there's only one item selected, and that item is the iDontDeselect
  5260. // then our work is done...
  5261. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  5262. if (plv->nSelected == 1 && fWasSelected)
  5263. return;
  5264. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeSel);
  5265. ListView_OnSetItemState(plv, -1, 0, LVIS_SELECTED);
  5266. if (fWasSelected)
  5267. {
  5268. ListView_OnSetItemState(plv, iDontDeselect, LVIS_SELECTED, LVIS_SELECTED);
  5269. nSkipped = 1;
  5270. }
  5271. }
  5272. else
  5273. {
  5274. if (iDontDeselect != plv->iFocus)
  5275. {
  5276. ListView_OnSetItemState(plv, plv->iFocus, 0, LVIS_SELECTED);
  5277. }
  5278. while ((plv->nSelected - nSkipped) && (i = ListView_OnGetNextItem(plv, i, LVNI_SELECTED)) != -1)
  5279. {
  5280. if (i != iDontDeselect)
  5281. {
  5282. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5283. }
  5284. else
  5285. {
  5286. if (fWasSelected)
  5287. {
  5288. nSkipped++;
  5289. }
  5290. }
  5291. }
  5292. }
  5293. RIPMSG((plv->nSelected - nSkipped) == 0, "ListView_DeselectAll: Do not refuse a deselect when telling listview to Deselect all.");
  5294. plv->nSelected = nSkipped;
  5295. }
  5296. // toggle the selection state of an item
  5297. void ListView_ToggleSelection(LV* plv, int iItem)
  5298. {
  5299. UINT cur_state;
  5300. if (iItem != -1)
  5301. {
  5302. cur_state = ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  5303. ListView_OnSetItemState(plv, iItem, cur_state ^ LVIS_SELECTED, LVIS_SELECTED);
  5304. }
  5305. }
  5306. // Selects (or toggles) a range of items in the list.
  5307. // The curent iFocus is the starting location
  5308. // iItem - is the ending item
  5309. // fToggle - Well set all of the selection state of all of the items to
  5310. // inverse the starting location
  5311. //
  5312. void ListView_SelectRangeTo(LV* plv, int iItem, BOOL fResetRest)
  5313. {
  5314. int iMin, iMax;
  5315. int i = -1;
  5316. UINT uSelVal = LVIS_SELECTED;
  5317. if (plv->iMark == -1)
  5318. {
  5319. ListView_SetFocusSel(plv, iItem, TRUE, TRUE, FALSE);
  5320. return;
  5321. }
  5322. if (!fResetRest)
  5323. uSelVal = ListView_OnGetItemState(plv, plv->iMark, LVIS_SELECTED);
  5324. // If we are in report view or list view we simply walk through the
  5325. // indexes to see which items to select or deselect. otherwise it
  5326. // is is based off of the location of the objects being within the
  5327. // rectangle that is defined by
  5328. if (ListView_IsListView(plv) || (ListView_IsReportView(plv) && !plv->fGroupView))
  5329. {
  5330. iMin = min(iItem, plv->iMark);
  5331. iMax = max(iItem, plv->iMark);
  5332. if (ListView_IsOwnerData(plv))
  5333. {
  5334. if (fResetRest)
  5335. {
  5336. ListView_DeselectAll(plv, -1);
  5337. }
  5338. if (iMax > iMin)
  5339. {
  5340. if (LVIS_SELECTED & uSelVal)
  5341. {
  5342. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, iMin, iMax)))
  5343. return;
  5344. }
  5345. else
  5346. {
  5347. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, iMin, iMax)))
  5348. return;
  5349. }
  5350. ListView_SendODChangeAndInvalidate(plv, iMin, iMax, uSelVal ^ LVIS_SELECTED, uSelVal);
  5351. }
  5352. else
  5353. {
  5354. ListView_OnSetItemState(plv, iMin, uSelVal, LVIS_SELECTED);
  5355. }
  5356. }
  5357. else
  5358. {
  5359. if (fResetRest)
  5360. {
  5361. while ((i = ListView_OnGetNextItem(plv, i, LVNI_SELECTED)) != -1)
  5362. {
  5363. if (i < iMin || i > iMax)
  5364. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5365. }
  5366. }
  5367. while (iMin <= iMax)
  5368. {
  5369. ListView_OnSetItemState(plv, iMin, uSelVal, LVIS_SELECTED);
  5370. iMin++;
  5371. }
  5372. }
  5373. }
  5374. else
  5375. {
  5376. RECT rcTemp;
  5377. RECT rcTemp2;
  5378. RECT rcBounding;
  5379. int iFirstItem = (plv->iMark < iItem)? plv->iMark: iItem;
  5380. int iSecondItem = (plv->iMark > iItem)? plv->iMark: iItem;
  5381. ListView_GetRects(plv, iFirstItem, QUERY_DEFAULT, NULL, NULL, NULL, &rcTemp);
  5382. ListView_GetRects(plv, iSecondItem, QUERY_DEFAULT, NULL, NULL, NULL, &rcTemp2);
  5383. UnionRect(&rcBounding, &rcTemp, &rcTemp2);
  5384. ListView_CalcMinMaxIndex(plv, &rcBounding, &iMin, &iMax);
  5385. if (ListView_IsOwnerData(plv) && (iMax > iMin))
  5386. {
  5387. if (fResetRest)
  5388. {
  5389. ListView_DeselectAll(plv, -1);
  5390. }
  5391. iMax = min(iMax, ListView_Count(plv));
  5392. iMin = max(iMin, 0);
  5393. if (LVIS_SELECTED & uSelVal)
  5394. {
  5395. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, iMin, iMax - 1)))
  5396. return;
  5397. }
  5398. else
  5399. {
  5400. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, iMin, iMax - 1)))
  5401. return;
  5402. }
  5403. ListView_SendODChangeAndInvalidate(plv, iMin, iMax, uSelVal ^ LVIS_SELECTED, uSelVal);
  5404. }
  5405. else
  5406. {
  5407. int iZ;
  5408. POINT pt;
  5409. RECT rcItem;
  5410. for (i = 0; i < ListView_Count(plv); i++)
  5411. {
  5412. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, NULL, &rcItem);
  5413. pt.x = (rcItem.right + rcItem.left) / 2; // center of item
  5414. pt.y = (rcItem.bottom + rcItem.top) / 2;
  5415. // Is the item within the y bound of the first and last item?
  5416. if (pt.y > rcTemp.top &&
  5417. pt.y < rcTemp2.bottom)
  5418. {
  5419. // Yes. Check to see if the item is in the first row.
  5420. if (pt.y < rcTemp.bottom)
  5421. {
  5422. // It is. Then check to see if it's before the first item in that row.
  5423. if (pt.x < rcTemp.left)
  5424. {
  5425. // It is. Then this item is not to be selected.
  5426. if (fResetRest)
  5427. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5428. // Continue to the next item
  5429. continue;
  5430. }
  5431. }
  5432. // Is the item in the last row?
  5433. if (pt.y > rcTemp2.top)
  5434. {
  5435. // Yes. Is it after the last item in the selection?
  5436. if (pt.x > rcTemp2.right)
  5437. {
  5438. // It is. Then this item is not to be selected.
  5439. if (fResetRest)
  5440. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5441. // Continue to the next item
  5442. continue;
  5443. }
  5444. }
  5445. // The item is in the selection range. Go ahead and select it
  5446. if (!ListView_IsOwnerData(plv))
  5447. {
  5448. iZ = ListView_ZOrderIndex(plv, i);
  5449. if (iZ > 0)
  5450. DPA_InsertPtr(plv->hdpaZOrder, 0, DPA_DeletePtr(plv->hdpaZOrder, iZ));
  5451. }
  5452. ListView_OnSetItemState(plv, i, uSelVal, LVIS_SELECTED);
  5453. }
  5454. else if (fResetRest)
  5455. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5456. }
  5457. }
  5458. }
  5459. }
  5460. // makes an item the focused item and optionally selects it
  5461. //
  5462. // in:
  5463. // iItem item to get the focus
  5464. // fSelectAlso select this item as well as set it as the focus
  5465. // fDeselectAll deselect all items first
  5466. // fToggleSel toggle the selection state of the item
  5467. //
  5468. // returns:
  5469. // index of focus item (if focus change was refused)
  5470. int ListView_SetFocusSel(LV* plv, int iItem, BOOL fSelectAlso, BOOL fDeselectAll, BOOL fToggleSel)
  5471. {
  5472. int iFocus = plv->iFocus;
  5473. // if we're single sel mode, don't bother with this because
  5474. // the set item will do it for us
  5475. if (!(plv->ci.style & LVS_SINGLESEL) && (fDeselectAll))
  5476. ListView_DeselectAll(plv, -1);
  5477. if (iItem != plv->iFocus)
  5478. {
  5479. // remove the old focus
  5480. if (plv->iFocus != -1)
  5481. {
  5482. // If he refuses to give up the focus, bail out.
  5483. if (!ListView_OnSetItemState(plv, plv->iFocus, 0, LVIS_FOCUSED))
  5484. return plv->iFocus;
  5485. }
  5486. }
  5487. if (!ListView_IsOwnerData(plv))
  5488. {
  5489. if (fSelectAlso)
  5490. {
  5491. if (ListView_IsIconView(plv) || ListView_IsSmallView(plv) || ListView_IsTileView(plv))
  5492. {
  5493. int iZ = ListView_ZOrderIndex(plv, iItem);
  5494. if (iZ > 0)
  5495. DPA_InsertPtr(plv->hdpaZOrder, 0, DPA_DeletePtr(plv->hdpaZOrder, iZ));
  5496. }
  5497. }
  5498. }
  5499. /* Ensure that when moving focus that we refresh the previous focus
  5500. owner properly. */
  5501. if (iFocus != -1 && iFocus != plv->iFocus && (plv->flags & LVF_UNFOLDED))
  5502. ListView_InvalidateFoldedItem(plv, iFocus, FALSE, RDW_INVALIDATE);
  5503. if (plv->iMark == -1)
  5504. plv->iMark = iItem;
  5505. SetTimer(plv->ci.hwnd, IDT_SCROLLWAIT, GetDoubleClickTime(), NULL);
  5506. plv->flags |= LVF_SCROLLWAIT;
  5507. if (fToggleSel)
  5508. {
  5509. ListView_ToggleSelection(plv, iItem);
  5510. ListView_OnSetItemState(plv, iItem, LVIS_FOCUSED, LVIS_FOCUSED);
  5511. }
  5512. else
  5513. {
  5514. UINT flags = ((fSelectAlso || plv->ci.style & LVS_SINGLESEL) ?
  5515. (LVIS_SELECTED | LVIS_FOCUSED) : LVIS_FOCUSED);
  5516. ListView_OnSetItemState(plv, iItem, flags, flags);
  5517. }
  5518. return iItem;
  5519. }
  5520. UINT GetLVKeyFlags()
  5521. {
  5522. UINT uFlags = 0;
  5523. if (GetKeyState(VK_MENU) < 0)
  5524. uFlags |= LVKF_ALT;
  5525. if (GetKeyState(VK_CONTROL) < 0)
  5526. uFlags |= LVKF_CONTROL;
  5527. if (GetKeyState(VK_SHIFT) < 0)
  5528. uFlags |= LVKF_SHIFT;
  5529. return uFlags;
  5530. }
  5531. void ListView_OnKey(LV* plv, UINT vk, BOOL fDown, int cRepeat, UINT flags)
  5532. {
  5533. UINT lvni = 0;
  5534. int iNewFocus;
  5535. BOOL fCtlDown;
  5536. BOOL fShiftDown;
  5537. LV_KEYDOWN nm;
  5538. HWND hwnd = plv->ci.hwnd;
  5539. if (!fDown)
  5540. return;
  5541. // Cancel manual tip track if any key is pressed
  5542. ListView_CancelTipTrack(plv);
  5543. // Swap the left and right arrow key if the control is mirrored.
  5544. vk = RTLSwapLeftRightArrows(&plv->ci, vk);
  5545. //prevent any change in selected items before the dbl click timer goes off
  5546. //so that we don't launch wrong item(s)
  5547. if (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickHappened && plv->fOneClickOK)
  5548. {
  5549. //if a key is pressed with a mouse click with one click activate and double click
  5550. //timer, we end up setting up a timer and then processing the keydown
  5551. //this causes an item to be launched right away (from this code) and in case
  5552. //of return being pressed it causes double activation
  5553. //prevent these cases:
  5554. if (vk == VK_SHIFT || vk == VK_CONTROL || vk == VK_MENU || vk == VK_RETURN)
  5555. return;
  5556. KillTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED);
  5557. plv->fOneClickHappened = FALSE;
  5558. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  5559. if (!IsWindow(hwnd))
  5560. return;
  5561. }
  5562. // Notify
  5563. nm.wVKey = (WORD) vk;
  5564. nm.flags = flags;
  5565. if (CCSendNotify(&plv->ci, LVN_KEYDOWN, &nm.hdr))
  5566. {
  5567. plv->iPuntChar++;
  5568. return;
  5569. }
  5570. else if (plv->iPuntChar)
  5571. {
  5572. // this is tricky... if we want to punt the char, just increment the
  5573. // count. if we do NOT, then we must clear the queue of WM_CHAR's
  5574. // this is to preserve the iPuntChar to mean "punt the next n WM_CHAR messages
  5575. MSG msg;
  5576. while(plv->iPuntChar && PeekMessage(&msg, plv->ci.hwnd, WM_CHAR, WM_CHAR, PM_REMOVE))
  5577. {
  5578. plv->iPuntChar--;
  5579. }
  5580. ASSERT(!plv->iPuntChar);
  5581. }
  5582. if (ListView_Count(plv) == 0) // don't blow up on empty list
  5583. return;
  5584. fCtlDown = GetKeyState(VK_CONTROL) < 0;
  5585. fShiftDown = GetKeyState(VK_SHIFT) < 0;
  5586. switch (vk)
  5587. {
  5588. case VK_SPACE:
  5589. // If shift (extend) or control (disjoint) select,
  5590. // then toggle selection state of focused item.
  5591. if (fCtlDown)
  5592. {
  5593. plv->iMark = plv->iFocus;
  5594. ListView_ToggleSelection(plv, plv->iFocus);
  5595. plv->iPuntChar++;
  5596. }
  5597. if (fShiftDown)
  5598. {
  5599. ListView_SelectRangeTo(plv, plv->iFocus, TRUE);
  5600. }
  5601. if (ListView_CheckBoxes(plv))
  5602. {
  5603. if (plv->iFocus != -1)
  5604. ListView_HandleStateIconClick(plv, plv->iFocus);
  5605. if (ListView_IsSimpleSelect(plv))
  5606. {
  5607. int iToggle = -1;
  5608. while ((iToggle = ListView_OnGetNextItem(plv, iToggle, LVNI_SELECTED)) != -1)
  5609. {
  5610. if (plv->iFocus != iToggle)
  5611. {
  5612. ListView_HandleStateIconClick(plv, iToggle);
  5613. }
  5614. }
  5615. }
  5616. }
  5617. //notify of navigation key usage
  5618. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5619. return;
  5620. case VK_RETURN:
  5621. CCSendNotify(&plv->ci, NM_RETURN, NULL);
  5622. /// some (comdlg32 for example) destroy on double click
  5623. // we need to bail if that happens because plv is no longer valid
  5624. if (!IsWindow(hwnd))
  5625. return;
  5626. {
  5627. NMITEMACTIVATE nm;
  5628. nm.iItem = plv->iFocus;
  5629. nm.iSubItem = 0;
  5630. nm.uChanged = 0;
  5631. nm.ptAction.x = -1;
  5632. nm.ptAction.y = -1;
  5633. nm.uKeyFlags = GetLVKeyFlags();
  5634. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &nm.hdr);
  5635. if (!IsWindow(hwnd))
  5636. return;
  5637. }
  5638. //notify of navigation key usage
  5639. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5640. return;
  5641. case VK_ADD:
  5642. if (ListView_IsReportView(plv) && (GetKeyState(VK_CONTROL) < 0))
  5643. {
  5644. HCURSOR hcurPrev;
  5645. int i;
  5646. hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  5647. for (i=0; i < plv->cCol; i++)
  5648. {
  5649. ListView_RSetColumnWidth(plv, i, -1);
  5650. }
  5651. SetCursor(hcurPrev);
  5652. //notify of navigation key usage
  5653. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5654. return;
  5655. }
  5656. }
  5657. if (GetKeyState(VK_MENU) < 0)
  5658. return;
  5659. // For a single selection listview, disable extending the selection
  5660. // by turning off the keyboard modifiers.
  5661. if (plv->ci.style & LVS_SINGLESEL)
  5662. {
  5663. fCtlDown = FALSE;
  5664. fShiftDown = FALSE;
  5665. }
  5666. //
  5667. // Let the Arrow function attempt to process the key.
  5668. //
  5669. iNewFocus = ListView_Arrow(plv, plv->iFocus, vk);
  5670. // If control (disjoint) selection, don't change selection.
  5671. // If shift (extend) or control selection, don't deselect all.
  5672. //
  5673. if (iNewFocus != -1)
  5674. {
  5675. if (fShiftDown)
  5676. {
  5677. ListView_SelectRangeTo(plv, iNewFocus, TRUE);
  5678. ListView_SetFocusSel(plv, iNewFocus, FALSE, FALSE, FALSE);
  5679. }
  5680. else
  5681. {
  5682. if (!fCtlDown)
  5683. plv->iMark = iNewFocus;
  5684. ListView_SetFocusSel(plv, iNewFocus, !fCtlDown, !fShiftDown && !fCtlDown, FALSE);
  5685. }
  5686. IncrementSearchString(&plv->is, 0, NULL);
  5687. CCPlaySound(c_szSelect);
  5688. ListView_OnKeyboardSelected(plv, iNewFocus);
  5689. }
  5690. // on keyboard movement, scroll immediately.
  5691. if (ListView_CancelScrollWait(plv))
  5692. {
  5693. ListView_OnEnsureVisible(plv, plv->iFocus, FALSE);
  5694. UpdateWindow(plv->ci.hwnd);
  5695. }
  5696. //notify of navigation key usage
  5697. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5698. }
  5699. //
  5700. // LVN_INCREMENTALSEARCH gives the app the opportunity to customize
  5701. // incremental search. For example, if the items are numeric,
  5702. // the app can do numerical search instead of string search.
  5703. //
  5704. // App sets pnmfi->lvfi.lParam to the result of the incremental search,
  5705. // or to -2 to fai the search and just beep.
  5706. //
  5707. // App can return 2 to indicate that all processing should stop, if
  5708. // app wants to take over incremental search completely.
  5709. //
  5710. BOOL ListView_IncrementalSearch(LV *plv, int iStartFrom, LPNMLVFINDITEM pnmfi, int *pi)
  5711. {
  5712. INT_PTR fRc;
  5713. ASSERT(!(pnmfi->lvfi.flags & LVFI_PARAM));
  5714. pnmfi->lvfi.lParam = -1;
  5715. fRc = CCSendNotify(&plv->ci, LVN_INCREMENTALSEARCH, &pnmfi->hdr);
  5716. *pi = (int)pnmfi->lvfi.lParam;
  5717. // Cannot just return fRc because some apps return 1 to all WM_NOTIFY's
  5718. return fRc == 2;
  5719. }
  5720. // Now only Korean version is interested in incremental search with composition string.
  5721. LPTSTR GET_COMP_STRING(HIMC hImc, DWORD dwFlags)
  5722. {
  5723. LONG iNumComp;
  5724. PTSTR pszCompStr;
  5725. iNumComp = ImmGetCompositionString(hImc, dwFlags, NULL, 0);
  5726. pszCompStr = (PTSTR)LocalAlloc(LPTR, sizeof(TCHAR)*(iNumComp+1));
  5727. if (pszCompStr)
  5728. {
  5729. if (iNumComp)
  5730. ImmGetCompositionString(hImc, dwFlags, pszCompStr, iNumComp+1);
  5731. pszCompStr[iNumComp] = TEXT('\0');
  5732. }
  5733. return pszCompStr;
  5734. }
  5735. #define FREE_COMP_STRING(pszCompStr) LocalFree((HLOCAL)(pszCompStr))
  5736. BOOL ListView_OnImeComposition(LV* plv, WPARAM wParam, LPARAM lParam)
  5737. {
  5738. LPTSTR lpsz;
  5739. NMLVFINDITEM nmfi;
  5740. int i;
  5741. int iStartFrom = -1;
  5742. int iLen;
  5743. int iCount;
  5744. HIMC hImc;
  5745. TCHAR *pszCompStr;
  5746. BOOL fRet = TRUE;
  5747. iCount = ListView_Count(plv);
  5748. if (!iCount || plv->iFocus == -1)
  5749. return fRet;
  5750. if (hImc = ImmGetContext(plv->ci.hwnd))
  5751. {
  5752. if (lParam & GCS_RESULTSTR)
  5753. {
  5754. fRet = FALSE;
  5755. pszCompStr = GET_COMP_STRING(hImc, GCS_RESULTSTR);
  5756. if (pszCompStr)
  5757. {
  5758. IncrementSearchImeCompStr(&plv->is, FALSE, pszCompStr, &lpsz);
  5759. FREE_COMP_STRING(pszCompStr);
  5760. }
  5761. }
  5762. if (lParam & GCS_COMPSTR)
  5763. {
  5764. fRet = TRUE;
  5765. pszCompStr = GET_COMP_STRING(hImc, GCS_COMPSTR);
  5766. if (pszCompStr)
  5767. {
  5768. if (IncrementSearchImeCompStr(&plv->is, TRUE, pszCompStr, &lpsz))
  5769. iStartFrom = plv->iFocus;
  5770. else
  5771. iStartFrom = ((plv->iFocus - 1) + iCount)% iCount;
  5772. nmfi.lvfi.flags = LVFI_SUBSTRING | LVFI_STRING | LVFI_WRAP;
  5773. nmfi.lvfi.psz = lpsz;
  5774. iLen = lstrlen(lpsz);
  5775. // special case space as the first character
  5776. if ((iLen == 1) && (*lpsz == TEXT(' ')))
  5777. {
  5778. if (plv->iFocus != -1)
  5779. {
  5780. ListView_OnSetItemState(plv, plv->iFocus, LVIS_SELECTED, LVIS_SELECTED);
  5781. IncrementSearchString(&plv->is, 0, NULL);
  5782. }
  5783. //notify of navigation key usage
  5784. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5785. return fRet;
  5786. }
  5787. // Give caller full string in case they want to do something custom
  5788. if (ListView_IncrementalSearch(plv, iStartFrom, &nmfi, &i))
  5789. return fRet;
  5790. if (iLen > 0 && SameChars(lpsz, lpsz[0]))
  5791. {
  5792. // The user has been typing the same char over and over again.
  5793. // Switch from incremental search to Windows 3.1 style search.
  5794. iStartFrom = plv->iFocus;
  5795. nmfi.lvfi.psz = lpsz + iLen - 1;
  5796. }
  5797. if (i == -1)
  5798. i = ListView_OnFindItem(plv, iStartFrom, &nmfi.lvfi);
  5799. if (!ListView_IsValidItemNumber(plv, i))
  5800. {
  5801. i = -1;
  5802. }
  5803. TraceMsg(TF_LISTVIEW, "CIme listsearch %d %s %d", (LPTSTR)lpsz, (LPTSTR)lpsz, i);
  5804. if (i != -1)
  5805. {
  5806. ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
  5807. plv->iMark = i;
  5808. if (ListView_CancelScrollWait(plv))
  5809. ListView_OnEnsureVisible(plv, i, FALSE);
  5810. }
  5811. else
  5812. {
  5813. // Don't beep on spaces, we use it for selection.
  5814. IncrementSearchBeep(&plv->is);
  5815. }
  5816. //notify of navigation key usage
  5817. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5818. FREE_COMP_STRING(pszCompStr);
  5819. }
  5820. }
  5821. ImmReleaseContext(plv->ci.hwnd, hImc);
  5822. }
  5823. return fRet;
  5824. }
  5825. // REVIEW: We will want to reset ichCharBuf to 0 on certain conditions,
  5826. // such as: focus change, ENTER, arrow key, mouse click, etc.
  5827. //
  5828. void ListView_OnChar(LV* plv, UINT ch, int cRepeat)
  5829. {
  5830. LPTSTR lpsz;
  5831. NMLVFINDITEM nmfi;
  5832. int i;
  5833. int iStartFrom = -1;
  5834. int iLen;
  5835. int iCount;
  5836. iCount = ListView_Count(plv);
  5837. if (!iCount)
  5838. return;
  5839. // Don't search for chars that cannot be in a file name (like ENTER and TAB)
  5840. // The Polish keyboard layout uses CTRL+ALT to
  5841. // enter some normal letters, so don't punt if the CTRL key is down or
  5842. // people in Poland are in trouble! We need to fix this. NTRAID 5262.
  5843. if (ch < TEXT(' '))// || GetKeyState(VK_CONTROL) < 0)
  5844. {
  5845. IncrementSearchString(&plv->is, 0, NULL);
  5846. return;
  5847. }
  5848. if (IncrementSearchString(&plv->is, ch, &lpsz))
  5849. iStartFrom = plv->iFocus;
  5850. else
  5851. iStartFrom = ((plv->iFocus - 1) + iCount)% iCount;
  5852. nmfi.lvfi.flags = LVFI_SUBSTRING | LVFI_STRING | LVFI_WRAP;
  5853. nmfi.lvfi.psz = lpsz;
  5854. iLen = lstrlen(lpsz);
  5855. // special case space as the first character
  5856. if ((iLen == 1) && (*lpsz == ' '))
  5857. {
  5858. if (plv->iFocus != -1)
  5859. {
  5860. ListView_OnSetItemState(plv, plv->iFocus, LVIS_SELECTED, LVIS_SELECTED);
  5861. IncrementSearchString(&plv->is, 0, NULL);
  5862. }
  5863. //notify of navigation key usage
  5864. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5865. return;
  5866. }
  5867. // Give caller full string in case they want to do something custom
  5868. if (ListView_IncrementalSearch(plv, iStartFrom, &nmfi, &i))
  5869. return;
  5870. if (iLen > 0 && SameChars(lpsz, lpsz[0]))
  5871. {
  5872. //
  5873. // The user has been typing the same char over and over again.
  5874. // Switch from incremental search to Windows 3.1 style search.
  5875. //
  5876. iStartFrom = plv->iFocus;
  5877. nmfi.lvfi.psz = lpsz + iLen - 1;
  5878. }
  5879. if (i == -1)
  5880. i = ListView_OnFindItem(plv, iStartFrom, &nmfi.lvfi);
  5881. if (!ListView_IsValidItemNumber(plv, i))
  5882. {
  5883. i = -1;
  5884. }
  5885. TraceMsg(TF_LISTVIEW, "listsearch %d %s %d", (LPTSTR)lpsz, (LPTSTR)lpsz, i);
  5886. if (i != -1)
  5887. {
  5888. ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
  5889. plv->iMark = i;
  5890. if (ListView_CancelScrollWait(plv))
  5891. ListView_OnEnsureVisible(plv, i, FALSE);
  5892. }
  5893. else
  5894. {
  5895. // Don't beep on spaces, we use it for selection.
  5896. IncrementSearchBeep(&plv->is);
  5897. }
  5898. //notify of navigation key usage
  5899. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5900. }
  5901. BOOL SameChars(LPTSTR lpsz, TCHAR c)
  5902. {
  5903. while (*lpsz)
  5904. {
  5905. if (*lpsz++ != c)
  5906. return FALSE;
  5907. }
  5908. return TRUE;
  5909. }
  5910. UINT ListView_OnGetDlgCode(LV* plv, MSG* lpmsg)
  5911. {
  5912. return DLGC_WANTARROWS | DLGC_WANTCHARS;
  5913. }
  5914. int ListView_ComputeCXItemSize(LV* plv)
  5915. {
  5916. int cxItem;
  5917. cxItem = 16 * plv->cxLabelChar + plv->cxSmIcon;
  5918. if (cxItem == 0)
  5919. {
  5920. cxItem = g_cxBorder;
  5921. }
  5922. ASSERT(cxItem != 0);
  5923. return cxItem;
  5924. }
  5925. int ListView_ComputeCYItemSize(LV* plv)
  5926. {
  5927. int cyItem;
  5928. cyItem = max(plv->cyLabelChar, plv->cySmIcon);
  5929. if (plv->himlState)
  5930. {
  5931. cyItem = max(cyItem, plv->cyState);
  5932. }
  5933. cyItem += g_cyBorder;
  5934. ASSERT(cyItem != 0);
  5935. return cyItem;
  5936. }
  5937. void ListView_InvalidateCachedLabelSizes(LV* plv)
  5938. {
  5939. int i;
  5940. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  5941. // Label wrapping has changed, so we need to invalidate the
  5942. // size of the items, such that they will be recomputed.
  5943. //
  5944. if (!ListView_IsOwnerData(plv))
  5945. {
  5946. for (i = ListView_Count(plv) - 1; i >= 0; i--)
  5947. {
  5948. LISTITEM* pitem = ListView_FastGetItemPtr(plv, i);
  5949. ListView_SetSRecompute(pitem);
  5950. }
  5951. }
  5952. plv->rcView.left = RECOMPUTE;
  5953. if ((plv->ci.style & LVS_OWNERDRAWFIXED) && ListView_IsReportView(plv))
  5954. plv->cyItemSave = ListView_ComputeCYItemSize(plv);
  5955. else
  5956. {
  5957. plv->cyItem = ListView_ComputeCYItemSize(plv);
  5958. }
  5959. }
  5960. void ListView_OnStyleChanging(LV* plv, UINT gwl, LPSTYLESTRUCT pinfo)
  5961. {
  5962. if (gwl == GWL_STYLE)
  5963. {
  5964. // Don't allow LVS_OWNERDATA to change after creation
  5965. DWORD stylePreserve = LVS_OWNERDATA;
  5966. // Don't allow a LVS_EX_REGIONAL listview to change type, since
  5967. // it must be LVS_ICON
  5968. // Similarly, HideLabels only works in large icon mode so keep the type.
  5969. if ((plv->exStyle & LVS_EX_REGIONAL) || ListView_HideLabels(plv))
  5970. stylePreserve |= LVS_TYPEMASK;
  5971. // Preserve the bits that must be preserved
  5972. pinfo->styleNew ^= (pinfo->styleNew ^ pinfo->styleOld) & stylePreserve;
  5973. // If we're in group view, then listview must be in autoarrange
  5974. if (plv->fGroupView)
  5975. {
  5976. pinfo->styleNew |= LVS_AUTOARRANGE;
  5977. }
  5978. }
  5979. }
  5980. WORD MapViewStyle(DWORD style)
  5981. {
  5982. if (style == LVS_LIST)
  5983. return LV_VIEW_LIST;
  5984. if (style == LVS_SMALLICON)
  5985. return LV_VIEW_SMALLICON;
  5986. if (style == LVS_REPORT)
  5987. return LV_VIEW_DETAILS;
  5988. return LV_VIEW_ICON;
  5989. }
  5990. void ListView_OnStyleChanged(LV* plv, UINT gwl, LPSTYLESTRUCT pinfo)
  5991. {
  5992. // Style changed: redraw everything...
  5993. //
  5994. // try to do this smartly, avoiding unnecessary redraws
  5995. if (gwl == GWL_STYLE)
  5996. {
  5997. BOOL fRedraw = FALSE, fShouldScroll = FALSE;
  5998. DWORD changeFlags, styleOld;
  5999. ListView_DismissEdit(plv, FALSE);
  6000. changeFlags = plv->ci.style ^ pinfo->styleNew;
  6001. styleOld = plv->ci.style;
  6002. // (dli) Setting the small icon width here and only in the case when we go
  6003. // from large icon view to some other view because of three reasons:
  6004. // 1. According to chee, we want to set this before we change the style bit in
  6005. // plv or after we scale.
  6006. // 2. We don't want to do it after we scale because we want to set the width to
  6007. // the maximum value so that the items in this listview do not cover each other
  6008. // 3. we do it from large icon view because large icon view has fixed width for
  6009. // each item, small icon view width can be scaled.
  6010. //
  6011. if ((changeFlags & LVS_TYPEMASK) && (plv->wView == LV_VIEW_ICON))
  6012. ListView_ISetColumnWidth(plv, 0,
  6013. LV_GetNewColWidth(plv, 0, ListView_Count(plv)-1), FALSE);
  6014. plv->ci.style = pinfo->styleNew; // change our version
  6015. if (changeFlags & (WS_BORDER | WS_CAPTION | WS_THICKFRAME))
  6016. {
  6017. // the changing of these bits affect the size of the window
  6018. // but not until after this message is handled
  6019. // so post ourself a message.
  6020. PostMessage(plv->ci.hwnd, LVMP_WINDOWPOSCHANGED, 0, 0);
  6021. }
  6022. if (changeFlags & LVS_NOCOLUMNHEADER)
  6023. {
  6024. if (plv->hwndHdr)
  6025. {
  6026. SetWindowBits(plv->hwndHdr, GWL_STYLE, HDS_HIDDEN,
  6027. (plv->ci.style & LVS_NOCOLUMNHEADER) ? HDS_HIDDEN : 0);
  6028. fRedraw = TRUE;
  6029. fShouldScroll = TRUE;
  6030. }
  6031. }
  6032. if (changeFlags & LVS_NOLABELWRAP)
  6033. {
  6034. ListView_InvalidateCachedLabelSizes(plv);
  6035. fShouldScroll = TRUE;
  6036. fRedraw = TRUE;
  6037. }
  6038. if (changeFlags & LVS_TYPEMASK)
  6039. {
  6040. WORD wViewOld = plv->wView;
  6041. plv->wView = MapViewStyle(plv->ci.style & LVS_TYPEMASK);
  6042. ListView_TypeChange(plv, wViewOld, (BOOL)BOOLIFY(styleOld & LVS_OWNERDRAWFIXED));
  6043. fShouldScroll = TRUE;
  6044. fRedraw = TRUE;
  6045. }
  6046. if (changeFlags & LVS_AUTOARRANGE)
  6047. {
  6048. if (plv->ci.style & LVS_AUTOARRANGE)
  6049. {
  6050. // Turned on.
  6051. ListView_OnArrange(plv, LVA_DEFAULT);
  6052. fRedraw = TRUE;
  6053. }
  6054. else
  6055. {
  6056. // Turned off. Nuke insertmark, because that's not allowed when
  6057. // auto-arrange is off.
  6058. LVINSERTMARK lvim = {0};
  6059. lvim.cbSize = sizeof(LVINSERTMARK);
  6060. lvim.iItem = -1;
  6061. ListView_OnSetInsertMark(plv, &lvim);
  6062. }
  6063. }
  6064. // previously, this was the else to
  6065. // (changeFlags & LVS_AUTOARRANGE && (plv->ci.style & LVS_AUTOARRANGE))
  6066. // I'm not sure that was really the right thing..
  6067. if (fShouldScroll)
  6068. {
  6069. // Else we would like to make the most important item to still
  6070. // be visible. So first we will look for a cursorered item
  6071. // if this fails, we will look for the first selected item,
  6072. // else we will simply ask for the first item (assuming the
  6073. // count > 0
  6074. //
  6075. int i;
  6076. // And make sure the scrollbars are up to date Note this
  6077. // also updates some variables that some views need
  6078. ListView_UpdateScrollBars(plv);
  6079. i = (plv->iFocus >= 0) ? plv->iFocus : ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  6080. if ((i == -1) && (ListView_Count(plv) > 0))
  6081. i = 0;
  6082. if (i != -1)
  6083. ListView_OnEnsureVisible(plv, i, TRUE);
  6084. }
  6085. if (fRedraw)
  6086. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  6087. }
  6088. else if (gwl == GWL_EXSTYLE)
  6089. {
  6090. //
  6091. // If the RTL_MIRROR extended style bit had changed, let's
  6092. // repaint the control window.
  6093. //
  6094. if ((plv->ci.dwExStyle&RTL_MIRRORED_WINDOW) != (pinfo->styleNew&RTL_MIRRORED_WINDOW))
  6095. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  6096. // Save the new ex-style bits
  6097. plv->ci.dwExStyle = pinfo->styleNew;
  6098. }
  6099. // Change of styles also changes tooltip policy, so pop it
  6100. ListView_PopBubble(plv);
  6101. }
  6102. void ListView_TypeChange(LV* plv, WORD wViewOld, BOOL fOwnerDrawFixed)
  6103. {
  6104. RECT rc;
  6105. int i;
  6106. //
  6107. // Invalidate all cached string metrics because customdraw clients
  6108. // may draw differently depending on the type. This happens more
  6109. // often than you might think, not on purpose, but because apps are
  6110. // buggy.
  6111. //
  6112. if (!ListView_IsOwnerData(plv))
  6113. {
  6114. for (i = 0; i < ListView_Count(plv); i++)
  6115. {
  6116. LISTITEM *pitem = ListView_FastGetItemPtr(plv, i);
  6117. ListView_SetSRecompute(pitem);
  6118. }
  6119. }
  6120. switch (wViewOld)
  6121. {
  6122. case LV_VIEW_DETAILS:
  6123. ShowWindow(plv->hwndHdr, SW_HIDE);
  6124. if (fOwnerDrawFixed)
  6125. {
  6126. // swap cyItem and cyFixed;
  6127. int temp = plv->cyItem;
  6128. plv->cyItem = plv->cyItemSave;
  6129. plv->cyItemSave = temp;
  6130. }
  6131. break;
  6132. default:
  6133. break;
  6134. }
  6135. plv->ptOrigin.x = 0;
  6136. plv->ptOrigin.y = 0;
  6137. plv->ptlRptOrigin.x = 0;
  6138. plv->ptlRptOrigin.y = 0;
  6139. plv->rcView.left = RECOMPUTE;
  6140. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  6141. // Now handle any special setup needed for the new view
  6142. switch (plv->wView)
  6143. {
  6144. case LV_VIEW_LIST:
  6145. // We may need to resize the columns
  6146. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  6147. break;
  6148. case LV_VIEW_DETAILS:
  6149. // if it's owner draw fixed, we may have to do funky stuff
  6150. if (wViewOld != LV_VIEW_DETAILS)
  6151. {
  6152. plv->cyItemSave = plv->cyItem;
  6153. }
  6154. ListView_RInitialize(plv, FALSE);
  6155. break;
  6156. default:
  6157. break;
  6158. }
  6159. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  6160. GetClientRect(plv->ci.hwnd, &rc);
  6161. plv->sizeClient.cx = rc.right;
  6162. plv->sizeClient.cy = rc.bottom;
  6163. }
  6164. int ListView_OnHitTest(LV* plv, LV_HITTESTINFO* pinfo)
  6165. {
  6166. UINT flags;
  6167. int x, y;
  6168. if (!pinfo) return -1;
  6169. x = pinfo->pt.x;
  6170. y = pinfo->pt.y;
  6171. pinfo->iItem = -1;
  6172. flags = 0;
  6173. if (x < 0)
  6174. flags |= LVHT_TOLEFT;
  6175. else if (x >= plv->sizeClient.cx)
  6176. flags |= LVHT_TORIGHT;
  6177. if (y < 0)
  6178. flags |= LVHT_ABOVE;
  6179. else if (y >= plv->sizeClient.cy)
  6180. flags |= LVHT_BELOW;
  6181. if (flags == 0)
  6182. {
  6183. pinfo->iItem = _ListView_ItemHitTest(plv, x, y, &flags, NULL);
  6184. }
  6185. pinfo->flags = flags;
  6186. if (pinfo->iItem >= ListView_Count(plv))
  6187. {
  6188. pinfo->iItem = -1;
  6189. pinfo->flags = LVHT_NOWHERE;
  6190. }
  6191. return pinfo->iItem;
  6192. }
  6193. int ScrollAmount(int large, int iSmall, int unit)
  6194. {
  6195. return (((large - iSmall) + (unit - 1)) / unit) * unit;
  6196. }
  6197. // NOTE: this is duplicated in shell32.dll
  6198. //
  6199. // checks to see if we are at the end position of a scroll bar
  6200. // to avoid scrolling when not needed (avoid flashing)
  6201. //
  6202. // in:
  6203. // code SB_VERT or SB_HORZ
  6204. // bDown FALSE is up or left
  6205. // TRUE is down or right
  6206. BOOL CanScroll(LV* plv, int code, BOOL bDown)
  6207. {
  6208. SCROLLINFO si;
  6209. si.cbSize = sizeof(SCROLLINFO);
  6210. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
  6211. if (ListView_GetScrollInfo(plv, code, &si))
  6212. {
  6213. if (bDown)
  6214. {
  6215. if (si.nPage)
  6216. si.nMax -= (si.nPage - 1);
  6217. return si.nPos < si.nMax;
  6218. }
  6219. else
  6220. {
  6221. return si.nPos > si.nMin;
  6222. }
  6223. }
  6224. else
  6225. {
  6226. return FALSE;
  6227. }
  6228. }
  6229. // detect if we should auto scroll the window
  6230. //
  6231. // in:
  6232. // pt cursor pos in hwnd's client coords
  6233. // out:
  6234. // pdx, pdy ammount scrolled in x and y
  6235. //
  6236. // REVIEW, this should make sure a certain amount of time has passed
  6237. // before scrolling.
  6238. void ScrollDetect(LV* plv, POINT pt, int *pdx, int *pdy)
  6239. {
  6240. int dx, dy;
  6241. *pdx = *pdy = 0;
  6242. if (!(plv->ci.style & (WS_HSCROLL | WS_VSCROLL)))
  6243. return;
  6244. dx = dy = plv->cyIcon / 16;
  6245. if (ListView_IsReportView(plv))
  6246. {
  6247. if (!plv->fGroupView) // Groupview is always in pixels
  6248. dy = plv->cyItem; // we scroll in units of items...
  6249. if (!dx)
  6250. dx = plv->cxSmIcon;
  6251. }
  6252. if (ListView_IsListView(plv))
  6253. dx = plv->cxItem;
  6254. if (!dx)
  6255. dx = 1;
  6256. if (!dy)
  6257. dy = 1;
  6258. // we need to check if we can scroll before acutally doing it
  6259. // since the selection rect is adjusted based on how much
  6260. // we scroll by
  6261. if (plv->ci.style & WS_VSCROLL) // scroll vertically?
  6262. {
  6263. if (pt.y >= plv->sizeClient.cy)
  6264. {
  6265. if (CanScroll(plv, SB_VERT, TRUE))
  6266. *pdy = ScrollAmount(pt.y, plv->sizeClient.cy, dy); // down
  6267. }
  6268. else if (pt.y <= 0)
  6269. {
  6270. if (CanScroll(plv, SB_VERT, FALSE))
  6271. *pdy = -ScrollAmount(0, pt.y, dy); // up
  6272. }
  6273. }
  6274. if (plv->ci.style & WS_HSCROLL) // horizontally
  6275. {
  6276. if (pt.x >= plv->sizeClient.cx)
  6277. {
  6278. if (CanScroll(plv, SB_HORZ, TRUE))
  6279. *pdx = ScrollAmount(pt.x, plv->sizeClient.cx, dx); // right
  6280. }
  6281. else if (pt.x <= 0)
  6282. {
  6283. if (CanScroll(plv, SB_HORZ, FALSE))
  6284. *pdx = -ScrollAmount(0, pt.x, dx); // left
  6285. }
  6286. }
  6287. // REARCHITECT: this will potentially scroll outside the bounds of the
  6288. // listview. we should bound the scroll amount in CanScroll()
  6289. // or ScrollAmount().
  6290. if (*pdx || *pdy)
  6291. {
  6292. ListView_ValidateScrollParams(plv, pdx, pdy);
  6293. }
  6294. }
  6295. #define swap(pi1, pi2) {int i = *(pi1) ; *(pi1) = *(pi2) ; *(pi2) = i ;}
  6296. void OrderRect(RECT *prc)
  6297. {
  6298. if (prc->left > prc->right)
  6299. swap(&prc->left, &prc->right);
  6300. if (prc->bottom < prc->top)
  6301. swap(&prc->bottom, &prc->top);
  6302. }
  6303. // in:
  6304. // x, y starting point in client coords
  6305. #define SCROLL_FREQ (GetDoubleClickTime()/2) // 1/5 of a second between scrolls
  6306. BOOL ShouldScroll(LV* plv, LPPOINT ppt, LPRECT lprc)
  6307. {
  6308. ASSERT(ppt);
  6309. if (plv->ci.style & WS_VSCROLL)
  6310. {
  6311. if (ppt->y >= lprc->bottom)
  6312. {
  6313. if (CanScroll(plv, SB_VERT, TRUE))
  6314. return TRUE;
  6315. }
  6316. else if (ppt->y <= lprc->top)
  6317. {
  6318. if (CanScroll(plv, SB_VERT, FALSE))
  6319. return TRUE;
  6320. }
  6321. }
  6322. if (plv->ci.style & WS_HSCROLL)
  6323. {
  6324. if (ppt->x >= lprc->right)
  6325. {
  6326. if (CanScroll(plv, SB_HORZ, TRUE))
  6327. return TRUE;
  6328. }
  6329. else if (ppt->x <= lprc->left)
  6330. {
  6331. if (CanScroll(plv, SB_HORZ, FALSE))
  6332. return TRUE;
  6333. }
  6334. }
  6335. return FALSE;
  6336. }
  6337. BOOL DrawFocusRectClip(HDC hdc, CONST RECT * prc, CONST RECT * prcClip)
  6338. {
  6339. RECT rc;
  6340. IntersectRect(&rc, prc, prcClip);
  6341. return DrawFocusRect(hdc, &rc);
  6342. }
  6343. // Listview is "Alpha Capable" if:
  6344. // Colors >= 16bpp (Needed for alpha)
  6345. // The Listview is double buffered (Needed for flicker)
  6346. // The use has "Show window contents while dragging" (Needed to turn off on slow machines)
  6347. // NOTE: g_fDragFullWindows is turned off in comctl32 when running a remote session
  6348. BOOL ListView_IsAlphaMarqueeCapable(LV* plv)
  6349. {
  6350. BOOL fAlphaCapable = FALSE;
  6351. if (ListView_IsDoubleBuffer(plv))
  6352. {
  6353. if (AreAllMonitorsAtLeast(16))
  6354. {
  6355. fAlphaCapable = plv->fListviewAlphaSelect;
  6356. }
  6357. }
  6358. return fAlphaCapable;
  6359. }
  6360. void ListView_DragSelect(LV *plv, int x, int y)
  6361. {
  6362. RECT rc, rcWindow, rcOld, rcUnion, rcTemp2, rcClip;
  6363. POINT pt;
  6364. MSG32 msg32;
  6365. HDC hdc;
  6366. HWND hwnd = plv->ci.hwnd;
  6367. int i, iEnd, dx, dy;
  6368. BOOL bInOld, bInNew = FALSE, bLocked = FALSE;
  6369. DWORD dwTime, dwNewTime;
  6370. HRGN hrgnUpdate = NULL, hrgnLV = NULL;
  6371. BOOL fAlphaMarquee = ListView_IsAlphaMarqueeCapable(plv);
  6372. rc.left = rc.right = x;
  6373. rc.top = rc.bottom = y;
  6374. rcOld = rc;
  6375. UpdateWindow(plv->ci.hwnd);
  6376. if (plv->exStyle & LVS_EX_REGIONAL)
  6377. {
  6378. if ((hrgnUpdate = CreateRectRgn(0,0,0,0)) &&
  6379. (hrgnLV = CreateRectRgn(0,0,0,0)) &&
  6380. (LockWindowUpdate(GetParent(hwnd))))
  6381. {
  6382. hdc = GetDCEx(hwnd, NULL, DCX_PARENTCLIP | DCX_LOCKWINDOWUPDATE);
  6383. bLocked = TRUE;
  6384. }
  6385. else
  6386. {
  6387. goto BailOut;
  6388. }
  6389. }
  6390. else
  6391. {
  6392. hdc = GetDC(hwnd);
  6393. }
  6394. SetCapture(hwnd);
  6395. if (fAlphaMarquee)
  6396. {
  6397. plv->flags |= LVF_MARQUEE;
  6398. plv->rcMarquee = rc;
  6399. InvalidateRect(plv->ci.hwnd, &plv->rcMarquee, TRUE);
  6400. }
  6401. else
  6402. {
  6403. DrawFocusRect(hdc, &rc);
  6404. }
  6405. GetClientRect(hwnd, &rcClip);
  6406. GetWindowRect(hwnd, &rcWindow);
  6407. dwTime = GetTickCount();
  6408. for (;;)
  6409. {
  6410. // WM_CANCELMODE messages will unset the capture, in that
  6411. // case I want to exit this loop
  6412. if (GetCapture() != hwnd)
  6413. {
  6414. break;
  6415. }
  6416. if (!PeekMessage32(&msg32, NULL, 0, 0, PM_REMOVE, TRUE))
  6417. {
  6418. // if the cursor is outside of the window rect
  6419. // we need to generate messages to make autoscrolling
  6420. // keep going
  6421. if (!PtInRect(&rcWindow, msg32.pt) &&
  6422. ShouldScroll(plv, &msg32.pt, &rcWindow))
  6423. {
  6424. SetCursorPos(msg32.pt.x, msg32.pt.y);
  6425. }
  6426. else
  6427. {
  6428. WaitMessage();
  6429. }
  6430. continue;
  6431. }
  6432. // See if the application wants to process the message...
  6433. if (CallMsgFilter32(&msg32, MSGF_COMMCTRL_DRAGSELECT, TRUE) != 0)
  6434. continue;
  6435. switch (msg32.message)
  6436. {
  6437. case WM_LBUTTONUP:
  6438. case WM_RBUTTONUP:
  6439. case WM_LBUTTONDOWN:
  6440. case WM_MBUTTONDOWN:
  6441. case WM_MBUTTONUP:
  6442. case WM_RBUTTONDOWN:
  6443. CCReleaseCapture(&plv->ci);
  6444. goto EndOfLoop;
  6445. case WM_TIMER:
  6446. if (msg32.wParam != IDT_MARQUEE)
  6447. goto DoDefault;
  6448. // else fall through
  6449. case WM_MOUSEMOVE:
  6450. {
  6451. int dMax = -1;
  6452. pt = msg32.pt;
  6453. ScreenToClient(hwnd, &pt);
  6454. dwNewTime = GetTickCount();
  6455. // if (1 || (dwNewTime - dwTime) > SCROLL_FREQ)
  6456. // {
  6457. dwTime = dwNewTime; // reset scroll timer
  6458. ScrollDetect(plv, pt, &dx, &dy);
  6459. // }
  6460. // else
  6461. // {
  6462. // dx = dy = 0;
  6463. // }
  6464. //SetTimer(plv->ci.hwnd, IDT_MARQUEE, SCROLL_FREQ, NULL);
  6465. y -= dy; // scroll up/down
  6466. x -= dx; // scroll left/right
  6467. rc.left = x;
  6468. rc.top = y;
  6469. rc.right = pt.x;
  6470. rc.bottom = pt.y;
  6471. // clip drag rect to the window
  6472. //
  6473. if (rc.right > rcClip.right)
  6474. rc.right = rcClip.right;
  6475. if (rc.right < rcClip.left)
  6476. rc.right = rcClip.left;
  6477. if (rc.bottom > rcClip.bottom)
  6478. rc.bottom = rcClip.bottom;
  6479. if (rc.bottom < rcClip.top)
  6480. rc.bottom = rcClip.top;
  6481. OrderRect(&rc);
  6482. if (EqualRect(&rc, &rcOld))
  6483. break;
  6484. // move the old rect
  6485. if (!fAlphaMarquee)
  6486. {
  6487. DrawFocusRect(hdc, &rcOld); // erase old
  6488. }
  6489. if (dx || dy)
  6490. ListView_OnScroll(plv, dx, dy);
  6491. OffsetRect(&rcOld, -dx, -dy);
  6492. //
  6493. // For Report and List view, we can speed things up by
  6494. // only searching through those items that are visible. We
  6495. // use the hittest to calculate the first item to paint.
  6496. // REARCHITECT:: We are using state specific info here...
  6497. //
  6498. UnionRect(&rcUnion, &rc, &rcOld);
  6499. if (ListView_IsReportView(plv) && !plv->fGroupView)
  6500. {
  6501. i = (int)((plv->ptlRptOrigin.y + rcUnion.top - plv->yTop)
  6502. / plv->cyItem);
  6503. iEnd = (int)((plv->ptlRptOrigin.y + rcUnion.bottom - plv->yTop)
  6504. / plv->cyItem) + 1;
  6505. }
  6506. else if (ListView_IsListView(plv))
  6507. {
  6508. i = ((plv->xOrigin + rcUnion.left)/ plv->cxItem)
  6509. * plv->cItemCol + rcUnion.top / plv->cyItem;
  6510. iEnd = ((plv->xOrigin + rcUnion.right)/ plv->cxItem)
  6511. * plv->cItemCol + rcUnion.bottom / plv->cyItem + 1;
  6512. }
  6513. else
  6514. {
  6515. if (ListView_IsOwnerData(plv))
  6516. {
  6517. ListView_CalcMinMaxIndex(plv, &rcUnion, &i, &iEnd);
  6518. }
  6519. else
  6520. {
  6521. i = 0;
  6522. iEnd = ListView_Count(plv);
  6523. }
  6524. }
  6525. // make sure our endpoint is in range.
  6526. if (iEnd > ListView_Count(plv))
  6527. iEnd = ListView_Count(plv);
  6528. if (i < 0)
  6529. i = 0;
  6530. if (ListView_IsOwnerData(plv) && (i < iEnd))
  6531. {
  6532. ListView_NotifyCacheHint(plv, i, iEnd-1);
  6533. }
  6534. if (bInNew && !(msg32.wParam & (MK_CONTROL | MK_SHIFT)))
  6535. {
  6536. plv->iMark = -1;
  6537. }
  6538. for (; i < iEnd; i++)
  6539. {
  6540. RECT dummy;
  6541. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, NULL, &rcTemp2);
  6542. // don't do this infaltion if we're in report&full row mode
  6543. // in that case, just touching is good enough
  6544. if (!(ListView_IsReportView(plv) && ListView_FullRowSelect(plv)))
  6545. {
  6546. int cxInflate = (rcTemp2.right - rcTemp2.left) / 4;
  6547. if (ListView_IsListView(plv))
  6548. {
  6549. cxInflate = min(cxInflate, plv->cxSmIcon);
  6550. }
  6551. InflateRect(&rcTemp2, -cxInflate, -(rcTemp2.bottom - rcTemp2.top) / 4);
  6552. }
  6553. bInOld = (IntersectRect(&dummy, &rcOld, &rcTemp2) != 0);
  6554. bInNew = (IntersectRect(&dummy, &rc, &rcTemp2) != 0);
  6555. if (msg32.wParam & MK_CONTROL)
  6556. {
  6557. if (bInOld != bInNew)
  6558. {
  6559. ListView_ToggleSelection(plv, i);
  6560. }
  6561. }
  6562. else
  6563. {
  6564. // was there a change?
  6565. if (bInOld != bInNew)
  6566. {
  6567. ListView_OnSetItemState(plv, i, bInOld ? 0 : LVIS_SELECTED, LVIS_SELECTED);
  6568. }
  6569. // if no alternate keys are down.. set the mark to
  6570. // the item furthest from the cursor
  6571. if (bInNew && !(msg32.wParam & (MK_CONTROL | MK_SHIFT)))
  6572. {
  6573. int dItem;
  6574. dItem = (rcTemp2.left - pt.x) * (rcTemp2.left - pt.x) +
  6575. (rcTemp2.top - pt.y) * (rcTemp2.top - pt.y);
  6576. // if it's further away, set this as the mark
  6577. //DebugMsg(TF_LISTVIEW, "dItem = %d, dMax = %d", dItem, dMax);
  6578. if (dItem > dMax)
  6579. {
  6580. //DebugMsg(TF_LISTVIEW, "taking dItem .. iMark = %d", i);
  6581. dMax = dItem;
  6582. plv->iMark = i;
  6583. }
  6584. }
  6585. }
  6586. }
  6587. if (fAlphaMarquee)
  6588. {
  6589. RECT rcInvalid;
  6590. UnionRect(&rcInvalid, &rcOld, &rc);
  6591. InflateRect(&rcInvalid, 1, 1);
  6592. plv->flags |= LVF_MARQUEE;
  6593. plv->rcMarquee = rc;
  6594. InvalidateRect(plv->ci.hwnd, &rcInvalid, TRUE);
  6595. }
  6596. //DebugMsg(TF_LISTVIEW, "Final iMark = %d", plv->iMark);
  6597. if (bLocked)
  6598. {
  6599. if (GetUpdateRgn(plv->ci.hwnd, hrgnUpdate, FALSE) > NULLREGION)
  6600. {
  6601. ValidateRect(plv->ci.hwnd, NULL);
  6602. GetWindowRgn(plv->ci.hwnd, hrgnLV);
  6603. CombineRgn(hrgnUpdate, hrgnUpdate, hrgnLV, RGN_AND);
  6604. SelectClipRgn(hdc, hrgnUpdate);
  6605. SendMessage(plv->ci.hwnd, WM_PRINTCLIENT, (WPARAM)hdc, 0);
  6606. SelectClipRgn(hdc, NULL);
  6607. }
  6608. }
  6609. else
  6610. {
  6611. UpdateWindow(plv->ci.hwnd); // make selection draw
  6612. }
  6613. if (!fAlphaMarquee)
  6614. {
  6615. DrawFocusRect(hdc, &rc);
  6616. }
  6617. rcOld = rc;
  6618. break;
  6619. }
  6620. case WM_KEYDOWN:
  6621. switch (msg32.wParam)
  6622. {
  6623. case VK_ESCAPE:
  6624. ListView_DeselectAll(plv, -1);
  6625. goto EndOfLoop;
  6626. }
  6627. case WM_CHAR:
  6628. case WM_KEYUP:
  6629. // don't process thay keyboard stuff during marquee
  6630. break;
  6631. default:
  6632. // don't process mouse wheel stuff
  6633. if (msg32.message == g_msgMSWheel)
  6634. break;
  6635. DoDefault:
  6636. TranslateMessage32(&msg32, TRUE);
  6637. DispatchMessage32(&msg32, TRUE);
  6638. }
  6639. }
  6640. EndOfLoop:
  6641. plv->flags &= ~LVF_MARQUEE;
  6642. if (fAlphaMarquee)
  6643. {
  6644. InvalidateRect(plv->ci.hwnd, &rcOld, TRUE);
  6645. }
  6646. else
  6647. {
  6648. DrawFocusRect(hdc, &rcOld); // erase old
  6649. }
  6650. ReleaseDC(hwnd, hdc);
  6651. BailOut:
  6652. if (hrgnUpdate)
  6653. DeleteObject(hrgnUpdate);
  6654. if (hrgnLV)
  6655. DeleteObject(hrgnLV);
  6656. if (bLocked)
  6657. LockWindowUpdate(NULL);
  6658. }
  6659. #define SHIFT_DOWN(keyFlags) (keyFlags & MK_SHIFT)
  6660. #define CONTROL_DOWN(keyFlags) (keyFlags & MK_CONTROL)
  6661. #define RIGHTBUTTON(keyFlags) (keyFlags & MK_RBUTTON)
  6662. void ListView_ButtonSelect(LV* plv, int iItem, UINT keyFlags, BOOL bSelected)
  6663. {
  6664. if (SHIFT_DOWN(keyFlags))
  6665. {
  6666. ListView_SelectRangeTo(plv, iItem, !CONTROL_DOWN(keyFlags));
  6667. ListView_SetFocusSel(plv, iItem, TRUE, FALSE, FALSE);
  6668. }
  6669. else if (!CONTROL_DOWN(keyFlags))
  6670. {
  6671. ListView_SetFocusSel(plv, iItem, TRUE, !bSelected, FALSE);
  6672. if (!RIGHTBUTTON(keyFlags) && bSelected && ListView_IsSimpleSelect(plv))
  6673. {
  6674. ListView_HandleStateIconClick(plv, iItem);
  6675. }
  6676. }
  6677. }
  6678. void ListView_HandleStateIconClick(LV* plv, int iItem)
  6679. {
  6680. int iState =
  6681. ListView_OnGetItemState(plv, iItem, LVIS_STATEIMAGEMASK);
  6682. iState = STATEIMAGEMASKTOINDEX(iState) -1;
  6683. iState++;
  6684. iState %= ImageList_GetImageCount(plv->himlState);
  6685. iState++;
  6686. ListView_OnSetItemState(plv, iItem, INDEXTOSTATEIMAGEMASK(iState), LVIS_STATEIMAGEMASK);
  6687. }
  6688. BOOL ListView_RBeginMarquee(LV* plv, int x, int y, LPLVHITTESTINFO plvhti)
  6689. {
  6690. if (ListView_FullRowSelect(plv) &&
  6691. ListView_IsReportView(plv) &&
  6692. !(plv->ci.style & LVS_SINGLESEL) &&
  6693. !ListView_OwnerDraw(plv) &&
  6694. plvhti->iSubItem == 0)
  6695. {
  6696. // can only begin marquee in column 0.
  6697. if (plvhti->flags == LVHT_ONITEM)
  6698. {
  6699. return TRUE;
  6700. }
  6701. }
  6702. return FALSE;
  6703. }
  6704. void ListView_HandleMouse(LV* plv, BOOL fDoubleClick, int x, int y, UINT keyFlags, BOOL bMouseWheel)
  6705. {
  6706. LV_HITTESTINFO ht;
  6707. NMITEMACTIVATE nm;
  6708. int iItem, click, drag;
  6709. BOOL bSelected, fHadFocus, fNotifyReturn = FALSE;
  6710. BOOL fActive;
  6711. HWND hwnd = plv->ci.hwnd;
  6712. if (plv->fButtonDown)
  6713. return;
  6714. plv->fButtonDown = TRUE;
  6715. if (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickHappened && plv->fOneClickOK)
  6716. {
  6717. KillTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED);
  6718. plv->fOneClickHappened = FALSE;
  6719. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  6720. if (!IsWindow(hwnd))
  6721. return;
  6722. }
  6723. fHadFocus = (GetFocus() == plv->ci.hwnd);
  6724. click = RIGHTBUTTON(keyFlags) ? NM_RCLICK : NM_CLICK;
  6725. drag = RIGHTBUTTON(keyFlags) ? LVN_BEGINRDRAG : LVN_BEGINDRAG;
  6726. fActive = ChildOfActiveWindow(plv->ci.hwnd) || fShouldFirstClickActivate() ||
  6727. ChildOfDesktop(plv->ci.hwnd);
  6728. TraceMsg(TF_LISTVIEW, "ListView_OnButtonDown %d", fDoubleClick);
  6729. SetCapture(plv->ci.hwnd);
  6730. plv->ptCapture.x = x;
  6731. plv->ptCapture.y = y;
  6732. if (!ListView_DismissEdit(plv, FALSE) && GetCapture() != plv->ci.hwnd)
  6733. goto EndButtonDown;
  6734. CCReleaseCapture(&plv->ci);
  6735. // REVIEW: right button implies no shift or control stuff
  6736. // Single selection style also implies no modifiers
  6737. //if (RIGHTBUTTON(keyFlags) || (plv->ci.style & LVS_SINGLESEL))
  6738. if ((plv->ci.style & LVS_SINGLESEL))
  6739. keyFlags &= ~(MK_SHIFT | MK_CONTROL);
  6740. ht.pt.x = x;
  6741. ht.pt.y = y;
  6742. iItem = ListView_OnSubItemHitTest(plv, &ht);
  6743. if (ht.iSubItem != 0)
  6744. {
  6745. // if we're not in full row select,
  6746. // hitting on a subitem is like hitting on nowhere
  6747. // also, in win95, ownerdraw fixed effectively had full row select
  6748. if (!ListView_FullRowSelect(plv) &&
  6749. !(plv->ci.style & LVS_OWNERDRAWFIXED))
  6750. {
  6751. iItem = -1;
  6752. ht.flags = LVHT_NOWHERE;
  6753. }
  6754. }
  6755. nm.iItem = iItem;
  6756. nm.iSubItem = ht.iSubItem;
  6757. nm.uChanged = 0;
  6758. nm.ptAction.x = x;
  6759. nm.ptAction.y = y;
  6760. nm.uKeyFlags = GetLVKeyFlags();
  6761. // FProt Profesional assumed that if the notification structure pointer + 14h bytes
  6762. // had a value 2 that it was a displayinfo structure and they then used offset +2c as lparam...
  6763. nm.uNewState = 0;
  6764. plv->iNoHover = iItem;
  6765. bSelected = (iItem >= 0) && ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  6766. if (fDoubleClick)
  6767. {
  6768. // Cancel any name editing that might happen.
  6769. ListView_CancelPendingEdit(plv);
  6770. KillTimer(plv->ci.hwnd, IDT_SCROLLWAIT);
  6771. if (ht.flags & LVHT_NOWHERE)
  6772. {
  6773. // this would have been done in the first click in win95 except
  6774. // now we blow off the first click on focus change
  6775. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags))
  6776. ListView_DeselectAll(plv, -1);
  6777. }
  6778. click = RIGHTBUTTON(keyFlags) ? NM_RDBLCLK : NM_DBLCLK ;
  6779. if (CCSendNotify(&plv->ci, click, &nm.hdr))
  6780. goto EndButtonDown;
  6781. /// some (comdlg32 for example) destroy on double click
  6782. // we need to bail if that happens because plv is no longer valid
  6783. if (!IsWindow(hwnd))
  6784. return;
  6785. if (click == NM_DBLCLK)
  6786. {
  6787. // these shift control flags are to mirror when we don't send out the activate on the single click,
  6788. // but are in the oneclick activate mode (see below)
  6789. if (ht.flags & (LVHT_ONITEMLABEL | LVHT_ONITEMICON))
  6790. {
  6791. // possible scenarios below:
  6792. // 1) we're using classic windows style so double click => launch
  6793. // 2) we're using single click activate
  6794. // a) shift is down and item is selected => launch
  6795. // this implies that the first click selected it
  6796. // b) control is down => launch
  6797. // the first click toggled the selection so if the item was
  6798. // the only item selected and we double clicked on it
  6799. // the first click deselects it and no item is selected
  6800. // so nothing will be launched - this is win95 behavior
  6801. if (!(plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK) ||
  6802. (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK &&
  6803. (SHIFT_DOWN(keyFlags) || CONTROL_DOWN(keyFlags))))
  6804. {
  6805. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &nm.hdr);
  6806. }
  6807. }
  6808. // Double-click on checkbox state icon cycles it just like single click
  6809. else if ((ht.flags & LVHT_ONITEMSTATEICON) && ListView_CheckBoxes(plv))
  6810. {
  6811. ListView_HandleStateIconClick(plv, iItem);
  6812. }
  6813. }
  6814. if (!IsWindow(hwnd))
  6815. return;
  6816. goto EndButtonDown;
  6817. }
  6818. if (ht.flags & (LVHT_ONITEMLABEL | LVHT_ONITEMICON))
  6819. {
  6820. // if it wasn't selected, we're about to select it... play
  6821. // a little ditty for us...
  6822. CCPlaySound(c_szSelect);
  6823. if (!RIGHTBUTTON(keyFlags) || (!CONTROL_DOWN(keyFlags) && !SHIFT_DOWN(keyFlags)))
  6824. ListView_ButtonSelect(plv, iItem, keyFlags, bSelected);
  6825. // handle full row select
  6826. // If single-select listview, disable marquee selection.
  6827. //
  6828. // Careful - CheckForDragBegin yields and the app may have
  6829. // destroyed the item we were thinking about dragging!
  6830. //
  6831. if (!bMouseWheel && CheckForDragBegin(plv->ci.hwnd, x, y))
  6832. {
  6833. // should we do a marquee?
  6834. if (ListView_RBeginMarquee(plv, x, y, &ht) &&
  6835. !CCSendNotify(&plv->ci, LVN_MARQUEEBEGIN, &nm.hdr))
  6836. {
  6837. ListView_DragSelect(plv, x, y);
  6838. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6839. }
  6840. else
  6841. {
  6842. // Before we start dragging, make it sure that it is
  6843. // selected and has the focus.
  6844. ListView_SetFocusSel(plv, iItem, TRUE, FALSE, FALSE);
  6845. if (!SHIFT_DOWN(keyFlags))
  6846. plv->iMark = iItem;
  6847. // Then, we need to update the window before start dragging
  6848. // to show the selection chagne.
  6849. UpdateWindow(plv->ci.hwnd);
  6850. // Remember which item we're dragging, as it affects ListView_OnInsertMarkHitTest
  6851. plv->iDrag = iItem;
  6852. CCSendNotify(&plv->ci, drag, &nm.hdr);
  6853. plv->iDrag = -1;
  6854. goto EndButtonDown;
  6855. }
  6856. }
  6857. // CheckForDragBegin yields, so revalidate before continuing
  6858. else if (IsWindow(hwnd))
  6859. {
  6860. // button came up and we are not dragging
  6861. if (!RIGHTBUTTON(keyFlags))
  6862. {
  6863. if (CONTROL_DOWN(keyFlags))
  6864. {
  6865. // do this on the button up so that ctrl-dragging a range
  6866. // won't toggle the select.
  6867. if (SHIFT_DOWN(keyFlags))
  6868. ListView_SetFocusSel(plv, iItem, FALSE, FALSE, FALSE);
  6869. else
  6870. {
  6871. ListView_SetFocusSel(plv, iItem, TRUE, FALSE, TRUE);
  6872. }
  6873. }
  6874. }
  6875. if (!SHIFT_DOWN(keyFlags))
  6876. plv->iMark = iItem;
  6877. if (!ListView_SetFocus(plv->ci.hwnd)) // activate this window
  6878. return;
  6879. // now do the deselect stuff
  6880. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags) && !RIGHTBUTTON(keyFlags))
  6881. {
  6882. ListView_DeselectAll(plv, iItem);
  6883. if ((ht.flags & LVHT_ONITEMLABEL) && bSelected &&
  6884. !(plv->exStyle & (LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE)))
  6885. {
  6886. // doing this check for ownerdrawfixed is for compatability.
  6887. // we don't want to go into edit mode if the user just happened to click
  6888. // to this window when a different one had focus,
  6889. // but ms hammer relied upon the notification being sent (and we
  6890. // don't go into edit mode anyways for ownerdraw)
  6891. if (fHadFocus ||
  6892. (plv->ci.style & LVS_OWNERDRAWFIXED))
  6893. {
  6894. // Click on item label. It was selected and
  6895. // no modifier keys were pressed and no drag operation
  6896. // So setup for name edit mode. Still need to wait
  6897. // to make sure user is not doing double click.
  6898. //
  6899. ListView_SetupPendingNameEdit(plv);
  6900. }
  6901. }
  6902. }
  6903. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6904. if (!IsWindow(hwnd))
  6905. return;
  6906. if (plv->exStyle & (LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE))
  6907. {
  6908. if (!RIGHTBUTTON(keyFlags))
  6909. {
  6910. // We don't ItemActivate within one double-click time of creating
  6911. // this listview. This is a common occurence for people used to
  6912. // double-clicking. The first click pops up a new window which
  6913. // receives the second click and ItemActivates the item...
  6914. //
  6915. if ((plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK) || bSelected)
  6916. {
  6917. if (fActive)
  6918. {
  6919. // condition: if we're in a single click activate mode
  6920. // don't launch if control or shift keys are pressed
  6921. BOOL bCond = plv->exStyle & LVS_EX_ONECLICKACTIVATE && !CONTROL_DOWN(keyFlags) && !SHIFT_DOWN(keyFlags);
  6922. if ((bSelected && plv->exStyle & LVS_EX_TWOCLICKACTIVATE) ||
  6923. (bCond && !g_bUseDblClickTimer))
  6924. {
  6925. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &nm.hdr);
  6926. if (!IsWindow(hwnd))
  6927. return;
  6928. }
  6929. else if (bCond && g_bUseDblClickTimer)
  6930. {
  6931. plv->fOneClickHappened = TRUE;
  6932. plv->nmOneClickHappened = nm;
  6933. SetTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED, GetDoubleClickTime(), NULL);
  6934. }
  6935. }
  6936. }
  6937. }
  6938. }
  6939. }
  6940. else
  6941. {
  6942. // IsWindow() failed. Bail.
  6943. return;
  6944. }
  6945. }
  6946. else if (ht.flags & LVHT_ONITEMSTATEICON)
  6947. {
  6948. // Should activate window and send notificiation to parent...
  6949. if (!ListView_SetFocus(plv->ci.hwnd)) // activate this window
  6950. return;
  6951. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6952. if (fNotifyReturn && ListView_CheckBoxes(plv))
  6953. {
  6954. ListView_HandleStateIconClick(plv, iItem);
  6955. }
  6956. }
  6957. else if (ht.flags & LVHT_NOWHERE)
  6958. {
  6959. if (!ListView_SetFocus(plv->ci.hwnd)) // activate this window
  6960. return;
  6961. // If single-select listview, disable marquee selection.
  6962. if (!(plv->ci.style & LVS_SINGLESEL) && CheckForDragBegin(plv->ci.hwnd, x, y) &&
  6963. !CCSendNotify(&plv->ci, LVN_MARQUEEBEGIN, &nm.hdr))
  6964. {
  6965. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags))
  6966. ListView_DeselectAll(plv, -1);
  6967. ListView_DragSelect(plv, x, y);
  6968. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6969. }
  6970. else if (IsWindow(hwnd))
  6971. {
  6972. // if we didn't have focus and aren't showing selection always,
  6973. // make the first click just set focus
  6974. BOOL fDoFirstClickSelection = (fHadFocus || plv->ci.style & LVS_SHOWSELALWAYS ||
  6975. CONTROL_DOWN(keyFlags) || SHIFT_DOWN(keyFlags) ||
  6976. RIGHTBUTTON(keyFlags));
  6977. if (fDoFirstClickSelection && fActive)
  6978. {
  6979. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags))
  6980. ListView_DeselectAll(plv, -1);
  6981. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6982. }
  6983. }
  6984. else
  6985. {
  6986. // IsWindow() failed. Bail.
  6987. return;
  6988. }
  6989. }
  6990. // re-check the key state so we don't get confused by multiple clicks
  6991. // this needs to check the GetKeyState stuff only when we've gone into
  6992. // a modal loop waiting for the rbutton up.
  6993. if (fNotifyReturn && (click == NM_RCLICK)) // && (GetKeyState(VK_RBUTTON)>=0))
  6994. {
  6995. POINT pt = { x, y };
  6996. ClientToScreen(plv->ci.hwnd, &pt);
  6997. FORWARD_WM_CONTEXTMENU(plv->ci.hwnd, plv->ci.hwnd, pt.x, pt.y, SendMessage);
  6998. }
  6999. EndButtonDown:
  7000. if (IsWindow(hwnd))
  7001. plv->fButtonDown = FALSE;
  7002. }
  7003. void ListView_OnButtonDown(LV* plv, BOOL fDoubleClick, int x, int y, UINT keyFlags)
  7004. {
  7005. ListView_HandleMouse(plv, fDoubleClick, x, y, keyFlags, FALSE);
  7006. }
  7007. BOOL ListView_CancelPendingTimer(LV* plv, UINT fFlags, int idTimer)
  7008. {
  7009. if (plv->flags & fFlags)
  7010. {
  7011. KillTimer(plv->ci.hwnd, idTimer);
  7012. plv->flags &= ~fFlags;
  7013. return TRUE;
  7014. }
  7015. return FALSE;
  7016. }
  7017. //
  7018. // ListView_OnTimer:
  7019. // process the WM_TIMER message. If the timer id is thta
  7020. // of the name editing, we should then start the name editing mode.
  7021. //
  7022. void ListView_OnTimer(LV* plv, UINT id)
  7023. {
  7024. KillTimer(plv->ci.hwnd, id);
  7025. if (id == IDT_NAMEEDIT)
  7026. {
  7027. // Kill the timer as we wont need any more messages from it.
  7028. if (ListView_CancelPendingEdit(plv))
  7029. {
  7030. // And start name editing mode.
  7031. if (!ListView_OnEditLabel(plv, plv->iFocus, NULL))
  7032. {
  7033. ListView_DismissEdit(plv, FALSE);
  7034. ListView_SetFocusSel(plv, plv->iFocus, TRUE, TRUE, FALSE);
  7035. }
  7036. }
  7037. }
  7038. else if (id == IDT_SCROLLWAIT)
  7039. {
  7040. if (ListView_CancelScrollWait(plv))
  7041. {
  7042. ListView_OnEnsureVisible(plv, plv->iFocus, TRUE);
  7043. }
  7044. }
  7045. else if (id == IDT_ONECLICKOK)
  7046. {
  7047. plv->fOneClickOK = TRUE;
  7048. }
  7049. else if (id == IDT_ONECLICKHAPPENED)
  7050. {
  7051. //if (!g_bUseDblClickTimer)
  7052. //{
  7053. //// EnableWindow(plv->ci.hwnd, TRUE);
  7054. // SetWindowBits(plv->ci.hwnd, GWL_STYLE, WS_DISABLED, 0);
  7055. // plv->fOneClickHappened = FALSE;
  7056. //}
  7057. // check the bit just in case they double-clicked
  7058. //else
  7059. if (plv->fOneClickHappened)
  7060. {
  7061. plv->fOneClickHappened = FALSE;
  7062. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  7063. }
  7064. }
  7065. else if (id == IDT_TRACKINGTIP)
  7066. {
  7067. // Display keyboard nav tracking tooltip popups
  7068. if (ListView_IsKbdTipTracking(plv)) // Item requires tracking popup
  7069. {
  7070. // Ensure index is still valid
  7071. if (ListView_IsValidItemNumber(plv, plv->iTracking))
  7072. {
  7073. TOOLINFO ti = {0};
  7074. ti.cbSize = sizeof(TOOLINFO);
  7075. ti.hwnd = plv->ci.hwnd;
  7076. // Cancel previous
  7077. SendMessage(plv->hwndToolTips, TTM_TRACKACTIVATE, FALSE, (LPARAM)&ti);
  7078. // Switch ListView's tooltip window to "tracking" (manual) mode
  7079. SendMessage(plv->hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti);
  7080. ti.uFlags |= TTF_TRACK;
  7081. SendMessage(plv->hwndToolTips, TTM_SETTOOLINFO, 0, (LPARAM)&ti);
  7082. // Activate and establish size
  7083. SendMessage(plv->hwndToolTips, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
  7084. }
  7085. else
  7086. {
  7087. // Index was invalid (ListView set of items changed), tip track cancel, no popup
  7088. plv->iTracking = LVKTT_NOTRACK;
  7089. }
  7090. }
  7091. }
  7092. }
  7093. //
  7094. // ListView_SetupPendingNameEdit:
  7095. // Sets up a timer to begin name editing at a delayed time. This
  7096. // will allow the user to double click on the already selected item
  7097. // without going into name editing mode, which is especially important
  7098. // in those views that only show a small icon.
  7099. //
  7100. void ListView_SetupPendingNameEdit(LV* plv)
  7101. {
  7102. SetTimer(plv->ci.hwnd, IDT_NAMEEDIT, GetDoubleClickTime(), NULL);
  7103. plv->flags |= LVF_NMEDITPEND;
  7104. }
  7105. void ListView_OnHVScroll(LV* plv, UINT code, int pos, int sb)
  7106. {
  7107. int iScrollCount = 0;
  7108. SCROLLINFO si;
  7109. si.cbSize = sizeof(SCROLLINFO);
  7110. si.fMask = SIF_TRACKPOS;
  7111. // if we're in 32bits, don't trust the pos since it's only 16bit's worth
  7112. if (ListView_GetScrollInfo(plv, sb, &si))
  7113. pos = (int)si.nTrackPos;
  7114. ListView_DismissEdit(plv, FALSE);
  7115. _ListView_OnScroll(plv, code, pos, sb);
  7116. switch (code)
  7117. {
  7118. case SB_PAGELEFT:
  7119. case SB_PAGERIGHT:
  7120. if (plv->iScrollCount < SMOOTHSCROLLLIMIT)
  7121. plv->iScrollCount += 3;
  7122. break;
  7123. case SB_LINELEFT:
  7124. case SB_LINERIGHT:
  7125. if (plv->iScrollCount < SMOOTHSCROLLLIMIT)
  7126. plv->iScrollCount++;
  7127. break;
  7128. case SB_ENDSCROLL:
  7129. plv->iScrollCount = 0;
  7130. break;
  7131. }
  7132. }
  7133. void ListView_OnVScroll(LV* plv, HWND hwndCtl, UINT code, int pos)
  7134. {
  7135. ListView_OnHVScroll(plv, code, pos, SB_VERT);
  7136. }
  7137. void ListView_OnHScroll(LV* plv, HWND hwndCtl, UINT code, int pos)
  7138. {
  7139. ListView_OnHVScroll(plv, code, pos, SB_HORZ);
  7140. }
  7141. int ListView_ValidateOneScrollParam(LV* plv, int iDirection, int dx)
  7142. {
  7143. SCROLLINFO si;
  7144. si.cbSize = sizeof(SCROLLINFO);
  7145. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  7146. if (!ListView_GetScrollInfo(plv, iDirection, &si))
  7147. return 0;
  7148. if (si.nPage)
  7149. si.nMax -= (si.nPage - 1);
  7150. si.nPos += dx;
  7151. if (si.nPos < si.nMin)
  7152. {
  7153. dx += (int)(si.nMin - si.nPos);
  7154. }
  7155. else if (si.nPos > si.nMax)
  7156. {
  7157. dx -= (int)(si.nPos - si.nMax);
  7158. }
  7159. return dx;
  7160. }
  7161. BOOL ListView_ValidateScrollParams(LV* plv, int * pdx, int *pdy)
  7162. {
  7163. int dx = *pdx;
  7164. int dy = *pdy;
  7165. if (plv->ci.style & LVS_NOSCROLL)
  7166. return FALSE;
  7167. if (ListView_IsListView(plv))
  7168. {
  7169. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  7170. #ifdef COLUMN_VIEW
  7171. if (dx < 0)
  7172. dx = (dx - (plv->cxItem - 1)) / plv->cxItem;
  7173. else
  7174. dx = (dx + (plv->cxItem - 1)) / plv->cxItem;
  7175. if (dy)
  7176. return FALSE;
  7177. #else
  7178. if (dy < 0)
  7179. dy = (dy - (plv->cyItem - 1)) / plv->cyItem;
  7180. else
  7181. dy = (dy + (plv->cyItem - 1)) / plv->cyItem;
  7182. if (dx)
  7183. return FALSE;
  7184. #endif
  7185. }
  7186. else if (ListView_IsReportView(plv))
  7187. {
  7188. //
  7189. // Note: This function expects that dy is in number of lines
  7190. // and we are working with pixels so do a conversion use some
  7191. // rounding up and down to make it right
  7192. if (dy > 0)
  7193. dy = (dy + plv->cyItem/2) / plv->cyItem;
  7194. else
  7195. dy = (dy - plv->cyItem/2) / plv->cyItem;
  7196. }
  7197. if (dy)
  7198. {
  7199. dy = ListView_ValidateOneScrollParam(plv, SB_VERT, dy);
  7200. if (ListView_IsReportView(plv)
  7201. #ifndef COLUMN_VIEW
  7202. || ListView_IsListView(plv)
  7203. #endif
  7204. )
  7205. {
  7206. // convert back to pixels
  7207. dy *= plv->cyItem;
  7208. }
  7209. *pdy = dy;
  7210. }
  7211. if (dx)
  7212. {
  7213. dx = ListView_ValidateOneScrollParam(plv, SB_HORZ, dx);
  7214. #ifdef COLUMN_VIEW
  7215. if (ListView_IsListView(plv))
  7216. {
  7217. dx *= plv->cxItem;
  7218. }
  7219. #endif
  7220. *pdx = dx;
  7221. }
  7222. return TRUE;
  7223. }
  7224. BOOL ListView_SendScrollNotify(LV* plv, BOOL fBegin, int dx, int dy)
  7225. {
  7226. NMLVSCROLL nm;
  7227. nm.dx = dx;
  7228. nm.dy = dy;
  7229. return !CCSendNotify(&plv->ci, fBegin ? LVN_BEGINSCROLL : LVN_ENDSCROLL, &nm.hdr);
  7230. }
  7231. BOOL ListView_OnScrollSelectSmooth(LV* plv, int dx, int dy, UINT uSmooth)
  7232. {
  7233. if (plv->ci.style & LVS_NOSCROLL)
  7234. return FALSE;
  7235. #ifdef DEBUG
  7236. // If we try and scroll an illegal amount then ListView_IScroll2_SmoothScroll
  7237. // will offset ptOrigin incorrectly (it doesn't check min/max range) which then
  7238. // mucks up hit testing and insert marks
  7239. if (ListView_IsIScrollView(plv))
  7240. {
  7241. int dxTmp = dx;
  7242. int dyTmp = dy;
  7243. ASSERT(ListView_ValidateScrollParams(plv, &dxTmp, &dyTmp) &&
  7244. dxTmp == dx && dyTmp == dy);
  7245. }
  7246. #endif
  7247. if (ListView_IsListView(plv))
  7248. {
  7249. // Scale pixel count to column count
  7250. //
  7251. #ifdef COLUMN_VIEW
  7252. if (dx < 0)
  7253. dx -= plv->cxItem - 1;
  7254. else
  7255. dx += plv->cxItem - 1;
  7256. dx = dx / plv->cxItem;
  7257. if (dy)
  7258. return FALSE;
  7259. #else
  7260. if (dy < 0)
  7261. dy -= plv->cyItem - 1;
  7262. else
  7263. dy += plv->cyItem - 1;
  7264. dy = dy / plv->cyItem;
  7265. if (dx)
  7266. return FALSE;
  7267. #endif
  7268. }
  7269. else if (ListView_IsReportView(plv) && !plv->fGroupView)
  7270. {
  7271. //
  7272. // Note: This function expects that dy is in number of lines
  7273. // and we are working with pixels so do a conversion use some
  7274. // rounding up and down to make it right
  7275. if (dy > 0)
  7276. dy = (dy + plv->cyItem/2) / plv->cyItem;
  7277. else
  7278. dy = (dy - plv->cyItem/2) / plv->cyItem;
  7279. }
  7280. ListView_SendScrollNotify(plv, TRUE, dx, dy);
  7281. _ListView_Scroll2(plv, dx, dy, uSmooth);
  7282. ListView_SendScrollNotify(plv, FALSE, dx, dy);
  7283. ListView_UpdateScrollBars(plv);
  7284. return TRUE;
  7285. }
  7286. BOOL ListView_OnScroll(LV* plv, int dx, int dy)
  7287. {
  7288. return ListView_OnScrollSelectSmooth(plv, dx, dy, 0);
  7289. }
  7290. #ifdef DEBUG
  7291. BOOL ListView_ValidatercView(LV* plv, RECT* prcView, BOOL fRecalcDone)
  7292. {
  7293. BOOL fRet = prcView->left != RECOMPUTE ? TRUE : !fRecalcDone;
  7294. // hitting this assert is only valuable if there's a manual repro, which never happens in stress
  7295. #ifdef FULL_DEBUG
  7296. if (!ListView_IsOwnerData(plv) && ListView_IsIScrollView(plv) && !(plv->fGroupView && plv->hdpaGroups) && ListView_RedrawEnabled(plv))
  7297. {
  7298. RECT rcViewTmp;
  7299. fRet = ListView_ICalcViewRect(plv, TRUE, &rcViewTmp);
  7300. if (fRet)
  7301. {
  7302. ASSERT(prcView->left != RECOMPUTE);
  7303. fRet = IsEqualRect(rcViewTmp, *prcView);
  7304. }
  7305. else
  7306. {
  7307. fRet = !fRecalcDone;
  7308. }
  7309. }
  7310. #endif
  7311. return fRet;
  7312. }
  7313. BOOL ListView_ValidateScrollPositions(LV* plv, RECT* prcClient)
  7314. {
  7315. BOOL fRet = TRUE;
  7316. // hitting this assert is only valuable if there's a manual repro, which never happens in stress
  7317. #ifdef FULL_DEBUG
  7318. // if we're in ListView_FixIScrollPositions, then it will fix up the scroll positions when we unwind
  7319. if (ListView_IsIScrollView(plv) && (!plv->fInFixIScrollPositions) && ListView_RedrawEnabled(plv))
  7320. {
  7321. if (!(plv->ci.style & LVS_NOSCROLL))
  7322. {
  7323. // if we don't have a client rect there's no way to validate anything, assume everything will be recomputed later
  7324. RECT rcClient;
  7325. if (!prcClient)
  7326. {
  7327. if (plv->rcView.left != RECOMPUTE)
  7328. {
  7329. ListView_GetStyleAndClientRectGivenViewRect(plv, &plv->rcView, &rcClient);
  7330. prcClient = &rcClient;
  7331. }
  7332. }
  7333. if (prcClient)
  7334. {
  7335. if (fRet)
  7336. {
  7337. if (RECTWIDTH(*prcClient) < RECTWIDTH(plv->rcView))
  7338. {
  7339. fRet = (plv->rcView.left <= plv->ptOrigin.x) && (plv->ptOrigin.x+RECTWIDTH(*prcClient) <= plv->rcView.right);
  7340. }
  7341. else
  7342. {
  7343. fRet = (plv->ptOrigin.x <= plv->rcView.left) && (plv->rcView.right <= plv->ptOrigin.x+RECTWIDTH(*prcClient));
  7344. }
  7345. }
  7346. if (fRet)
  7347. {
  7348. if (RECTHEIGHT(*prcClient) < RECTHEIGHT(plv->rcView))
  7349. {
  7350. fRet = (plv->rcView.top <= plv->ptOrigin.y) && (plv->ptOrigin.y+RECTHEIGHT(*prcClient) <= plv->rcView.bottom);
  7351. }
  7352. else
  7353. {
  7354. fRet = (plv->ptOrigin.y <= plv->rcView.top) && (plv->rcView.bottom <= plv->ptOrigin.y+RECTHEIGHT(*prcClient));
  7355. }
  7356. }
  7357. }
  7358. }
  7359. else
  7360. {
  7361. fRet = (plv->ptOrigin.x == 0) && (plv->ptOrigin.y == 0);
  7362. }
  7363. }
  7364. #endif
  7365. return fRet;
  7366. }
  7367. #endif
  7368. BOOL ListView_OnEnsureVisible(LV* plv, int i, BOOL fPartialOK)
  7369. {
  7370. RECT rcBounds;
  7371. RECT rc;
  7372. RECT rcClient;
  7373. int dx, dy;
  7374. if (!ListView_IsValidItemNumber(plv, i) || plv->ci.style & LVS_NOSCROLL)
  7375. return FALSE;
  7376. // we need to do this again inside because some callers don't do it.
  7377. // other callers that do this need to do it outside so that
  7378. // they can know not to call us if there's not wait pending
  7379. ListView_CancelScrollWait(plv);
  7380. if (ListView_IsReportView(plv))
  7381. return ListView_ROnEnsureVisible(plv, i, fPartialOK);
  7382. ListView_GetRects(plv, i, QUERY_DEFAULT, &rc, NULL, &rcBounds, NULL);
  7383. if (plv->fGroupView)
  7384. {
  7385. LISTITEM* pitem = ListView_GetItemPtr(plv, i);
  7386. if (pitem)
  7387. {
  7388. LISTGROUP* pgrp = ListView_FindFirstVisibleGroup(plv);
  7389. if (pitem->pGroup == pgrp && pgrp)
  7390. {
  7391. rcBounds.top -= LISTGROUP_HEIGHT(plv, pgrp);
  7392. }
  7393. }
  7394. }
  7395. if (!fPartialOK)
  7396. rc = rcBounds;
  7397. // Scrolling is done relative to this calculated rect, not the size of hwndListview (plv->sizeClient)
  7398. ListView_GetClientRect(plv, &rcClient, TRUE, NULL);
  7399. ASSERT(ListView_ValidateScrollPositions(plv, &rcClient));
  7400. // If any part of rc is outside of rcClient, then
  7401. // scroll so that all of rcBounds is visible.
  7402. //
  7403. dx = 0;
  7404. if (rc.left < 0 || (rc.right >= rcClient.right && rcClient.right != 0))
  7405. {
  7406. dx = rcBounds.left - 0;
  7407. if (dx >= 0)
  7408. {
  7409. dx = rcBounds.right - rcClient.right;
  7410. if (dx <= 0)
  7411. dx = 0;
  7412. else if ((rcBounds.left - dx) < 0)
  7413. dx = rcBounds.left - 0; // Not all fits...
  7414. }
  7415. }
  7416. dy = 0;
  7417. if (rc.top < 0 || (rc.bottom >= rcClient.bottom && rcClient.bottom != 0))
  7418. {
  7419. dy = rcBounds.top - 0;
  7420. if (dy >= 0)
  7421. {
  7422. dy = rcBounds.bottom - rcClient.bottom;
  7423. if (dy < 0)
  7424. dy = 0;
  7425. }
  7426. }
  7427. // if rcClient is 0 or 1 pixel in size, it is impossible to scroll it
  7428. if (dx | dy)
  7429. ListView_ValidateScrollParams(plv, &dx, &dy);
  7430. if (dx | dy)
  7431. return ListView_OnScrollSelectSmooth(plv, dx, dy, SSW_EX_IMMEDIATE);
  7432. return TRUE;
  7433. }
  7434. void ListView_UpdateScrollBars(LV* plv)
  7435. {
  7436. RECT rc;
  7437. DWORD dwStyle;
  7438. if ((plv->ci.style & LVS_NOSCROLL) ||
  7439. (!(ListView_RedrawEnabled(plv))))
  7440. return;
  7441. _ListView_UpdateScrollBars(plv);
  7442. GetClientRect(plv->ci.hwnd, &rc);
  7443. plv->sizeClient.cx = rc.right;
  7444. plv->sizeClient.cy = rc.bottom;
  7445. dwStyle = ListView_GetWindowStyle(plv);
  7446. plv->ci.style = (plv->ci.style & ~(WS_HSCROLL | WS_VSCROLL)) | (dwStyle & WS_HSCROLL | WS_VSCROLL);
  7447. }
  7448. #ifndef WINNT
  7449. #pragma optimize ("gle", off)
  7450. // Crappy hack for Sage, which passes unitialized memory to SetWindowPlacement.
  7451. // They used to get lucky and get zeros for the max position, but now they end
  7452. // up with non-zero stack trash that causes bad things to happen when sage is
  7453. // maximized. Thus, zero a bunch of stack to save their tail...
  7454. void ZeroSomeStackForSage()
  7455. {
  7456. BYTE aByte[1024];
  7457. memset(aByte, 0, sizeof(aByte));
  7458. aByte;
  7459. }
  7460. #pragma optimize ("", on)
  7461. #endif
  7462. void ListView_OnSetFont(LV* plv, HFONT hfont, BOOL fRedraw)
  7463. {
  7464. HDC hdc;
  7465. SIZE siz;
  7466. LOGFONT lf;
  7467. HFONT hfontPrev;
  7468. TEXTMETRIC tm;
  7469. if ((plv->flags & LVF_FONTCREATED) && plv->hfontLabel)
  7470. {
  7471. DeleteObject(plv->hfontLabel);
  7472. plv->flags &= ~LVF_FONTCREATED;
  7473. }
  7474. if (hfont == NULL)
  7475. {
  7476. SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
  7477. hfont = CreateFontIndirect(&lf);
  7478. plv->flags |= LVF_FONTCREATED;
  7479. }
  7480. hdc = GetDC(HWND_DESKTOP);
  7481. if (hdc)
  7482. {
  7483. hfontPrev = SelectFont(hdc, hfont);
  7484. GetTextMetrics(hdc, &tm);
  7485. plv->cyLabelChar = tm.tmHeight;
  7486. plv->cxLabelChar = tm.tmAveCharWidth; // Maybe this should tm.tmMaxCharWidth
  7487. GetTextExtentPoint(hdc, c_szEllipses, CCHELLIPSES, &siz);
  7488. plv->cxEllipses = siz.cx;
  7489. SelectFont(hdc, hfontPrev);
  7490. ReleaseDC(HWND_DESKTOP, hdc);
  7491. }
  7492. plv->hfontLabel = hfont;
  7493. if (plv->hfontLabel)
  7494. {
  7495. LOGFONT lf;
  7496. if (GetObject(plv->hfontLabel, sizeof(LOGFONT), &lf))
  7497. {
  7498. if (plv->hfontGroup)
  7499. DeleteObject(plv->hfontGroup);
  7500. CCAdjustForBold(&lf);
  7501. plv->hfontGroup = CreateFontIndirect(&lf);
  7502. }
  7503. }
  7504. plv->ci.uiCodePage = GetCodePageForFont(hfont);
  7505. ListView_InvalidateCachedLabelSizes(plv);
  7506. /* Ensure that our tooltip control uses the same font as the list view is using, therefore
  7507. / avoiding any nasty formatting problems. */
  7508. if (plv->hwndToolTips)
  7509. {
  7510. FORWARD_WM_SETFONT(plv->hwndToolTips, plv->hfontLabel, FALSE, SendMessage);
  7511. }
  7512. // If we have a header window, we need to forward this to it also
  7513. // as we have destroyed the hfont that they are using...
  7514. if (plv->hwndHdr)
  7515. {
  7516. FORWARD_WM_SETFONT(plv->hwndHdr, plv->hfontLabel, FALSE, SendMessage);
  7517. ListView_UpdateScrollBars(plv);
  7518. }
  7519. if (plv->hFontHot)
  7520. {
  7521. DeleteObject(plv->hFontHot);
  7522. plv->hFontHot = NULL;
  7523. }
  7524. CCGetHotFont(plv->hfontLabel, &plv->hFontHot);
  7525. plv->iFreeSlot = -1;
  7526. if (fRedraw)
  7527. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  7528. #ifndef WINNT
  7529. ZeroSomeStackForSage();
  7530. #endif
  7531. }
  7532. HFONT ListView_OnGetFont(LV* plv)
  7533. {
  7534. return plv->hfontLabel;
  7535. }
  7536. // This function process the WM_SETREDRAW message by setting or clearing
  7537. // a bit in the listview structure, which several places in the code will
  7538. // check...
  7539. //
  7540. // REVIEW: Should probably forward to DefWindowProc()
  7541. //
  7542. void ListView_OnSetRedraw(LV* plv, BOOL fRedraw)
  7543. {
  7544. if (fRedraw)
  7545. {
  7546. BOOL fChanges = FALSE;
  7547. // Only do work if we're turning redraw back on...
  7548. //
  7549. if (!(plv->flags & LVF_REDRAW))
  7550. {
  7551. plv->flags |= LVF_REDRAW;
  7552. // deal with any accumulated invalid regions
  7553. if (plv->hrgnInval)
  7554. {
  7555. UINT fRedraw = (plv->flags & LVF_ERASE) ? RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW : RDW_UPDATENOW|RDW_INVALIDATE;
  7556. if (plv->hrgnInval == (HRGN)ENTIRE_REGION)
  7557. plv->hrgnInval = NULL;
  7558. RedrawWindow(plv->ci.hwnd, NULL, plv->hrgnInval, fRedraw);
  7559. ListView_DeleteHrgnInval(plv);
  7560. fChanges = TRUE;
  7561. }
  7562. plv->flags &= ~LVF_ERASE;
  7563. // Turning redraw on recomputes listview.
  7564. if (plv->fGroupView)
  7565. {
  7566. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  7567. }
  7568. if (plv->fGroupView || ListView_IsDoubleBuffer(plv))
  7569. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  7570. // now deal with the optimized stuff
  7571. if (ListView_IsListView(plv) ||
  7572. ListView_IsReportView(plv))
  7573. {
  7574. if (plv->iFirstChangedNoRedraw != -1)
  7575. {
  7576. // We may try to resize the column
  7577. if (!ListView_MaybeResizeListColumns(plv, plv->iFirstChangedNoRedraw,
  7578. ListView_Count(plv)-1))
  7579. ListView_OnUpdate(plv, plv->iFirstChangedNoRedraw);
  7580. }
  7581. else
  7582. ListView_UpdateScrollBars(plv);
  7583. }
  7584. else
  7585. {
  7586. int iCount;
  7587. if (plv->iFirstChangedNoRedraw != -1)
  7588. {
  7589. for (iCount = ListView_Count(plv) ; plv->iFirstChangedNoRedraw < iCount; plv->iFirstChangedNoRedraw++)
  7590. {
  7591. ListView_InvalidateItem(plv, plv->iFirstChangedNoRedraw, FALSE, RDW_INVALIDATE);
  7592. }
  7593. fChanges = TRUE;
  7594. }
  7595. if (fChanges)
  7596. {
  7597. ListView_RecalcRegion(plv, TRUE, TRUE);
  7598. }
  7599. if (((plv->ci.style & LVS_AUTOARRANGE) ||(plv->exStyle & LVS_EX_SNAPTOGRID)) && fChanges)
  7600. {
  7601. ListView_OnUpdate(plv, plv->iFirstChangedNoRedraw);
  7602. }
  7603. else
  7604. {
  7605. ListView_UpdateScrollBars(plv);
  7606. }
  7607. }
  7608. }
  7609. }
  7610. else
  7611. {
  7612. plv->iFirstChangedNoRedraw = -1;
  7613. plv->flags &= ~LVF_REDRAW;
  7614. }
  7615. }
  7616. HIMAGELIST ListView_OnGetImageList(LV* plv, int iImageList)
  7617. {
  7618. switch (iImageList)
  7619. {
  7620. case LVSIL_NORMAL:
  7621. return plv->himl;
  7622. case LVSIL_SMALL:
  7623. return plv->himlSmall;
  7624. case LVSIL_STATE:
  7625. return plv->himlState;
  7626. }
  7627. RIPMSG(0, "ListView_GetImageList: Invalid Imagelist asked for");
  7628. return NULL;
  7629. }
  7630. HIMAGELIST ListView_OnSetImageList(LV* plv, HIMAGELIST himl, int iImageList)
  7631. {
  7632. HIMAGELIST hImageOld = NULL;
  7633. BOOL fImageSizeChanged = FALSE; //Assume the size hasn't changed!
  7634. switch (iImageList)
  7635. {
  7636. case LVSIL_NORMAL:
  7637. hImageOld = plv->himl;
  7638. plv->himl = himl;
  7639. if (himl)
  7640. {
  7641. int cxIconNew, cyIconNew;
  7642. //Get the Icon sizes from the new image list.
  7643. if (CCGetIconSize(&plv->ci, himl, &cxIconNew , &cyIconNew))
  7644. {
  7645. //Check to see if the sizes have changed!
  7646. if((cxIconNew != plv->cxIcon) || (cyIconNew != plv->cyIcon))
  7647. {
  7648. fImageSizeChanged = TRUE;
  7649. plv->cxIcon = cxIconNew;
  7650. plv->cyIcon = cyIconNew;
  7651. }
  7652. }
  7653. if (fImageSizeChanged && (!(plv->flags & LVF_ICONSPACESET)))
  7654. {
  7655. ListView_OnSetIconSpacing(plv, (LPARAM)-1);
  7656. }
  7657. }
  7658. break;
  7659. case LVSIL_SMALL:
  7660. hImageOld = plv->himlSmall;
  7661. plv->himlSmall = himl;
  7662. if (himl)
  7663. {
  7664. int cxSmIconNew, cySmIconNew;
  7665. //Get the small icon sizes from the new image list.
  7666. if(CCGetIconSize(&plv->ci, himl, &cxSmIconNew , &cySmIconNew))
  7667. {
  7668. //Check to see if the sizes have changed!
  7669. if((cxSmIconNew != plv->cxSmIcon) || (cySmIconNew != plv->cySmIcon))
  7670. {
  7671. fImageSizeChanged = TRUE;
  7672. plv->cxSmIcon = cxSmIconNew;
  7673. plv->cySmIcon = cySmIconNew;
  7674. }
  7675. }
  7676. }
  7677. if (fImageSizeChanged)
  7678. {
  7679. plv->cxItem = ListView_ComputeCXItemSize(plv);
  7680. // After changing the imagelist, try to resize the columns, because we can't
  7681. // guess what the new size is going to be. Discovered by Thumbview
  7682. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  7683. plv->cyItem = ListView_ComputeCYItemSize(plv);
  7684. }
  7685. if (plv->hwndHdr)
  7686. SendMessage(plv->hwndHdr, HDM_SETIMAGELIST, 0, (LPARAM)himl);
  7687. break;
  7688. case LVSIL_STATE:
  7689. fImageSizeChanged = TRUE;
  7690. if (himl)
  7691. {
  7692. CCGetIconSize(&plv->ci, himl, &plv->cxState , &plv->cyState);
  7693. }
  7694. else
  7695. {
  7696. plv->cxState = 0;
  7697. }
  7698. hImageOld = plv->himlState;
  7699. plv->himlState = himl;
  7700. plv->cyItem = ListView_ComputeCYItemSize(plv);
  7701. break;
  7702. default:
  7703. fImageSizeChanged = TRUE;
  7704. TraceMsg(TF_LISTVIEW, "sh TR - LVM_SETIMAGELIST: unrecognized iImageList");
  7705. break;
  7706. }
  7707. if (himl && !(plv->ci.style & LVS_SHAREIMAGELISTS))
  7708. ImageList_SetBkColor(himl, plv->clrBk);
  7709. // Imagelist size changed... if we're in tileview, we need to recalculate the tilesize.
  7710. if (ListView_IsTileView(plv) && (iImageList == LVSIL_STATE || iImageList == LVSIL_NORMAL))
  7711. {
  7712. ListView_RecalcTileSize(plv);
  7713. }
  7714. if(fImageSizeChanged)
  7715. {
  7716. // Now, recompute!
  7717. plv->rcView.left = RECOMPUTE; // invalidate this up front to avoid the asserts - it'll get recalculated anyway
  7718. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  7719. }
  7720. if (ListView_Count(plv) > 0)
  7721. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  7722. return hImageOld;
  7723. }
  7724. #ifdef UNICODE
  7725. BOOL ListView_OnGetItemA(LV* plv, LV_ITEMA *plvi)
  7726. {
  7727. LPWSTR pszW = NULL;
  7728. LPSTR pszC = NULL;
  7729. BOOL fRet;
  7730. //HACK ALERT -- this code assumes that LV_ITEMA is exactly the same
  7731. // as LV_ITEMW except for the pointer to the string.
  7732. COMPILETIME_ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW))
  7733. if (!plvi)
  7734. return FALSE;
  7735. if ((plvi->mask & LVIF_TEXT) && (plvi->pszText != NULL))
  7736. {
  7737. pszC = plvi->pszText;
  7738. pszW = LocalAlloc(LMEM_FIXED, plvi->cchTextMax * sizeof(WCHAR));
  7739. if (pszW == NULL)
  7740. return FALSE;
  7741. plvi->pszText = (LPSTR)pszW;
  7742. }
  7743. fRet = ListView_OnGetItem(plv, (LV_ITEM *) plvi);
  7744. if (pszW)
  7745. {
  7746. if (plvi->pszText != LPSTR_TEXTCALLBACKA)
  7747. {
  7748. if (fRet && plvi->cchTextMax)
  7749. ConvertWToAN(plv->ci.uiCodePage, pszC, plvi->cchTextMax, (LPWSTR)plvi->pszText, -1);
  7750. plvi->pszText = pszC;
  7751. }
  7752. LocalFree(pszW);
  7753. }
  7754. return fRet;
  7755. }
  7756. #endif
  7757. BOOL ListView_OnGetItem(LV* plv, LV_ITEM* plvi)
  7758. {
  7759. UINT mask;
  7760. LISTITEM* pitem = NULL;
  7761. LV_DISPINFO nm;
  7762. if (!plvi)
  7763. {
  7764. RIPMSG(0, "LVM_GET(ITEM|ITEMTEXT): Invalid pitem = NULL");
  7765. return FALSE;
  7766. }
  7767. if (!ListView_IsValidItemNumber(plv, plvi->iItem))
  7768. {
  7769. #ifdef DEBUG
  7770. // owner data views (e.g. docfind) may change the number of items in listview
  7771. // while we are doing something, thus hitting this rip
  7772. if (!ListView_IsOwnerData(plv))
  7773. RIPMSG(0, "LVM_GET(ITEM|ITEMTEXT|ITEMSTATE): item=%d does not exist", plvi->iItem);
  7774. #endif
  7775. return FALSE;
  7776. }
  7777. nm.item.mask = 0;
  7778. mask = plvi->mask;
  7779. if (!ListView_IsOwnerData(plv))
  7780. {
  7781. // Standard listviews
  7782. pitem = ListView_FastGetItemPtr(plv, plvi->iItem);
  7783. ASSERT(pitem);
  7784. // Handle sub-item cases for report view
  7785. //
  7786. if (plvi->iSubItem != 0)
  7787. {
  7788. LISTSUBITEM lsi;
  7789. ListView_GetSubItem(plv, plvi->iItem, plvi->iSubItem, &lsi);
  7790. if (mask & LVIF_TEXT)
  7791. {
  7792. if (lsi.pszText != LPSTR_TEXTCALLBACK)
  7793. {
  7794. Str_GetPtr0(lsi.pszText, plvi->pszText, plvi->cchTextMax);
  7795. }
  7796. else
  7797. {
  7798. // if this is LVIF_NORECOMPUTE we will update pszText later
  7799. nm.item.mask |= LVIF_TEXT;
  7800. }
  7801. }
  7802. if ((mask & LVIF_IMAGE) && (plv->exStyle & LVS_EX_SUBITEMIMAGES))
  7803. {
  7804. plvi->iImage = lsi.iImage;
  7805. if (lsi.iImage == I_IMAGECALLBACK)
  7806. nm.item.mask |= LVIF_IMAGE;
  7807. }
  7808. if (mask & LVIF_STATE)
  7809. {
  7810. if (ListView_FullRowSelect(plv))
  7811. {
  7812. // if we're in full row select,
  7813. // the state bit for select and focus follows column 0.
  7814. lsi.state |= pitem->state & (LVIS_SELECTED | LVIS_FOCUSED | LVIS_DROPHILITED);
  7815. }
  7816. plvi->state = lsi.state & plvi->stateMask;
  7817. if (plv->stateCallbackMask)
  7818. {
  7819. nm.item.stateMask = (plvi->stateMask & plv->stateCallbackMask);
  7820. if (nm.item.stateMask)
  7821. {
  7822. nm.item.mask |= LVIF_STATE;
  7823. nm.item.state = 0;
  7824. }
  7825. }
  7826. }
  7827. }
  7828. else
  7829. {
  7830. if (mask & LVIF_TEXT)
  7831. {
  7832. if (pitem->pszText != LPSTR_TEXTCALLBACK)
  7833. {
  7834. Str_GetPtr0(pitem->pszText, plvi->pszText, plvi->cchTextMax);
  7835. }
  7836. else
  7837. {
  7838. // if this is LVIF_NORECOMPUTE we will update pszText later
  7839. nm.item.mask |= LVIF_TEXT;
  7840. }
  7841. }
  7842. if (mask & LVIF_IMAGE)
  7843. {
  7844. plvi->iImage = pitem->iImage;
  7845. if (pitem->iImage == I_IMAGECALLBACK)
  7846. nm.item.mask |= LVIF_IMAGE;
  7847. }
  7848. if (mask & LVIF_INDENT)
  7849. {
  7850. plvi->iIndent = pitem->iIndent;
  7851. if (pitem->iIndent == I_INDENTCALLBACK)
  7852. nm.item.mask |= LVIF_INDENT;
  7853. }
  7854. if (mask & LVIF_STATE)
  7855. {
  7856. plvi->state = (pitem->state & plvi->stateMask);
  7857. if (plv->stateCallbackMask)
  7858. {
  7859. nm.item.stateMask = (plvi->stateMask & plv->stateCallbackMask);
  7860. if (nm.item.stateMask)
  7861. {
  7862. nm.item.mask |= LVIF_STATE;
  7863. nm.item.state = 0;
  7864. }
  7865. }
  7866. }
  7867. if (mask & LVIF_GROUPID)
  7868. {
  7869. if (LISTITEM_HASGROUP(pitem))
  7870. {
  7871. plvi->iGroupId = pitem->pGroup->iGroupId;
  7872. }
  7873. else
  7874. {
  7875. nm.item.mask |= LVIF_GROUPID;
  7876. }
  7877. }
  7878. if (mask & LVIF_COLUMNS)
  7879. {
  7880. if ((plvi->puColumns == NULL) || (plvi->cColumns > CCMAX_TILE_COLUMNS))
  7881. {
  7882. return FALSE;
  7883. }
  7884. if (pitem->cColumns == I_COLUMNSCALLBACK)
  7885. {
  7886. nm.item.mask |= LVIF_COLUMNS;
  7887. }
  7888. else
  7889. {
  7890. plvi->cColumns = pitem->cColumns;
  7891. if (plvi->cColumns < pitem->cColumns)
  7892. {
  7893. // Not enough room to store the columns
  7894. return FALSE;
  7895. }
  7896. // Copy the array
  7897. if (plvi->puColumns && pitem->puColumns)
  7898. {
  7899. CopyMemory(plvi->puColumns, pitem->puColumns, plvi->cColumns * sizeof(UINT));
  7900. }
  7901. }
  7902. }
  7903. }
  7904. if (mask & LVIF_PARAM)
  7905. plvi->lParam = pitem->lParam;
  7906. }
  7907. else
  7908. {
  7909. // Complete call back for info...
  7910. // Handle sub-item cases for report view
  7911. //
  7912. if (plvi->iSubItem != 0)
  7913. {
  7914. // if there are no subitem images, don't query for them
  7915. if (!(plv->exStyle & LVS_EX_SUBITEMIMAGES))
  7916. mask &= ~LVIF_IMAGE;
  7917. // don't allow indent on the non-0th column
  7918. mask &= ~LVIF_INDENT;
  7919. }
  7920. if (mask & LVIF_PARAM)
  7921. plvi->lParam = 0L; // Dont have any to return now...
  7922. if (mask & LVIF_STATE)
  7923. {
  7924. plvi->state = 0;
  7925. if ((plvi->iSubItem == 0) || ListView_FullRowSelect(plv))
  7926. {
  7927. if (plvi->iItem == plv->iFocus)
  7928. plvi->state |= LVIS_FOCUSED;
  7929. if (plv->plvrangeSel->lpVtbl->IsSelected(plv->plvrangeSel, plvi->iItem) == S_OK)
  7930. plvi->state |= LVIS_SELECTED;
  7931. if (plv->plvrangeCut->lpVtbl->IsSelected(plv->plvrangeCut, plvi->iItem) == S_OK)
  7932. plvi->state |= LVIS_CUT;
  7933. if (plvi->iItem == plv->iDropHilite)
  7934. plvi->state |= LVIS_DROPHILITED;
  7935. plvi->state &= plvi->stateMask;
  7936. }
  7937. if (plv->stateCallbackMask)
  7938. {
  7939. nm.item.stateMask = (plvi->stateMask & plv->stateCallbackMask);
  7940. if (nm.item.stateMask)
  7941. {
  7942. nm.item.mask |= LVIF_STATE;
  7943. nm.item.state = 0;
  7944. }
  7945. }
  7946. }
  7947. if (mask & LVIF_COLUMNS)
  7948. {
  7949. nm.item.mask |= LVIF_COLUMNS;
  7950. }
  7951. nm.item.mask |= (mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_INDENT));
  7952. }
  7953. if (mask & LVIF_NORECOMPUTE)
  7954. {
  7955. if (nm.item.mask & LVIF_TEXT)
  7956. plvi->pszText = LPSTR_TEXTCALLBACK;
  7957. if (nm.item.mask & LVIF_COLUMNS)
  7958. plvi->cColumns = I_COLUMNSCALLBACK;
  7959. }
  7960. else if (nm.item.mask)
  7961. {
  7962. UINT rguColumns[CCMAX_TILE_COLUMNS];
  7963. nm.item.iItem = plvi->iItem;
  7964. nm.item.iSubItem = plvi->iSubItem;
  7965. if (ListView_IsOwnerData(plv))
  7966. nm.item.lParam = 0L;
  7967. else
  7968. nm.item.lParam = pitem->lParam;
  7969. // just in case LVIF_IMAGE is set and callback doesn't fill it in
  7970. // ... we'd rather have a -1 than whatever garbage is on the stack
  7971. nm.item.iImage = -1;
  7972. nm.item.iIndent = 0;
  7973. if (nm.item.mask & LVIF_TEXT)
  7974. {
  7975. RIPMSG(plvi->pszText != NULL, "LVM_GET(ITEM|ITEMTEXT) null string pointer");
  7976. if (plvi->pszText)
  7977. {
  7978. nm.item.pszText = plvi->pszText;
  7979. nm.item.cchTextMax = plvi->cchTextMax;
  7980. // Make sure the buffer is zero terminated...
  7981. if (nm.item.cchTextMax)
  7982. *nm.item.pszText = 0;
  7983. }
  7984. else
  7985. {
  7986. // Don't make caller smash null pointer
  7987. nm.item.mask &= ~LVIF_TEXT;
  7988. }
  7989. }
  7990. if (nm.item.mask & LVIF_COLUMNS)
  7991. {
  7992. nm.item.cColumns = plvi->cColumns;
  7993. nm.item.puColumns = rguColumns;
  7994. if (plvi->puColumns && plvi->cColumns && plvi->cColumns < ARRAYSIZE(rguColumns))
  7995. {
  7996. CopyMemory(rguColumns, plvi->puColumns, sizeof(UINT) * plvi->cColumns);
  7997. }
  7998. }
  7999. CCSendNotify(&plv->ci, LVN_GETDISPINFO, &nm.hdr);
  8000. // use nm.item.mask to give the app a chance to change values
  8001. if (nm.item.mask & LVIF_INDENT)
  8002. plvi->iIndent = nm.item.iIndent;
  8003. if (nm.item.mask & LVIF_GROUPID)
  8004. {
  8005. if (pitem)
  8006. {
  8007. if (nm.item.iGroupId == I_GROUPIDNONE)
  8008. {
  8009. ListView_RemoveItemFromItsGroup(plv, pitem);
  8010. LISTITEM_SETASKEDFORGROUP(pitem);
  8011. }
  8012. else
  8013. {
  8014. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, nm.item.iGroupId, NULL);
  8015. if (pgrp != pitem->pGroup)
  8016. {
  8017. ListView_RemoveItemFromItsGroup(plv, pitem);
  8018. pitem->pGroup = pgrp;
  8019. if (pgrp)
  8020. {
  8021. DPA_AppendPtr(pgrp->hdpa, pitem);
  8022. }
  8023. }
  8024. }
  8025. }
  8026. plvi->iGroupId = nm.item.iGroupId;
  8027. }
  8028. if (nm.item.mask & LVIF_STATE)
  8029. plvi->state ^= ((plvi->state ^ nm.item.state) & nm.item.stateMask);
  8030. if (nm.item.mask & LVIF_IMAGE)
  8031. plvi->iImage = nm.item.iImage;
  8032. if (nm.item.mask & LVIF_TEXT)
  8033. plvi->pszText = CCReturnDispInfoText(nm.item.pszText, plvi->pszText, plvi->cchTextMax);
  8034. if (nm.item.mask & LVIF_COLUMNS)
  8035. {
  8036. // Put the # of columns back in the LV_ITEM struct. Don't need to
  8037. // do anything with puColumns.
  8038. UINT cColumns = (nm.item.cColumns == I_COLUMNSCALLBACK) ? 0 : nm.item.cColumns;
  8039. UINT cColumnsToCopy = min(cColumns, plvi->cColumns);
  8040. // Copy rguColumns back into the thing we were passed.
  8041. CopyMemory(plvi->puColumns, rguColumns, sizeof(UINT) * cColumnsToCopy);
  8042. plvi->cColumns = cColumnsToCopy;
  8043. }
  8044. if (pitem && (nm.item.mask & LVIF_DI_SETITEM))
  8045. {
  8046. //
  8047. // The SendNotify above can set about a terrible series of events
  8048. // whereby asking for DISPINFO causes the shell to look around
  8049. // (call peekmessage) to see if its got a new async icon for the
  8050. // listview. This lets other messages be delivered, such as an
  8051. // UPDATEIMAGE of Index == -1 (if the user is changing icon sizing
  8052. // at the same time). This causes a re-enumeration of the desktop
  8053. // and hence this very listview is torn down and rebuilt while
  8054. // we're sitting here for the DISPINFO to finish. Thus, as a cheap
  8055. // and dirty solution, I check to see if the item I think I have
  8056. // is the same one I had when I made the notify, and if not, I
  8057. // bail. Don't blame me, I'm just cleaning up the mess.
  8058. if (pitem != ListView_GetItemPtr(plv, plvi->iItem))
  8059. {
  8060. return FALSE;
  8061. }
  8062. if (nm.item.iSubItem == 0)
  8063. {
  8064. //DebugMsg(TF_LISTVIEW, "SAVING ITEMS!");
  8065. if (nm.item.mask & LVIF_IMAGE)
  8066. pitem->iImage = (short) nm.item.iImage;
  8067. if (nm.item.mask & LVIF_INDENT)
  8068. pitem->iIndent = (short) nm.item.iIndent;
  8069. if (nm.item.mask & LVIF_TEXT)
  8070. if (nm.item.pszText)
  8071. {
  8072. Str_Set(&pitem->pszText, nm.item.pszText);
  8073. }
  8074. if (nm.item.mask & LVIF_STATE)
  8075. pitem->state ^= ((pitem->state ^ nm.item.state) & nm.item.stateMask);
  8076. if (nm.item.mask & LVIF_COLUMNS)
  8077. {
  8078. Tile_Set(&pitem->puColumns, &pitem->cColumns, nm.item.puColumns, nm.item.cColumns);
  8079. // Just did a Tile_Set - need to recompute.
  8080. ListView_SetSRecompute(pitem);
  8081. }
  8082. }
  8083. else
  8084. {
  8085. ListView_SetSubItem(plv, &nm.item);
  8086. }
  8087. }
  8088. }
  8089. return TRUE;
  8090. }
  8091. BOOL ListView_OnSetItemA(LV* plv, LV_ITEMA* plvi)
  8092. {
  8093. LPWSTR pszW = NULL;
  8094. LPSTR pszC = NULL;
  8095. BOOL fRet;
  8096. // Let ListView_OnSetItem() handle owner-data validation
  8097. //HACK ALERT -- this code assumes that LV_ITEMA is exactly the same
  8098. // as LV_ITEMW except for the pointer to the string.
  8099. COMPILETIME_ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW));
  8100. if (!plvi)
  8101. return FALSE;
  8102. if ((plvi->mask & LVIF_TEXT) && (plvi->pszText != NULL))
  8103. {
  8104. pszC = plvi->pszText;
  8105. pszW = ProduceWFromA(plv->ci.uiCodePage, pszC);
  8106. if (pszW == NULL)
  8107. return FALSE;
  8108. plvi->pszText = (LPSTR)pszW;
  8109. }
  8110. fRet = ListView_OnSetItem(plv, (const LV_ITEM*) plvi);
  8111. if (pszW != NULL)
  8112. {
  8113. plvi->pszText = pszC;
  8114. FreeProducedString(pszW);
  8115. }
  8116. return fRet;
  8117. }
  8118. void ListView_OffsetRect(LV* plv, RECT* prc)
  8119. {
  8120. if (ListView_IsReportView(plv))
  8121. {
  8122. OffsetRect(prc, -plv->ptlRptOrigin.x, -plv->ptlRptOrigin.y + plv->yTop);
  8123. }
  8124. else
  8125. {
  8126. OffsetRect(prc, -plv->ptOrigin.x, -plv->ptOrigin.y);
  8127. }
  8128. }
  8129. BOOL ListView_OnSetItem(LV* plv, const LV_ITEM* plvi)
  8130. {
  8131. LISTITEM* pitem = NULL;
  8132. UINT mask;
  8133. UINT maskChanged;
  8134. UINT rdwFlags=RDW_INVALIDATE;
  8135. int i;
  8136. UINT stateOld, stateNew;
  8137. BOOL fFocused = FALSE;
  8138. BOOL fSelected = FALSE;
  8139. BOOL fStateImageChanged = FALSE;
  8140. if (ListView_IsOwnerData(plv))
  8141. {
  8142. RIPMSG(0, "LVM_SETITEM: Invalid for owner-data listview");
  8143. return FALSE;
  8144. }
  8145. if (!plvi)
  8146. return FALSE;
  8147. RIPMSG(plvi->iSubItem >= 0, "ListView_OnSetItem: Invalid item index");
  8148. if (plv->himl && (plv->clrBk != ImageList_GetBkColor(plv->himl)))
  8149. rdwFlags |= RDW_ERASE;
  8150. mask = plvi->mask;
  8151. if (!mask)
  8152. return TRUE;
  8153. // If we're setting a subitem, handle it elsewhere...
  8154. //
  8155. if (plvi->iSubItem > 0)
  8156. return ListView_SetSubItem(plv, plvi);
  8157. i = plvi->iItem;
  8158. pitem = ListView_GetItemPtr(plv, i);
  8159. if (!pitem)
  8160. return FALSE;
  8161. //REVIEW: This is a BOGUS HACK, and should be fixed.
  8162. //This incorrectly calculates the old state (since we may
  8163. // have to send LVN_GETDISPINFO to get it).
  8164. //
  8165. stateOld = stateNew = 0;
  8166. if (mask & LVIF_STATE)
  8167. {
  8168. stateOld = pitem->state & plvi->stateMask;
  8169. stateNew = plvi->state & plvi->stateMask;
  8170. }
  8171. // Prevent multiple selections in a single-select listview.
  8172. if ((plv->ci.style & LVS_SINGLESEL) && (mask & LVIF_STATE) && (stateNew & LVIS_SELECTED))
  8173. {
  8174. ListView_DeselectAll(plv, i);
  8175. // Refresh the old state information
  8176. stateOld = pitem->state & plvi->stateMask;
  8177. }
  8178. if (!ListView_SendChange(plv, i, 0, LVN_ITEMCHANGING, stateOld, stateNew, mask, pitem->lParam))
  8179. return FALSE;
  8180. maskChanged = 0;
  8181. if (mask & LVIF_STATE)
  8182. {
  8183. UINT change = (pitem->state ^ plvi->state) & plvi->stateMask;
  8184. if (change)
  8185. {
  8186. pitem->state ^= change;
  8187. maskChanged |= LVIF_STATE;
  8188. // the selection state has changed.. update selected count
  8189. if (change & LVIS_SELECTED)
  8190. {
  8191. fSelected = TRUE;
  8192. if (pitem->state & LVIS_SELECTED)
  8193. {
  8194. plv->nSelected++;
  8195. }
  8196. else
  8197. {
  8198. if (plv->nSelected > 0)
  8199. plv->nSelected--;
  8200. }
  8201. }
  8202. // For some bits we can only invert the label area...
  8203. // fSelectOnlyChange = ((change & ~(LVIS_SELECTED | LVIS_FOCUSED | LVIS_DROPHILITED)) == 0);
  8204. // fEraseItem = ((change & ~(LVIS_SELECTED | LVIS_DROPHILITED)) != 0);
  8205. // try to steal focus from the previous guy.
  8206. if (change & LVIS_FOCUSED)
  8207. {
  8208. BOOL fUnfolded = ListView_IsItemUnfolded(plv, plv->iFocus);
  8209. int iOldFocus = plv->iFocus;
  8210. RECT rcLabel;
  8211. fFocused = TRUE;
  8212. if (plv->iFocus != i)
  8213. {
  8214. if ((plv->iFocus == -1) || ListView_OnSetItemState(plv, plv->iFocus, 0, LVIS_FOCUSED))
  8215. {
  8216. ASSERT(pitem->state & LVIS_FOCUSED);
  8217. plv->iFocus = i;
  8218. if (plv->iMark == -1)
  8219. plv->iMark = i;
  8220. }
  8221. else
  8222. {
  8223. fFocused = FALSE;
  8224. pitem->state &= ~LVIS_FOCUSED;
  8225. }
  8226. }
  8227. else
  8228. {
  8229. ASSERT(!(pitem->state & LVIS_FOCUSED));
  8230. plv->iFocus = -1;
  8231. }
  8232. // If we were previously unfolded and we move the focus we must
  8233. // attempt to refresh the previous focus owner to referect this change.
  8234. if (fUnfolded && !ListView_IsItemUnfolded(plv, iOldFocus) && (plv->iItemDrawing != iOldFocus))
  8235. {
  8236. ListView_GetUnfoldedRect(plv, iOldFocus, &rcLabel);
  8237. RedrawWindow(plv->ci.hwnd, &rcLabel, NULL, RDW_INVALIDATE|RDW_ERASE);
  8238. }
  8239. // Kill the tooltip if focus moves, it causes us headaches otherwise!
  8240. ListView_PopBubble(plv);
  8241. }
  8242. if (change & LVIS_CUT ||
  8243. plv->clrTextBk == CLR_NONE)
  8244. rdwFlags |= RDW_ERASE;
  8245. if (change & LVIS_OVERLAYMASK)
  8246. {
  8247. // Overlay changed, so need to blow away icon region cache
  8248. if (pitem->hrgnIcon)
  8249. {
  8250. if (pitem->hrgnIcon != (HANDLE) -1)
  8251. DeleteObject(pitem->hrgnIcon);
  8252. pitem->hrgnIcon = NULL;
  8253. }
  8254. }
  8255. fStateImageChanged = (change & LVIS_STATEIMAGEMASK);
  8256. }
  8257. }
  8258. if (mask & LVIF_TEXT)
  8259. {
  8260. // need to do this now because we're changing the text
  8261. // so we need to get the rect of the thing before the text changes
  8262. // but don't redraw the item we are currently painting
  8263. if (plv->iItemDrawing != i)
  8264. {
  8265. ListView_InvalidateItemEx(plv, i, FALSE,
  8266. RDW_INVALIDATE | RDW_ERASE, LVIF_TEXT);
  8267. }
  8268. if (!Str_Set(&pitem->pszText, plvi->pszText))
  8269. return FALSE;
  8270. plv->rcView.left = RECOMPUTE;
  8271. ListView_SetSRecompute(pitem);
  8272. maskChanged |= LVIF_TEXT;
  8273. }
  8274. if (mask & LVIF_INDENT)
  8275. {
  8276. if (pitem->iIndent != plvi->iIndent)
  8277. {
  8278. pitem->iIndent = (short) plvi->iIndent;
  8279. maskChanged |= LVIF_INDENT;
  8280. if (ListView_IsReportView(plv))
  8281. rdwFlags |= RDW_ERASE;
  8282. }
  8283. }
  8284. if (mask & LVIF_IMAGE)
  8285. {
  8286. if (pitem->iImage != plvi->iImage)
  8287. {
  8288. pitem->iImage = (short) plvi->iImage;
  8289. maskChanged |= LVIF_IMAGE;
  8290. if (pitem->hrgnIcon)
  8291. {
  8292. if (pitem->hrgnIcon != (HANDLE) -1)
  8293. DeleteObject(pitem->hrgnIcon);
  8294. pitem->hrgnIcon = NULL;
  8295. }
  8296. // erase if there was a set image
  8297. if (pitem->iImage != I_IMAGECALLBACK)
  8298. rdwFlags |= RDW_ERASE;
  8299. }
  8300. }
  8301. if (mask & LVIF_PARAM)
  8302. {
  8303. if (pitem->lParam != plvi->lParam)
  8304. {
  8305. pitem->lParam = plvi->lParam;
  8306. maskChanged |= LVIF_PARAM;
  8307. }
  8308. }
  8309. if (mask & LVIF_GROUPID)
  8310. {
  8311. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, plvi->iGroupId, NULL);
  8312. if (pgrp)
  8313. {
  8314. if (pitem->pGroup != pgrp)
  8315. {
  8316. ListView_RemoveItemFromItsGroup(plv, pitem);
  8317. pitem->pGroup = pgrp;
  8318. DPA_AppendPtr(pgrp->hdpa, pitem);
  8319. if (ListView_RedrawEnabled(plv))
  8320. {
  8321. _ListView_RecomputeEx(plv, NULL, 0, FALSE);
  8322. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  8323. }
  8324. maskChanged |= LVIF_GROUPID;
  8325. }
  8326. }
  8327. }
  8328. if (mask & LVIF_COLUMNS)
  8329. {
  8330. UINT uNumColumns = (plvi->cColumns == I_COLUMNSCALLBACK) ? 0 : plvi->cColumns;
  8331. if (((uNumColumns > 0) && (plvi->puColumns == NULL)) || // Didn't provide any columns
  8332. (uNumColumns > CCMAX_TILE_COLUMNS)) // Provided too many
  8333. {
  8334. return FALSE; // See note below about premature return.
  8335. }
  8336. if (!Tile_Set(&pitem->puColumns, &pitem->cColumns, plvi->puColumns, plvi->cColumns))
  8337. return FALSE;
  8338. // Note: if we fail here, we may have still set the LVIF_TEXT above...
  8339. // so the call partially succeeded. Oh well, no way to undo that.
  8340. maskChanged |= LVIF_COLUMNS;
  8341. // Columns changed - need to recompute this guy.
  8342. ListView_SetSRecompute(pitem);
  8343. }
  8344. if (maskChanged)
  8345. {
  8346. // don't redraw the item we are currently painting
  8347. if (plv->iItemDrawing != i)
  8348. ListView_InvalidateItemEx(plv, i, FALSE, rdwFlags, maskChanged);
  8349. TraceMsg(DM_LVSENDCHANGE, "LV - SendChange %d %d %d %d", i, stateOld, stateNew, maskChanged);
  8350. ListView_SendChange(plv, i, 0, LVN_ITEMCHANGED, stateOld, stateNew, maskChanged, pitem->lParam);
  8351. if (maskChanged & LVIF_TEXT)
  8352. NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, plv->ci.hwnd, OBJID_CLIENT, i+1);
  8353. if (maskChanged & LVIF_STATE)
  8354. {
  8355. if (fFocused)
  8356. ListView_NotifyFocusEvent(plv);
  8357. if (fSelected)
  8358. {
  8359. if (stateNew & LVIS_SELECTED)
  8360. {
  8361. NotifyWinEvent((plv->nSelected == 1) ? EVENT_OBJECT_SELECTION :
  8362. EVENT_OBJECT_SELECTIONADD, plv->ci.hwnd, OBJID_CLIENT, i+1);
  8363. }
  8364. else
  8365. {
  8366. NotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE, plv->ci.hwnd, OBJID_CLIENT, i+1);
  8367. }
  8368. }
  8369. if (fStateImageChanged)
  8370. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, plv->ci.hwnd, OBJID_CLIENT, i+1);
  8371. }
  8372. }
  8373. return TRUE;
  8374. }
  8375. UINT ListView_OnGetItemState(LV* plv, int i, UINT mask)
  8376. {
  8377. LV_ITEM lvi;
  8378. lvi.mask = LVIF_STATE;
  8379. lvi.stateMask = mask;
  8380. lvi.iItem = i;
  8381. lvi.iSubItem = 0;
  8382. if (!ListView_OnGetItem(plv, &lvi))
  8383. return 0;
  8384. return lvi.state;
  8385. }
  8386. BOOL ListView_OnSetItemState(LV* plv, int i, UINT data, UINT mask)
  8387. {
  8388. UINT rdwFlags = RDW_INVALIDATE;
  8389. LV_ITEM lvi;
  8390. lvi.mask = LVIF_STATE;
  8391. lvi.state = data;
  8392. lvi.stateMask = mask;
  8393. lvi.iItem = i;
  8394. lvi.iSubItem = 0;
  8395. // if the item is -1, we will do it for all items. We special case
  8396. // a few cases here as to speed it up. For example if the mask is
  8397. // LVIS_SELECTED and data is zero it implies that we will deselect
  8398. // all items...
  8399. //
  8400. if (ListView_IsOwnerData(plv))
  8401. {
  8402. UINT uOldData = 0;
  8403. // these are the only two we handled
  8404. mask &= (LVIS_SELECTED | LVIS_FOCUSED | LVIS_CUT | LVIS_DROPHILITED);
  8405. if (!mask)
  8406. return TRUE;
  8407. if (plv->clrTextBk == CLR_NONE ||
  8408. (plv->himl && (plv->clrBk != ImageList_GetBkColor(plv->himl))))
  8409. {
  8410. rdwFlags |= RDW_ERASE;
  8411. }
  8412. if (i == -1)
  8413. {
  8414. // request selection state change for all
  8415. if (mask & LVIS_SELECTED)
  8416. {
  8417. if (data & LVIS_SELECTED)
  8418. { // set selection
  8419. if ((plv->ci.style & LVS_SINGLESEL))
  8420. { // cant make multiple selections in a single-select listview.
  8421. return FALSE;
  8422. }
  8423. if (plv->cTotalItems)
  8424. {
  8425. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, 0, plv->cTotalItems - 1)))
  8426. return FALSE;
  8427. }
  8428. RedrawWindow(plv->ci.hwnd, NULL, NULL, rdwFlags);
  8429. }
  8430. else
  8431. { // clear selection
  8432. if (plv->nSelected > 0)
  8433. {
  8434. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeSel);
  8435. if (FAILED(plv->plvrangeSel->lpVtbl->Clear(plv->plvrangeSel)))
  8436. return FALSE;
  8437. }
  8438. else
  8439. {
  8440. // if nothing was selected, then there's nothing to clear
  8441. // no change.
  8442. mask &= ~ LVIS_SELECTED;
  8443. }
  8444. }
  8445. uOldData |= (LVIS_SELECTED & (mask ^ data));
  8446. // Update our internal count to what the list thinks is the number selected...
  8447. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  8448. }
  8449. // can maybe combine with above code...
  8450. if (mask & LVIS_CUT)
  8451. {
  8452. if (data & LVIS_CUT)
  8453. { // set selection
  8454. if (plv->cTotalItems)
  8455. if (FAILED(plv->plvrangeCut->lpVtbl->IncludeRange(plv->plvrangeCut, 0, plv->cTotalItems - 1)))
  8456. return FALSE;
  8457. RedrawWindow(plv->ci.hwnd, NULL, NULL, rdwFlags);
  8458. }
  8459. else
  8460. { // clear selection
  8461. if (plv->plvrangeCut->lpVtbl->IsEmpty(plv->plvrangeCut) != S_OK)
  8462. {
  8463. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeCut);
  8464. if (FAILED(plv->plvrangeCut->lpVtbl->Clear(plv->plvrangeCut)))
  8465. return FALSE;
  8466. }
  8467. else
  8468. {
  8469. // if nothing was selected, then there's nothing to clear
  8470. // no change.
  8471. mask &= ~ LVIS_CUT;
  8472. }
  8473. }
  8474. uOldData |= (LVIS_CUT & (mask ^ data));
  8475. }
  8476. // request focus state change
  8477. if (mask & LVIS_FOCUSED)
  8478. {
  8479. if (data & LVIS_FOCUSED)
  8480. { // cant set focus to all
  8481. return FALSE;
  8482. }
  8483. else if (plv->iFocus != -1)
  8484. {
  8485. int iOldFocus = plv->iFocus;
  8486. // clear focus
  8487. uOldData |= (LVIS_FOCUSED & (mask ^ data));
  8488. plv->iFocus = -1;
  8489. // notify that the old focus is being lost
  8490. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), iOldFocus, LVIS_FOCUSED, 0);
  8491. ListView_SendChange(plv, iOldFocus, 0, LVN_ITEMCHANGED, LVIS_FOCUSED, 0, LVIF_STATE, 0);
  8492. ListView_InvalidateFoldedItem(plv, iOldFocus, TRUE, RDW_INVALIDATE |RDW_ERASE);
  8493. }
  8494. }
  8495. if (mask & LVIS_DROPHILITED)
  8496. {
  8497. if (data & LVIS_DROPHILITED)
  8498. { // cant set focus to all
  8499. return FALSE;
  8500. }
  8501. else if (plv->iDropHilite != -1)
  8502. {
  8503. int iOldDropHilite = plv->iDropHilite;
  8504. // clear focus
  8505. uOldData |= (LVIS_FOCUSED & (mask ^ data));
  8506. plv->iDropHilite = -1;
  8507. // notify that the old focus is being lost
  8508. ListView_SendChange(plv, iOldDropHilite, 0, LVN_ITEMCHANGED, LVIS_DROPHILITED, 0, LVIF_STATE, 0);
  8509. ListView_InvalidateFoldedItem(plv, iOldDropHilite, TRUE, RDW_INVALIDATE |RDW_ERASE);
  8510. }
  8511. }
  8512. // invalidate and notify if there was a change
  8513. if (uOldData ^ (data & mask))
  8514. {
  8515. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), i, uOldData, data);
  8516. ListView_SendChange(plv, i, 0, LVN_ITEMCHANGED, uOldData, data, LVIF_STATE, 0);
  8517. if (mask & LVIS_SELECTED)
  8518. {
  8519. // Tell accessibility, "Selection changed in a complex way"
  8520. // (There is no "select all" or "select none" notification)
  8521. NotifyWinEvent(EVENT_OBJECT_SELECTIONWITHIN, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  8522. }
  8523. }
  8524. }
  8525. else
  8526. {
  8527. if (!ListView_IsValidItemNumber(plv, i))
  8528. return FALSE;
  8529. // request selection state change
  8530. // and the selection state is new...
  8531. if ((mask & LVIS_SELECTED))
  8532. {
  8533. if (((plv->plvrangeSel->lpVtbl->IsSelected(plv->plvrangeSel, i) == S_OK) ? LVIS_SELECTED : 0) ^ (data & LVIS_SELECTED))
  8534. {
  8535. if (data & LVIS_SELECTED)
  8536. { // set selection
  8537. if ((plv->ci.style & LVS_SINGLESEL))
  8538. {
  8539. // in single selection mode, we need to deselect everything else
  8540. if (!ListView_OnSetItemState(plv, -1, 0, LVIS_SELECTED))
  8541. return FALSE;
  8542. }
  8543. // now select the new item
  8544. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, i, i)))
  8545. return FALSE;
  8546. }
  8547. else
  8548. { // clear selection
  8549. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, i, i)))
  8550. return FALSE;
  8551. }
  8552. // something actually changed (or else we wouldn't be in this
  8553. // if block
  8554. uOldData |= (LVIS_SELECTED & (mask ^ data));
  8555. }
  8556. else
  8557. {
  8558. // nothing changed... so make the uOldData be the same for this bit
  8559. // else make this the same as
  8560. uOldData |= (LVIS_SELECTED & (mask & data));
  8561. }
  8562. // Update our internal count to what the list thinks is the number selected...
  8563. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  8564. }
  8565. if ((mask & LVIS_CUT))
  8566. {
  8567. if (((plv->plvrangeCut->lpVtbl->IsSelected(plv->plvrangeCut, i) == S_OK) ? LVIS_CUT : 0) ^ (data & LVIS_CUT))
  8568. {
  8569. if (data & LVIS_CUT)
  8570. {
  8571. // now select the new item
  8572. if (FAILED(plv->plvrangeCut->lpVtbl->IncludeRange(plv->plvrangeCut, i, i)))
  8573. return FALSE;
  8574. }
  8575. else
  8576. { // clear selection
  8577. if (FAILED(plv->plvrangeCut->lpVtbl->ExcludeRange(plv->plvrangeCut, i, i)))
  8578. return FALSE;
  8579. }
  8580. // something actually changed (or else we wouldn't be in this
  8581. // if block
  8582. uOldData |= (LVIS_CUT & (mask ^ data));
  8583. rdwFlags |= RDW_ERASE;
  8584. }
  8585. else
  8586. {
  8587. // nothing changed... so make the uOldData be the same for this bit
  8588. // else make this the same as
  8589. uOldData |= (LVIS_CUT & (mask & data));
  8590. }
  8591. }
  8592. // request focus state change
  8593. if (mask & LVIS_FOCUSED)
  8594. {
  8595. int iOldFocus = plv->iFocus;
  8596. if (data & LVIS_FOCUSED)
  8597. { // set focus
  8598. if (i != plv->iFocus)
  8599. {
  8600. // we didn't have the focus before
  8601. plv->iFocus = i;
  8602. if (plv->iMark == -1)
  8603. plv->iMark = i;
  8604. if (iOldFocus != -1)
  8605. {
  8606. // we're stealing it from someone
  8607. // notify of the change
  8608. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), iOldFocus, LVIS_FOCUSED, 0);
  8609. ListView_SendChange(plv, iOldFocus, 0, LVN_ITEMCHANGED, LVIS_FOCUSED, 0, LVIF_STATE, 0);
  8610. }
  8611. }
  8612. else
  8613. {
  8614. // we DID have the focus before
  8615. uOldData |= LVIS_FOCUSED;
  8616. }
  8617. }
  8618. else
  8619. { // clear focus
  8620. if (i == plv->iFocus)
  8621. {
  8622. plv->iFocus = -1;
  8623. uOldData |= LVIS_FOCUSED;
  8624. }
  8625. }
  8626. }
  8627. // request focus state change
  8628. if (mask & LVIS_DROPHILITED)
  8629. {
  8630. int iOldDropHilite = plv->iDropHilite;
  8631. if (data & LVIS_DROPHILITED)
  8632. { // set Drop Hilite
  8633. if (i != plv->iDropHilite)
  8634. {
  8635. // we didn't have the Drop Hilite before
  8636. plv->iDropHilite = i;
  8637. if (iOldDropHilite != -1)
  8638. {
  8639. // we're stealing it from someone
  8640. // notify of the change
  8641. ListView_SendChange(plv, iOldDropHilite, 0, LVN_ITEMCHANGED, LVIS_DROPHILITED, 0, LVIF_STATE, 0);
  8642. ListView_InvalidateFoldedItem(plv, iOldDropHilite, TRUE, RDW_INVALIDATE |RDW_ERASE);
  8643. }
  8644. }
  8645. else
  8646. {
  8647. // we DID have the Drop Hilite before
  8648. uOldData |= LVIS_DROPHILITED;
  8649. }
  8650. }
  8651. else
  8652. { // clear Drop Hilite
  8653. if (i == plv->iDropHilite)
  8654. {
  8655. plv->iDropHilite = -1;
  8656. uOldData |= LVIS_DROPHILITED;
  8657. }
  8658. }
  8659. }
  8660. // invalidate and notify if there was a change
  8661. if (uOldData ^ (data & mask))
  8662. {
  8663. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), i, uOldData, data);
  8664. ListView_SendChange(plv, i, 0, LVN_ITEMCHANGED, uOldData, data, LVIF_STATE, 0);
  8665. ListView_InvalidateItem(plv, i, TRUE, rdwFlags);
  8666. // Kill the tooltip if focus moves, it causes us headaches otherwise!
  8667. if ((uOldData ^ (data & mask)) & LVIS_FOCUSED)
  8668. {
  8669. ListView_PopBubble(plv);
  8670. ListView_NotifyFocusEvent(plv);
  8671. }
  8672. // Tell accessibility about the changes
  8673. if (mask & LVIS_SELECTED)
  8674. {
  8675. UINT event;
  8676. if (data & LVIS_SELECTED)
  8677. {
  8678. if (plv->nSelected == 1)
  8679. event = EVENT_OBJECT_SELECTION; // this object is the entire selection
  8680. else
  8681. event = EVENT_OBJECT_SELECTIONADD; // this object is selected
  8682. }
  8683. else
  8684. event = EVENT_OBJECT_SELECTIONREMOVE; // this object is unselected
  8685. NotifyWinEvent(event, plv->ci.hwnd, OBJID_CLIENT, i + 1);
  8686. }
  8687. }
  8688. }
  8689. }
  8690. else
  8691. {
  8692. if (i != -1)
  8693. {
  8694. return ListView_OnSetItem(plv, &lvi);
  8695. }
  8696. else
  8697. {
  8698. UINT flags = LVNI_ALL;
  8699. if (data == 0)
  8700. {
  8701. switch (mask)
  8702. {
  8703. case LVIS_SELECTED:
  8704. flags = LVNI_SELECTED;
  8705. break;
  8706. case LVIS_CUT:
  8707. flags = LVNI_CUT;
  8708. break;
  8709. }
  8710. }
  8711. else if ((plv->ci.style & LVS_SINGLESEL) && (mask == LVIS_SELECTED))
  8712. return FALSE; /* can't select all in single-select listview */
  8713. else if ((mask & data) & LVIS_FOCUSED)
  8714. {
  8715. return FALSE; // can't set focus to everything
  8716. }
  8717. //
  8718. // Now iterate over all of the items that match our criteria and
  8719. // set their new value.
  8720. //
  8721. while ((lvi.iItem = ListView_OnGetNextItem(plv, lvi.iItem, flags)) != -1)
  8722. {
  8723. ListView_OnSetItem(plv, &lvi);
  8724. }
  8725. }
  8726. }
  8727. return TRUE;
  8728. }
  8729. //
  8730. // Returns TRUE if the label of an item is not truncated (is unfolded) and FALSE
  8731. // otherwise. If FALSE is returned, it also fills the Unfolding text in pszText.
  8732. // If TRUE is returned, pszText is set to empty string.
  8733. //
  8734. BOOL ListView_IsItemUnfolded2(LV* plv, int iItem, int iSubItem, LPTSTR pszText, int cchTextMax)
  8735. {
  8736. BOOL bItemUnfolded = ListView_IsItemUnfolded(plv, iItem);
  8737. if (pszText && cchTextMax > 0) // Sanity checks on input params.
  8738. {
  8739. pszText[0] = 0;
  8740. if (!bItemUnfolded)
  8741. {
  8742. RECT rcLabel = {0};
  8743. LV_ITEM item;
  8744. item.iItem = iItem;
  8745. item.iSubItem = iSubItem;
  8746. item.mask = LVIF_TEXT | LVIF_PARAM;
  8747. if (ListView_IsTileView(plv))
  8748. {
  8749. TCalculateSubItemRect(plv, NULL, NULL, iItem, iSubItem, NULL, NULL, &bItemUnfolded);
  8750. if (!bItemUnfolded)
  8751. {
  8752. // Need to supply text.
  8753. item.pszText = pszText;
  8754. item.cchTextMax = cchTextMax;
  8755. ListView_OnGetItem(plv, &item);
  8756. }
  8757. }
  8758. else if (!ListView_IsIconView(plv))
  8759. {
  8760. if (ListView_IsLabelTip(plv) || ListView_IsInfoTip(plv))
  8761. {
  8762. BOOL fSuccess;
  8763. rcLabel.left = LVIR_LABEL;
  8764. if (iSubItem)
  8765. {
  8766. rcLabel.top = iSubItem;
  8767. fSuccess = ListView_OnGetSubItemRect(plv, iItem, &rcLabel);
  8768. }
  8769. else
  8770. {
  8771. fSuccess = ListView_OnGetItemRect(plv, iItem, &rcLabel);
  8772. }
  8773. if (fSuccess)
  8774. {
  8775. TCHAR szText[INFOTIPSIZE];
  8776. item.pszText = szText;
  8777. item.cchTextMax = min(ARRAYSIZE(szText), cchTextMax);
  8778. if (ListView_OnGetItem(plv, &item) && item.pszText != LPSTR_TEXTCALLBACK)
  8779. {
  8780. SIZE siz;
  8781. LVFAKEDRAW lvfd;
  8782. int cx;
  8783. HRESULT hr = E_FAIL;
  8784. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  8785. ListView_BeginFakeItemDraw(&lvfd);
  8786. // ---------Label width----------- ---Client width---
  8787. cx = min(rcLabel.right - g_cxLabelMargin, plv->sizeClient.cx);
  8788. hr = GetTextExtentPoint32(lvfd.nmcd.nmcd.hdc, item.pszText, lstrlen(item.pszText), &siz) ?
  8789. S_OK : E_FAIL;
  8790. if (SUCCEEDED(hr) &&
  8791. (rcLabel.left + g_cxLabelMargin + siz.cx) > cx)
  8792. {
  8793. lstrcpyn(pszText, item.pszText, item.cchTextMax);
  8794. }
  8795. else
  8796. {
  8797. // Not truncated after all
  8798. bItemUnfolded = TRUE;
  8799. }
  8800. ListView_EndFakeItemDraw(&lvfd);
  8801. ListView_EndFakeCustomDraw(&lvfd);
  8802. }
  8803. }
  8804. }
  8805. }
  8806. else
  8807. {
  8808. // Large icon view is the only one that folds
  8809. if (ListView_GetUnfoldedRect(plv, iItem, &rcLabel))
  8810. {
  8811. item.pszText = pszText;
  8812. item.cchTextMax = cchTextMax;
  8813. ListView_OnGetItem(plv, &item);
  8814. }
  8815. else
  8816. {
  8817. // Item was never folded
  8818. bItemUnfolded = TRUE;
  8819. }
  8820. }
  8821. }
  8822. }
  8823. return bItemUnfolded;
  8824. }
  8825. #ifdef UNICODE
  8826. // Rather than thunking to ListView_OnGetItemText, we let ListView_GetItemA
  8827. // do the work.
  8828. int ListView_OnGetItemTextA(LV* plv, int i, LV_ITEMA *plvi)
  8829. {
  8830. if (!plvi)
  8831. return 0;
  8832. RIPMSG(plvi->pszText != NULL, "LVM_GETITEMTEXT null string pointer");
  8833. plvi->mask = LVIF_TEXT;
  8834. plvi->iItem = i;
  8835. if (!ListView_OnGetItemA(plv, plvi))
  8836. return 0;
  8837. return lstrlenA(plvi->pszText);
  8838. }
  8839. #endif
  8840. int ListView_OnGetItemText(LV* plv, int i, LV_ITEM *plvi)
  8841. {
  8842. if (!plvi)
  8843. return 0;
  8844. RIPMSG(plvi->pszText != NULL, "LVM_GETITEMTEXT null string pointer");
  8845. plvi->mask = LVIF_TEXT;
  8846. plvi->iItem = i;
  8847. if (!ListView_OnGetItem(plv, plvi))
  8848. return 0;
  8849. return lstrlen(plvi->pszText);
  8850. }
  8851. #ifdef UNICODE
  8852. BOOL WINAPI ListView_OnSetItemTextA(LV* plv, int i, int iSubItem, LPCSTR pszText)
  8853. {
  8854. LPWSTR pszW = NULL;
  8855. BOOL fRet;
  8856. // Let ListView_OnSetItemText() handle owner-data validation
  8857. if (pszText != NULL)
  8858. {
  8859. pszW = ProduceWFromA(plv->ci.uiCodePage, pszText);
  8860. if (pszW == NULL)
  8861. {
  8862. return FALSE;
  8863. }
  8864. }
  8865. fRet = ListView_OnSetItemText(plv, i, iSubItem, pszW);
  8866. FreeProducedString(pszW);
  8867. return fRet;
  8868. }
  8869. #endif
  8870. BOOL WINAPI ListView_OnSetItemText(LV* plv, int i, int iSubItem, LPCTSTR pszText)
  8871. {
  8872. LV_ITEM lvi;
  8873. if (ListView_IsOwnerData(plv))
  8874. {
  8875. RIPMSG(0, "LVM_SETITEMTEXT: Invalid for owner-data listview");
  8876. return FALSE;
  8877. }
  8878. ListView_InvalidateTTLastHit(plv, i);
  8879. lvi.mask = LVIF_TEXT;
  8880. lvi.pszText = (LPTSTR)pszText;
  8881. lvi.iItem = i;
  8882. lvi.iSubItem = iSubItem;
  8883. return ListView_OnSetItem(plv, &lvi);
  8884. }
  8885. VOID CALLBACK ImgCtxCallback(void * pvImgCtx, void * pvArg)
  8886. {
  8887. LV *plv = (LV *)pvArg;
  8888. ULONG ulState;
  8889. SIZE sizeImg;
  8890. IImgCtx *pImgCtx = plv->pImgCtx;
  8891. IImgCtx_GetStateInfo(pImgCtx, &ulState, &sizeImg, TRUE);
  8892. if (ulState & (IMGLOAD_STOPPED | IMGLOAD_ERROR))
  8893. {
  8894. TraceMsg(TF_BKIMAGE, "Listview ImageCallback: Error!");
  8895. plv->fImgCtxComplete = FALSE;
  8896. }
  8897. else if (ulState & IMGCHG_COMPLETE)
  8898. {
  8899. TraceMsg(TF_BKIMAGE, "Listview ImageCallback: Complete!");
  8900. plv->fImgCtxComplete = TRUE;
  8901. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  8902. }
  8903. }
  8904. void ListView_ReleaseBkImage(LV *plv)
  8905. {
  8906. if (plv->pImgCtx)
  8907. {
  8908. IImgCtx_Release(plv->pImgCtx);
  8909. plv->pImgCtx = NULL;
  8910. if (plv->hpalHalftone)
  8911. {
  8912. // No need to delete the half tone palette since we really
  8913. // share it with the image context and it will clean up.
  8914. plv->hpalHalftone = NULL;
  8915. }
  8916. }
  8917. if (plv->hbmBkImage)
  8918. {
  8919. DeleteObject(plv->hbmBkImage);
  8920. plv->hbmBkImage = NULL;
  8921. }
  8922. if (plv->pszBkImage)
  8923. {
  8924. LocalFree(plv->pszBkImage);
  8925. plv->pszBkImage = NULL;
  8926. }
  8927. }
  8928. BOOL WINAPI ListView_OnSetBkImage(LV* plv, LPLVBKIMAGE pbi)
  8929. {
  8930. BOOL fRet = FALSE;
  8931. if (!pbi)
  8932. return FALSE;
  8933. if (pbi->ulFlags & LVBKIF_TYPE_WATERMARK)
  8934. {
  8935. BITMAP bm;
  8936. if (pbi->ulFlags & ~LVBKIF_TYPE_WATERMARK)
  8937. return FALSE; // We don't support anything else with a watermark
  8938. if (plv->hbmpWatermark)
  8939. {
  8940. DeleteObject(plv->hbmpWatermark);
  8941. plv->hbmpWatermark = NULL;
  8942. }
  8943. if (pbi->hbm && GetObject(pbi->hbm, sizeof(bm), &bm))
  8944. {
  8945. plv->hbmpWatermark = pbi->hbm;
  8946. plv->szWatermark.cx = bm.bmWidth;
  8947. plv->szWatermark.cy = bm.bmHeight;
  8948. fRet = TRUE;
  8949. }
  8950. }
  8951. else
  8952. {
  8953. LPCTSTR pszImage = pbi->pszImage;
  8954. long fl;
  8955. switch (pbi->ulFlags & LVBKIF_SOURCE_MASK)
  8956. {
  8957. case LVBKIF_SOURCE_NONE:
  8958. TraceMsg(TF_BKIMAGE, "LV SetBkImage to none");
  8959. ListView_ReleaseBkImage(plv);
  8960. break;
  8961. case LVBKIF_SOURCE_HBITMAP:
  8962. TraceMsg(TF_BKIMAGE, "LV SetBkImage to hBitmap %08lX", pbi->hbm);
  8963. ListView_ReleaseBkImage(plv);
  8964. if (pbi->hbm &&
  8965. (plv->pImgCtx = CBitmapImgCtx_Create(pbi->hbm)) != NULL)
  8966. {
  8967. plv->hbmBkImage = pbi->hbm;
  8968. }
  8969. else
  8970. {
  8971. pbi->ulFlags &= ~LVBKIF_SOURCE_HBITMAP;
  8972. }
  8973. break;
  8974. case LVBKIF_SOURCE_URL:
  8975. TraceMsg(TF_BKIMAGE, "LV SetBkImage to URL");
  8976. ListView_ReleaseBkImage(plv);
  8977. if (pszImage && pszImage[0])
  8978. {
  8979. HRESULT (*pfnCoCreateInstance)(REFCLSID, IUnknown *, DWORD, REFIID, void * *);
  8980. HRESULT hr;
  8981. HMODULE hmodOLE;
  8982. plv->pszBkImage = LocalAlloc(LPTR, (lstrlen(pszImage) + 1) * sizeof(TCHAR));
  8983. if (plv->pszBkImage == NULL)
  8984. {
  8985. TraceMsg(TF_BKIMAGE, "Wow, could not allocate memory for string!");
  8986. return FALSE;
  8987. }
  8988. lstrcpy(plv->pszBkImage, pszImage);
  8989. if (((hmodOLE = GetModuleHandle(TEXT("OLE32"))) == NULL) ||
  8990. ((pfnCoCreateInstance = (HRESULT (*)(REFCLSID, IUnknown *, DWORD, REFIID, void * *))GetProcAddress(hmodOLE, "CoCreateInstance")) == NULL))
  8991. {
  8992. TraceMsg(TF_BKIMAGE, "Could not find CoCreateInstance!");
  8993. TraceMsg(TF_BKIMAGE, "Did the caller remember to call CoInitialize?");
  8994. return FALSE;
  8995. }
  8996. hr = pfnCoCreateInstance(&CLSID_IImgCtx, NULL, CLSCTX_INPROC_SERVER,
  8997. &IID_IImgCtx, (void * *)&plv->pImgCtx);
  8998. if (FAILED(hr))
  8999. {
  9000. TraceMsg(TF_BKIMAGE, "Could not create a pImgCtx!");
  9001. TraceMsg(TF_BKIMAGE, "Did you remember to register IEIMAGE.DLL?");
  9002. return FALSE;
  9003. }
  9004. //
  9005. // Mirror the downloaded image if the listview window is RTL mirrored,
  9006. // so that it would be displayed as is. [samera]
  9007. //
  9008. fl = ((IS_WINDOW_RTL_MIRRORED(plv->ci.hwnd)) ? DWN_MIRRORIMAGE : 0);
  9009. hr = IImgCtx_Load(plv->pImgCtx, pszImage, fl);
  9010. if (FAILED(hr))
  9011. {
  9012. IImgCtx_Release(plv->pImgCtx);
  9013. plv->pImgCtx = NULL;
  9014. TraceMsg(TF_BKIMAGE, "Could not init a pImgCtx!");
  9015. return FALSE;
  9016. }
  9017. }
  9018. else
  9019. {
  9020. pbi->ulFlags &= ~LVBKIF_SOURCE_URL;
  9021. }
  9022. break;
  9023. default:
  9024. RIPMSG(0, "LVM_SETBKIMAGE: Unsupported image type %d", pbi->ulFlags & LVBKIF_SOURCE_MASK);
  9025. return FALSE;
  9026. }
  9027. plv->ulBkImageFlags = pbi->ulFlags;
  9028. plv->xOffsetPercent = pbi->xOffsetPercent;
  9029. plv->yOffsetPercent = pbi->yOffsetPercent;
  9030. //
  9031. // If we actually created a pImgCtx, initialize it here.
  9032. //
  9033. if (plv->pImgCtx)
  9034. {
  9035. if (plv->hpalHalftone == NULL)
  9036. {
  9037. IImgCtx_GetPalette(plv->pImgCtx, &plv->hpalHalftone);
  9038. }
  9039. plv->fImgCtxComplete = FALSE;
  9040. IImgCtx_SetCallback(plv->pImgCtx, ImgCtxCallback, plv);
  9041. IImgCtx_SelectChanges(plv->pImgCtx, IMGCHG_COMPLETE, 0, TRUE);
  9042. TraceMsg(TF_BKIMAGE, " SUCCESS!");
  9043. fRet = TRUE;
  9044. }
  9045. }
  9046. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9047. return fRet;
  9048. }
  9049. #ifdef UNICODE
  9050. BOOL WINAPI ListView_OnSetBkImageA(LV* plv, LPLVBKIMAGEA pbiA)
  9051. {
  9052. BOOL fProducedString = FALSE;
  9053. BOOL fRet;
  9054. LVBKIMAGEW biW;
  9055. CopyMemory(&biW, pbiA, sizeof(LVBKIMAGE));
  9056. switch (biW.ulFlags & LVBKIF_SOURCE_MASK)
  9057. {
  9058. case LVBKIF_SOURCE_NONE:
  9059. case LVBKIF_SOURCE_HBITMAP:
  9060. break;
  9061. case LVBKIF_SOURCE_URL:
  9062. if (biW.pszImage != NULL)
  9063. {
  9064. biW.pszImage = ProduceWFromA(plv->ci.uiCodePage, (LPCSTR)biW.pszImage);
  9065. if (biW.pszImage == (LPARAM)NULL)
  9066. {
  9067. return FALSE;
  9068. }
  9069. fProducedString = TRUE;
  9070. }
  9071. break;
  9072. default:
  9073. // Let ListView_OnSetBkImage() complain about the invalid parameter
  9074. break;
  9075. }
  9076. fRet = ListView_OnSetBkImage(plv, &biW);
  9077. if (fProducedString)
  9078. {
  9079. FreeProducedString((void *)biW.pszImage);
  9080. }
  9081. return fRet;
  9082. }
  9083. #endif
  9084. BOOL WINAPI ListView_OnGetBkImage(LV* plv, LPLVBKIMAGE pbi)
  9085. {
  9086. BOOL fRet = FALSE;
  9087. if (!IsBadWritePtr(pbi, sizeof(*pbi)))
  9088. {
  9089. if (pbi->ulFlags & LVBKIF_TYPE_WATERMARK)
  9090. {
  9091. pbi->hbm = plv->hbmpWatermark;
  9092. fRet = TRUE;
  9093. }
  9094. else
  9095. {
  9096. pbi->ulFlags = plv->ulBkImageFlags;
  9097. switch (plv->ulBkImageFlags & LVBKIF_SOURCE_MASK)
  9098. {
  9099. case LVBKIF_SOURCE_NONE:
  9100. fRet = TRUE;
  9101. break;
  9102. case LVBKIF_SOURCE_HBITMAP:
  9103. pbi->hbm = plv->hbmBkImage;
  9104. fRet = TRUE;
  9105. break;
  9106. case LVBKIF_SOURCE_URL:
  9107. if (!IsBadWritePtr(pbi->pszImage, pbi->cchImageMax * sizeof(TCHAR)))
  9108. {
  9109. lstrcpyn(pbi->pszImage, plv->pszBkImage, pbi->cchImageMax);
  9110. fRet = TRUE;
  9111. }
  9112. break;
  9113. default:
  9114. RIPMSG(0, "ListView_OnGetBkImage: Invalid source");
  9115. break;
  9116. }
  9117. pbi->xOffsetPercent = plv->xOffsetPercent;
  9118. pbi->yOffsetPercent = plv->yOffsetPercent;
  9119. }
  9120. }
  9121. return fRet;
  9122. }
  9123. #ifdef UNICODE
  9124. BOOL WINAPI ListView_OnGetBkImageA(LV* plv, LPLVBKIMAGEA pbiA)
  9125. {
  9126. BOOL fRet = FALSE;
  9127. if (!IsBadWritePtr(pbiA, sizeof(*pbiA)))
  9128. {
  9129. pbiA->ulFlags = plv->ulBkImageFlags;
  9130. switch (plv->ulBkImageFlags & LVBKIF_SOURCE_MASK)
  9131. {
  9132. case LVBKIF_SOURCE_NONE:
  9133. fRet = TRUE;
  9134. break;
  9135. case LVBKIF_SOURCE_HBITMAP:
  9136. pbiA->hbm = plv->hbmBkImage;
  9137. fRet = TRUE;
  9138. break;
  9139. case LVBKIF_SOURCE_URL:
  9140. if (!IsBadWritePtr(pbiA->pszImage, pbiA->cchImageMax))
  9141. {
  9142. ConvertWToAN(plv->ci.uiCodePage, pbiA->pszImage,
  9143. pbiA->cchImageMax, plv->pszBkImage, -1);
  9144. fRet = TRUE;
  9145. }
  9146. break;
  9147. default:
  9148. RIPMSG(0, "ListView_OnGetBkImage: Invalid source");
  9149. break;
  9150. }
  9151. pbiA->xOffsetPercent = plv->xOffsetPercent;
  9152. pbiA->yOffsetPercent = plv->yOffsetPercent;
  9153. }
  9154. return fRet;
  9155. }
  9156. #endif
  9157. void ListView_FreeSubItem(PLISTSUBITEM plsi)
  9158. {
  9159. if (plsi)
  9160. {
  9161. Str_Set(&plsi->pszText, NULL);
  9162. LocalFree(plsi);
  9163. }
  9164. }
  9165. int ListView_GetCxScrollbar(LV* plv)
  9166. {
  9167. int cx;
  9168. if (((plv->exStyle & LVS_EX_FLATSB) == 0) ||
  9169. !FlatSB_GetScrollProp(plv->ci.hwnd, WSB_PROP_CXVSCROLL, &cx))
  9170. {
  9171. cx = g_cxScrollbar;
  9172. }
  9173. return cx;
  9174. }
  9175. int ListView_GetCyScrollbar(LV* plv)
  9176. {
  9177. int cy;
  9178. if (((plv->exStyle & LVS_EX_FLATSB) == 0) ||
  9179. !FlatSB_GetScrollProp(plv->ci.hwnd, WSB_PROP_CYHSCROLL, &cy))
  9180. {
  9181. cy = g_cyScrollbar;
  9182. }
  9183. return cy;
  9184. }
  9185. DWORD ListView_GetWindowStyle(LV* plv)
  9186. {
  9187. DWORD dwStyle;
  9188. if (((plv->exStyle & LVS_EX_FLATSB) == 0) ||
  9189. !FlatSB_GetScrollProp(plv->ci.hwnd, WSB_PROP_WINSTYLE, (LPINT)&dwStyle))
  9190. {
  9191. dwStyle = GetWindowStyle(plv->ci.hwnd);
  9192. }
  9193. return dwStyle;
  9194. }
  9195. int ListView_SetScrollInfo(LV *plv, int fnBar, LPSCROLLINFO lpsi, BOOL fRedraw)
  9196. {
  9197. int iRc;
  9198. if (plv->exStyle & LVS_EX_FLATSB)
  9199. {
  9200. iRc = FlatSB_SetScrollInfo(plv->ci.hwnd, fnBar, lpsi, fRedraw);
  9201. }
  9202. else
  9203. {
  9204. iRc = SetScrollInfo(plv->ci.hwnd, fnBar, lpsi, fRedraw);
  9205. }
  9206. //
  9207. // You'd think we were finished, but in fact the game is only half over.
  9208. //
  9209. // Some apps (e.g., Font Folder) will do
  9210. //
  9211. // SetWindowLong(hwnd, GWL_STYLE, newStyle);
  9212. //
  9213. // where newStyle toggles the WS_HSCROLL and/or WS_VSCROLL bits.
  9214. // This causes USER's internal bookkeeping to go completely out
  9215. // of whack: The ScrollInfo says that there is a scrollbar, but
  9216. // the window style says there isn't, or vice versa. The result
  9217. // is that we get a scrollbar when we shouldn't or vice versa.
  9218. //
  9219. // So each time we tweak the scroll info in a manner that changes
  9220. // the range and page, we kick USER in the head to make sure USER's
  9221. // view of the world (via style bits) is the same as the scroll
  9222. // bar's view of the world (via SCROLLINFO).
  9223. //
  9224. //
  9225. // We should always change SIF_PAGE and SIF_RANGE at the same time.
  9226. //
  9227. ASSERT((lpsi->fMask & (SIF_PAGE | SIF_RANGE)) == 0 ||
  9228. (lpsi->fMask & (SIF_PAGE | SIF_RANGE)) == (SIF_PAGE | SIF_RANGE));
  9229. if ((lpsi->fMask & (SIF_PAGE | SIF_RANGE)) == (SIF_PAGE | SIF_RANGE))
  9230. {
  9231. BOOL fShow;
  9232. fShow = lpsi->nMax && (int)lpsi->nPage <= lpsi->nMax;
  9233. #ifdef DEBUG
  9234. {
  9235. DWORD dwStyle, dwScroll, dwWant;
  9236. dwScroll = (fnBar == SB_VERT) ? WS_VSCROLL : WS_HSCROLL;
  9237. //
  9238. // We can short-circuit some logic with secret knowledge about how
  9239. // ListView uses SetScrollInfo.
  9240. //
  9241. ASSERT(lpsi->nMin == 0);
  9242. dwWant = fShow ? dwScroll : 0;
  9243. dwStyle = ListView_GetWindowStyle(plv);
  9244. if ((dwStyle & dwScroll) != dwWant)
  9245. {
  9246. TraceMsg(TF_LISTVIEW, "ListView_SetScrollInfo: App twiddled WS_[VH]SCROLL");
  9247. }
  9248. }
  9249. #endif
  9250. if (plv->exStyle & LVS_EX_FLATSB)
  9251. FlatSB_ShowScrollBar(plv->ci.hwnd, fnBar, fShow);
  9252. else
  9253. ShowScrollBar(plv->ci.hwnd, fnBar, fShow);
  9254. }
  9255. return iRc;
  9256. }
  9257. // Add/remove/replace item
  9258. BOOL ListView_FreeItem(LV* plv, LISTITEM* pitem)
  9259. {
  9260. ASSERT(!ListView_IsOwnerData(plv));
  9261. if (pitem)
  9262. {
  9263. if ((pitem->puColumns) && (pitem->cColumns != I_COLUMNSCALLBACK))
  9264. LocalFree(pitem->puColumns);
  9265. Str_Set(&pitem->pszText, NULL);
  9266. if (pitem->hrgnIcon && pitem->hrgnIcon!=(HANDLE)-1)
  9267. DeleteObject(pitem->hrgnIcon);
  9268. // NOTE: We never remove items from the image list; that's
  9269. // the app's responsibility.
  9270. // REVIEW: Should we do this? Or should we just provide
  9271. // a message that will adjust image indices for the guy
  9272. // when one is removed?
  9273. //
  9274. ControlFree(plv->hheap, pitem);
  9275. }
  9276. return FALSE;
  9277. }
  9278. LISTITEM* ListView_CreateItem(LV* plv, const LV_ITEM* plvi)
  9279. {
  9280. LISTITEM* pitem = ControlAlloc(plv->hheap, sizeof(LISTITEM));
  9281. ASSERT(!ListView_IsOwnerData(plv));
  9282. if (pitem)
  9283. {
  9284. if (plvi->mask & LVIF_STATE)
  9285. {
  9286. if (plvi->state & ~LVIS_ALL)
  9287. {
  9288. DebugMsg(DM_ERROR, TEXT("ListView: Invalid state: %04x"), plvi->state);
  9289. return NULL;
  9290. }
  9291. // If adding a selected item to a single-select listview, deselect
  9292. // any other items.
  9293. if ((plv->ci.style & LVS_SINGLESEL) && (plvi->state & LVIS_SELECTED))
  9294. ListView_DeselectAll(plv, -1);
  9295. pitem->state = (plvi->state & ~(LVIS_FOCUSED | LVIS_SELECTED));
  9296. }
  9297. if (plvi->mask & LVIF_PARAM)
  9298. pitem->lParam = plvi->lParam;
  9299. if (plvi->mask & LVIF_IMAGE)
  9300. pitem->iImage = (short) plvi->iImage;
  9301. if (plvi->mask & LVIF_INDENT)
  9302. pitem->iIndent = (short) plvi->iIndent;
  9303. pitem->pt.x = pitem->pt.y = RECOMPUTE;
  9304. ListView_SetSRecompute(pitem);
  9305. pitem->pszText = NULL;
  9306. if (plvi->mask & LVIF_TEXT)
  9307. {
  9308. if (!Str_Set(&pitem->pszText, plvi->pszText))
  9309. {
  9310. ListView_FreeItem(plv, pitem);
  9311. return NULL;
  9312. }
  9313. }
  9314. if ((plvi->mask & LVIF_COLUMNS) && plvi->cColumns)
  9315. {
  9316. pitem->cColumns = plvi->cColumns;
  9317. if (plvi->cColumns != I_COLUMNSCALLBACK)
  9318. {
  9319. // Too many columns, or no column array? Then fail.
  9320. if ((plvi->cColumns > CCMAX_TILE_COLUMNS) || (plvi->puColumns == NULL))
  9321. {
  9322. ListView_FreeItem(plv, pitem);
  9323. return NULL;
  9324. }
  9325. pitem->puColumns = LocalAlloc(LPTR, sizeof(UINT) * pitem->cColumns);
  9326. if (pitem->puColumns == NULL)
  9327. {
  9328. ListView_FreeItem(plv, pitem);
  9329. return NULL;
  9330. }
  9331. CopyMemory(pitem->puColumns, plvi->puColumns, sizeof(UINT) * pitem->cColumns);
  9332. }
  9333. }
  9334. else
  9335. {
  9336. pitem->cColumns = 0;
  9337. pitem->puColumns = NULL;
  9338. }
  9339. pitem->dwId = plv->idNext++; // This may overflow. How to deal?
  9340. }
  9341. return pitem;
  9342. }
  9343. // HACK ALERT!! -- fSmoothScroll is an added parameter! It allows for smooth
  9344. // scrolling when deleting items. ListView_LRInvalidateBelow is only currently
  9345. // called from ListView_OnUpdate and ListView_OnDeleteItem. Both these calls
  9346. // have been modified to work correctly and be backwards compatible.
  9347. //
  9348. void ListView_LRInvalidateBelow(LV* plv, int i, int fSmoothScroll)
  9349. {
  9350. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  9351. {
  9352. RECT rcItem;
  9353. if (!ListView_RedrawEnabled(plv) ||
  9354. (ListView_IsReportView(plv) && (plv->pImgCtx != NULL)))
  9355. fSmoothScroll = FALSE;
  9356. if (i >= 0)// && i < ListView_Count(plv))
  9357. {
  9358. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, &rcItem, NULL);
  9359. }
  9360. else
  9361. {
  9362. rcItem.left = rcItem.top = 0;
  9363. rcItem.right = plv->sizeClient.cx;
  9364. rcItem.bottom = plv->sizeClient.cy;
  9365. }
  9366. // Don't try to scroll over the header part
  9367. if (ListView_IsReportView(plv) && rcItem.top < plv->yTop)
  9368. rcItem.top = plv->yTop;
  9369. // For both List and report view need to erase the item and
  9370. // below. Note: do simple test to see if there is anything
  9371. // to redraw
  9372. // we can't check for bottom/right > 0 because if we nuked something
  9373. // above or to the left of the view, it may affect us all
  9374. if ((rcItem.top <= plv->sizeClient.cy) &&
  9375. (rcItem.left <= plv->sizeClient.cx))
  9376. {
  9377. rcItem.bottom = plv->sizeClient.cy;
  9378. if (ListView_RedrawEnabled(plv))
  9379. {
  9380. if ((plv->clrBk == CLR_NONE) && (plv->pImgCtx == NULL))
  9381. {
  9382. LVSeeThruScroll(plv, &rcItem);
  9383. }
  9384. else if (ListView_IsReportView(plv) && fSmoothScroll)
  9385. {
  9386. #ifndef UNIX
  9387. SMOOTHSCROLLINFO si =
  9388. {
  9389. sizeof(si),
  9390. SSIF_MINSCROLL,
  9391. plv->ci.hwnd,
  9392. 0,
  9393. -(plv->cyItem),
  9394. &rcItem,
  9395. &rcItem,
  9396. NULL,
  9397. NULL,
  9398. SW_INVALIDATE|SW_ERASE,
  9399. SSI_DEFAULT,
  9400. 1,
  9401. 1,
  9402. };
  9403. #else
  9404. SMOOTHSCROLLINFO si;
  9405. si.cbSize = sizeof(si);
  9406. si.fMask = SSIF_MINSCROLL;
  9407. si.hwnd = plv->ci.hwnd;
  9408. si.dx = 0;
  9409. si.dy = -(plv->cyItem);
  9410. si.lprcSrc = &rcItem;
  9411. si.lprcClip = &rcItem;
  9412. si.hrgnUpdate = NULL;
  9413. si.lprcUpdate = NULL;
  9414. si.fuScroll = SW_INVALIDATE|SW_ERASE;
  9415. si.uMaxScrollTime = SSI_DEFAULT;
  9416. si.cxMinScroll = 1;
  9417. si.cyMinScroll = 1;
  9418. si.pfnScrollProc = NULL;
  9419. #endif
  9420. SmoothScrollWindow(&si);
  9421. }
  9422. else
  9423. {
  9424. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  9425. }
  9426. }
  9427. else
  9428. {
  9429. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  9430. }
  9431. if (ListView_IsListView(plv))
  9432. {
  9433. RECT rcClient;
  9434. // For Listview we need to erase the other columns...
  9435. rcClient.left = rcItem.right;
  9436. rcClient.top = 0;
  9437. rcClient.bottom = plv->sizeClient.cy;
  9438. rcClient.right = plv->sizeClient.cx;
  9439. RedrawWindow(plv->ci.hwnd, &rcClient, NULL, RDW_INVALIDATE | RDW_ERASE);
  9440. }
  9441. }
  9442. }
  9443. }
  9444. // Used in Ownerdata Icon views to try to not invalidate the whole world...
  9445. void ListView_IInvalidateBelow(LV* plv, int i)
  9446. {
  9447. RECT rcItem;
  9448. if (i >= 0)
  9449. {
  9450. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, &rcItem, NULL);
  9451. }
  9452. else
  9453. {
  9454. rcItem.left = rcItem.top = 0;
  9455. rcItem.right = plv->sizeClient.cx;
  9456. rcItem.bottom = plv->sizeClient.cy;
  9457. }
  9458. // For Iconviews we need to invalidate everything to the right of us in this
  9459. // row and everything below the row...
  9460. // below. Note: do simple test to see if there is anything
  9461. // to redraw
  9462. if ((rcItem.top <= plv->sizeClient.cy) &&
  9463. (rcItem.left <= plv->sizeClient.cx))
  9464. {
  9465. rcItem.right = plv->sizeClient.cx;
  9466. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  9467. // Now erase everything below...
  9468. rcItem.top = rcItem.bottom;
  9469. rcItem.bottom = plv->sizeClient.cy;
  9470. rcItem.left = 0;
  9471. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  9472. }
  9473. }
  9474. void ListView_OnUpdate(LV* plv, int i)
  9475. {
  9476. // If in icon/small view, don't call InvalidateItem, since that'll force
  9477. // FindFreeSlot to get called, which is pig-like. Instead, just
  9478. // force a WM_PAINT message, which we'll catch and call Recompute with.
  9479. //
  9480. if (ListView_IsAutoArrangeView(plv))
  9481. {
  9482. ListView_ArrangeOrSnapToGrid(plv);
  9483. if (!(plv->ci.style & LVS_AUTOARRANGE))
  9484. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INTERNALPAINT | RDW_NOCHILDREN);
  9485. }
  9486. else
  9487. {
  9488. // HACK ALERT!! -- The third parameter is new. It allows for
  9489. // smooth scrolling when items are deleted in reportview.
  9490. // Passing 0, tells it NOT to scroll.
  9491. //
  9492. ListView_LRInvalidateBelow(plv, i, 0);
  9493. }
  9494. ListView_UpdateScrollBars(plv);
  9495. }
  9496. #ifdef UNICODE
  9497. int ListView_OnInsertItemA(LV* plv, LV_ITEMA* plvi)
  9498. {
  9499. LPWSTR pszW = NULL;
  9500. LPSTR pszC = NULL;
  9501. int iRet;
  9502. //HACK ALERT -- this code assumes that LV_ITEMA is exactly the same
  9503. // as LV_ITEMW except for the pointer to the string.
  9504. COMPILETIME_ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW));
  9505. if (!plvi)
  9506. {
  9507. return -1;
  9508. }
  9509. if ((plvi->mask & LVIF_TEXT) && (plvi->pszText != NULL))
  9510. {
  9511. pszC = plvi->pszText;
  9512. pszW = ProduceWFromA(plv->ci.uiCodePage, pszC);
  9513. if (pszW == NULL)
  9514. return -1;
  9515. plvi->pszText = (LPSTR)pszW;
  9516. }
  9517. iRet = ListView_OnInsertItem(plv, (const LV_ITEM*) plvi);
  9518. if (pszW != NULL)
  9519. {
  9520. plvi->pszText = pszC;
  9521. FreeProducedString(pszW);
  9522. }
  9523. return iRet;
  9524. }
  9525. #endif
  9526. int ListView_OnInsertItem(LV* plv, const LV_ITEM* plvi)
  9527. {
  9528. int i;
  9529. ListView_InsertItemInternal(plv, plvi, &i);
  9530. return i;
  9531. }
  9532. LISTITEM* ListView_InsertItemInternal(LV* plv, const LV_ITEM* plvi, int* pi)
  9533. {
  9534. int iItem;
  9535. LISTITEM *pitem = NULL;
  9536. *pi = -1;
  9537. if (plvi == NULL)
  9538. {
  9539. RIPMSG(0, "ListView_InsertItem: Do not pass a NULL LV_ITEM.");
  9540. return NULL;
  9541. }
  9542. if (plvi->iSubItem != 0) // can only insert the 0th item
  9543. {
  9544. RIPMSG(0, "ListView_InsertItem: iSubItem must be 0 (app passed %d)", plvi->iSubItem);
  9545. return NULL;
  9546. }
  9547. // If sorted, then insert sorted.
  9548. //
  9549. if (plv->ci.style & (LVS_SORTASCENDING | LVS_SORTDESCENDING)
  9550. && !ListView_IsOwnerData(plv))
  9551. {
  9552. if (plvi->pszText == LPSTR_TEXTCALLBACK)
  9553. {
  9554. DebugMsg(DM_ERROR, TEXT("Don't use LPSTR_TEXTCALLBACK with LVS_SORTASCENDING or LVS_SORTDESCENDING"));
  9555. return NULL;
  9556. }
  9557. iItem = ListView_LookupString(plv, plvi->pszText, LVFI_SUBSTRING | LVFI_NEARESTXY, 0);
  9558. }
  9559. else
  9560. iItem = plvi->iItem;
  9561. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  9562. if (!ListView_IsOwnerData(plv))
  9563. {
  9564. int iZ;
  9565. static s_blah = 0;
  9566. UINT uSelMask = plvi->mask & LVIF_STATE ?
  9567. (plvi->state & (LVIS_FOCUSED | LVIS_SELECTED))
  9568. : 0;
  9569. UINT uSel = uSelMask;
  9570. pitem = ListView_CreateItem(plv, plvi);
  9571. if (!pitem)
  9572. return NULL;
  9573. iItem = DPA_InsertPtr(plv->hdpa, iItem, pitem);
  9574. if (iItem == -1)
  9575. {
  9576. ListView_FreeItem(plv, pitem);
  9577. return NULL;
  9578. }
  9579. plv->cTotalItems++;
  9580. if (plv->hdpaSubItems)
  9581. {
  9582. int iCol;
  9583. // slide all the colum DPAs down to match the location of the
  9584. // inserted item
  9585. //
  9586. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  9587. {
  9588. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  9589. if (hdpa) // this is optional, call backs don't have them
  9590. {
  9591. // insert a blank item (REVIEW: should this be callback?)
  9592. // since this can be a tail sparce array,
  9593. // we need to make sure enough items are there.
  9594. if (iItem >= DPA_GetPtrCount(hdpa))
  9595. DPA_SetPtr(hdpa, iItem, NULL);
  9596. else if (DPA_InsertPtr(hdpa, iItem, NULL) != iItem)
  9597. goto Failure;
  9598. // Bad assert since hdpa can be tail sparse
  9599. // ASSERT(ListView_Count(plv) == DPA_GetPtrCount(hdpa));
  9600. ASSERT(ListView_Count(plv) >= DPA_GetPtrCount(hdpa));
  9601. }
  9602. }
  9603. }
  9604. // Add item to end of z order
  9605. //
  9606. iZ = DPA_InsertPtr(plv->hdpaZOrder, ListView_Count(plv), IntToPtr(iItem));
  9607. if (iZ == -1)
  9608. {
  9609. Failure:
  9610. DebugMsg(TF_LISTVIEW, TEXT("ListView_OnInsertItem() failed"));
  9611. if (DPA_DeletePtr(plv->hdpa, iItem))
  9612. plv->cTotalItems--;
  9613. ListView_FreeItem(plv, pitem);
  9614. return NULL;
  9615. }
  9616. // if we inserted before the focus point, move the focus point up one
  9617. if (iItem <= plv->iFocus)
  9618. plv->iFocus++;
  9619. // do the same thing for the mark
  9620. if (iItem <= plv->iMark)
  9621. plv->iMark++;
  9622. // If the item was not added at the end of the list we need
  9623. // to update the other indexes in the list
  9624. if (iItem != ListView_Count(plv) - 1)
  9625. {
  9626. int i2;
  9627. for (i2 = iZ - 1; i2 >= 0; i2--)
  9628. {
  9629. int iItemZ = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, i2);
  9630. if (iItemZ >= iItem)
  9631. DPA_SetPtr(plv->hdpaZOrder, i2, (void *)(UINT_PTR)(iItemZ + 1));
  9632. }
  9633. }
  9634. if (ListView_CheckBoxes(plv))
  9635. {
  9636. uSelMask |= LVIS_STATEIMAGEMASK;
  9637. uSel |= INDEXTOSTATEIMAGEMASK(1);
  9638. }
  9639. if (uSelMask)
  9640. {
  9641. // we masked off these in the createitem above.
  9642. // because turning these on means more than setting the bits.
  9643. ListView_OnSetItemState(plv, iItem, uSel, uSelMask);
  9644. }
  9645. if (plvi->mask & LVIF_GROUPID)
  9646. {
  9647. int iGroupId = plvi->iGroupId;
  9648. if (iGroupId == I_GROUPIDNONE)
  9649. {
  9650. LISTITEM_SETASKEDFORGROUP(pitem);
  9651. }
  9652. else if (iGroupId != I_GROUPIDCALLBACK)
  9653. {
  9654. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, iGroupId, NULL);
  9655. if (!pgrp)
  9656. {
  9657. ListView_FreeItem(plv, pitem);
  9658. return NULL;
  9659. }
  9660. pitem->pGroup = pgrp;
  9661. DPA_AppendPtr(pgrp->hdpa, pitem);
  9662. }
  9663. }
  9664. else
  9665. {
  9666. LISTITEM_SETHASNOTASKEDFORGROUP(pitem);
  9667. }
  9668. if (plv->fGroupView && (plv->flags & LVF_REDRAW))
  9669. {
  9670. _ListView_RecomputeEx(plv, NULL, 0, FALSE);
  9671. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9672. }
  9673. }
  9674. else
  9675. {
  9676. //
  9677. // simply adjust selection and count
  9678. //
  9679. if ((iItem >= 0) && (iItem <= MAX_LISTVIEWITEMS))
  9680. {
  9681. if (FAILED(plv->plvrangeSel->lpVtbl->InsertItem(plv->plvrangeSel, iItem)))
  9682. {
  9683. return NULL;
  9684. }
  9685. plv->cTotalItems++;
  9686. plv->rcView.left = RECOMPUTE;
  9687. ListView_Recompute(plv);
  9688. if (!ListView_IsReportView(plv) && !ListView_IsListView(plv))
  9689. {
  9690. // We need to erase the background so that we don't leave
  9691. // turds from wrapped labels in large icon mode. This could
  9692. // be optimized by only invalidating to the right of and
  9693. // below the inserted item.
  9694. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9695. }
  9696. // if we inserted before the focus point, move the focus point up
  9697. if (iItem <= plv->iFocus)
  9698. plv->iFocus++;
  9699. // do the same thing for the mark
  9700. if (iItem <= plv->iMark)
  9701. plv->iMark++;
  9702. }
  9703. }
  9704. if (!ListView_IsOwnerData(plv))
  9705. {
  9706. ASSERT(ListView_Count(plv) == DPA_GetPtrCount(plv->hdpaZOrder));
  9707. }
  9708. if (ListView_RedrawEnabled(plv))
  9709. {
  9710. // Update region
  9711. ListView_RecalcRegion(plv, TRUE, TRUE);
  9712. // The Maybe resize colmns may resize things in which case the next call
  9713. // to Update is not needed.
  9714. if (!ListView_MaybeResizeListColumns(plv, iItem, iItem))
  9715. ListView_OnUpdate(plv, iItem);
  9716. // this trick makes inserting lots of items cheap
  9717. // even if redraw is enabled.... don't calc or position items
  9718. // until this postmessage comes around
  9719. if (!plv->uUnplaced)
  9720. {
  9721. PostMessage(plv->ci.hwnd, LVMI_PLACEITEMS, 0, 0);
  9722. }
  9723. plv->uUnplaced++;
  9724. }
  9725. else
  9726. {
  9727. //
  9728. // Special case code to make using SetRedraw work reasonably well
  9729. // for adding items to a listview which is in a non layout mode...
  9730. //
  9731. if ((plv->iFirstChangedNoRedraw == -1) ||
  9732. (iItem < plv->iFirstChangedNoRedraw))
  9733. plv->iFirstChangedNoRedraw = iItem;
  9734. }
  9735. // Nuke insertmark... it may be invalid now that an item has been added.
  9736. {
  9737. LVINSERTMARK lvim = {0};
  9738. lvim.cbSize = sizeof(LVINSERTMARK);
  9739. lvim.iItem = -1;
  9740. ListView_OnSetInsertMark(plv, (LPLVINSERTMARK)&lvim);
  9741. }
  9742. ListView_Notify(plv, iItem, 0, LVN_INSERTITEM);
  9743. NotifyWinEvent(EVENT_OBJECT_CREATE, plv->ci.hwnd, OBJID_CLIENT, iItem+1);
  9744. *pi = iItem;
  9745. return pitem;
  9746. }
  9747. BOOL ListView_OnDeleteItem(LV* plv, int iItem)
  9748. {
  9749. int iCount = ListView_Count(plv);
  9750. if (!ListView_IsValidItemNumber(plv, iItem))
  9751. return FALSE; // out of range
  9752. NotifyWinEvent(EVENT_OBJECT_DESTROY, plv->ci.hwnd, OBJID_CLIENT, iItem+1);
  9753. ListView_DismissEdit(plv, TRUE); // cancel edits
  9754. ListView_OnSetItemState(plv, iItem, 0, LVIS_SELECTED);
  9755. if (plv->iFocus == iItem)
  9756. ListView_OnSetItemState(plv, (iItem == iCount - 1 ? iItem - 1 : iItem + 1), LVIS_FOCUSED, LVIS_FOCUSED);
  9757. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  9758. if (!ListView_IsOwnerData(plv))
  9759. {
  9760. LISTITEM* pitem = ListView_FastGetItemPtr(plv, iItem);
  9761. int iZ;
  9762. if ((plv->rcView.left != RECOMPUTE) && ListView_IsSlotView(plv))
  9763. {
  9764. if (LV_IsItemOnViewEdge(plv, pitem))
  9765. {
  9766. plv->rcView.left = RECOMPUTE;
  9767. }
  9768. }
  9769. ListView_RemoveItemFromItsGroup(plv, pitem);
  9770. // We don't need to invalidate the item in report view because we
  9771. // will be scrolling on top of it.
  9772. //
  9773. if (!ListView_IsReportView(plv))
  9774. ListView_InvalidateItem(plv, iItem, FALSE, RDW_INVALIDATE | RDW_ERASE);
  9775. // this notify must be done AFTER the Invalidate because some items need callbacks
  9776. // to calculate the rect, but the notify might free it out
  9777. ListView_Notify(plv, iItem, 0, LVN_DELETEITEM);
  9778. // During the notify, the app might've done something to the listview
  9779. // so revalidate the item number pointer so we don't fault
  9780. #ifdef DEBUG
  9781. // Validate internally because DPA_DeletePtr will ASSERT if you ask it
  9782. // to delete something that doesn't exist.
  9783. if (!ListView_IsValidItemNumber(plv, iItem))
  9784. pitem = NULL;
  9785. else
  9786. #endif
  9787. pitem = DPA_DeletePtr(plv->hdpa, iItem);
  9788. if (!pitem)
  9789. {
  9790. RIPMSG(0, "Something strange happened during LVN_DELETEITEM; abandoning LVM_DELETEITEM");
  9791. return FALSE;
  9792. }
  9793. plv->cTotalItems = DPA_GetPtrCount(plv->hdpa);
  9794. // remove from the z-order, this is a lisearch to find this!
  9795. DPA_DeletePtr(plv->hdpaZOrder, ListView_ZOrderIndex(plv, iItem));
  9796. //
  9797. // As the Z-order hdpa is a set of indexes we also need to decrement
  9798. // all indexes that exceed the one we are deleting.
  9799. //
  9800. for (iZ = ListView_Count(plv) - 1; iZ >= 0; iZ--)
  9801. {
  9802. int iItemZ = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, iZ);
  9803. if (iItemZ > iItem)
  9804. DPA_SetPtr(plv->hdpaZOrder, iZ, IntToPtr(iItemZ - 1));
  9805. }
  9806. // remove from sub item DPAs if necessary
  9807. if (plv->hdpaSubItems)
  9808. {
  9809. int iCol;
  9810. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  9811. {
  9812. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  9813. if (hdpa)
  9814. { // this is optional, call backs don't have them
  9815. PLISTSUBITEM plsi;
  9816. // These DPAs are tail sparse, so don't get upset if we
  9817. // try to delete something that's past the end of the list
  9818. #ifdef DEBUG
  9819. plsi = iItem < DPA_GetPtrCount(hdpa) ? DPA_DeletePtr(hdpa, iItem) : NULL;
  9820. #else
  9821. plsi = DPA_DeletePtr(hdpa, iItem);
  9822. #endif
  9823. ListView_FreeSubItem(plsi);
  9824. }
  9825. }
  9826. }
  9827. ListView_FreeItem(plv, pitem); // ... finaly the item pointer
  9828. if (plv->fGroupView && (plv->flags & LVF_REDRAW))
  9829. {
  9830. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  9831. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9832. }
  9833. }
  9834. else
  9835. {
  9836. //
  9837. // simply notify and then fixup selection state and count
  9838. //
  9839. if ((iItem >= 0) && (iItem <= MAX_LISTVIEWITEMS))
  9840. {
  9841. ListView_Notify(plv, iItem, 0, LVN_DELETEITEM);
  9842. if (FAILED(plv->plvrangeSel->lpVtbl->RemoveItem(plv->plvrangeSel, iItem)))
  9843. {
  9844. SetLastError(ERROR_OUTOFMEMORY);
  9845. return FALSE;
  9846. }
  9847. plv->cTotalItems--;
  9848. plv->rcView.left = RECOMPUTE;
  9849. ListView_Recompute(plv);
  9850. if (!ListView_IsReportView(plv) && !ListView_IsListView(plv))
  9851. {
  9852. // We need to erase the background so that the last item gets
  9853. // erased in both icon modes and so that we don't leave turds
  9854. // from wrapped labels in large icon mode. This could be
  9855. // optimized by only invalidating to the right of and below
  9856. // the deleted item.
  9857. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9858. }
  9859. }
  9860. else
  9861. {
  9862. return FALSE;
  9863. }
  9864. }
  9865. iCount = ListView_Count(plv); // regrab count incase someone updated item...
  9866. if (!ListView_IsOwnerData(plv))
  9867. {
  9868. ASSERT(ListView_Count(plv) == DPA_GetPtrCount(plv->hdpaZOrder));
  9869. }
  9870. if (plv->iFocus == iItem)
  9871. {
  9872. if (plv->iFocus >= iCount)
  9873. {
  9874. plv->iFocus = iCount - 1;
  9875. }
  9876. }
  9877. if (plv->iFocus > iItem)
  9878. {
  9879. plv->iFocus--; // slide the focus index down
  9880. }
  9881. // same with the mark
  9882. if (plv->iMark == iItem)
  9883. {
  9884. // deleted the mark item
  9885. if (plv->iMark >= iCount) // did we nuke the last item?
  9886. plv->iMark = iCount - 1;
  9887. }
  9888. else if (plv->iMark > iItem)
  9889. plv->iMark--; // slide the mark index down
  9890. // Free up the hot item
  9891. if (plv->iHot == iItem)
  9892. plv->iHot = -1;
  9893. // Deleting an icon invalidates the icon positioning cache
  9894. plv->iFreeSlot = -1;
  9895. // HACK ALERT!! -- This construct with ReportView steals code from
  9896. // ListView_OnUpdate. Currently, it will work exactly the same as before,
  9897. // EXCEPT, that it won't call ListView_OnUpdate. This is to allow us to
  9898. // send a flag to ListView_LRUpdateBelow to tell it we're scrolling up.
  9899. //
  9900. if (ListView_IsReportView(plv))
  9901. {
  9902. // if the new count is zero and we will be showing empty text, simply invalidate the
  9903. // rect and redraw, else go through the invalidate below code...
  9904. // we don't know if we are going to show empty text if pszEmptyText is NULL, or not
  9905. // because we may get one through notify, so if iCount is 0 invalidate everything
  9906. if (iCount == 0)
  9907. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9908. else
  9909. ListView_LRInvalidateBelow(plv,iItem,1);
  9910. if (ListView_RedrawEnabled(plv))
  9911. ListView_UpdateScrollBars(plv);
  9912. else {
  9913. //
  9914. // Special case code to make using SetRedraw work reasonably well
  9915. // for adding items to a listview which is in a non layout mode...
  9916. //
  9917. if ((plv->iFirstChangedNoRedraw != -1) && (iItem < plv->iFirstChangedNoRedraw))
  9918. plv->iFirstChangedNoRedraw--;
  9919. }
  9920. }
  9921. else
  9922. {
  9923. if (ListView_RedrawEnabled(plv))
  9924. ListView_OnUpdate(plv, iItem);
  9925. else
  9926. {
  9927. ListView_LRInvalidateBelow(plv, iItem, 0);
  9928. //
  9929. // Special case code to make using SetRedraw work reasonably well
  9930. // for adding items to a listview which is in a non layout mode...
  9931. //
  9932. if ((plv->iFirstChangedNoRedraw != -1) && (iItem < plv->iFirstChangedNoRedraw))
  9933. plv->iFirstChangedNoRedraw--;
  9934. }
  9935. }
  9936. ListView_RecalcRegion(plv, TRUE, TRUE);
  9937. return TRUE;
  9938. }
  9939. void ListView_DeleteAllGroupItems(LV* plv)
  9940. {
  9941. if (plv->hdpaGroups)
  9942. {
  9943. int iGroup, cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  9944. for (iGroup = 0; iGroup < cGroups; iGroup++)
  9945. {
  9946. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  9947. DPA_Destroy(pgrp->hdpa);
  9948. pgrp->hdpa = DPA_Create(5);
  9949. }
  9950. }
  9951. }
  9952. BOOL ListView_OnDeleteAllItems(LV* plv)
  9953. {
  9954. int i;
  9955. BOOL bAlreadyNotified;
  9956. BOOL fHasItemData = !ListView_IsOwnerData(plv);
  9957. ListView_DismissEdit(plv, TRUE); // cancel edits
  9958. ListView_DeleteAllGroupItems(plv);
  9959. // Must neutralize the focus because some apps will call
  9960. // ListView_OnGetNextItem(LVNI_FOCUSED) during delete notifications,
  9961. // so we need to make sure the focus is in a safe place.
  9962. // May as well neutralize the mark, too.
  9963. plv->iMark = plv->iFocus = -1;
  9964. // Also nuke the icon positioning cache
  9965. plv->iFreeSlot = -1;
  9966. // Since we delete all items, There is no insertion slot!
  9967. plv->iInsertItem = -1;
  9968. bAlreadyNotified = (BOOL)ListView_Notify(plv, -1, 0, LVN_DELETEALLITEMS);
  9969. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  9970. if (fHasItemData || !bAlreadyNotified)
  9971. {
  9972. for (i = ListView_Count(plv) - 1; i >= 0; i--)
  9973. {
  9974. if (!bAlreadyNotified)
  9975. ListView_Notify(plv, i, 0, LVN_DELETEITEM);
  9976. if (fHasItemData)
  9977. {
  9978. ListView_FreeItem(plv, ListView_FastGetItemPtr(plv, i));
  9979. //
  9980. // CAREFUL! Applications such as NT Backup call back
  9981. // into ListView during the LVN_DELETEITEM notification,
  9982. // so we need to kill this item or we will fault at the
  9983. // next iteration because everybody relies on
  9984. // ListView_Count for validation.
  9985. //
  9986. DPA_FastDeleteLastPtr(plv->hdpa);
  9987. plv->cTotalItems--;
  9988. }
  9989. }
  9990. }
  9991. if (ListView_IsOwnerData(plv))
  9992. {
  9993. if (FAILED(plv->plvrangeSel->lpVtbl->Clear(plv->plvrangeSel)))
  9994. {
  9995. SetLastError(ERROR_OUTOFMEMORY);
  9996. }
  9997. plv->cTotalItems = 0;
  9998. }
  9999. else
  10000. {
  10001. DPA_DeleteAllPtrs(plv->hdpa);
  10002. DPA_DeleteAllPtrs(plv->hdpaZOrder);
  10003. plv->cTotalItems = 0;
  10004. if (plv->hdpaSubItems)
  10005. {
  10006. int iCol;
  10007. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  10008. {
  10009. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  10010. if (hdpa)
  10011. {
  10012. DPA_EnumCallback(hdpa, ListView_FreeColumnData, 0);
  10013. DPA_DeleteAllPtrs(hdpa);
  10014. }
  10015. }
  10016. }
  10017. }
  10018. plv->rcView.left = RECOMPUTE;
  10019. plv->xOrigin = 0;
  10020. plv->nSelected = 0;
  10021. plv->ptlRptOrigin.x = 0;
  10022. plv->ptlRptOrigin.y = 0;
  10023. // reset the cxItem width
  10024. if (!(plv->flags & LVF_COLSIZESET))
  10025. {
  10026. plv->cxItem = ListView_ComputeCXItemSize(plv);
  10027. }
  10028. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  10029. ListView_UpdateScrollBars(plv);
  10030. return TRUE;
  10031. }
  10032. int ListView_IFindNearestItem(LV* plv, int left, int top, UINT vk)
  10033. {
  10034. int iMin = -1;
  10035. if (ListView_IsOwnerData(plv))
  10036. {
  10037. POINT pt;
  10038. int cSlots;
  10039. int iWidth = 0, iHeight = 0;
  10040. ASSERT(!ListView_IsReportView(plv) && !ListView_IsListView(plv));
  10041. pt.x = left + plv->ptOrigin.x;
  10042. pt.y = top + plv->ptOrigin.y;
  10043. cSlots = ListView_GetSlotCount(plv, TRUE, &iWidth, &iHeight);
  10044. iMin = ListView_CalcHitSlot(plv, pt, cSlots, iWidth, iHeight);
  10045. switch(vk)
  10046. {
  10047. case VK_HOME:
  10048. iMin = 0;
  10049. break;
  10050. case VK_END:
  10051. iMin = ListView_Count(plv) - 1;
  10052. break;
  10053. case VK_LEFT:
  10054. if (iMin % cSlots)
  10055. iMin -= 1;
  10056. break;
  10057. case VK_RIGHT:
  10058. if ((iMin + 1) % cSlots)
  10059. iMin += 1;
  10060. break;
  10061. case VK_UP:
  10062. if (iMin >= cSlots)
  10063. iMin -= cSlots;
  10064. break;
  10065. case VK_DOWN:
  10066. if (iMin + cSlots < ListView_Count(plv))
  10067. iMin += cSlots;
  10068. break;
  10069. default: ;
  10070. }
  10071. iMin = max(0, iMin);
  10072. iMin = min(ListView_Count(plv) - 1, iMin);
  10073. }
  10074. else
  10075. {
  10076. ULONGLONG dMin = 0;
  10077. int cyItem;
  10078. int yEnd = 0, yLimit = 0, xEnd = 0;
  10079. int iCount;
  10080. int i;
  10081. if (ListView_UseLargeIcons(plv))
  10082. {
  10083. cyItem = plv->cyIcon;
  10084. }
  10085. else
  10086. {
  10087. cyItem = plv->cyItem;
  10088. }
  10089. iCount = ListView_Count(plv);
  10090. if (iCount == 1)
  10091. return 0;
  10092. if (vk == VK_HOME)
  10093. {
  10094. yEnd = yLimit = plv->rcView.bottom;
  10095. xEnd = plv->rcView.right;
  10096. }
  10097. else if (vk == VK_END)
  10098. {
  10099. yEnd = yLimit = plv->rcView.top;
  10100. xEnd = plv->rcView.left;
  10101. }
  10102. for (i = 0; i < iCount; i++)
  10103. {
  10104. RECT rc;
  10105. int dx;
  10106. ULONGLONG dxAbs, dyAbs, dOffset;
  10107. int dy;
  10108. ListView_GetRects(plv, i, QUERY_DEFAULT, &rc, NULL, NULL, NULL);
  10109. dx = rc.left - left;
  10110. dxAbs = (ULONGLONG)(dx < 0 ? -dx : dx);
  10111. dy = rc.top - top;
  10112. dyAbs = (ULONGLONG)(dy < 0 ? -dy : dy);
  10113. if ((vk == VK_LEFT) && (dxAbs < dyAbs || dx >= 0))
  10114. continue;
  10115. else if ((vk == VK_RIGHT) && (dxAbs < dyAbs || dx <= 0))
  10116. continue;
  10117. else if ((vk == VK_UP) && (dxAbs > dyAbs || dy >= 0))
  10118. continue;
  10119. else if ((vk == VK_DOWN) && (dxAbs > dyAbs || dy <= 0))
  10120. continue;
  10121. if (vk == VK_HOME || vk == VK_END)
  10122. {
  10123. // home is not the nearest to the top corner, it's the leftmost of the top row.
  10124. // ditto (reversed) for end. thus we can't use the stuff below. bummer
  10125. if (vk == VK_HOME)
  10126. {
  10127. if ((rc.top + cyItem < yEnd) || // if it's fully above the highest line so, take it!
  10128. ((rc.top < yLimit) && // if it's on the same row as the top item to date
  10129. (rc.left < xEnd)))
  10130. {
  10131. iMin = i;
  10132. xEnd = rc.left;
  10133. yEnd = rc.top;
  10134. if (rc.top + cyItem < yLimit)
  10135. yLimit = rc.top + cyItem;
  10136. }
  10137. }
  10138. else
  10139. {
  10140. if ((rc.top > yEnd) || //if it's full below the lowest row
  10141. ((rc.top + cyItem > yLimit) && // if it's on the same row
  10142. (rc.right > xEnd)))
  10143. {
  10144. iMin = i;
  10145. xEnd = rc.right;
  10146. yEnd = rc.top;
  10147. if (rc.top > yLimit)
  10148. yLimit = rc.top;
  10149. }
  10150. }
  10151. }
  10152. else
  10153. {
  10154. dOffset = ((dxAbs * dxAbs) + (dyAbs * dyAbs));
  10155. if (iMin == -1 || (dMin > dOffset))
  10156. {
  10157. dMin = dOffset;
  10158. iMin = i;
  10159. }
  10160. }
  10161. }
  10162. }
  10163. return iMin;
  10164. }
  10165. int ListView_Arrow(LV* plv, int iStart, UINT vk)
  10166. {
  10167. RECT rcFocus;
  10168. int i;
  10169. int dx;
  10170. int iCount;
  10171. //
  10172. // The algorithm to find which item depends if we are in a view
  10173. // that is arrange(layout) oriented or a sorted (list) view.
  10174. // For the sorted views we will use some optimizations to make
  10175. // it faster
  10176. //
  10177. iCount = ListView_Count(plv);
  10178. if ((ListView_IsReportView(plv) || ListView_IsListView(plv)) && !plv->fGroupView)
  10179. {
  10180. //
  10181. // For up and down arrows, simply increment or decrement the
  10182. // index. Note: in listview this will cause it to wrap columns
  10183. // which is fine as it is compatible with the file manager
  10184. //
  10185. // Assumes only one of these flags is set...
  10186. switch (vk)
  10187. {
  10188. case VK_LEFT:
  10189. if (ListView_IsReportView(plv))
  10190. {
  10191. ListView_ROnScroll(plv, (GetAsyncKeyState(VK_CONTROL) < 0) ? SB_PAGELEFT : SB_LINELEFT, 0, SB_HORZ);
  10192. }
  10193. else
  10194. iStart -= plv->cItemCol;
  10195. break;
  10196. case VK_RIGHT:
  10197. if (ListView_IsReportView(plv))
  10198. {
  10199. // Make this horizontally scroll the report view
  10200. ListView_ROnScroll(plv, (GetAsyncKeyState(VK_CONTROL) < 0) ? SB_PAGERIGHT : SB_LINERIGHT, 0, SB_HORZ);
  10201. }
  10202. else
  10203. iStart += plv->cItemCol;
  10204. break;
  10205. case VK_UP:
  10206. iStart--;
  10207. break;
  10208. case VK_DOWN:
  10209. iStart++;
  10210. break;
  10211. case VK_HOME:
  10212. iStart = 0;
  10213. break;
  10214. case VK_END:
  10215. iStart = iCount -1;
  10216. break;
  10217. case VK_NEXT:
  10218. if (ListView_IsReportView(plv))
  10219. {
  10220. i = iStart; // save away to make sure we dont go wrong way!
  10221. // First go to end of page...
  10222. iStart = (int)(((LONG)(plv->sizeClient.cy - (plv->cyItem)
  10223. - plv->yTop) + plv->ptlRptOrigin.y) / plv->cyItem);
  10224. // If Same item, increment by page size.
  10225. if (iStart <= i)
  10226. iStart = i + max(
  10227. (plv->sizeClient.cy - plv->yTop)/ plv->cyItem - 1,
  10228. 1);
  10229. if (iStart >= iCount)
  10230. iStart = iCount - 1;
  10231. }
  10232. else
  10233. {
  10234. // multiply by 2/3 to give a good feel.. when the item is mostly shown
  10235. // you want to go to the next column
  10236. dx = (plv->sizeClient.cx + (plv->cxItem*2)/3) / plv->cxItem;
  10237. if (!dx)
  10238. dx = 1;
  10239. iStart += plv->cItemCol * dx;
  10240. if (plv->cItemCol)
  10241. {
  10242. while (iStart >= iCount)
  10243. iStart -= plv->cItemCol;
  10244. }
  10245. }
  10246. break;
  10247. case VK_PRIOR:
  10248. if (ListView_IsReportView(plv))
  10249. {
  10250. i = iStart; // save away to make sure we dont go wrong way!
  10251. // First go to end of page...
  10252. iStart = (int)(plv->ptlRptOrigin.y / plv->cyItem);
  10253. // If Same item, increment by page size.
  10254. if (iStart >= i)
  10255. iStart = i - max(
  10256. (plv->sizeClient.cy - plv->yTop)/ plv->cyItem - 1,
  10257. 1);
  10258. if (iStart < 0)
  10259. iStart = 0;
  10260. }
  10261. else
  10262. {
  10263. dx = (plv->sizeClient.cx + (plv->cxItem*2)/3) / plv->cxItem;
  10264. if (!dx)
  10265. dx = 1;
  10266. iStart -= plv->cItemCol * dx;
  10267. if (plv->cItemCol)
  10268. {
  10269. while (iStart < 0)
  10270. iStart += plv->cItemCol;
  10271. }
  10272. }
  10273. break;
  10274. default:
  10275. return -1; // Out of range
  10276. }
  10277. // Make sure it is in range!.
  10278. if ((iStart >= 0) && (iStart < iCount))
  10279. return iStart;
  10280. else if (iCount == 1)
  10281. return 0;
  10282. else
  10283. return -1;
  10284. }
  10285. else
  10286. {
  10287. //
  10288. // Layout type view. we need to use the position of the items
  10289. // to figure out the next item
  10290. //
  10291. if (ListView_IsOwnerData(plv))
  10292. {
  10293. iStart = max(0, iStart);
  10294. // if it does not matches any of the entries in the case statement below
  10295. // this is done to skip the call back by the GetRects
  10296. //
  10297. if (vk != VK_LEFT &&
  10298. vk != VK_RIGHT &&
  10299. vk != VK_UP &&
  10300. vk != VK_DOWN &&
  10301. vk != VK_HOME &&
  10302. vk != VK_END &&
  10303. vk != VK_NEXT &&
  10304. vk != VK_PRIOR)
  10305. {
  10306. return -1;
  10307. }
  10308. ListView_GetRects(plv, iStart, QUERY_DEFAULT, &rcFocus, NULL, NULL, NULL);
  10309. }
  10310. else
  10311. {
  10312. if (iStart != -1)
  10313. {
  10314. ListView_GetRects(plv, iStart, QUERY_DEFAULT, &rcFocus, NULL, NULL, NULL);
  10315. }
  10316. }
  10317. switch (vk)
  10318. {
  10319. // For standard arrow keys just fall out of here.
  10320. case VK_LEFT:
  10321. case VK_RIGHT:
  10322. case VK_UP:
  10323. case VK_DOWN:
  10324. if (ListView_IsOwnerData(plv))
  10325. {
  10326. break;
  10327. }
  10328. else
  10329. {
  10330. if (iStart != -1)
  10331. {
  10332. // all keys map to VK_HOME except VK_END
  10333. break;
  10334. }
  10335. // Fall through
  10336. vk = VK_HOME;
  10337. }
  10338. case VK_HOME:
  10339. rcFocus.left = - plv->ptOrigin.x;
  10340. rcFocus.top = - plv->ptOrigin.y;
  10341. break;
  10342. case VK_END:
  10343. rcFocus.left = plv->rcView.right;
  10344. rcFocus.top = plv->rcView.bottom;
  10345. break;
  10346. case VK_NEXT:
  10347. rcFocus.top += plv->sizeClient.cy;
  10348. vk = VK_UP;
  10349. break;
  10350. case VK_PRIOR:
  10351. vk = VK_DOWN;
  10352. rcFocus.top -= plv->sizeClient.cy;
  10353. break;
  10354. default:
  10355. return -1; // Out of range
  10356. }
  10357. return ListView_IFindNearestItem(plv, rcFocus.left, rcFocus.top, vk);
  10358. }
  10359. }
  10360. int ListView_OnGetNextItem(LV* plv, int i, UINT flags)
  10361. {
  10362. int iStart = i;
  10363. int cItemMax = ListView_Count(plv);
  10364. // Note that -1 is a valid starting point
  10365. if (i < -1 || i >= cItemMax)
  10366. return -1;
  10367. if (ListView_IsOwnerData(plv))
  10368. {
  10369. if (flags & (LVNI_CUT | LVNI_DROPHILITED | LVNI_PREVIOUS))
  10370. {
  10371. return -1;
  10372. }
  10373. }
  10374. if (flags & LVNI_FOCUSED)
  10375. {
  10376. // we know which item is focused, jump right to it.
  10377. // but we have to mimick the code below exactly for compat:
  10378. // if directional bits are set, they take precedence.
  10379. if (!(flags & (LVNI_ABOVE | LVNI_BELOW | LVNI_TORIGHT | LVNI_TOLEFT)))
  10380. {
  10381. // there are no more focused items after iFocus
  10382. if (i >= plv->iFocus)
  10383. return -1;
  10384. // subtract one here -- we increment it below
  10385. i = plv->iFocus - 1;
  10386. }
  10387. }
  10388. while (TRUE)
  10389. {
  10390. if (flags & (LVNI_ABOVE | LVNI_BELOW | LVNI_TORIGHT | LVNI_TOLEFT))
  10391. {
  10392. UINT vk;
  10393. if (flags & LVNI_ABOVE)
  10394. vk = VK_UP;
  10395. else if (flags & LVNI_BELOW)
  10396. vk = VK_DOWN;
  10397. else if (flags & LVNI_TORIGHT)
  10398. vk = VK_RIGHT;
  10399. else
  10400. vk = VK_LEFT;
  10401. if (i != -1)
  10402. i = ListView_Arrow(plv, i, vk);
  10403. if (i == -1)
  10404. return i;
  10405. }
  10406. else
  10407. {
  10408. i++;
  10409. if (i == cItemMax)
  10410. return -1;
  10411. }
  10412. // See if any other restrictions are set
  10413. if (flags & ~(LVNI_ABOVE | LVNI_BELOW | LVNI_TORIGHT | LVNI_TOLEFT))
  10414. {
  10415. WORD wItemState;
  10416. if (ListView_IsOwnerData(plv))
  10417. {
  10418. if (flags & LVNI_FOCUSED)
  10419. {
  10420. // we check LVNI_FOCUSED before the loop, so i == iFocus
  10421. ASSERT(i == plv->iFocus && i != -1);
  10422. if (flags & LVNI_SELECTED)
  10423. {
  10424. if (plv->plvrangeSel->lpVtbl->IsSelected(plv->plvrangeSel, i) != S_OK)
  10425. {
  10426. i = -1;
  10427. }
  10428. }
  10429. }
  10430. else if (flags & LVNI_SELECTED)
  10431. {
  10432. i = max(i, 0);
  10433. plv->plvrangeSel->lpVtbl->NextSelected(plv->plvrangeSel, i, &i);
  10434. }
  10435. else
  10436. {
  10437. i = -1;
  10438. }
  10439. }
  10440. else
  10441. {
  10442. {
  10443. LISTITEM* pitem = ListView_FastGetItemPtr(plv, i);
  10444. wItemState = pitem->state;
  10445. }
  10446. // for LVNI_FOCUSED, we start at the LVIS_FOCUSED element, if we're
  10447. // not on that element, one of the below continues was hit, so
  10448. // we'll never find the element. bail out early.
  10449. if ((flags & LVNI_FOCUSED) && !(wItemState & LVIS_FOCUSED))
  10450. {
  10451. ASSERT(i == plv->iFocus || i == plv->iFocus+1);
  10452. return -1;
  10453. }
  10454. if (((flags & LVNI_SELECTED) && !(wItemState & LVIS_SELECTED)) ||
  10455. ((flags & LVNI_CUT) && !(wItemState & LVIS_CUT)) ||
  10456. ((flags & LVNI_DROPHILITED) && !(wItemState & LVIS_DROPHILITED)))
  10457. {
  10458. if (i != iStart)
  10459. continue;
  10460. else
  10461. {
  10462. // we've looped and we can't find anything to fit this criteria
  10463. return -1;
  10464. }
  10465. }
  10466. }
  10467. }
  10468. return i;
  10469. }
  10470. }
  10471. int ListView_CompareString(LV* plv, int i, LPCTSTR pszFind, UINT flags, int iLen)
  10472. {
  10473. // REARCHITECT: non protected globals
  10474. int cb;
  10475. TCHAR ach[CCHLABELMAX];
  10476. LV_ITEM item;
  10477. ASSERT(!ListView_IsOwnerData(plv));
  10478. ASSERT(pszFind);
  10479. item.iItem = i;
  10480. item.iSubItem = 0;
  10481. item.mask = LVIF_TEXT;
  10482. item.pszText = ach;
  10483. item.cchTextMax = ARRAYSIZE(ach);
  10484. ListView_OnGetItem(plv, &item);
  10485. if (!(flags & (LVFI_PARTIAL | LVFI_SUBSTRING)))
  10486. return lstrcmpi(item.pszText, pszFind);
  10487. // FEATURE: LVFI_SUBSTRING is not really implemented yet.
  10488. cb = lstrlen(pszFind);
  10489. if (iLen && (cb > iLen))
  10490. {
  10491. cb = iLen;
  10492. }
  10493. //
  10494. // If the sub strings not equal then return the ordering based
  10495. // on the entire string.
  10496. //
  10497. #ifndef UNIX
  10498. return IntlStrEqNI(item.pszText, pszFind, cb) ? 0 : lstrcmp(item.pszText, pszFind);
  10499. #else
  10500. return IntlStrEqNI(item.pszText, pszFind, cb) ? 0 : lstrcmpi(item.pszText, pszFind);
  10501. #endif
  10502. }
  10503. #ifdef UNICODE
  10504. int ListView_OnFindItemA(LV* plv, int iStart, LV_FINDINFOA * plvfi)
  10505. {
  10506. LPWSTR pszW = NULL;
  10507. LPCSTR pszC = NULL;
  10508. int iRet;
  10509. //HACK ALERT -- this code assumes that LV_FINDINFOA is exactly the same
  10510. // as LV_FINDINFOW except for the pointer to the string.
  10511. COMPILETIME_ASSERT(sizeof(LV_FINDINFOA) == sizeof(LV_FINDINFOW));
  10512. if (!plvfi)
  10513. return -1;
  10514. if (!(plvfi->flags & LVFI_PARAM) && !(plvfi->flags & LVFI_NEARESTXY))
  10515. {
  10516. pszC = plvfi->psz;
  10517. if ((pszW = ProduceWFromA(plv->ci.uiCodePage, pszC)) == NULL)
  10518. return -1;
  10519. plvfi->psz = (LPSTR)pszW;
  10520. }
  10521. iRet = ListView_OnFindItem(plv, iStart, (const LV_FINDINFO *)plvfi);
  10522. if (pszW != NULL)
  10523. {
  10524. plvfi->psz = pszC;
  10525. FreeProducedString(pszW);
  10526. }
  10527. return iRet;
  10528. }
  10529. #endif
  10530. int ListView_OnFindItem(LV* plv, int iStart, const LV_FINDINFO* plvfi)
  10531. {
  10532. int i;
  10533. int j;
  10534. int cItem;
  10535. UINT flags;
  10536. if (!plvfi)
  10537. return -1;
  10538. if (plvfi->flags & LVFI_NEARESTXY)
  10539. {
  10540. if (ListView_IsSlotView(plv))
  10541. {
  10542. return ListView_IFindNearestItem(plv, plvfi->pt.x, plvfi->pt.y, plvfi->vkDirection);
  10543. }
  10544. else
  10545. return -1;
  10546. }
  10547. // Note that -1 is a valid starting point
  10548. if (iStart < -1 || iStart >= ListView_Count(plv))
  10549. return -1;
  10550. if (ListView_IsOwnerData(plv))
  10551. {
  10552. // call back to owner for search
  10553. return (int) ListView_RequestFindItem(plv, plvfi, iStart + 1);
  10554. }
  10555. else
  10556. {
  10557. flags = plvfi->flags;
  10558. i = iStart;
  10559. cItem = ListView_Count(plv);
  10560. if (flags & LVFI_PARAM)
  10561. {
  10562. LPARAM lParam = plvfi->lParam;
  10563. // Lisearch with wraparound...
  10564. //
  10565. for (j = cItem; j-- != 0;)
  10566. {
  10567. ++i;
  10568. if (i == cItem)
  10569. {
  10570. if (flags & LVFI_WRAP)
  10571. i = 0;
  10572. else
  10573. break;
  10574. }
  10575. if (ListView_FastGetItemPtr(plv, i)->lParam == lParam)
  10576. return i;
  10577. }
  10578. }
  10579. else // if (flags & (LVFI_STRING | LVFI_SUBSTRING | LVFI_PARTIAL))
  10580. {
  10581. LPCTSTR pszFind = plvfi->psz;
  10582. if (!pszFind)
  10583. return -1;
  10584. if (plv->ci.style & (LVS_SORTASCENDING | LVS_SORTDESCENDING))
  10585. return ListView_LookupString(plv, pszFind, flags, i + 1);
  10586. for (j = cItem; j-- != 0;)
  10587. {
  10588. ++i;
  10589. if (i == cItem)
  10590. {
  10591. if (flags & LVFI_WRAP)
  10592. i = 0;
  10593. else
  10594. break;
  10595. }
  10596. if (ListView_CompareString(plv,
  10597. i,
  10598. pszFind,
  10599. (flags & (LVFI_PARTIAL | LVFI_SUBSTRING)), 0) == 0)
  10600. {
  10601. return i;
  10602. }
  10603. }
  10604. }
  10605. }
  10606. return -1;
  10607. }
  10608. BOOL ListView_OnGetItemRect(LV* plv, int i, RECT* prc)
  10609. {
  10610. LPRECT pRects[LVIR_MAX];
  10611. // validate parameters
  10612. if (!ListView_IsValidItemNumber(plv, i))
  10613. {
  10614. RIPMSG(0, "LVM_GETITEMRECT: invalid index %d", i);
  10615. return FALSE;
  10616. }
  10617. if (!prc || prc->left >= LVIR_MAX || prc->left < 0)
  10618. {
  10619. RIPMSG(0, "LVM_GETITEMRECT: invalid rect pointer");
  10620. return FALSE;
  10621. }
  10622. pRects[0] = NULL;
  10623. pRects[1] = NULL;
  10624. pRects[2] = NULL;
  10625. pRects[3] = NULL;
  10626. pRects[prc->left] = prc;
  10627. ListView_GetRects(plv, i, QUERY_DEFAULT, pRects[LVIR_ICON], pRects[LVIR_LABEL],
  10628. pRects[LVIR_BOUNDS], pRects[LVIR_SELECTBOUNDS]);
  10629. return TRUE;
  10630. }
  10631. //
  10632. // in:
  10633. // plv
  10634. // iItem MUST be a valid item index (in range)
  10635. // out:
  10636. // prcIcon icon bounding rect
  10637. // prcLabel label text bounding rect, for details this is the first column
  10638. // prcBounds entire item (all text and icon), including columns in details
  10639. // prcSelectionBounds union of icon and label rects, does NOT include columns
  10640. // in details view
  10641. // REARCHITECT raymondc - Need to pass an HDC parameter for measurement
  10642. // since sometimes we do this while painting
  10643. // This returns rects in Window Coordinates
  10644. void ListView_GetRects(LV* plv, int iItem, UINT fQueryLabelRects,
  10645. RECT* prcIcon, RECT* prcLabel, RECT* prcBounds,
  10646. RECT* prcSelectBounds)
  10647. {
  10648. ASSERT(plv);
  10649. if (ListView_IsReportView(plv))
  10650. {
  10651. ListView_RGetRects(plv, iItem, prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10652. }
  10653. else if (ListView_IsListView(plv))
  10654. {
  10655. ListView_LGetRects(plv, iItem, prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10656. }
  10657. else
  10658. {
  10659. if (ListView_IsOwnerData(plv))
  10660. {
  10661. RECT rcIcon;
  10662. RECT rcTextBounds;
  10663. LISTITEM item;
  10664. if (ListView_IsIconView(plv))
  10665. ListView_IGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, &item, FALSE);
  10666. else if (ListView_IsSmallView(plv))
  10667. ListView_SGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, &item, FALSE);
  10668. else if (ListView_IsTileView(plv))
  10669. ListView_TGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, &item, FALSE);
  10670. if (prcIcon)
  10671. *prcIcon = rcIcon;
  10672. if (prcLabel)
  10673. *prcLabel = rcTextBounds;
  10674. if (prcBounds)
  10675. UnionRect(prcBounds, &rcIcon, &rcTextBounds);
  10676. if (prcSelectBounds)
  10677. UnionRect(prcSelectBounds, &rcIcon, &rcTextBounds);
  10678. }
  10679. else
  10680. {
  10681. if (iItem >= ListView_Count(plv))
  10682. {
  10683. return;
  10684. }
  10685. else
  10686. {
  10687. LISTITEM *pitem = ListView_FastGetItemPtr(plv, iItem);
  10688. if (pitem->cyFoldedLabel == SRECOMPUTE)
  10689. {
  10690. _ListView_RecomputeLabelSize(plv, pitem, iItem, NULL, FALSE);
  10691. }
  10692. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem, fQueryLabelRects,
  10693. prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10694. }
  10695. }
  10696. }
  10697. }
  10698. void ListView_GetRectsOwnerData(LV* plv, int iItem,
  10699. RECT* prcIcon, RECT* prcLabel, RECT* prcBounds,
  10700. RECT* prcSelectBounds, LISTITEM* pitem)
  10701. {
  10702. ASSERT(plv);
  10703. ASSERT(ListView_IsOwnerData(plv));
  10704. if (ListView_IsReportView(plv))
  10705. {
  10706. ListView_RGetRects(plv, iItem, prcIcon, prcLabel, prcBounds,
  10707. prcSelectBounds);
  10708. }
  10709. else if (ListView_IsListView(plv))
  10710. {
  10711. ListView_LGetRects(plv, iItem, prcIcon, prcLabel, prcBounds,
  10712. prcSelectBounds);
  10713. }
  10714. else
  10715. {
  10716. RECT rcIcon;
  10717. RECT rcTextBounds;
  10718. if (ListView_IsIconView(plv))
  10719. ListView_IGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, pitem, TRUE);
  10720. else if (ListView_IsSmallView(plv))
  10721. ListView_SGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, pitem, TRUE);
  10722. else if (ListView_IsTileView(plv))
  10723. ListView_TGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, pitem, TRUE);
  10724. // Don't need to check for folding here, as will have been handled in user data
  10725. // rectangle fetching functions.
  10726. if (prcIcon)
  10727. *prcIcon = rcIcon;
  10728. if (prcLabel)
  10729. *prcLabel = rcTextBounds;
  10730. if (prcBounds)
  10731. UnionRect(prcBounds, &rcIcon, &rcTextBounds);
  10732. if (prcSelectBounds)
  10733. UnionRect(prcSelectBounds, &rcIcon, &rcTextBounds);
  10734. }
  10735. }
  10736. BOOL ListView_OnRedrawItems(LV* plv, int iFirst, int iLast)
  10737. {
  10738. int iCount = ListView_Count(plv);
  10739. if (iFirst < iCount)
  10740. {
  10741. if (iLast >= iCount)
  10742. iLast = iCount - 1;
  10743. while (iFirst <= iLast)
  10744. ListView_InvalidateItem(plv, iFirst++, FALSE, RDW_INVALIDATE | RDW_ERASE);
  10745. }
  10746. return TRUE;
  10747. }
  10748. // fSelectionOnly use the selection bounds only, ie. don't include
  10749. // columns in invalidation if in details view
  10750. //
  10751. void ListView_InvalidateItemEx(LV* plv, int iItem, BOOL fSelectionOnly,
  10752. UINT fRedraw, UINT maskChanged)
  10753. {
  10754. RECT rc;
  10755. LPRECT prcIcon;
  10756. LPRECT prcLabel;
  10757. LPRECT prcBounds;
  10758. LPRECT prcSelectBounds;
  10759. LISTITEM* pitem = NULL;
  10760. if (iItem == -1)
  10761. return;
  10762. // Ok if NULL
  10763. if (plv->hdpa)
  10764. pitem = ListView_GetItemPtr(plv, iItem);
  10765. prcIcon = prcLabel = prcBounds = prcSelectBounds = NULL;
  10766. // if we're in owner draw mode, and there's been a new font,
  10767. // we don't really know what the selection bounds is, so always use the bounds
  10768. // in that case... unless we're in fullrowselect mode
  10769. if (ListView_IsOwnerData(plv) && plv->flags & LVF_CUSTOMFONT &&
  10770. !ListView_FullRowSelect(plv))
  10771. {
  10772. fSelectionOnly = FALSE;
  10773. }
  10774. // if we're owner draw, there's no such thing as selection only
  10775. if (plv->ci.style & LVS_OWNERDRAWFIXED)
  10776. fSelectionOnly = FALSE;
  10777. if (fSelectionOnly)
  10778. {
  10779. // In report mode non-fullrowselect,
  10780. // we have to use the full label rectangle rather
  10781. // than just the selection bounds, since the stuff outside the
  10782. // rectangle might need redrawing, too.
  10783. if (ListView_IsReportView(plv) && !ListView_FullRowSelect(plv))
  10784. prcLabel = &rc;
  10785. else
  10786. prcSelectBounds = &rc;
  10787. }
  10788. else
  10789. {
  10790. // if _only_the_text_ or _only_the_image_ changed then limit the redraw
  10791. switch (maskChanged)
  10792. {
  10793. case LVIF_IMAGE:
  10794. prcIcon = &rc;
  10795. break;
  10796. case LVIF_TEXT:
  10797. prcLabel = &rc;
  10798. break;
  10799. default:
  10800. prcBounds = &rc;
  10801. break;
  10802. }
  10803. }
  10804. if (ListView_RedrawEnabled(plv))
  10805. {
  10806. ListView_GetRects(plv, iItem, QUERY_DEFAULT,
  10807. prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10808. if (RECTS_IN_SIZE(plv->sizeClient, rc))
  10809. {
  10810. if (ListView_IsBorderSelect(plv))
  10811. {
  10812. InflateRect(&rc, 4 + g_cxIconMargin, 4 + g_cyIconMargin); // account for selection border and seperation since drawing otside of icon
  10813. fRedraw |= RDW_ERASE;
  10814. }
  10815. // Affects only allowed if dubble buffering
  10816. if (ListView_IsDoubleBuffer(plv))
  10817. {
  10818. if ((pitem && (pitem->state & LVIS_GLOW)))
  10819. {
  10820. InflateRect(&rc, GLOW_EXPAND, GLOW_EXPAND);
  10821. fRedraw |= RDW_ERASE;
  10822. }
  10823. }
  10824. ListView_DebugDrawInvalidRegion(plv, &rc, NULL);
  10825. RedrawWindow(plv->ci.hwnd, &rc, NULL, fRedraw);
  10826. }
  10827. }
  10828. else
  10829. {
  10830. // if we're not visible, we'll get a full
  10831. // erase bk when we do become visible, so only do this stuff when
  10832. // we're on setredraw false
  10833. if (!(plv->flags & LVF_REDRAW))
  10834. {
  10835. // if we're invalidating that's new (thus hasn't been painted yet)
  10836. // blow it off
  10837. if ((plv->iFirstChangedNoRedraw != -1) &&
  10838. (iItem >= plv->iFirstChangedNoRedraw))
  10839. {
  10840. return;
  10841. }
  10842. ListView_GetRects(plv, iItem, QUERY_DEFAULT,
  10843. prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10844. // Affects only allowed if dubble buffering
  10845. if (ListView_IsDoubleBuffer(plv))
  10846. {
  10847. if (pitem && (pitem->state & LVIS_GLOW))
  10848. {
  10849. InflateRect(&rc, GLOW_EXPAND, GLOW_EXPAND);
  10850. fRedraw |= RDW_ERASE;
  10851. }
  10852. }
  10853. if (ListView_IsBorderSelect(plv))
  10854. {
  10855. InflateRect(&rc, 4 + g_cxIconMargin, 4 + g_cyIconMargin); // account for selection border and seperation since drawing otside of icon
  10856. fRedraw |= RDW_ERASE;
  10857. }
  10858. // if it had the erase bit, add it to our region
  10859. if (RECTS_IN_SIZE(plv->sizeClient, rc))
  10860. {
  10861. HRGN hrgn = CreateRectRgnIndirect(&rc);
  10862. ListView_InvalidateRegion(plv, hrgn);
  10863. if (fRedraw & RDW_ERASE)
  10864. plv->flags |= LVF_ERASE;
  10865. }
  10866. }
  10867. }
  10868. }
  10869. // this returns BF_* flags to indicate which if any edge the item I is touching
  10870. // or crossing...
  10871. UINT LV_IsItemOnViewEdge(LV* plv, LISTITEM* pitem)
  10872. {
  10873. RECT rcItem;
  10874. UINT uRet = 0;
  10875. // as far as rcView goes, unfolded label rects determine edge-ness
  10876. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem, QUERY_RCVIEW|QUERY_UNFOLDED,
  10877. NULL, NULL, &rcItem, NULL);
  10878. // translate from window coordinates to listview coordinate
  10879. OffsetRect(&rcItem, plv->ptOrigin.x, plv->ptOrigin.y);
  10880. // include the rcView buffer
  10881. ListView_AddViewRectBuffer(plv, &rcItem);
  10882. if (rcItem.right >= plv->rcView.right)
  10883. uRet |= BF_RIGHT;
  10884. if (rcItem.left <= plv->rcView.left)
  10885. uRet |= BF_LEFT;
  10886. if (rcItem.top <= plv->rcView.top)
  10887. uRet |= BF_TOP;
  10888. if (rcItem.bottom >= plv->rcView.bottom)
  10889. uRet |= BF_BOTTOM;
  10890. return uRet;
  10891. }
  10892. // Move pitem to x,y
  10893. // Update rcView to accomodate this if we can, or mark rcView for recomputation
  10894. void LV_AdjustViewRectOnMove(LV* plv, LISTITEM *pitem, int x, int y)
  10895. {
  10896. plv->iFreeSlot = -1; // The "free slot" cache is no good once an item moves
  10897. // if we have to recompute anyways, don't bother
  10898. if (!ListView_IsOwnerData(plv))
  10899. {
  10900. if ((plv->rcView.left != RECOMPUTE) &&
  10901. x != RECOMPUTE && y != RECOMPUTE &&
  10902. pitem->cyFoldedLabel != SRECOMPUTE)
  10903. {
  10904. RECT rcClient, rcAfter;
  10905. RECT rcView = plv->rcView;
  10906. // Our optimized move-adjust-rcView must maintain this, make sure it's true before we even start:
  10907. ASSERT(ListView_ValidatercView(plv, &plv->rcView, FALSE));
  10908. ListView_GetClientRect(plv, &rcClient, TRUE, NULL);
  10909. ASSERT(ListView_ValidateScrollPositions(plv, &rcClient));
  10910. if (pitem->pt.x != RECOMPUTE)
  10911. {
  10912. UINT uEdges;
  10913. uEdges = LV_IsItemOnViewEdge(plv, pitem);
  10914. pitem->pt.x = x;
  10915. pitem->pt.y = y;
  10916. // before and after the move, they need to be touching the
  10917. // same edges or not at all
  10918. if (uEdges != LV_IsItemOnViewEdge(plv, pitem))
  10919. {
  10920. goto FullRecompute;
  10921. }
  10922. }
  10923. else
  10924. {
  10925. // if the position wasn't set before
  10926. // we just need to find out what it is afterwards and
  10927. // enlarge the view... we don't need to shrink it
  10928. pitem->pt.x = x;
  10929. pitem->pt.y = y;
  10930. }
  10931. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem, QUERY_RCVIEW|QUERY_UNFOLDED,
  10932. NULL, NULL, &rcAfter, NULL);
  10933. // translate from window coordinates to listview coordinates
  10934. OffsetRect(&rcAfter, plv->ptOrigin.x, plv->ptOrigin.y);
  10935. // include the rcView buffer
  10936. ListView_AddViewRectBuffer(plv, &rcAfter);
  10937. // if we make it here, we just have to make sure the new view rect
  10938. // encompases this new item
  10939. UnionRect(&rcView, &rcView, &rcAfter);
  10940. DebugMsg(TF_LISTVIEW, TEXT("Score! (%d %d %d %d) was (%d %d %d %d)"),
  10941. rcView.left, rcView.top, rcView.right, rcView.bottom,
  10942. plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
  10943. // Our optimized move-adjust-rcView must maintain this:
  10944. ASSERT(ListView_ValidatercView(plv, &rcView, FALSE));
  10945. plv->rcView = rcView;
  10946. // make sure our scroll positions are correct
  10947. if (ListView_IsIScrollView(plv))
  10948. ListView_FixIScrollPositions(plv, FALSE, &rcClient);
  10949. ASSERT(ListView_ValidateScrollPositions(plv, &rcClient));
  10950. }
  10951. else
  10952. {
  10953. FullRecompute:
  10954. plv->rcView.left = RECOMPUTE;
  10955. }
  10956. }
  10957. DebugMsg(TF_LISTVIEW, TEXT("LV -- AdjustViewRect pitem %d -- (%x, %x)"),
  10958. pitem,
  10959. pitem->pt.x, pitem->pt.y);
  10960. pitem->pt.x = x;
  10961. pitem->pt.y = y;
  10962. // Compute the workarea of this item if applicable
  10963. ListView_FindWorkArea(plv, pitem->pt, &(pitem->iWorkArea));
  10964. }
  10965. BOOL ListView_OnSetItemPosition(LV* plv, int i, int x, int y)
  10966. {
  10967. LISTITEM* pitem;
  10968. if (plv->fGroupView)
  10969. return FALSE;
  10970. if (ListView_IsListView(plv))
  10971. return FALSE;
  10972. if (ListView_IsOwnerData(plv))
  10973. {
  10974. RIPMSG(0, "LVM_SETITEMPOSITION: Invalid for owner-data listview");
  10975. return FALSE;
  10976. }
  10977. pitem = ListView_GetItemPtr(plv, i);
  10978. if (!pitem)
  10979. return FALSE;
  10980. //
  10981. // this is a hack to fix a bug in OLE drag/drop loop
  10982. //
  10983. if (x >= 0xF000 && x < 0x10000)
  10984. {
  10985. DebugMsg(TF_LISTVIEW, TEXT("LV -- On SetItemPosition fixing truncated negative number 0x%08X"), x);
  10986. x = x - 0x10000;
  10987. }
  10988. if (y >= 0xF000 && y < 0x10000)
  10989. {
  10990. DebugMsg(TF_LISTVIEW, TEXT("LV -- On SetItemPosition fixing truncated negative number 0x%08X"), y);
  10991. y = y - 0x10000;
  10992. }
  10993. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  10994. if (pitem->cyFoldedLabel == SRECOMPUTE)
  10995. {
  10996. _ListView_RecomputeLabelSize(plv, pitem, i, NULL, FALSE);
  10997. }
  10998. // erase old
  10999. if (y != pitem->pt.y || x != pitem->pt.x)
  11000. {
  11001. // Don't invalidate if it hasn't got a position yet
  11002. if (pitem->pt.y != RECOMPUTE)
  11003. {
  11004. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
  11005. }
  11006. else if (plv->uUnplaced)
  11007. {
  11008. // this means an unplaced item got placed
  11009. plv->uUnplaced--;
  11010. if (!plv->uUnplaced)
  11011. {
  11012. MSG msg;
  11013. // if this is now 0, pull out the postmessage
  11014. PeekMessage(&msg, plv->ci.hwnd, LVMI_PLACEITEMS, LVMI_PLACEITEMS, PM_REMOVE);
  11015. }
  11016. }
  11017. if (y == RECOMPUTE)
  11018. {
  11019. // if they're setting the new position to be a "any open spot" post that we
  11020. // need to calc this later
  11021. if (!plv->uUnplaced)
  11022. {
  11023. PostMessage(plv->ci.hwnd, LVMI_PLACEITEMS, 0, 0);
  11024. }
  11025. plv->uUnplaced++;
  11026. }
  11027. }
  11028. DebugMsg(TF_LISTVIEW, TEXT("LV -- On SetItemPosition %d %d %d %d -- (%x, %x)"),
  11029. plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom,
  11030. pitem->pt.x, pitem->pt.y);
  11031. LV_AdjustViewRectOnMove(plv, pitem, x, y);
  11032. // and draw at new position
  11033. ListView_RecalcRegion(plv, FALSE, TRUE);
  11034. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE);
  11035. // If autoarrange is turned on, do it now...
  11036. if (ListView_RedrawEnabled(plv))
  11037. {
  11038. ListView_ArrangeOrSnapToGrid(plv);
  11039. if (!(plv->ci.style & LVS_AUTOARRANGE))
  11040. ListView_UpdateScrollBars(plv);
  11041. }
  11042. if (!(plv->ci.style & LVS_AUTOARRANGE))
  11043. {
  11044. plv->fIconsPositioned = TRUE;
  11045. }
  11046. return TRUE;
  11047. }
  11048. BOOL ListView_OnGetItemPosition(LV* plv, int i, POINT* ppt)
  11049. {
  11050. LISTITEM* pitem;
  11051. //
  11052. // This needs to handle all views as it is used to figure out
  11053. // where the item is during drag and drop and the like
  11054. //
  11055. if (!ppt)
  11056. {
  11057. RIPMSG(0, "LVM_GETITEMPOSITION: Invalid ppt = NULL");
  11058. return FALSE;
  11059. }
  11060. if (ListView_IsListView(plv) || ListView_IsReportView(plv)
  11061. || ListView_IsOwnerData(plv))
  11062. {
  11063. RECT rcIcon;
  11064. ListView_GetRects(plv, i, QUERY_DEFAULT, &rcIcon, NULL, NULL, NULL);
  11065. ppt->x = rcIcon.left;
  11066. ppt->y = rcIcon.top;
  11067. }
  11068. else
  11069. {
  11070. pitem = ListView_GetItemPtr(plv, i);
  11071. if (!pitem)
  11072. return FALSE;
  11073. if (pitem->pt.x == RECOMPUTE)
  11074. ListView_Recompute(plv);
  11075. ppt->x = pitem->pt.x;
  11076. ppt->y = pitem->pt.y;
  11077. }
  11078. return TRUE;
  11079. }
  11080. BOOL ListView_OnGetOrigin(LV* plv, POINT* ppt)
  11081. {
  11082. if (!ppt)
  11083. {
  11084. DebugMsg(DM_ERROR, TEXT("ListView_OnGetOrigin: ppt is NULL"));
  11085. return FALSE;
  11086. }
  11087. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  11088. return FALSE;
  11089. *ppt = plv->ptOrigin;
  11090. return TRUE;
  11091. }
  11092. int ListView_OnGetStringWidthA(LV* plv, LPCSTR psz, HDC hdc)
  11093. {
  11094. LPWSTR pszW = NULL;
  11095. int iRet;
  11096. if (!psz)
  11097. return 0;
  11098. if ((psz != NULL) && (pszW = ProduceWFromA(plv->ci.uiCodePage, psz)) == NULL)
  11099. return 0;
  11100. iRet = ListView_OnGetStringWidth(plv, pszW, hdc);
  11101. FreeProducedString(pszW);
  11102. return iRet;
  11103. }
  11104. int ListView_OnGetStringWidth(LV* plv, LPCTSTR psz, HDC hdc)
  11105. {
  11106. SIZE siz;
  11107. HDC hdcFree = NULL;
  11108. HFONT hfontPrev;
  11109. if (!psz || psz == LPSTR_TEXTCALLBACK)
  11110. return 0;
  11111. if (!hdc)
  11112. {
  11113. hdcFree = hdc = GetDC(plv->ci.hwnd);
  11114. }
  11115. hfontPrev = SelectFont(hdc, plv->hfontLabel);
  11116. GetTextExtentPoint(hdc, psz, lstrlen(psz), &siz);
  11117. SelectFont(hdc, hfontPrev);
  11118. if (hdcFree)
  11119. {
  11120. ReleaseDC(plv->ci.hwnd, hdcFree);
  11121. }
  11122. return siz.cx;
  11123. }
  11124. int ListView_OnGetColumnWidth(LV* plv, int iCol)
  11125. {
  11126. if (ListView_IsReportView(plv))
  11127. return ListView_RGetColumnWidth(plv, iCol);
  11128. else if (ListView_IsListView(plv))
  11129. return plv->cxItem;
  11130. return 0;
  11131. }
  11132. BOOL ListView_ISetColumnWidth(LV* plv, int iCol, int cx, BOOL fExplicit)
  11133. {
  11134. if (ListView_IsListView(plv))
  11135. {
  11136. if (iCol != 0 || cx <= 0)
  11137. return FALSE;
  11138. // if it's different and this is an explicit set, or we've never set it explicitly
  11139. if (plv->cxItem != cx && (fExplicit || !(plv->flags & LVF_COLSIZESET)))
  11140. {
  11141. // REVIEW: Should optimize what gets invalidated here...
  11142. plv->cxItem = cx;
  11143. if (fExplicit)
  11144. plv->flags |= LVF_COLSIZESET; // Set the fact that we explictly set size!.
  11145. if (ListView_IsLabelTip(plv))
  11146. {
  11147. // A truncated label may have been exposed or vice versa.
  11148. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  11149. }
  11150. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  11151. ListView_UpdateScrollBars(plv);
  11152. }
  11153. return TRUE;
  11154. }
  11155. else if (ListView_IsReportView(plv))
  11156. {
  11157. if (ListView_IsLabelTip(plv))
  11158. {
  11159. // A truncated label may have been exposed or vice versa.
  11160. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  11161. }
  11162. return ListView_RSetColumnWidth(plv, iCol, cx);
  11163. }
  11164. else
  11165. {
  11166. if (cx && plv->cxItem != cx && (fExplicit || !(plv->flags & LVF_COLSIZESET)))
  11167. {
  11168. // REVIEW: Should optimize what gets invalidated here...
  11169. plv->cxItem = cx;
  11170. if (fExplicit)
  11171. plv->flags |= LVF_COLSIZESET; // Set the fact that we explictly set size!.
  11172. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  11173. ListView_UpdateScrollBars(plv);
  11174. }
  11175. // BUG-FOR-BUG COMPATIBILITY: IE4 accidentally returned FALSE here.
  11176. }
  11177. return FALSE;
  11178. }
  11179. void DrawGradiantLine(HDC hdc, RECT* prcText, RECT* prcGroup)
  11180. {
  11181. COLORREF cr1 = GetSysColor(COLOR_GRADIENTACTIVECAPTION);
  11182. COLORREF cr2 = GetSysColor(COLOR_WINDOW);
  11183. TRIVERTEX pt[2];
  11184. GRADIENT_RECT gr;
  11185. RECT rc = {prcGroup->left, prcText->bottom-1, prcGroup->left + GRADIENT_WIDTH, prcText->bottom};
  11186. pt[0].x = rc.left;
  11187. pt[0].y = rc.top;
  11188. pt[1].x = rc.right;
  11189. pt[1].y = rc.bottom;
  11190. pt[0].Red = GetRValue(cr1) << 8;
  11191. pt[0].Green = GetGValue(cr1) << 8;
  11192. pt[0].Blue = GetBValue(cr1) << 8;
  11193. pt[0].Alpha = 0xFF00;
  11194. pt[1].Red = GetRValue(cr2) << 8;
  11195. pt[1].Green = GetGValue(cr2) << 8;
  11196. pt[1].Blue = GetBValue(cr2) << 8;
  11197. pt[1].Alpha = 0x0000;
  11198. gr.UpperLeft = 0;
  11199. gr.LowerRight = 1;
  11200. GdiGradientFill(hdc, pt, 2, &gr, 1, GRADIENT_FILL_RECT_H);
  11201. }
  11202. void ListView_Redraw(LV* plv, HDC hdc, RECT* prcClip)
  11203. {
  11204. int i = 0;
  11205. int cItem = ListView_Count(plv);
  11206. NMCUSTOMDRAW nmcd;
  11207. LVDRAWITEM lvdi = {0};
  11208. SetBkMode(hdc, TRANSPARENT);
  11209. SelectFont(hdc, plv->hfontLabel);
  11210. nmcd.hdc = hdc;
  11211. nmcd.rc = *prcClip;
  11212. plv->ci.dwCustom = CICustomDrawNotify(&plv->ci, CDDS_PREPAINT, &nmcd);
  11213. if (!(plv->ci.dwCustom & CDRF_SKIPDEFAULT))
  11214. {
  11215. int cGroups;
  11216. // Just before doing any painting, see if the region is up to date...
  11217. ListView_RecalcRegion(plv, FALSE, TRUE);
  11218. //
  11219. // For list view and report view, we can save a lot of time
  11220. // by calculating the index of the first item that may need
  11221. // painting...
  11222. //
  11223. switch (plv->wView)
  11224. {
  11225. case LV_VIEW_DETAILS:
  11226. if (!plv->fGroupView)
  11227. {
  11228. i = ListView_RYHitTest(plv, prcClip->top);
  11229. cItem = ListView_RYHitTest(plv, prcClip->bottom) + 1;
  11230. }
  11231. break;
  11232. case LV_VIEW_LIST:
  11233. i = ListView_LCalcViewItem(plv, prcClip->left, prcClip->top);
  11234. cItem = ListView_LCalcViewItem(plv, prcClip->right, prcClip->bottom) + 1;
  11235. break;
  11236. default:
  11237. if (ListView_IsOwnerData(plv))
  11238. {
  11239. ListView_CalcMinMaxIndex(plv, prcClip, &i, &cItem);
  11240. break;
  11241. }
  11242. }
  11243. if (i < 0)
  11244. i = 0;
  11245. cItem = min(ListView_Count(plv), cItem);
  11246. if (ListView_IsOwnerData(plv) && (cItem > i))
  11247. {
  11248. ListView_NotifyCacheHint(plv, i, cItem-1);
  11249. ListView_LazyCreateWinEvents(plv, i, cItem-1);
  11250. }
  11251. lvdi.plv = plv;
  11252. lvdi.nmcd.nmcd.hdc = hdc;
  11253. lvdi.prcClip = prcClip;
  11254. lvdi.pitem = NULL;
  11255. if (plv->hdpaGroups)
  11256. {
  11257. cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  11258. if (plv->fGroupView && cGroups > 0 && ListView_IsGroupedView(plv))
  11259. {
  11260. int iGroup;
  11261. RECT rcClient;
  11262. GetClientRect(plv->ci.hwnd, &rcClient);
  11263. for (iGroup = 0; iGroup < cGroups; iGroup++)
  11264. {
  11265. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  11266. int cItems = DPA_GetPtrCount(pgrp->hdpa);
  11267. if (cItems > 0)
  11268. {
  11269. RECT rcT;
  11270. RECT rc;
  11271. SetRect(&rc, 0,
  11272. pgrp->rc.top - LISTGROUP_HEIGHT(plv, pgrp),
  11273. rcClient.right,
  11274. pgrp->rc.bottom + plv->rcBorder.bottom + plv->paddingBottom);
  11275. if (ListView_IsReportView(plv))
  11276. {
  11277. OffsetRect(&rc, -plv->ptlRptOrigin.x, -plv->ptlRptOrigin.y + plv->yTop);
  11278. }
  11279. else
  11280. {
  11281. OffsetRect(&rc, -plv->ptOrigin.x, -plv->ptOrigin.y);
  11282. }
  11283. if (IntersectRect(&rcT, &rc, prcClip))
  11284. {
  11285. NMLVCUSTOMDRAW nmcdGroup = {0};
  11286. DWORD dwCust;
  11287. UINT uAlign = LVCFMT_LEFT;
  11288. HFONT hfontOld;
  11289. RECT rcBorder = plv->rcBorder;
  11290. rcBorder.top = max(pgrp->cyTitle + 6, plv->rcBorder.top);
  11291. nmcdGroup.nmcd.hdc = hdc;
  11292. nmcdGroup.nmcd.rc = rc;
  11293. nmcdGroup.nmcd.dwItemSpec = pgrp->iGroupId;
  11294. nmcdGroup.dwItemType = LVCDI_GROUP;
  11295. nmcdGroup.rcText.left = rc.left + plv->paddingLeft;
  11296. nmcdGroup.rcText.top = rc.top;
  11297. nmcdGroup.rcText.bottom = rc.top + max(pgrp->cyTitle + 6, plv->rcBorder.top);
  11298. nmcdGroup.rcText.right = rc.right;
  11299. nmcdGroup.uAlign = pgrp->uAlign;
  11300. nmcdGroup.clrText = plv->crHeader;
  11301. dwCust = CICustomDrawNotify(&plv->ci, CDDS_PREPAINT, &nmcdGroup.nmcd);
  11302. if (!(dwCust & CDRF_SKIPDEFAULT))
  11303. {
  11304. RECT rcHeader = {0};
  11305. if (!(LVCDRF_NOGROUPFRAME & dwCust))
  11306. {
  11307. DrawGradiantLine(hdc, &nmcdGroup.rcText, &nmcdGroup.nmcd.rc);
  11308. }
  11309. if (!(dwCust & CDRF_NEWFONT))
  11310. {
  11311. hfontOld = SelectObject(hdc, plv->hfontGroup);
  11312. }
  11313. if (nmcdGroup.uAlign & LVGA_HEADER_CENTER)
  11314. uAlign = LVCFMT_CENTER;
  11315. else if (nmcdGroup.uAlign & LVGA_HEADER_RIGHT)
  11316. uAlign = LVCFMT_RIGHT;
  11317. SHDrawText(hdc, pgrp->pszHeader,
  11318. &nmcdGroup.rcText, uAlign, SHDT_VCENTER | SHDT_LEFT,
  11319. plv->cyLabelChar, plv->cxEllipses,
  11320. nmcdGroup.clrText, CLR_NONE);
  11321. // Need do do this before we unselect so that we get the right font...
  11322. DrawText(hdc, pgrp->pszHeader, -1, &rcHeader, DT_LV | DT_CALCRECT);
  11323. if (!(dwCust & CDRF_NEWFONT))
  11324. {
  11325. SelectObject(hdc, hfontOld);
  11326. }
  11327. }
  11328. dwCust = CICustomDrawNotify(&plv->ci, CDDS_POSTPAINT, &nmcdGroup.nmcd);
  11329. }
  11330. }
  11331. }
  11332. }
  11333. }
  11334. cItem = min(ListView_Count(plv), cItem);
  11335. for (; i < cItem; i++)
  11336. {
  11337. BOOL bSuccess;
  11338. int i2;
  11339. if (ListView_IsRearrangeableView(plv) &&
  11340. !ListView_IsOwnerData(plv))
  11341. {
  11342. LISTITEM *pitem;
  11343. // Icon views: Draw back-to-front mapped through
  11344. // Z-order array for proper Z order appearance - If autoarrange
  11345. // is on, we don't need to do this as our arrange code is setup
  11346. // to not overlap items!
  11347. //
  11348. // For the cases where we might have overlap, we sped this up,
  11349. // by converting the hdpaZorder into a list of indexes instead
  11350. // of pointers. This ovoids the costly convert pointer to
  11351. // index call.
  11352. //
  11353. i2 = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, (cItem - 1) -i);
  11354. //
  11355. // do a fast clip check on the item so we dont even try to
  11356. // draw it unless it is visible
  11357. //
  11358. // for small icon view we cant clip on the left without
  11359. // getting the text
  11360. //
  11361. // for large icon view we cant clip on the top without
  11362. // getting the text
  11363. //
  11364. // for large icon view in NOLABELWRAP mode, we can't clip
  11365. // on the top without getting the text, nor can we clip to
  11366. // the left or right in case the text is long.
  11367. //
  11368. // we can always clip to the bottom
  11369. //
  11370. pitem = ListView_FastGetItemPtr(plv, i2);
  11371. if (pitem && pitem->pt.x != RECOMPUTE)
  11372. {
  11373. int yBias = 0;
  11374. if (ListView_IsBorderSelect(plv))
  11375. yBias = BORDERSELECT_THICKNESS;
  11376. if (pitem->pt.y - yBias - plv->ptOrigin.y > prcClip->bottom)
  11377. continue;
  11378. if (plv->wView == LV_VIEW_SMALLICON)
  11379. {
  11380. if (pitem->pt.x - plv->ptOrigin.x - plv->cxState > prcClip->right)
  11381. continue;
  11382. if (pitem->pt.y + yBias + plv->cyItem - plv->ptOrigin.y < prcClip->top)
  11383. continue;
  11384. }
  11385. else if (!(plv->ci.style & LVS_NOLABELWRAP))
  11386. {
  11387. if (plv->wView == LV_VIEW_TILE)
  11388. {
  11389. if (pitem->pt.x - plv->sizeTile.cx - plv->ptOrigin.x > prcClip->right)
  11390. continue;
  11391. if (pitem->pt.x + yBias + plv->sizeTile.cx - plv->ptOrigin.x < prcClip->left)
  11392. continue;
  11393. }
  11394. else // LV_VIEW_ICON
  11395. {
  11396. if (pitem->pt.x - plv->cxIconSpacing - plv->ptOrigin.x > prcClip->right)
  11397. continue;
  11398. if (pitem->pt.x + yBias + plv->cxIconSpacing - plv->ptOrigin.x < prcClip->left)
  11399. continue;
  11400. }
  11401. }
  11402. }
  11403. if (plv->fGroupView &&
  11404. !LISTITEM_HASGROUP(pitem))
  11405. {
  11406. continue; // Don't paint items not in a group.
  11407. }
  11408. }
  11409. else
  11410. i2 = i;
  11411. plv->iItemDrawing = i2;
  11412. lvdi.nmcd.nmcd.dwItemSpec = i2;
  11413. // these may get changed
  11414. lvdi.lpptOrg = NULL;
  11415. lvdi.flags = 0;
  11416. lvdi.nmcd.clrText = plv->clrText;
  11417. lvdi.nmcd.clrTextBk = plv->clrTextBk;
  11418. lvdi.nmcd.clrFace = plv->clrBk;
  11419. lvdi.nmcd.iIconEffect = ILD_NORMAL;
  11420. lvdi.nmcd.iIconPhase = 0;
  11421. bSuccess = ListView_DrawItem(&lvdi);
  11422. if (!bSuccess)
  11423. {
  11424. break;
  11425. }
  11426. }
  11427. if (ListView_IsRearrangeableView(plv) &&
  11428. (ListView_IsOwnerData(plv)) &&
  11429. plv->iFocus != -1)
  11430. {
  11431. // since there's no zorder in ownerdata, we explicitly draw the focus guy last (again)
  11432. // so that it'll appear on top
  11433. // we may potentially want to do this for all items that are selected
  11434. plv->iItemDrawing = plv->iFocus;
  11435. lvdi.nmcd.nmcd.dwItemSpec = plv->iItemDrawing;
  11436. // these may get changed
  11437. lvdi.lpptOrg = NULL;
  11438. lvdi.flags = 0;
  11439. lvdi.nmcd.clrText = plv->clrText;
  11440. lvdi.nmcd.clrTextBk = plv->clrTextBk;
  11441. ListView_DrawItem(&lvdi);
  11442. }
  11443. // this is an NT5/Memphis feature.
  11444. if (ListView_Count(plv) == 0)
  11445. {
  11446. // there're no items in this view
  11447. // check if we need to display some text in this case.
  11448. if (ListView_GetEmptyText(plv))
  11449. {
  11450. RECT rcClip;
  11451. UINT flags = 0;
  11452. // Put some edging between the text and the border of the
  11453. // window so we don't slam up against the border.
  11454. // This keeps DBCS from looking horrid.
  11455. rcClip.left = g_cxEdge;
  11456. rcClip.top = g_cyEdge;
  11457. if (plv->dwExStyle & WS_EX_RTLREADING)
  11458. flags |= SHDT_RTLREADING;
  11459. // if its a report view && we have a header then move the text down
  11460. if (ListView_IsReportView(plv) && (!(plv->ci.style & LVS_NOCOLUMNHEADER)))
  11461. rcClip.top += plv->cyItem;
  11462. // Note: Use the full sizeClient.cx as the right margin
  11463. // in case pszEmptyText is wider than the client rectangle.
  11464. rcClip.left -= (int)plv->ptlRptOrigin.x;
  11465. rcClip.right = plv->sizeClient.cx;
  11466. rcClip.bottom = rcClip.top + plv->cyItem;
  11467. SHDrawText(hdc, plv->pszEmptyText,
  11468. &rcClip, LVCFMT_LEFT, flags,
  11469. plv->cyLabelChar, plv->cxEllipses,
  11470. plv->clrText, plv->clrBk);
  11471. }
  11472. }
  11473. plv->iItemDrawing = -1;
  11474. // post painting.... this is to do any extra (non item) painting
  11475. // such a grid lines
  11476. switch (plv->wView)
  11477. {
  11478. case LV_VIEW_DETAILS:
  11479. ListView_RAfterRedraw(plv, hdc);
  11480. break;
  11481. }
  11482. // Insert mark
  11483. {
  11484. RECT rcInsertMark;
  11485. if (ListView_OnGetInsertMarkRect(plv, &rcInsertMark))
  11486. {
  11487. OffsetRect(&rcInsertMark, -plv->ptOrigin.x, -plv->ptOrigin.y);
  11488. CCDrawInsertMark(hdc,
  11489. &rcInsertMark,
  11490. ((plv->ci.style & LVS_ALIGNMASK) == LVS_ALIGNTOP),
  11491. ListView_OnGetInsertMarkColor(plv));
  11492. }
  11493. }
  11494. // notify parent afterwards if they want us to
  11495. if (plv->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
  11496. {
  11497. CICustomDrawNotify(&plv->ci, CDDS_POSTPAINT, &nmcd);
  11498. }
  11499. }
  11500. }
  11501. BOOL ListView_DrawItem(PLVDRAWITEM plvdi)
  11502. {
  11503. BOOL fAllowHotSelection = FALSE;
  11504. BOOL bRet = TRUE;
  11505. UINT state;
  11506. if (!ListView_IsOwnerData(plvdi->plv) && (!plvdi->plv->hdpa || plvdi->nmcd.nmcd.dwItemSpec > (UINT)DPA_GetPtrCount(plvdi->plv->hdpa)))
  11507. return FALSE;
  11508. if (!ListView_IsOwnerData(plvdi->plv))
  11509. {
  11510. plvdi->pitem = ListView_FastGetItemPtr(plvdi->plv, plvdi->nmcd.nmcd.dwItemSpec);
  11511. }
  11512. // notify on custom draw then do it!
  11513. plvdi->nmcd.nmcd.uItemState = 0;
  11514. plvdi->nmcd.nmcd.lItemlParam = (plvdi->pitem)? plvdi->pitem->lParam : 0;
  11515. if (!(plvdi->flags & LVDI_NOWAYFOCUS))
  11516. {
  11517. if (plvdi->plv->flags & LVF_FOCUSED)
  11518. {
  11519. // if we're ownerdraw or asked to callback, go
  11520. // fetch the state
  11521. if (!plvdi->pitem || (plvdi->plv->stateCallbackMask & (LVIS_SELECTED | LVIS_FOCUSED)))
  11522. {
  11523. state = (WORD) ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec,
  11524. LVIS_SELECTED | LVIS_FOCUSED);
  11525. }
  11526. else
  11527. {
  11528. state = plvdi->pitem->state;
  11529. }
  11530. if (state & LVIS_FOCUSED)
  11531. {
  11532. plvdi->nmcd.nmcd.uItemState |= CDIS_FOCUS;
  11533. }
  11534. if (state & LVIS_SELECTED)
  11535. {
  11536. plvdi->nmcd.nmcd.uItemState |= CDIS_SELECTED;
  11537. }
  11538. }
  11539. // NOTE: This is a bug. We should set CDIS_SELECTED only if the item
  11540. // really is selected. But this bug has existed forever so who knows
  11541. // what apps are relying on it. Standard workaround is for the client
  11542. // to do a GetItemState and reconfirm the LVIS_SELECTED flag.
  11543. // That's what we do in ListView_DrawImageEx.
  11544. if (plvdi->plv->ci.style & LVS_SHOWSELALWAYS)
  11545. {
  11546. plvdi->nmcd.nmcd.uItemState |= CDIS_SELECTED;
  11547. }
  11548. }
  11549. if (!(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS))
  11550. {
  11551. plvdi->nmcd.nmcd.uItemState |= CDIS_SHOWKEYBOARDCUES;
  11552. }
  11553. plvdi->nmcd.clrText = plvdi->plv->clrText;
  11554. plvdi->nmcd.clrTextBk = (plvdi->plv->ci.style & WS_DISABLED ? plvdi->plv->clrBk : plvdi->plv->clrTextBk);
  11555. if ((plvdi->plv->exStyle & LVS_EX_UNDERLINEHOT) &&
  11556. plvdi->plv->iHot == (int)plvdi->nmcd.nmcd.dwItemSpec &&
  11557. (plvdi->plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  11558. ((plvdi->plv->exStyle & LVS_EX_TWOCLICKACTIVATE) &&
  11559. ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec, LVIS_SELECTED)))
  11560. {
  11561. fAllowHotSelection = TRUE;
  11562. // Handle the HOT case
  11563. if (plvdi->plv->clrHotlight != CLR_DEFAULT)
  11564. {
  11565. plvdi->nmcd.clrText = plvdi->plv->clrHotlight;
  11566. }
  11567. else
  11568. {
  11569. plvdi->nmcd.clrText = GetSysColor(COLOR_HOTLIGHT);
  11570. }
  11571. // if hotlight color is the same as the background
  11572. // color you don't see the text -- slam to a visible color in this case.
  11573. if (plvdi->nmcd.clrText == plvdi->nmcd.clrTextBk)
  11574. {
  11575. if (COLORISLIGHT(plvdi->nmcd.clrTextBk))
  11576. plvdi->nmcd.clrText = 0x000000; // black
  11577. else
  11578. plvdi->nmcd.clrText = 0xFFFFFF; // white
  11579. }
  11580. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hFontHot);
  11581. plvdi->nmcd.nmcd.uItemState |= CDIS_HOT;
  11582. }
  11583. else if ((plvdi->plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  11584. ((plvdi->plv->exStyle & LVS_EX_TWOCLICKACTIVATE) &&
  11585. ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec, LVIS_SELECTED)))
  11586. {
  11587. // Handle the non-hot webview case
  11588. if ((plvdi->plv->exStyle & LVS_EX_UNDERLINECOLD) &&
  11589. plvdi->plv->hFontHot)
  11590. {
  11591. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hFontHot);
  11592. }
  11593. else
  11594. {
  11595. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  11596. }
  11597. }
  11598. else
  11599. {
  11600. // Handle the non-webview case
  11601. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  11602. }
  11603. plvdi->dwCustom = CICustomDrawNotify(&plvdi->plv->ci, CDDS_ITEMPREPAINT, &plvdi->nmcd.nmcd);
  11604. plvdi->flags &= ~(LVDI_FOCUS | LVDI_SELECTED);
  11605. if (plvdi->nmcd.nmcd.uItemState & CDIS_FOCUS)
  11606. plvdi->flags |= LVDI_FOCUS;
  11607. if (plvdi->nmcd.nmcd.uItemState & CDIS_SELECTED)
  11608. {
  11609. if (plvdi->plv->flags & LVF_FOCUSED)
  11610. plvdi->flags |= LVDI_SELECTED;
  11611. else
  11612. plvdi->flags |= LVDI_SELECTNOFOCUS;
  11613. if (plvdi->plv->iHot == (int)plvdi->nmcd.nmcd.dwItemSpec && fAllowHotSelection)
  11614. plvdi->flags |= LVDI_HOTSELECTED;
  11615. }
  11616. if (!(plvdi->dwCustom & CDRF_SKIPDEFAULT))
  11617. {
  11618. if (!ListView_IsOwnerData(plvdi->plv))
  11619. {
  11620. if (plvdi->dwCustom & CDRF_NEWFONT)
  11621. {
  11622. _ListView_RecomputeLabelSize(plvdi->plv, plvdi->pitem, (int) plvdi->nmcd.nmcd.dwItemSpec, plvdi->nmcd.nmcd.hdc, FALSE);
  11623. }
  11624. }
  11625. bRet = _ListView_DrawItem(plvdi);
  11626. if (plvdi->dwCustom & CDRF_NOTIFYPOSTPAINT)
  11627. {
  11628. plvdi->nmcd.iSubItem = 0;
  11629. CICustomDrawNotify(&plvdi->plv->ci, CDDS_ITEMPOSTPAINT, &plvdi->nmcd.nmcd);
  11630. }
  11631. if (plvdi->dwCustom & CDRF_NEWFONT)
  11632. {
  11633. SelectObject(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  11634. plvdi->plv->flags |= LVF_CUSTOMFONT;
  11635. }
  11636. }
  11637. return bRet;
  11638. }
  11639. void WINAPI SHThemeDrawText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCTSTR pszText, RECT* prc, int fmt,
  11640. UINT flags, int cyChar, int cxEllipses, COLORREF clrText, COLORREF clrTextBk)
  11641. {
  11642. int cchText;
  11643. COLORREF clrSave = GetTextColor(hdc), clrSaveBk = 0;
  11644. RECT rc;
  11645. UINT uETOFlags = 0;
  11646. BOOL fForeOnly = FALSE;
  11647. TCHAR ach[CCHLABELMAX + CCHELLIPSES];
  11648. int align;
  11649. BOOL fUseShadowedText = (flags & SHDT_SHADOWTEXT) && (!g_fHighContrast);
  11650. if (!pszText)
  11651. return;
  11652. if (IsRectEmpty(prc))
  11653. return;
  11654. if (flags & SHDT_RTLREADING)
  11655. {
  11656. align = GetTextAlign(hdc);
  11657. SetTextAlign(hdc, align | TA_RTLREADING);
  11658. }
  11659. rc = *prc;
  11660. if (fUseShadowedText)
  11661. {
  11662. if (!AreAllMonitorsAtLeast(16))
  11663. fUseShadowedText = FALSE;
  11664. }
  11665. // If needed, add in a little extra margin...
  11666. //
  11667. if (flags & SHDT_EXTRAMARGIN)
  11668. {
  11669. rc.left += g_cxLabelMargin * 3;
  11670. rc.right -= g_cxLabelMargin * 3;
  11671. }
  11672. else if (!(flags & SHDT_NOMARGIN))
  11673. {
  11674. rc.left += g_cxLabelMargin;
  11675. rc.right -= g_cxLabelMargin;
  11676. }
  11677. if ((rc.left >= rc.right) && !(flags & (SHDT_SELECTED | SHDT_DESELECTED | SHDT_SELECTNOFOCUS)))
  11678. return;
  11679. if ((flags & SHDT_ELLIPSES) &&
  11680. ListView_NeedsEllipses(hdc, pszText, &rc, &cchText, cxEllipses))
  11681. {
  11682. // In some cases cchText was comming back bigger than
  11683. // ARRYASIZE(ach), so we need to make sure we don't overflow the buffer
  11684. // if cchText is too big for the buffer, truncate it down to size
  11685. if (cchText >= ARRAYSIZE(ach) - CCHELLIPSES)
  11686. cchText = ARRAYSIZE(ach) - CCHELLIPSES - 1;
  11687. memcpy(ach, pszText, cchText * sizeof(TCHAR));
  11688. lstrcpy(ach + cchText, c_szEllipses);
  11689. pszText = ach;
  11690. // Left-justify, in case there's no room for all of ellipses
  11691. //
  11692. fmt = LVCFMT_LEFT;
  11693. cchText += CCHELLIPSES;
  11694. }
  11695. else
  11696. {
  11697. cchText = lstrlen(pszText);
  11698. }
  11699. if (((clrTextBk == CLR_NONE) && !(flags & (SHDT_SELECTED | SHDT_SELECTNOFOCUS))) || (flags & SHDT_TRANSPARENT))
  11700. {
  11701. fForeOnly = TRUE;
  11702. clrSave = SetTextColor(hdc, (flags & SHDT_TRANSPARENT) ? 0 : clrText);
  11703. }
  11704. else if (!hTheme || clrTextBk != CLR_NONE)
  11705. {
  11706. HBRUSH hbrUse = NULL;
  11707. HBRUSH hbrDelete = NULL;
  11708. uETOFlags |= ETO_OPAQUE;
  11709. if ((flags & SHDT_SELECTED || flags & SHDT_SELECTNOFOCUS) && !(flags & SHDT_NOSELECTED))
  11710. {
  11711. fUseShadowedText = FALSE;
  11712. if (flags & SHDT_SELECTNOFOCUS)
  11713. {
  11714. clrText = g_clrBtnText;
  11715. clrTextBk = g_clrBtnFace;
  11716. if (flags & SHDT_DRAWTEXT)
  11717. {
  11718. hbrUse = g_hbrBtnFace;
  11719. }
  11720. }
  11721. else
  11722. {
  11723. clrText = g_clrHighlightText;
  11724. if (flags & SHDT_HOTSELECTED)
  11725. clrTextBk = GetSysColor(COLOR_HOTLIGHT);
  11726. else
  11727. clrTextBk = g_clrHighlight;
  11728. if (flags & SHDT_DRAWTEXT)
  11729. hbrUse = (flags & SHDT_HOTSELECTED)?GetSysColorBrush(COLOR_HOTLIGHT): g_hbrHighlight;
  11730. }
  11731. }
  11732. else if (clrText == CLR_DEFAULT && clrTextBk == CLR_DEFAULT)
  11733. {
  11734. fUseShadowedText = FALSE;
  11735. clrText = g_clrWindowText;
  11736. clrTextBk = g_clrWindow;
  11737. if ((flags & (SHDT_DRAWTEXT | SHDT_DESELECTED)) ==
  11738. (SHDT_DRAWTEXT | SHDT_DESELECTED))
  11739. {
  11740. hbrUse = g_hbrWindow;
  11741. }
  11742. }
  11743. else
  11744. {
  11745. if (clrText == CLR_DEFAULT)
  11746. clrText = g_clrWindowText;
  11747. if (clrTextBk == CLR_DEFAULT)
  11748. clrTextBk = g_clrWindow;
  11749. if (fUseShadowedText == FALSE &&
  11750. ((flags & (SHDT_DRAWTEXT | SHDT_DESELECTED)) ==
  11751. (SHDT_DRAWTEXT | SHDT_DESELECTED) || hTheme))
  11752. {
  11753. hbrUse = CreateSolidBrush(GetNearestColor(hdc, clrTextBk));
  11754. if (hbrUse)
  11755. {
  11756. hbrDelete = hbrUse;
  11757. }
  11758. else
  11759. hbrUse = GetStockObject(WHITE_BRUSH);
  11760. }
  11761. }
  11762. // now set it
  11763. clrSave = SetTextColor(hdc, clrText);
  11764. clrSaveBk = SetBkColor(hdc, clrTextBk);
  11765. if (hbrUse)
  11766. {
  11767. FillRect(hdc, prc, hbrUse);
  11768. if (hbrDelete)
  11769. DeleteObject(hbrDelete);
  11770. }
  11771. }
  11772. // If we want the item to display as if it was depressed, we will
  11773. // offset the text rectangle down and to the left
  11774. if (flags & SHDT_DEPRESSED)
  11775. OffsetRect(&rc, g_cxBorder, g_cyBorder);
  11776. if (flags & SHDT_DRAWTEXT || hTheme)
  11777. {
  11778. HRESULT hr = E_FAIL;
  11779. UINT uDTFlags;
  11780. if (flags & SHDT_DRAWTEXT)
  11781. {
  11782. uDTFlags= DT_LVWRAP | DT_END_ELLIPSIS;
  11783. }
  11784. else
  11785. {
  11786. uDTFlags = DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER;
  11787. if (fmt & LVCFMT_CENTER)
  11788. uDTFlags |= DT_CENTER;
  11789. else if (fmt & LVCFMT_RIGHT)
  11790. uDTFlags |= DT_RIGHT;
  11791. }
  11792. if (flags & SHDT_DTELLIPSIS)
  11793. uDTFlags |= DT_WORD_ELLIPSIS;
  11794. if (!(flags & SHDT_CLIPPED))
  11795. uDTFlags |= DT_NOCLIP;
  11796. if (flags & SHDT_NODBCSBREAK)
  11797. uDTFlags |= DT_NOFULLWIDTHCHARBREAK;
  11798. if (flags & SHDT_VCENTER)
  11799. uDTFlags |= DT_VCENTER | DT_SINGLELINE;
  11800. if (flags & SHDT_LEFT)
  11801. uDTFlags = DT_LEFT | uDTFlags & ~DT_CENTER;
  11802. if (fUseShadowedText)
  11803. {
  11804. DrawShadowText(hdc, pszText, cchText, &rc, uDTFlags, RGB(255,255,255), RGB(0,0,0), 2, 2);
  11805. }
  11806. else
  11807. {
  11808. if (hTheme)
  11809. hr = DrawThemeText(hTheme, hdc, iPartId, iStateId, pszText, -1, uDTFlags, 0, &rc);
  11810. if (FAILED(hr))
  11811. DrawText(hdc, pszText, cchText, &rc, uDTFlags);
  11812. }
  11813. }
  11814. else
  11815. {
  11816. if (fmt != LVCFMT_LEFT)
  11817. {
  11818. SIZE siz;
  11819. GetTextExtentPoint(hdc, pszText, cchText, &siz);
  11820. if (fmt == LVCFMT_CENTER)
  11821. rc.left = (rc.left + rc.right - siz.cx) / 2;
  11822. else // fmt == LVCFMT_RIGHT
  11823. rc.left = rc.right - siz.cx;
  11824. }
  11825. // Center vertically in case the bitmap (to the left) is larger than
  11826. // the height of one line
  11827. rc.top += (rc.bottom - rc.top - cyChar) / 2;
  11828. if (flags & SHDT_CLIPPED)
  11829. uETOFlags |= ETO_CLIPPED;
  11830. // HACK: ExtTextOut() has an off-by-one bug in its rendering of RTL
  11831. // text. We need this hack to render properly (RAID 439915).
  11832. // Note that this bug is NOT present in the DrawText() API.
  11833. if (flags & SHDT_RTLREADING)
  11834. rc.left--;
  11835. ExtTextOut(hdc, rc.left, rc.top, uETOFlags, prc, pszText, cchText, NULL);
  11836. }
  11837. if (flags & (SHDT_SELECTED | SHDT_DESELECTED | SHDT_TRANSPARENT))
  11838. {
  11839. SetTextColor(hdc, clrSave);
  11840. if (!fForeOnly)
  11841. SetBkColor(hdc, clrSaveBk);
  11842. }
  11843. if (flags & SHDT_RTLREADING)
  11844. SetTextAlign(hdc, align);
  11845. }
  11846. void WINAPI SHDrawText(HDC hdc, LPCTSTR pszText, RECT* prc, int fmt,
  11847. UINT flags, int cyChar, int cxEllipses, COLORREF clrText, COLORREF clrTextBk)
  11848. {
  11849. SHThemeDrawText(NULL, hdc, 0, 0, pszText, prc, fmt,
  11850. flags, cyChar, cxEllipses, clrText, clrTextBk);
  11851. }
  11852. /*----------------------------------------------------------------
  11853. ** Create an imagelist to be used for dragging.
  11854. **
  11855. ** 1) create mask and image bitmap matching the select bounds size
  11856. ** 2) draw the text to both bitmaps (in black for now)
  11857. ** 3) create an imagelist with these bitmaps
  11858. ** 4) make a dithered copy of the image onto the new imagelist
  11859. **----------------------------------------------------------------*/
  11860. HIMAGELIST ListView_OnCreateDragImage(LV *plv, int iItem, LPPOINT lpptUpLeft)
  11861. {
  11862. HWND hwndLV = plv->ci.hwnd;
  11863. RECT rcBounds, rcImage, rcLabel;
  11864. HDC hdcMem = NULL;
  11865. HBITMAP hbmImage = NULL;
  11866. HBITMAP hbmMask = NULL;
  11867. HBITMAP hbmOld;
  11868. HIMAGELIST himl = NULL;
  11869. int dx, dy;
  11870. HIMAGELIST himlSrc;
  11871. LV_ITEM item;
  11872. POINT ptOrg;
  11873. LVDRAWITEM lvdi = {0};
  11874. RECT rcSelBounds;
  11875. BOOL bMirroredWnd = (plv->ci.dwExStyle&RTL_MIRRORED_WINDOW);
  11876. int iImageList;
  11877. if (!lpptUpLeft)
  11878. return NULL;
  11879. if (iItem >= ListView_Count(plv))
  11880. return NULL;
  11881. if (plv->iHot == iItem)
  11882. {
  11883. ListView_OnSetHotItem(plv, -1);
  11884. UpdateWindow(plv->ci.hwnd);
  11885. }
  11886. ListView_GetRects(plv, iItem, QUERY_DEFAULT, &rcImage, &rcLabel, &rcBounds, &rcSelBounds);
  11887. if (ListView_IsIconView(plv))
  11888. {
  11889. ListView_UnfoldRects(plv, iItem, &rcImage, &rcLabel,
  11890. &rcBounds, &rcSelBounds);
  11891. InflateRect(&rcImage, -g_cxIconMargin, -g_cyIconMargin);
  11892. }
  11893. // chop off any extra filler above icon
  11894. ptOrg.x = rcBounds.left - rcSelBounds.left;
  11895. ptOrg.y = rcBounds.top - rcImage.top;
  11896. dx = rcSelBounds.right - rcSelBounds.left;
  11897. dy = rcSelBounds.bottom - rcImage.top;
  11898. lpptUpLeft->x = rcSelBounds.left;
  11899. lpptUpLeft->y = rcImage.top;
  11900. if (!(hdcMem = CreateCompatibleDC(NULL)))
  11901. goto CDI_Exit;
  11902. if (!(hbmImage = CreateColorBitmap(dx, dy)))
  11903. goto CDI_Exit;
  11904. if (!(hbmMask = CreateMonoBitmap(dx, dy)))
  11905. goto CDI_Exit;
  11906. //
  11907. // Mirror the memory DC so that the transition from
  11908. // mirrored(memDC)->non-mirrored(imagelist DCs)->mirrored(screenDC)
  11909. // is consistent. [samera]
  11910. //
  11911. if (bMirroredWnd)
  11912. {
  11913. SET_DC_RTL_MIRRORED(hdcMem);
  11914. }
  11915. // prepare for drawing the item
  11916. SelectObject(hdcMem, plv->hfontLabel);
  11917. SetBkMode(hdcMem, TRANSPARENT);
  11918. lvdi.plv = plv;
  11919. lvdi.nmcd.nmcd.dwItemSpec = iItem;
  11920. lvdi.pitem = NULL; // make sure it is null as Owner data uses this to trigger things...
  11921. lvdi.nmcd.nmcd.hdc = hdcMem;
  11922. lvdi.lpptOrg = &ptOrg;
  11923. lvdi.prcClip = NULL;
  11924. lvdi.flags = LVDI_NOIMAGE | LVDI_TRANSTEXT | LVDI_NOWAYFOCUS | LVDI_UNFOLDED;
  11925. lvdi.nmcd.clrFace = CLR_NONE;
  11926. /*
  11927. ** draw the text to both bitmaps
  11928. */
  11929. hbmOld = SelectObject(hdcMem, hbmImage);
  11930. // fill image with black for transparency
  11931. PatBlt(hdcMem, 0, 0, dx, dy, BLACKNESS);
  11932. ListView_DrawItem(&lvdi);
  11933. if (bMirroredWnd)
  11934. MirrorBitmapInDC(hdcMem, hbmImage);
  11935. lvdi.flags = LVDI_NOIMAGE | LVDI_TRANSTEXT | LVDI_NOWAYFOCUS | LVDI_UNFOLDED;
  11936. SelectObject(hdcMem, hbmMask);
  11937. // fill mask with white for transparency
  11938. PatBlt(hdcMem, 0, 0, dx, dy, WHITENESS);
  11939. ListView_DrawItem(&lvdi);
  11940. if (bMirroredWnd)
  11941. MirrorBitmapInDC(hdcMem, hbmMask);
  11942. // unselect objects that we used
  11943. SelectObject(hdcMem, hbmOld);
  11944. SelectObject(hdcMem, g_hfontSystem);
  11945. if (ListView_IsIconView(plv) || ListView_IsTileView(plv))
  11946. iImageList = LVSIL_NORMAL;
  11947. else
  11948. iImageList = LVSIL_SMALL;
  11949. himlSrc = ListView_OnGetImageList(plv, iImageList);
  11950. /*
  11951. ** make an image list that for now only has the text
  11952. ** we use ImageList_Clone so we get a imagelist that
  11953. ** the same color depth as our own imagelist
  11954. */
  11955. if (!(himl = ImageList_Clone(himlSrc, dx, dy, ILC_MASK, 1, 0)))
  11956. goto CDI_Exit;
  11957. ImageList_SetBkColor(himl, CLR_NONE);
  11958. ImageList_Add(himl, hbmImage, hbmMask);
  11959. /*
  11960. ** make a dithered copy of the image part onto our bitmaps
  11961. ** (need both bitmap and mask to be dithered)
  11962. */
  11963. if (himlSrc)
  11964. {
  11965. item.iItem = iItem;
  11966. item.iSubItem = 0;
  11967. item.mask = LVIF_IMAGE |LVIF_STATE;
  11968. item.stateMask = LVIS_OVERLAYMASK;
  11969. ListView_OnGetItem(plv, &item);
  11970. ImageList_CopyDitherImage(himl, 0, rcImage.left - rcSelBounds.left, 0, himlSrc, item.iImage, ((plv->ci.dwExStyle & dwExStyleRTLMirrorWnd) ? ILD_MIRROR : 0L) | (item.state & LVIS_OVERLAYMASK));
  11971. }
  11972. CDI_Exit:
  11973. if (hdcMem)
  11974. DeleteObject(hdcMem);
  11975. if (hbmImage)
  11976. DeleteObject(hbmImage);
  11977. if (hbmMask)
  11978. DeleteObject(hbmMask);
  11979. return himl;
  11980. }
  11981. // ListView_OnGetTopIndex -- Gets the index of the first visible item
  11982. // For list view and report view this calculates the actual index
  11983. // for iconic views it alway returns 0
  11984. //
  11985. int ListView_OnGetTopIndex(LV* plv)
  11986. {
  11987. if (ListView_IsReportView(plv) && !plv->fGroupView)
  11988. return (int)((plv->ptlRptOrigin.y) / plv->cyItem);
  11989. else if (ListView_IsListView(plv))
  11990. return (plv->xOrigin / plv->cxItem) * plv->cItemCol;
  11991. else
  11992. return 0;
  11993. }
  11994. // ListView_OnGetCountPerPage -- Gets the count of items that will fit
  11995. // on a page For list view and report view this calculates the
  11996. // count depending on the size of the window and for Iconic views it
  11997. // will always return the count of items in the list view.
  11998. //
  11999. int ListView_OnGetCountPerPage(LV* plv)
  12000. {
  12001. if (ListView_IsReportView(plv))
  12002. return (plv->sizeClient.cy - plv->yTop) / plv->cyItem;
  12003. else if (ListView_IsListView(plv))
  12004. return ((plv->sizeClient.cx)/ plv->cxItem)
  12005. * plv->cItemCol;
  12006. else
  12007. return (ListView_Count(plv));
  12008. }
  12009. /* Purpose:
  12010. / Provides support for invalidating items within list views.
  12011. /
  12012. / Notes:
  12013. / Copes with invalidating the extra region in the list view that requires
  12014. / us to erase the background. Design to optimise out the ERASURE of the
  12015. / background.
  12016. /
  12017. / For details on the API see ListView_InvalidateItem.
  12018. /
  12019. / In:
  12020. / plv->ListView structure to work with
  12021. / iItem = item number
  12022. / bSrelectionOnly = refesh the selection
  12023. / fRedraw = Flags for RedrawWindow
  12024. / Out:
  12025. / -
  12026. */
  12027. void ListView_InvalidateFoldedItem(LV* plv, int iItem, BOOL fSelectionOnly, UINT fRedraw)
  12028. {
  12029. ListView_InvalidateItem(plv, iItem, fSelectionOnly, fRedraw);
  12030. if (ListView_IsIconView(plv) &&
  12031. (!ListView_IsItemUnfolded(plv, iItem) || (fRedraw & RDW_ERASE)))
  12032. {
  12033. RECT rcLabel;
  12034. if (ListView_GetUnfoldedRect(plv, iItem, &rcLabel))
  12035. {
  12036. RedrawWindow(plv->ci.hwnd, &rcLabel, NULL, fRedraw|RDW_ERASE);
  12037. }
  12038. }
  12039. }
  12040. /*
  12041. / Purpose:
  12042. / Having previously called get rects, then call this function to ensure
  12043. / that they are correctly unfolded.
  12044. /
  12045. / Notes:
  12046. / -
  12047. /
  12048. / In:
  12049. / plv-> list view to unfold on
  12050. / iItem = item number
  12051. / prcIcon -> icon bounding box
  12052. / prcLabel -> rectangle for the label structure
  12053. / prcBounds -> bounds rectangle / == NULL for none / These are currently the same for large icons
  12054. / prcSelectBounds -> selection bounds / == NULL /
  12055. / Out: TRUE if unfolding the item was worth anything
  12056. / -
  12057. */
  12058. BOOL ListView_UnfoldRects(LV* plv, int iItem,
  12059. RECT * prcIcon, RECT * prcLabel,
  12060. RECT * prcBounds, RECT * prcSelectBounds)
  12061. {
  12062. LISTITEM item;
  12063. LISTITEM* pitem = &item;
  12064. BOOL fRc = FALSE;
  12065. if (!ListView_IsIconView(plv))
  12066. return fRc;
  12067. // If we have a label pointer then expand as required
  12068. // nb - different paths for owner data
  12069. if (prcLabel)
  12070. {
  12071. if (!ListView_IsOwnerData(plv))
  12072. {
  12073. pitem = ListView_GetItemPtr(plv, iItem);
  12074. if (!EVAL(pitem))
  12075. {
  12076. // DavidShi was able to get us into here with an invalid
  12077. // item number during a delete notification. So if the
  12078. // item number is invalid, just return a blank rectangle
  12079. // instead of faulting.
  12080. SetRectEmpty(prcLabel);
  12081. goto doneLabel;
  12082. }
  12083. }
  12084. else
  12085. {
  12086. _ListView_RecomputeLabelSize(plv, pitem, iItem, NULL, FALSE);
  12087. }
  12088. if (prcLabel->bottom != prcLabel->top + max(pitem->cyUnfoldedLabel, pitem->cyFoldedLabel))
  12089. fRc = TRUE;
  12090. // In HideLabel mode it's always "worthwhile" to "unfold" the rects because the label is not shown
  12091. // by default. By returning TRUE we cause the item's label to display in a tooltip where the label
  12092. // would normally be.
  12093. if (ListView_HideLabels(plv))
  12094. fRc = TRUE;
  12095. prcLabel->bottom = prcLabel->top + pitem->cyUnfoldedLabel;
  12096. }
  12097. doneLabel:
  12098. // Build the unions if required
  12099. if (prcBounds && prcIcon && prcLabel)
  12100. {
  12101. ListView_CalcBounds(plv, QUERY_UNFOLDED, prcIcon, prcLabel, prcBounds);
  12102. }
  12103. if (prcSelectBounds && prcIcon && prcLabel)
  12104. {
  12105. if (ListView_HideLabels(plv))
  12106. *prcBounds = *prcIcon;
  12107. else
  12108. UnionRect(prcSelectBounds, prcIcon, prcLabel);
  12109. }
  12110. return fRc;
  12111. }
  12112. void ListView_InvalidateMark(LV* plv)
  12113. {
  12114. RECT rc;
  12115. if (ListView_OnGetInsertMarkRect(plv, &rc))
  12116. {
  12117. OffsetRect(&rc, -plv->ptOrigin.x, -plv->ptOrigin.y);
  12118. InvalidateRect(plv->ci.hwnd, &rc, TRUE);
  12119. }
  12120. }
  12121. // Returns the insertmark rect in listview coordinates. Returns FALSE if there is no insertmarkrect
  12122. BOOL ListView_OnGetInsertMarkRect(LV* plv, LPRECT prc)
  12123. {
  12124. BOOL fVert;
  12125. RECT rcSlot;
  12126. LISTITEM *pitem;
  12127. if (plv->iInsertItem == -1)
  12128. return FALSE;
  12129. pitem = ListView_GetItemPtr(plv, plv->iInsertItem);
  12130. if (!pitem)
  12131. {
  12132. return FALSE;
  12133. }
  12134. fVert = !((plv->ci.style & LVS_ALIGNMASK) == LVS_ALIGNTOP);
  12135. ListView_CalcItemSlotAndRect(plv, pitem, NULL, &rcSlot);
  12136. if (fVert)
  12137. {
  12138. int iY;
  12139. prc->left = rcSlot.left;
  12140. prc->right = rcSlot.right;
  12141. iY = (plv->fInsertAfter) ? rcSlot.bottom : rcSlot.top;
  12142. prc->top = iY - INSERTMARKSIZE/2;
  12143. prc->bottom = iY + INSERTMARKSIZE/2 + 1;
  12144. }
  12145. else
  12146. {
  12147. int iX;
  12148. prc->top = rcSlot.top;
  12149. prc->bottom = rcSlot.bottom;
  12150. iX = (plv->fInsertAfter) ? rcSlot.right : rcSlot.left;
  12151. prc->left = iX - INSERTMARKSIZE/2;
  12152. prc->right = iX + INSERTMARKSIZE/2 + 1;
  12153. }
  12154. return TRUE;
  12155. }
  12156. COLORREF ListView_OnGetInsertMarkColor(LV* plv)
  12157. {
  12158. if (plv->clrim == CLR_DEFAULT)
  12159. return plv->clrText;
  12160. else
  12161. return plv->clrim;
  12162. }