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

14445 lines
447 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. const PFNLISTVIEW_RECOMPUTELABELSIZE pfnListView_RecomputeLabelSize[5] =
  90. {
  91. ListView_IRecomputeLabelSize,
  92. ListView_IRecomputeLabelSize,
  93. ListView_IRecomputeLabelSize,
  94. ListView_IRecomputeLabelSize,
  95. ListView_TRecomputeLabelSize,
  96. };
  97. BOOL ListView_NULLRecomputeEx(LV* plv, HDPA hdpaSort, int iFrom, BOOL fForce)
  98. {
  99. return FALSE;
  100. }
  101. const PFNLISTVIEW_RECOMPUTEEX pfnListView_RecomputeEx[5] =
  102. {
  103. ListView_IRecomputeEx,
  104. ListView_RRecomputeEx,
  105. ListView_IRecomputeEx,
  106. ListView_NULLRecomputeEx,
  107. ListView_IRecomputeEx,
  108. };
  109. #ifdef DEBUG_PAINT
  110. void ListView_DebugDrawInvalidRegion(LV* plv, RECT* prc, HRGN hrgn)
  111. {
  112. HDC hdc;
  113. HBRUSH hbrush;
  114. int bkMode;
  115. static int s_iclr;
  116. static COLORREF s_aclr[] =
  117. {
  118. RGB(255, 0, 0), RGB(0, 255, 0),
  119. RGB(255, 255, 0), RGB(0, 255, 255),
  120. };
  121. s_iclr = (s_iclr + 1) % ARRAYSIZE(s_aclr);
  122. hdc = GetDC(plv->ci.hwnd);
  123. hbrush = CreateHatchBrush(HS_DIAGCROSS, s_aclr[s_iclr]);
  124. bkMode = SetBkMode(hdc, TRANSPARENT);
  125. if (prc)
  126. {
  127. FillRect(hdc, prc, hbrush);
  128. }
  129. else if (hrgn)
  130. {
  131. FillRgn(hdc, hrgn, hbrush);
  132. }
  133. DeleteObject((HGDIOBJ)hbrush);
  134. SetBkMode(hdc, bkMode);
  135. ReleaseDC(plv->ci.hwnd, hdc);
  136. Sleep(120);
  137. }
  138. BOOL ListView_DebugDrawInvalidItem(LV* plv, int iItem)
  139. {
  140. RECT rcLabel;
  141. RECT rcIcon;
  142. ListView_GetRects(plv, iItem, QUERY_DEFAULT,
  143. &rcIcon, &rcLabel, NULL, NULL);
  144. ListView_DebugDrawInvalidRegion(plv, &rcIcon, NULL);
  145. ListView_DebugDrawInvalidRegion(plv, &rcLabel, NULL);
  146. return TRUE;
  147. }
  148. void ListView_DebugDisplayClipRegion(LV* plv, RECT* prc, HRGN hrgn)
  149. {
  150. HDC hdc = GetDC(plv->ci.hwnd);
  151. if (prc)
  152. {
  153. InvertRect(hdc, prc);
  154. }
  155. else if (hrgn)
  156. {
  157. InvertRgn(hdc, hrgn);
  158. }
  159. Sleep(120);
  160. if (prc)
  161. {
  162. InvertRect(hdc, prc);
  163. }
  164. else if (hrgn)
  165. {
  166. InvertRgn(hdc, hrgn);
  167. }
  168. ReleaseDC(plv->ci.hwnd, hdc);
  169. }
  170. #else
  171. #define ListView_DebugDrawInvalidItem(plv, iItem) FALSE
  172. #endif
  173. // redefine to trace at most calls to ListView_SendChange
  174. #define DM_LVSENDCHANGE 0
  175. // penwin.h is messed up; define local stuff for now
  176. #define HN_BEGINDIALOG 40 // Lens/EditText/garbage detection dialog is about
  177. // to come up on this hedit/bedit
  178. #define HN_ENDDIALOG 41 // Lens/EditText/garbage detection dialog has
  179. // just been destroyed
  180. //---------------------------------------------------------
  181. // no way am I gonna make TWO function calls where I can do FOUR comparisons!
  182. //
  183. #define RECTS_IN_SIZE(sz, r2) (!RECTS_NOT_IN_SIZE(sz, r2))
  184. #define RECTS_NOT_IN_SIZE(sz, r2) (\
  185. ((sz).cx <= (r2).left) ||\
  186. (0 >= (r2).right) ||\
  187. ((sz).cy <= (r2).top) ||\
  188. (0 >= (r2).bottom))
  189. //---------------------------------------------------------
  190. void ListView_OnUpdate(LV* plv, int i);
  191. void ListView_OnDestroy(LV* plv);
  192. BOOL ListView_ValidateScrollParams(LV* plv, int * dx, int *dy);
  193. void ListView_ButtonSelect(LV* plv, int iItem, UINT keyFlags, BOOL bSelected);
  194. void ListView_DeselectAll(LV* plv, int iDontDeselect);
  195. void ListView_LRInvalidateBelow(LV* plv, int i, int fSmoothScroll);
  196. void ListView_IInvalidateBelow(LV* plv, int i);
  197. void ListView_InvalidateFoldedItem(LV* plv, int iItem, BOOL fSelectionOnly, UINT fRedraw);
  198. void ListView_ReleaseBkImage(LV *plv);
  199. void ListView_RecalcRegion(LV *plv, BOOL fForce, BOOL fRedraw);
  200. BOOL g_fSlowMachine = -1;
  201. BOOL ListView_Init(HINSTANCE hinst)
  202. {
  203. WNDCLASS wc;
  204. wc.lpfnWndProc = ListView_WndProc;
  205. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  206. wc.hIcon = NULL;
  207. wc.lpszMenuName = NULL;
  208. wc.hInstance = hinst;
  209. wc.lpszClassName = c_szListViewClass;
  210. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // NULL;
  211. wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
  212. wc.cbWndExtra = sizeof(LV*);
  213. wc.cbClsExtra = 0;
  214. return (RegisterClass(&wc) || (GetLastError() == ERROR_CLASS_ALREADY_EXISTS));
  215. }
  216. // Cancel tracking tooltips which are activated by item focus via keyboard
  217. void ListView_CancelTipTrack(LV* plv)
  218. {
  219. // Make sure in tracking mode
  220. if (plv->hwndToolTips)
  221. {
  222. // Cancel any pending timer
  223. KillTimer(plv->ci.hwnd, IDT_TRACKINGTIP);
  224. if (ListView_IsKbdTipTracking(plv))
  225. {
  226. TOOLINFO ti = {0};
  227. // Mark as tracking nothing
  228. plv->iTracking = LVKTT_NOTRACK;
  229. // Reset tooltip to non-tracking
  230. ti.cbSize = sizeof(TOOLINFO);
  231. ti.hwnd = plv->ci.hwnd;
  232. SendMessage(plv->hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti);
  233. SendMessage(plv->hwndToolTips, TTM_TRACKACTIVATE, FALSE, (LPARAM)&ti);
  234. // Switch tooltip window back to non-tracking (manual) mode
  235. ti.uFlags &= ~TTF_TRACK;
  236. SendMessage(plv->hwndToolTips, TTM_SETTOOLINFO, 0, (LPARAM)&ti);
  237. }
  238. }
  239. }
  240. BOOL ListView_GetRegIASetting(BOOL *pb)
  241. {
  242. HKEY hkey;
  243. BOOL bRet = FALSE;
  244. BOOL bValue = TRUE;
  245. if (RegOpenKeyEx(HKEY_CURRENT_USER, IE_SETTINGS, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
  246. {
  247. DWORD dwType;
  248. DWORD dwValue;
  249. DWORD cbValue = sizeof(DWORD);
  250. if (RegQueryValueEx(hkey, (LPTSTR)USE_DBL_CLICK_TIMER, 0, &dwType, (LPBYTE)&dwValue, &cbValue) == ERROR_SUCCESS)
  251. {
  252. bValue = (BOOL)dwValue;
  253. bRet = TRUE;
  254. }
  255. RegCloseKey(hkey);
  256. }
  257. *pb = bValue;
  258. return bRet;
  259. }
  260. BOOL ListView_NotifyCacheHint(LV* plv, int iFrom, int iTo)
  261. {
  262. NM_CACHEHINT nm;
  263. if (iFrom <= iTo)
  264. {
  265. nm.iFrom = iFrom;
  266. nm.iTo = iTo;
  267. return !(BOOL)CCSendNotify(&plv->ci, LVN_ODCACHEHINT, &nm.hdr);
  268. }
  269. return FALSE;
  270. }
  271. void ListView_LazyCreateObjects(LV *plv, int iMin, int iMax)
  272. {
  273. for (; iMin < iMax; iMin++)
  274. NotifyWinEvent(EVENT_OBJECT_CREATE, plv->ci.hwnd, OBJID_CLIENT, 1 + iMin);
  275. }
  276. //
  277. // Owner-data causes MSAA lots of grief, because there is no way to tell
  278. // MSAA "I just created 25 million items". You have to tell it one at a
  279. // time. Instead of sending out 25 million "add item" notifications, we
  280. // just send them out as they scroll into view.
  281. //
  282. // plv->iMSAAMin and plv->iMSAAMax are the range of items we most
  283. // recently told MSAA about. MSAAMax is *exclusive*, just like RECTs.
  284. // It makes the math easier.
  285. //
  286. // We use iMSAAMin and iMSAAMax to avoid sending blatantly redundant
  287. // notifications, which would other happen very frequently.
  288. //
  289. void ListView_LazyCreateWinEvents(LV *plv, int iFrom, int iTo)
  290. {
  291. int iMin = iFrom;
  292. int iMax = iTo+1; // Convert from [From,To] to [Min,Max)
  293. //
  294. // If the incoming range is entirely contained within the existing
  295. // range, then there is nothing to do. This happens a lot.
  296. //
  297. if (iMin >= plv->iMSAAMin && iMax <= plv->iMSAAMax)
  298. return;
  299. //
  300. // If the incoming range is adjacent to or overlaps the low end
  301. // of the existing range... (This happens when scrolling backwards.)
  302. //
  303. if (iMin <= plv->iMSAAMin && iMax >= plv->iMSAAMin)
  304. {
  305. // Notify the low end.
  306. ListView_LazyCreateObjects(plv, iMin, plv->iMSAAMin);
  307. // Extend the list of things we've notified.
  308. plv->iMSAAMin = iMin;
  309. // Remove it from the things left to be notified.
  310. iMin = plv->iMSAAMax;
  311. }
  312. //
  313. // Now do the same thing to the top end.
  314. // (This happens when scrolling forwards.)
  315. //
  316. if (iMax >= plv->iMSAAMax && iMin <= plv->iMSAAMax)
  317. {
  318. // Notify the top end.
  319. ListView_LazyCreateObjects(plv, plv->iMSAAMax, iMax);
  320. // Extend the list of things we've notified.
  321. plv->iMSAAMax = iMax;
  322. // Remove it from the things left to be notified.
  323. iMax = plv->iMSAAMin;
  324. }
  325. //
  326. // If there are still things to be notified, then it means that the
  327. // incoming range isn't contiguous with the previous range, so throw
  328. // away the old range and just set it to the current range.
  329. // (This happens when you grab the scrollbar and jump to a completely
  330. // unrelated part of the listview.)
  331. //
  332. if (iMin < iMax)
  333. {
  334. plv->iMSAAMin = iMin;
  335. plv->iMSAAMax = iMax;
  336. ListView_LazyCreateObjects(plv, iMin, iMax);
  337. }
  338. }
  339. LRESULT ListView_RequestFindItem(LV* plv, CONST LV_FINDINFO* plvfi, int iStart)
  340. {
  341. NM_FINDITEM nm;
  342. nm.lvfi = *plvfi;
  343. nm.iStart = iStart;
  344. return CCSendNotify(&plv->ci, LVN_ODFINDITEM, &nm.hdr);
  345. }
  346. BOOL ListView_SendChange(LV* plv, int i, int iSubItem, int code, UINT oldState, UINT newState,
  347. UINT changed, LPARAM lParam)
  348. {
  349. NM_LISTVIEW nm;
  350. nm.iItem = i;
  351. nm.iSubItem = iSubItem;
  352. nm.uNewState = newState;
  353. nm.uOldState = oldState;
  354. nm.uChanged = changed;
  355. nm.ptAction.x = 0;
  356. nm.ptAction.y = 0;
  357. nm.lParam = lParam;
  358. return !CCSendNotify(&plv->ci, code, &nm.hdr);
  359. }
  360. void ListView_SendODChangeAndInvalidate(LV* plv, int iFrom, int iTo, UINT oldState,
  361. UINT newState)
  362. {
  363. NM_ODSTATECHANGE nm;
  364. nm.iFrom = iFrom;
  365. nm.iTo = iTo;
  366. nm.uNewState = newState;
  367. nm.uOldState = oldState;
  368. CCSendNotify(&plv->ci, LVN_ODSTATECHANGED, &nm.hdr);
  369. // Tell accessibility, "Selection changed in a complex way"
  370. NotifyWinEvent(EVENT_OBJECT_SELECTIONWITHIN, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  371. // considerable speed increase less than 100 to do this method
  372. // while over 100, the other method works faster
  373. if ((iTo - iFrom) > 100)
  374. {
  375. InvalidateRect(plv->ci.hwnd, NULL, FALSE);
  376. }
  377. else
  378. {
  379. while (iFrom <= iTo)
  380. {
  381. ListView_InvalidateItem(plv, iFrom, TRUE, RDW_INVALIDATE);
  382. iFrom++;
  383. }
  384. }
  385. }
  386. //
  387. // This function autoarranges or snaps to grid based on the style and ExStyle
  388. //
  389. // Note: AutoArrange overrides the snap-to-grid style.
  390. //
  391. void ListView_ArrangeOrSnapToGrid(LV *plv)
  392. {
  393. if (plv->ci.style & LVS_AUTOARRANGE)
  394. ListView_OnArrange(plv, LVA_DEFAULT);
  395. else if (plv->exStyle & LVS_EX_SNAPTOGRID)
  396. ListView_OnArrange(plv, LVA_SNAPTOGRID);
  397. }
  398. BOOL ListView_Notify(LV* plv, int i, int iSubItem, int code)
  399. {
  400. NM_LISTVIEW nm;
  401. nm.iItem = i;
  402. nm.iSubItem = iSubItem;
  403. nm.uNewState = nm.uOldState = 0;
  404. nm.uChanged = 0;
  405. nm.lParam = 0;
  406. if (!ListView_IsOwnerData(plv))
  407. {
  408. if (code == LVN_DELETEITEM)
  409. {
  410. LISTITEM * pItem = ListView_GetItemPtr(plv, i);
  411. if (pItem)
  412. nm.lParam = pItem->lParam;
  413. }
  414. }
  415. return (BOOL)CCSendNotify(&plv->ci, code, &nm.hdr);
  416. }
  417. BOOL ListView_GetEmptyText(LV* plv)
  418. {
  419. BOOL fRet;
  420. if (plv->fNoEmptyText)
  421. {
  422. fRet = FALSE;
  423. }
  424. else if (plv->pszEmptyText)
  425. {
  426. fRet = TRUE;
  427. }
  428. else
  429. {
  430. TCHAR szText[80];
  431. NMLVDISPINFO nm = {0};
  432. // For each listview control, we will only send this notify
  433. // once if necessary.
  434. szText[0] = TEXT('\0');
  435. nm.item.mask = LVIF_TEXT;
  436. nm.item.pszText = szText;
  437. nm.item.cchTextMax = ARRAYSIZE(szText);
  438. fRet = (BOOL)CCSendNotify(&plv->ci, LVN_GETEMPTYTEXT, &nm.hdr);
  439. if (fRet)
  440. {
  441. // save the text so we don't notify again.
  442. Str_Set(&plv->pszEmptyText, szText);
  443. }
  444. else
  445. {
  446. // set a flag so we don't notify again.
  447. plv->fNoEmptyText = TRUE;
  448. }
  449. }
  450. return fRet;
  451. }
  452. void ListView_NotifyFocusEvent(LV *plv)
  453. {
  454. if (plv->iFocus != -1 && IsWindowVisible(plv->ci.hwnd) && GetFocus() == plv->ci.hwnd)
  455. NotifyWinEvent(EVENT_OBJECT_FOCUS, plv->ci.hwnd, OBJID_CLIENT,
  456. plv->iFocus+1);
  457. }
  458. //
  459. // Call this function when the listview has changed in a radical manner.
  460. // It notifies MSAA that "Whoa, things are completely different now."
  461. //
  462. void ListView_NotifyRecreate(LV *plv)
  463. {
  464. NotifyWinEvent(EVENT_OBJECT_DESTROY, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  465. NotifyWinEvent(EVENT_OBJECT_CREATE, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  466. plv->iMSAAMin = plv->iMSAAMax = 0;
  467. }
  468. int ListView_OnSetItemCount(LV *plv, int iItems, DWORD dwFlags)
  469. {
  470. BOOL frt = TRUE;
  471. // For compatability we assume 0 for flags implies old (Athena) type of functionality and
  472. // does a Invalidate all otherwise if low bit is set we try to be a bit smarter. First pass
  473. // If the first added item is visible invalidate all. Yes we can do better...
  474. if (ListView_IsOwnerData(plv))
  475. {
  476. int iItem;
  477. int cTotalItemsOld = plv->cTotalItems;
  478. BOOL fInvalidateAll = ((dwFlags & LVSICF_NOINVALIDATEALL) == 0);
  479. if ((iItems >= 0) && (iItems <= MAX_LISTVIEWITEMS))
  480. {
  481. plv->cTotalItems = iItems;
  482. // check focus
  483. if (plv->iFocus >= iItems)
  484. plv->iFocus = -1;
  485. if (plv->iDropHilite >= iItems)
  486. plv->iDropHilite = -1;
  487. // check mark
  488. if (plv->iMark >= iItems)
  489. plv->iMark = -1;
  490. // make sure no selections above number of items
  491. plv->plvrangeCut->lpVtbl->ExcludeRange(plv->plvrangeCut, iItems, SELRANGE_MAXVALUE);
  492. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, iItems, SELRANGE_MAXVALUE)))
  493. {
  494. SetLastError(ERROR_OUTOFMEMORY);
  495. return FALSE;
  496. }
  497. plv->rcView.left = RECOMPUTE; // recompute view rect
  498. if (ListView_IsAutoArrangeView(plv))
  499. {
  500. // Call off to the arrange function.
  501. ListView_OnArrange(plv, LVA_DEFAULT);
  502. if (!fInvalidateAll)
  503. {
  504. // Try to be smart and invalidate only what we need to.
  505. // Add a little logic to erase any message like no items found when
  506. // the view was previously empty...
  507. if (cTotalItemsOld < iItems)
  508. iItem = cTotalItemsOld;
  509. else
  510. iItem = iItems - 1; // Get the index
  511. if ((iItem >= 0) && (cTotalItemsOld > 0))
  512. ListView_IInvalidateBelow(plv, iItem);
  513. else
  514. fInvalidateAll = TRUE;
  515. }
  516. }
  517. else
  518. {
  519. ListView_Recompute(plv);
  520. // if we have empty text and old count was zero... then we should redraw all
  521. if (plv->pszEmptyText && (cTotalItemsOld == 0) && (iItems > 0))
  522. fInvalidateAll = TRUE;
  523. // Try to do smart invalidates...
  524. if (!fInvalidateAll)
  525. {
  526. // Try to be smart and invalidate only what we need to.
  527. if (cTotalItemsOld < iItems)
  528. iItem = cTotalItemsOld;
  529. else
  530. iItem = iItems - 1; // Get the index
  531. if (iItem >= 0)
  532. ListView_LRInvalidateBelow(plv, iItem, FALSE);
  533. }
  534. // We may try to resize the column
  535. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  536. // For compatability we assume 0 for flags implies old type
  537. // of functionality and scrolls the important item into view.
  538. // If second bit is set, we leave the scroll position alone.
  539. if ((dwFlags & LVSICF_NOSCROLL) == 0)
  540. {
  541. // what is the important item
  542. iItem = (plv->iFocus >= 0) ?
  543. plv->iFocus :
  544. ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  545. iItem = max(0, iItem);
  546. // make important item visable
  547. ListView_OnEnsureVisible(plv, iItem, FALSE);
  548. }
  549. }
  550. if (fInvalidateAll)
  551. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  552. ListView_UpdateScrollBars(plv);
  553. ListView_NotifyRecreate(plv);
  554. ListView_NotifyFocusEvent(plv);
  555. }
  556. else
  557. {
  558. frt = FALSE;
  559. }
  560. }
  561. else
  562. {
  563. if (plv->hdpaSubItems)
  564. {
  565. int iCol;
  566. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  567. {
  568. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  569. if (hdpa) // this is optional, call backs don't have them
  570. DPA_Grow(hdpa, iItems);
  571. }
  572. }
  573. DPA_Grow(plv->hdpa, iItems);
  574. DPA_Grow(plv->hdpaZOrder, iItems);
  575. }
  576. return frt;
  577. }
  578. VOID ListView_InvalidateTTLastHit(LV* plv, int iNewHit)
  579. {
  580. if (plv->iTTLastHit == iNewHit)
  581. {
  582. plv->iTTLastHit = -1;
  583. if (plv->pszTip && plv->pszTip != LPSTR_TEXTCALLBACK)
  584. {
  585. plv->pszTip[0] = 0;
  586. }
  587. }
  588. }
  589. typedef struct
  590. {
  591. LV *plv;
  592. BOOL fSortIndices;
  593. PFNLVCOMPARE pfnCompare;
  594. LPARAM lParam;
  595. BOOL bPassLP;
  596. } LVSortInfo;
  597. int CALLBACK ListView_SortCallback(void * dw1, void * dw2, LPARAM lParam)
  598. {
  599. LISTITEM *pitem1;
  600. LISTITEM *pitem2;
  601. LVSortInfo *pSortInfo = (LVSortInfo *)lParam;
  602. ASSERT(!ListView_IsOwnerData(pSortInfo->plv));
  603. // determine whether dw1 and dw2 are indices or the real items
  604. // and assign pitem? accordingly
  605. if (pSortInfo->fSortIndices)
  606. {
  607. pitem1 = ListView_GetItemPtr(pSortInfo->plv, PtrToUlong(dw1));
  608. pitem2 = ListView_GetItemPtr(pSortInfo->plv, PtrToUlong(dw2));
  609. }
  610. else
  611. {
  612. pitem1 = (LISTITEM *)dw1;
  613. pitem2 = (LISTITEM *)dw2;
  614. }
  615. if (!pSortInfo->pfnCompare)
  616. {
  617. // Treat NULL pszText like null string.
  618. LPCTSTR pszText1 = pitem1->pszText ? pitem1->pszText : c_szNULL;
  619. LPCTSTR pszText2 = pitem2->pszText ? pitem2->pszText : c_szNULL;
  620. // REARCHITECT: should allow callbacks in text
  621. if (pszText1 != LPSTR_TEXTCALLBACK &&
  622. pszText2 != LPSTR_TEXTCALLBACK)
  623. {
  624. return lstrcmpi(pitem1->pszText, pitem2->pszText);
  625. }
  626. RIPMSG(0, "LVM_SORTITEM(EX): Cannot combine NULL callback with LPSTR_TEXTCALLBACK");
  627. return -1;
  628. }
  629. else
  630. {
  631. if (pSortInfo->bPassLP)
  632. return pSortInfo->pfnCompare(pitem1->lParam, pitem2->lParam, pSortInfo->lParam);
  633. else
  634. {
  635. if (pSortInfo->fSortIndices)
  636. return pSortInfo->pfnCompare((LPARAM)dw1, (LPARAM)dw2, pSortInfo->lParam);
  637. else
  638. {
  639. // we want to sort by the indices, but all we've got are pointers to the items
  640. // and there is no way to get back from that pointer to an index
  641. RIPMSG(0, "LVM_SORTITEM(EX): Want to sort by indicies, but only have pointers");
  642. return -1;
  643. }
  644. }
  645. }
  646. RIPMSG(0, "LVM_SORTITEM(EX): Didn't seem to sort by anything");
  647. return -1;
  648. }
  649. LISTGROUP* ListView_FindGroupFromID(LV* plv, int iGroupId, int* piIndex)
  650. {
  651. if (plv->hdpaGroups)
  652. {
  653. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  654. int iGroup;
  655. for (iGroup = 0; iGroup < cGroups; iGroup++)
  656. {
  657. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  658. if (pgrp->iGroupId == iGroupId)
  659. {
  660. if (piIndex)
  661. *piIndex = iGroup;
  662. return pgrp;
  663. }
  664. }
  665. }
  666. return NULL;
  667. }
  668. BOOL ListView_VerifyGroupIdIsUnique(LV* plv, int iGroupId)
  669. {
  670. if (plv->hdpaGroups)
  671. {
  672. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  673. int iGroup;
  674. for (iGroup = 0; iGroup < cGroups; iGroup++)
  675. {
  676. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  677. if (pgrp->iGroupId == iGroupId)
  678. return FALSE;
  679. }
  680. }
  681. return TRUE;
  682. }
  683. BOOL ListView_RemoveItemFromItsGroup(LV* plv, LISTITEM* pitem)
  684. {
  685. if (LISTITEM_HASGROUP(pitem))
  686. {
  687. LISTGROUP* pgrp = pitem->pGroup;
  688. int cItems = DPA_GetPtrCount(pgrp->hdpa);
  689. int iItem;
  690. for (iItem = 0; iItem < cItems; iItem++)
  691. {
  692. LISTITEM* pgitem = DPA_FastGetPtr(pgrp->hdpa, iItem);
  693. if (pgitem == pitem)
  694. {
  695. DPA_DeletePtr(pgrp->hdpa, iItem);
  696. return TRUE;
  697. }
  698. }
  699. }
  700. return FALSE;
  701. }
  702. BOOL ListView_FixupGroupsAfterSorting(LV *plv)
  703. {
  704. BOOL fRet = FALSE;
  705. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  706. int *rgiGroupIds = LocalAlloc(LPTR, sizeof(int) * cGroups);
  707. // rgi will be where we keep the index in each group as we add items to them
  708. int *rgi = LocalAlloc(LPTR, sizeof(int) * cGroups);
  709. if (rgiGroupIds && rgi)
  710. {
  711. int i;
  712. int iMax = DPA_GetPtrCount(plv->hdpa);
  713. // Save away the group IDs, and temporary replace them with straight indices
  714. for (i=0; i < cGroups; i++)
  715. {
  716. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  717. rgiGroupIds[i] = pgrp->iGroupId;
  718. pgrp->iGroupId = i;
  719. }
  720. // Now all the items are sorted, and all we need to do it put them back in their
  721. // respective groups is sorted order
  722. for (i=0; i < iMax;i++)
  723. {
  724. LISTITEM *pitem = ListView_FastGetItemPtr(plv, i);
  725. LISTGROUP* pgrp = LISTITEM_GROUP(pitem);
  726. if (pgrp)
  727. {
  728. ASSERT(pgrp->hdpa);
  729. DPA_SetPtr(pgrp->hdpa, rgi[pgrp->iGroupId]++, pitem);
  730. }
  731. }
  732. #if DEBUG
  733. // At this point, we should still have the proper number of items in each group!
  734. for (i=0; i < cGroups; i++)
  735. {
  736. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  737. ASSERT(DPA_GetPtrCount(pgrp->hdpa) == rgi[i]);
  738. }
  739. #endif
  740. // Restore the proper GroupIds now
  741. for (i=0; i < cGroups; i++)
  742. {
  743. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, i);
  744. pgrp->iGroupId = rgiGroupIds[i];
  745. }
  746. fRet = TRUE;
  747. }
  748. LocalFree(rgiGroupIds);
  749. LocalFree(rgi);
  750. return fRet;
  751. }
  752. BOOL ListView_SortAllColumns(LV* plv, LVSortInfo * psi)
  753. {
  754. BOOL fReturn;
  755. ASSERT(!ListView_IsOwnerData(plv));
  756. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  757. // don't do this optimization if we will need the indices to sort by
  758. if (psi->bPassLP &&
  759. ((!plv->hdpaSubItems) ||
  760. !DPA_GetPtrCount(plv->hdpaSubItems)))
  761. {
  762. psi->fSortIndices = FALSE;
  763. fReturn = DPA_Sort(plv->hdpa, ListView_SortCallback, (LPARAM)psi);
  764. }
  765. else
  766. {
  767. // if we need to sort several hdpa's, create one DPA of just indices
  768. // and sort that, then fix up all the dpa's
  769. // initialize the hdpa with indices
  770. HDPA hdpa = DPA_Clone(plv->hdpa, NULL);
  771. fReturn = FALSE;
  772. if (hdpa)
  773. {
  774. int i, iMax;
  775. void **ph;
  776. void **pNewIndices;
  777. ASSERT(DPA_GetPtrCount(plv->hdpa) == DPA_GetPtrCount(hdpa));
  778. ph = pNewIndices = DPA_GetPtrPtr(hdpa);
  779. iMax = DPA_GetPtrCount(hdpa);
  780. for (i = 0; i < iMax; ph++, i++)
  781. {
  782. *ph = IntToPtr(i);
  783. }
  784. psi->fSortIndices = TRUE;
  785. if (DPA_Sort(hdpa, ListView_SortCallback, (LPARAM)psi))
  786. {
  787. ph = LocalAlloc(LPTR, sizeof(void *) * iMax);
  788. if (ph)
  789. {
  790. int j;
  791. void **pSubItems;
  792. // we could get here because bPassLP is false, even if we don't have subitems
  793. if (plv->hdpaSubItems && DPA_GetPtrCount(plv->hdpaSubItems))
  794. {
  795. for (i = DPA_GetPtrCount(plv->hdpaSubItems) - 1; i >= 0; i--)
  796. {
  797. HDPA hdpaSubItem = ListView_GetSubItemDPA(plv, i);
  798. if (hdpaSubItem)
  799. {
  800. // make sure it's of the right size
  801. while (DPA_GetPtrCount(hdpaSubItem) < iMax)
  802. {
  803. if (DPA_InsertPtr(hdpaSubItem, iMax, NULL) == -1)
  804. goto Bail;
  805. }
  806. // actually copy across the dpa with the new indices
  807. pSubItems = DPA_GetPtrPtr(hdpaSubItem);
  808. for (j = 0; j < iMax; j++)
  809. {
  810. ph[j] = pSubItems[PtrToUlong(pNewIndices[j])];
  811. }
  812. // finally, copy it all back to the pSubItems;
  813. memcpy(pSubItems, ph, sizeof(void *) * iMax);
  814. }
  815. }
  816. }
  817. // now do the main hdpa
  818. pSubItems = DPA_GetPtrPtr(plv->hdpa);
  819. for (j = 0; j < iMax; j++)
  820. {
  821. ph[j] = pSubItems[PtrToUlong(pNewIndices[j])];
  822. }
  823. // finally, copy it all back to the pSubItems;
  824. memcpy(pSubItems, ph, sizeof(void *) * iMax);
  825. fReturn = TRUE;
  826. Bail:
  827. LocalFree(ph);
  828. }
  829. }
  830. DPA_Destroy(hdpa);
  831. }
  832. }
  833. if (fReturn && plv->fGroupView && plv->hdpaGroups && DPA_GetPtrCount(plv->hdpaGroups) > 0 && ListView_IsGroupedView(plv))
  834. {
  835. fReturn = ListView_FixupGroupsAfterSorting(plv);
  836. }
  837. return fReturn;
  838. }
  839. DWORD ListView_OnApproximateViewRect(LV* plv, int iCount, int iWidth, int iHeight)
  840. {
  841. if (iCount == -1)
  842. iCount = ListView_Count(plv);
  843. if (iWidth == -1)
  844. iWidth = plv->sizeClient.cx;
  845. if (iHeight == -1)
  846. iHeight = plv->sizeClient.cy;
  847. return _ListView_ApproximateViewRect(plv, iCount, iWidth, iHeight);
  848. }
  849. DWORD ListView_OnSetLVRangeObject(LV* plv, int iWhich, ILVRange *plvrange)
  850. {
  851. ILVRange **pplvrange;
  852. switch (iWhich)
  853. {
  854. case LVSR_SELECTION:
  855. pplvrange = &plv->plvrangeSel;
  856. break;
  857. case LVSR_CUT:
  858. pplvrange = &plv->plvrangeCut;
  859. break;
  860. default:
  861. return FALSE;
  862. }
  863. if (*pplvrange)
  864. {
  865. // Release the old one
  866. (*pplvrange)->lpVtbl->Release(*pplvrange);
  867. }
  868. *pplvrange = plvrange;
  869. // Hold onto the pointer...
  870. if (plvrange)
  871. plvrange->lpVtbl->AddRef(plvrange);
  872. return TRUE;
  873. }
  874. BOOL ListView_OnSortItems(LV *plv, LPARAM lParam, PFNLVCOMPARE pfnCompare, BOOL bPassLP)
  875. {
  876. LVSortInfo SortInfo;
  877. LISTITEM *pitemFocused;
  878. SortInfo.pfnCompare = pfnCompare;
  879. SortInfo.lParam = lParam;
  880. SortInfo.plv = plv;
  881. SortInfo.bPassLP = bPassLP;
  882. if (ListView_IsOwnerData(plv))
  883. {
  884. RIPMSG(0, "LVM_SORTITEMS: Invalid for owner-data listview");
  885. return FALSE;
  886. }
  887. ListView_DismissEdit(plv, TRUE); // cancel edits
  888. // we're going to mess with the indices, so stash away the pointer to the
  889. // focused item.
  890. if (plv->iFocus != -1)
  891. {
  892. pitemFocused = ListView_GetItemPtr(plv, plv->iFocus);
  893. }
  894. else
  895. pitemFocused = NULL;
  896. if (ListView_SortAllColumns(plv, &SortInfo))
  897. {
  898. // restore the focused item.
  899. if (pitemFocused)
  900. {
  901. int i;
  902. for (i = ListView_Count(plv) - 1; i >= 0 ; i--)
  903. {
  904. if (ListView_GetItemPtr(plv, i) == pitemFocused)
  905. {
  906. plv->iFocus = i;
  907. plv->iMark = i;
  908. }
  909. }
  910. }
  911. if (ListView_IsAutoArrangeView(plv))
  912. {
  913. ListView_CommonArrange(plv, LVA_DEFAULT, plv->hdpa);
  914. }
  915. else
  916. {
  917. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  918. }
  919. // The items in the view have moved around; let apps know
  920. NotifyWinEvent(EVENT_OBJECT_REORDER, plv->ci.hwnd, OBJID_CLIENT, 0);
  921. return TRUE;
  922. }
  923. return FALSE;
  924. }
  925. void ListView_EnableWindow(LV* plv, BOOL wParam)
  926. {
  927. if (wParam)
  928. {
  929. if (plv->ci.style & WS_DISABLED)
  930. {
  931. plv->ci.style &= ~WS_DISABLED; // enabled
  932. ListView_OnSetBkColor(plv, plv->clrBkSave);
  933. }
  934. }
  935. else
  936. {
  937. if (!(plv->ci.style & WS_DISABLED))
  938. {
  939. plv->clrBkSave = plv->clrBk;
  940. plv->ci.style |= WS_DISABLED; // disabled
  941. ListView_OnSetBkColor(plv, g_clrBtnFace);
  942. }
  943. }
  944. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  945. }
  946. BOOL ListView_IsItemVisibleI(LV* plv, int i)
  947. // Assumes parmss ok etc for speed. Called inside region calc code.
  948. {
  949. RECT rcBounds;
  950. // get bounding rect of item
  951. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, &rcBounds, NULL);
  952. // Should perf this up for multimonitor case where there are dead zones in work area...
  953. return RECTS_IN_SIZE(plv->sizeClient, rcBounds);
  954. }
  955. // Helper for ListView_RecalcRegion
  956. #define BitOn(lpbits, x, y, cx) (*((BYTE *)(lpbits + ((y * cx) + (x / 8)))) & (0x80 >> (x % 8)))
  957. void ListView_RecalcRegion(LV* plv, BOOL fForce, BOOL fRedraw)
  958. {
  959. HRGN hrgnUnion = NULL;
  960. HRGN hrgn = NULL;
  961. int i;
  962. HDC hdc = NULL;
  963. BYTE * lpBits = NULL;
  964. HBITMAP hbmp = NULL, hbmpOld = NULL;
  965. RECT rc, rcIcon = {0};
  966. LISTITEM * pitem;
  967. BITMAP bm;
  968. // Bail out if we don't need to do any work
  969. if (!(plv->exStyle & LVS_EX_REGIONAL) || !ListView_RedrawEnabled(plv) ||
  970. (plv->flags & LVF_INRECALCREGION))
  971. return;
  972. // To prevent recursion
  973. plv->flags |= LVF_INRECALCREGION;
  974. if ((ListView_Count(plv) > 0))
  975. {
  976. int cxIcon, cyIcon;
  977. int dxOffset, dyOffset;
  978. // Run through first to see if anything changed - bail if not!
  979. if (!fForce)
  980. {
  981. for (i = 0; i < ListView_Count(plv); i++)
  982. {
  983. pitem = ListView_FastGetItemPtr(plv, i);
  984. if (!ListView_IsItemVisibleI(plv, i))
  985. {
  986. if (pitem->hrgnIcon == (HANDLE)-1 || !pitem->hrgnIcon)
  987. // Item was invisible and still is. Nothing changed.
  988. continue;
  989. if (pitem->hrgnIcon)
  990. {
  991. // Item was visible and now is invisible... Something
  992. // changed.
  993. pitem->ptRgn.x = RECOMPUTE;
  994. pitem->ptRgn.y = RECOMPUTE;
  995. DeleteObject(pitem->hrgnIcon);
  996. pitem->hrgnIcon = NULL;
  997. }
  998. }
  999. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, &rc, NULL, NULL);
  1000. // If the location of the icon or the text rectangle have
  1001. // changed, then we need to continue so that we can recalculate
  1002. // the region.
  1003. if ((pitem->pt.x != pitem->ptRgn.x) ||
  1004. (pitem->pt.y != pitem->ptRgn.y) ||
  1005. (!pitem->hrgnIcon) ||
  1006. !EqualRect((CONST RECT *)&pitem->rcTextRgn, (CONST RECT *)&rc))
  1007. goto changed;
  1008. }
  1009. // If we go through all the items and nothing changed, then
  1010. // we can return without doing any work!
  1011. ASSERT(i == ListView_Count(plv));
  1012. goto exit;
  1013. changed:;
  1014. }
  1015. // Figure out the dimensions of the Icon rectangle - assumes
  1016. // each Icon rectangle is the same size.
  1017. ListView_GetRects(plv, 0, QUERY_DEFAULT, &rcIcon, NULL, NULL, NULL);
  1018. // Center the icon in the rectangle
  1019. CCGetIconSize(&plv->ci, plv->himl, &cxIcon, &cyIcon);
  1020. dxOffset = (rcIcon.right - rcIcon.left - cxIcon) / 2;
  1021. dyOffset = (rcIcon.bottom - rcIcon.top - cyIcon) / 2;
  1022. cxIcon = rcIcon.right - rcIcon.left;
  1023. cyIcon = rcIcon.bottom - rcIcon.top;
  1024. if (!(hdc = CreateCompatibleDC(NULL)) ||
  1025. (!(hbmp = CreateBitmap(cxIcon, cyIcon, 1, 1, NULL))))
  1026. {
  1027. goto BailOut;
  1028. }
  1029. GetObject(hbmp, sizeof(bm), &bm);
  1030. if (!(lpBits = (BYTE *)GlobalAlloc(GPTR, bm.bmWidthBytes * bm.bmHeight)))
  1031. goto BailOut;
  1032. hbmpOld = SelectObject(hdc, hbmp);
  1033. PatBlt(hdc, 0, 0, cxIcon, cyIcon, WHITENESS);
  1034. if (hrgnUnion = CreateRectRgn(0, 0, 0, 0))
  1035. {
  1036. for (i = 0; i < ListView_Count(plv); i++)
  1037. {
  1038. int x, y, iResult;
  1039. BOOL fStarted = FALSE;
  1040. LPRECT lprc;
  1041. pitem = ListView_FastGetItemPtr(plv, i);
  1042. if (pitem->pt.y == RECOMPUTE)
  1043. continue;
  1044. if (!ListView_IsItemVisibleI(plv, i))
  1045. {
  1046. // ignore invisible items
  1047. if (pitem->hrgnIcon && pitem->hrgnIcon!=(HANDLE)-1)
  1048. {
  1049. pitem->ptRgn.x = RECOMPUTE;
  1050. pitem->ptRgn.y = RECOMPUTE;
  1051. DeleteObject(pitem->hrgnIcon);
  1052. pitem->hrgnIcon = (HANDLE)-1;
  1053. }
  1054. continue;
  1055. }
  1056. // Add the region for the icon text first
  1057. ListView_GetRects(plv, i, QUERY_DEFAULT, &rcIcon, &rc, NULL, NULL);
  1058. // If we're in edit mode always use rcTextRgn
  1059. if (i == plv->iEdit)
  1060. lprc = &pitem->rcTextRgn;
  1061. else
  1062. lprc = &rc;
  1063. if (!(hrgn = CreateRectRgnIndirect(lprc)))
  1064. goto Error;
  1065. iResult = CombineRgn(hrgnUnion, hrgn, hrgnUnion, RGN_OR);
  1066. DeleteObject(hrgn);
  1067. if (iResult == ERROR)
  1068. {
  1069. // Error case - out of memory. Just select in a NULL region.
  1070. Error:
  1071. DeleteObject(hrgnUnion);
  1072. hrgnUnion = NULL;
  1073. break;
  1074. }
  1075. // Succeeded, copy the rectangle to rcTextRgn so we
  1076. // can test against it in the future. Don't copy over
  1077. // it if we are in edit mode, the rectangle is used to
  1078. // store the edit window in that case.
  1079. if (plv->iEdit != i)
  1080. CopyRect(&pitem->rcTextRgn, (CONST RECT *)&rc);
  1081. // Now create a region for the icon mask - or use the cached one
  1082. if (!pitem->hrgnIcon || pitem->hrgnIcon == (HANDLE)-1)
  1083. {
  1084. // (pitem->pt.x != pitem->ptRgn.x) ||
  1085. // (pitem->pt.y != pitem->ptRgn.y))
  1086. HRGN hrgnIcon = NULL;
  1087. // On slow machines, we'll just wrap the icon with a rectangle. But on
  1088. // faster machines, we'll build a region that corresponds to the
  1089. // mask for the icon so it looks sweet.
  1090. if (g_fSlowMachine)
  1091. {
  1092. // Modify the rectangle slightly so it looks better
  1093. // Glue the icon and text rectangles together
  1094. rcIcon.bottom = rc.top;
  1095. // Shrink the width of the rectangle so it's only as big as the icon itself
  1096. InflateRect(&rcIcon, -dxOffset, 0);
  1097. hrgnIcon = CreateRectRgnIndirect(&rcIcon);
  1098. }
  1099. else
  1100. {
  1101. // If the image isn't around, get it now.
  1102. if (pitem->iImage == I_IMAGECALLBACK)
  1103. {
  1104. LV_ITEM item;
  1105. item.iItem = i;
  1106. item.iSubItem = 0;
  1107. item.mask = LVIF_IMAGE;
  1108. item.stateMask = LVIS_ALL;
  1109. item.pszText = NULL;
  1110. item.cchTextMax = 0;
  1111. // BOGUS - do we need to worry about our state
  1112. // getting messed up during the callback?
  1113. ListView_OnGetItem(plv, &item);
  1114. }
  1115. ImageList_Draw(plv->himl, pitem->iImage, hdc, 0, 0, ILD_MASK | (pitem->state & LVIS_OVERLAYMASK));
  1116. GetBitmapBits(hbmp, bm.bmWidthBytes * bm.bmHeight, (void *)lpBits);
  1117. for (y = 0; y < cyIcon; y++)
  1118. {
  1119. for (x = 0; x < cxIcon; x++)
  1120. {
  1121. if (!fStarted && !BitOn(lpBits, x, y, bm.bmWidthBytes))
  1122. {
  1123. rc.left = x;
  1124. rc.top = y;
  1125. rc.bottom = y + 1;
  1126. fStarted = TRUE;
  1127. if (x == (cxIcon - 1))
  1128. {
  1129. x++;
  1130. goto AddIt;
  1131. }
  1132. else
  1133. {
  1134. continue;
  1135. }
  1136. }
  1137. if (fStarted && BitOn(lpBits, x, y, bm.bmWidthBytes))
  1138. {
  1139. AddIt:
  1140. rc.right = x;
  1141. //
  1142. // Mirror the region so that the icons get displayed ok. [samera]
  1143. //
  1144. if (plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  1145. {
  1146. int iLeft = rc.left;
  1147. rc.left = (cxIcon - (rc.right+1));
  1148. rc.right = (cxIcon - (iLeft+1));
  1149. OffsetRect(&rc, rcIcon.left - dxOffset, rcIcon.top + dyOffset);
  1150. }
  1151. else
  1152. OffsetRect(&rc, rcIcon.left + dxOffset, rcIcon.top + dyOffset);
  1153. if (hrgn = CreateRectRgnIndirect(&rc))
  1154. {
  1155. if (hrgnIcon || (hrgnIcon = CreateRectRgn(0, 0, 0, 0)))
  1156. iResult = CombineRgn(hrgnIcon, hrgn, hrgnIcon, RGN_OR);
  1157. else
  1158. iResult = ERROR;
  1159. DeleteObject(hrgn);
  1160. }
  1161. if (!hrgn || (iResult == ERROR))
  1162. {
  1163. if (hrgnIcon)
  1164. DeleteObject(hrgnIcon);
  1165. goto Error;
  1166. }
  1167. fStarted = FALSE;
  1168. }
  1169. }
  1170. }
  1171. }
  1172. if (hrgnIcon)
  1173. {
  1174. // Cache it since it takes a long time to build it
  1175. if (pitem->hrgnIcon && pitem->hrgnIcon != (HANDLE)-1)
  1176. DeleteObject(pitem->hrgnIcon);
  1177. pitem->hrgnIcon = hrgnIcon;
  1178. pitem->ptRgn = pitem->pt;
  1179. // Add it to the accumulated window region
  1180. if (ERROR == CombineRgn(hrgnUnion, hrgnIcon, hrgnUnion, RGN_OR))
  1181. goto Error;
  1182. }
  1183. }
  1184. else
  1185. {
  1186. OffsetRgn(pitem->hrgnIcon, pitem->pt.x - pitem->ptRgn.x, pitem->pt.y - pitem->ptRgn.y);
  1187. pitem->ptRgn = pitem->pt;
  1188. if (ERROR == CombineRgn(hrgnUnion, pitem->hrgnIcon, hrgnUnion, RGN_OR))
  1189. goto Error;
  1190. }
  1191. }
  1192. }
  1193. }
  1194. BailOut:
  1195. if (lpBits)
  1196. GlobalFree((HGLOBAL)lpBits);
  1197. if (hbmp)
  1198. {
  1199. SelectObject(hdc, hbmpOld);
  1200. DeleteObject(hbmp);
  1201. }
  1202. if (hdc)
  1203. DeleteDC(hdc);
  1204. // Windows takes ownership of the region when we select it in to the window
  1205. SetWindowRgn(plv->ci.hwnd, hrgnUnion, fRedraw);
  1206. exit:
  1207. plv->flags &= ~LVF_INRECALCREGION;
  1208. }
  1209. HIMAGELIST CreateCheckBoxImagelist(HIMAGELIST himl, BOOL fTree, BOOL fUseColorKey, BOOL fMirror)
  1210. {
  1211. int cxImage, cyImage;
  1212. HBITMAP hbm;
  1213. HBITMAP hbmTemp;
  1214. COLORREF clrMask = CLR_DEFAULT;
  1215. HDC hdcDesk = GetDC(NULL);
  1216. HDC hdc;
  1217. RECT rc;
  1218. int nImages = fTree ? 3 : 2;
  1219. HTHEME hTheme;
  1220. DTBGOPTS dtbg = {sizeof(DTBGOPTS), DTBG_DRAWSOLID, 0,}; // tell drawthemebackground to preserve the alpha channel
  1221. if (!hdcDesk)
  1222. return NULL;
  1223. hdc = CreateCompatibleDC(hdcDesk);
  1224. ReleaseDC(NULL, hdcDesk);
  1225. if (!hdc)
  1226. return NULL;
  1227. hTheme = OpenThemeData(NULL, L"Button");
  1228. // Must protect against ImageList_GetIconSize failing in case app
  1229. // gave us a bad himl
  1230. if (himl && ImageList_GetIconSize(himl, &cxImage, &cyImage))
  1231. {
  1232. // cxImage and cyImage are okay
  1233. }
  1234. else
  1235. {
  1236. cxImage = g_cxSmIcon;
  1237. cyImage = g_cySmIcon;
  1238. }
  1239. himl = ImageList_Create(cxImage, cyImage, ILC_MASK | ILC_COLOR32, 0, nImages);
  1240. hbm = CreateColorBitmap(cxImage * nImages, cyImage);
  1241. // fill
  1242. hbmTemp = SelectObject(hdc, hbm);
  1243. rc.left = rc.top = 0;
  1244. rc.bottom = cyImage;
  1245. rc.right = cxImage * nImages;
  1246. if (!hTheme)
  1247. {
  1248. if (fUseColorKey)
  1249. {
  1250. clrMask = RGB(255,000,255); // magenta
  1251. if (clrMask == g_clrWindow)
  1252. clrMask = RGB(000,000,255); // blue
  1253. }
  1254. else
  1255. {
  1256. clrMask = g_clrWindow;
  1257. }
  1258. // Don't fill the image with the mask when themes are on. We want this to
  1259. // "Alpha blend to zero" or be clear. No transparent blt needed.
  1260. FillRectClr(hdc, &rc, clrMask);
  1261. }
  1262. rc.right = cxImage;
  1263. // now draw the real controls on
  1264. InflateRect(&rc, -g_cxEdge, -g_cyEdge);
  1265. rc.right++;
  1266. rc.bottom++;
  1267. if (fTree)
  1268. OffsetRect(&rc, cxImage, 0);
  1269. if (hTheme)
  1270. {
  1271. DrawThemeBackgroundEx(hTheme, hdc, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, &rc, &dtbg);
  1272. }
  1273. else
  1274. {
  1275. DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT |
  1276. (fUseColorKey? 0 : DFCS_TRANSPARENT));
  1277. }
  1278. OffsetRect(&rc, cxImage, 0);
  1279. // [msadek]; For the mirrored case, there is an off-by-one somewhere in MirrorIcon() or System API.
  1280. // Since I will not be touching MirrorIcon() by any mean and no chance to fix a system API,
  1281. // let's compensate for it here.
  1282. if (fMirror)
  1283. {
  1284. OffsetRect(&rc, -1, 0);
  1285. }
  1286. if (hTheme)
  1287. {
  1288. DrawThemeBackgroundEx(hTheme, hdc, BP_CHECKBOX, CBS_CHECKEDNORMAL, &rc, &dtbg);
  1289. }
  1290. else
  1291. {
  1292. DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | DFCS_CHECKED |
  1293. (fUseColorKey? 0 : DFCS_TRANSPARENT));
  1294. }
  1295. SelectObject(hdc, hbmTemp);
  1296. if (fUseColorKey)
  1297. {
  1298. ImageList_AddMasked(himl, hbm, clrMask);
  1299. }
  1300. else
  1301. {
  1302. ImageList_Add(himl, hbm, NULL);
  1303. }
  1304. if (fMirror)
  1305. {
  1306. HICON hIcon = ImageList_ExtractIcon(0, himl, nImages-1);
  1307. MirrorIcon(&hIcon, NULL);
  1308. ImageList_ReplaceIcon(himl, nImages-1, hIcon);
  1309. }
  1310. DeleteDC(hdc);
  1311. DeleteObject(hbm);
  1312. if (hTheme)
  1313. CloseThemeData(hTheme);
  1314. return himl;
  1315. }
  1316. void ListView_InitCheckBoxes(LV* plv, BOOL fInitializeState)
  1317. {
  1318. HIMAGELIST himlCopy = (plv->himlSmall ? plv->himlSmall : plv->himl);
  1319. HIMAGELIST himl;
  1320. BOOL bMirror = FALSE;
  1321. // [msadek], CheckBoxed need not to be mirrored.
  1322. // mirroer it during imagelist creation time so that it displays correctly
  1323. himl = CreateCheckBoxImagelist(himlCopy, FALSE, TRUE, IS_WINDOW_RTL_MIRRORED(plv->ci.hwnd));
  1324. ImageList_SetBkColor(himl, IsUsingCleartype()? (plv->clrBk) : (CLR_NONE));
  1325. ListView_OnSetImageList(plv, himl, LVSIL_STATE);
  1326. if (fInitializeState)
  1327. ListView_OnSetItemState(plv, -1, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK);
  1328. }
  1329. void ListView_PopBubble(LV *plv)
  1330. {
  1331. if (plv->hwndToolTips)
  1332. SendMessage(plv->hwndToolTips, TTM_POP, 0, 0);
  1333. }
  1334. DWORD ListView_ExtendedStyleChange(LV* plv, DWORD dwNewStyle, DWORD dwExMask)
  1335. {
  1336. DWORD dwOldStyle = plv->exStyle;
  1337. // this will change the listview report size and painting algorithm
  1338. // because of the leading edge, so need to re-update scroll bars
  1339. // and repaint everything
  1340. if (ListView_IsReportView(plv))
  1341. {
  1342. ListView_RUpdateScrollBars(plv);
  1343. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1344. }
  1345. // Change of styles may also changes tooltip policy, so pop it
  1346. ListView_PopBubble(plv);
  1347. if (dwExMask)
  1348. dwNewStyle = (plv->exStyle & ~ dwExMask) | (dwNewStyle & dwExMask);
  1349. // Currently, LVS_EX_REGIONAL, LVS_EX_MULTIWORKAREAS, LVS_EX_HIDELABELS, and
  1350. // LVS_EX_SINGLEROW are only supported for large icon view
  1351. if (!ListView_IsIconView(plv))
  1352. {
  1353. dwNewStyle &= ~(LVS_EX_REGIONAL | LVS_EX_MULTIWORKAREAS | LVS_EX_HIDELABELS | LVS_EX_SINGLEROW);
  1354. }
  1355. // LVS_EX_REGIONAL and LVS_EX_SINGLEROW are not supported for ownerdata
  1356. if (ListView_IsOwnerData(plv))
  1357. {
  1358. dwNewStyle &= ~(LVS_EX_REGIONAL | LVS_EX_SINGLEROW);
  1359. }
  1360. plv->exStyle = dwNewStyle;
  1361. // do any invalidation or whatever is needed here.
  1362. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_HIDELABELS)
  1363. {
  1364. plv->rcView.left = RECOMPUTE;
  1365. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1366. }
  1367. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_GRIDLINES)
  1368. {
  1369. if (ListView_IsReportView(plv))
  1370. {
  1371. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1372. }
  1373. }
  1374. if ((dwOldStyle ^ dwNewStyle) & (LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD |
  1375. LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE |
  1376. LVS_EX_SUBITEMIMAGES | LVS_EX_SNAPTOGRID))
  1377. {
  1378. plv->rcView.left = RECOMPUTE;
  1379. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1380. }
  1381. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_CHECKBOXES)
  1382. {
  1383. if (dwNewStyle & LVS_EX_CHECKBOXES)
  1384. {
  1385. ListView_InitCheckBoxes(plv, TRUE);
  1386. }
  1387. else
  1388. {
  1389. // destroy the check boxes!
  1390. HIMAGELIST himl = ListView_OnSetImageList(plv, NULL, LVSIL_STATE);
  1391. if (himl)
  1392. ImageList_Destroy(himl);
  1393. }
  1394. }
  1395. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_FLATSB)
  1396. {
  1397. if (dwNewStyle & LVS_EX_FLATSB)
  1398. {
  1399. InitializeFlatSB(plv->ci.hwnd);
  1400. if (plv->hwndHdr)
  1401. {
  1402. SetWindowBits(plv->hwndHdr, GWL_STYLE, HDS_FLAT, HDS_FLAT);
  1403. }
  1404. }
  1405. else
  1406. {
  1407. if (plv->hwndHdr)
  1408. {
  1409. SetWindowBits(plv->hwndHdr, GWL_STYLE, HDS_FLAT, 0);
  1410. }
  1411. UninitializeFlatSB(plv->ci.hwnd);
  1412. }
  1413. }
  1414. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_REGIONAL)
  1415. {
  1416. g_fSlowMachine = FALSE;
  1417. if (dwNewStyle & LVS_EX_REGIONAL)
  1418. {
  1419. ListView_RecalcRegion(plv, TRUE, TRUE);
  1420. }
  1421. else
  1422. {
  1423. int i;
  1424. LISTITEM * pitem;
  1425. // Delete all the cached regions, then NULL out our selected region.
  1426. for (i = 0; i < ListView_Count(plv); i++)
  1427. {
  1428. pitem = ListView_FastGetItemPtr(plv, i);
  1429. if (pitem->hrgnIcon && pitem->hrgnIcon!=(HANDLE)-1)
  1430. {
  1431. DeleteObject(pitem->hrgnIcon);
  1432. }
  1433. pitem->hrgnIcon = NULL;
  1434. }
  1435. SetWindowRgn(plv->ci.hwnd, (HRGN)NULL, TRUE);
  1436. }
  1437. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1438. }
  1439. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_SINGLEROW)
  1440. {
  1441. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  1442. }
  1443. if (ListView_IsDoubleBuffer(plv))
  1444. {
  1445. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1446. }
  1447. return dwOldStyle;
  1448. }
  1449. // Bug#94368 raymondc v6.0: Doesn't detect WM_WINDOWPOSCHANGING as a way
  1450. // of being shown. NT5 defview has to hack around it pretty grossly.
  1451. // Fix for v6.0.
  1452. void LV_OnShowWindow(LV* plv, BOOL fShow)
  1453. {
  1454. if (fShow)
  1455. {
  1456. if (!(plv->flags & LVF_VISIBLE))
  1457. {
  1458. plv->flags |= LVF_VISIBLE;
  1459. if (plv->fGroupView)
  1460. _ListView_RecomputeEx(plv, NULL, 0, FALSE);
  1461. ListView_UpdateScrollBars(plv);
  1462. }
  1463. }
  1464. else
  1465. plv->flags &= ~LVF_VISIBLE;
  1466. }
  1467. LRESULT ListView_OnHelp(LV* plv, LPHELPINFO lpHelpInfo)
  1468. {
  1469. // If we're seeing WM_HELP because of our child header control, then
  1470. // munge the HELPINFO structure to use the ListView's control id.
  1471. // win\core\user\combo.c has similiar code to handle the child edit
  1472. // control of a combo box.
  1473. if ((lpHelpInfo != NULL) && (plv->wView == LV_VIEW_DETAILS) &&
  1474. (lpHelpInfo->iCtrlId == LVID_HEADER))
  1475. {
  1476. lpHelpInfo->hItemHandle = plv->ci.hwnd;
  1477. lpHelpInfo->iCtrlId = GetWindowID(plv->ci.hwnd);
  1478. // Shouldn't have to do this: USER would have filled in the appropriate
  1479. // context id by walking up the parent hwnd chain.
  1480. //lpHelpInfo->dwContextId = GetContextHelpId(hwnd);
  1481. }
  1482. return DefWindowProc(plv->ci.hwnd, WM_HELP, 0, (LPARAM)lpHelpInfo);
  1483. }
  1484. DWORD ListView_OnSetIconSpacing(LV* plv, LPARAM lParam)
  1485. {
  1486. DWORD dwOld = MAKELONG(plv->cxIconSpacing, plv->cyIconSpacing);
  1487. int cxIconSpacing, cyIconSpacing;
  1488. if (lParam == (LPARAM)-1)
  1489. {
  1490. // go back to using defaults
  1491. plv->flags &= ~LVF_ICONSPACESET;
  1492. cxIconSpacing = (plv->cxIcon + (g_cxIconSpacing - g_cxIcon));
  1493. cyIconSpacing = (plv->cyIcon + (g_cyIconSpacing - g_cyIcon));
  1494. }
  1495. else
  1496. {
  1497. if (LOWORD(lParam))
  1498. {
  1499. cxIconSpacing = LOWORD(lParam);
  1500. if (ListView_IsDPIScaled(plv))
  1501. CCDPIScaleX(&cxIconSpacing);
  1502. }
  1503. else
  1504. {
  1505. cxIconSpacing = plv->cxIconSpacing;
  1506. }
  1507. if (HIWORD(lParam))
  1508. {
  1509. cyIconSpacing = HIWORD(lParam);
  1510. if (ListView_IsDPIScaled(plv))
  1511. CCDPIScaleY(&cyIconSpacing);
  1512. }
  1513. else
  1514. {
  1515. cyIconSpacing = plv->cyIconSpacing;
  1516. }
  1517. plv->flags |= LVF_ICONSPACESET;
  1518. }
  1519. if ((cxIconSpacing != plv->cxIconSpacing) ||
  1520. (cyIconSpacing != plv->cyIconSpacing))
  1521. {
  1522. plv->cxIconSpacing = cxIconSpacing;
  1523. plv->cyIconSpacing = cyIconSpacing;
  1524. plv->rcView.left = RECOMPUTE;
  1525. // Recomputing is necessary except when snap-to-grid is toggled. Snap to grid assumes icon spacing
  1526. // is the grid, however this is the only style that makes this assumption.
  1527. if(!(plv->exStyle & LVS_EX_SNAPTOGRID))
  1528. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  1529. plv->iFreeSlot = -1;
  1530. }
  1531. return dwOld;
  1532. }
  1533. BOOL ListView_OnSetCursorMsg(LV* plv)
  1534. {
  1535. if (plv->exStyle & (LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE))
  1536. {
  1537. if (plv->iHot != -1)
  1538. {
  1539. if (((plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK)) ||
  1540. ListView_OnGetItemState(plv, plv->iHot, LVIS_SELECTED))
  1541. {
  1542. if (!plv->hCurHot)
  1543. {
  1544. plv->hCurHot = LoadCursor(NULL, IDC_HAND);
  1545. }
  1546. SetCursor(plv->hCurHot);
  1547. return TRUE;
  1548. }
  1549. }
  1550. }
  1551. return FALSE;
  1552. }
  1553. void ListView_OnSetHotItem(LV* plv, int iItem)
  1554. {
  1555. if (iItem != plv->iHot)
  1556. {
  1557. if ((plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  1558. (plv->exStyle & LVS_EX_TWOCLICKACTIVATE)) // We only change visuals for hot with Underline
  1559. {
  1560. BOOL fSelectOnly;
  1561. UINT uInvalidateFlags = RDW_INVALIDATE;
  1562. BOOL fBlended = FALSE;
  1563. // Check to see if the item we are making not is in a blended state
  1564. if (iItem != -1)
  1565. {
  1566. // Cut is blended so we need to erase...
  1567. fBlended = ListView_OnGetItemState(plv, iItem, LVIS_CUT);
  1568. if (!fBlended)
  1569. fBlended = ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  1570. }
  1571. // If we need to erase either one then we erase both.
  1572. if (plv->iHot != -1 && ListView_IsValidItemNumber(plv, plv->iHot) && !fBlended)
  1573. {
  1574. // Cut is blended so we need to erase...
  1575. fBlended = ListView_OnGetItemState(plv, plv->iHot, LVIS_CUT);
  1576. if (!fBlended)
  1577. fBlended = ListView_OnGetItemState(plv, plv->iHot, LVIS_SELECTED);
  1578. }
  1579. if (ImageList_GetFlags(plv->himl) & ILC_COLOR32)
  1580. fBlended = TRUE;
  1581. // Affects only apply if double buffering
  1582. if (ListView_IsDoubleBuffer(plv) ||
  1583. plv->fListviewShadowText ||
  1584. fBlended)
  1585. {
  1586. uInvalidateFlags |= RDW_ERASE;
  1587. }
  1588. fSelectOnly = ListView_FullRowSelect(plv);
  1589. ListView_InvalidateItemEx(plv, plv->iHot, fSelectOnly, uInvalidateFlags, LVIF_TEXT | LVIF_IMAGE);
  1590. ListView_InvalidateItemEx(plv, iItem, fSelectOnly, uInvalidateFlags, LVIF_TEXT | LVIF_IMAGE);
  1591. }
  1592. plv->iHot = iItem;
  1593. }
  1594. }
  1595. BOOL fShouldFirstClickActivate()
  1596. {
  1597. static BOOL fInited = FALSE;
  1598. static BOOL fActivate = TRUE;
  1599. if (!fInited)
  1600. {
  1601. long cb = 0;
  1602. if (RegQueryValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\NoFirstClickActivate"),
  1603. NULL, &cb) == ERROR_SUCCESS)
  1604. {
  1605. fActivate = FALSE;
  1606. }
  1607. fInited = TRUE;
  1608. }
  1609. return fActivate;
  1610. }
  1611. BOOL ChildOfDesktop(HWND hwnd)
  1612. {
  1613. return IsChild(GetShellWindow(), hwnd);
  1614. }
  1615. void ListView_OnMouseMove(LV* plv, int x, int y, UINT uFlags)
  1616. {
  1617. int iItem;
  1618. LV_HITTESTINFO ht;
  1619. NMLISTVIEW nm;
  1620. ht.pt.x = x;
  1621. ht.pt.y = y;
  1622. iItem = ListView_OnSubItemHitTest(plv, &ht);
  1623. if (ht.iSubItem != 0)
  1624. {
  1625. // if we're not in full row select,
  1626. // hitting on a subitem is like hitting on nowhere
  1627. // also, in win95, ownerdraw fixed effectively had full row select
  1628. if (!ListView_FullRowSelect(plv) &&
  1629. !(plv->ci.style & LVS_OWNERDRAWFIXED))
  1630. {
  1631. iItem = -1;
  1632. ht.flags = LVHT_NOWHERE;
  1633. }
  1634. }
  1635. if (ht.flags & LVHT_NOWHERE ||
  1636. ht.flags & LVHT_ONITEMSTATEICON)
  1637. {
  1638. iItem = -1; // this is possible in the list mode (sigh)
  1639. }
  1640. nm.iItem = iItem;
  1641. nm.iSubItem = ht.iSubItem;
  1642. nm.uChanged = 0;
  1643. nm.ptAction.x = x;
  1644. nm.ptAction.y = y;
  1645. if (!CCSendNotify(&plv->ci, LVN_HOTTRACK, &nm.hdr))
  1646. {
  1647. #ifdef DEBUG
  1648. if ((nm.iItem != -1) && nm.iSubItem != 0)
  1649. nm.iItem = -1;
  1650. #endif
  1651. ListView_OnSetHotItem(plv, nm.iItem);
  1652. // Ensure our cursor is correct now since the WM_SETCURSOR
  1653. // message was already generated for this mouse event.
  1654. ListView_OnSetCursorMsg(plv);
  1655. // this lets us know when we've left an item
  1656. // and can then reselect/toggle it on hover events
  1657. if (iItem != plv->iNoHover)
  1658. {
  1659. plv->iNoHover = -1;
  1660. }
  1661. }
  1662. }
  1663. BOOL EditBoxHasFocus()
  1664. {
  1665. HWND hwndFocus = GetFocus();
  1666. if (hwndFocus)
  1667. {
  1668. if (SendMessage(hwndFocus, WM_GETDLGCODE, 0, 0) & DLGC_HASSETSEL)
  1669. return TRUE;
  1670. }
  1671. return FALSE;
  1672. }
  1673. void ListView_OnMouseHover(LV* plv, int x, int y, UINT uFlags)
  1674. {
  1675. int iItem;
  1676. BOOL bSelected;
  1677. LV_HITTESTINFO ht;
  1678. BOOL fControl;
  1679. BOOL fShift;
  1680. BOOL fNotifyReturn = FALSE;
  1681. if (GetCapture() || !ChildOfActiveWindow(plv->ci.hwnd) ||
  1682. EditBoxHasFocus())
  1683. return; // ignore hover while editing or any captured (d/d) operation
  1684. if (CCSendNotify(&plv->ci, NM_HOVER, NULL))
  1685. {
  1686. return;
  1687. }
  1688. // REVIEW: right button implies no shift or control stuff
  1689. // Single selection style also implies no modifiers
  1690. //if (RIGHTBUTTON(keyFlags) || (plv->ci.style & LVS_SINGLESEL))
  1691. if ((plv->ci.style & LVS_SINGLESEL))
  1692. {
  1693. fControl = FALSE;
  1694. fShift = FALSE;
  1695. }
  1696. else
  1697. {
  1698. fControl = GetAsyncKeyState(VK_CONTROL) < 0;
  1699. fShift = GetAsyncKeyState(VK_SHIFT) < 0;
  1700. }
  1701. ht.pt.x = x;
  1702. ht.pt.y = y;
  1703. iItem = ListView_OnHitTest(plv, &ht);
  1704. if (iItem == -1 ||
  1705. iItem == plv->iNoHover)
  1706. return;
  1707. //before we hover select we launch any pending item
  1708. //this prevents clicking on one item and hover selecting other before
  1709. //the timer goes off which result in wrong item being launched
  1710. if (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickHappened && plv->fOneClickOK)
  1711. {
  1712. HWND hwnd = plv->ci.hwnd;
  1713. KillTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED);
  1714. plv->fOneClickHappened = FALSE;
  1715. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  1716. if (!IsWindow(hwnd))
  1717. return;
  1718. }
  1719. plv->iNoHover = iItem;
  1720. bSelected = ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  1721. if (ht.flags & (LVHT_ONITEMLABEL | LVHT_ONITEMICON))
  1722. {
  1723. UINT keyFlags = 0;
  1724. if (fShift)
  1725. keyFlags |= MK_SHIFT;
  1726. if (fControl)
  1727. keyFlags |= MK_CONTROL;
  1728. if (!bSelected)
  1729. {
  1730. // if it wasn't selected, we're about to select it... play
  1731. // a little ditty for us...
  1732. CCPlaySound(c_szSelect);
  1733. }
  1734. ListView_ButtonSelect(plv, iItem, keyFlags, bSelected);
  1735. if (fControl)
  1736. {
  1737. ListView_SetFocusSel(plv, iItem, !fShift, FALSE, !fShift);
  1738. }
  1739. if (!fShift)
  1740. plv->iMark = iItem;
  1741. ListView_OnSetCursorMsg(plv);
  1742. SetFocus(plv->ci.hwnd); // activate this window
  1743. }
  1744. }
  1745. BOOL EqualRects(LPRECT prcNew, LPRECT prcOld, int nRects)
  1746. {
  1747. int i;
  1748. for (i = 0; i < nRects; i++)
  1749. if (!EqualRect(&prcNew[i], &prcOld[i]))
  1750. return FALSE;
  1751. return TRUE;
  1752. }
  1753. BOOL ListView_FindWorkArea(LV * plv, POINT pt, short * piWorkArea)
  1754. {
  1755. int iWork;
  1756. for (iWork = 0; iWork < plv->nWorkAreas; iWork++)
  1757. {
  1758. if (PtInRect(&plv->prcWorkAreas[iWork], pt))
  1759. {
  1760. *piWorkArea = (short)iWork;
  1761. return TRUE;
  1762. }
  1763. }
  1764. // (dli) default case is the primary work area
  1765. *piWorkArea = 0;
  1766. return FALSE;
  1767. }
  1768. void ListView_BullyIconsOnWorkarea(LV * plv, HDPA hdpaLostItems)
  1769. {
  1770. int ihdpa;
  1771. int iFree = -1; // the last free slot number
  1772. LVFAKEDRAW lvfd;
  1773. LV_ITEM item;
  1774. // Caller should've filtered this case out
  1775. ASSERT(DPA_GetPtrCount(hdpaLostItems) > 0);
  1776. // Set up in case caller is customdraw
  1777. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  1778. item.mask = LVIF_PARAM;
  1779. item.iSubItem = 0;
  1780. // Go through my hdpa list of lost icons and try to place them within bound
  1781. for (ihdpa = 0; ihdpa < DPA_GetPtrCount(hdpaLostItems); ihdpa++)
  1782. {
  1783. POINT ptNew, pt;
  1784. RECT rcBound = {0};
  1785. int cxBound, cyBound;
  1786. int iWidth, iHeight;
  1787. int iItem;
  1788. LISTITEM * pitem;
  1789. iItem = PtrToUlong(DPA_GetPtr(hdpaLostItems, ihdpa));
  1790. pitem = ListView_FastGetItemPtr(plv, iItem);
  1791. pt = pitem->pt;
  1792. iWidth = RECTWIDTH(plv->prcWorkAreas[pitem->iWorkArea]);
  1793. iHeight = RECTHEIGHT(plv->prcWorkAreas[pitem->iWorkArea]);
  1794. ListView_GetRects(plv, iItem, QUERY_DEFAULT, NULL, NULL, &rcBound, NULL);
  1795. cxBound = RECTWIDTH(rcBound);
  1796. cyBound = RECTHEIGHT(rcBound);
  1797. pt.x -= plv->prcWorkAreas[pitem->iWorkArea].left;
  1798. pt.y -= plv->prcWorkAreas[pitem->iWorkArea].top;
  1799. if (pt.x < (-cxBound / 2))
  1800. {
  1801. ptNew.x = 0;
  1802. }
  1803. else if (pt.x > (iWidth - (cxBound / 2)))
  1804. {
  1805. ptNew.x = iWidth - cxBound;
  1806. }
  1807. else
  1808. ptNew.x = pt.x;
  1809. if (pt.y < (-cyBound/2))
  1810. {
  1811. ptNew.y = 0;
  1812. }
  1813. else if (pt.y > (iHeight - (cyBound / 2)))
  1814. {
  1815. ptNew.y = iHeight - cyBound;
  1816. }
  1817. else
  1818. ptNew.y = pt.y;
  1819. if ((ptNew.x != pt.x) || (ptNew.y != pt.y))
  1820. {
  1821. BOOL fUpdate;
  1822. RECT rcTest;
  1823. ptNew.x += plv->prcWorkAreas[pitem->iWorkArea].left;
  1824. ptNew.y += plv->prcWorkAreas[pitem->iWorkArea].top;
  1825. // See if the potential rectangle intersects other items.
  1826. rcTest.left = ptNew.x - plv->ptOrigin.x;
  1827. rcTest.top = ptNew.y - plv->ptOrigin.y;
  1828. rcTest.right = rcTest.left + cxBound;
  1829. rcTest.bottom = rcTest.top + cyBound;
  1830. item.iItem = iItem;
  1831. item.lParam = pitem->lParam;
  1832. ListView_BeginFakeItemDraw(&lvfd);
  1833. if (!ListView_IsCleanRect(plv, &rcTest, iItem, QUERY_DEFAULT, &fUpdate, lvfd.nmcd.nmcd.hdc))
  1834. {
  1835. // doh! We hit another item, let's try to find an available location
  1836. // for this item
  1837. BOOL fUpdateSB;
  1838. BOOL fAppendAtEnd = FALSE;
  1839. int iWidth, iHeight;
  1840. int cSlots = ListView_GetSlotCountEx(plv, FALSE, pitem->iWorkArea, &iWidth, &iHeight);
  1841. iFree = ListView_FindFreeSlot(plv, iItem, iFree + 1, cSlots, QUERY_DEFAULT, &fUpdateSB, &fAppendAtEnd, lvfd.nmcd.nmcd.hdc, iWidth, iHeight);
  1842. if (iFree == -1)
  1843. goto SetFirstGuess;
  1844. ListView_SetIconPos(plv, pitem, iFree, cSlots);
  1845. ListView_EndFakeItemDraw(&lvfd);
  1846. continue;
  1847. }
  1848. SetFirstGuess:
  1849. ListView_EndFakeItemDraw(&lvfd);
  1850. ListView_OnSetItemPosition(plv, iItem, ptNew.x, ptNew.y);
  1851. }
  1852. }
  1853. ListView_EndFakeCustomDraw(&lvfd);
  1854. }
  1855. #define DPA_LAST 0x7fffffff
  1856. //
  1857. // ListView_OnSetWorkAreas
  1858. //
  1859. // set the "work areas" for the list view.
  1860. // the "work areas" are a group of sub rectanges of the list view client rect
  1861. // where icons are aranged, and parked by default.
  1862. //
  1863. void ListView_OnSetWorkAreas(LV* plv, int nWorkAreas, LPRECT prc)
  1864. {
  1865. int iWork;
  1866. HDPA hdpaLostItems = NULL;
  1867. RECT rcOldWorkAreas[LV_MAX_WORKAREAS];
  1868. BOOL bAutoArrange = plv->ci.style & LVS_AUTOARRANGE;
  1869. int nOldWorkAreas = plv->nWorkAreas;
  1870. if (nOldWorkAreas > 0)
  1871. {
  1872. ASSERT(plv->prcWorkAreas != NULL);
  1873. memcpy(&rcOldWorkAreas[0], &plv->prcWorkAreas[0], sizeof(RECT) * nOldWorkAreas);
  1874. }
  1875. // for the mirrored case, the coordinates are reversed. IsRectEmpty() will always succeed
  1876. if (nWorkAreas == 0 || prc == NULL || ((IsRectEmpty(prc)) && !(plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)))
  1877. plv->nWorkAreas = 0;
  1878. else
  1879. {
  1880. plv->nWorkAreas = min(nWorkAreas, LV_MAX_WORKAREAS);
  1881. if (plv->prcWorkAreas == NULL)
  1882. plv->prcWorkAreas = (LPRECT)LocalAlloc(LPTR, sizeof(RECT) * LV_MAX_WORKAREAS);
  1883. if (plv->prcWorkAreas == NULL)
  1884. return;
  1885. //Should we check if they intersect? This problem is sort of
  1886. // solved (or made more confusing) by ListView_GetFreeSlot since it checks all of the icons for
  1887. // intersection instead of just the ones in the workarea.
  1888. for (iWork = 0; iWork < plv->nWorkAreas; iWork++)
  1889. CopyRect(&plv->prcWorkAreas[iWork], &prc[iWork]);
  1890. }
  1891. // We don't support workareas for owner-data because our icon placement
  1892. // algorithm (ListView_IGetRectsOwnerData) completely ignores workareas
  1893. // and just dumps the icons in a rectangular array starting at (0,0).
  1894. if (!ListView_IsOwnerData(plv) &&
  1895. plv->nWorkAreas > 0 &&
  1896. ((plv->nWorkAreas != nOldWorkAreas) ||
  1897. (!EqualRects(&plv->prcWorkAreas[0], &rcOldWorkAreas[0], nOldWorkAreas))))
  1898. {
  1899. int iItem;
  1900. LISTITEM * pitem;
  1901. //
  1902. // Subtle - ListView_Recompute cleans up all the RECOMPUTE icons,
  1903. // but in order to do that, it needs to have valid work area
  1904. // rectangles. So the call must happen after the CopyRect but before
  1905. // the loop that checks the icon positions.
  1906. //
  1907. ListView_Recompute(plv);
  1908. for (iItem = 0; iItem < ListView_Count(plv); iItem++)
  1909. {
  1910. pitem = ListView_FastGetItemPtr(plv, iItem);
  1911. if (pitem->pt.x == RECOMPUTE || pitem->pt.y == RECOMPUTE)
  1912. {
  1913. // ListView_Recompute should've fixed these if we were in
  1914. // an iconical view.
  1915. ASSERT(!(ListView_IsIconView(plv) || ListView_IsSmallView(plv)));
  1916. continue;
  1917. }
  1918. // Try to move me to the same location relative to the same workarea.
  1919. // This will give the cool shift effect when tools bars take the border areas.
  1920. // And we only want to do this for the workareas that changed
  1921. // Don't bully the icons on the workareas, Autoarrange will do the work for us
  1922. if (nOldWorkAreas > 0)
  1923. {
  1924. int iOldWorkArea;
  1925. iOldWorkArea = pitem->iWorkArea;
  1926. if (iOldWorkArea >= plv->nWorkAreas)
  1927. {
  1928. // My workarea is gone, put me on the primary workarea i.e. #0
  1929. pitem->iWorkArea = 0;
  1930. if (!bAutoArrange)
  1931. {
  1932. // If this item point location is already in the new primary workarea,
  1933. // move it out, and let ListView_BullyIconsOnWorkarea arrange it to the
  1934. // right place. NOTE: this could happen in the case the old secondary monitor
  1935. // is to the left of the old primary monitor, and user kills the secondary monitor
  1936. if (PtInRect(&plv->prcWorkAreas[0], pitem->pt))
  1937. {
  1938. pitem->pt.x = plv->prcWorkAreas[0].right + 1;
  1939. plv->iFreeSlot = -1; // an item moved -- old slot info is invalid
  1940. }
  1941. goto InsertLostItemsArray;
  1942. }
  1943. }
  1944. else if ((!bAutoArrange) && (!EqualRect(&plv->prcWorkAreas[iOldWorkArea], &rcOldWorkAreas[iOldWorkArea])))
  1945. {
  1946. RECT rcBound = {0};
  1947. POINT ptCenter;
  1948. pitem->pt.x += plv->prcWorkAreas[iOldWorkArea].left - rcOldWorkAreas[iOldWorkArea].left;
  1949. pitem->pt.y += plv->prcWorkAreas[iOldWorkArea].top - rcOldWorkAreas[iOldWorkArea].top;
  1950. // Use the center of this icon to determine whether it's out of bound
  1951. ListView_GetRects(plv, iItem, QUERY_DEFAULT, NULL, NULL, &rcBound, NULL);
  1952. ptCenter.x = pitem->pt.x + RECTWIDTH(rcBound) / 2;
  1953. ptCenter.y = pitem->pt.y + RECTHEIGHT(rcBound) / 2;
  1954. // If this shifted me out of bounds, register to be bullied on the workarea
  1955. if (!PtInRect(&plv->prcWorkAreas[iOldWorkArea], ptCenter))
  1956. {
  1957. InsertLostItemsArray:
  1958. if (!hdpaLostItems)
  1959. {
  1960. hdpaLostItems = DPA_Create(4);
  1961. }
  1962. if (hdpaLostItems)
  1963. DPA_InsertPtr(hdpaLostItems, DPA_LAST, IntToPtr(iItem));
  1964. }
  1965. }
  1966. }
  1967. else
  1968. {
  1969. // My first time in a multi-workarea system, so find out my workarea
  1970. if (!ListView_FindWorkArea(plv, pitem->pt, &(pitem->iWorkArea)) && !bAutoArrange)
  1971. goto InsertLostItemsArray;
  1972. }
  1973. if ((plv->exStyle & LVS_EX_REGIONAL) && (pitem->hrgnIcon))
  1974. {
  1975. if (pitem->hrgnIcon != (HANDLE)-1)
  1976. DeleteObject(pitem->hrgnIcon);
  1977. pitem->hrgnIcon = NULL;
  1978. }
  1979. }
  1980. if (hdpaLostItems)
  1981. {
  1982. ASSERT(!bAutoArrange);
  1983. if (DPA_GetPtrCount(hdpaLostItems) > 0)
  1984. ListView_BullyIconsOnWorkarea(plv, hdpaLostItems);
  1985. DPA_Destroy(hdpaLostItems);
  1986. }
  1987. if (plv->exStyle & LVS_EX_REGIONAL)
  1988. ListView_RecalcRegion(plv, TRUE, TRUE);
  1989. if (ListView_IsSmallView(plv) || ListView_IsIconView(plv))
  1990. ListView_ArrangeOrSnapToGrid(plv);
  1991. }
  1992. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  1993. }
  1994. void ListView_OnGetNumberOfWorkAreas(LV* plv, int * pnWorkAreas)
  1995. {
  1996. if (pnWorkAreas)
  1997. *pnWorkAreas = plv->nWorkAreas;
  1998. }
  1999. void ListView_OnGetWorkAreas(LV* plv, int nWorkAreas, LPRECT prc)
  2000. {
  2001. if (prc != NULL)
  2002. {
  2003. int i;
  2004. for (i = 0; i < min(plv->nWorkAreas, nWorkAreas); i++)
  2005. {
  2006. if (i < plv->nWorkAreas)
  2007. {
  2008. CopyRect(&prc[i], &plv->prcWorkAreas[i]);
  2009. }
  2010. else
  2011. {
  2012. // Set the workareas to all zeros if we don't have it.
  2013. ZeroMemory(&prc[i], sizeof(RECT));
  2014. }
  2015. }
  2016. }
  2017. }
  2018. // test an item to see if it is unfolded (because it is focused)
  2019. BOOL ListView_IsItemUnfolded(LV *plv, int item)
  2020. {
  2021. return plv && (item >= 0) && ListView_IsIconView(plv) &&
  2022. (plv->flags & LVF_UNFOLDED) && (plv->iFocus == item);
  2023. }
  2024. BOOL ListView_IsItemUnfoldedPtr(LV *plv, LISTITEM *pitem)
  2025. {
  2026. return plv && pitem && ListView_IsIconView(plv) &&
  2027. (plv->flags & LVF_UNFOLDED) && (pitem->state & LVIS_FOCUSED);
  2028. }
  2029. // Returns TRUE if unfolding the item will be worthwhile
  2030. BOOL ListView_GetUnfoldedRect(LV* plv, int iItem, RECT *prc)
  2031. {
  2032. ListView_GetRects(plv, iItem, QUERY_DEFAULT, NULL, prc, NULL, NULL);
  2033. return ListView_UnfoldRects(plv, iItem, NULL, prc, NULL, NULL);
  2034. }
  2035. BOOL ListView_OnSetGroupInfoInternal(LV* plv, PLVGROUP plvgrp, LISTGROUP* pgrp)
  2036. {
  2037. if (plvgrp == NULL ||
  2038. plvgrp->cbSize < sizeof(LVGROUP))
  2039. {
  2040. return FALSE;
  2041. }
  2042. if (plvgrp->mask & LVGF_STATE)
  2043. {
  2044. if ((plvgrp->state & LVGS_MASK) != plvgrp->state)
  2045. return FALSE;
  2046. pgrp->state = plvgrp->state;
  2047. }
  2048. if (plvgrp->mask & LVGF_ALIGN)
  2049. {
  2050. if ((plvgrp->uAlign & LVGA_ALIGN_MASK) != plvgrp->uAlign)
  2051. return FALSE;
  2052. pgrp->uAlign = plvgrp->uAlign;
  2053. }
  2054. else
  2055. {
  2056. pgrp->uAlign = LVGA_HEADER_LEFT;
  2057. }
  2058. if (ListView_VerifyGroupIdIsUnique(plv, plvgrp->iGroupId))
  2059. {
  2060. pgrp->iGroupId = plvgrp->iGroupId;
  2061. }
  2062. else
  2063. {
  2064. return FALSE;
  2065. }
  2066. if (plvgrp->mask & LVGF_HEADER)
  2067. {
  2068. Str_SetPtr(&pgrp->pszHeader, plvgrp->pszHeader);
  2069. }
  2070. if (plvgrp->mask & LVGF_FOOTER)
  2071. {
  2072. Str_SetPtr(&pgrp->pszFooter, plvgrp->pszFooter);
  2073. }
  2074. // Update the group.
  2075. InvalidateRect(plv->ci.hwnd, &pgrp->rc, TRUE);
  2076. return TRUE;
  2077. }
  2078. int ListView_OnSetGroupInfo(LV* plv, int iGroupId, PLVGROUP plvgrp)
  2079. {
  2080. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, iGroupId, NULL);
  2081. if (pgrp)
  2082. {
  2083. ListView_OnSetGroupInfoInternal(plv, plvgrp, pgrp);
  2084. return iGroupId;
  2085. }
  2086. return -1;
  2087. }
  2088. int ListView_OnGetGroupInfo(LV* plv, int iGroupId, PLVGROUP plvgrp)
  2089. {
  2090. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, iGroupId, NULL);
  2091. if (plvgrp != NULL &&
  2092. plvgrp->cbSize >= sizeof(LVGROUP) &&
  2093. pgrp)
  2094. {
  2095. if (plvgrp->mask & LVGF_HEADER)
  2096. {
  2097. plvgrp->pszHeader = pgrp->pszHeader;
  2098. }
  2099. if (plvgrp->mask & LVGF_FOOTER)
  2100. {
  2101. plvgrp->pszFooter = pgrp->pszFooter;
  2102. }
  2103. if (plvgrp->mask & LVGF_STATE)
  2104. {
  2105. plvgrp->state = pgrp->state & plvgrp->stateMask;
  2106. }
  2107. if (plvgrp->mask & LVGF_ALIGN)
  2108. {
  2109. plvgrp->uAlign = pgrp->uAlign;
  2110. }
  2111. if (plvgrp->mask & LVGF_GROUPID)
  2112. {
  2113. plvgrp->iGroupId = pgrp->iGroupId;
  2114. }
  2115. return iGroupId;
  2116. }
  2117. return -1;
  2118. }
  2119. LISTGROUP* ListView_CreateGroup(LV* plv, PLVGROUP plvgrp)
  2120. {
  2121. LISTGROUP* pgrp;
  2122. // Validate Group
  2123. if (plvgrp == NULL ||
  2124. plvgrp->cbSize < sizeof(LVGROUP))
  2125. {
  2126. return NULL;
  2127. }
  2128. if (!(plvgrp->mask & LVGF_GROUPID))
  2129. {
  2130. // Have to have a group id...
  2131. return NULL;
  2132. }
  2133. pgrp = LocalAlloc(LPTR, sizeof(LISTGROUP));
  2134. if (pgrp)
  2135. {
  2136. if (!ListView_OnSetGroupInfoInternal(plv, plvgrp, pgrp))
  2137. {
  2138. LocalFree(pgrp);
  2139. return NULL;
  2140. }
  2141. pgrp->hdpa = DPA_Create(5);
  2142. SetRect(&pgrp->rc, 0, 0, 0, 0);
  2143. }
  2144. return pgrp;
  2145. }
  2146. void ListView_FreeGroupItem(LISTGROUP* pgrp)
  2147. {
  2148. DPA_Destroy(pgrp->hdpa);
  2149. Str_SetPtr(&pgrp->pszFooter, NULL);
  2150. Str_SetPtr(&pgrp->pszHeader, NULL);
  2151. LocalFree(pgrp);
  2152. }
  2153. LISTGROUP* ListView_FindFirstVisibleGroup(LV* plv)
  2154. {
  2155. LISTGROUP* pgrp = NULL;
  2156. int iGroup;
  2157. int cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  2158. // Find the first group with an item in it.
  2159. for (iGroup = 0; iGroup < cGroups; iGroup++)
  2160. {
  2161. pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  2162. if (DPA_GetPtrCount(pgrp->hdpa) > 0)
  2163. break;
  2164. }
  2165. return pgrp;
  2166. }
  2167. LRESULT ListView_OnInsertGroup(LV* plv, int iGroup, PLVGROUP plvgrp)
  2168. {
  2169. int iInsert = -1;
  2170. LISTGROUP* pgrp = ListView_CreateGroup(plv, plvgrp);
  2171. if (!pgrp)
  2172. {
  2173. return -1;
  2174. }
  2175. if (iGroup == -1)
  2176. {
  2177. iGroup = DA_LAST;
  2178. }
  2179. if (plv->hdpaGroups == NULL)
  2180. plv->hdpaGroups = DPA_Create(4);
  2181. if (plv->hdpaGroups)
  2182. iInsert = DPA_InsertPtr(plv->hdpaGroups, iGroup, pgrp);
  2183. if (iInsert == -1)
  2184. {
  2185. ListView_FreeGroupItem(pgrp);
  2186. }
  2187. plv->rcView.left = RECOMPUTE;
  2188. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2189. return iInsert;
  2190. }
  2191. LRESULT ListView_OnRemoveGroup(LV* plv, int iGroupId)
  2192. {
  2193. int iIndex;
  2194. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, iGroupId, &iIndex);
  2195. if (pgrp)
  2196. {
  2197. int cItems = DPA_GetPtrCount(pgrp->hdpa);
  2198. int iItem;
  2199. for (iItem = 0; iItem < cItems; iItem++)
  2200. {
  2201. LISTITEM* pitem = DPA_FastGetPtr(pgrp->hdpa, iItem);
  2202. if (pitem)
  2203. {
  2204. LISTITEM_SETHASNOTASKEDFORGROUP(pitem);
  2205. }
  2206. }
  2207. ListView_FreeGroupItem(pgrp);
  2208. DPA_DeletePtr(plv->hdpaGroups, iIndex);
  2209. plv->rcView.left = RECOMPUTE;
  2210. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2211. return iIndex;
  2212. }
  2213. return -1;
  2214. }
  2215. int CALLBACK DestroyGroups(void* pv, void* pvData)
  2216. {
  2217. LISTGROUP* pgrp = (LISTGROUP*)pv;
  2218. ListView_FreeGroupItem(pgrp);
  2219. return 1;
  2220. }
  2221. LRESULT ListView_OnRemoveAllGroups(LV* plv)
  2222. {
  2223. if (!ListView_IsOwnerData(plv) && plv->hdpaGroups)
  2224. {
  2225. int i;
  2226. int cItems = ListView_Count(plv);
  2227. plv->fGroupView = FALSE;
  2228. for (i = 0; i < cItems; i++)
  2229. {
  2230. LISTITEM* pitem = DPA_FastGetPtr(plv->hdpa, i);
  2231. if (pitem)
  2232. LISTITEM_SETHASNOTASKEDFORGROUP(pitem);
  2233. }
  2234. DPA_DestroyCallback(plv->hdpaGroups, DestroyGroups, NULL);
  2235. plv->hdpaGroups = NULL;
  2236. plv->rcView.left = RECOMPUTE;
  2237. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2238. }
  2239. return 1;
  2240. }
  2241. LRESULT ListView_OnSetGroupMetrics(LV* plv, PLVGROUPMETRICS pgm)
  2242. {
  2243. BOOL fRecompute = FALSE;
  2244. if (pgm->mask & LVGMF_BORDERSIZE)
  2245. {
  2246. plv->rcBorder.left = pgm->Left;
  2247. plv->rcBorder.top = pgm->Top;
  2248. plv->rcBorder.right = pgm->Right;
  2249. plv->rcBorder.bottom = pgm->Bottom;
  2250. fRecompute = TRUE;
  2251. }
  2252. if (pgm->mask & LVGMF_BORDERCOLOR)
  2253. {
  2254. plv->crTop = pgm->crTop;
  2255. plv->crLeft = pgm->crLeft;
  2256. plv->crRight = pgm->crRight;
  2257. plv->crBottom = pgm->crBottom;
  2258. }
  2259. if (pgm->mask & LVGMF_TEXTCOLOR)
  2260. {
  2261. plv->crHeader = pgm->crHeader;
  2262. plv->crFooter = pgm->crFooter;
  2263. }
  2264. if (fRecompute)
  2265. {
  2266. plv->rcView.left = RECOMPUTE;
  2267. ListView_Recompute(plv);
  2268. ListView_UpdateScrollBars(plv);
  2269. }
  2270. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2271. return 1;
  2272. }
  2273. LRESULT ListView_OnGetGroupMetrics(LV* plv, PLVGROUPMETRICS pgm)
  2274. {
  2275. if (pgm->mask & LVGMF_BORDERSIZE)
  2276. {
  2277. pgm->Left = plv->rcBorder.left;
  2278. pgm->Top = plv->rcBorder.top;
  2279. pgm->Right = plv->rcBorder.right;
  2280. pgm->Bottom = plv->rcBorder.bottom;
  2281. }
  2282. if (pgm->mask & LVGMF_BORDERCOLOR)
  2283. {
  2284. pgm->crTop = plv->crTop;
  2285. pgm->crLeft = plv->crLeft;
  2286. pgm->crRight = plv->crRight;
  2287. pgm->crBottom = plv->crBottom;
  2288. }
  2289. if (pgm->mask & LVGMF_TEXTCOLOR)
  2290. {
  2291. pgm->crHeader = plv->crHeader;
  2292. pgm->crFooter= plv->crFooter;
  2293. }
  2294. return 1;
  2295. }
  2296. typedef struct
  2297. {
  2298. PFNLVGROUPCOMPARE pfnCompare;
  2299. void * pvData;
  2300. } SORTGROUPDATA;
  2301. int CALLBACK pfnGroupSort(LPARAM one, LPARAM two, LPARAM pvData)
  2302. {
  2303. SORTGROUPDATA* psg = (SORTGROUPDATA*)pvData;
  2304. LISTGROUP* pgrp1 = (LISTGROUP*)one;
  2305. LISTGROUP* pgrp2 = (LISTGROUP*)two;
  2306. if (!one)
  2307. return 1;
  2308. if (!two)
  2309. return -1;
  2310. return psg->pfnCompare(pgrp1->iGroupId, pgrp2->iGroupId, psg->pvData);
  2311. }
  2312. LRESULT ListView_OnSortGroups(LV* plv, PFNLVGROUPCOMPARE pfnCompare, void * pvData)
  2313. {
  2314. if (plv->hdpaGroups)
  2315. {
  2316. SORTGROUPDATA sg;
  2317. sg.pfnCompare = pfnCompare;
  2318. sg.pvData = pvData;
  2319. DPA_Sort(plv->hdpaGroups, (PFNDPACOMPARE)pfnGroupSort, (LPARAM)&sg);
  2320. plv->rcView.left = RECOMPUTE;
  2321. ListView_Recompute(plv);
  2322. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2323. ListView_UpdateScrollBars(plv);
  2324. return 1;
  2325. }
  2326. return 0;
  2327. }
  2328. LRESULT ListView_OnInsertGroupSorted(LV* plv, LVINSERTGROUPSORTED* pinsert)
  2329. {
  2330. int iInsertIndex = -1;
  2331. SORTGROUPDATA sg;
  2332. LISTGROUP* pgrp = ListView_CreateGroup(plv, &pinsert->lvGroup);
  2333. sg.pfnCompare = pinsert->pfnGroupCompare;
  2334. sg.pvData = pinsert->pvData;
  2335. if (pgrp)
  2336. {
  2337. if (plv->hdpaGroups == NULL)
  2338. plv->hdpaGroups = DPA_Create(4);
  2339. if (plv->hdpaGroups)
  2340. {
  2341. iInsertIndex = DPA_SortedInsertPtr(plv->hdpaGroups, pgrp, 0, (PFNDPACOMPARE)pfnGroupSort,
  2342. (LPARAM)&sg, DPAS_INSERTAFTER, pgrp);
  2343. }
  2344. if (iInsertIndex == -1)
  2345. ListView_FreeGroupItem(pgrp);
  2346. plv->rcView.left = RECOMPUTE;
  2347. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2348. }
  2349. return iInsertIndex;
  2350. }
  2351. BOOL ListView_OnSetTileViewInfo(LV* plv, PLVTILEVIEWINFO pTileViewInfo)
  2352. {
  2353. BOOL bRecompute = FALSE;
  2354. if (!pTileViewInfo || (pTileViewInfo->cbSize != sizeof(LVTILEVIEWINFO)))
  2355. return FALSE;
  2356. if (pTileViewInfo->dwMask & LVTVIM_COLUMNS)
  2357. {
  2358. if (plv->cSubItems != pTileViewInfo->cLines)
  2359. {
  2360. bRecompute = TRUE;
  2361. plv->cSubItems = pTileViewInfo->cLines;
  2362. }
  2363. }
  2364. if (pTileViewInfo->dwMask & LVTVIM_TILESIZE)
  2365. {
  2366. DWORD dwTileFlags = pTileViewInfo->dwFlags & (LVTVIF_FIXEDHEIGHT | LVTVIF_FIXEDWIDTH);
  2367. if (plv->dwTileFlags != dwTileFlags)
  2368. {
  2369. plv->dwTileFlags = dwTileFlags;
  2370. bRecompute = TRUE;
  2371. }
  2372. if (ListView_IsDPIScaled(plv))
  2373. {
  2374. CCDPIScaleX(&pTileViewInfo->sizeTile.cx);
  2375. CCDPIScaleY(&pTileViewInfo->sizeTile.cy);
  2376. }
  2377. if ((plv->dwTileFlags & LVTVIF_FIXEDHEIGHT) &&
  2378. plv->sizeTile.cy != pTileViewInfo->sizeTile.cy)
  2379. {
  2380. plv->sizeTile.cy = pTileViewInfo->sizeTile.cy;
  2381. bRecompute = TRUE;
  2382. }
  2383. if ((plv->dwTileFlags & LVTVIF_FIXEDWIDTH) &&
  2384. plv->sizeTile.cx != pTileViewInfo->sizeTile.cx)
  2385. {
  2386. plv->sizeTile.cx = pTileViewInfo->sizeTile.cx;
  2387. bRecompute = TRUE;
  2388. }
  2389. }
  2390. if ((pTileViewInfo->dwMask & LVTVIM_LABELMARGIN) &&
  2391. !EqualRect(&plv->rcTileLabelMargin, &pTileViewInfo->rcLabelMargin))
  2392. {
  2393. plv->rcTileLabelMargin = pTileViewInfo->rcLabelMargin;
  2394. bRecompute = TRUE;
  2395. }
  2396. if (bRecompute)
  2397. {
  2398. ListView_RecalcTileSize(plv);
  2399. plv->rcView.left = RECOMPUTE;
  2400. ListView_Recompute(plv);
  2401. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2402. }
  2403. return TRUE;
  2404. }
  2405. BOOL ListView_OnGetTileViewInfo(LV* plv, PLVTILEVIEWINFO pTileViewInfo)
  2406. {
  2407. if (!pTileViewInfo || (pTileViewInfo->cbSize != sizeof(LVTILEVIEWINFO)))
  2408. return FALSE;
  2409. if (pTileViewInfo->dwMask & LVTVIM_COLUMNS)
  2410. {
  2411. pTileViewInfo->cLines = plv->cSubItems;
  2412. }
  2413. if (pTileViewInfo->dwMask & LVTVIM_TILESIZE)
  2414. {
  2415. pTileViewInfo->dwFlags = plv->dwTileFlags;
  2416. pTileViewInfo->sizeTile = plv->sizeTile;
  2417. }
  2418. if (pTileViewInfo->dwMask & LVTVIM_LABELMARGIN)
  2419. {
  2420. pTileViewInfo->rcLabelMargin = plv->rcTileLabelMargin;
  2421. }
  2422. return TRUE;
  2423. }
  2424. BOOL ListView_OnSetTileInfo(LV* plv, PLVTILEINFO pTileInfo)
  2425. {
  2426. LVITEM lvi;
  2427. if (!pTileInfo || (pTileInfo->cbSize != sizeof(LVTILEINFO)))
  2428. return FALSE;
  2429. lvi.mask = LVIF_COLUMNS;
  2430. lvi.cColumns = pTileInfo->cColumns;
  2431. lvi.puColumns = pTileInfo->puColumns;
  2432. lvi.iSubItem = 0;
  2433. lvi.iItem = pTileInfo->iItem;
  2434. return ListView_OnSetItem(plv, &lvi);
  2435. }
  2436. BOOL ListView_OnGetTileInfo(LV* plv, PLVTILEINFO pTileInfo)
  2437. {
  2438. LVITEM lvi;
  2439. if (!pTileInfo || (pTileInfo->cbSize != sizeof(LVTILEINFO)))
  2440. return FALSE;
  2441. lvi.mask = LVIF_COLUMNS;
  2442. lvi.iSubItem = 0;
  2443. lvi.iItem = pTileInfo->iItem;
  2444. lvi.cColumns = pTileInfo->cColumns;
  2445. lvi.puColumns = pTileInfo->puColumns;
  2446. if (ListView_OnGetItem(plv, &lvi))
  2447. {
  2448. pTileInfo->cColumns = lvi.cColumns;
  2449. return TRUE;
  2450. }
  2451. return FALSE;
  2452. }
  2453. LRESULT ListView_OnSetInsertMark(LV* plv, LPLVINSERTMARK plvim)
  2454. {
  2455. if (plvim->cbSize != sizeof(LVINSERTMARK))
  2456. return 0;
  2457. if (plvim->iItem != plv->iInsertItem ||
  2458. BOOLIFY(plv->fInsertAfter) != BOOLIFY(plvim->dwFlags & LVIM_AFTER))
  2459. {
  2460. if (plv->iInsertItem != -1)
  2461. ListView_InvalidateMark(plv);
  2462. plv->iInsertItem = plvim->iItem;
  2463. plv->fInsertAfter = BOOLIFY(plvim->dwFlags & LVIM_AFTER);
  2464. if (plv->iInsertItem != -1)
  2465. ListView_InvalidateMark(plv);
  2466. }
  2467. return 1;
  2468. }
  2469. LRESULT ListView_OnSetInfoTip(LV *plv, PLVSETINFOTIP plvSetInfoTip)
  2470. {
  2471. LPWSTR pszText = NULL;
  2472. LPWSTR pszProduced = NULL;
  2473. LRESULT lRet = 0;
  2474. // Check size and flags. MBZ for now.
  2475. if (plvSetInfoTip->cbSize == sizeof(LVSETINFOTIP) &&
  2476. plvSetInfoTip->dwFlags == 0 &&
  2477. plvSetInfoTip->pszText != NULL)
  2478. {
  2479. pszText = plvSetInfoTip->pszText;
  2480. // If we are still looking at the same item, then set its text, and pop up the tip.
  2481. if (plvSetInfoTip->iItem == plv->iTTLastHit && plvSetInfoTip->iSubItem == plv->iTTLastSubHit)
  2482. {
  2483. TCHAR szBuf[INFOTIPSIZE];
  2484. BOOL bItemUnfolded;
  2485. BOOL fInfoTip = FALSE;
  2486. szBuf[0] = 0;
  2487. // preload the default tip text for folded items.
  2488. bItemUnfolded = ListView_IsItemUnfolded2(plv, plv->iTTLastHit, plv->iTTLastSubHit, szBuf, ARRAYSIZE(szBuf));
  2489. if (ListView_IsInfoTip(plv) && plv->iTTLastSubHit == 0)
  2490. {
  2491. if (*pszText && lstrcmp(szBuf, pszText) != 0)
  2492. {
  2493. // App changed something - there is a real infotip
  2494. fInfoTip = TRUE;
  2495. }
  2496. }
  2497. else
  2498. {
  2499. pszText = szBuf;
  2500. }
  2501. //
  2502. // Set the margins now before the TTN_SHOW because it will be too late then.
  2503. //
  2504. // We want fat margins if we're an infotip, thin margins if we're an
  2505. // in-place tooltip.
  2506. //
  2507. if (fInfoTip)
  2508. {
  2509. static const RECT rcMargin = {4, 4, 4, 4};
  2510. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  2511. CCSetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  2512. plv->fPlaceTooltip = FALSE; // Set it to TRUE only if Unfolding tip is set
  2513. }
  2514. else
  2515. {
  2516. static const RECT rcMargin = {0, 0, 0, 0};
  2517. plv->fPlaceTooltip = TRUE;
  2518. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  2519. CCResetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  2520. }
  2521. Str_Set(&plv->pszTip, pszText);
  2522. // Re-display tooltip. If tracking, call tracking start code (same as timer code)
  2523. if (!ListView_IsKbdTipTracking(plv))
  2524. lRet = SendMessage(plv->hwndToolTips, TTM_POPUP, 0, 0);
  2525. else
  2526. ListView_OnTimer(plv, IDT_TRACKINGTIP);
  2527. }
  2528. if (pszProduced)
  2529. FreeProducedString(pszProduced);
  2530. }
  2531. return lRet;
  2532. }
  2533. LRESULT ListView_OnNotify(LV* plv, WPARAM wParam, LPNMHDR pnmh)
  2534. {
  2535. // we can't switch on the control ID because the tooltip is a WS_POPUP window
  2536. // and does not have a control ID. (header and tooltip both have 0 as ID)
  2537. if (plv->hwndHdr && (plv->hwndHdr == pnmh->hwndFrom))
  2538. {
  2539. // this is a notify for the header, deal with it as needed
  2540. return ListView_HeaderNotify(plv, (HD_NOTIFY *)pnmh);
  2541. }
  2542. else if (plv->hwndToolTips && (plv->hwndToolTips == pnmh->hwndFrom))
  2543. {
  2544. // implement unfolding the text for items as well as info tip support
  2545. switch (pnmh->code)
  2546. {
  2547. case TTN_NEEDTEXT:
  2548. {
  2549. POINT pt;
  2550. UINT uFlags;
  2551. int iNewHit;
  2552. int iNewSubHit;
  2553. NMTTDISPINFO *pttt = (NMTTDISPINFO *)pnmh;
  2554. // If keyboard tracking, do not hit test based on last cursor position
  2555. if (ListView_IsKbdTipTracking(plv))
  2556. {
  2557. RECT rcItem;
  2558. ListView_GetItemRect(plv->ci.hwnd, plv->iTracking, &rcItem, LVIR_LABEL);
  2559. pt.x = rcItem.left;
  2560. pt.y = rcItem.top;
  2561. }
  2562. else
  2563. GetMessagePosClient(plv->ci.hwnd, &pt);
  2564. iNewHit = _ListView_ItemHitTest(plv, pt.x, pt.y, &uFlags, &iNewSubHit);
  2565. if (iNewHit != plv->iTTLastHit || iNewSubHit != plv->iTTLastSubHit)
  2566. {
  2567. plv->fPlaceTooltip = FALSE; // Set it to TRUE only if Unfolding tip is set
  2568. Str_Set(&plv->pszTip, NULL); // clear the old tip
  2569. plv->iTTLastHit = iNewHit;
  2570. plv->iTTLastSubHit = iNewSubHit;
  2571. if ((iNewHit >= 0) && (plv->iEdit == -1))
  2572. {
  2573. TCHAR szBuf[INFOTIPSIZE], szBuf2[INFOTIPSIZE];
  2574. BOOL bItemUnfolded;
  2575. BOOL fInfoTip = FALSE;
  2576. LPTSTR pszTip = szBuf; // Use this one first
  2577. szBuf[0] = 0;
  2578. szBuf2[0] = 0;
  2579. // preload the tip text for folded items. this
  2580. // may be overridden by callback below
  2581. bItemUnfolded = ListView_IsItemUnfolded2(plv, plv->iTTLastHit, plv->iTTLastSubHit, szBuf, ARRAYSIZE(szBuf));
  2582. // Backup the unfolding text
  2583. StringCchCopy(szBuf2, ARRAYSIZE(szBuf2), szBuf);
  2584. if (ListView_IsInfoTip(plv) && iNewSubHit == 0)
  2585. {
  2586. NMLVGETINFOTIP git;
  2587. git.dwFlags = bItemUnfolded ? LVGIT_UNFOLDED : 0;
  2588. git.pszText = szBuf;
  2589. git.cchTextMax = ARRAYSIZE(szBuf);
  2590. git.iItem = plv->iTTLastHit;
  2591. git.iSubItem = 0;
  2592. git.lParam = 0;
  2593. // for folded items pszText is prepopulated with the
  2594. // item text, clients should append to this
  2595. CCSendNotify(&plv->ci, LVN_GETINFOTIP, &git.hdr);
  2596. if (*szBuf && lstrcmp(szBuf, szBuf2) != 0)
  2597. {
  2598. // App changed something - there is a real infotip
  2599. fInfoTip = TRUE;
  2600. }
  2601. }
  2602. //
  2603. // Set the margins now before the TTN_SHOW because it will be too late then.
  2604. //
  2605. // We want fat margins if we're an infotip, thin margins if we're an
  2606. // in-place tooltip.
  2607. //
  2608. if (fInfoTip)
  2609. {
  2610. static const RECT rcMargin = {4, 4, 4, 4};
  2611. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  2612. CCSetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  2613. }
  2614. else
  2615. {
  2616. static const RECT rcMargin = {0, 0, 0, 0};
  2617. plv->fPlaceTooltip = TRUE;
  2618. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  2619. CCResetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  2620. }
  2621. Str_Set(&plv->pszTip, pszTip);
  2622. }
  2623. }
  2624. pttt->lpszText = plv->pszTip; // here it is...
  2625. }
  2626. break;
  2627. // Handle custom draw as we want the tooltip painted as a multi-line that
  2628. // matches the formatting used by the list view.
  2629. case NM_CUSTOMDRAW:
  2630. {
  2631. LPNMTTCUSTOMDRAW pnm = (LPNMTTCUSTOMDRAW) pnmh;
  2632. if (plv->fPlaceTooltip &&
  2633. (pnm->nmcd.dwDrawStage == CDDS_PREPAINT ||
  2634. pnm->nmcd.dwDrawStage == CDDS_ITEMPREPAINT))
  2635. {
  2636. DWORD dwCustom = 0;
  2637. //
  2638. // Set up the customdraw DC to match the font of the LV item.
  2639. //
  2640. if (plv->iTTLastHit != -1)
  2641. {
  2642. LVFAKEDRAW lvfd;
  2643. LV_ITEM item;
  2644. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  2645. item.iItem = plv->iTTLastHit;
  2646. item.iSubItem = plv->iTTLastSubHit;
  2647. item.mask = LVIF_PARAM;
  2648. ListView_OnGetItem(plv, &item);
  2649. dwCustom = ListView_BeginFakeItemDraw(&lvfd);
  2650. // If client changed the font, then transfer the font
  2651. // from our private hdc into the tooltip's HDC. We use
  2652. // a private HDC because we only want to let the app change
  2653. // the font, not the colors or anything else.
  2654. if (dwCustom & CDRF_NEWFONT)
  2655. {
  2656. SelectObject(pnm->nmcd.hdc, GetCurrentObject(lvfd.nmcd.nmcd.hdc, OBJ_FONT));
  2657. }
  2658. ListView_EndFakeItemDraw(&lvfd);
  2659. ListView_EndFakeCustomDraw(&lvfd);
  2660. }
  2661. //
  2662. // The Large Icon tooltip needs to be drawn specially.
  2663. //
  2664. if (ListView_IsIconView(plv))
  2665. {
  2666. pnm->uDrawFlags &= ~(DT_SINGLELINE|DT_LEFT);
  2667. pnm->uDrawFlags |= DT_CENTER|DT_LVWRAP;
  2668. if (pnm->uDrawFlags & DT_CALCRECT)
  2669. {
  2670. pnm->nmcd.rc.right = pnm->nmcd.rc.left + (plv->cxIconSpacing - g_cxLabelMargin * 2);
  2671. pnm->nmcd.rc.bottom = pnm->nmcd.rc.top + 0x10000; // big number, no limit!
  2672. }
  2673. }
  2674. // Don't return other wacky flags to TT, since all we
  2675. // did was change the font (if even that)
  2676. return dwCustom & CDRF_NEWFONT;
  2677. }
  2678. }
  2679. break;
  2680. case TTN_SHOW:
  2681. if (plv->iTTLastHit != -1)
  2682. {
  2683. if (plv->fPlaceTooltip)
  2684. {
  2685. LPNMTTSHOWINFO psi = (LPNMTTSHOWINFO)pnmh;
  2686. RECT rcLabel;
  2687. // In case we're doing subitem hit-testing
  2688. rcLabel.top = plv->iTTLastSubHit;
  2689. rcLabel.left = LVIR_LABEL;
  2690. // reposition to allign with the text rect and
  2691. // set it to topmost
  2692. if (plv->iTTLastSubHit && ListView_OnGetSubItemRect(plv, plv->iTTLastHit, &rcLabel))
  2693. {
  2694. LV_ITEM item;
  2695. // we got the subitem rect. When we draw subitems, we give
  2696. // them SHDT_EXTRAMARGIN, so we have to also
  2697. rcLabel.left += g_cxLabelMargin * 3;
  2698. rcLabel.right -= g_cxLabelMargin * 3;
  2699. // And take the image into account, too.
  2700. // ListView_OnGetItem will worry about LVS_EX_SUBITEMIMAGES.
  2701. item.mask = LVIF_IMAGE;
  2702. item.iImage = -1;
  2703. item.iItem = plv->iTTLastHit;
  2704. item.iSubItem = plv->iTTLastSubHit;
  2705. ListView_OnGetItem(plv, &item);
  2706. if (item.iImage != -1)
  2707. rcLabel.left += plv->cxSmIcon;
  2708. }
  2709. else
  2710. { // a tip from subitem zero
  2711. ListView_GetUnfoldedRect(plv, plv->iTTLastHit, &rcLabel);
  2712. // SHDrawText actually leaves a g_cxLabelMargin margin
  2713. rcLabel.left += g_cxLabelMargin;
  2714. rcLabel.right -= g_cxLabelMargin;
  2715. }
  2716. // In report and list views, SHDrawText does vertical
  2717. // centering (without consulting the custom-draw client,
  2718. // even, so it just centers by a random amount).
  2719. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  2720. {
  2721. rcLabel.top += (rcLabel.bottom - rcLabel.top - plv->cyLabelChar) / 2;
  2722. }
  2723. SendMessage(plv->hwndToolTips, TTM_ADJUSTRECT, TRUE, (LPARAM)&rcLabel);
  2724. MapWindowRect(plv->ci.hwnd, HWND_DESKTOP, &rcLabel);
  2725. if (!ListView_IsIconView(plv))
  2726. {
  2727. // In non-large-icon view, the label size may be greater than the rect returned by ListView_GetUnfoldedRect.
  2728. // So don't specify the size
  2729. SetWindowPos(plv->hwndToolTips, HWND_TOP,
  2730. rcLabel.left, rcLabel.top,
  2731. 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW);
  2732. }
  2733. else
  2734. {
  2735. SetWindowPos(plv->hwndToolTips, HWND_TOP,
  2736. rcLabel.left, rcLabel.top,
  2737. (rcLabel.right - rcLabel.left), (rcLabel.bottom - rcLabel.top),
  2738. SWP_NOACTIVATE | SWP_HIDEWINDOW);
  2739. }
  2740. // This is an inplace tooltip, so disable animation.
  2741. psi->dwStyle |= TTS_NOANIMATE;
  2742. return TRUE;
  2743. }
  2744. else if (ListView_IsKbdTipTracking(plv)) // Size tip when keyboard tracking
  2745. {
  2746. RECT rc;
  2747. RECT rcTT;
  2748. RECT rcItem;
  2749. POINT ptTT;
  2750. POINT ptItem;
  2751. MONITORINFO mi = {0};
  2752. mi.cbSize = sizeof(MONITORINFO);
  2753. // Establish item screen position and size
  2754. ListView_GetItemRect(plv->ci.hwnd, plv->iTracking, &rcItem, LVIR_ICON);
  2755. ListView_GetItemRect(plv->ci.hwnd, plv->iTracking, &rc, LVIR_BOUNDS);
  2756. rcItem.top = rc.top;
  2757. rcItem.bottom = rc.bottom;
  2758. ptItem.x = rcItem.left;
  2759. ptItem.y = rcItem.top;
  2760. ClientToScreen(plv->ci.hwnd, &ptItem);
  2761. // Get tip rect
  2762. GetWindowRect(plv->hwndToolTips, &rcTT);
  2763. // Init tooltip position
  2764. ptTT.x = ptItem.x + RECTWIDTH(rcItem);
  2765. ptTT.y = ptItem.y + RECTHEIGHT(rcItem);
  2766. // Get screen info where tooltip is being displayed
  2767. GetMonitorInfo(MonitorFromPoint(ptTT, MONITOR_DEFAULTTONEAREST), &mi);
  2768. // Update tooltip position if it runs off the screen
  2769. if ((ptTT.x + RECTWIDTH(rcTT)) > mi.rcMonitor.right)
  2770. ptTT.x = (ptItem.x + g_cxIconMargin) - RECTWIDTH(rcTT);
  2771. if ((ptTT.y + RECTHEIGHT(rcTT)) > mi.rcMonitor.bottom)
  2772. ptTT.y = ptItem.y - RECTHEIGHT(rcTT);
  2773. SetWindowPos(plv->hwndToolTips, NULL, ptTT.x, ptTT.y, 0, 0, SWP_NOSIZE|SWP_NOACTIVATE);
  2774. return TRUE;
  2775. }
  2776. }
  2777. break;
  2778. }
  2779. }
  2780. return 0;
  2781. }
  2782. // Pass the focus to the given window, and then check to see if it exists.
  2783. // Passing focus can cause the window to be destroyed (by the Explorer
  2784. // when renaming).
  2785. BOOL ListView_SetFocus(HWND hwnd)
  2786. {
  2787. SetFocus(hwnd);
  2788. return IsWindow(hwnd);
  2789. }
  2790. void ListView_Realize(LV* plv, HDC hdcParam, BOOL fBackground, BOOL fForceRepaint)
  2791. {
  2792. if (plv->hpalHalftone)
  2793. {
  2794. HDC hdc = hdcParam ? hdcParam : GetDC(plv->ci.hwnd);
  2795. if (hdc)
  2796. {
  2797. BOOL fRepaint;
  2798. SelectPalette(hdc, plv->hpalHalftone, fBackground);
  2799. fRepaint = RealizePalette(hdc) || fForceRepaint;
  2800. if (!hdcParam)
  2801. ReleaseDC(plv->ci.hwnd, hdc);
  2802. if (fRepaint)
  2803. {
  2804. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2805. }
  2806. }
  2807. }
  2808. }
  2809. BOOL RectInRect(const RECT* prcOuter, const RECT* prcInner)
  2810. {
  2811. RECT rcDummy;
  2812. return IntersectRect(&rcDummy, prcOuter, prcInner);
  2813. }
  2814. LRESULT LVGenerateDragImage(LV* plv, SHDRAGIMAGE* pshdi)
  2815. {
  2816. LRESULT lRet = 0;
  2817. int iNumSelected = plv->nSelected;
  2818. int iIndex;
  2819. int iSelectedItem;
  2820. RECT rc = {0, 0, 0, 0};
  2821. RECT rcVisRect;
  2822. HBITMAP hbmpOld = NULL;
  2823. HDC hdcDragImage;
  2824. BOOL fBorderSelect = (plv->exStyle & LVS_EX_BORDERSELECT);
  2825. // First loop through can get the selection rect
  2826. if (ListView_IsOwnerData(plv))
  2827. {
  2828. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &iNumSelected);
  2829. }
  2830. if (iNumSelected == 0)
  2831. return FALSE;
  2832. plv->flags |= LVF_DRAGIMAGE;
  2833. GetClientRect(plv->ci.hwnd, &rcVisRect);
  2834. // Loop Through and calculate the enclosing rect.
  2835. for (iIndex = iNumSelected - 1, iSelectedItem = -1; iIndex >= 0; iIndex--)
  2836. {
  2837. iSelectedItem = ListView_OnGetNextItem(plv, iSelectedItem, LVNI_SELECTED);
  2838. if (iSelectedItem != -1)
  2839. {
  2840. RECT rcItemBounds;
  2841. // Make sure this is in the visible region
  2842. if (ListView_GetItemRect(plv->ci.hwnd, iSelectedItem, &rcItemBounds, LVIR_SELECTBOUNDS) &&
  2843. RectInRect(&rcVisRect, &rcItemBounds))
  2844. {
  2845. UnionRect(&rc, &rc, &rcItemBounds);
  2846. }
  2847. }
  2848. }
  2849. hdcDragImage = CreateCompatibleDC(NULL);
  2850. if (hdcDragImage)
  2851. {
  2852. RGBQUAD* prgbBits;
  2853. BITMAPINFO bi;
  2854. // Need to turn this off because it doesn't look good.
  2855. plv->exStyle &= ~LVS_EX_BORDERSELECT;
  2856. // After this rc contains the bounds of all the items in Client Coordinates.
  2857. //
  2858. // Mirror the the DC, if the listview is mirrored.
  2859. //
  2860. if (plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  2861. {
  2862. SET_DC_RTL_MIRRORED(hdcDragImage);
  2863. }
  2864. #define MAX_DRAG_RECT_WIDTH 300
  2865. #define MAX_DRAG_RECT_HEIGHT 300
  2866. // If this rect is too big, fix it.
  2867. if (RECTWIDTH(rc) > MAX_DRAG_RECT_WIDTH)
  2868. {
  2869. int iLeft = MAX_DRAG_RECT_WIDTH / 2;
  2870. int iRight = MAX_DRAG_RECT_WIDTH /2;
  2871. int iRectOriginalLeft = rc.left;
  2872. // Is the left boundry outside the visible rect?
  2873. if (rc.left < plv->ptCapture.x - iLeft)
  2874. {
  2875. // Yes, then we have to clip it.
  2876. rc.left = plv->ptCapture.x - iLeft;
  2877. }
  2878. else
  2879. {
  2880. // No? Well then shift the visible rect to the right, so that we have
  2881. // more room.
  2882. iRight += rc.left - (plv->ptCapture.x - iLeft);
  2883. }
  2884. // Is the right boundry outside the visible rect?
  2885. if (rc.right > plv->ptCapture.x + iRight)
  2886. {
  2887. // Yes, then we have to clip it.
  2888. rc.right = plv->ptCapture.x + iRight;
  2889. }
  2890. else
  2891. {
  2892. // No? Then try and add it to the left
  2893. if (rc.left > iRectOriginalLeft)
  2894. {
  2895. rc.left -= iRight - (rc.right - plv->ptCapture.x);
  2896. if (rc.left < iRectOriginalLeft)
  2897. rc.left = iRectOriginalLeft;
  2898. }
  2899. }
  2900. }
  2901. if (RECTHEIGHT(rc) > MAX_DRAG_RECT_HEIGHT)
  2902. {
  2903. // same for top and bottom:
  2904. // Is the top boundry outside the visible rect?
  2905. int iTop = MAX_DRAG_RECT_HEIGHT / 2;
  2906. int iBottom = MAX_DRAG_RECT_HEIGHT /2;
  2907. int iRectOriginalTop = rc.top;
  2908. if (rc.top < plv->ptCapture.y - iTop)
  2909. {
  2910. // Yes, then we have to clip it.
  2911. rc.top = plv->ptCapture.y - iTop;
  2912. }
  2913. else
  2914. {
  2915. // No? Well then shift the visible rect to the right, so that we have
  2916. // more room.
  2917. iBottom += rc.top - (plv->ptCapture.y - iTop);
  2918. }
  2919. // Is the right boundry outside the visible rect?
  2920. if (rc.bottom > plv->ptCapture.y + iBottom)
  2921. {
  2922. // Yes, then we have to clip it.
  2923. rc.bottom = plv->ptCapture.y + iBottom;
  2924. }
  2925. else
  2926. {
  2927. // No? Then try and add it to the top
  2928. if (rc.top > iRectOriginalTop)
  2929. {
  2930. rc.top -= iBottom - (rc.bottom - plv->ptCapture.y);
  2931. if (rc.top < iRectOriginalTop)
  2932. rc.top = iRectOriginalTop;
  2933. }
  2934. }
  2935. }
  2936. pshdi->sizeDragImage.cx = RECTWIDTH(rc) + 1;
  2937. pshdi->sizeDragImage.cy = RECTHEIGHT(rc) + 1;
  2938. bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
  2939. bi.bmiHeader.biWidth = pshdi->sizeDragImage.cx;
  2940. bi.bmiHeader.biHeight = pshdi->sizeDragImage.cy;
  2941. bi.bmiHeader.biPlanes = 1;
  2942. bi.bmiHeader.biBitCount = 32;
  2943. bi.bmiHeader.biCompression = BI_RGB;
  2944. pshdi->hbmpDragImage = CreateDIBSection(hdcDragImage, &bi, DIB_RGB_COLORS, &prgbBits, NULL, 0);
  2945. if (pshdi->hbmpDragImage)
  2946. {
  2947. int iTotal = bi.bmiHeader.biWidth * bi.bmiHeader.biHeight;
  2948. LVDRAWITEM lvdi;
  2949. int cItem;
  2950. RECT rcImage = {0, 0, pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy};
  2951. hbmpOld = SelectObject(hdcDragImage, pshdi->hbmpDragImage);
  2952. ZeroMemory(prgbBits, pshdi->sizeDragImage.cx * pshdi->sizeDragImage.cy);
  2953. pshdi->crColorKey = CLR_NONE;
  2954. // Calculate the offset... The cursor should be in the bitmap rect.
  2955. if (plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  2956. pshdi->ptOffset.x = rc.right - plv->ptCapture.x;
  2957. else
  2958. pshdi->ptOffset.x = plv->ptCapture.x - rc.left;
  2959. pshdi->ptOffset.y = plv->ptCapture.y - rc.top;
  2960. lvdi.prcClip = NULL;
  2961. lvdi.plv = plv;
  2962. lvdi.nmcd.nmcd.hdc = hdcDragImage;
  2963. lvdi.pitem = NULL;
  2964. cItem = ListView_Count(plv);
  2965. // Now loop through again for the paint cycle
  2966. for (iIndex = cItem - 1, iSelectedItem = -1; iIndex >= 0; iIndex--)
  2967. {
  2968. if (ListView_IsOwnerData(plv))
  2969. {
  2970. iSelectedItem++;
  2971. plv->plvrangeSel->lpVtbl->NextSelected(plv->plvrangeSel, iSelectedItem, &iSelectedItem);
  2972. }
  2973. else
  2974. {
  2975. LISTITEM* pitem;
  2976. iSelectedItem = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, iIndex);
  2977. pitem = ListView_FastGetItemPtr(plv, iSelectedItem);
  2978. if (!(pitem->state & LVIS_SELECTED))
  2979. iSelectedItem = -1;
  2980. }
  2981. if (iSelectedItem != -1)
  2982. {
  2983. int iOldItemDrawing;
  2984. COLORREF crSave;
  2985. POINT ptOrigin = {-rc.left, -rc.top}; //Offset the rects by...
  2986. RECT rcItemBounds;
  2987. RECT rcTemp;
  2988. iOldItemDrawing = plv->iItemDrawing;
  2989. plv->iItemDrawing = iSelectedItem;
  2990. lvdi.nmcd.nmcd.dwItemSpec = iSelectedItem;
  2991. ListView_GetRects(plv, iSelectedItem, QUERY_DEFAULT, NULL, NULL, &rcItemBounds, NULL);
  2992. // Make sure this is in the visible region
  2993. if (IntersectRect(&rcTemp, &rcVisRect, &rcItemBounds))
  2994. {
  2995. ptOrigin.x += rcItemBounds.left;
  2996. ptOrigin.y += rcItemBounds.top;
  2997. // these may get changed
  2998. lvdi.lpptOrg = &ptOrigin;
  2999. lvdi.flags = LVDI_NOEFFECTS;
  3000. lvdi.nmcd.clrText = plv->clrText;
  3001. lvdi.nmcd.clrTextBk = plv->clrTextBk;
  3002. lvdi.nmcd.clrFace = plv->clrBk;
  3003. lvdi.nmcd.iIconEffect = ILD_NORMAL;
  3004. lvdi.nmcd.iIconPhase = 0;
  3005. // Save the Background color!
  3006. crSave = plv->clrBk;
  3007. plv->clrBk = CLR_NONE; // None so that it "bleeds" into the alpha channel
  3008. ListView_DrawItem(&lvdi);
  3009. plv->clrBk = crSave;
  3010. }
  3011. plv->iItemDrawing = iOldItemDrawing;
  3012. }
  3013. }
  3014. for (iIndex = 0; iIndex < iTotal; iIndex++)
  3015. {
  3016. RGBQUAD* prgb = &prgbBits[iIndex];
  3017. if (prgb->rgbReserved == 0 &&
  3018. (prgb->rgbRed || prgb->rgbGreen || prgb->rgbBlue)) // Do we have color an no alpha?
  3019. {
  3020. prgb->rgbReserved = 0xFF;
  3021. }
  3022. }
  3023. SelectObject(hdcDragImage, hbmpOld);
  3024. DeleteDC(hdcDragImage);
  3025. // We're passing back the created HBMP.
  3026. lRet = 1;
  3027. }
  3028. if (fBorderSelect)
  3029. plv->exStyle |= LVS_EX_BORDERSELECT;
  3030. }
  3031. plv->flags &= ~LVF_DRAGIMAGE;
  3032. return lRet;
  3033. }
  3034. LRESULT ListView_OnEnableGroupView(LV* plv, BOOL fEnable)
  3035. {
  3036. if (plv->ci.style & LVS_OWNERDATA) // Not supported in ownerdata case.
  3037. return -1;
  3038. if (fEnable ^ plv->fGroupView)
  3039. {
  3040. if (fEnable)
  3041. {
  3042. // Turning on groupview, so nuke insertmark, because that's not allowed
  3043. // in group view
  3044. LVINSERTMARK lvim = {0};
  3045. lvim.cbSize = sizeof(LVINSERTMARK);
  3046. lvim.iItem = -1;
  3047. ListView_OnSetInsertMark(plv, &lvim);
  3048. }
  3049. plv->fGroupView = fEnable;
  3050. if (fEnable)
  3051. {
  3052. if (plv->hdpaGroups == NULL)
  3053. plv->hdpaGroups = DPA_Create(4);
  3054. if (plv->hdpaGroups == NULL)
  3055. return -1;
  3056. }
  3057. plv->rcView.left = RECOMPUTE;
  3058. SetWindowLongPtr(plv->ci.hwnd, GWL_STYLE, GetWindowLongPtr(plv->ci.hwnd, GWL_STYLE) | LVS_AUTOARRANGE);
  3059. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  3060. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3061. ListView_UpdateScrollBars(plv);
  3062. return 1;
  3063. }
  3064. return 0;
  3065. }
  3066. LRESULT ListView_SetViewType(LV* plv, WORD wView)
  3067. {
  3068. if (wView > LV_VIEW_MAX)
  3069. return -1;
  3070. else if (plv->wView != wView)
  3071. {
  3072. int i;
  3073. WORD wViewOld = plv->wView;
  3074. ListView_DismissEdit(plv, FALSE);
  3075. // (dli) Setting the small icon width here and only in the case when we go
  3076. // from large icon view to some other view because of three reasons:
  3077. // 1. According to chee, we want to set this before we change the style bit in
  3078. // plv or after we scale.
  3079. // 2. We don't want to do it after we scale because we want to set the width to
  3080. // the maximum value so that the items in this listview do not cover each other
  3081. // 3. we do it from large icon view because large icon view has fixed width for
  3082. // each item, small icon view width can be scaled.
  3083. if (wViewOld == LV_VIEW_ICON)
  3084. ListView_ISetColumnWidth(plv, 0, LV_GetNewColWidth(plv, 0, ListView_Count(plv)-1), FALSE);
  3085. if (wView == LV_VIEW_TILE)
  3086. {
  3087. ListView_RecalcTileSize(plv);
  3088. }
  3089. plv->wView = wView;
  3090. ListView_TypeChange(plv, wViewOld, BOOLIFY(plv->ci.style & LVS_OWNERDRAWFIXED));
  3091. // Else we would like to make the most important item to still
  3092. // be visible. So first we will look for a cursorered item
  3093. // if this fails, we will look for the first selected item,
  3094. // else we will simply ask for the first item (assuming the
  3095. // count > 0
  3096. //
  3097. // And make sure the scrollbars are up to date Note this
  3098. // also updates some variables that some views need
  3099. ListView_UpdateScrollBars(plv);
  3100. i = (plv->iFocus >= 0) ? plv->iFocus : ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  3101. if ((i == -1) && (ListView_Count(plv) > 0))
  3102. i = 0;
  3103. if (i != -1)
  3104. ListView_OnEnsureVisible(plv, i, TRUE);
  3105. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  3106. // Change of styles also changes tooltip policy, so pop it
  3107. ListView_PopBubble(plv);
  3108. }
  3109. return 1;
  3110. }
  3111. BOOL ListView_OnGetFrozenSlot(LV* plv, LPRECT pSlotRect)
  3112. {
  3113. int cSlots, iWidth = 0, iHeight = 0;
  3114. LISTITEM *pItem;
  3115. if((plv->iFrozenSlot == LV_NOFROZENSLOT) || !ListView_IsIconView(plv) || ListView_IsOwnerData(plv) || (pSlotRect == NULL)) //Supported only in Large Icon mode!
  3116. return FALSE;
  3117. cSlots = ListView_GetSlotCount(plv, TRUE, &iWidth, &iHeight);
  3118. //We need to have a valid pItem to pass to ListView_CalcSlotRect() function.
  3119. pItem = plv->pFrozenItem; //Try to use a frozen item, if present.
  3120. if(pItem == NULL)
  3121. pItem = ListView_GetItemPtr(plv, 0); //Or else, use the first item.
  3122. if(pItem == NULL) //If we couldn't get any pItem, then we can't call CalcSlotRect().
  3123. return FALSE; //... Hence, we have to return failure.
  3124. else
  3125. {
  3126. ListView_CalcSlotRect(plv, pItem, plv->iFrozenSlot, cSlots, FALSE,
  3127. iWidth, iHeight,pSlotRect);
  3128. return TRUE;
  3129. }
  3130. }
  3131. BOOL ListView_OnSetFrozenSlot(LV* plv, BOOL fFreeze, LPPOINT pPt)
  3132. {
  3133. if(!ListView_IsIconView(plv) || ListView_IsOwnerData(plv)) //Supported only in Large Icon mode!
  3134. return FALSE;
  3135. if(fFreeze)
  3136. {
  3137. //First, find the slot where the given point lies.
  3138. int cSlots, iWidth = 0, iHeight = 0;
  3139. cSlots = ListView_GetSlotCount(plv, TRUE, &iWidth, &iHeight);
  3140. plv->iFrozenSlot = ListView_CalcHitSlot(plv, *pPt, cSlots, iWidth, iHeight);
  3141. }
  3142. else
  3143. {
  3144. //Unfreeze a frozen slot.
  3145. plv->iFrozenSlot = LV_NOFROZENSLOT; //No slot is frozen.
  3146. }
  3147. return TRUE;
  3148. }
  3149. int ListView_OnGetFrozenItem(LV* plv)
  3150. {
  3151. int i;
  3152. LISTITEM *pItem;
  3153. if((plv->pFrozenItem == NULL) || !ListView_IsIconView(plv) || ListView_IsOwnerData(plv)) //Supported only in Large Icon mode!
  3154. return LV_NOFROZENITEM;
  3155. for(i = 0; i < ListView_Count(plv); i++)
  3156. {
  3157. pItem = ListView_GetItemPtr(plv, i);
  3158. if((pItem != NULL) && (pItem == plv->pFrozenItem))
  3159. return (i);
  3160. }
  3161. return LV_NOFROZENITEM;
  3162. }
  3163. BOOL ListView_OnSetFrozenItem(LV* plv, BOOL fFreeze, int iIndex)
  3164. {
  3165. LISTITEM *pitem;
  3166. if(!ListView_IsIconView(plv) || ListView_IsOwnerData(plv)) //Supported only in Large Icon mode!
  3167. return FALSE;
  3168. if(fFreeze)
  3169. {
  3170. //Freeze the given item.
  3171. pitem = ListView_GetItemPtr(plv, iIndex);
  3172. if(pitem == NULL)
  3173. return FALSE;
  3174. plv->pFrozenItem = pitem;
  3175. }
  3176. else
  3177. {
  3178. //Unfreeze the currently frozen item.
  3179. plv->pFrozenItem = NULL;
  3180. }
  3181. return TRUE;
  3182. }
  3183. // Item focus changed via the keyboard, start tracking tooltip timeout for keyboard nav popups
  3184. //
  3185. BOOL ListView_OnKeyboardSelected(LV* plv, int iNewFocus)
  3186. {
  3187. if (iNewFocus >= 0 && plv->hwndToolTips)
  3188. {
  3189. // Focus via the keyboard (already cancelled via entry into this function)
  3190. plv->iTracking = iNewFocus;
  3191. // Delay will be replaced with an SPI
  3192. SetTimer(plv->ci.hwnd, IDT_TRACKINGTIP, GetDoubleClickTime() * 2, NULL);
  3193. }
  3194. return TRUE;
  3195. }
  3196. LRESULT ListView_OnMapIndexToID(LV* plv, UINT iItem)
  3197. {
  3198. LISTITEM* pitem;
  3199. if (!ListView_IsValidItemNumber(plv, iItem) || ListView_IsOwnerData(plv))
  3200. {
  3201. return -1;
  3202. }
  3203. pitem = ListView_FastGetItemPtr(plv, iItem);
  3204. ASSERT (pitem);
  3205. return (LRESULT)pitem->dwId;
  3206. }
  3207. #ifdef DEBUG
  3208. UINT uAverageSeekCount = 0;
  3209. UINT uTotalSeeks = 0;
  3210. UINT uPerSeekCount = 0;
  3211. #endif
  3212. LRESULT ListView_OnMapIdToIndex(LV* plv, UINT Id)
  3213. {
  3214. DWORD dwRet = -1;
  3215. UINT cCounter = 0;
  3216. UINT cItems = ListView_Count(plv);
  3217. UINT i;
  3218. if (ListView_IsOwnerData(plv))
  3219. return -1;
  3220. if (plv->iLastId >= cItems)
  3221. plv->iLastId = 0;
  3222. DEBUG_CODE(uTotalSeeks++);
  3223. for (i = plv->iLastId; cCounter < cItems; cCounter++)
  3224. {
  3225. LISTITEM* pitem = ListView_FastGetItemPtr(plv, i);
  3226. if (pitem->dwId == Id)
  3227. {
  3228. if (plv->iLastId > i)
  3229. plv->iIncrement = -1;
  3230. else
  3231. plv->iIncrement = 1;
  3232. plv->iLastId = dwRet = i;
  3233. break;
  3234. }
  3235. DEBUG_CODE(uPerSeekCount++);
  3236. i += (DWORD)plv->iIncrement;
  3237. if (i == -1) // Wrapped around to "Less than zero"?
  3238. i = cItems - 1;
  3239. if (i >= cItems)
  3240. i = 0;
  3241. }
  3242. DEBUG_CODE(uAverageSeekCount = uPerSeekCount / uTotalSeeks);
  3243. return (LRESULT)dwRet;
  3244. }
  3245. void ListView_OnSize(LV* plv)
  3246. {
  3247. if (plv->hwndToolTips)
  3248. {
  3249. TOOLINFO ti;
  3250. if (ListView_IsLabelTip(plv))
  3251. {
  3252. // A truncated label may have been exposed or vice versa.
  3253. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  3254. }
  3255. ti.cbSize = sizeof(ti);
  3256. ti.hwnd = plv->ci.hwnd;
  3257. ti.uId = 0;
  3258. // Resize the tooltip control so that it covers the entire
  3259. // area of the window when its parent gets resized.
  3260. GetClientRect(plv->ci.hwnd, &ti.rect);
  3261. SendMessage(plv->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM) &ti);
  3262. }
  3263. // if we're supposed to center the image,
  3264. // we need to do a full redraw on each size
  3265. if ((plv->ulBkImageFlags & LVBKIF_SOURCE_MASK) &&
  3266. (plv->ulBkImageFlags & LVBKIF_STYLE_MASK) == LVBKIF_STYLE_NORMAL &&
  3267. (plv->xOffsetPercent || plv->yOffsetPercent))
  3268. {
  3269. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3270. }
  3271. }
  3272. BOOL ListView_OnSetViewMargins(LV* plv, RECT *prc)
  3273. {
  3274. if (!IsEqualRect(plv->rcViewMargin, *prc))
  3275. {
  3276. plv->rcViewMargin = *prc;
  3277. plv->rcView.left = RECOMPUTE;
  3278. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3279. }
  3280. return TRUE;
  3281. }
  3282. BOOL ListView_OnGetViewMargins(LV* plv, RECT *prc)
  3283. {
  3284. *prc = plv->rcViewMargin;
  3285. return TRUE;
  3286. }
  3287. LRESULT CALLBACK ListView_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3288. {
  3289. LV* plv = ListView_GetPtr(hwnd);
  3290. if (plv == NULL)
  3291. {
  3292. if (uMsg == WM_NCCREATE)
  3293. {
  3294. plv = (LV*)LocalAlloc(LPTR, sizeof(LV));
  3295. if (!plv)
  3296. {
  3297. TraceMsg(TF_ERROR, "ListView: Out of memory");
  3298. return 0L; // fail the window create
  3299. }
  3300. plv->ci.hwnd = hwnd;
  3301. plv->flags = LVF_REDRAW; // assume that redrawing enabled!
  3302. plv->iFocus = -1; // no focus
  3303. plv->iMark = -1;
  3304. plv->iSelCol = -1;
  3305. plv->iDropHilite = -1; // Assume no item has drop hilite...
  3306. plv->cyItem = plv->cyItemSave = 1; // never let these be zero, not even for a moment
  3307. plv->hTheme = OpenThemeData(hwnd, L"ListView");
  3308. plv->iInsertItem = -1; // No insert mark by default of course
  3309. plv->clrim = CLR_DEFAULT;
  3310. plv->iTracking = LVKTT_NOTRACK;
  3311. plv->hheap = GetProcessHeap();
  3312. plv->iFrozenSlot = LV_NOFROZENSLOT; //No slot is frozen to begin with!
  3313. plv->iIncrement = -1;
  3314. ListView_SetPtr(hwnd, plv);
  3315. }
  3316. goto DoDefault;
  3317. }
  3318. if ((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST))
  3319. {
  3320. if (plv->exStyle & (LVS_EX_TRACKSELECT|LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE))
  3321. {
  3322. TRACKMOUSEEVENT tme;
  3323. tme.cbSize = sizeof(tme);
  3324. tme.hwndTrack = plv->ci.hwnd;
  3325. tme.dwHoverTime = plv->dwHoverTime;
  3326. tme.dwFlags = TME_LEAVE | TME_HOVER | TME_QUERY;
  3327. // see what's set
  3328. TrackMouseEvent(&tme);
  3329. tme.dwFlags &= TME_HOVER | TME_LEAVE;
  3330. // set these bits if they aren't already set
  3331. tme.dwFlags ^= TME_LEAVE;
  3332. if (plv->exStyle & LVS_EX_TRACKSELECT)
  3333. {
  3334. tme.dwFlags ^= TME_HOVER;
  3335. }
  3336. tme.cbSize = sizeof(tme);
  3337. tme.hwndTrack = plv->ci.hwnd;
  3338. tme.dwHoverTime = plv->dwHoverTime;
  3339. // set it if there's anything to set
  3340. if (tme.dwFlags & (TME_HOVER | TME_LEAVE))
  3341. {
  3342. TrackMouseEvent(&tme);
  3343. }
  3344. }
  3345. }
  3346. if (uMsg == g_uDragImages)
  3347. {
  3348. return LVGenerateDragImage(plv, (SHDRAGIMAGE*)lParam);
  3349. }
  3350. switch (uMsg)
  3351. {
  3352. HANDLE_MSG(plv, WM_CREATE, ListView_OnCreate);
  3353. HANDLE_MSG(plv, WM_DESTROY, ListView_OnDestroy);
  3354. HANDLE_MSG(plv, WM_ERASEBKGND, ListView_OnEraseBkgnd);
  3355. HANDLE_MSG(plv, WM_COMMAND, ListView_OnCommand);
  3356. HANDLE_MSG(plv, WM_SETFOCUS, ListView_OnSetFocus);
  3357. HANDLE_MSG(plv, WM_KILLFOCUS, ListView_OnKillFocus);
  3358. HANDLE_MSG(plv, WM_HSCROLL, ListView_OnHScroll);
  3359. HANDLE_MSG(plv, WM_VSCROLL, ListView_OnVScroll);
  3360. HANDLE_MSG(plv, WM_GETDLGCODE, ListView_OnGetDlgCode);
  3361. HANDLE_MSG(plv, WM_SETFONT, ListView_OnSetFont);
  3362. HANDLE_MSG(plv, WM_GETFONT, ListView_OnGetFont);
  3363. HANDLE_MSG(plv, WM_TIMER, ListView_OnTimer);
  3364. HANDLE_MSG(plv, WM_SETREDRAW, ListView_OnSetRedraw);
  3365. HANDLE_MSG(plv, WM_NCDESTROY, ListView_OnNCDestroy);
  3366. case WM_SETCURSOR:
  3367. if (ListView_OnSetCursorMsg(plv))
  3368. return TRUE;
  3369. break;
  3370. case WM_PALETTECHANGED:
  3371. if ((HWND)wParam == hwnd)
  3372. break;
  3373. case WM_QUERYNEWPALETTE:
  3374. // Want to pass FALSE if WM_QUERYNEWPALETTE...
  3375. ListView_Realize(plv, NULL, uMsg == WM_PALETTECHANGED, uMsg == WM_PALETTECHANGED);
  3376. return TRUE;
  3377. case LVMP_WINDOWPOSCHANGED:
  3378. case WM_WINDOWPOSCHANGED:
  3379. HANDLE_WM_WINDOWPOSCHANGED(plv, wParam, lParam, ListView_OnWindowPosChanged);
  3380. break;
  3381. case WM_WINDOWPOSCHANGING:
  3382. {
  3383. WINDOWPOS* wp = (WINDOWPOS*)lParam;
  3384. if ((wp->flags & SWP_SHOWWINDOW)||
  3385. (wp->flags & SWP_HIDEWINDOW))
  3386. {
  3387. BOOL fShow = (wp->flags & SWP_SHOWWINDOW);
  3388. LV_OnShowWindow(plv, fShow);
  3389. }
  3390. if (ListView_IsWatermarked(plv))
  3391. {
  3392. RECT rc = {wp->x, wp->y, wp->x + wp->cx, wp->y + wp->y};
  3393. // Invalidate New.
  3394. rc.left = rc.right - plv->szWatermark.cx;
  3395. rc.top = rc.bottom - plv->szWatermark.cy;
  3396. InvalidateRect(plv->ci.hwnd, &rc, TRUE);
  3397. // and Old:
  3398. GetClientRect(plv->ci.hwnd, &rc);
  3399. rc.left = rc.right - plv->szWatermark.cx;
  3400. rc.top = rc.bottom - plv->szWatermark.cy;
  3401. InvalidateRect(plv->ci.hwnd, &rc, TRUE);
  3402. }
  3403. }
  3404. break;
  3405. case WM_MBUTTONDOWN:
  3406. if (ListView_SetFocus(hwnd) && plv->hwndToolTips)
  3407. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  3408. break;
  3409. case WM_LBUTTONDBLCLK:
  3410. case WM_RBUTTONDBLCLK:
  3411. // Cancel manual tip track on any mouse button down
  3412. ListView_CancelTipTrack(plv);
  3413. if (plv->hwndToolTips)
  3414. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  3415. ListView_OnButtonDown(plv, TRUE, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  3416. break;
  3417. case WM_LBUTTONDOWN:
  3418. case WM_RBUTTONDOWN:
  3419. // Cancel manual tip track on any mouse button down
  3420. ListView_CancelTipTrack(plv);
  3421. if (plv->hwndToolTips)
  3422. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  3423. ListView_OnButtonDown(plv, FALSE, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  3424. break;
  3425. case WM_LBUTTONUP:
  3426. case WM_RBUTTONUP:
  3427. case WM_MBUTTONUP:
  3428. case WM_NCMOUSEMOVE:
  3429. if (plv->hwndToolTips)
  3430. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  3431. break;
  3432. case WM_PRINTCLIENT:
  3433. case WM_PAINT:
  3434. ListView_OnPaint(plv, (HDC)wParam);
  3435. return 0;
  3436. case WM_NCPAINT:
  3437. {
  3438. if (plv->hTheme && plv->ci.dwExStyle & WS_EX_CLIENTEDGE)
  3439. {
  3440. HRGN hrgn = (wParam != 1) ? (HRGN)wParam : NULL;
  3441. if (CCDrawNonClientTheme(plv->hTheme, hwnd, hrgn, plv->hbrBk, 0, 0))
  3442. {
  3443. return 1;
  3444. }
  3445. }
  3446. }
  3447. break;
  3448. case WM_SHOWWINDOW:
  3449. LV_OnShowWindow(plv, BOOLFROMPTR(wParam));
  3450. break;
  3451. case WM_MOUSEHOVER:
  3452. ListView_OnMouseHover(plv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  3453. break;
  3454. case WM_MOUSELEAVE:
  3455. ListView_OnSetHotItem(plv, -1);
  3456. plv->iNoHover = -1;
  3457. break;
  3458. case WM_KEYUP:
  3459. plv->iScrollCount = 0;
  3460. break;
  3461. case WM_KEYDOWN:
  3462. HANDLE_WM_KEYDOWN(plv, wParam, lParam, ListView_OnKey);
  3463. break;
  3464. case WM_IME_COMPOSITION:
  3465. // Now only Korean version is interested in incremental search with composition string.
  3466. if (g_fDBCSInputEnabled)
  3467. {
  3468. if (((ULONG_PTR)GetKeyboardLayout(0L) & 0xF000FFFFL) == 0xE0000412L)
  3469. {
  3470. if (ListView_OnImeComposition(plv, wParam, lParam))
  3471. {
  3472. lParam &= ~GCS_RESULTSTR;
  3473. break;
  3474. }
  3475. else
  3476. return 0;
  3477. }
  3478. }
  3479. break;
  3480. case WM_CHAR:
  3481. if (plv->iPuntChar)
  3482. {
  3483. plv->iPuntChar--;
  3484. return TRUE;
  3485. }
  3486. else
  3487. {
  3488. return HANDLE_WM_CHAR(plv, wParam, lParam, ListView_OnChar);
  3489. }
  3490. case WM_WININICHANGE:
  3491. ListView_OnWinIniChange(plv, wParam, lParam);
  3492. break;
  3493. case WM_NOTIFYFORMAT:
  3494. return CIHandleNotifyFormat(&plv->ci, lParam);
  3495. case WM_ENABLE:
  3496. // HACK: we don't get WM_STYLECHANGE on EnableWindow()
  3497. ListView_EnableWindow(plv, BOOLFROMPTR(wParam));
  3498. break;
  3499. case WM_SYSCOLORCHANGE:
  3500. InitGlobalColors();
  3501. if (plv->ci.style & WS_DISABLED)
  3502. {
  3503. if (!(plv->flags & LVF_USERBKCLR))
  3504. plv->clrBkSave = g_clrWindow;
  3505. ListView_OnSetBkColor(plv, g_clrBtnFace);
  3506. }
  3507. else if (!(plv->flags & LVF_USERBKCLR))
  3508. {
  3509. ListView_OnSetBkColor(plv, g_clrWindow);
  3510. }
  3511. if (plv->exStyle & LVS_EX_CHECKBOXES)
  3512. {
  3513. ListView_InitCheckBoxes(plv, FALSE);
  3514. }
  3515. plv->crHeader = GetSysColor(COLOR_WINDOWTEXT);
  3516. plv->crTop = GetSysColor(COLOR_BTNFACE);
  3517. plv->crLeft = GetSysColor(COLOR_BTNFACE);
  3518. // 98/11/19 #249967 vtan: Always invalidate the list view
  3519. // rectangle so that the color change causes a refresh.
  3520. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3521. break;
  3522. // don't use HANDLE_MSG because this needs to go to the default handler
  3523. case WM_SYSKEYDOWN:
  3524. HANDLE_WM_SYSKEYDOWN(plv, wParam, lParam, ListView_OnKey);
  3525. break;
  3526. case WM_UPDATEUISTATE:
  3527. {
  3528. DWORD dwUIStateMask = MAKEWPARAM(0xFFFF, UISF_HIDEFOCUS);
  3529. // we care only about focus not accel, and redraw only if changed
  3530. if (CCOnUIState(&(plv->ci), WM_UPDATEUISTATE, wParam & dwUIStateMask, lParam))
  3531. {
  3532. if (plv->iFocus >= 0)
  3533. {
  3534. // an item has the focus, invalidate it
  3535. ListView_InvalidateItem(plv, plv->iFocus, FALSE, RDW_INVALIDATE | RDW_ERASE);
  3536. }
  3537. }
  3538. goto DoDefault;
  3539. }
  3540. case LVM_GETITEMA:
  3541. return (LRESULT)ListView_OnGetItemA(plv, (LV_ITEMA *)lParam);
  3542. case LVM_SETITEMA:
  3543. return (LRESULT)ListView_OnSetItemA(plv, (LV_ITEMA *)lParam);
  3544. case LVM_INSERTITEMA:
  3545. return (LRESULT)ListView_OnInsertItemA(plv, (LV_ITEMA *)lParam);
  3546. case LVM_FINDITEMA:
  3547. return (LRESULT)ListView_OnFindItemA(plv, (int)wParam, (LV_FINDINFOA *)lParam);
  3548. case LVM_GETSTRINGWIDTHA:
  3549. return (LRESULT)ListView_OnGetStringWidthA(plv, (LPCSTR)lParam, NULL);
  3550. case LVM_GETCOLUMNA:
  3551. return (LRESULT)ListView_OnGetColumnA(plv, (int)wParam, (LV_COLUMNA *)lParam);
  3552. case LVM_SETCOLUMNA:
  3553. return (LRESULT)ListView_OnSetColumnA(plv, (int)wParam, (LV_COLUMNA *)lParam);
  3554. case LVM_INSERTCOLUMNA:
  3555. return (LRESULT)ListView_OnInsertColumnA(plv, (int)wParam, (LV_COLUMNA *)lParam);
  3556. case LVM_GETITEMTEXTA:
  3557. return (LRESULT)ListView_OnGetItemTextA(plv, (int)wParam, (LV_ITEMA *)lParam);
  3558. case LVM_SETITEMTEXTA:
  3559. if (!lParam)
  3560. return FALSE;
  3561. return (LRESULT)ListView_OnSetItemTextA(plv, (int)wParam,
  3562. ((LV_ITEMA *)lParam)->iSubItem,
  3563. (LPCSTR)((LV_ITEMA *)lParam)->pszText);
  3564. case LVM_GETBKIMAGEA:
  3565. return (LRESULT)ListView_OnGetBkImageA(plv, (LPLVBKIMAGEA)lParam);
  3566. case LVM_SETBKIMAGEA:
  3567. return (LRESULT)ListView_OnSetBkImageA(plv, (LPLVBKIMAGEA)lParam);
  3568. case WM_STYLECHANGING:
  3569. ListView_OnStyleChanging(plv, (UINT)wParam, (LPSTYLESTRUCT)lParam);
  3570. return 0;
  3571. case WM_STYLECHANGED:
  3572. ListView_OnStyleChanged(plv, (UINT) wParam, (LPSTYLESTRUCT)lParam);
  3573. return 0L;
  3574. case WM_HELP:
  3575. return ListView_OnHelp(plv, (LPHELPINFO)lParam);
  3576. case LVM_GETIMAGELIST:
  3577. return (LRESULT)(UINT_PTR)(ListView_OnGetImageList(plv, (int)wParam));
  3578. case LVM_SETIMAGELIST:
  3579. return (LRESULT)(UINT_PTR)ListView_OnSetImageList(plv, (HIMAGELIST)lParam, (int)wParam);
  3580. case LVM_GETBKCOLOR:
  3581. return (LRESULT)(plv->ci.style & WS_DISABLED ? plv->clrBkSave : plv->clrBk);
  3582. case LVM_SETBKCOLOR:
  3583. plv->flags |= LVF_USERBKCLR;
  3584. if (plv->ci.style & WS_DISABLED)
  3585. {
  3586. plv->clrBkSave = (COLORREF)lParam;
  3587. return TRUE;
  3588. }
  3589. else
  3590. {
  3591. return (LRESULT)ListView_OnSetBkColor(plv, (COLORREF)lParam);
  3592. }
  3593. case LVM_GETTEXTCOLOR:
  3594. return (LRESULT)plv->clrText;
  3595. case LVM_SETTEXTCOLOR:
  3596. plv->clrText = (COLORREF)lParam;
  3597. return TRUE;
  3598. case LVM_GETTEXTBKCOLOR:
  3599. return (LRESULT)plv->clrTextBk;
  3600. case LVM_SETTEXTBKCOLOR:
  3601. plv->clrTextBk = (COLORREF)lParam;
  3602. return TRUE;
  3603. case LVM_GETHOTLIGHTCOLOR:
  3604. return (LRESULT)plv->clrHotlight;
  3605. case LVM_SETHOTLIGHTCOLOR:
  3606. plv->clrHotlight = (COLORREF)lParam;
  3607. return TRUE;
  3608. case LVM_GETITEMCOUNT:
  3609. if (ListView_IsOwnerData(plv))
  3610. return (LRESULT)plv->cTotalItems;
  3611. else if (!plv->hdpa)
  3612. return 0;
  3613. else
  3614. return (LRESULT)DPA_GetPtrCount(plv->hdpa);
  3615. break;
  3616. case LVM_GETITEM:
  3617. return (LRESULT)ListView_OnGetItem(plv, (LV_ITEM*)lParam);
  3618. case LVM_GETITEMSTATE:
  3619. return (LRESULT)ListView_OnGetItemState(plv, (int)wParam, (UINT)lParam);
  3620. case LVM_SETITEMSTATE:
  3621. if (!lParam)
  3622. return FALSE;
  3623. return (LRESULT)ListView_OnSetItemState(plv, (int)wParam,
  3624. ((LV_ITEM *)lParam)->state,
  3625. ((LV_ITEM *)lParam)->stateMask);
  3626. case LVM_SETITEMTEXT:
  3627. if (!lParam)
  3628. return FALSE;
  3629. return (LRESULT)ListView_OnSetItemText(plv, (int)wParam,
  3630. ((LV_ITEM *)lParam)->iSubItem,
  3631. (LPCTSTR)((LV_ITEM *)lParam)->pszText);
  3632. case LVM_GETITEMTEXT:
  3633. return (LRESULT)ListView_OnGetItemText(plv, (int)wParam, (LV_ITEM *)lParam);
  3634. case LVM_GETBKIMAGE:
  3635. return (LRESULT)ListView_OnGetBkImage(plv, (LPLVBKIMAGE)lParam);
  3636. case LVM_SETBKIMAGE:
  3637. return (LRESULT)ListView_OnSetBkImage(plv, (LPLVBKIMAGE)lParam);
  3638. case LVM_GETSELECTEDCOLUMN:
  3639. return plv->iLastColSort;
  3640. case LVM_SETSELECTEDCOLUMN:
  3641. plv->iLastColSort = (int) wParam;
  3642. if (ListView_IsTileView(plv))
  3643. {
  3644. // Tileview displays the selected column on the second line, if available. The second
  3645. // line might be blank w/o it. So when this changes, we need to recompute each tile.
  3646. if (!ListView_IsOwnerData(plv))
  3647. {
  3648. int i;
  3649. for (i = 0; i < ListView_Count(plv); i++)
  3650. {
  3651. LISTITEM *pitem = ListView_FastGetItemPtr(plv, i);
  3652. ListView_SetSRecompute(pitem);
  3653. }
  3654. }
  3655. plv->rcView.left = RECOMPUTE;
  3656. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3657. }
  3658. return 1;
  3659. case LVM_SETVIEW:
  3660. return ListView_SetViewType(plv, (WORD)wParam);
  3661. case LVM_GETVIEW:
  3662. return plv->wView;
  3663. case LVM_SETITEM:
  3664. return (LRESULT)ListView_OnSetItem(plv, (const LV_ITEM*)lParam);
  3665. case LVM_INSERTITEM:
  3666. return (LRESULT)ListView_OnInsertItem(plv, (const LV_ITEM*)lParam);
  3667. case LVM_DELETEITEM:
  3668. return (LRESULT)ListView_OnDeleteItem(plv, (int)wParam);
  3669. case LVM_UPDATE:
  3670. ListView_OnUpdate(plv, (int)wParam);
  3671. UpdateWindow(plv->ci.hwnd);
  3672. return TRUE;
  3673. case LVM_DELETEALLITEMS:
  3674. lParam = (LRESULT)ListView_OnDeleteAllItems(plv);
  3675. // Optimization: Instead of sending out a zillion EVENT_OBJECT_DESTROY's,
  3676. // we send out a destroy of ourselves followed by a fresh create.
  3677. // For compatibility with IE4, we still send out the REORDER notification.
  3678. NotifyWinEvent(EVENT_OBJECT_REORDER, hwnd, OBJID_CLIENT, 0);
  3679. ListView_NotifyRecreate(plv);
  3680. return lParam;
  3681. case LVM_GETITEMRECT:
  3682. return (LRESULT)ListView_OnGetItemRect(plv, (int)wParam, (RECT*)lParam);
  3683. case LVM_GETSUBITEMRECT:
  3684. return (LRESULT)ListView_OnGetSubItemRect(plv, (int)wParam, (LPRECT)lParam);
  3685. case LVM_SUBITEMHITTEST:
  3686. return (LRESULT)ListView_OnSubItemHitTest(plv, (LPLVHITTESTINFO)lParam);
  3687. case LVM_GETISEARCHSTRINGA:
  3688. if (GetFocus() == plv->ci.hwnd)
  3689. return (LRESULT)GetIncrementSearchStringA(&plv->is, plv->ci.uiCodePage, (LPSTR)lParam);
  3690. else
  3691. return 0;
  3692. case LVM_GETISEARCHSTRING:
  3693. if (GetFocus() == plv->ci.hwnd)
  3694. return (LRESULT)GetIncrementSearchString(&plv->is, (LPTSTR)lParam);
  3695. else
  3696. return 0;
  3697. case LVM_GETITEMSPACING:
  3698. if (wParam)
  3699. return MAKELONG(plv->cxItem, plv->cyItem);
  3700. else
  3701. return MAKELONG(plv->cxIconSpacing, plv->cyIconSpacing);
  3702. case LVM_GETNEXTITEM:
  3703. return (LRESULT)ListView_OnGetNextItem(plv, (int)wParam, (UINT)lParam);
  3704. case LVM_FINDITEM:
  3705. return (LRESULT)ListView_OnFindItem(plv, (int)wParam, (const LV_FINDINFO*)lParam);
  3706. case LVM_SETSELECTIONMARK:
  3707. {
  3708. int iOldMark = plv->iMark;
  3709. int iNewMark = (int)lParam;
  3710. if (iNewMark == -1 || ListView_IsValidItemNumber(plv, iNewMark))
  3711. {
  3712. plv->iMark = iNewMark;
  3713. }
  3714. return iOldMark;
  3715. }
  3716. case LVM_GETSELECTIONMARK:
  3717. return plv->iMark;
  3718. case LVM_GETITEMPOSITION:
  3719. return (LRESULT)ListView_OnGetItemPosition(plv, (int)wParam,
  3720. (POINT*)lParam);
  3721. case LVM_SETITEMPOSITION:
  3722. return (LRESULT)ListView_OnSetItemPosition(plv, (int)wParam,
  3723. GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  3724. case LVM_SETITEMPOSITION32:
  3725. if (!lParam)
  3726. return FALSE;
  3727. return (LRESULT)ListView_OnSetItemPosition(plv, (int)wParam,
  3728. ((LPPOINT)lParam)->x, ((LPPOINT)lParam)->y);
  3729. case LVM_SCROLL:
  3730. {
  3731. int dx = (int)wParam;
  3732. int dy = (int)lParam;
  3733. return (LRESULT)(ListView_ValidateScrollParams(plv, &dx, &dy) &&
  3734. ListView_OnScroll(plv, dx, dy));
  3735. }
  3736. case LVM_ENSUREVISIBLE:
  3737. return (LRESULT)ListView_OnEnsureVisible(plv, (int)wParam, BOOLFROMPTR(lParam));
  3738. case LVM_REDRAWITEMS:
  3739. return (LRESULT)ListView_OnRedrawItems(plv, (int)wParam, (int)lParam);
  3740. case LVM_ARRANGE:
  3741. return (LRESULT)ListView_OnArrange(plv, (UINT)wParam);
  3742. case LVM_GETEDITCONTROL:
  3743. return (LRESULT)(UINT_PTR)plv->hwndEdit;
  3744. case LVM_EDITLABELA:
  3745. {
  3746. LPWSTR lpEditString = NULL;
  3747. HWND hRet;
  3748. if (lParam)
  3749. {
  3750. lpEditString = ProduceWFromA(plv->ci.uiCodePage, (LPSTR)lParam);
  3751. }
  3752. hRet = ListView_OnEditLabel(plv, (int)wParam, lpEditString);
  3753. if (lpEditString)
  3754. {
  3755. FreeProducedString(lpEditString);
  3756. }
  3757. return (LRESULT)hRet;
  3758. }
  3759. case LVM_EDITLABEL:
  3760. return (LRESULT)(UINT_PTR)ListView_OnEditLabel(plv, (int)wParam, (LPTSTR)lParam);
  3761. case LVM_HITTEST:
  3762. return (LRESULT)ListView_OnHitTest(plv, (LV_HITTESTINFO*)lParam);
  3763. case LVM_GETSTRINGWIDTH:
  3764. return (LRESULT)ListView_OnGetStringWidth(plv, (LPCTSTR)lParam, NULL);
  3765. case LVM_GETCOLUMN:
  3766. return (LRESULT)ListView_OnGetColumn(plv, (int)wParam, (LV_COLUMN*)lParam);
  3767. case LVM_SETCOLUMN:
  3768. return (LRESULT)ListView_OnSetColumn(plv, (int)wParam, (const LV_COLUMN*)lParam);
  3769. case LVM_SETCOLUMNORDERARRAY:
  3770. return SendMessage(plv->hwndHdr, HDM_SETORDERARRAY, wParam, lParam);
  3771. case LVM_GETCOLUMNORDERARRAY:
  3772. return SendMessage(plv->hwndHdr, HDM_GETORDERARRAY, wParam, lParam);
  3773. case LVM_GETHEADER:
  3774. {
  3775. HWND hwndOld = plv->hwndHdr;
  3776. if (lParam && IsWindow((HWND)lParam))
  3777. {
  3778. plv->hwndHdr = (HWND)lParam;
  3779. }
  3780. return (LRESULT)hwndOld;
  3781. }
  3782. case LVM_INSERTCOLUMN:
  3783. return (LRESULT)ListView_OnInsertColumn(plv, (int)wParam, (const LV_COLUMN*)lParam);
  3784. case LVM_DELETECOLUMN:
  3785. return (LRESULT)ListView_OnDeleteColumn(plv, (int)wParam);
  3786. case LVM_CREATEDRAGIMAGE:
  3787. return (LRESULT)(UINT_PTR)ListView_OnCreateDragImage(plv, (int)wParam, (LPPOINT)lParam);
  3788. case LVMI_PLACEITEMS:
  3789. if (plv->uUnplaced)
  3790. {
  3791. ListView_Recompute(plv);
  3792. ListView_UpdateScrollBars(plv);
  3793. }
  3794. return 0;
  3795. case LVM_GETVIEWRECT:
  3796. return (LPARAM)ListView_OnGetViewRect(plv, (RECT*)lParam);
  3797. case LVM_GETCOLUMNWIDTH:
  3798. return (LPARAM)ListView_OnGetColumnWidth(plv, (int)wParam);
  3799. case LVM_SETCOLUMNWIDTH:
  3800. return (LPARAM)ListView_ISetColumnWidth(plv, (int)wParam,
  3801. GET_X_LPARAM(lParam), TRUE);
  3802. case LVM_SETCALLBACKMASK:
  3803. plv->stateCallbackMask = (UINT)wParam;
  3804. return (LPARAM)TRUE;
  3805. case LVM_GETCALLBACKMASK:
  3806. return (LPARAM)(UINT)plv->stateCallbackMask;
  3807. case LVM_GETTOPINDEX:
  3808. return (LPARAM)ListView_OnGetTopIndex(plv);
  3809. case LVM_GETCOUNTPERPAGE:
  3810. return (LPARAM)ListView_OnGetCountPerPage(plv);
  3811. case LVM_GETORIGIN:
  3812. return (LPARAM)ListView_OnGetOrigin(plv, (POINT*)lParam);
  3813. case LVM_SETITEMCOUNT:
  3814. return ListView_OnSetItemCount(plv, (int)wParam, (DWORD)lParam);
  3815. case LVM_GETSELECTEDCOUNT:
  3816. if (ListView_IsOwnerData(plv))
  3817. {
  3818. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  3819. }
  3820. return plv->nSelected;
  3821. case LVM_SORTITEMS:
  3822. return ListView_OnSortItems(plv, (LPARAM)wParam, (PFNLVCOMPARE)lParam, TRUE);
  3823. case LVM_SORTITEMSEX:
  3824. return ListView_OnSortItems(plv, (LPARAM)wParam, (PFNLVCOMPARE)lParam, FALSE);
  3825. case LVM_SETEXTENDEDLISTVIEWSTYLE:
  3826. return ListView_ExtendedStyleChange(plv, (DWORD) lParam, (DWORD) wParam);
  3827. case LVM_GETEXTENDEDLISTVIEWSTYLE:
  3828. return plv->exStyle;
  3829. case LVM_GETHOVERTIME:
  3830. return plv->dwHoverTime;
  3831. case LVM_SETHOVERTIME:
  3832. {
  3833. DWORD dwRet = plv->dwHoverTime;
  3834. plv->dwHoverTime = (DWORD)lParam;
  3835. return dwRet;
  3836. }
  3837. case LVM_GETTOOLTIPS:
  3838. return (LRESULT)plv->hwndToolTips;
  3839. case LVM_SETTOOLTIPS:
  3840. {
  3841. HWND hwndToolTips = plv->hwndToolTips;
  3842. plv->hwndToolTips = (HWND)wParam;
  3843. return (LRESULT)hwndToolTips;
  3844. }
  3845. case LVM_SETICONSPACING:
  3846. {
  3847. DWORD dwRet = ListView_OnSetIconSpacing(plv, lParam);
  3848. // rearrange as necessary
  3849. if (ListView_RedrawEnabled(plv) &&
  3850. (ListView_IsSmallView(plv) || ListView_IsIconView(plv)))
  3851. {
  3852. ListView_ArrangeOrSnapToGrid(plv);
  3853. }
  3854. return dwRet;
  3855. }
  3856. case LVM_SETHOTITEM:
  3857. {
  3858. int iOld = plv->iHot;
  3859. int iNew = (int)wParam;
  3860. if (iNew == -1 || ListView_IsValidItemNumber(plv, iNew))
  3861. {
  3862. ListView_OnSetHotItem(plv, (int)wParam);
  3863. }
  3864. return iOld;
  3865. }
  3866. case LVM_GETHOTITEM:
  3867. return plv->iHot;
  3868. // hCurHot is used iff LVS_EX_TRACKSELECT
  3869. case LVM_SETHOTCURSOR:
  3870. {
  3871. HCURSOR hCurOld = plv->hCurHot;
  3872. plv->hCurHot = (HCURSOR)lParam;
  3873. return (LRESULT)hCurOld;
  3874. }
  3875. case LVM_GETHOTCURSOR:
  3876. if (!plv->hCurHot)
  3877. {
  3878. plv->hCurHot = LoadCursor(NULL, IDC_HAND);
  3879. }
  3880. return (LRESULT)plv->hCurHot;
  3881. case LVM_APPROXIMATEVIEWRECT:
  3882. return ListView_OnApproximateViewRect(plv, (int)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  3883. case LVM_SETLVRANGEOBJECT:
  3884. return ListView_OnSetLVRangeObject(plv, (int)wParam, (ILVRange *)lParam);
  3885. case LVM_SETWORKAREAS:
  3886. ListView_OnSetWorkAreas(plv, (int)wParam, (RECT *)lParam);
  3887. return 0;
  3888. case LVM_GETWORKAREAS:
  3889. ListView_OnGetWorkAreas(plv, (int)wParam, (RECT *)lParam);
  3890. return 0;
  3891. case LVM_GETNUMBEROFWORKAREAS:
  3892. ListView_OnGetNumberOfWorkAreas(plv, (int *)lParam);
  3893. return 0;
  3894. case LVM_RESETEMPTYTEXT:
  3895. plv->fNoEmptyText = FALSE;
  3896. Str_Set(&plv->pszEmptyText, NULL);
  3897. if (ListView_Count(plv) == 0)
  3898. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  3899. return 1;
  3900. case LVM_INSERTGROUPSORTED:
  3901. return ListView_OnInsertGroupSorted(plv, (LVINSERTGROUPSORTED*) wParam);
  3902. case LVM_SORTGROUPS:
  3903. return ListView_OnSortGroups(plv, (PFNLVGROUPCOMPARE)wParam, (void *)lParam);
  3904. case LVM_ENABLEGROUPVIEW:
  3905. return ListView_OnEnableGroupView(plv, (BOOL)wParam);
  3906. case LVM_ISGROUPVIEWENABLED:
  3907. return plv->fGroupView;
  3908. case LVM_INSERTGROUP:
  3909. return ListView_OnInsertGroup(plv, (int) wParam, (PLVGROUP)lParam);
  3910. case LVM_SETGROUPINFO:
  3911. return ListView_OnSetGroupInfo(plv, (int) wParam, (PLVGROUP)lParam);
  3912. case LVM_GETGROUPINFO:
  3913. return ListView_OnGetGroupInfo(plv, (int) wParam, (PLVGROUP)lParam);
  3914. case LVM_REMOVEGROUP:
  3915. return ListView_OnRemoveGroup(plv, (int) wParam);
  3916. case LVM_REMOVEALLGROUPS:
  3917. return ListView_OnRemoveAllGroups(plv);
  3918. case LVM_HASGROUP:
  3919. {
  3920. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, (int)wParam, NULL);
  3921. if (pgrp)
  3922. return 1;
  3923. else
  3924. return 0;
  3925. }
  3926. break;
  3927. case LVM_SETGROUPMETRICS:
  3928. return ListView_OnSetGroupMetrics(plv, (PLVGROUPMETRICS)lParam);
  3929. case LVM_GETGROUPMETRICS:
  3930. return ListView_OnGetGroupMetrics(plv, (PLVGROUPMETRICS)lParam);
  3931. case LVM_SETTILEVIEWINFO:
  3932. return ListView_OnSetTileViewInfo(plv, (PLVTILEVIEWINFO)lParam);
  3933. case LVM_GETTILEVIEWINFO:
  3934. return ListView_OnGetTileViewInfo(plv, (PLVTILEVIEWINFO)lParam);
  3935. case LVM_SETTILEINFO:
  3936. return ListView_OnSetTileInfo(plv, (PLVTILEINFO)lParam);
  3937. case LVM_GETTILEINFO:
  3938. return ListView_OnGetTileInfo(plv, (PLVTILEINFO)lParam);
  3939. case LVM_SETINSERTMARK:
  3940. if (ListView_IsRearrangeableView(plv) && (plv->ci.style & LVS_AUTOARRANGE) && !plv->fGroupView)
  3941. return ListView_OnSetInsertMark(plv, (LPLVINSERTMARK)lParam);
  3942. else
  3943. return FALSE;
  3944. case LVM_GETINSERTMARK:
  3945. {
  3946. LPLVINSERTMARK plvim = (LPLVINSERTMARK)lParam;
  3947. if (plvim->cbSize != sizeof(LVINSERTMARK))
  3948. return FALSE;
  3949. plvim->dwFlags = (plv->fInsertAfter ? LVIM_AFTER : 0) | LVIM_SETFROMINFO;
  3950. plvim->iItem = plv->iInsertItem;
  3951. return TRUE;
  3952. }
  3953. case LVM_GETINSERTMARKRECT:
  3954. {
  3955. return ListView_OnGetInsertMarkRect(plv, (LPRECT)lParam);
  3956. }
  3957. case LVM_SETINSERTMARKCOLOR:
  3958. {
  3959. LRESULT lres = (LRESULT)ListView_OnGetInsertMarkColor(plv);
  3960. plv->clrim = (COLORREF) lParam;
  3961. return lres;
  3962. }
  3963. case LVM_GETINSERTMARKCOLOR:
  3964. return ListView_OnGetInsertMarkColor(plv);
  3965. case LVM_INSERTMARKHITTEST:
  3966. {
  3967. LPPOINT ppt = (LPPOINT)wParam;
  3968. return ListView_OnInsertMarkHitTest(plv, ppt->x, ppt->y, (LPLVINSERTMARK)lParam);
  3969. }
  3970. case LVM_SETINFOTIP:
  3971. {
  3972. return ListView_OnSetInfoTip(plv, (PLVSETINFOTIP)lParam);
  3973. }
  3974. case LVM_SETOUTLINECOLOR:
  3975. {
  3976. LRESULT lres = (LRESULT)plv->clrOutline;
  3977. plv->clrOutline = (COLORREF) lParam;
  3978. return lres;
  3979. }
  3980. case LVM_GETOUTLINECOLOR:
  3981. return (LRESULT)plv->clrOutline;
  3982. case LVM_SETFROZENITEM:
  3983. return ListView_OnSetFrozenItem(plv, (BOOL) wParam, (int) lParam);
  3984. case LVM_GETFROZENITEM:
  3985. return ListView_OnGetFrozenItem(plv);
  3986. case LVM_SETFROZENSLOT:
  3987. return ListView_OnSetFrozenSlot(plv, (BOOL) wParam, (LPPOINT)lParam);
  3988. case LVM_GETFROZENSLOT:
  3989. return ListView_OnGetFrozenSlot(plv, (LPRECT)lParam);
  3990. case LVM_SETVIEWMARGINS:
  3991. return ListView_OnSetViewMargins(plv, (LPRECT)lParam);
  3992. case LVM_GETVIEWMARGINS:
  3993. return ListView_OnGetViewMargins(plv, (LPRECT)lParam);
  3994. case LVM_KEYBOARDSELECTED:
  3995. ListView_CancelTipTrack(plv);
  3996. return lParam == 0 ? ListView_OnKeyboardSelected(plv, (int)wParam) : FALSE;
  3997. case LVM_CANCELEDITLABEL:
  3998. ListView_DismissEdit(plv, FALSE);
  3999. return 1;
  4000. case LVM_MAPINDEXTOID:
  4001. return ListView_OnMapIndexToID(plv, (UINT)wParam);
  4002. case LVM_MAPIDTOINDEX:
  4003. return ListView_OnMapIdToIndex(plv, (UINT)wParam);
  4004. case LVM_ISITEMVISIBLE:
  4005. if (ListView_IsValidItemNumber(plv, (UINT)wParam))
  4006. {
  4007. return ListView_IsItemVisibleI(plv, (UINT)wParam);
  4008. }
  4009. else
  4010. {
  4011. return FALSE;
  4012. }
  4013. case WM_SIZE:
  4014. if (plv)
  4015. {
  4016. ListView_OnSize(plv);
  4017. }
  4018. break;
  4019. case WM_NOTIFY:
  4020. return ListView_OnNotify(plv, wParam, (LPNMHDR)lParam);
  4021. case WM_MOUSEMOVE:
  4022. // Cancel manual track if mouse moved
  4023. if (plv->lLastMMove != lParam)
  4024. {
  4025. ListView_CancelTipTrack(plv);
  4026. if (plv->hwndToolTips)
  4027. {
  4028. UINT uFlags;
  4029. int iHit, iSubHit;
  4030. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  4031. if (!ListView_IsKbdTipTracking(plv))
  4032. {
  4033. // check that we are still on the hit item, pop it!
  4034. iHit = _ListView_ItemHitTest(plv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &uFlags, &iSubHit);
  4035. if (iHit != plv->iTTLastHit || iSubHit != plv->iTTLastSubHit)
  4036. ListView_PopBubble(plv);
  4037. }
  4038. }
  4039. }
  4040. plv->lLastMMove = lParam;
  4041. ListView_OnMouseMove(plv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  4042. break;
  4043. case WM_GETOBJECT:
  4044. if (lParam == OBJID_QUERYCLASSNAMEIDX)
  4045. return MSAA_CLASSNAMEIDX_LISTVIEW;
  4046. break;
  4047. case WM_THEMECHANGED:
  4048. if (plv->hTheme)
  4049. CloseThemeData(plv->hTheme);
  4050. plv->hTheme = OpenThemeData(plv->ci.hwnd, L"ListView");
  4051. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  4052. break;
  4053. default:
  4054. {
  4055. LRESULT lres;
  4056. if (CCWndProc(&plv->ci, uMsg, wParam, lParam, &lres))
  4057. return lres;
  4058. }
  4059. if (uMsg == g_msgMSWheel)
  4060. {
  4061. DWORD dwStyle;
  4062. int sb;
  4063. SCROLLINFO si;
  4064. int cScrollUnitsPerLine;
  4065. int cPage;
  4066. int cLinesPerDetant;
  4067. int cDetants;
  4068. int dPos;
  4069. int iWheelDelta = (int)(short)HIWORD(wParam);
  4070. BOOL fScroll = !(wParam & (MK_SHIFT | MK_CONTROL));
  4071. BOOL fDataZoom = (BOOL) (wParam & MK_SHIFT);
  4072. // Update count of scroll amount
  4073. gcWheelDelta -= iWheelDelta;
  4074. cDetants = gcWheelDelta / WHEEL_DELTA;
  4075. if (cDetants != 0)
  4076. {
  4077. gcWheelDelta %= WHEEL_DELTA;
  4078. }
  4079. if (fScroll)
  4080. {
  4081. if (g_ucScrollLines > 0 &&
  4082. cDetants != 0 &&
  4083. ((WS_VSCROLL | WS_HSCROLL) & (dwStyle = ListView_GetWindowStyle(plv))))
  4084. {
  4085. sb = (dwStyle & WS_VSCROLL) ? SB_VERT : SB_HORZ;
  4086. // Get the scroll amount of one line
  4087. cScrollUnitsPerLine = _ListView_GetScrollUnitsPerLine(plv, sb);
  4088. ASSERT(cScrollUnitsPerLine > 0);
  4089. si.cbSize = sizeof(SCROLLINFO);
  4090. si.fMask = SIF_PAGE | SIF_POS;
  4091. if (!ListView_GetScrollInfo(plv, sb, &si))
  4092. return 1;
  4093. // The size of a page is at least one line, and
  4094. // leaves one line of overlap
  4095. cPage = (max(cScrollUnitsPerLine, (int)si.nPage - cScrollUnitsPerLine)) / cScrollUnitsPerLine;
  4096. // Don't scroll more than one page per detant
  4097. cLinesPerDetant = (int) min((ULONG) cPage, (ULONG) g_ucScrollLines);
  4098. dPos = cLinesPerDetant * cDetants * cScrollUnitsPerLine;
  4099. ListView_DismissEdit(plv, FALSE);
  4100. ListView_ComOnScroll(plv, SB_THUMBTRACK, si.nPos + dPos,
  4101. sb, cScrollUnitsPerLine, - 1);
  4102. ListView_UpdateScrollBars(plv);
  4103. // After scrolling, the tooltip might need to change
  4104. // so send the tooltip a fake mousemove message to force
  4105. // a recompute. We use WM_NCMOUSEMOVE since our lParam
  4106. // is in screen coordinates, not client coordinates.
  4107. ListView_PopBubble(plv);
  4108. RelayToToolTips(plv->hwndToolTips, plv->ci.hwnd,
  4109. WM_NCMOUSEMOVE, HTCLIENT, lParam);
  4110. }
  4111. return 1;
  4112. }
  4113. else if (fDataZoom)
  4114. {
  4115. LV_HITTESTINFO ht;
  4116. ht.pt.x = GET_X_LPARAM(lParam);
  4117. ht.pt.y = GET_Y_LPARAM(lParam);
  4118. ScreenToClient(hwnd, &(ht.pt));
  4119. // If we are rolling forward and we hit an item then navigate
  4120. // into that item (simulate dblclk which will open it). Otherwise
  4121. // just fall through so it isn't handled. In that case if we
  4122. // are being hosted in explorer it will do a backwards
  4123. // history navigation.
  4124. if ((iWheelDelta > 0) && (ListView_OnSubItemHitTest(plv, &ht) >= 0) &&
  4125. (ht.flags & LVHT_ONITEM) && cDetants != 0)
  4126. {
  4127. BYTE aKeyState[256];
  4128. // This is a bit yucky but when ListView_HandleMouse sends the
  4129. // notification to the listview owner we need to make sure that
  4130. // it doesn't think the shift key is down. Otherwise it may
  4131. // perform some "alternate" action but in this case we always
  4132. // want it to perform the default open action.
  4133. //
  4134. // Strip the high bit of VK_SHIFT so that the shift key is
  4135. // not down.
  4136. if (GetKeyboardState(aKeyState))
  4137. {
  4138. aKeyState[VK_SHIFT] &= 0x7f;
  4139. SetKeyboardState(aKeyState);
  4140. }
  4141. ListView_HandleMouse(plv, FALSE, ht.pt.x, ht.pt.y, 0, TRUE);
  4142. ListView_HandleMouse(plv, TRUE, ht.pt.x, ht.pt.y, 0, TRUE);
  4143. return 1;
  4144. }
  4145. // else fall through
  4146. }
  4147. }
  4148. break;
  4149. }
  4150. DoDefault:
  4151. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  4152. }
  4153. BOOL Listview_UpdateViewEffects(LV* plv)
  4154. {
  4155. BOOL fChanged = FALSE;
  4156. UINT fScroll = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewScrollOver"),
  4157. FALSE, // Don't ignore HKCU
  4158. LISTVIEW_VFX_DEFAULT); // Assume a fast enough machine
  4159. UINT fWatermark = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewWatermark"),
  4160. FALSE, // Don't ignore HKCU
  4161. LISTVIEW_VFX_DEFAULT); // Assume a fast enough machine
  4162. UINT fAlphaSelect = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewAlphaSelect"),
  4163. FALSE, // Don't ignore HKCU
  4164. LISTVIEW_VFX_DEFAULT); // Assume a fast enough machine
  4165. UINT fShadow = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("ListviewShadow"),
  4166. FALSE, // Don't ignore HKCU
  4167. LISTVIEW_VFX_DEFAULT); // Assume a fast enough machine
  4168. if (plv->fListviewAlphaSelect != fAlphaSelect ||
  4169. plv->fListviewShadowText != fShadow ||
  4170. plv->fListviewWatermarkBackgroundImages != fScroll ||
  4171. plv->fListviewEnableWatermark != fWatermark)
  4172. {
  4173. fChanged = TRUE;
  4174. }
  4175. plv->fListviewAlphaSelect = BOOLIFY(fAlphaSelect);
  4176. plv->fListviewShadowText = BOOLIFY(fShadow);
  4177. plv->fListviewWatermarkBackgroundImages = BOOLIFY(fScroll);
  4178. plv->fListviewEnableWatermark = BOOLIFY(fWatermark);
  4179. return fChanged;
  4180. }
  4181. void ListView_OnWinIniChange(LV* plv, WPARAM wParam, LPARAM lParam)
  4182. {
  4183. // REARCHITECT: will this also catch sysparametersinfo?
  4184. // we need a general way of handling this, not
  4185. // just relying on the listview.
  4186. InitGlobalMetrics(wParam);
  4187. if (Listview_UpdateViewEffects(plv))
  4188. {
  4189. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  4190. }
  4191. switch (wParam)
  4192. {
  4193. case 0:
  4194. case SPI_SETNONCLIENTMETRICS:
  4195. case SPI_SETICONTITLELOGFONT:
  4196. case SPI_SETICONMETRICS:
  4197. // If wParam is 0, only reload settings if lParam is 0 too. This catches the wild-card scenario
  4198. // (like the old plus tab which does WM_WININICHANGE, 0, 0) but allows us to ignore wParam = 0
  4199. // and lParam = lpszSectionName. Reduces unecessary flashing.
  4200. if (wParam || !lParam)
  4201. {
  4202. if (!(plv->flags & LVF_ICONSPACESET))
  4203. ListView_OnSetIconSpacing(plv, (LPARAM)-1);
  4204. if (plv->flags & LVF_FONTCREATED)
  4205. ListView_OnSetFont(plv, NULL, TRUE);
  4206. // Force a recalc of all the icon regions by stripping and
  4207. // then adding back the LVS_EX_REGIONAL bit.
  4208. if (plv->exStyle & LVS_EX_REGIONAL)
  4209. {
  4210. ListView_ExtendedStyleChange(plv, 0, LVS_EX_REGIONAL);
  4211. ListView_ExtendedStyleChange(plv, LVS_EX_REGIONAL, LVS_EX_REGIONAL);
  4212. }
  4213. }
  4214. break;
  4215. default:
  4216. break;
  4217. }
  4218. // If we are in an Iconic view and the user is in autoarrange mode,
  4219. // then we need to arrange the items.
  4220. //
  4221. if ((ListView_IsSmallView(plv) || ListView_IsIconView(plv)))
  4222. {
  4223. // Call off to the arrange function.
  4224. if (ListView_IsOwnerData(plv))
  4225. ListView_OnArrange(plv, LVA_DEFAULT);
  4226. else
  4227. ListView_ArrangeOrSnapToGrid(plv);
  4228. }
  4229. }
  4230. BOOL ListView_OnCreate(LV* plv, CREATESTRUCT* lpCreateStruct)
  4231. {
  4232. Listview_UpdateViewEffects(plv);
  4233. InitDitherBrush();
  4234. CIInitialize(&plv->ci, plv->ci.hwnd, lpCreateStruct);
  4235. plv->wView = (WORD)(plv->ci.style & LVS_TYPEMASK);
  4236. plv->dwExStyle = lpCreateStruct->dwExStyle;
  4237. if (plv->ci.style & WS_VISIBLE)
  4238. plv->flags |= LVF_VISIBLE;
  4239. ListView_GetRegIASetting(&g_bUseDblClickTimer);
  4240. if (ListView_IsOwnerData(plv))
  4241. {
  4242. // ownerdata initialization
  4243. plv->plvrangeSel = LVRange_Create();
  4244. if (NULL == plv->plvrangeSel)
  4245. goto error0;
  4246. plv->plvrangeCut = LVRange_Create();
  4247. if (NULL == plv->plvrangeCut)
  4248. goto error0;
  4249. }
  4250. else
  4251. {
  4252. ASSERT(plv->plvrangeSel == NULL);
  4253. ASSERT(plv->plvrangeCut == NULL);
  4254. plv->hdpa = DPA_CreateEx(LV_HDPA_GROW, plv->hheap);
  4255. if (!plv->hdpa)
  4256. goto error0;
  4257. plv->hdpaZOrder = DPA_CreateEx(LV_HDPA_GROW, plv->hheap);
  4258. if (!plv->hdpaZOrder)
  4259. goto error1;
  4260. }
  4261. ASSERT(plv->nWorkAreas == 0);
  4262. ASSERT(plv->prcWorkAreas == NULL);
  4263. ASSERT(plv->fIconsPositioned == FALSE);
  4264. plv->iNoHover = -1;
  4265. plv->dwHoverTime = HOVER_DEFAULT;
  4266. plv->iHot = -1;
  4267. plv->iEdit = -1;
  4268. plv->iFocus = -1;
  4269. plv->iDrag = -1;
  4270. plv->iTTLastHit = -1;
  4271. plv->iFreeSlot = -1;
  4272. plv->rcView.left = RECOMPUTE;
  4273. plv->iLastColSort = -1;
  4274. ASSERT(plv->sizeTile.cx == 0);
  4275. ASSERT(plv->sizeTile.cy == 0);
  4276. ASSERT(plv->dwTileFlags == 0);
  4277. plv->cSubItems = 1;
  4278. SetRect(&plv->rcBorder, 0, 12, 0, 0);
  4279. plv->crHeader = GetSysColor(COLOR_WINDOWTEXT);
  4280. plv->crTop = GetSysColor(COLOR_BTNFACE);
  4281. plv->crBottom = CLR_NONE;
  4282. plv->crLeft = CLR_NONE;
  4283. plv->crRight = CLR_NONE;
  4284. plv->paddingLeft = 12;
  4285. plv->paddingTop = 12;
  4286. plv->paddingRight = 0;
  4287. plv->paddingBottom = 12;
  4288. plv->szWatermark.cx = 200;
  4289. plv->szWatermark.cy = 200;
  4290. ASSERT(plv->iMSAAMin == plv->iMSAAMax);
  4291. plv->sizeClient.cx = lpCreateStruct->cx;
  4292. plv->sizeClient.cy = lpCreateStruct->cy;
  4293. // Setup flag to say if positions are in small or large view
  4294. if (ListView_IsSmallView(plv))
  4295. plv->flags |= LVF_ICONPOSSML;
  4296. // force calculation of listview metrics
  4297. ListView_OnSetFont(plv, NULL, FALSE);
  4298. plv->cxItem = ListView_ComputeCXItemSize(plv);
  4299. // if we're in ownerdraw report mode, the size got saved to cyItemSave
  4300. // at creation time, both need to have this
  4301. if ((plv->ci.style & LVS_OWNERDRAWFIXED) && ListView_IsReportView(plv))
  4302. plv->cyItem = plv->cyItemSave;
  4303. else
  4304. plv->cyItemSave = plv->cyItem;
  4305. ListView_OnSetIconSpacing(plv, (LPARAM)-1);
  4306. ListView_UpdateScrollBars(plv); // sets plv->cItemCol
  4307. plv->clrBk = CLR_NONE;
  4308. plv->clrText = CLR_DEFAULT;
  4309. plv->clrTextBk = CLR_DEFAULT;
  4310. plv->clrHotlight = CLR_DEFAULT;
  4311. plv->clrOutline = CLR_DEFAULT;
  4312. // create the bk brush, and set the imagelists colors if needed
  4313. ListView_OnSetBkColor(plv, g_clrWindow);
  4314. // Initialize report view fields
  4315. plv->xTotalColumnWidth = RECOMPUTE;
  4316. if (ListView_IsReportView(plv))
  4317. ListView_RInitialize(plv, FALSE);
  4318. if (plv->ci.style & WS_DISABLED)
  4319. {
  4320. plv->ci.style &= ~WS_DISABLED;
  4321. ListView_EnableWindow(plv, FALSE);
  4322. }
  4323. // tooltip for unfolding name lables
  4324. plv->hwndToolTips = CreateWindowEx(WS_EX_TRANSPARENT, TOOLTIPS_CLASS, NULL,
  4325. WS_POPUP|TTS_NOPREFIX, 0, 0, 0, 0,
  4326. NULL, NULL, g_hinst, NULL);
  4327. if (plv->hwndToolTips)
  4328. {
  4329. TOOLINFO ti;
  4330. ti.cbSize = sizeof(ti);
  4331. ti.uFlags = TTF_TRANSPARENT|TTF_ABSOLUTE;
  4332. ti.hwnd = plv->ci.hwnd;
  4333. ti.uId = 0;
  4334. ti.hinst = NULL;
  4335. ti.lpszText = LPSTR_TEXTCALLBACK;
  4336. GetClientRect(plv->ci.hwnd, &ti.rect);
  4337. SendMessage(plv->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM) &ti);
  4338. /* Ensure that the tooltips use the same font as the view */
  4339. FORWARD_WM_SETFONT(plv->hwndToolTips, plv->hfontLabel, FALSE, SendMessage);
  4340. }
  4341. SetTimer(plv->ci.hwnd, IDT_ONECLICKOK, GetDoubleClickTime(), NULL);
  4342. return TRUE;
  4343. error1:
  4344. DPA_Destroy(plv->hdpa);
  4345. plv->hdpa = NULL;
  4346. error0:
  4347. if (plv->plvrangeSel)
  4348. {
  4349. plv->plvrangeSel->lpVtbl->Release(plv->plvrangeSel);
  4350. plv->plvrangeSel = NULL;
  4351. }
  4352. if (plv->plvrangeCut)
  4353. {
  4354. plv->plvrangeCut->lpVtbl->Release(plv->plvrangeCut);
  4355. plv->plvrangeCut = NULL;
  4356. }
  4357. return FALSE;
  4358. }
  4359. void ListView_DeleteHrgnInval(LV* plv)
  4360. {
  4361. if (plv->hrgnInval && plv->hrgnInval != (HRGN)ENTIRE_REGION)
  4362. DeleteObject(plv->hrgnInval);
  4363. plv->hrgnInval = NULL;
  4364. }
  4365. void ListView_OnDestroy(LV* plv)
  4366. {
  4367. //
  4368. // The tooltip window may or may not exist at this point. It
  4369. // depends if the owning window of the tips is also being destroy.
  4370. // If so, then the tips are gone already.
  4371. //
  4372. if (IsWindow(plv->hwndToolTips))
  4373. DestroyWindow(plv->hwndToolTips);
  4374. if (plv->hCurHot)
  4375. DestroyCursor(plv->hCurHot);
  4376. plv->hwndToolTips = NULL;
  4377. Str_Set(&plv->pszTip, NULL);
  4378. Str_Set(&plv->pszEmptyText, NULL);
  4379. TerminateDitherBrush();
  4380. if (!ListView_IsOwnerData(plv))
  4381. {
  4382. // Make sure to notify the app
  4383. ListView_OnDeleteAllItems(plv);
  4384. }
  4385. if ((plv->flags & LVF_FONTCREATED) && plv->hfontLabel)
  4386. {
  4387. DeleteObject(plv->hfontLabel);
  4388. // plv->flags &= ~LVF_FONTCREATED;
  4389. // plv->hwfontLabel = NULL;
  4390. }
  4391. if (plv->hfontGroup)
  4392. {
  4393. DeleteObject(plv->hfontGroup);
  4394. }
  4395. if (plv->hFontHot)
  4396. {
  4397. DeleteObject(plv->hFontHot);
  4398. }
  4399. if (plv->hbmpWatermark)
  4400. {
  4401. DeleteObject(plv->hbmpWatermark);
  4402. }
  4403. ListView_DeleteHrgnInval(plv);
  4404. if (plv->prcWorkAreas)
  4405. {
  4406. // This assert is bogus: If the app created work areas then deleted
  4407. // them, nWorkAreas will be 0 but prcWorkAreas will be non-NULL.
  4408. // ASSERT(plv->nWorkAreas > 0);
  4409. LocalFree(plv->prcWorkAreas);
  4410. }
  4411. if (plv->hdpaGroups)
  4412. {
  4413. DPA_DestroyCallback(plv->hdpaGroups, DestroyGroups, NULL);
  4414. plv->hdpaGroups = NULL;
  4415. }
  4416. }
  4417. void ListView_OnNCDestroy(LV* plv)
  4418. {
  4419. if ((!(plv->ci.style & LVS_SHAREIMAGELISTS)) || ListView_CheckBoxes(plv))
  4420. {
  4421. if (plv->himlState &&
  4422. (plv->himlState != plv->himl) &&
  4423. (plv->himlState != plv->himlSmall))
  4424. {
  4425. ImageList_Destroy(plv->himlState);
  4426. }
  4427. }
  4428. if (!(plv->ci.style & LVS_SHAREIMAGELISTS))
  4429. {
  4430. if (plv->himl)
  4431. ImageList_Destroy(plv->himl);
  4432. if (plv->himlSmall)
  4433. ImageList_Destroy(plv->himlSmall);
  4434. }
  4435. if (ListView_IsOwnerData(plv))
  4436. {
  4437. if (plv->plvrangeSel)
  4438. {
  4439. plv->plvrangeSel->lpVtbl->Release(plv->plvrangeSel);
  4440. plv->plvrangeSel = NULL;
  4441. }
  4442. if (plv->plvrangeCut)
  4443. {
  4444. plv->plvrangeCut->lpVtbl->Release(plv->plvrangeCut);
  4445. plv->plvrangeCut = NULL;
  4446. }
  4447. plv->cTotalItems = 0;
  4448. }
  4449. ListView_ReleaseBkImage(plv);
  4450. if (plv->hbrBk)
  4451. DeleteBrush(plv->hbrBk);
  4452. if (plv->hdpa)
  4453. DPA_Destroy(plv->hdpa);
  4454. if (plv->hdpaZOrder)
  4455. DPA_Destroy(plv->hdpaZOrder);
  4456. ListView_RDestroy(plv);
  4457. IncrementSearchFree(&plv->is);
  4458. ListView_SetPtr(plv->ci.hwnd, NULL);
  4459. if (plv->hTheme)
  4460. CloseThemeData(plv->hTheme);
  4461. NearFree(plv);
  4462. }
  4463. // sets the background color for the listview
  4464. //
  4465. // this creats the brush for drawing the background as well
  4466. // as sets the imagelists background color if needed
  4467. BOOL ListView_OnSetBkColor(LV* plv, COLORREF clrBk)
  4468. {
  4469. if (plv->clrBk != clrBk)
  4470. {
  4471. if (plv->hbrBk)
  4472. {
  4473. DeleteBrush(plv->hbrBk);
  4474. plv->hbrBk = NULL;
  4475. }
  4476. if (clrBk != CLR_NONE)
  4477. {
  4478. plv->hbrBk = CreateSolidBrush(clrBk);
  4479. if (!plv->hbrBk)
  4480. return FALSE;
  4481. }
  4482. // don't mess with the imagelist color if things are shared
  4483. if (!(plv->ci.style & LVS_SHAREIMAGELISTS))
  4484. {
  4485. if (plv->himl)
  4486. ImageList_SetBkColor(plv->himl, clrBk);
  4487. if (plv->himlSmall)
  4488. ImageList_SetBkColor(plv->himlSmall, clrBk);
  4489. if (plv->himlState)
  4490. ImageList_SetBkColor(plv->himlState, clrBk);
  4491. }
  4492. plv->clrBk = clrBk;
  4493. }
  4494. return TRUE;
  4495. }
  4496. void InitBrushOrg(LV* plv, HDC hdc)
  4497. {
  4498. int x;
  4499. if (ListView_IsSmallView(plv) || ListView_IsIconView(plv))
  4500. {
  4501. x = plv->ptOrigin.x;
  4502. }
  4503. else if (ListView_IsListView(plv))
  4504. {
  4505. x = plv->xOrigin;
  4506. }
  4507. else
  4508. {
  4509. x = (int)plv->ptlRptOrigin.x;
  4510. }
  4511. SetBrushOrgEx(hdc, -x, 0, NULL);
  4512. }
  4513. void ListView_InvalidateRegion(LV* plv, HRGN hrgn)
  4514. {
  4515. if (hrgn)
  4516. {
  4517. if (plv->hrgnInval == NULL)
  4518. {
  4519. plv->hrgnInval = hrgn;
  4520. }
  4521. else
  4522. {
  4523. // union it in if the entire region isn't marked for invalidate
  4524. if (plv->hrgnInval != (HRGN)ENTIRE_REGION)
  4525. {
  4526. UnionRgn(plv->hrgnInval, plv->hrgnInval, hrgn);
  4527. }
  4528. DeleteObject(hrgn);
  4529. }
  4530. }
  4531. }
  4532. //
  4533. // Used when a watermark is the listview's background (detected via clrTextBk
  4534. // being CLR_NONE) to perform a flicker-free scroll of the client area, using
  4535. // an offscreen bitmap
  4536. //
  4537. // potential perf issue -- caching DC and/or bitmap instead of create/destroy
  4538. // on each call
  4539. //
  4540. // jeffbog 2/29/96
  4541. //
  4542. void LVSeeThruScroll(LV *plv, LPRECT lprcUpdate)
  4543. {
  4544. HDC hdcOff;
  4545. HBITMAP hbmpOff;
  4546. int x,y,cx,cy;
  4547. HDC hdc = GetDC(plv->ci.hwnd);
  4548. if (!lprcUpdate)
  4549. {
  4550. x = y = 0;
  4551. cx = plv->sizeClient.cx;
  4552. cy = plv->sizeClient.cy;
  4553. }
  4554. else
  4555. {
  4556. x = lprcUpdate->left;
  4557. y = lprcUpdate->top;
  4558. cx = lprcUpdate->right - x;
  4559. cy = lprcUpdate->bottom - y;
  4560. }
  4561. hdcOff = CreateCompatibleDC(hdc);
  4562. hbmpOff = CreateCompatibleBitmap(hdc, plv->sizeClient.cx, plv->sizeClient.cy);
  4563. SelectObject(hdcOff, hbmpOff);
  4564. SendMessage(plv->ci.hwnd, WM_PRINT, (WPARAM)hdcOff, PRF_CLIENT | PRF_ERASEBKGND);
  4565. BitBlt(hdc, x, y, cx, cy, hdcOff, x, y, SRCCOPY);
  4566. ReleaseDC(plv->ci.hwnd, hdc);
  4567. DeleteDC(hdcOff);
  4568. DeleteObject(hbmpOff);
  4569. }
  4570. void ListView_OnPaint(LV* plv, HDC hdc)
  4571. {
  4572. PAINTSTRUCT ps;
  4573. RECT rcUpdate;
  4574. HDC hPaintDC = hdc;
  4575. HDC hMemDC = NULL;
  4576. HBITMAP hMemBm = NULL;
  4577. HBITMAP hOldBm;
  4578. BOOL fInternDC = FALSE;
  4579. // Before handling WM_PAINT, go ensure everything's recomputed...
  4580. //
  4581. if (plv->rcView.left == RECOMPUTE)
  4582. ListView_Recompute(plv);
  4583. // If we're in report view, update the header window: it looks
  4584. // better this way...
  4585. //
  4586. if (ListView_IsReportView(plv) && plv->hwndHdr)
  4587. UpdateWindow(plv->hwndHdr);
  4588. // If nothing to do (i.e., we recieved a WM_PAINT because
  4589. // of an RDW_INTERNALPAINT, and we didn't invalidate anything)
  4590. // don't bother with the Begin/EndPaint.
  4591. //
  4592. if (hdc || GetUpdateRect(plv->ci.hwnd, &rcUpdate, FALSE))
  4593. {
  4594. if (!(plv->flags & LVF_VISIBLE))
  4595. {
  4596. plv->flags |= LVF_VISIBLE;
  4597. // We may try to resize the column
  4598. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  4599. ListView_UpdateScrollBars(plv);
  4600. }
  4601. // this needs to be done before the beginpaint because it clears
  4602. // out the update region
  4603. if (!(plv->flags & LVF_REDRAW))
  4604. {
  4605. // add this region to our local invalidate region
  4606. HRGN hrgn = CreateRectRgn(0, 0, 0,0);
  4607. if (hrgn)
  4608. {
  4609. // ok if GetUpdateRgn fails... then hrgn will still be
  4610. // and empty region..
  4611. GetUpdateRgn(plv->ci.hwnd, hrgn, FALSE);
  4612. ListView_InvalidateRegion(plv, hrgn);
  4613. }
  4614. }
  4615. // Get device context
  4616. if (!hdc)
  4617. {
  4618. hPaintDC = hdc = BeginPaint(plv->ci.hwnd, &ps);
  4619. fInternDC = TRUE;
  4620. }
  4621. else
  4622. {
  4623. GetClipBox(hdc, &ps.rcPaint);
  4624. }
  4625. // Skip painting if redrawing is not enabled but complete cycle (EndPaint)
  4626. if (ListView_RedrawEnabled(plv))
  4627. {
  4628. // Create memory surface and map rendering context if double buffering
  4629. if (ListView_IsDoubleBuffer(plv))
  4630. {
  4631. // Only make large enough for clipping region
  4632. hMemDC = CreateCompatibleDC(hdc);
  4633. if (hMemDC)
  4634. {
  4635. hMemBm = CreateCompatibleBitmap(hdc, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint));
  4636. if (hMemBm)
  4637. {
  4638. hOldBm = SelectObject(hMemDC, hMemBm);
  4639. // Offset painting to paint in region
  4640. OffsetWindowOrgEx(hMemDC, ps.rcPaint.left, ps.rcPaint.top, NULL);
  4641. }
  4642. else
  4643. {
  4644. DeleteDC(hMemDC);
  4645. hMemDC = NULL;
  4646. }
  4647. }
  4648. }
  4649. if (hMemDC)
  4650. {
  4651. // Use memory DC if it was created
  4652. hPaintDC = hMemDC;
  4653. }
  4654. if (hPaintDC)
  4655. {
  4656. // Setup brush offset for list view scrolling
  4657. InitBrushOrg(plv, hPaintDC);
  4658. ListView_DebugDisplayClipRegion(plv, &ps.rcPaint, NULL);
  4659. // Draw backround in this pass if double buffering, otherwise, it was handled in WM_ERASEBKGND
  4660. if (ListView_IsDoubleBuffer(plv))
  4661. {
  4662. // Add on buffer offset to scrolling offset
  4663. POINT ptBrOrg;
  4664. GetBrushOrgEx(hPaintDC, &ptBrOrg);
  4665. SetBrushOrgEx(hPaintDC, ptBrOrg.x - ps.rcPaint.left, ptBrOrg.y - ps.rcPaint.top, NULL);
  4666. ListView_DrawBackground(plv, hPaintDC, &ps.rcPaint);
  4667. }
  4668. // Draw foreground
  4669. ListView_Redraw(plv, hPaintDC, &ps.rcPaint);
  4670. // Complete double buffering by blitting and freeing off-screen objects
  4671. if (ListView_IsDoubleBuffer(plv) &&
  4672. hMemDC)
  4673. {
  4674. if (plv->flags & LVF_MARQUEE)
  4675. {
  4676. HDC h = CreateCompatibleDC(hMemDC);
  4677. if (h)
  4678. {
  4679. HBITMAP hbmp, hbmpOld;
  4680. BLENDFUNCTION bf = {0};
  4681. RECT rcInvalid;
  4682. RECT rcMarquee = {0, 0, RECTWIDTH(plv->rcMarquee), RECTHEIGHT(plv->rcMarquee)};
  4683. IntersectRect(&rcInvalid, &ps.rcPaint, &plv->rcMarquee);
  4684. if (!IsRectEmpty(&rcInvalid))
  4685. {
  4686. hbmp = CreateCompatibleBitmap(hMemDC, RECTWIDTH(rcInvalid), RECTHEIGHT(rcInvalid));
  4687. if (hbmp)
  4688. {
  4689. hbmpOld = SelectObject(h, hbmp);
  4690. FillRectClr(h, &rcMarquee, g_clrMenuHilight);
  4691. bf.BlendOp = AC_SRC_OVER;
  4692. bf.SourceConstantAlpha = 70;
  4693. GdiAlphaBlend(hMemDC, rcInvalid.left, rcInvalid.top, RECTWIDTH(rcInvalid), RECTHEIGHT(rcInvalid),
  4694. h, 0, 0, RECTWIDTH(rcInvalid), RECTHEIGHT(rcInvalid), bf);
  4695. SelectObject(h, hbmpOld);
  4696. DeleteObject(hbmp);
  4697. }
  4698. SHOutlineRect(hMemDC, &plv->rcMarquee, g_clrHighlight, g_clrHighlight);
  4699. }
  4700. DeleteDC(h);
  4701. }
  4702. }
  4703. BitBlt(hdc, ps.rcPaint.left, ps.rcPaint.top, RECTWIDTH(ps.rcPaint), RECTHEIGHT(ps.rcPaint), hMemDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
  4704. SelectObject(hMemDC, hOldBm);
  4705. DeleteObject(hMemBm);
  4706. DeleteDC(hMemDC);
  4707. }
  4708. }
  4709. }
  4710. // Free DC if necessary
  4711. if (fInternDC)
  4712. EndPaint(plv->ci.hwnd, &ps);
  4713. }
  4714. }
  4715. void ListView_DrawSimpleBackground(LV *plv, HDC hdc, POINT* ppt, RECT *prcClip)
  4716. {
  4717. if (plv->clrBk != CLR_NONE)
  4718. {
  4719. //
  4720. // We just have a simple background color.
  4721. //
  4722. FillRect(hdc, prcClip, plv->hbrBk);
  4723. }
  4724. else
  4725. {
  4726. //
  4727. // Parent HWND draws the background for us.
  4728. //
  4729. POINT pt = {0,0}, ptOrig;
  4730. MapWindowPoints(plv->ci.hwnd, plv->ci.hwndParent, &pt, 1); //Map it to parent's co-ordinates
  4731. OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptOrig);
  4732. SendMessage(plv->ci.hwndParent, WM_ERASEBKGND, (WPARAM)hdc, (LPARAM)0); //Make the parent draw into child's DC
  4733. SetWindowOrgEx(hdc, ptOrig.x, ptOrig.y, NULL);
  4734. }
  4735. }
  4736. #define SATURATE(x, y) { int ___cTemp; ___cTemp = (x) + ((y + 1) * 100 * (x)) / 1000; if (___cTemp > 0xFF) ___cTemp = 0xFF; (x) = (BYTE)___cTemp; }
  4737. void SaturateDC(void * pvBitmapBits, int Amount, RECT* prcColumn, RECT* prcImage)
  4738. {
  4739. long x, y;
  4740. long uHeight = RECTHEIGHT(*prcImage);
  4741. long uWidth = RECTWIDTH(*prcImage);
  4742. ULONG* pul = (ULONG*)pvBitmapBits;
  4743. for (y = 0; y < uHeight ;y++)
  4744. {
  4745. for (x = 0; x < uWidth; x++)
  4746. {
  4747. if (x + prcImage->left >= prcColumn->left && x + prcImage->left <= prcColumn->right)
  4748. {
  4749. RGBQUAD* prgb = (RGBQUAD*)&pul[y * uWidth + x];
  4750. SATURATE(prgb->rgbRed, Amount);
  4751. SATURATE(prgb->rgbGreen, Amount);
  4752. SATURATE(prgb->rgbBlue, Amount);
  4753. }
  4754. }
  4755. }
  4756. }
  4757. void SaturateSortColumn(LV* plv, HDC hdc, void * pvBitmapBits, POINT* ppt, RECT* prcClip)
  4758. {
  4759. RECT rc;
  4760. RECT rcUpdate = *prcClip;
  4761. Header_GetItemRect(plv->hwndHdr, plv->iLastColSort, &rc);
  4762. OffsetRect(&rc, ppt->x, 0);
  4763. if (rcUpdate.left < rc.left)
  4764. rcUpdate.left = rc.left;
  4765. if (rcUpdate.right > rc.right)
  4766. rcUpdate.right = rc.right;
  4767. if (rcUpdate.left < rcUpdate.right ||
  4768. IntersectRect(&rc, &rcUpdate, prcClip))
  4769. {
  4770. SaturateDC(pvBitmapBits, 0, &rcUpdate, prcClip);
  4771. }
  4772. }
  4773. HDC PrepBackgroundDIBSection(HDC hdcDest, RECT* prc, void ** ppvBitmap, HBITMAP* phbmpOld)
  4774. {
  4775. HDC hdcRet = CreateCompatibleDC(hdcDest);
  4776. if (hdcRet)
  4777. {
  4778. HBITMAP hbmp;
  4779. BITMAPINFO bi = {0};
  4780. bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
  4781. bi.bmiHeader.biWidth = RECTWIDTH(*prc);
  4782. bi.bmiHeader.biHeight = RECTHEIGHT(*prc);
  4783. bi.bmiHeader.biPlanes = 1;
  4784. bi.bmiHeader.biBitCount = 32;
  4785. bi.bmiHeader.biCompression = BI_RGB;
  4786. hbmp = CreateDIBSection(hdcRet, &bi, DIB_RGB_COLORS, ppvBitmap, NULL, 0);
  4787. *phbmpOld = (HBITMAP)SelectObject(hdcRet, hbmp);
  4788. SetViewportOrgEx(hdcRet, -prc->left, -prc->top, NULL);
  4789. }
  4790. return hdcRet;
  4791. }
  4792. void CleanupBackgroundDIBSection(HDC hdc, HBITMAP hbmpOld)
  4793. {
  4794. if (hdc)
  4795. {
  4796. HBITMAP hbmp = SelectObject(hdc, hbmpOld);
  4797. if (hbmp)
  4798. DeleteObject(hbmp);
  4799. DeleteDC(hdc);
  4800. }
  4801. }
  4802. void ListView_DrawBackground(LV *plv, HDC hdc, RECT *prcClip)
  4803. {
  4804. HRGN hrgnClipSave;
  4805. RECT rcClip;
  4806. POINT ptBackOrg = {0};
  4807. //
  4808. // Compute ptBackOrg (aka scrolling offset), based on view style.
  4809. //
  4810. switch (plv->wView)
  4811. {
  4812. case LV_VIEW_LIST:
  4813. ptBackOrg.x = -plv->xOrigin;
  4814. ptBackOrg.y = 0;
  4815. break;
  4816. case LV_VIEW_DETAILS:
  4817. ptBackOrg.x = -plv->ptlRptOrigin.x;
  4818. ptBackOrg.y = -plv->ptlRptOrigin.y + plv->yTop;
  4819. break;
  4820. default:
  4821. ptBackOrg.x = -plv->ptOrigin.x;
  4822. ptBackOrg.y = -plv->ptOrigin.y;
  4823. break;
  4824. }
  4825. // Optimize the common/simple case
  4826. if (!(plv->pImgCtx && plv->fImgCtxComplete))
  4827. {
  4828. ListView_DrawSimpleBackground(plv, hdc, &ptBackOrg, prcClip);
  4829. if (ListView_IsWatermarked(plv))
  4830. {
  4831. HDC hdcMem = CreateCompatibleDC(hdc);
  4832. if (hdcMem)
  4833. {
  4834. HBITMAP hbmp = (HBITMAP)SelectObject(hdcMem, plv->hbmpWatermark);
  4835. RECT rcWatermark;
  4836. GetClientRect(plv->ci.hwnd, &rcWatermark);
  4837. rcWatermark.left = rcWatermark.right - plv->szWatermark.cx;
  4838. rcWatermark.top = rcWatermark.bottom - plv->szWatermark.cy;
  4839. BitBlt(hdc, rcWatermark.left, rcWatermark.top, plv->szWatermark.cx, plv->szWatermark.cy,
  4840. hdcMem, 0, 0, SRCCOPY);
  4841. SelectObject(hdcMem, hbmp);
  4842. DeleteDC(hdcMem);
  4843. }
  4844. }
  4845. if (plv->wView == LV_VIEW_DETAILS &&
  4846. plv->iLastColSort != -1 && !plv->fGroupView)
  4847. {
  4848. RECT rcUpdate = *prcClip;
  4849. RECT rc;
  4850. COLORREF cr;
  4851. Header_GetItemRect(plv->hwndHdr, plv->iLastColSort, &rc);
  4852. OffsetRect(&rc, ptBackOrg.x, 0);
  4853. if (rcUpdate.left < rc.left)
  4854. rcUpdate.left = rc.left;
  4855. if (rcUpdate.right > rc.right)
  4856. rcUpdate.right = rc.right;
  4857. cr = GetSortColor(10, plv->clrBk);
  4858. FillRectClr(hdc, &rcUpdate, cr);
  4859. }
  4860. return;
  4861. }
  4862. //
  4863. // Save the old clipping region,
  4864. // since we whack on it a lot.
  4865. //
  4866. hrgnClipSave = CreateRectRgnIndirect(prcClip);
  4867. if (hrgnClipSave)
  4868. {
  4869. if (GetClipRgn(hdc, hrgnClipSave) <= 0)
  4870. {
  4871. DeleteObject(hrgnClipSave);
  4872. hrgnClipSave = NULL;
  4873. }
  4874. }
  4875. //
  4876. // Clip the clipping region to the caller's rectangle,
  4877. // and save the final clipping rectangle in rcClip.
  4878. //
  4879. if (prcClip != NULL)
  4880. {
  4881. IntersectClipRect(hdc, prcClip->left, prcClip->top,
  4882. prcClip->right, prcClip->bottom);
  4883. }
  4884. GetClipBox(hdc, &rcClip);
  4885. if (plv->pImgCtx && plv->fImgCtxComplete)
  4886. {
  4887. RECT rcImage, rcClient;
  4888. ULONG ulState;
  4889. SIZE sizeImg;
  4890. ListView_Realize(plv, hdc, TRUE, FALSE);
  4891. switch (plv->ulBkImageFlags & LVBKIF_STYLE_MASK)
  4892. {
  4893. case LVBKIF_STYLE_TILE:
  4894. {
  4895. HDC hdcBackBuffer = hdc;
  4896. HBITMAP hbmpOld;
  4897. void * pvBits = NULL;
  4898. POINT ptBackTile = {0};
  4899. if (plv->wView == LV_VIEW_DETAILS &&
  4900. plv->iLastColSort != -1)
  4901. {
  4902. hdcBackBuffer = PrepBackgroundDIBSection(hdc, prcClip, &pvBits, &hbmpOld);
  4903. if (hdcBackBuffer == NULL)
  4904. hdcBackBuffer = hdc;
  4905. }
  4906. if (!plv->fListviewWatermarkBackgroundImages)
  4907. ptBackTile = ptBackOrg;
  4908. if (plv->ulBkImageFlags & LVBKIF_FLAG_TILEOFFSET)
  4909. {
  4910. // These offsets are in pixels, not percent (sorry)
  4911. ptBackTile.x -= plv->xOffsetPercent;
  4912. ptBackTile.y -= plv->yOffsetPercent;
  4913. }
  4914. IImgCtx_Tile(plv->pImgCtx, hdcBackBuffer, &ptBackTile, prcClip, NULL);
  4915. if (hdcBackBuffer != hdc)
  4916. {
  4917. SaturateSortColumn(plv, hdcBackBuffer, pvBits, &ptBackOrg, prcClip);
  4918. BitBlt(hdc, prcClip->left, prcClip->top, RECTWIDTH(*prcClip), RECTHEIGHT(*prcClip), hdcBackBuffer, prcClip->left, prcClip->top, SRCCOPY);
  4919. CleanupBackgroundDIBSection(hdcBackBuffer, hbmpOld);
  4920. }
  4921. }
  4922. ExcludeClipRect(hdc, prcClip->left, prcClip->top,
  4923. prcClip->right, prcClip->bottom);
  4924. break;
  4925. case LVBKIF_STYLE_NORMAL:
  4926. //
  4927. // Start with the base image.
  4928. //
  4929. IImgCtx_GetStateInfo(plv->pImgCtx, &ulState, &sizeImg, FALSE);
  4930. rcImage.left = 0;
  4931. rcImage.top = 0;
  4932. rcImage.right = sizeImg.cx;
  4933. rcImage.bottom = sizeImg.cy;
  4934. //
  4935. // Adjust for caller offsets.
  4936. //
  4937. GetClientRect(plv->ci.hwnd, &rcClient);
  4938. if (plv->xOffsetPercent)
  4939. {
  4940. LONG dx = plv->xOffsetPercent * (rcClient.right - sizeImg.cx) / 100;
  4941. rcImage.left += dx;
  4942. rcImage.right += dx;
  4943. }
  4944. if (plv->yOffsetPercent)
  4945. {
  4946. LONG dy = plv->yOffsetPercent * (rcClient.bottom - sizeImg.cy) / 100;
  4947. rcImage.top += dy;
  4948. rcImage.bottom += dy;
  4949. }
  4950. //
  4951. // Adjust for ptBackOrg (scrolling offset).
  4952. //
  4953. rcImage.left += ptBackOrg.x;
  4954. rcImage.top += ptBackOrg.y;
  4955. rcImage.right += ptBackOrg.x;
  4956. rcImage.bottom += ptBackOrg.y;
  4957. //
  4958. // Draw the image, if necessary.
  4959. //
  4960. if (RectVisible(hdc, &rcImage))
  4961. {
  4962. IImgCtx_Draw(plv->pImgCtx, hdc, &rcImage);
  4963. ExcludeClipRect(hdc, rcImage.left, rcImage.top,
  4964. rcImage.right, rcImage.bottom);
  4965. }
  4966. break;
  4967. }
  4968. }
  4969. //
  4970. // Now draw the rest of the background.
  4971. //
  4972. if (RectVisible(hdc, prcClip))
  4973. {
  4974. ListView_DrawSimpleBackground(plv, hdc, &ptBackOrg, prcClip);
  4975. }
  4976. //
  4977. // Restore old clipping region.
  4978. //
  4979. SelectClipRgn(hdc, hrgnClipSave);
  4980. if (hrgnClipSave)
  4981. {
  4982. DeleteObject(hrgnClipSave);
  4983. }
  4984. }
  4985. BOOL ListView_OnEraseBkgnd(LV *plv, HDC hdc)
  4986. {
  4987. // If redraw is turned off, still process erase bk
  4988. if (ListView_IsDoubleBuffer(plv) && (plv->flags & LVF_REDRAW))
  4989. {
  4990. // No erase, will happen in WM_PAINT handler (ListView_OnPaint)
  4991. return FALSE;
  4992. }
  4993. else
  4994. {
  4995. RECT rcClip;
  4996. //
  4997. // We draw our own background, erase with it.
  4998. //
  4999. GetClipBox(hdc, &rcClip);
  5000. ListView_DrawBackground(plv, hdc, &rcClip);
  5001. return TRUE;
  5002. }
  5003. }
  5004. void ListView_OnCommand(LV* plv, int id, HWND hwndCtl, UINT codeNotify)
  5005. {
  5006. if (hwndCtl == plv->hwndEdit)
  5007. {
  5008. switch (codeNotify)
  5009. {
  5010. case EN_UPDATE:
  5011. // We don't want flicker during replacing current selection
  5012. // as we use selection for IME composition.
  5013. //
  5014. if ((g_fDBCSInputEnabled) && (plv->flags & LVF_INSERTINGCOMP))
  5015. break;
  5016. // We will use the ID of the window as a Dirty flag...
  5017. if (IsWindowVisible(plv->hwndEdit))
  5018. {
  5019. SetWindowID(plv->hwndEdit, 1);
  5020. ListView_SetEditSize(plv);
  5021. }
  5022. break;
  5023. case EN_KILLFOCUS:
  5024. // We lost focus, so dismiss edit and save changes
  5025. // (Note that the owner might reject the change and restart
  5026. // edit mode, which traps the user. Owners need to give the
  5027. // user a way to get out.)
  5028. //
  5029. //
  5030. // Fix horrible undocumented hanging problem: LVN_ENDLABELEDIT
  5031. // is sent in response to EN_KILLFOCUS, which is send in response
  5032. // to WM_KILLFOCUS, and it is undocumented that you cannot display
  5033. // UI during WM_KILLFOCUS when a journal record hook is active,
  5034. // because the presence of a hook forces serialization of activation,
  5035. // and so when you put up UI, you generate activation changes, which
  5036. // get stuck because you haven't finished responding to the previous
  5037. // WM_KILLFOCUS message yet.
  5038. //
  5039. // See NT bug 414634.
  5040. //
  5041. if (InSendMessage())
  5042. ReplyMessage(0);
  5043. if (!ListView_DismissEdit(plv, FALSE))
  5044. return;
  5045. break;
  5046. case HN_BEGINDIALOG: // pen windows is bringing up a dialog
  5047. ASSERT(GetSystemMetrics(SM_PENWINDOWS)); // only on a pen system
  5048. plv->fNoDismissEdit = TRUE;
  5049. break;
  5050. case HN_ENDDIALOG: // pen windows has destroyed dialog
  5051. ASSERT(GetSystemMetrics(SM_PENWINDOWS)); // only on a pen system
  5052. plv->fNoDismissEdit = FALSE;
  5053. break;
  5054. }
  5055. // Forward edit control notifications up to parent
  5056. //
  5057. if (IsWindow(hwndCtl))
  5058. FORWARD_WM_COMMAND(plv->ci.hwndParent, id, hwndCtl, codeNotify, SendMessage);
  5059. }
  5060. }
  5061. void ListView_OnWindowPosChanged(LV* plv, const WINDOWPOS* lpwpos)
  5062. {
  5063. if (!lpwpos || !(lpwpos->flags & SWP_NOSIZE))
  5064. {
  5065. RECT rc;
  5066. int iOldSlots;
  5067. // Update scrollbars first, since ListView_OnEnsureVisible requires accurate scroll info
  5068. ListView_UpdateScrollBars(plv);
  5069. if (ListView_IsOwnerData(plv) &&
  5070. ListView_IsSlotView(plv))
  5071. {
  5072. iOldSlots = ListView_GetSlotCount(plv, TRUE, NULL, NULL);
  5073. }
  5074. GetClientRect(plv->ci.hwnd, &rc);
  5075. plv->sizeClient.cx = rc.right;
  5076. plv->sizeClient.cy = rc.bottom;
  5077. if (ListView_IsAutoArrangeView(plv))
  5078. {
  5079. // Call off to the arrange function.
  5080. ListView_ArrangeOrSnapToGrid(plv);
  5081. }
  5082. if (ListView_IsOwnerData(plv))
  5083. {
  5084. plv->rcView.left = RECOMPUTE;
  5085. ListView_Recompute(plv);
  5086. ListView_DismissEdit(plv, FALSE);
  5087. if (ListView_IsSlotView(plv))
  5088. {
  5089. // Uses the
  5090. int iNewSlots = ListView_GetSlotCount(plv, TRUE, NULL, NULL);
  5091. if ((iNewSlots != iOldSlots) && (ListView_Count(plv) > min(iNewSlots, iOldSlots)))
  5092. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  5093. }
  5094. }
  5095. ListView_RInitialize(plv, TRUE);
  5096. if (ListView_IsWatermarked(plv))
  5097. {
  5098. GetClientRect(plv->ci.hwnd, &rc);
  5099. rc.left = rc.right - plv->szWatermark.cx;
  5100. rc.top = rc.bottom - plv->szWatermark.cy;
  5101. InvalidateRect(plv->ci.hwnd, &rc, TRUE);
  5102. }
  5103. }
  5104. }
  5105. void ListView_InvalidateSelectedOrCutOwnerData(LV* plv, ILVRange *plvrangeSel)
  5106. {
  5107. UINT rdwFlags = RDW_INVALIDATE;
  5108. int cItem = ListView_Count(plv);
  5109. DWORD dwType = plv->wView;
  5110. int i;
  5111. RECT rcView;
  5112. ASSERT(ListView_IsOwnerData(plv));
  5113. ASSERT(plv);
  5114. GetClientRect(plv->ci.hwnd, &rcView);
  5115. if (plv->clrTextBk == CLR_NONE
  5116. || (plv->himl && (plv->clrBk != ImageList_GetBkColor(plv->himl))))
  5117. {
  5118. // always do an erase, otherwise the text background won't paint right
  5119. rdwFlags |= RDW_ERASE;
  5120. }
  5121. // calculate start of items and end of items visible on the view
  5122. //
  5123. switch (dwType)
  5124. {
  5125. case LV_VIEW_DETAILS:
  5126. i = ListView_RYHitTest(plv, rcView.top);
  5127. cItem = ListView_RYHitTest(plv, rcView.bottom) + 1;
  5128. break;
  5129. case LV_VIEW_LIST:
  5130. i = ListView_LCalcViewItem(plv, rcView.left, rcView.top);
  5131. cItem = ListView_LCalcViewItem(plv, rcView.right, rcView.bottom) + 1;
  5132. break;
  5133. default:
  5134. ListView_CalcMinMaxIndex(plv, &rcView, &i, &cItem);
  5135. break;
  5136. }
  5137. i = max(i, 0);
  5138. cItem = min(ListView_Count(plv), cItem);
  5139. if (cItem > i)
  5140. {
  5141. ListView_NotifyCacheHint(plv, i, cItem-1);
  5142. }
  5143. for (; i < cItem; i++)
  5144. {
  5145. if (plvrangeSel->lpVtbl->IsSelected(plvrangeSel, i) == S_OK)
  5146. {
  5147. ListView_InvalidateItem(plv, i, FALSE, rdwFlags);
  5148. }
  5149. }
  5150. }
  5151. void ListView_RedrawSelection(LV* plv)
  5152. {
  5153. if (ListView_IsOwnerData(plv))
  5154. {
  5155. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeSel);
  5156. }
  5157. else
  5158. {
  5159. int i = -1;
  5160. while ((i = ListView_OnGetNextItem(plv, i, LVNI_SELECTED)) != -1)
  5161. {
  5162. ListView_InvalidateItem(plv, i, TRUE, RDW_INVALIDATE | RDW_ERASE);
  5163. }
  5164. if (ListView_IsReportView(plv))
  5165. {
  5166. int iEnd = ListView_RYHitTest(plv, plv->sizeClient.cy) + 1;
  5167. iEnd = min(iEnd, ListView_Count(plv));
  5168. // if we're in report mode, sub items may have selection focus
  5169. for (i = ListView_RYHitTest(plv, 0); i < iEnd; i++)
  5170. {
  5171. int iCol;
  5172. for (iCol = 1; iCol < plv->cCol; iCol++)
  5173. {
  5174. LISTSUBITEM lsi;
  5175. ListView_GetSubItem(plv, i, iCol, &lsi);
  5176. if (lsi.state & LVIS_SELECTED)
  5177. {
  5178. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
  5179. }
  5180. break;
  5181. }
  5182. }
  5183. }
  5184. }
  5185. UpdateWindow(plv->ci.hwnd);
  5186. }
  5187. void ListView_OnSetFocus(LV* plv, HWND hwndOldFocus)
  5188. {
  5189. ASSERT(gcWheelDelta == 0);
  5190. // due to the way listview call SetFocus on themselves on buttondown,
  5191. // the window can get a strange sequence of focus messages: first
  5192. // set, then kill, and then set again. since these are not really
  5193. // focus changes, ignore them and only handle "real" cases.
  5194. //
  5195. // But still send out the accessibility notification because USER
  5196. // has already pushed focus back to the listview instead of to the
  5197. // focus item.
  5198. if (hwndOldFocus == plv->ci.hwnd)
  5199. {
  5200. ListView_NotifyFocusEvent(plv);
  5201. return;
  5202. }
  5203. plv->flags |= ListView_HideLabels(plv) ? LVF_FOCUSED : LVF_FOCUSED | LVF_UNFOLDED;
  5204. if (IsWindowVisible(plv->ci.hwnd))
  5205. {
  5206. if (plv->iFocus != -1)
  5207. {
  5208. ListView_InvalidateItem(plv, plv->iFocus, TRUE, RDW_INVALIDATE | RDW_ERASE);
  5209. ListView_NotifyFocusEvent(plv);
  5210. }
  5211. ListView_RedrawSelection(plv);
  5212. }
  5213. // Let the parent window know that we are getting the focus.
  5214. CCSendNotify(&plv->ci, NM_SETFOCUS, NULL);
  5215. }
  5216. void ListView_OnKillFocus(LV* plv, HWND hwndNewFocus)
  5217. {
  5218. // Reset wheel scroll amount
  5219. gcWheelDelta = 0;
  5220. // due to the way listview call SetFocus on themselves on buttondown,
  5221. // the window can get a strange sequence of focus messages: first
  5222. // set, then kill, and then set again. since these are not really
  5223. // focus changes, ignore them and only handle "real" cases.
  5224. if (!plv || hwndNewFocus == plv->ci.hwnd)
  5225. return;
  5226. ListView_CancelTipTrack(plv);
  5227. plv->flags &= ~(LVF_FOCUSED|LVF_UNFOLDED);
  5228. // Blow this off if we are not currently visible (being destroyed!)
  5229. if (IsWindowVisible(plv->ci.hwnd))
  5230. {
  5231. if (plv->iFocus != -1)
  5232. {
  5233. UINT fRedraw = RDW_INVALIDATE;
  5234. if (plv->clrTextBk == CLR_NONE || plv->fListviewShadowText)
  5235. fRedraw |= RDW_ERASE;
  5236. ListView_InvalidateFoldedItem(plv, plv->iFocus, TRUE, fRedraw);
  5237. }
  5238. ListView_RedrawSelection(plv);
  5239. }
  5240. // Let the parent window know that we are losing the focus.
  5241. CCSendNotify(&plv->ci, NM_KILLFOCUS, NULL);
  5242. IncrementSearchString(&plv->is, 0, NULL);
  5243. }
  5244. void ListView_DeselectAll(LV* plv, int iDontDeselect)
  5245. {
  5246. int i = -1;
  5247. int nSkipped = 0;
  5248. BOOL fWasSelected = FALSE;
  5249. if (iDontDeselect != -1)
  5250. {
  5251. if (ListView_OnGetItemState(plv, iDontDeselect, LVIS_SELECTED))
  5252. fWasSelected = TRUE;
  5253. }
  5254. if (ListView_IsOwnerData(plv))
  5255. {
  5256. // if there's only one item selected, and that item is the iDontDeselect
  5257. // then our work is done...
  5258. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  5259. if (plv->nSelected == 1 && fWasSelected)
  5260. return;
  5261. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeSel);
  5262. ListView_OnSetItemState(plv, -1, 0, LVIS_SELECTED);
  5263. if (fWasSelected)
  5264. {
  5265. ListView_OnSetItemState(plv, iDontDeselect, LVIS_SELECTED, LVIS_SELECTED);
  5266. nSkipped = 1;
  5267. }
  5268. }
  5269. else
  5270. {
  5271. if (iDontDeselect != plv->iFocus)
  5272. {
  5273. ListView_OnSetItemState(plv, plv->iFocus, 0, LVIS_SELECTED);
  5274. }
  5275. while ((plv->nSelected - nSkipped) && (i = ListView_OnGetNextItem(plv, i, LVNI_SELECTED)) != -1)
  5276. {
  5277. if (i != iDontDeselect)
  5278. {
  5279. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5280. }
  5281. else
  5282. {
  5283. if (fWasSelected)
  5284. {
  5285. nSkipped++;
  5286. }
  5287. }
  5288. }
  5289. }
  5290. RIPMSG((plv->nSelected - nSkipped) == 0, "ListView_DeselectAll: Do not refuse a deselect when telling listview to Deselect all.");
  5291. plv->nSelected = nSkipped;
  5292. }
  5293. // toggle the selection state of an item
  5294. void ListView_ToggleSelection(LV* plv, int iItem)
  5295. {
  5296. UINT cur_state;
  5297. if (iItem != -1)
  5298. {
  5299. cur_state = ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  5300. ListView_OnSetItemState(plv, iItem, cur_state ^ LVIS_SELECTED, LVIS_SELECTED);
  5301. }
  5302. }
  5303. // Selects (or toggles) a range of items in the list.
  5304. // The curent iFocus is the starting location
  5305. // iItem - is the ending item
  5306. // fToggle - Well set all of the selection state of all of the items to
  5307. // inverse the starting location
  5308. //
  5309. void ListView_SelectRangeTo(LV* plv, int iItem, BOOL fResetRest)
  5310. {
  5311. int iMin, iMax;
  5312. int i = -1;
  5313. UINT uSelVal = LVIS_SELECTED;
  5314. if (plv->iMark == -1)
  5315. {
  5316. ListView_SetFocusSel(plv, iItem, TRUE, TRUE, FALSE);
  5317. return;
  5318. }
  5319. if (!fResetRest)
  5320. uSelVal = ListView_OnGetItemState(plv, plv->iMark, LVIS_SELECTED);
  5321. // If we are in report view or list view we simply walk through the
  5322. // indexes to see which items to select or deselect. otherwise it
  5323. // is is based off of the location of the objects being within the
  5324. // rectangle that is defined by
  5325. if (ListView_IsListView(plv) || (ListView_IsReportView(plv) && !plv->fGroupView))
  5326. {
  5327. iMin = min(iItem, plv->iMark);
  5328. iMax = max(iItem, plv->iMark);
  5329. if (ListView_IsOwnerData(plv))
  5330. {
  5331. if (fResetRest)
  5332. {
  5333. ListView_DeselectAll(plv, -1);
  5334. }
  5335. if (iMax > iMin)
  5336. {
  5337. if (LVIS_SELECTED & uSelVal)
  5338. {
  5339. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, iMin, iMax)))
  5340. return;
  5341. }
  5342. else
  5343. {
  5344. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, iMin, iMax)))
  5345. return;
  5346. }
  5347. ListView_SendODChangeAndInvalidate(plv, iMin, iMax, uSelVal ^ LVIS_SELECTED, uSelVal);
  5348. }
  5349. else
  5350. {
  5351. ListView_OnSetItemState(plv, iMin, uSelVal, LVIS_SELECTED);
  5352. }
  5353. }
  5354. else
  5355. {
  5356. if (fResetRest)
  5357. {
  5358. while ((i = ListView_OnGetNextItem(plv, i, LVNI_SELECTED)) != -1)
  5359. {
  5360. if (i < iMin || i > iMax)
  5361. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5362. }
  5363. }
  5364. while (iMin <= iMax)
  5365. {
  5366. ListView_OnSetItemState(plv, iMin, uSelVal, LVIS_SELECTED);
  5367. iMin++;
  5368. }
  5369. }
  5370. }
  5371. else
  5372. {
  5373. RECT rcTemp;
  5374. RECT rcTemp2;
  5375. RECT rcBounding;
  5376. int iFirstItem = (plv->iMark < iItem)? plv->iMark: iItem;
  5377. int iSecondItem = (plv->iMark > iItem)? plv->iMark: iItem;
  5378. ListView_GetRects(plv, iFirstItem, QUERY_DEFAULT, NULL, NULL, NULL, &rcTemp);
  5379. ListView_GetRects(plv, iSecondItem, QUERY_DEFAULT, NULL, NULL, NULL, &rcTemp2);
  5380. UnionRect(&rcBounding, &rcTemp, &rcTemp2);
  5381. ListView_CalcMinMaxIndex(plv, &rcBounding, &iMin, &iMax);
  5382. if (ListView_IsOwnerData(plv) && (iMax > iMin))
  5383. {
  5384. if (fResetRest)
  5385. {
  5386. ListView_DeselectAll(plv, -1);
  5387. }
  5388. iMax = min(iMax, ListView_Count(plv));
  5389. iMin = max(iMin, 0);
  5390. if (LVIS_SELECTED & uSelVal)
  5391. {
  5392. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, iMin, iMax - 1)))
  5393. return;
  5394. }
  5395. else
  5396. {
  5397. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, iMin, iMax - 1)))
  5398. return;
  5399. }
  5400. ListView_SendODChangeAndInvalidate(plv, iMin, iMax, uSelVal ^ LVIS_SELECTED, uSelVal);
  5401. }
  5402. else
  5403. {
  5404. int iZ;
  5405. POINT pt;
  5406. RECT rcItem;
  5407. for (i = 0; i < ListView_Count(plv); i++)
  5408. {
  5409. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, NULL, &rcItem);
  5410. pt.x = (rcItem.right + rcItem.left) / 2; // center of item
  5411. pt.y = (rcItem.bottom + rcItem.top) / 2;
  5412. // Is the item within the y bound of the first and last item?
  5413. if (pt.y > rcTemp.top &&
  5414. pt.y < rcTemp2.bottom)
  5415. {
  5416. // Yes. Check to see if the item is in the first row.
  5417. if (pt.y < rcTemp.bottom)
  5418. {
  5419. // It is. Then check to see if it's before the first item in that row.
  5420. if (pt.x < rcTemp.left)
  5421. {
  5422. // It is. Then this item is not to be selected.
  5423. if (fResetRest)
  5424. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5425. // Continue to the next item
  5426. continue;
  5427. }
  5428. }
  5429. // Is the item in the last row?
  5430. if (pt.y > rcTemp2.top)
  5431. {
  5432. // Yes. Is it after the last item in the selection?
  5433. if (pt.x > rcTemp2.right)
  5434. {
  5435. // It is. Then this item is not to be selected.
  5436. if (fResetRest)
  5437. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5438. // Continue to the next item
  5439. continue;
  5440. }
  5441. }
  5442. // The item is in the selection range. Go ahead and select it
  5443. if (!ListView_IsOwnerData(plv))
  5444. {
  5445. iZ = ListView_ZOrderIndex(plv, i);
  5446. if (iZ > 0)
  5447. DPA_InsertPtr(plv->hdpaZOrder, 0, DPA_DeletePtr(plv->hdpaZOrder, iZ));
  5448. }
  5449. ListView_OnSetItemState(plv, i, uSelVal, LVIS_SELECTED);
  5450. }
  5451. else if (fResetRest)
  5452. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  5453. }
  5454. }
  5455. }
  5456. }
  5457. // makes an item the focused item and optionally selects it
  5458. //
  5459. // in:
  5460. // iItem item to get the focus
  5461. // fSelectAlso select this item as well as set it as the focus
  5462. // fDeselectAll deselect all items first
  5463. // fToggleSel toggle the selection state of the item
  5464. //
  5465. // returns:
  5466. // index of focus item (if focus change was refused)
  5467. int ListView_SetFocusSel(LV* plv, int iItem, BOOL fSelectAlso, BOOL fDeselectAll, BOOL fToggleSel)
  5468. {
  5469. int iFocus = plv->iFocus;
  5470. // if we're single sel mode, don't bother with this because
  5471. // the set item will do it for us
  5472. if (!(plv->ci.style & LVS_SINGLESEL) && (fDeselectAll))
  5473. ListView_DeselectAll(plv, -1);
  5474. if (iItem != plv->iFocus)
  5475. {
  5476. // remove the old focus
  5477. if (plv->iFocus != -1)
  5478. {
  5479. // If he refuses to give up the focus, bail out.
  5480. if (!ListView_OnSetItemState(plv, plv->iFocus, 0, LVIS_FOCUSED))
  5481. return plv->iFocus;
  5482. }
  5483. }
  5484. if (!ListView_IsOwnerData(plv))
  5485. {
  5486. if (fSelectAlso)
  5487. {
  5488. if (ListView_IsIconView(plv) || ListView_IsSmallView(plv) || ListView_IsTileView(plv))
  5489. {
  5490. int iZ = ListView_ZOrderIndex(plv, iItem);
  5491. if (iZ > 0)
  5492. DPA_InsertPtr(plv->hdpaZOrder, 0, DPA_DeletePtr(plv->hdpaZOrder, iZ));
  5493. }
  5494. }
  5495. }
  5496. /* Ensure that when moving focus that we refresh the previous focus
  5497. owner properly. */
  5498. if (iFocus != -1 && iFocus != plv->iFocus && (plv->flags & LVF_UNFOLDED))
  5499. ListView_InvalidateFoldedItem(plv, iFocus, FALSE, RDW_INVALIDATE);
  5500. if (plv->iMark == -1)
  5501. plv->iMark = iItem;
  5502. SetTimer(plv->ci.hwnd, IDT_SCROLLWAIT, GetDoubleClickTime(), NULL);
  5503. plv->flags |= LVF_SCROLLWAIT;
  5504. if (fToggleSel)
  5505. {
  5506. ListView_ToggleSelection(plv, iItem);
  5507. ListView_OnSetItemState(plv, iItem, LVIS_FOCUSED, LVIS_FOCUSED);
  5508. }
  5509. else
  5510. {
  5511. UINT flags = ((fSelectAlso || plv->ci.style & LVS_SINGLESEL) ?
  5512. (LVIS_SELECTED | LVIS_FOCUSED) : LVIS_FOCUSED);
  5513. ListView_OnSetItemState(plv, iItem, flags, flags);
  5514. }
  5515. return iItem;
  5516. }
  5517. UINT GetLVKeyFlags()
  5518. {
  5519. UINT uFlags = 0;
  5520. if (GetKeyState(VK_MENU) < 0)
  5521. uFlags |= LVKF_ALT;
  5522. if (GetKeyState(VK_CONTROL) < 0)
  5523. uFlags |= LVKF_CONTROL;
  5524. if (GetKeyState(VK_SHIFT) < 0)
  5525. uFlags |= LVKF_SHIFT;
  5526. return uFlags;
  5527. }
  5528. void ListView_OnKey(LV* plv, UINT vk, BOOL fDown, int cRepeat, UINT flags)
  5529. {
  5530. UINT lvni = 0;
  5531. int iNewFocus;
  5532. BOOL fCtlDown;
  5533. BOOL fShiftDown;
  5534. LV_KEYDOWN nm;
  5535. HWND hwnd = plv->ci.hwnd;
  5536. if (!fDown)
  5537. return;
  5538. // Cancel manual tip track if any key is pressed
  5539. ListView_CancelTipTrack(plv);
  5540. // Swap the left and right arrow key if the control is mirrored.
  5541. vk = RTLSwapLeftRightArrows(&plv->ci, vk);
  5542. //prevent any change in selected items before the dbl click timer goes off
  5543. //so that we don't launch wrong item(s)
  5544. if (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickHappened && plv->fOneClickOK)
  5545. {
  5546. //if a key is pressed with a mouse click with one click activate and double click
  5547. //timer, we end up setting up a timer and then processing the keydown
  5548. //this causes an item to be launched right away (from this code) and in case
  5549. //of return being pressed it causes double activation
  5550. //prevent these cases:
  5551. if (vk == VK_SHIFT || vk == VK_CONTROL || vk == VK_MENU || vk == VK_RETURN)
  5552. return;
  5553. KillTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED);
  5554. plv->fOneClickHappened = FALSE;
  5555. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  5556. if (!IsWindow(hwnd))
  5557. return;
  5558. }
  5559. // Notify
  5560. nm.wVKey = (WORD) vk;
  5561. nm.flags = flags;
  5562. if (CCSendNotify(&plv->ci, LVN_KEYDOWN, &nm.hdr))
  5563. {
  5564. plv->iPuntChar++;
  5565. return;
  5566. }
  5567. else if (plv->iPuntChar)
  5568. {
  5569. // this is tricky... if we want to punt the char, just increment the
  5570. // count. if we do NOT, then we must clear the queue of WM_CHAR's
  5571. // this is to preserve the iPuntChar to mean "punt the next n WM_CHAR messages
  5572. MSG msg;
  5573. while(plv->iPuntChar && PeekMessage(&msg, plv->ci.hwnd, WM_CHAR, WM_CHAR, PM_REMOVE))
  5574. {
  5575. plv->iPuntChar--;
  5576. }
  5577. ASSERT(!plv->iPuntChar);
  5578. }
  5579. if (ListView_Count(plv) == 0) // don't blow up on empty list
  5580. return;
  5581. fCtlDown = GetKeyState(VK_CONTROL) < 0;
  5582. fShiftDown = GetKeyState(VK_SHIFT) < 0;
  5583. switch (vk)
  5584. {
  5585. case VK_SPACE:
  5586. // If shift (extend) or control (disjoint) select,
  5587. // then toggle selection state of focused item.
  5588. if (fCtlDown)
  5589. {
  5590. plv->iMark = plv->iFocus;
  5591. ListView_ToggleSelection(plv, plv->iFocus);
  5592. plv->iPuntChar++;
  5593. }
  5594. if (fShiftDown)
  5595. {
  5596. ListView_SelectRangeTo(plv, plv->iFocus, TRUE);
  5597. }
  5598. if (ListView_CheckBoxes(plv))
  5599. {
  5600. if (plv->iFocus != -1)
  5601. ListView_HandleStateIconClick(plv, plv->iFocus);
  5602. if (ListView_IsSimpleSelect(plv))
  5603. {
  5604. int iToggle = -1;
  5605. while ((iToggle = ListView_OnGetNextItem(plv, iToggle, LVNI_SELECTED)) != -1)
  5606. {
  5607. if (plv->iFocus != iToggle)
  5608. {
  5609. ListView_HandleStateIconClick(plv, iToggle);
  5610. }
  5611. }
  5612. }
  5613. }
  5614. //notify of navigation key usage
  5615. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5616. return;
  5617. case VK_RETURN:
  5618. CCSendNotify(&plv->ci, NM_RETURN, NULL);
  5619. /// some (comdlg32 for example) destroy on double click
  5620. // we need to bail if that happens because plv is no longer valid
  5621. if (!IsWindow(hwnd))
  5622. return;
  5623. {
  5624. NMITEMACTIVATE nm;
  5625. nm.iItem = plv->iFocus;
  5626. nm.iSubItem = 0;
  5627. nm.uChanged = 0;
  5628. nm.ptAction.x = -1;
  5629. nm.ptAction.y = -1;
  5630. nm.uKeyFlags = GetLVKeyFlags();
  5631. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &nm.hdr);
  5632. if (!IsWindow(hwnd))
  5633. return;
  5634. }
  5635. //notify of navigation key usage
  5636. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5637. return;
  5638. case VK_ADD:
  5639. if (ListView_IsReportView(plv) && (GetKeyState(VK_CONTROL) < 0))
  5640. {
  5641. HCURSOR hcurPrev;
  5642. int i;
  5643. hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  5644. for (i=0; i < plv->cCol; i++)
  5645. {
  5646. ListView_RSetColumnWidth(plv, i, -1);
  5647. }
  5648. SetCursor(hcurPrev);
  5649. //notify of navigation key usage
  5650. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5651. return;
  5652. }
  5653. }
  5654. if (GetKeyState(VK_MENU) < 0)
  5655. return;
  5656. // For a single selection listview, disable extending the selection
  5657. // by turning off the keyboard modifiers.
  5658. if (plv->ci.style & LVS_SINGLESEL)
  5659. {
  5660. fCtlDown = FALSE;
  5661. fShiftDown = FALSE;
  5662. }
  5663. //
  5664. // Let the Arrow function attempt to process the key.
  5665. //
  5666. iNewFocus = ListView_Arrow(plv, plv->iFocus, vk);
  5667. // If control (disjoint) selection, don't change selection.
  5668. // If shift (extend) or control selection, don't deselect all.
  5669. //
  5670. if (iNewFocus != -1)
  5671. {
  5672. if (fShiftDown)
  5673. {
  5674. ListView_SelectRangeTo(plv, iNewFocus, TRUE);
  5675. ListView_SetFocusSel(plv, iNewFocus, FALSE, FALSE, FALSE);
  5676. }
  5677. else
  5678. {
  5679. if (!fCtlDown)
  5680. plv->iMark = iNewFocus;
  5681. ListView_SetFocusSel(plv, iNewFocus, !fCtlDown, !fShiftDown && !fCtlDown, FALSE);
  5682. }
  5683. IncrementSearchString(&plv->is, 0, NULL);
  5684. CCPlaySound(c_szSelect);
  5685. ListView_OnKeyboardSelected(plv, iNewFocus);
  5686. }
  5687. // on keyboard movement, scroll immediately.
  5688. if (ListView_CancelScrollWait(plv))
  5689. {
  5690. ListView_OnEnsureVisible(plv, plv->iFocus, FALSE);
  5691. UpdateWindow(plv->ci.hwnd);
  5692. }
  5693. //notify of navigation key usage
  5694. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5695. }
  5696. //
  5697. // LVN_INCREMENTALSEARCH gives the app the opportunity to customize
  5698. // incremental search. For example, if the items are numeric,
  5699. // the app can do numerical search instead of string search.
  5700. //
  5701. // App sets pnmfi->lvfi.lParam to the result of the incremental search,
  5702. // or to -2 to fai the search and just beep.
  5703. //
  5704. // App can return 2 to indicate that all processing should stop, if
  5705. // app wants to take over incremental search completely.
  5706. //
  5707. BOOL ListView_IncrementalSearch(LV *plv, int iStartFrom, LPNMLVFINDITEM pnmfi, int *pi)
  5708. {
  5709. INT_PTR fRc;
  5710. ASSERT(!(pnmfi->lvfi.flags & LVFI_PARAM));
  5711. pnmfi->lvfi.lParam = -1;
  5712. fRc = CCSendNotify(&plv->ci, LVN_INCREMENTALSEARCH, &pnmfi->hdr);
  5713. *pi = (int)pnmfi->lvfi.lParam;
  5714. // Cannot just return fRc because some apps return 1 to all WM_NOTIFY's
  5715. return fRc == 2;
  5716. }
  5717. // Now only Korean version is interested in incremental search with composition string.
  5718. LPTSTR GET_COMP_STRING(HIMC hImc, DWORD dwFlags)
  5719. {
  5720. LONG iNumComp;
  5721. PTSTR pszCompStr;
  5722. iNumComp = ImmGetCompositionString(hImc, dwFlags, NULL, 0);
  5723. pszCompStr = (PTSTR)LocalAlloc(LPTR, sizeof(TCHAR)*(iNumComp+1));
  5724. if (pszCompStr)
  5725. {
  5726. if (iNumComp)
  5727. ImmGetCompositionString(hImc, dwFlags, pszCompStr, iNumComp+1);
  5728. pszCompStr[iNumComp] = TEXT('\0');
  5729. }
  5730. return pszCompStr;
  5731. }
  5732. #define FREE_COMP_STRING(pszCompStr) LocalFree((HLOCAL)(pszCompStr))
  5733. BOOL ListView_OnImeComposition(LV* plv, WPARAM wParam, LPARAM lParam)
  5734. {
  5735. LPTSTR lpsz;
  5736. NMLVFINDITEM nmfi;
  5737. int i;
  5738. int iStartFrom = -1;
  5739. int iLen;
  5740. int iCount;
  5741. HIMC hImc;
  5742. TCHAR *pszCompStr;
  5743. BOOL fRet = TRUE;
  5744. iCount = ListView_Count(plv);
  5745. if (!iCount || plv->iFocus == -1)
  5746. return fRet;
  5747. if (hImc = ImmGetContext(plv->ci.hwnd))
  5748. {
  5749. if (lParam & GCS_RESULTSTR)
  5750. {
  5751. fRet = FALSE;
  5752. pszCompStr = GET_COMP_STRING(hImc, GCS_RESULTSTR);
  5753. if (pszCompStr)
  5754. {
  5755. IncrementSearchImeCompStr(&plv->is, FALSE, pszCompStr, &lpsz);
  5756. FREE_COMP_STRING(pszCompStr);
  5757. }
  5758. }
  5759. if (lParam & GCS_COMPSTR)
  5760. {
  5761. fRet = TRUE;
  5762. pszCompStr = GET_COMP_STRING(hImc, GCS_COMPSTR);
  5763. if (pszCompStr)
  5764. {
  5765. if (IncrementSearchImeCompStr(&plv->is, TRUE, pszCompStr, &lpsz))
  5766. iStartFrom = plv->iFocus;
  5767. else
  5768. iStartFrom = ((plv->iFocus - 1) + iCount)% iCount;
  5769. nmfi.lvfi.flags = LVFI_SUBSTRING | LVFI_STRING | LVFI_WRAP;
  5770. nmfi.lvfi.psz = lpsz;
  5771. iLen = lstrlen(lpsz);
  5772. // special case space as the first character
  5773. if ((iLen == 1) && (*lpsz == TEXT(' ')))
  5774. {
  5775. if (plv->iFocus != -1)
  5776. {
  5777. ListView_OnSetItemState(plv, plv->iFocus, LVIS_SELECTED, LVIS_SELECTED);
  5778. IncrementSearchString(&plv->is, 0, NULL);
  5779. }
  5780. //notify of navigation key usage
  5781. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5782. return fRet;
  5783. }
  5784. // Give caller full string in case they want to do something custom
  5785. if (ListView_IncrementalSearch(plv, iStartFrom, &nmfi, &i))
  5786. return fRet;
  5787. if (iLen > 0 && SameChars(lpsz, lpsz[0]))
  5788. {
  5789. // The user has been typing the same char over and over again.
  5790. // Switch from incremental search to Windows 3.1 style search.
  5791. iStartFrom = plv->iFocus;
  5792. nmfi.lvfi.psz = lpsz + iLen - 1;
  5793. }
  5794. if (i == -1)
  5795. i = ListView_OnFindItem(plv, iStartFrom, &nmfi.lvfi);
  5796. if (!ListView_IsValidItemNumber(plv, i))
  5797. {
  5798. i = -1;
  5799. }
  5800. TraceMsg(TF_LISTVIEW, "CIme listsearch %d %s %d", (LPTSTR)lpsz, (LPTSTR)lpsz, i);
  5801. if (i != -1)
  5802. {
  5803. ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
  5804. plv->iMark = i;
  5805. if (ListView_CancelScrollWait(plv))
  5806. ListView_OnEnsureVisible(plv, i, FALSE);
  5807. }
  5808. else
  5809. {
  5810. // Don't beep on spaces, we use it for selection.
  5811. IncrementSearchBeep(&plv->is);
  5812. }
  5813. //notify of navigation key usage
  5814. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5815. FREE_COMP_STRING(pszCompStr);
  5816. }
  5817. }
  5818. ImmReleaseContext(plv->ci.hwnd, hImc);
  5819. }
  5820. return fRet;
  5821. }
  5822. // REVIEW: We will want to reset ichCharBuf to 0 on certain conditions,
  5823. // such as: focus change, ENTER, arrow key, mouse click, etc.
  5824. //
  5825. void ListView_OnChar(LV* plv, UINT ch, int cRepeat)
  5826. {
  5827. LPTSTR lpsz;
  5828. NMLVFINDITEM nmfi;
  5829. int i;
  5830. int iStartFrom = -1;
  5831. int iLen;
  5832. int iCount;
  5833. iCount = ListView_Count(plv);
  5834. if (!iCount)
  5835. return;
  5836. // Don't search for chars that cannot be in a file name (like ENTER and TAB)
  5837. // The Polish keyboard layout uses CTRL+ALT to
  5838. // enter some normal letters, so don't punt if the CTRL key is down or
  5839. // people in Poland are in trouble! We need to fix this. NTRAID 5262.
  5840. if (ch < TEXT(' '))// || GetKeyState(VK_CONTROL) < 0)
  5841. {
  5842. IncrementSearchString(&plv->is, 0, NULL);
  5843. return;
  5844. }
  5845. if (IncrementSearchString(&plv->is, ch, &lpsz))
  5846. iStartFrom = plv->iFocus;
  5847. else
  5848. iStartFrom = ((plv->iFocus - 1) + iCount)% iCount;
  5849. nmfi.lvfi.flags = LVFI_SUBSTRING | LVFI_STRING | LVFI_WRAP;
  5850. nmfi.lvfi.psz = lpsz;
  5851. iLen = lstrlen(lpsz);
  5852. // special case space as the first character
  5853. if ((iLen == 1) && (*lpsz == ' '))
  5854. {
  5855. if (plv->iFocus != -1)
  5856. {
  5857. ListView_OnSetItemState(plv, plv->iFocus, LVIS_SELECTED, LVIS_SELECTED);
  5858. IncrementSearchString(&plv->is, 0, NULL);
  5859. }
  5860. //notify of navigation key usage
  5861. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5862. return;
  5863. }
  5864. // Give caller full string in case they want to do something custom
  5865. if (ListView_IncrementalSearch(plv, iStartFrom, &nmfi, &i))
  5866. return;
  5867. if (iLen > 0 && SameChars(lpsz, lpsz[0]))
  5868. {
  5869. //
  5870. // The user has been typing the same char over and over again.
  5871. // Switch from incremental search to Windows 3.1 style search.
  5872. //
  5873. iStartFrom = plv->iFocus;
  5874. nmfi.lvfi.psz = lpsz + iLen - 1;
  5875. }
  5876. if (i == -1)
  5877. i = ListView_OnFindItem(plv, iStartFrom, &nmfi.lvfi);
  5878. if (!ListView_IsValidItemNumber(plv, i))
  5879. {
  5880. i = -1;
  5881. }
  5882. TraceMsg(TF_LISTVIEW, "listsearch %d %s %d", (LPTSTR)lpsz, (LPTSTR)lpsz, i);
  5883. if (i != -1)
  5884. {
  5885. ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
  5886. plv->iMark = i;
  5887. if (ListView_CancelScrollWait(plv))
  5888. ListView_OnEnsureVisible(plv, i, FALSE);
  5889. }
  5890. else
  5891. {
  5892. // Don't beep on spaces, we use it for selection.
  5893. IncrementSearchBeep(&plv->is);
  5894. }
  5895. //notify of navigation key usage
  5896. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  5897. }
  5898. BOOL SameChars(LPTSTR lpsz, TCHAR c)
  5899. {
  5900. while (*lpsz)
  5901. {
  5902. if (*lpsz++ != c)
  5903. return FALSE;
  5904. }
  5905. return TRUE;
  5906. }
  5907. UINT ListView_OnGetDlgCode(LV* plv, MSG* lpmsg)
  5908. {
  5909. return DLGC_WANTARROWS | DLGC_WANTCHARS;
  5910. }
  5911. int ListView_ComputeCXItemSize(LV* plv)
  5912. {
  5913. int cxItem;
  5914. cxItem = 16 * plv->cxLabelChar + plv->cxSmIcon;
  5915. if (cxItem == 0)
  5916. {
  5917. cxItem = g_cxBorder;
  5918. }
  5919. ASSERT(cxItem != 0);
  5920. return cxItem;
  5921. }
  5922. int ListView_ComputeCYItemSize(LV* plv)
  5923. {
  5924. int cyItem;
  5925. cyItem = max(plv->cyLabelChar, plv->cySmIcon);
  5926. if (plv->himlState)
  5927. {
  5928. cyItem = max(cyItem, plv->cyState);
  5929. }
  5930. cyItem += g_cyBorder;
  5931. ASSERT(cyItem != 0);
  5932. return cyItem;
  5933. }
  5934. void ListView_InvalidateCachedLabelSizes(LV* plv)
  5935. {
  5936. int i;
  5937. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  5938. // Label wrapping has changed, so we need to invalidate the
  5939. // size of the items, such that they will be recomputed.
  5940. //
  5941. if (!ListView_IsOwnerData(plv))
  5942. {
  5943. for (i = ListView_Count(plv) - 1; i >= 0; i--)
  5944. {
  5945. LISTITEM* pitem = ListView_FastGetItemPtr(plv, i);
  5946. ListView_SetSRecompute(pitem);
  5947. }
  5948. }
  5949. plv->rcView.left = RECOMPUTE;
  5950. if ((plv->ci.style & LVS_OWNERDRAWFIXED) && ListView_IsReportView(plv))
  5951. plv->cyItemSave = ListView_ComputeCYItemSize(plv);
  5952. else
  5953. {
  5954. plv->cyItem = ListView_ComputeCYItemSize(plv);
  5955. }
  5956. }
  5957. void ListView_OnStyleChanging(LV* plv, UINT gwl, LPSTYLESTRUCT pinfo)
  5958. {
  5959. if (gwl == GWL_STYLE)
  5960. {
  5961. // Don't allow LVS_OWNERDATA to change after creation
  5962. DWORD stylePreserve = LVS_OWNERDATA;
  5963. // Don't allow a LVS_EX_REGIONAL listview to change type, since
  5964. // it must be LVS_ICON
  5965. // Similarly, HideLabels only works in large icon mode so keep the type.
  5966. if ((plv->exStyle & LVS_EX_REGIONAL) || ListView_HideLabels(plv))
  5967. stylePreserve |= LVS_TYPEMASK;
  5968. // Preserve the bits that must be preserved
  5969. pinfo->styleNew ^= (pinfo->styleNew ^ pinfo->styleOld) & stylePreserve;
  5970. // If we're in group view, then listview must be in autoarrange
  5971. if (plv->fGroupView)
  5972. {
  5973. pinfo->styleNew |= LVS_AUTOARRANGE;
  5974. }
  5975. }
  5976. }
  5977. WORD MapViewStyle(DWORD style)
  5978. {
  5979. if (style == LVS_LIST)
  5980. return LV_VIEW_LIST;
  5981. if (style == LVS_SMALLICON)
  5982. return LV_VIEW_SMALLICON;
  5983. if (style == LVS_REPORT)
  5984. return LV_VIEW_DETAILS;
  5985. return LV_VIEW_ICON;
  5986. }
  5987. void ListView_OnStyleChanged(LV* plv, UINT gwl, LPSTYLESTRUCT pinfo)
  5988. {
  5989. // Style changed: redraw everything...
  5990. //
  5991. // try to do this smartly, avoiding unnecessary redraws
  5992. if (gwl == GWL_STYLE)
  5993. {
  5994. BOOL fRedraw = FALSE, fShouldScroll = FALSE;
  5995. DWORD changeFlags, styleOld;
  5996. ListView_DismissEdit(plv, FALSE);
  5997. changeFlags = plv->ci.style ^ pinfo->styleNew;
  5998. styleOld = plv->ci.style;
  5999. // (dli) Setting the small icon width here and only in the case when we go
  6000. // from large icon view to some other view because of three reasons:
  6001. // 1. According to chee, we want to set this before we change the style bit in
  6002. // plv or after we scale.
  6003. // 2. We don't want to do it after we scale because we want to set the width to
  6004. // the maximum value so that the items in this listview do not cover each other
  6005. // 3. we do it from large icon view because large icon view has fixed width for
  6006. // each item, small icon view width can be scaled.
  6007. //
  6008. if ((changeFlags & LVS_TYPEMASK) && (plv->wView == LV_VIEW_ICON))
  6009. ListView_ISetColumnWidth(plv, 0,
  6010. LV_GetNewColWidth(plv, 0, ListView_Count(plv)-1), FALSE);
  6011. plv->ci.style = pinfo->styleNew; // change our version
  6012. if (changeFlags & (WS_BORDER | WS_CAPTION | WS_THICKFRAME))
  6013. {
  6014. // the changing of these bits affect the size of the window
  6015. // but not until after this message is handled
  6016. // so post ourself a message.
  6017. PostMessage(plv->ci.hwnd, LVMP_WINDOWPOSCHANGED, 0, 0);
  6018. }
  6019. if (changeFlags & LVS_NOCOLUMNHEADER)
  6020. {
  6021. if (plv->hwndHdr)
  6022. {
  6023. SetWindowBits(plv->hwndHdr, GWL_STYLE, HDS_HIDDEN,
  6024. (plv->ci.style & LVS_NOCOLUMNHEADER) ? HDS_HIDDEN : 0);
  6025. fRedraw = TRUE;
  6026. fShouldScroll = TRUE;
  6027. }
  6028. }
  6029. if (changeFlags & LVS_NOLABELWRAP)
  6030. {
  6031. ListView_InvalidateCachedLabelSizes(plv);
  6032. fShouldScroll = TRUE;
  6033. fRedraw = TRUE;
  6034. }
  6035. if (changeFlags & LVS_TYPEMASK)
  6036. {
  6037. WORD wViewOld = plv->wView;
  6038. plv->wView = MapViewStyle(plv->ci.style & LVS_TYPEMASK);
  6039. ListView_TypeChange(plv, wViewOld, (BOOL)BOOLIFY(styleOld & LVS_OWNERDRAWFIXED));
  6040. fShouldScroll = TRUE;
  6041. fRedraw = TRUE;
  6042. }
  6043. if (changeFlags & LVS_AUTOARRANGE)
  6044. {
  6045. if (plv->ci.style & LVS_AUTOARRANGE)
  6046. {
  6047. // Turned on.
  6048. ListView_OnArrange(plv, LVA_DEFAULT);
  6049. fRedraw = TRUE;
  6050. }
  6051. else
  6052. {
  6053. // Turned off. Nuke insertmark, because that's not allowed when
  6054. // auto-arrange is off.
  6055. LVINSERTMARK lvim = {0};
  6056. lvim.cbSize = sizeof(LVINSERTMARK);
  6057. lvim.iItem = -1;
  6058. ListView_OnSetInsertMark(plv, &lvim);
  6059. }
  6060. }
  6061. // previously, this was the else to
  6062. // (changeFlags & LVS_AUTOARRANGE && (plv->ci.style & LVS_AUTOARRANGE))
  6063. // I'm not sure that was really the right thing..
  6064. if (fShouldScroll)
  6065. {
  6066. // Else we would like to make the most important item to still
  6067. // be visible. So first we will look for a cursorered item
  6068. // if this fails, we will look for the first selected item,
  6069. // else we will simply ask for the first item (assuming the
  6070. // count > 0
  6071. //
  6072. int i;
  6073. // And make sure the scrollbars are up to date Note this
  6074. // also updates some variables that some views need
  6075. ListView_UpdateScrollBars(plv);
  6076. i = (plv->iFocus >= 0) ? plv->iFocus : ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  6077. if ((i == -1) && (ListView_Count(plv) > 0))
  6078. i = 0;
  6079. if (i != -1)
  6080. ListView_OnEnsureVisible(plv, i, TRUE);
  6081. }
  6082. if (fRedraw)
  6083. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  6084. }
  6085. else if (gwl == GWL_EXSTYLE)
  6086. {
  6087. //
  6088. // If the RTL_MIRROR extended style bit had changed, let's
  6089. // repaint the control window.
  6090. //
  6091. if ((plv->ci.dwExStyle&RTL_MIRRORED_WINDOW) != (pinfo->styleNew&RTL_MIRRORED_WINDOW))
  6092. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  6093. // Save the new ex-style bits
  6094. plv->ci.dwExStyle = pinfo->styleNew;
  6095. }
  6096. // Change of styles also changes tooltip policy, so pop it
  6097. ListView_PopBubble(plv);
  6098. }
  6099. void ListView_TypeChange(LV* plv, WORD wViewOld, BOOL fOwnerDrawFixed)
  6100. {
  6101. RECT rc;
  6102. int i;
  6103. //
  6104. // Invalidate all cached string metrics because customdraw clients
  6105. // may draw differently depending on the type. This happens more
  6106. // often than you might think, not on purpose, but because apps are
  6107. // buggy.
  6108. //
  6109. if (!ListView_IsOwnerData(plv))
  6110. {
  6111. for (i = 0; i < ListView_Count(plv); i++)
  6112. {
  6113. LISTITEM *pitem = ListView_FastGetItemPtr(plv, i);
  6114. ListView_SetSRecompute(pitem);
  6115. }
  6116. }
  6117. switch (wViewOld)
  6118. {
  6119. case LV_VIEW_DETAILS:
  6120. ShowWindow(plv->hwndHdr, SW_HIDE);
  6121. if (fOwnerDrawFixed)
  6122. {
  6123. // swap cyItem and cyFixed;
  6124. int temp = plv->cyItem;
  6125. plv->cyItem = plv->cyItemSave;
  6126. plv->cyItemSave = temp;
  6127. }
  6128. break;
  6129. default:
  6130. break;
  6131. }
  6132. plv->ptOrigin.x = 0;
  6133. plv->ptOrigin.y = 0;
  6134. plv->ptlRptOrigin.x = 0;
  6135. plv->ptlRptOrigin.y = 0;
  6136. plv->rcView.left = RECOMPUTE;
  6137. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  6138. // Now handle any special setup needed for the new view
  6139. switch (plv->wView)
  6140. {
  6141. case LV_VIEW_LIST:
  6142. // We may need to resize the columns
  6143. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  6144. break;
  6145. case LV_VIEW_DETAILS:
  6146. // if it's owner draw fixed, we may have to do funky stuff
  6147. if (wViewOld != LV_VIEW_DETAILS)
  6148. {
  6149. plv->cyItemSave = plv->cyItem;
  6150. }
  6151. ListView_RInitialize(plv, FALSE);
  6152. break;
  6153. default:
  6154. break;
  6155. }
  6156. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  6157. GetClientRect(plv->ci.hwnd, &rc);
  6158. plv->sizeClient.cx = rc.right;
  6159. plv->sizeClient.cy = rc.bottom;
  6160. }
  6161. int ListView_OnHitTest(LV* plv, LV_HITTESTINFO* pinfo)
  6162. {
  6163. UINT flags;
  6164. int x, y;
  6165. if (!pinfo) return -1;
  6166. x = pinfo->pt.x;
  6167. y = pinfo->pt.y;
  6168. pinfo->iItem = -1;
  6169. flags = 0;
  6170. if (x < 0)
  6171. flags |= LVHT_TOLEFT;
  6172. else if (x >= plv->sizeClient.cx)
  6173. flags |= LVHT_TORIGHT;
  6174. if (y < 0)
  6175. flags |= LVHT_ABOVE;
  6176. else if (y >= plv->sizeClient.cy)
  6177. flags |= LVHT_BELOW;
  6178. if (flags == 0)
  6179. {
  6180. pinfo->iItem = _ListView_ItemHitTest(plv, x, y, &flags, NULL);
  6181. }
  6182. pinfo->flags = flags;
  6183. if (pinfo->iItem >= ListView_Count(plv))
  6184. {
  6185. pinfo->iItem = -1;
  6186. pinfo->flags = LVHT_NOWHERE;
  6187. }
  6188. return pinfo->iItem;
  6189. }
  6190. int ScrollAmount(int large, int iSmall, int unit)
  6191. {
  6192. return (((large - iSmall) + (unit - 1)) / unit) * unit;
  6193. }
  6194. // NOTE: this is duplicated in shell32.dll
  6195. //
  6196. // checks to see if we are at the end position of a scroll bar
  6197. // to avoid scrolling when not needed (avoid flashing)
  6198. //
  6199. // in:
  6200. // code SB_VERT or SB_HORZ
  6201. // bDown FALSE is up or left
  6202. // TRUE is down or right
  6203. BOOL CanScroll(LV* plv, int code, BOOL bDown)
  6204. {
  6205. SCROLLINFO si;
  6206. si.cbSize = sizeof(SCROLLINFO);
  6207. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
  6208. if (ListView_GetScrollInfo(plv, code, &si))
  6209. {
  6210. if (bDown)
  6211. {
  6212. if (si.nPage)
  6213. si.nMax -= (si.nPage - 1);
  6214. return si.nPos < si.nMax;
  6215. }
  6216. else
  6217. {
  6218. return si.nPos > si.nMin;
  6219. }
  6220. }
  6221. else
  6222. {
  6223. return FALSE;
  6224. }
  6225. }
  6226. // detect if we should auto scroll the window
  6227. //
  6228. // in:
  6229. // pt cursor pos in hwnd's client coords
  6230. // out:
  6231. // pdx, pdy ammount scrolled in x and y
  6232. //
  6233. // REVIEW, this should make sure a certain amount of time has passed
  6234. // before scrolling.
  6235. void ScrollDetect(LV* plv, POINT pt, int *pdx, int *pdy)
  6236. {
  6237. int dx, dy;
  6238. *pdx = *pdy = 0;
  6239. if (!(plv->ci.style & (WS_HSCROLL | WS_VSCROLL)))
  6240. return;
  6241. dx = dy = plv->cyIcon / 16;
  6242. if (ListView_IsReportView(plv))
  6243. {
  6244. if (!plv->fGroupView) // Groupview is always in pixels
  6245. dy = plv->cyItem; // we scroll in units of items...
  6246. if (!dx)
  6247. dx = plv->cxSmIcon;
  6248. }
  6249. if (ListView_IsListView(plv))
  6250. dx = plv->cxItem;
  6251. if (!dx)
  6252. dx = 1;
  6253. if (!dy)
  6254. dy = 1;
  6255. // we need to check if we can scroll before acutally doing it
  6256. // since the selection rect is adjusted based on how much
  6257. // we scroll by
  6258. if (plv->ci.style & WS_VSCROLL) // scroll vertically?
  6259. {
  6260. if (pt.y >= plv->sizeClient.cy)
  6261. {
  6262. if (CanScroll(plv, SB_VERT, TRUE))
  6263. *pdy = ScrollAmount(pt.y, plv->sizeClient.cy, dy); // down
  6264. }
  6265. else if (pt.y <= 0)
  6266. {
  6267. if (CanScroll(plv, SB_VERT, FALSE))
  6268. *pdy = -ScrollAmount(0, pt.y, dy); // up
  6269. }
  6270. }
  6271. if (plv->ci.style & WS_HSCROLL) // horizontally
  6272. {
  6273. if (pt.x >= plv->sizeClient.cx)
  6274. {
  6275. if (CanScroll(plv, SB_HORZ, TRUE))
  6276. *pdx = ScrollAmount(pt.x, plv->sizeClient.cx, dx); // right
  6277. }
  6278. else if (pt.x <= 0)
  6279. {
  6280. if (CanScroll(plv, SB_HORZ, FALSE))
  6281. *pdx = -ScrollAmount(0, pt.x, dx); // left
  6282. }
  6283. }
  6284. // REARCHITECT: this will potentially scroll outside the bounds of the
  6285. // listview. we should bound the scroll amount in CanScroll()
  6286. // or ScrollAmount().
  6287. if (*pdx || *pdy)
  6288. {
  6289. ListView_ValidateScrollParams(plv, pdx, pdy);
  6290. }
  6291. }
  6292. #define swap(pi1, pi2) {int i = *(pi1) ; *(pi1) = *(pi2) ; *(pi2) = i ;}
  6293. void OrderRect(RECT *prc)
  6294. {
  6295. if (prc->left > prc->right)
  6296. swap(&prc->left, &prc->right);
  6297. if (prc->bottom < prc->top)
  6298. swap(&prc->bottom, &prc->top);
  6299. }
  6300. // in:
  6301. // x, y starting point in client coords
  6302. #define SCROLL_FREQ (GetDoubleClickTime()/2) // 1/5 of a second between scrolls
  6303. BOOL ShouldScroll(LV* plv, LPPOINT ppt, LPRECT lprc)
  6304. {
  6305. ASSERT(ppt);
  6306. if (plv->ci.style & WS_VSCROLL)
  6307. {
  6308. if (ppt->y >= lprc->bottom)
  6309. {
  6310. if (CanScroll(plv, SB_VERT, TRUE))
  6311. return TRUE;
  6312. }
  6313. else if (ppt->y <= lprc->top)
  6314. {
  6315. if (CanScroll(plv, SB_VERT, FALSE))
  6316. return TRUE;
  6317. }
  6318. }
  6319. if (plv->ci.style & WS_HSCROLL)
  6320. {
  6321. if (ppt->x >= lprc->right)
  6322. {
  6323. if (CanScroll(plv, SB_HORZ, TRUE))
  6324. return TRUE;
  6325. }
  6326. else if (ppt->x <= lprc->left)
  6327. {
  6328. if (CanScroll(plv, SB_HORZ, FALSE))
  6329. return TRUE;
  6330. }
  6331. }
  6332. return FALSE;
  6333. }
  6334. // Listview is "Alpha Capable" if:
  6335. // Colors >= 16bpp (Needed for alpha)
  6336. // The Listview is double buffered (Needed for flicker)
  6337. // The use has "Show window contents while dragging" (Needed to turn off on slow machines)
  6338. // NOTE: g_fDragFullWindows is turned off in comctl32 when running a remote session
  6339. BOOL ListView_IsAlphaMarqueeCapable(LV* plv)
  6340. {
  6341. BOOL fAlphaCapable = FALSE;
  6342. if (ListView_IsDoubleBuffer(plv))
  6343. {
  6344. if (AreAllMonitorsAtLeast(16))
  6345. {
  6346. fAlphaCapable = plv->fListviewAlphaSelect;
  6347. }
  6348. }
  6349. return fAlphaCapable;
  6350. }
  6351. void ListView_DragSelect(LV *plv, int x, int y)
  6352. {
  6353. RECT rc, rcWindow, rcOld, rcUnion, rcTemp2, rcClip;
  6354. POINT pt;
  6355. MSG32 msg32;
  6356. HDC hdc;
  6357. HWND hwnd = plv->ci.hwnd;
  6358. int i, iEnd, dx, dy;
  6359. BOOL bInOld, bInNew = FALSE, bLocked = FALSE;
  6360. DWORD dwTime, dwNewTime;
  6361. HRGN hrgnUpdate = NULL, hrgnLV = NULL;
  6362. BOOL fAlphaMarquee = ListView_IsAlphaMarqueeCapable(plv);
  6363. rc.left = rc.right = x;
  6364. rc.top = rc.bottom = y;
  6365. rcOld = rc;
  6366. UpdateWindow(plv->ci.hwnd);
  6367. if (plv->exStyle & LVS_EX_REGIONAL)
  6368. {
  6369. if ((hrgnUpdate = CreateRectRgn(0,0,0,0)) &&
  6370. (hrgnLV = CreateRectRgn(0,0,0,0)) &&
  6371. (LockWindowUpdate(GetParent(hwnd))))
  6372. {
  6373. hdc = GetDCEx(hwnd, NULL, DCX_PARENTCLIP | DCX_LOCKWINDOWUPDATE);
  6374. bLocked = TRUE;
  6375. }
  6376. else
  6377. {
  6378. goto BailOut;
  6379. }
  6380. }
  6381. else
  6382. {
  6383. hdc = GetDC(hwnd);
  6384. }
  6385. SetCapture(hwnd);
  6386. if (fAlphaMarquee)
  6387. {
  6388. plv->flags |= LVF_MARQUEE;
  6389. plv->rcMarquee = rc;
  6390. InvalidateRect(plv->ci.hwnd, &plv->rcMarquee, TRUE);
  6391. }
  6392. else
  6393. {
  6394. DrawFocusRect(hdc, &rc);
  6395. }
  6396. GetClientRect(hwnd, &rcClip);
  6397. GetWindowRect(hwnd, &rcWindow);
  6398. dwTime = GetTickCount();
  6399. for (;;)
  6400. {
  6401. // WM_CANCELMODE messages will unset the capture, in that
  6402. // case I want to exit this loop
  6403. if (GetCapture() != hwnd)
  6404. {
  6405. break;
  6406. }
  6407. if (!PeekMessage32(&msg32, NULL, 0, 0, PM_REMOVE, TRUE))
  6408. {
  6409. // if the cursor is outside of the window rect
  6410. // we need to generate messages to make autoscrolling
  6411. // keep going
  6412. if (!PtInRect(&rcWindow, msg32.pt) &&
  6413. ShouldScroll(plv, &msg32.pt, &rcWindow))
  6414. {
  6415. SetCursorPos(msg32.pt.x, msg32.pt.y);
  6416. }
  6417. else
  6418. {
  6419. WaitMessage();
  6420. }
  6421. continue;
  6422. }
  6423. // See if the application wants to process the message...
  6424. if (CallMsgFilter32(&msg32, MSGF_COMMCTRL_DRAGSELECT, TRUE) != 0)
  6425. continue;
  6426. switch (msg32.message)
  6427. {
  6428. case WM_LBUTTONUP:
  6429. case WM_RBUTTONUP:
  6430. case WM_LBUTTONDOWN:
  6431. case WM_MBUTTONDOWN:
  6432. case WM_MBUTTONUP:
  6433. case WM_RBUTTONDOWN:
  6434. CCReleaseCapture(&plv->ci);
  6435. goto EndOfLoop;
  6436. case WM_TIMER:
  6437. if (msg32.wParam != IDT_MARQUEE)
  6438. goto DoDefault;
  6439. // else fall through
  6440. case WM_MOUSEMOVE:
  6441. {
  6442. int dMax = -1;
  6443. pt = msg32.pt;
  6444. ScreenToClient(hwnd, &pt);
  6445. dwNewTime = GetTickCount();
  6446. // if (1 || (dwNewTime - dwTime) > SCROLL_FREQ)
  6447. // {
  6448. dwTime = dwNewTime; // reset scroll timer
  6449. ScrollDetect(plv, pt, &dx, &dy);
  6450. // }
  6451. // else
  6452. // {
  6453. // dx = dy = 0;
  6454. // }
  6455. //SetTimer(plv->ci.hwnd, IDT_MARQUEE, SCROLL_FREQ, NULL);
  6456. y -= dy; // scroll up/down
  6457. x -= dx; // scroll left/right
  6458. rc.left = x;
  6459. rc.top = y;
  6460. rc.right = pt.x;
  6461. rc.bottom = pt.y;
  6462. // clip drag rect to the window
  6463. //
  6464. if (rc.right > rcClip.right)
  6465. rc.right = rcClip.right;
  6466. if (rc.right < rcClip.left)
  6467. rc.right = rcClip.left;
  6468. if (rc.bottom > rcClip.bottom)
  6469. rc.bottom = rcClip.bottom;
  6470. if (rc.bottom < rcClip.top)
  6471. rc.bottom = rcClip.top;
  6472. OrderRect(&rc);
  6473. if (EqualRect(&rc, &rcOld))
  6474. break;
  6475. // move the old rect
  6476. if (!fAlphaMarquee)
  6477. {
  6478. DrawFocusRect(hdc, &rcOld); // erase old
  6479. }
  6480. if (dx || dy)
  6481. ListView_OnScroll(plv, dx, dy);
  6482. OffsetRect(&rcOld, -dx, -dy);
  6483. //
  6484. // For Report and List view, we can speed things up by
  6485. // only searching through those items that are visible. We
  6486. // use the hittest to calculate the first item to paint.
  6487. // REARCHITECT:: We are using state specific info here...
  6488. //
  6489. UnionRect(&rcUnion, &rc, &rcOld);
  6490. if (ListView_IsReportView(plv) && !plv->fGroupView)
  6491. {
  6492. i = (int)((plv->ptlRptOrigin.y + rcUnion.top - plv->yTop)
  6493. / plv->cyItem);
  6494. iEnd = (int)((plv->ptlRptOrigin.y + rcUnion.bottom - plv->yTop)
  6495. / plv->cyItem) + 1;
  6496. }
  6497. else if (ListView_IsListView(plv))
  6498. {
  6499. i = ((plv->xOrigin + rcUnion.left)/ plv->cxItem)
  6500. * plv->cItemCol + rcUnion.top / plv->cyItem;
  6501. iEnd = ((plv->xOrigin + rcUnion.right)/ plv->cxItem)
  6502. * plv->cItemCol + rcUnion.bottom / plv->cyItem + 1;
  6503. }
  6504. else
  6505. {
  6506. if (ListView_IsOwnerData(plv))
  6507. {
  6508. ListView_CalcMinMaxIndex(plv, &rcUnion, &i, &iEnd);
  6509. }
  6510. else
  6511. {
  6512. i = 0;
  6513. iEnd = ListView_Count(plv);
  6514. }
  6515. }
  6516. // make sure our endpoint is in range.
  6517. if (iEnd > ListView_Count(plv))
  6518. iEnd = ListView_Count(plv);
  6519. if (i < 0)
  6520. i = 0;
  6521. if (ListView_IsOwnerData(plv) && (i < iEnd))
  6522. {
  6523. ListView_NotifyCacheHint(plv, i, iEnd-1);
  6524. }
  6525. if (bInNew && !(msg32.wParam & (MK_CONTROL | MK_SHIFT)))
  6526. {
  6527. plv->iMark = -1;
  6528. }
  6529. for (; i < iEnd; i++)
  6530. {
  6531. RECT dummy;
  6532. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, NULL, &rcTemp2);
  6533. // don't do this infaltion if we're in report&full row mode
  6534. // in that case, just touching is good enough
  6535. if (!(ListView_IsReportView(plv) && ListView_FullRowSelect(plv)))
  6536. {
  6537. int cxInflate = (rcTemp2.right - rcTemp2.left) / 4;
  6538. if (ListView_IsListView(plv))
  6539. {
  6540. cxInflate = min(cxInflate, plv->cxSmIcon);
  6541. }
  6542. InflateRect(&rcTemp2, -cxInflate, -(rcTemp2.bottom - rcTemp2.top) / 4);
  6543. }
  6544. bInOld = (IntersectRect(&dummy, &rcOld, &rcTemp2) != 0);
  6545. bInNew = (IntersectRect(&dummy, &rc, &rcTemp2) != 0);
  6546. if (msg32.wParam & MK_CONTROL)
  6547. {
  6548. if (bInOld != bInNew)
  6549. {
  6550. ListView_ToggleSelection(plv, i);
  6551. }
  6552. }
  6553. else
  6554. {
  6555. // was there a change?
  6556. if (bInOld != bInNew)
  6557. {
  6558. ListView_OnSetItemState(plv, i, bInOld ? 0 : LVIS_SELECTED, LVIS_SELECTED);
  6559. }
  6560. // if no alternate keys are down.. set the mark to
  6561. // the item furthest from the cursor
  6562. if (bInNew && !(msg32.wParam & (MK_CONTROL | MK_SHIFT)))
  6563. {
  6564. int dItem;
  6565. dItem = (rcTemp2.left - pt.x) * (rcTemp2.left - pt.x) +
  6566. (rcTemp2.top - pt.y) * (rcTemp2.top - pt.y);
  6567. // if it's further away, set this as the mark
  6568. //DebugMsg(TF_LISTVIEW, "dItem = %d, dMax = %d", dItem, dMax);
  6569. if (dItem > dMax)
  6570. {
  6571. //DebugMsg(TF_LISTVIEW, "taking dItem .. iMark = %d", i);
  6572. dMax = dItem;
  6573. plv->iMark = i;
  6574. }
  6575. }
  6576. }
  6577. }
  6578. if (fAlphaMarquee)
  6579. {
  6580. RECT rcInvalid;
  6581. UnionRect(&rcInvalid, &rcOld, &rc);
  6582. InflateRect(&rcInvalid, 1, 1);
  6583. plv->flags |= LVF_MARQUEE;
  6584. plv->rcMarquee = rc;
  6585. InvalidateRect(plv->ci.hwnd, &rcInvalid, TRUE);
  6586. }
  6587. //DebugMsg(TF_LISTVIEW, "Final iMark = %d", plv->iMark);
  6588. if (bLocked)
  6589. {
  6590. if (GetUpdateRgn(plv->ci.hwnd, hrgnUpdate, FALSE) > NULLREGION)
  6591. {
  6592. ValidateRect(plv->ci.hwnd, NULL);
  6593. GetWindowRgn(plv->ci.hwnd, hrgnLV);
  6594. CombineRgn(hrgnUpdate, hrgnUpdate, hrgnLV, RGN_AND);
  6595. SelectClipRgn(hdc, hrgnUpdate);
  6596. SendMessage(plv->ci.hwnd, WM_PRINTCLIENT, (WPARAM)hdc, 0);
  6597. SelectClipRgn(hdc, NULL);
  6598. }
  6599. }
  6600. else
  6601. {
  6602. UpdateWindow(plv->ci.hwnd); // make selection draw
  6603. }
  6604. if (!fAlphaMarquee)
  6605. {
  6606. DrawFocusRect(hdc, &rc);
  6607. }
  6608. rcOld = rc;
  6609. break;
  6610. }
  6611. case WM_KEYDOWN:
  6612. switch (msg32.wParam)
  6613. {
  6614. case VK_ESCAPE:
  6615. ListView_DeselectAll(plv, -1);
  6616. goto EndOfLoop;
  6617. }
  6618. case WM_CHAR:
  6619. case WM_KEYUP:
  6620. // don't process thay keyboard stuff during marquee
  6621. break;
  6622. default:
  6623. // don't process mouse wheel stuff
  6624. if (msg32.message == g_msgMSWheel)
  6625. break;
  6626. DoDefault:
  6627. TranslateMessage32(&msg32, TRUE);
  6628. DispatchMessage32(&msg32, TRUE);
  6629. }
  6630. }
  6631. EndOfLoop:
  6632. plv->flags &= ~LVF_MARQUEE;
  6633. if (fAlphaMarquee)
  6634. {
  6635. InvalidateRect(plv->ci.hwnd, &rcOld, TRUE);
  6636. }
  6637. else
  6638. {
  6639. DrawFocusRect(hdc, &rcOld); // erase old
  6640. }
  6641. ReleaseDC(hwnd, hdc);
  6642. BailOut:
  6643. if (hrgnUpdate)
  6644. DeleteObject(hrgnUpdate);
  6645. if (hrgnLV)
  6646. DeleteObject(hrgnLV);
  6647. if (bLocked)
  6648. LockWindowUpdate(NULL);
  6649. }
  6650. #define SHIFT_DOWN(keyFlags) (keyFlags & MK_SHIFT)
  6651. #define CONTROL_DOWN(keyFlags) (keyFlags & MK_CONTROL)
  6652. #define RIGHTBUTTON(keyFlags) (keyFlags & MK_RBUTTON)
  6653. void ListView_ButtonSelect(LV* plv, int iItem, UINT keyFlags, BOOL bSelected)
  6654. {
  6655. if (SHIFT_DOWN(keyFlags))
  6656. {
  6657. ListView_SelectRangeTo(plv, iItem, !CONTROL_DOWN(keyFlags));
  6658. ListView_SetFocusSel(plv, iItem, TRUE, FALSE, FALSE);
  6659. }
  6660. else if (!CONTROL_DOWN(keyFlags))
  6661. {
  6662. ListView_SetFocusSel(plv, iItem, TRUE, !bSelected, FALSE);
  6663. if (!RIGHTBUTTON(keyFlags) && bSelected && ListView_IsSimpleSelect(plv))
  6664. {
  6665. ListView_HandleStateIconClick(plv, iItem);
  6666. }
  6667. }
  6668. }
  6669. void ListView_HandleStateIconClick(LV* plv, int iItem)
  6670. {
  6671. int iState =
  6672. ListView_OnGetItemState(plv, iItem, LVIS_STATEIMAGEMASK);
  6673. iState = STATEIMAGEMASKTOINDEX(iState) -1;
  6674. iState++;
  6675. iState %= ImageList_GetImageCount(plv->himlState);
  6676. iState++;
  6677. ListView_OnSetItemState(plv, iItem, INDEXTOSTATEIMAGEMASK(iState), LVIS_STATEIMAGEMASK);
  6678. }
  6679. BOOL ListView_RBeginMarquee(LV* plv, int x, int y, LPLVHITTESTINFO plvhti)
  6680. {
  6681. if (ListView_FullRowSelect(plv) &&
  6682. ListView_IsReportView(plv) &&
  6683. !(plv->ci.style & LVS_SINGLESEL) &&
  6684. !ListView_OwnerDraw(plv) &&
  6685. plvhti->iSubItem == 0)
  6686. {
  6687. // can only begin marquee in column 0.
  6688. if (plvhti->flags == LVHT_ONITEM)
  6689. {
  6690. return TRUE;
  6691. }
  6692. }
  6693. return FALSE;
  6694. }
  6695. void ListView_HandleMouse(LV* plv, BOOL fDoubleClick, int x, int y, UINT keyFlags, BOOL bMouseWheel)
  6696. {
  6697. LV_HITTESTINFO ht;
  6698. NMITEMACTIVATE nm;
  6699. int iItem, click, drag;
  6700. BOOL bSelected, fHadFocus, fNotifyReturn = FALSE;
  6701. BOOL fActive;
  6702. HWND hwnd = plv->ci.hwnd;
  6703. if (plv->fButtonDown)
  6704. return;
  6705. plv->fButtonDown = TRUE;
  6706. if (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickHappened && plv->fOneClickOK)
  6707. {
  6708. KillTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED);
  6709. plv->fOneClickHappened = FALSE;
  6710. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  6711. if (!IsWindow(hwnd))
  6712. return;
  6713. }
  6714. fHadFocus = (GetFocus() == plv->ci.hwnd);
  6715. click = RIGHTBUTTON(keyFlags) ? NM_RCLICK : NM_CLICK;
  6716. drag = RIGHTBUTTON(keyFlags) ? LVN_BEGINRDRAG : LVN_BEGINDRAG;
  6717. fActive = ChildOfActiveWindow(plv->ci.hwnd) || fShouldFirstClickActivate() ||
  6718. ChildOfDesktop(plv->ci.hwnd);
  6719. TraceMsg(TF_LISTVIEW, "ListView_OnButtonDown %d", fDoubleClick);
  6720. SetCapture(plv->ci.hwnd);
  6721. plv->ptCapture.x = x;
  6722. plv->ptCapture.y = y;
  6723. if (!ListView_DismissEdit(plv, FALSE) && GetCapture() != plv->ci.hwnd)
  6724. goto EndButtonDown;
  6725. CCReleaseCapture(&plv->ci);
  6726. // REVIEW: right button implies no shift or control stuff
  6727. // Single selection style also implies no modifiers
  6728. //if (RIGHTBUTTON(keyFlags) || (plv->ci.style & LVS_SINGLESEL))
  6729. if ((plv->ci.style & LVS_SINGLESEL))
  6730. keyFlags &= ~(MK_SHIFT | MK_CONTROL);
  6731. ht.pt.x = x;
  6732. ht.pt.y = y;
  6733. iItem = ListView_OnSubItemHitTest(plv, &ht);
  6734. if (ht.iSubItem != 0)
  6735. {
  6736. // if we're not in full row select,
  6737. // hitting on a subitem is like hitting on nowhere
  6738. // also, in win95, ownerdraw fixed effectively had full row select
  6739. if (!ListView_FullRowSelect(plv) &&
  6740. !(plv->ci.style & LVS_OWNERDRAWFIXED))
  6741. {
  6742. iItem = -1;
  6743. ht.flags = LVHT_NOWHERE;
  6744. }
  6745. }
  6746. nm.iItem = iItem;
  6747. nm.iSubItem = ht.iSubItem;
  6748. nm.uChanged = 0;
  6749. nm.ptAction.x = x;
  6750. nm.ptAction.y = y;
  6751. nm.uKeyFlags = GetLVKeyFlags();
  6752. // FProt Profesional assumed that if the notification structure pointer + 14h bytes
  6753. // had a value 2 that it was a displayinfo structure and they then used offset +2c as lparam...
  6754. nm.uNewState = 0;
  6755. plv->iNoHover = iItem;
  6756. bSelected = (iItem >= 0) && ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  6757. if (fDoubleClick)
  6758. {
  6759. // Cancel any name editing that might happen.
  6760. ListView_CancelPendingEdit(plv);
  6761. KillTimer(plv->ci.hwnd, IDT_SCROLLWAIT);
  6762. if (ht.flags & LVHT_NOWHERE)
  6763. {
  6764. // this would have been done in the first click in win95 except
  6765. // now we blow off the first click on focus change
  6766. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags))
  6767. ListView_DeselectAll(plv, -1);
  6768. }
  6769. click = RIGHTBUTTON(keyFlags) ? NM_RDBLCLK : NM_DBLCLK ;
  6770. if (CCSendNotify(&plv->ci, click, &nm.hdr))
  6771. goto EndButtonDown;
  6772. /// some (comdlg32 for example) destroy on double click
  6773. // we need to bail if that happens because plv is no longer valid
  6774. if (!IsWindow(hwnd))
  6775. return;
  6776. if (click == NM_DBLCLK)
  6777. {
  6778. // these shift control flags are to mirror when we don't send out the activate on the single click,
  6779. // but are in the oneclick activate mode (see below)
  6780. if (ht.flags & (LVHT_ONITEMLABEL | LVHT_ONITEMICON))
  6781. {
  6782. // possible scenarios below:
  6783. // 1) we're using classic windows style so double click => launch
  6784. // 2) we're using single click activate
  6785. // a) shift is down and item is selected => launch
  6786. // this implies that the first click selected it
  6787. // b) control is down => launch
  6788. // the first click toggled the selection so if the item was
  6789. // the only item selected and we double clicked on it
  6790. // the first click deselects it and no item is selected
  6791. // so nothing will be launched - this is win95 behavior
  6792. if (!(plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK) ||
  6793. (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK &&
  6794. (SHIFT_DOWN(keyFlags) || CONTROL_DOWN(keyFlags))))
  6795. {
  6796. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &nm.hdr);
  6797. }
  6798. }
  6799. // Double-click on checkbox state icon cycles it just like single click
  6800. else if ((ht.flags & LVHT_ONITEMSTATEICON) && ListView_CheckBoxes(plv))
  6801. {
  6802. ListView_HandleStateIconClick(plv, iItem);
  6803. }
  6804. }
  6805. if (!IsWindow(hwnd))
  6806. return;
  6807. goto EndButtonDown;
  6808. }
  6809. if (ht.flags & (LVHT_ONITEMLABEL | LVHT_ONITEMICON))
  6810. {
  6811. // if it wasn't selected, we're about to select it... play
  6812. // a little ditty for us...
  6813. CCPlaySound(c_szSelect);
  6814. if (!RIGHTBUTTON(keyFlags) || (!CONTROL_DOWN(keyFlags) && !SHIFT_DOWN(keyFlags)))
  6815. ListView_ButtonSelect(plv, iItem, keyFlags, bSelected);
  6816. // handle full row select
  6817. // If single-select listview, disable marquee selection.
  6818. //
  6819. // Careful - CheckForDragBegin yields and the app may have
  6820. // destroyed the item we were thinking about dragging!
  6821. //
  6822. if (!bMouseWheel && CheckForDragBegin(plv->ci.hwnd, x, y))
  6823. {
  6824. // should we do a marquee?
  6825. if (ListView_RBeginMarquee(plv, x, y, &ht) &&
  6826. !CCSendNotify(&plv->ci, LVN_MARQUEEBEGIN, &nm.hdr))
  6827. {
  6828. ListView_DragSelect(plv, x, y);
  6829. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6830. }
  6831. else
  6832. {
  6833. // Before we start dragging, make it sure that it is
  6834. // selected and has the focus.
  6835. ListView_SetFocusSel(plv, iItem, TRUE, FALSE, FALSE);
  6836. if (!SHIFT_DOWN(keyFlags))
  6837. plv->iMark = iItem;
  6838. // Then, we need to update the window before start dragging
  6839. // to show the selection chagne.
  6840. UpdateWindow(plv->ci.hwnd);
  6841. // Remember which item we're dragging, as it affects ListView_OnInsertMarkHitTest
  6842. plv->iDrag = iItem;
  6843. CCSendNotify(&plv->ci, drag, &nm.hdr);
  6844. plv->iDrag = -1;
  6845. goto EndButtonDown;
  6846. }
  6847. }
  6848. // CheckForDragBegin yields, so revalidate before continuing
  6849. else if (IsWindow(hwnd))
  6850. {
  6851. // button came up and we are not dragging
  6852. if (!RIGHTBUTTON(keyFlags))
  6853. {
  6854. if (CONTROL_DOWN(keyFlags))
  6855. {
  6856. // do this on the button up so that ctrl-dragging a range
  6857. // won't toggle the select.
  6858. if (SHIFT_DOWN(keyFlags))
  6859. ListView_SetFocusSel(plv, iItem, FALSE, FALSE, FALSE);
  6860. else
  6861. {
  6862. ListView_SetFocusSel(plv, iItem, TRUE, FALSE, TRUE);
  6863. }
  6864. }
  6865. }
  6866. if (!SHIFT_DOWN(keyFlags))
  6867. plv->iMark = iItem;
  6868. if (!ListView_SetFocus(plv->ci.hwnd)) // activate this window
  6869. return;
  6870. // now do the deselect stuff
  6871. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags) && !RIGHTBUTTON(keyFlags))
  6872. {
  6873. ListView_DeselectAll(plv, iItem);
  6874. if ((ht.flags & LVHT_ONITEMLABEL) && bSelected &&
  6875. !(plv->exStyle & (LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE)))
  6876. {
  6877. // doing this check for ownerdrawfixed is for compatability.
  6878. // we don't want to go into edit mode if the user just happened to click
  6879. // to this window when a different one had focus,
  6880. // but ms hammer relied upon the notification being sent (and we
  6881. // don't go into edit mode anyways for ownerdraw)
  6882. if (fHadFocus ||
  6883. (plv->ci.style & LVS_OWNERDRAWFIXED))
  6884. {
  6885. // Click on item label. It was selected and
  6886. // no modifier keys were pressed and no drag operation
  6887. // So setup for name edit mode. Still need to wait
  6888. // to make sure user is not doing double click.
  6889. //
  6890. ListView_SetupPendingNameEdit(plv);
  6891. }
  6892. }
  6893. }
  6894. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6895. if (!IsWindow(hwnd))
  6896. return;
  6897. if (plv->exStyle & (LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE))
  6898. {
  6899. if (!RIGHTBUTTON(keyFlags))
  6900. {
  6901. // We don't ItemActivate within one double-click time of creating
  6902. // this listview. This is a common occurence for people used to
  6903. // double-clicking. The first click pops up a new window which
  6904. // receives the second click and ItemActivates the item...
  6905. //
  6906. if ((plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK) || bSelected)
  6907. {
  6908. if (fActive)
  6909. {
  6910. // condition: if we're in a single click activate mode
  6911. // don't launch if control or shift keys are pressed
  6912. BOOL bCond = plv->exStyle & LVS_EX_ONECLICKACTIVATE && !CONTROL_DOWN(keyFlags) && !SHIFT_DOWN(keyFlags);
  6913. if ((bSelected && plv->exStyle & LVS_EX_TWOCLICKACTIVATE) ||
  6914. (bCond && !g_bUseDblClickTimer))
  6915. {
  6916. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &nm.hdr);
  6917. if (!IsWindow(hwnd))
  6918. return;
  6919. }
  6920. else if (bCond && g_bUseDblClickTimer)
  6921. {
  6922. plv->fOneClickHappened = TRUE;
  6923. plv->nmOneClickHappened = nm;
  6924. SetTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED, GetDoubleClickTime(), NULL);
  6925. }
  6926. }
  6927. }
  6928. }
  6929. }
  6930. }
  6931. else
  6932. {
  6933. // IsWindow() failed. Bail.
  6934. return;
  6935. }
  6936. }
  6937. else if (ht.flags & LVHT_ONITEMSTATEICON)
  6938. {
  6939. // Should activate window and send notificiation to parent...
  6940. if (!ListView_SetFocus(plv->ci.hwnd)) // activate this window
  6941. return;
  6942. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6943. if (fNotifyReturn && ListView_CheckBoxes(plv))
  6944. {
  6945. ListView_HandleStateIconClick(plv, iItem);
  6946. }
  6947. }
  6948. else if (ht.flags & LVHT_NOWHERE)
  6949. {
  6950. if (!ListView_SetFocus(plv->ci.hwnd)) // activate this window
  6951. return;
  6952. // If single-select listview, disable marquee selection.
  6953. if (!(plv->ci.style & LVS_SINGLESEL) && CheckForDragBegin(plv->ci.hwnd, x, y) &&
  6954. !CCSendNotify(&plv->ci, LVN_MARQUEEBEGIN, &nm.hdr))
  6955. {
  6956. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags))
  6957. ListView_DeselectAll(plv, -1);
  6958. ListView_DragSelect(plv, x, y);
  6959. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6960. }
  6961. else if (IsWindow(hwnd))
  6962. {
  6963. // if we didn't have focus and aren't showing selection always,
  6964. // make the first click just set focus
  6965. BOOL fDoFirstClickSelection = (fHadFocus || plv->ci.style & LVS_SHOWSELALWAYS ||
  6966. CONTROL_DOWN(keyFlags) || SHIFT_DOWN(keyFlags) ||
  6967. RIGHTBUTTON(keyFlags));
  6968. if (fDoFirstClickSelection && fActive)
  6969. {
  6970. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags))
  6971. ListView_DeselectAll(plv, -1);
  6972. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  6973. }
  6974. }
  6975. else
  6976. {
  6977. // IsWindow() failed. Bail.
  6978. return;
  6979. }
  6980. }
  6981. // re-check the key state so we don't get confused by multiple clicks
  6982. // this needs to check the GetKeyState stuff only when we've gone into
  6983. // a modal loop waiting for the rbutton up.
  6984. if (fNotifyReturn && (click == NM_RCLICK)) // && (GetKeyState(VK_RBUTTON)>=0))
  6985. {
  6986. POINT pt = { x, y };
  6987. ClientToScreen(plv->ci.hwnd, &pt);
  6988. FORWARD_WM_CONTEXTMENU(plv->ci.hwnd, plv->ci.hwnd, pt.x, pt.y, SendMessage);
  6989. }
  6990. EndButtonDown:
  6991. if (IsWindow(hwnd))
  6992. plv->fButtonDown = FALSE;
  6993. }
  6994. void ListView_OnButtonDown(LV* plv, BOOL fDoubleClick, int x, int y, UINT keyFlags)
  6995. {
  6996. ListView_HandleMouse(plv, fDoubleClick, x, y, keyFlags, FALSE);
  6997. }
  6998. BOOL ListView_CancelPendingTimer(LV* plv, UINT fFlags, int idTimer)
  6999. {
  7000. if (plv->flags & fFlags)
  7001. {
  7002. KillTimer(plv->ci.hwnd, idTimer);
  7003. plv->flags &= ~fFlags;
  7004. return TRUE;
  7005. }
  7006. return FALSE;
  7007. }
  7008. //
  7009. // ListView_OnTimer:
  7010. // process the WM_TIMER message. If the timer id is thta
  7011. // of the name editing, we should then start the name editing mode.
  7012. //
  7013. void ListView_OnTimer(LV* plv, UINT id)
  7014. {
  7015. KillTimer(plv->ci.hwnd, id);
  7016. if (id == IDT_NAMEEDIT)
  7017. {
  7018. // Kill the timer as we wont need any more messages from it.
  7019. if (ListView_CancelPendingEdit(plv))
  7020. {
  7021. // And start name editing mode.
  7022. if (!ListView_OnEditLabel(plv, plv->iFocus, NULL))
  7023. {
  7024. ListView_DismissEdit(plv, FALSE);
  7025. ListView_SetFocusSel(plv, plv->iFocus, TRUE, TRUE, FALSE);
  7026. }
  7027. }
  7028. }
  7029. else if (id == IDT_SCROLLWAIT)
  7030. {
  7031. if (ListView_CancelScrollWait(plv))
  7032. {
  7033. ListView_OnEnsureVisible(plv, plv->iFocus, TRUE);
  7034. }
  7035. }
  7036. else if (id == IDT_ONECLICKOK)
  7037. {
  7038. plv->fOneClickOK = TRUE;
  7039. }
  7040. else if (id == IDT_ONECLICKHAPPENED)
  7041. {
  7042. //if (!g_bUseDblClickTimer)
  7043. //{
  7044. //// EnableWindow(plv->ci.hwnd, TRUE);
  7045. // SetWindowBits(plv->ci.hwnd, GWL_STYLE, WS_DISABLED, 0);
  7046. // plv->fOneClickHappened = FALSE;
  7047. //}
  7048. // check the bit just in case they double-clicked
  7049. //else
  7050. if (plv->fOneClickHappened)
  7051. {
  7052. plv->fOneClickHappened = FALSE;
  7053. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  7054. }
  7055. }
  7056. else if (id == IDT_TRACKINGTIP)
  7057. {
  7058. // Display keyboard nav tracking tooltip popups
  7059. if (ListView_IsKbdTipTracking(plv)) // Item requires tracking popup
  7060. {
  7061. // Ensure index is still valid
  7062. if (ListView_IsValidItemNumber(plv, plv->iTracking))
  7063. {
  7064. TOOLINFO ti = {0};
  7065. ti.cbSize = sizeof(TOOLINFO);
  7066. ti.hwnd = plv->ci.hwnd;
  7067. // Cancel previous
  7068. SendMessage(plv->hwndToolTips, TTM_TRACKACTIVATE, FALSE, (LPARAM)&ti);
  7069. // Switch ListView's tooltip window to "tracking" (manual) mode
  7070. SendMessage(plv->hwndToolTips, TTM_GETTOOLINFO, 0, (LPARAM)&ti);
  7071. ti.uFlags |= TTF_TRACK;
  7072. SendMessage(plv->hwndToolTips, TTM_SETTOOLINFO, 0, (LPARAM)&ti);
  7073. // Activate and establish size
  7074. SendMessage(plv->hwndToolTips, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti);
  7075. }
  7076. else
  7077. {
  7078. // Index was invalid (ListView set of items changed), tip track cancel, no popup
  7079. plv->iTracking = LVKTT_NOTRACK;
  7080. }
  7081. }
  7082. }
  7083. }
  7084. //
  7085. // ListView_SetupPendingNameEdit:
  7086. // Sets up a timer to begin name editing at a delayed time. This
  7087. // will allow the user to double click on the already selected item
  7088. // without going into name editing mode, which is especially important
  7089. // in those views that only show a small icon.
  7090. //
  7091. void ListView_SetupPendingNameEdit(LV* plv)
  7092. {
  7093. SetTimer(plv->ci.hwnd, IDT_NAMEEDIT, GetDoubleClickTime(), NULL);
  7094. plv->flags |= LVF_NMEDITPEND;
  7095. }
  7096. void ListView_OnHVScroll(LV* plv, UINT code, int pos, int sb)
  7097. {
  7098. int iScrollCount = 0;
  7099. SCROLLINFO si;
  7100. si.cbSize = sizeof(SCROLLINFO);
  7101. si.fMask = SIF_TRACKPOS;
  7102. // if we're in 32bits, don't trust the pos since it's only 16bit's worth
  7103. if (ListView_GetScrollInfo(plv, sb, &si))
  7104. pos = (int)si.nTrackPos;
  7105. ListView_DismissEdit(plv, FALSE);
  7106. _ListView_OnScroll(plv, code, pos, sb);
  7107. switch (code)
  7108. {
  7109. case SB_PAGELEFT:
  7110. case SB_PAGERIGHT:
  7111. if (plv->iScrollCount < SMOOTHSCROLLLIMIT)
  7112. plv->iScrollCount += 3;
  7113. break;
  7114. case SB_LINELEFT:
  7115. case SB_LINERIGHT:
  7116. if (plv->iScrollCount < SMOOTHSCROLLLIMIT)
  7117. plv->iScrollCount++;
  7118. break;
  7119. case SB_ENDSCROLL:
  7120. plv->iScrollCount = 0;
  7121. break;
  7122. }
  7123. }
  7124. void ListView_OnVScroll(LV* plv, HWND hwndCtl, UINT code, int pos)
  7125. {
  7126. ListView_OnHVScroll(plv, code, pos, SB_VERT);
  7127. }
  7128. void ListView_OnHScroll(LV* plv, HWND hwndCtl, UINT code, int pos)
  7129. {
  7130. ListView_OnHVScroll(plv, code, pos, SB_HORZ);
  7131. }
  7132. int ListView_ValidateOneScrollParam(LV* plv, int iDirection, int dx)
  7133. {
  7134. SCROLLINFO si;
  7135. si.cbSize = sizeof(SCROLLINFO);
  7136. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  7137. if (!ListView_GetScrollInfo(plv, iDirection, &si))
  7138. return 0;
  7139. if (si.nPage)
  7140. si.nMax -= (si.nPage - 1);
  7141. si.nPos += dx;
  7142. if (si.nPos < si.nMin)
  7143. {
  7144. dx += (int)(si.nMin - si.nPos);
  7145. }
  7146. else if (si.nPos > si.nMax)
  7147. {
  7148. dx -= (int)(si.nPos - si.nMax);
  7149. }
  7150. return dx;
  7151. }
  7152. BOOL ListView_ValidateScrollParams(LV* plv, int * pdx, int *pdy)
  7153. {
  7154. int dx = *pdx;
  7155. int dy = *pdy;
  7156. if (plv->ci.style & LVS_NOSCROLL)
  7157. return FALSE;
  7158. if (ListView_IsListView(plv))
  7159. {
  7160. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  7161. #ifdef COLUMN_VIEW
  7162. if (dx < 0)
  7163. dx = (dx - (plv->cxItem - 1)) / plv->cxItem;
  7164. else
  7165. dx = (dx + (plv->cxItem - 1)) / plv->cxItem;
  7166. if (dy)
  7167. return FALSE;
  7168. #else
  7169. if (dy < 0)
  7170. dy = (dy - (plv->cyItem - 1)) / plv->cyItem;
  7171. else
  7172. dy = (dy + (plv->cyItem - 1)) / plv->cyItem;
  7173. if (dx)
  7174. return FALSE;
  7175. #endif
  7176. }
  7177. else if (ListView_IsReportView(plv))
  7178. {
  7179. //
  7180. // Note: This function expects that dy is in number of lines
  7181. // and we are working with pixels so do a conversion use some
  7182. // rounding up and down to make it right
  7183. if (dy > 0)
  7184. dy = (dy + plv->cyItem/2) / plv->cyItem;
  7185. else
  7186. dy = (dy - plv->cyItem/2) / plv->cyItem;
  7187. }
  7188. if (dy)
  7189. {
  7190. dy = ListView_ValidateOneScrollParam(plv, SB_VERT, dy);
  7191. if (ListView_IsReportView(plv)
  7192. #ifndef COLUMN_VIEW
  7193. || ListView_IsListView(plv)
  7194. #endif
  7195. )
  7196. {
  7197. // convert back to pixels
  7198. dy *= plv->cyItem;
  7199. }
  7200. *pdy = dy;
  7201. }
  7202. if (dx)
  7203. {
  7204. dx = ListView_ValidateOneScrollParam(plv, SB_HORZ, dx);
  7205. #ifdef COLUMN_VIEW
  7206. if (ListView_IsListView(plv))
  7207. {
  7208. dx *= plv->cxItem;
  7209. }
  7210. #endif
  7211. *pdx = dx;
  7212. }
  7213. return TRUE;
  7214. }
  7215. BOOL ListView_SendScrollNotify(LV* plv, BOOL fBegin, int dx, int dy)
  7216. {
  7217. NMLVSCROLL nm;
  7218. nm.dx = dx;
  7219. nm.dy = dy;
  7220. return !CCSendNotify(&plv->ci, fBegin ? LVN_BEGINSCROLL : LVN_ENDSCROLL, &nm.hdr);
  7221. }
  7222. BOOL ListView_OnScrollSelectSmooth(LV* plv, int dx, int dy, UINT uSmooth)
  7223. {
  7224. if (plv->ci.style & LVS_NOSCROLL)
  7225. return FALSE;
  7226. #ifdef DEBUG
  7227. // If we try and scroll an illegal amount then ListView_IScroll2_SmoothScroll
  7228. // will offset ptOrigin incorrectly (it doesn't check min/max range) which then
  7229. // mucks up hit testing and insert marks
  7230. if (ListView_IsIScrollView(plv))
  7231. {
  7232. int dxTmp = dx;
  7233. int dyTmp = dy;
  7234. ASSERT(ListView_ValidateScrollParams(plv, &dxTmp, &dyTmp) &&
  7235. dxTmp == dx && dyTmp == dy);
  7236. }
  7237. #endif
  7238. if (ListView_IsListView(plv))
  7239. {
  7240. // Scale pixel count to column count
  7241. //
  7242. #ifdef COLUMN_VIEW
  7243. if (dx < 0)
  7244. dx -= plv->cxItem - 1;
  7245. else
  7246. dx += plv->cxItem - 1;
  7247. dx = dx / plv->cxItem;
  7248. if (dy)
  7249. return FALSE;
  7250. #else
  7251. if (dy < 0)
  7252. dy -= plv->cyItem - 1;
  7253. else
  7254. dy += plv->cyItem - 1;
  7255. dy = dy / plv->cyItem;
  7256. if (dx)
  7257. return FALSE;
  7258. #endif
  7259. }
  7260. else if (ListView_IsReportView(plv) && !plv->fGroupView)
  7261. {
  7262. //
  7263. // Note: This function expects that dy is in number of lines
  7264. // and we are working with pixels so do a conversion use some
  7265. // rounding up and down to make it right
  7266. if (dy > 0)
  7267. dy = (dy + plv->cyItem/2) / plv->cyItem;
  7268. else
  7269. dy = (dy - plv->cyItem/2) / plv->cyItem;
  7270. }
  7271. ListView_SendScrollNotify(plv, TRUE, dx, dy);
  7272. _ListView_Scroll2(plv, dx, dy, uSmooth);
  7273. ListView_SendScrollNotify(plv, FALSE, dx, dy);
  7274. ListView_UpdateScrollBars(plv);
  7275. return TRUE;
  7276. }
  7277. BOOL ListView_OnScroll(LV* plv, int dx, int dy)
  7278. {
  7279. return ListView_OnScrollSelectSmooth(plv, dx, dy, 0);
  7280. }
  7281. #ifdef DEBUG
  7282. BOOL ListView_ValidatercView(LV* plv, RECT* prcView, BOOL fRecalcDone)
  7283. {
  7284. BOOL fRet = prcView->left != RECOMPUTE ? TRUE : !fRecalcDone;
  7285. // hitting this assert is only valuable if there's a manual repro, which never happens in stress
  7286. #ifdef FULL_DEBUG
  7287. if (!ListView_IsOwnerData(plv) && ListView_IsIScrollView(plv) && !(plv->fGroupView && plv->hdpaGroups) && ListView_RedrawEnabled(plv))
  7288. {
  7289. RECT rcViewTmp;
  7290. fRet = ListView_ICalcViewRect(plv, TRUE, &rcViewTmp);
  7291. if (fRet)
  7292. {
  7293. ASSERT(prcView->left != RECOMPUTE);
  7294. fRet = IsEqualRect(rcViewTmp, *prcView);
  7295. }
  7296. else
  7297. {
  7298. fRet = !fRecalcDone;
  7299. }
  7300. }
  7301. #endif
  7302. return fRet;
  7303. }
  7304. BOOL ListView_ValidateScrollPositions(LV* plv, RECT* prcClient)
  7305. {
  7306. BOOL fRet = TRUE;
  7307. // hitting this assert is only valuable if there's a manual repro, which never happens in stress
  7308. #ifdef FULL_DEBUG
  7309. // if we're in ListView_FixIScrollPositions, then it will fix up the scroll positions when we unwind
  7310. if (ListView_IsIScrollView(plv) && (!plv->fInFixIScrollPositions) && ListView_RedrawEnabled(plv))
  7311. {
  7312. if (!(plv->ci.style & LVS_NOSCROLL))
  7313. {
  7314. // if we don't have a client rect there's no way to validate anything, assume everything will be recomputed later
  7315. RECT rcClient;
  7316. if (!prcClient)
  7317. {
  7318. if (plv->rcView.left != RECOMPUTE)
  7319. {
  7320. ListView_GetStyleAndClientRectGivenViewRect(plv, &plv->rcView, &rcClient);
  7321. prcClient = &rcClient;
  7322. }
  7323. }
  7324. if (prcClient)
  7325. {
  7326. if (fRet)
  7327. {
  7328. if (RECTWIDTH(*prcClient) < RECTWIDTH(plv->rcView))
  7329. {
  7330. fRet = (plv->rcView.left <= plv->ptOrigin.x) && (plv->ptOrigin.x+RECTWIDTH(*prcClient) <= plv->rcView.right);
  7331. }
  7332. else
  7333. {
  7334. fRet = (plv->ptOrigin.x <= plv->rcView.left) && (plv->rcView.right <= plv->ptOrigin.x+RECTWIDTH(*prcClient));
  7335. }
  7336. }
  7337. if (fRet)
  7338. {
  7339. if (RECTHEIGHT(*prcClient) < RECTHEIGHT(plv->rcView))
  7340. {
  7341. fRet = (plv->rcView.top <= plv->ptOrigin.y) && (plv->ptOrigin.y+RECTHEIGHT(*prcClient) <= plv->rcView.bottom);
  7342. }
  7343. else
  7344. {
  7345. fRet = (plv->ptOrigin.y <= plv->rcView.top) && (plv->rcView.bottom <= plv->ptOrigin.y+RECTHEIGHT(*prcClient));
  7346. }
  7347. }
  7348. }
  7349. }
  7350. else
  7351. {
  7352. fRet = (plv->ptOrigin.x == 0) && (plv->ptOrigin.y == 0);
  7353. }
  7354. }
  7355. #endif
  7356. return fRet;
  7357. }
  7358. #endif
  7359. BOOL ListView_OnEnsureVisible(LV* plv, int i, BOOL fPartialOK)
  7360. {
  7361. RECT rcBounds;
  7362. RECT rc;
  7363. RECT rcClient;
  7364. int dx, dy;
  7365. if (!ListView_IsValidItemNumber(plv, i) || plv->ci.style & LVS_NOSCROLL)
  7366. return FALSE;
  7367. // we need to do this again inside because some callers don't do it.
  7368. // other callers that do this need to do it outside so that
  7369. // they can know not to call us if there's not wait pending
  7370. ListView_CancelScrollWait(plv);
  7371. if (ListView_IsReportView(plv))
  7372. return ListView_ROnEnsureVisible(plv, i, fPartialOK);
  7373. ListView_GetRects(plv, i, QUERY_DEFAULT, &rc, NULL, &rcBounds, NULL);
  7374. if (plv->fGroupView)
  7375. {
  7376. LISTITEM* pitem = ListView_GetItemPtr(plv, i);
  7377. if (pitem)
  7378. {
  7379. LISTGROUP* pgrp = ListView_FindFirstVisibleGroup(plv);
  7380. if (pitem->pGroup == pgrp && pgrp)
  7381. {
  7382. rcBounds.top -= LISTGROUP_HEIGHT(plv, pgrp);
  7383. }
  7384. }
  7385. }
  7386. if (!fPartialOK)
  7387. rc = rcBounds;
  7388. // Scrolling is done relative to this calculated rect, not the size of hwndListview (plv->sizeClient)
  7389. ListView_GetClientRect(plv, &rcClient, TRUE, NULL);
  7390. ASSERT(ListView_ValidateScrollPositions(plv, &rcClient));
  7391. // If any part of rc is outside of rcClient, then
  7392. // scroll so that all of rcBounds is visible.
  7393. //
  7394. dx = 0;
  7395. if (rc.left < 0 || (rc.right >= rcClient.right && rcClient.right != 0))
  7396. {
  7397. dx = rcBounds.left - 0;
  7398. if (dx >= 0)
  7399. {
  7400. dx = rcBounds.right - rcClient.right;
  7401. if (dx <= 0)
  7402. dx = 0;
  7403. else if ((rcBounds.left - dx) < 0)
  7404. dx = rcBounds.left - 0; // Not all fits...
  7405. }
  7406. }
  7407. dy = 0;
  7408. if (rc.top < 0 || (rc.bottom >= rcClient.bottom && rcClient.bottom != 0))
  7409. {
  7410. dy = rcBounds.top - 0;
  7411. if (dy >= 0)
  7412. {
  7413. dy = rcBounds.bottom - rcClient.bottom;
  7414. if (dy < 0)
  7415. dy = 0;
  7416. }
  7417. }
  7418. // if rcClient is 0 or 1 pixel in size, it is impossible to scroll it
  7419. if (dx | dy)
  7420. ListView_ValidateScrollParams(plv, &dx, &dy);
  7421. if (dx | dy)
  7422. return ListView_OnScrollSelectSmooth(plv, dx, dy, SSW_EX_IMMEDIATE);
  7423. return TRUE;
  7424. }
  7425. void ListView_UpdateScrollBars(LV* plv)
  7426. {
  7427. RECT rc;
  7428. DWORD dwStyle;
  7429. if ((plv->ci.style & LVS_NOSCROLL) ||
  7430. (!(ListView_RedrawEnabled(plv))))
  7431. return;
  7432. _ListView_UpdateScrollBars(plv);
  7433. GetClientRect(plv->ci.hwnd, &rc);
  7434. plv->sizeClient.cx = rc.right;
  7435. plv->sizeClient.cy = rc.bottom;
  7436. dwStyle = ListView_GetWindowStyle(plv);
  7437. plv->ci.style = (plv->ci.style & ~(WS_HSCROLL | WS_VSCROLL)) | (dwStyle & WS_HSCROLL | WS_VSCROLL);
  7438. }
  7439. void ListView_OnSetFont(LV* plv, HFONT hfont, BOOL fRedraw)
  7440. {
  7441. HDC hdc;
  7442. SIZE siz;
  7443. LOGFONT lf;
  7444. HFONT hfontPrev;
  7445. TEXTMETRIC tm;
  7446. if ((plv->flags & LVF_FONTCREATED) && plv->hfontLabel)
  7447. {
  7448. DeleteObject(plv->hfontLabel);
  7449. plv->flags &= ~LVF_FONTCREATED;
  7450. }
  7451. if (hfont == NULL)
  7452. {
  7453. SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
  7454. hfont = CreateFontIndirect(&lf);
  7455. plv->flags |= LVF_FONTCREATED;
  7456. }
  7457. hdc = GetDC(HWND_DESKTOP);
  7458. if (hdc)
  7459. {
  7460. hfontPrev = SelectFont(hdc, hfont);
  7461. GetTextMetrics(hdc, &tm);
  7462. plv->cyLabelChar = tm.tmHeight;
  7463. plv->cxLabelChar = tm.tmAveCharWidth; // Maybe this should tm.tmMaxCharWidth
  7464. GetTextExtentPoint(hdc, c_szEllipses, CCHELLIPSES, &siz);
  7465. plv->cxEllipses = siz.cx;
  7466. SelectFont(hdc, hfontPrev);
  7467. ReleaseDC(HWND_DESKTOP, hdc);
  7468. }
  7469. plv->hfontLabel = hfont;
  7470. if (plv->hfontLabel)
  7471. {
  7472. LOGFONT lf;
  7473. if (GetObject(plv->hfontLabel, sizeof(LOGFONT), &lf))
  7474. {
  7475. if (plv->hfontGroup)
  7476. DeleteObject(plv->hfontGroup);
  7477. CCAdjustForBold(&lf);
  7478. plv->hfontGroup = CreateFontIndirect(&lf);
  7479. }
  7480. }
  7481. plv->ci.uiCodePage = GetCodePageForFont(hfont);
  7482. ListView_InvalidateCachedLabelSizes(plv);
  7483. /* Ensure that our tooltip control uses the same font as the list view is using, therefore
  7484. / avoiding any nasty formatting problems. */
  7485. if (plv->hwndToolTips)
  7486. {
  7487. FORWARD_WM_SETFONT(plv->hwndToolTips, plv->hfontLabel, FALSE, SendMessage);
  7488. }
  7489. // If we have a header window, we need to forward this to it also
  7490. // as we have destroyed the hfont that they are using...
  7491. if (plv->hwndHdr)
  7492. {
  7493. FORWARD_WM_SETFONT(plv->hwndHdr, plv->hfontLabel, FALSE, SendMessage);
  7494. ListView_UpdateScrollBars(plv);
  7495. }
  7496. if (plv->hFontHot)
  7497. {
  7498. DeleteObject(plv->hFontHot);
  7499. plv->hFontHot = NULL;
  7500. }
  7501. CCGetHotFont(plv->hfontLabel, &plv->hFontHot);
  7502. plv->iFreeSlot = -1;
  7503. if (fRedraw)
  7504. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  7505. }
  7506. HFONT ListView_OnGetFont(LV* plv)
  7507. {
  7508. return plv->hfontLabel;
  7509. }
  7510. // This function process the WM_SETREDRAW message by setting or clearing
  7511. // a bit in the listview structure, which several places in the code will
  7512. // check...
  7513. //
  7514. // REVIEW: Should probably forward to DefWindowProc()
  7515. //
  7516. void ListView_OnSetRedraw(LV* plv, BOOL fRedraw)
  7517. {
  7518. if (fRedraw)
  7519. {
  7520. BOOL fChanges = FALSE;
  7521. // Only do work if we're turning redraw back on...
  7522. //
  7523. if (!(plv->flags & LVF_REDRAW))
  7524. {
  7525. plv->flags |= LVF_REDRAW;
  7526. // deal with any accumulated invalid regions
  7527. if (plv->hrgnInval)
  7528. {
  7529. UINT fRedraw = (plv->flags & LVF_ERASE) ? RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW : RDW_UPDATENOW|RDW_INVALIDATE;
  7530. if (plv->hrgnInval == (HRGN)ENTIRE_REGION)
  7531. plv->hrgnInval = NULL;
  7532. RedrawWindow(plv->ci.hwnd, NULL, plv->hrgnInval, fRedraw);
  7533. ListView_DeleteHrgnInval(plv);
  7534. fChanges = TRUE;
  7535. }
  7536. plv->flags &= ~LVF_ERASE;
  7537. // Turning redraw on recomputes listview.
  7538. if (plv->fGroupView)
  7539. {
  7540. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  7541. }
  7542. if (plv->fGroupView || ListView_IsDoubleBuffer(plv))
  7543. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  7544. // now deal with the optimized stuff
  7545. if (ListView_IsListView(plv) ||
  7546. ListView_IsReportView(plv))
  7547. {
  7548. if (plv->iFirstChangedNoRedraw != -1)
  7549. {
  7550. // We may try to resize the column
  7551. if (!ListView_MaybeResizeListColumns(plv, plv->iFirstChangedNoRedraw,
  7552. ListView_Count(plv)-1))
  7553. ListView_OnUpdate(plv, plv->iFirstChangedNoRedraw);
  7554. }
  7555. else
  7556. ListView_UpdateScrollBars(plv);
  7557. }
  7558. else
  7559. {
  7560. int iCount;
  7561. if (plv->iFirstChangedNoRedraw != -1)
  7562. {
  7563. for (iCount = ListView_Count(plv) ; plv->iFirstChangedNoRedraw < iCount; plv->iFirstChangedNoRedraw++)
  7564. {
  7565. ListView_InvalidateItem(plv, plv->iFirstChangedNoRedraw, FALSE, RDW_INVALIDATE);
  7566. }
  7567. fChanges = TRUE;
  7568. }
  7569. if (fChanges)
  7570. {
  7571. ListView_RecalcRegion(plv, TRUE, TRUE);
  7572. }
  7573. if (((plv->ci.style & LVS_AUTOARRANGE) ||(plv->exStyle & LVS_EX_SNAPTOGRID)) && fChanges)
  7574. {
  7575. ListView_OnUpdate(plv, plv->iFirstChangedNoRedraw);
  7576. }
  7577. else
  7578. {
  7579. ListView_UpdateScrollBars(plv);
  7580. }
  7581. }
  7582. }
  7583. }
  7584. else
  7585. {
  7586. plv->iFirstChangedNoRedraw = -1;
  7587. plv->flags &= ~LVF_REDRAW;
  7588. }
  7589. }
  7590. HIMAGELIST ListView_OnGetImageList(LV* plv, int iImageList)
  7591. {
  7592. switch (iImageList)
  7593. {
  7594. case LVSIL_NORMAL:
  7595. return plv->himl;
  7596. case LVSIL_SMALL:
  7597. return plv->himlSmall;
  7598. case LVSIL_STATE:
  7599. return plv->himlState;
  7600. }
  7601. RIPMSG(0, "ListView_GetImageList: Invalid Imagelist asked for");
  7602. return NULL;
  7603. }
  7604. HIMAGELIST ListView_OnSetImageList(LV* plv, HIMAGELIST himl, int iImageList)
  7605. {
  7606. HIMAGELIST hImageOld = NULL;
  7607. BOOL fImageSizeChanged = FALSE; //Assume the size hasn't changed!
  7608. switch (iImageList)
  7609. {
  7610. case LVSIL_NORMAL:
  7611. hImageOld = plv->himl;
  7612. plv->himl = himl;
  7613. if (himl)
  7614. {
  7615. int cxIconNew, cyIconNew;
  7616. //Get the Icon sizes from the new image list.
  7617. if (CCGetIconSize(&plv->ci, himl, &cxIconNew , &cyIconNew))
  7618. {
  7619. //Check to see if the sizes have changed!
  7620. if((cxIconNew != plv->cxIcon) || (cyIconNew != plv->cyIcon))
  7621. {
  7622. fImageSizeChanged = TRUE;
  7623. plv->cxIcon = cxIconNew;
  7624. plv->cyIcon = cyIconNew;
  7625. }
  7626. }
  7627. if (fImageSizeChanged && (!(plv->flags & LVF_ICONSPACESET)))
  7628. {
  7629. ListView_OnSetIconSpacing(plv, (LPARAM)-1);
  7630. }
  7631. }
  7632. break;
  7633. case LVSIL_SMALL:
  7634. hImageOld = plv->himlSmall;
  7635. plv->himlSmall = himl;
  7636. if (himl)
  7637. {
  7638. int cxSmIconNew, cySmIconNew;
  7639. //Get the small icon sizes from the new image list.
  7640. if(CCGetIconSize(&plv->ci, himl, &cxSmIconNew , &cySmIconNew))
  7641. {
  7642. //Check to see if the sizes have changed!
  7643. if((cxSmIconNew != plv->cxSmIcon) || (cySmIconNew != plv->cySmIcon))
  7644. {
  7645. fImageSizeChanged = TRUE;
  7646. plv->cxSmIcon = cxSmIconNew;
  7647. plv->cySmIcon = cySmIconNew;
  7648. }
  7649. }
  7650. }
  7651. if (fImageSizeChanged)
  7652. {
  7653. plv->cxItem = ListView_ComputeCXItemSize(plv);
  7654. // After changing the imagelist, try to resize the columns, because we can't
  7655. // guess what the new size is going to be. Discovered by Thumbview
  7656. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  7657. plv->cyItem = ListView_ComputeCYItemSize(plv);
  7658. }
  7659. if (plv->hwndHdr)
  7660. SendMessage(plv->hwndHdr, HDM_SETIMAGELIST, 0, (LPARAM)himl);
  7661. break;
  7662. case LVSIL_STATE:
  7663. fImageSizeChanged = TRUE;
  7664. if (himl)
  7665. {
  7666. CCGetIconSize(&plv->ci, himl, &plv->cxState , &plv->cyState);
  7667. }
  7668. else
  7669. {
  7670. plv->cxState = 0;
  7671. }
  7672. hImageOld = plv->himlState;
  7673. plv->himlState = himl;
  7674. plv->cyItem = ListView_ComputeCYItemSize(plv);
  7675. break;
  7676. default:
  7677. fImageSizeChanged = TRUE;
  7678. TraceMsg(TF_LISTVIEW, "sh TR - LVM_SETIMAGELIST: unrecognized iImageList");
  7679. break;
  7680. }
  7681. if (himl && !(plv->ci.style & LVS_SHAREIMAGELISTS))
  7682. ImageList_SetBkColor(himl, plv->clrBk);
  7683. // Imagelist size changed... if we're in tileview, we need to recalculate the tilesize.
  7684. if (ListView_IsTileView(plv) && (iImageList == LVSIL_STATE || iImageList == LVSIL_NORMAL))
  7685. {
  7686. ListView_RecalcTileSize(plv);
  7687. }
  7688. if(fImageSizeChanged)
  7689. {
  7690. // Now, recompute!
  7691. plv->rcView.left = RECOMPUTE; // invalidate this up front to avoid the asserts - it'll get recalculated anyway
  7692. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  7693. }
  7694. if (ListView_Count(plv) > 0)
  7695. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  7696. return hImageOld;
  7697. }
  7698. BOOL ListView_OnGetItemA(LV* plv, LV_ITEMA *plvi)
  7699. {
  7700. LPWSTR pszW = NULL;
  7701. LPSTR pszC = NULL;
  7702. BOOL fRet;
  7703. //HACK ALERT -- this code assumes that LV_ITEMA is exactly the same
  7704. // as LV_ITEMW except for the pointer to the string.
  7705. COMPILETIME_ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW))
  7706. if (!plvi)
  7707. return FALSE;
  7708. if ((plvi->mask & LVIF_TEXT) && (plvi->pszText != NULL))
  7709. {
  7710. pszC = plvi->pszText;
  7711. pszW = LocalAlloc(LMEM_FIXED, plvi->cchTextMax * sizeof(WCHAR));
  7712. if (pszW == NULL)
  7713. return FALSE;
  7714. plvi->pszText = (LPSTR)pszW;
  7715. }
  7716. fRet = ListView_OnGetItem(plv, (LV_ITEM *) plvi);
  7717. if (pszW)
  7718. {
  7719. if (plvi->pszText != LPSTR_TEXTCALLBACKA)
  7720. {
  7721. if (fRet && plvi->cchTextMax)
  7722. ConvertWToAN(plv->ci.uiCodePage, pszC, plvi->cchTextMax, (LPWSTR)plvi->pszText, -1);
  7723. plvi->pszText = pszC;
  7724. }
  7725. LocalFree(pszW);
  7726. }
  7727. return fRet;
  7728. }
  7729. BOOL ListView_OnGetItem(LV* plv, LV_ITEM* plvi)
  7730. {
  7731. UINT mask;
  7732. LISTITEM* pitem = NULL;
  7733. LV_DISPINFO nm;
  7734. if (!plvi)
  7735. {
  7736. RIPMSG(0, "LVM_GET(ITEM|ITEMTEXT): Invalid pitem = NULL");
  7737. return FALSE;
  7738. }
  7739. if (!ListView_IsValidItemNumber(plv, plvi->iItem))
  7740. {
  7741. #ifdef DEBUG
  7742. // owner data views (e.g. docfind) may change the number of items in listview
  7743. // while we are doing something, thus hitting this rip
  7744. if (!ListView_IsOwnerData(plv))
  7745. RIPMSG(0, "LVM_GET(ITEM|ITEMTEXT|ITEMSTATE): item=%d does not exist", plvi->iItem);
  7746. #endif
  7747. return FALSE;
  7748. }
  7749. nm.item.mask = 0;
  7750. mask = plvi->mask;
  7751. if (!ListView_IsOwnerData(plv))
  7752. {
  7753. // Standard listviews
  7754. pitem = ListView_FastGetItemPtr(plv, plvi->iItem);
  7755. ASSERT(pitem);
  7756. // Handle sub-item cases for report view
  7757. //
  7758. if (plvi->iSubItem != 0)
  7759. {
  7760. LISTSUBITEM lsi;
  7761. ListView_GetSubItem(plv, plvi->iItem, plvi->iSubItem, &lsi);
  7762. if (mask & LVIF_TEXT)
  7763. {
  7764. if (lsi.pszText != LPSTR_TEXTCALLBACK)
  7765. {
  7766. Str_GetPtr0(lsi.pszText, plvi->pszText, plvi->cchTextMax);
  7767. }
  7768. else
  7769. {
  7770. // if this is LVIF_NORECOMPUTE we will update pszText later
  7771. nm.item.mask |= LVIF_TEXT;
  7772. }
  7773. }
  7774. if ((mask & LVIF_IMAGE) && (plv->exStyle & LVS_EX_SUBITEMIMAGES))
  7775. {
  7776. plvi->iImage = lsi.iImage;
  7777. if (lsi.iImage == I_IMAGECALLBACK)
  7778. nm.item.mask |= LVIF_IMAGE;
  7779. }
  7780. if (mask & LVIF_STATE)
  7781. {
  7782. if (ListView_FullRowSelect(plv))
  7783. {
  7784. // if we're in full row select,
  7785. // the state bit for select and focus follows column 0.
  7786. lsi.state |= pitem->state & (LVIS_SELECTED | LVIS_FOCUSED | LVIS_DROPHILITED);
  7787. }
  7788. plvi->state = lsi.state & plvi->stateMask;
  7789. if (plv->stateCallbackMask)
  7790. {
  7791. nm.item.stateMask = (plvi->stateMask & plv->stateCallbackMask);
  7792. if (nm.item.stateMask)
  7793. {
  7794. nm.item.mask |= LVIF_STATE;
  7795. nm.item.state = 0;
  7796. }
  7797. }
  7798. }
  7799. }
  7800. else
  7801. {
  7802. if (mask & LVIF_TEXT)
  7803. {
  7804. if (pitem->pszText != LPSTR_TEXTCALLBACK)
  7805. {
  7806. Str_GetPtr0(pitem->pszText, plvi->pszText, plvi->cchTextMax);
  7807. }
  7808. else
  7809. {
  7810. // if this is LVIF_NORECOMPUTE we will update pszText later
  7811. nm.item.mask |= LVIF_TEXT;
  7812. }
  7813. }
  7814. if (mask & LVIF_IMAGE)
  7815. {
  7816. plvi->iImage = pitem->iImage;
  7817. if (pitem->iImage == I_IMAGECALLBACK)
  7818. nm.item.mask |= LVIF_IMAGE;
  7819. }
  7820. if (mask & LVIF_INDENT)
  7821. {
  7822. plvi->iIndent = pitem->iIndent;
  7823. if (pitem->iIndent == I_INDENTCALLBACK)
  7824. nm.item.mask |= LVIF_INDENT;
  7825. }
  7826. if (mask & LVIF_STATE)
  7827. {
  7828. plvi->state = (pitem->state & plvi->stateMask);
  7829. if (plv->stateCallbackMask)
  7830. {
  7831. nm.item.stateMask = (plvi->stateMask & plv->stateCallbackMask);
  7832. if (nm.item.stateMask)
  7833. {
  7834. nm.item.mask |= LVIF_STATE;
  7835. nm.item.state = 0;
  7836. }
  7837. }
  7838. }
  7839. if (mask & LVIF_GROUPID)
  7840. {
  7841. if (LISTITEM_HASGROUP(pitem))
  7842. {
  7843. plvi->iGroupId = pitem->pGroup->iGroupId;
  7844. }
  7845. else
  7846. {
  7847. nm.item.mask |= LVIF_GROUPID;
  7848. }
  7849. }
  7850. if (mask & LVIF_COLUMNS)
  7851. {
  7852. if ((plvi->puColumns == NULL) || (plvi->cColumns > CCMAX_TILE_COLUMNS))
  7853. {
  7854. return FALSE;
  7855. }
  7856. if (pitem->cColumns == I_COLUMNSCALLBACK)
  7857. {
  7858. nm.item.mask |= LVIF_COLUMNS;
  7859. }
  7860. else
  7861. {
  7862. plvi->cColumns = pitem->cColumns;
  7863. if (plvi->cColumns < pitem->cColumns)
  7864. {
  7865. // Not enough room to store the columns
  7866. return FALSE;
  7867. }
  7868. // Copy the array
  7869. if (plvi->puColumns && pitem->puColumns)
  7870. {
  7871. CopyMemory(plvi->puColumns, pitem->puColumns, plvi->cColumns * sizeof(UINT));
  7872. }
  7873. }
  7874. }
  7875. }
  7876. if (mask & LVIF_PARAM)
  7877. {
  7878. plvi->lParam = pitem->lParam;
  7879. }
  7880. }
  7881. else
  7882. {
  7883. // Complete call back for info...
  7884. // Handle sub-item cases for report view
  7885. //
  7886. if (plvi->iSubItem != 0)
  7887. {
  7888. // if there are no subitem images, don't query for them
  7889. if (!(plv->exStyle & LVS_EX_SUBITEMIMAGES))
  7890. mask &= ~LVIF_IMAGE;
  7891. // don't allow indent on the non-0th column
  7892. mask &= ~LVIF_INDENT;
  7893. }
  7894. if (mask & LVIF_PARAM)
  7895. plvi->lParam = 0L; // Dont have any to return now...
  7896. if (mask & LVIF_STATE)
  7897. {
  7898. plvi->state = 0;
  7899. if ((plvi->iSubItem == 0) || ListView_FullRowSelect(plv))
  7900. {
  7901. if (plvi->iItem == plv->iFocus)
  7902. plvi->state |= LVIS_FOCUSED;
  7903. if (plv->plvrangeSel->lpVtbl->IsSelected(plv->plvrangeSel, plvi->iItem) == S_OK)
  7904. plvi->state |= LVIS_SELECTED;
  7905. if (plv->plvrangeCut->lpVtbl->IsSelected(plv->plvrangeCut, plvi->iItem) == S_OK)
  7906. plvi->state |= LVIS_CUT;
  7907. if (plvi->iItem == plv->iDropHilite)
  7908. plvi->state |= LVIS_DROPHILITED;
  7909. plvi->state &= plvi->stateMask;
  7910. }
  7911. if (plv->stateCallbackMask)
  7912. {
  7913. nm.item.stateMask = (plvi->stateMask & plv->stateCallbackMask);
  7914. if (nm.item.stateMask)
  7915. {
  7916. nm.item.mask |= LVIF_STATE;
  7917. nm.item.state = 0;
  7918. }
  7919. }
  7920. }
  7921. if (mask & LVIF_COLUMNS)
  7922. {
  7923. nm.item.mask |= LVIF_COLUMNS;
  7924. }
  7925. nm.item.mask |= (mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_INDENT));
  7926. }
  7927. if (mask & LVIF_NORECOMPUTE)
  7928. {
  7929. if (nm.item.mask & LVIF_TEXT)
  7930. plvi->pszText = LPSTR_TEXTCALLBACK;
  7931. if (nm.item.mask & LVIF_COLUMNS)
  7932. plvi->cColumns = I_COLUMNSCALLBACK;
  7933. }
  7934. else if (nm.item.mask)
  7935. {
  7936. UINT rguColumns[CCMAX_TILE_COLUMNS];
  7937. nm.item.iItem = plvi->iItem;
  7938. nm.item.iSubItem = plvi->iSubItem;
  7939. if (ListView_IsOwnerData(plv))
  7940. nm.item.lParam = 0L;
  7941. else
  7942. nm.item.lParam = pitem->lParam;
  7943. // just in case LVIF_IMAGE is set and callback doesn't fill it in
  7944. // ... we'd rather have a -1 than whatever garbage is on the stack
  7945. nm.item.iImage = -1;
  7946. nm.item.iIndent = 0;
  7947. if (nm.item.mask & LVIF_TEXT)
  7948. {
  7949. RIPMSG(plvi->pszText != NULL, "LVM_GET(ITEM|ITEMTEXT) null string pointer");
  7950. if (plvi->pszText)
  7951. {
  7952. nm.item.pszText = plvi->pszText;
  7953. nm.item.cchTextMax = plvi->cchTextMax;
  7954. // Make sure the buffer is zero terminated...
  7955. if (nm.item.cchTextMax)
  7956. *nm.item.pszText = 0;
  7957. }
  7958. else
  7959. {
  7960. // Don't make caller smash null pointer
  7961. nm.item.mask &= ~LVIF_TEXT;
  7962. }
  7963. }
  7964. if (nm.item.mask & LVIF_COLUMNS)
  7965. {
  7966. nm.item.cColumns = plvi->cColumns;
  7967. nm.item.puColumns = rguColumns;
  7968. if (plvi->puColumns && plvi->cColumns && plvi->cColumns < ARRAYSIZE(rguColumns))
  7969. {
  7970. CopyMemory(rguColumns, plvi->puColumns, sizeof(UINT) * plvi->cColumns);
  7971. }
  7972. }
  7973. CCSendNotify(&plv->ci, LVN_GETDISPINFO, &nm.hdr);
  7974. // use nm.item.mask to give the app a chance to change values
  7975. if (nm.item.mask & LVIF_INDENT)
  7976. plvi->iIndent = nm.item.iIndent;
  7977. if (nm.item.mask & LVIF_GROUPID)
  7978. {
  7979. if (pitem)
  7980. {
  7981. if (nm.item.iGroupId == I_GROUPIDNONE)
  7982. {
  7983. ListView_RemoveItemFromItsGroup(plv, pitem);
  7984. LISTITEM_SETASKEDFORGROUP(pitem);
  7985. }
  7986. else
  7987. {
  7988. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, nm.item.iGroupId, NULL);
  7989. if (pgrp != pitem->pGroup)
  7990. {
  7991. ListView_RemoveItemFromItsGroup(plv, pitem);
  7992. pitem->pGroup = pgrp;
  7993. if (pgrp)
  7994. {
  7995. DPA_AppendPtr(pgrp->hdpa, pitem);
  7996. }
  7997. }
  7998. }
  7999. }
  8000. plvi->iGroupId = nm.item.iGroupId;
  8001. }
  8002. if (nm.item.mask & LVIF_STATE)
  8003. plvi->state ^= ((plvi->state ^ nm.item.state) & nm.item.stateMask);
  8004. if (nm.item.mask & LVIF_IMAGE)
  8005. plvi->iImage = nm.item.iImage;
  8006. if (nm.item.mask & LVIF_TEXT)
  8007. plvi->pszText = CCReturnDispInfoText(nm.item.pszText, plvi->pszText, plvi->cchTextMax);
  8008. if (nm.item.mask & LVIF_COLUMNS)
  8009. {
  8010. // Put the # of columns back in the LV_ITEM struct. Don't need to
  8011. // do anything with puColumns.
  8012. UINT cColumns = (nm.item.cColumns == I_COLUMNSCALLBACK) ? 0 : nm.item.cColumns;
  8013. UINT cColumnsToCopy = min(cColumns, plvi->cColumns);
  8014. // Copy rguColumns back into the thing we were passed.
  8015. CopyMemory(plvi->puColumns, rguColumns, sizeof(UINT) * cColumnsToCopy);
  8016. plvi->cColumns = cColumnsToCopy;
  8017. }
  8018. if (pitem && (nm.item.mask & LVIF_DI_SETITEM))
  8019. {
  8020. //
  8021. // The SendNotify above can set about a terrible series of events
  8022. // whereby asking for DISPINFO causes the shell to look around
  8023. // (call peekmessage) to see if its got a new async icon for the
  8024. // listview. This lets other messages be delivered, such as an
  8025. // UPDATEIMAGE of Index == -1 (if the user is changing icon sizing
  8026. // at the same time). This causes a re-enumeration of the desktop
  8027. // and hence this very listview is torn down and rebuilt while
  8028. // we're sitting here for the DISPINFO to finish. Thus, as a cheap
  8029. // and dirty solution, I check to see if the item I think I have
  8030. // is the same one I had when I made the notify, and if not, I
  8031. // bail. Don't blame me, I'm just cleaning up the mess.
  8032. if (pitem != ListView_GetItemPtr(plv, plvi->iItem))
  8033. {
  8034. return FALSE;
  8035. }
  8036. if (nm.item.iSubItem == 0)
  8037. {
  8038. //DebugMsg(TF_LISTVIEW, "SAVING ITEMS!");
  8039. if (nm.item.mask & LVIF_IMAGE)
  8040. pitem->iImage = (short) nm.item.iImage;
  8041. if (nm.item.mask & LVIF_INDENT)
  8042. pitem->iIndent = (short) nm.item.iIndent;
  8043. if (nm.item.mask & LVIF_TEXT)
  8044. if (nm.item.pszText)
  8045. {
  8046. Str_Set(&pitem->pszText, nm.item.pszText);
  8047. }
  8048. if (nm.item.mask & LVIF_STATE)
  8049. pitem->state ^= ((pitem->state ^ nm.item.state) & nm.item.stateMask);
  8050. if (nm.item.mask & LVIF_COLUMNS)
  8051. {
  8052. Tile_Set(&pitem->puColumns, &pitem->cColumns, nm.item.puColumns, nm.item.cColumns);
  8053. // Just did a Tile_Set - need to recompute.
  8054. ListView_SetSRecompute(pitem);
  8055. }
  8056. }
  8057. else
  8058. {
  8059. ListView_SetSubItem(plv, &nm.item);
  8060. }
  8061. }
  8062. }
  8063. return TRUE;
  8064. }
  8065. BOOL ListView_OnSetItemA(LV* plv, LV_ITEMA* plvi)
  8066. {
  8067. LPWSTR pszW = NULL;
  8068. LPSTR pszC = NULL;
  8069. BOOL fRet;
  8070. // Let ListView_OnSetItem() handle owner-data validation
  8071. //HACK ALERT -- this code assumes that LV_ITEMA is exactly the same
  8072. // as LV_ITEMW except for the pointer to the string.
  8073. COMPILETIME_ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW));
  8074. if (!plvi)
  8075. return FALSE;
  8076. if ((plvi->mask & LVIF_TEXT) && (plvi->pszText != NULL))
  8077. {
  8078. pszC = plvi->pszText;
  8079. pszW = ProduceWFromA(plv->ci.uiCodePage, pszC);
  8080. if (pszW == NULL)
  8081. return FALSE;
  8082. plvi->pszText = (LPSTR)pszW;
  8083. }
  8084. fRet = ListView_OnSetItem(plv, (const LV_ITEM*) plvi);
  8085. if (pszW != NULL)
  8086. {
  8087. plvi->pszText = pszC;
  8088. FreeProducedString(pszW);
  8089. }
  8090. return fRet;
  8091. }
  8092. BOOL ListView_OnSetItem(LV* plv, const LV_ITEM* plvi)
  8093. {
  8094. LISTITEM* pitem = NULL;
  8095. UINT mask;
  8096. UINT maskChanged;
  8097. UINT rdwFlags=RDW_INVALIDATE;
  8098. int i;
  8099. UINT stateOld, stateNew;
  8100. BOOL fFocused = FALSE;
  8101. BOOL fSelected = FALSE;
  8102. BOOL fStateImageChanged = FALSE;
  8103. if (ListView_IsOwnerData(plv))
  8104. {
  8105. RIPMSG(0, "LVM_SETITEM: Invalid for owner-data listview");
  8106. return FALSE;
  8107. }
  8108. if (!plvi)
  8109. return FALSE;
  8110. RIPMSG(plvi->iSubItem >= 0, "ListView_OnSetItem: Invalid item index");
  8111. if (plv->himl && (plv->clrBk != ImageList_GetBkColor(plv->himl)))
  8112. rdwFlags |= RDW_ERASE;
  8113. mask = plvi->mask;
  8114. if (!mask)
  8115. return TRUE;
  8116. // If we're setting a subitem, handle it elsewhere...
  8117. //
  8118. if (plvi->iSubItem > 0)
  8119. return ListView_SetSubItem(plv, plvi);
  8120. i = plvi->iItem;
  8121. pitem = ListView_GetItemPtr(plv, i);
  8122. if (!pitem)
  8123. return FALSE;
  8124. //REVIEW: This is a BOGUS HACK, and should be fixed.
  8125. //This incorrectly calculates the old state (since we may
  8126. // have to send LVN_GETDISPINFO to get it).
  8127. //
  8128. stateOld = stateNew = 0;
  8129. if (mask & LVIF_STATE)
  8130. {
  8131. stateOld = pitem->state & plvi->stateMask;
  8132. stateNew = plvi->state & plvi->stateMask;
  8133. }
  8134. // Prevent multiple selections in a single-select listview.
  8135. if ((plv->ci.style & LVS_SINGLESEL) && (mask & LVIF_STATE) && (stateNew & LVIS_SELECTED))
  8136. {
  8137. ListView_DeselectAll(plv, i);
  8138. // Refresh the old state information
  8139. stateOld = pitem->state & plvi->stateMask;
  8140. }
  8141. if (!ListView_SendChange(plv, i, 0, LVN_ITEMCHANGING, stateOld, stateNew, mask, pitem->lParam))
  8142. return FALSE;
  8143. maskChanged = 0;
  8144. if (mask & LVIF_STATE)
  8145. {
  8146. UINT change = (pitem->state ^ plvi->state) & plvi->stateMask;
  8147. if (change)
  8148. {
  8149. pitem->state ^= change;
  8150. maskChanged |= LVIF_STATE;
  8151. // the selection state has changed.. update selected count
  8152. if (change & LVIS_SELECTED)
  8153. {
  8154. fSelected = TRUE;
  8155. if (pitem->state & LVIS_SELECTED)
  8156. {
  8157. plv->nSelected++;
  8158. }
  8159. else
  8160. {
  8161. if (plv->nSelected > 0)
  8162. plv->nSelected--;
  8163. }
  8164. }
  8165. // For some bits we can only invert the label area...
  8166. // fSelectOnlyChange = ((change & ~(LVIS_SELECTED | LVIS_FOCUSED | LVIS_DROPHILITED)) == 0);
  8167. // fEraseItem = ((change & ~(LVIS_SELECTED | LVIS_DROPHILITED)) != 0);
  8168. // try to steal focus from the previous guy.
  8169. if (change & LVIS_FOCUSED)
  8170. {
  8171. BOOL fUnfolded = ListView_IsItemUnfolded(plv, plv->iFocus);
  8172. int iOldFocus = plv->iFocus;
  8173. RECT rcLabel;
  8174. fFocused = TRUE;
  8175. if (plv->iFocus != i)
  8176. {
  8177. if ((plv->iFocus == -1) || ListView_OnSetItemState(plv, plv->iFocus, 0, LVIS_FOCUSED))
  8178. {
  8179. ASSERT(pitem->state & LVIS_FOCUSED);
  8180. plv->iFocus = i;
  8181. if (plv->iMark == -1)
  8182. plv->iMark = i;
  8183. }
  8184. else
  8185. {
  8186. fFocused = FALSE;
  8187. pitem->state &= ~LVIS_FOCUSED;
  8188. }
  8189. }
  8190. else
  8191. {
  8192. ASSERT(!(pitem->state & LVIS_FOCUSED));
  8193. plv->iFocus = -1;
  8194. }
  8195. // If we were previously unfolded and we move the focus we must
  8196. // attempt to refresh the previous focus owner to referect this change.
  8197. if (fUnfolded && !ListView_IsItemUnfolded(plv, iOldFocus) && (plv->iItemDrawing != iOldFocus))
  8198. {
  8199. ListView_GetUnfoldedRect(plv, iOldFocus, &rcLabel);
  8200. RedrawWindow(plv->ci.hwnd, &rcLabel, NULL, RDW_INVALIDATE|RDW_ERASE);
  8201. }
  8202. // Kill the tooltip if focus moves, it causes us headaches otherwise!
  8203. ListView_PopBubble(plv);
  8204. }
  8205. if (change & LVIS_CUT ||
  8206. plv->clrTextBk == CLR_NONE)
  8207. rdwFlags |= RDW_ERASE;
  8208. if (change & LVIS_OVERLAYMASK)
  8209. {
  8210. // Overlay changed, so need to blow away icon region cache
  8211. if (pitem->hrgnIcon)
  8212. {
  8213. if (pitem->hrgnIcon != (HANDLE) -1)
  8214. DeleteObject(pitem->hrgnIcon);
  8215. pitem->hrgnIcon = NULL;
  8216. }
  8217. }
  8218. fStateImageChanged = (change & LVIS_STATEIMAGEMASK);
  8219. }
  8220. }
  8221. if (mask & LVIF_TEXT)
  8222. {
  8223. // need to do this now because we're changing the text
  8224. // so we need to get the rect of the thing before the text changes
  8225. // but don't redraw the item we are currently painting
  8226. if (plv->iItemDrawing != i)
  8227. {
  8228. ListView_InvalidateItemEx(plv, i, FALSE,
  8229. RDW_INVALIDATE | RDW_ERASE, LVIF_TEXT);
  8230. }
  8231. if (!Str_Set(&pitem->pszText, plvi->pszText))
  8232. return FALSE;
  8233. plv->rcView.left = RECOMPUTE;
  8234. ListView_SetSRecompute(pitem);
  8235. maskChanged |= LVIF_TEXT;
  8236. }
  8237. if (mask & LVIF_INDENT)
  8238. {
  8239. if (pitem->iIndent != plvi->iIndent)
  8240. {
  8241. pitem->iIndent = (short) plvi->iIndent;
  8242. maskChanged |= LVIF_INDENT;
  8243. if (ListView_IsReportView(plv))
  8244. rdwFlags |= RDW_ERASE;
  8245. }
  8246. }
  8247. if (mask & LVIF_IMAGE)
  8248. {
  8249. if (pitem->iImage != plvi->iImage)
  8250. {
  8251. pitem->iImage = (short) plvi->iImage;
  8252. maskChanged |= LVIF_IMAGE;
  8253. if (pitem->hrgnIcon)
  8254. {
  8255. if (pitem->hrgnIcon != (HANDLE) -1)
  8256. DeleteObject(pitem->hrgnIcon);
  8257. pitem->hrgnIcon = NULL;
  8258. }
  8259. // erase if there was a set image
  8260. if (pitem->iImage != I_IMAGECALLBACK)
  8261. rdwFlags |= RDW_ERASE;
  8262. }
  8263. }
  8264. if (mask & LVIF_PARAM)
  8265. {
  8266. if (pitem->lParam != plvi->lParam)
  8267. {
  8268. pitem->lParam = plvi->lParam;
  8269. maskChanged |= LVIF_PARAM;
  8270. }
  8271. }
  8272. if (mask & LVIF_GROUPID)
  8273. {
  8274. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, plvi->iGroupId, NULL);
  8275. if (pgrp)
  8276. {
  8277. if (pitem->pGroup != pgrp)
  8278. {
  8279. ListView_RemoveItemFromItsGroup(plv, pitem);
  8280. pitem->pGroup = pgrp;
  8281. DPA_AppendPtr(pgrp->hdpa, pitem);
  8282. if (ListView_RedrawEnabled(plv))
  8283. {
  8284. _ListView_RecomputeEx(plv, NULL, 0, FALSE);
  8285. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  8286. }
  8287. maskChanged |= LVIF_GROUPID;
  8288. }
  8289. }
  8290. }
  8291. if (mask & LVIF_COLUMNS)
  8292. {
  8293. UINT uNumColumns = (plvi->cColumns == I_COLUMNSCALLBACK) ? 0 : plvi->cColumns;
  8294. if (((uNumColumns > 0) && (plvi->puColumns == NULL)) || // Didn't provide any columns
  8295. (uNumColumns > CCMAX_TILE_COLUMNS)) // Provided too many
  8296. {
  8297. return FALSE; // See note below about premature return.
  8298. }
  8299. if (!Tile_Set(&pitem->puColumns, &pitem->cColumns, plvi->puColumns, plvi->cColumns))
  8300. return FALSE;
  8301. // Note: if we fail here, we may have still set the LVIF_TEXT above...
  8302. // so the call partially succeeded. Oh well, no way to undo that.
  8303. maskChanged |= LVIF_COLUMNS;
  8304. // Columns changed - need to recompute this guy.
  8305. ListView_SetSRecompute(pitem);
  8306. }
  8307. if (maskChanged)
  8308. {
  8309. // don't redraw the item we are currently painting
  8310. if (plv->iItemDrawing != i)
  8311. ListView_InvalidateItemEx(plv, i, FALSE, rdwFlags, maskChanged);
  8312. TraceMsg(DM_LVSENDCHANGE, "LV - SendChange %d %d %d %d", i, stateOld, stateNew, maskChanged);
  8313. ListView_SendChange(plv, i, 0, LVN_ITEMCHANGED, stateOld, stateNew, maskChanged, pitem->lParam);
  8314. if (maskChanged & LVIF_TEXT)
  8315. NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, plv->ci.hwnd, OBJID_CLIENT, i+1);
  8316. if (maskChanged & LVIF_STATE)
  8317. {
  8318. if (fFocused)
  8319. ListView_NotifyFocusEvent(plv);
  8320. if (fSelected)
  8321. {
  8322. if (stateNew & LVIS_SELECTED)
  8323. {
  8324. NotifyWinEvent((plv->nSelected == 1) ? EVENT_OBJECT_SELECTION :
  8325. EVENT_OBJECT_SELECTIONADD, plv->ci.hwnd, OBJID_CLIENT, i+1);
  8326. }
  8327. else
  8328. {
  8329. NotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE, plv->ci.hwnd, OBJID_CLIENT, i+1);
  8330. }
  8331. }
  8332. if (fStateImageChanged)
  8333. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, plv->ci.hwnd, OBJID_CLIENT, i+1);
  8334. }
  8335. }
  8336. return TRUE;
  8337. }
  8338. UINT ListView_OnGetItemState(LV* plv, int i, UINT mask)
  8339. {
  8340. LV_ITEM lvi;
  8341. lvi.mask = LVIF_STATE;
  8342. lvi.stateMask = mask;
  8343. lvi.iItem = i;
  8344. lvi.iSubItem = 0;
  8345. if (!ListView_OnGetItem(plv, &lvi))
  8346. return 0;
  8347. return lvi.state;
  8348. }
  8349. BOOL ListView_OnSetItemState(LV* plv, int i, UINT data, UINT mask)
  8350. {
  8351. UINT rdwFlags = RDW_INVALIDATE;
  8352. LV_ITEM lvi;
  8353. lvi.mask = LVIF_STATE;
  8354. lvi.state = data;
  8355. lvi.stateMask = mask;
  8356. lvi.iItem = i;
  8357. lvi.iSubItem = 0;
  8358. // if the item is -1, we will do it for all items. We special case
  8359. // a few cases here as to speed it up. For example if the mask is
  8360. // LVIS_SELECTED and data is zero it implies that we will deselect
  8361. // all items...
  8362. //
  8363. if (ListView_IsOwnerData(plv))
  8364. {
  8365. UINT uOldData = 0;
  8366. // these are the only two we handled
  8367. mask &= (LVIS_SELECTED | LVIS_FOCUSED | LVIS_CUT | LVIS_DROPHILITED);
  8368. if (!mask)
  8369. return TRUE;
  8370. if (plv->clrTextBk == CLR_NONE ||
  8371. (plv->himl && (plv->clrBk != ImageList_GetBkColor(plv->himl))))
  8372. {
  8373. rdwFlags |= RDW_ERASE;
  8374. }
  8375. if (i == -1)
  8376. {
  8377. // request selection state change for all
  8378. if (mask & LVIS_SELECTED)
  8379. {
  8380. if (data & LVIS_SELECTED)
  8381. { // set selection
  8382. if ((plv->ci.style & LVS_SINGLESEL))
  8383. { // cant make multiple selections in a single-select listview.
  8384. return FALSE;
  8385. }
  8386. if (plv->cTotalItems)
  8387. {
  8388. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, 0, plv->cTotalItems - 1)))
  8389. return FALSE;
  8390. }
  8391. RedrawWindow(plv->ci.hwnd, NULL, NULL, rdwFlags);
  8392. }
  8393. else
  8394. { // clear selection
  8395. if (plv->nSelected > 0)
  8396. {
  8397. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeSel);
  8398. if (FAILED(plv->plvrangeSel->lpVtbl->Clear(plv->plvrangeSel)))
  8399. return FALSE;
  8400. }
  8401. else
  8402. {
  8403. // if nothing was selected, then there's nothing to clear
  8404. // no change.
  8405. mask &= ~ LVIS_SELECTED;
  8406. }
  8407. }
  8408. uOldData |= (LVIS_SELECTED & (mask ^ data));
  8409. // Update our internal count to what the list thinks is the number selected...
  8410. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  8411. }
  8412. // can maybe combine with above code...
  8413. if (mask & LVIS_CUT)
  8414. {
  8415. if (data & LVIS_CUT)
  8416. { // set selection
  8417. if (plv->cTotalItems)
  8418. if (FAILED(plv->plvrangeCut->lpVtbl->IncludeRange(plv->plvrangeCut, 0, plv->cTotalItems - 1)))
  8419. return FALSE;
  8420. RedrawWindow(plv->ci.hwnd, NULL, NULL, rdwFlags);
  8421. }
  8422. else
  8423. { // clear selection
  8424. if (plv->plvrangeCut->lpVtbl->IsEmpty(plv->plvrangeCut) != S_OK)
  8425. {
  8426. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeCut);
  8427. if (FAILED(plv->plvrangeCut->lpVtbl->Clear(plv->plvrangeCut)))
  8428. return FALSE;
  8429. }
  8430. else
  8431. {
  8432. // if nothing was selected, then there's nothing to clear
  8433. // no change.
  8434. mask &= ~ LVIS_CUT;
  8435. }
  8436. }
  8437. uOldData |= (LVIS_CUT & (mask ^ data));
  8438. }
  8439. // request focus state change
  8440. if (mask & LVIS_FOCUSED)
  8441. {
  8442. if (data & LVIS_FOCUSED)
  8443. { // cant set focus to all
  8444. return FALSE;
  8445. }
  8446. else if (plv->iFocus != -1)
  8447. {
  8448. int iOldFocus = plv->iFocus;
  8449. // clear focus
  8450. uOldData |= (LVIS_FOCUSED & (mask ^ data));
  8451. plv->iFocus = -1;
  8452. // notify that the old focus is being lost
  8453. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), iOldFocus, LVIS_FOCUSED, 0);
  8454. ListView_SendChange(plv, iOldFocus, 0, LVN_ITEMCHANGED, LVIS_FOCUSED, 0, LVIF_STATE, 0);
  8455. ListView_InvalidateFoldedItem(plv, iOldFocus, TRUE, RDW_INVALIDATE |RDW_ERASE);
  8456. }
  8457. }
  8458. if (mask & LVIS_DROPHILITED)
  8459. {
  8460. if (data & LVIS_DROPHILITED)
  8461. { // cant set focus to all
  8462. return FALSE;
  8463. }
  8464. else if (plv->iDropHilite != -1)
  8465. {
  8466. int iOldDropHilite = plv->iDropHilite;
  8467. // clear focus
  8468. uOldData |= (LVIS_FOCUSED & (mask ^ data));
  8469. plv->iDropHilite = -1;
  8470. // notify that the old focus is being lost
  8471. ListView_SendChange(plv, iOldDropHilite, 0, LVN_ITEMCHANGED, LVIS_DROPHILITED, 0, LVIF_STATE, 0);
  8472. ListView_InvalidateFoldedItem(plv, iOldDropHilite, TRUE, RDW_INVALIDATE |RDW_ERASE);
  8473. }
  8474. }
  8475. // invalidate and notify if there was a change
  8476. if (uOldData ^ (data & mask))
  8477. {
  8478. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), i, uOldData, data);
  8479. ListView_SendChange(plv, i, 0, LVN_ITEMCHANGED, uOldData, data, LVIF_STATE, 0);
  8480. if (mask & LVIS_SELECTED)
  8481. {
  8482. // Tell accessibility, "Selection changed in a complex way"
  8483. // (There is no "select all" or "select none" notification)
  8484. NotifyWinEvent(EVENT_OBJECT_SELECTIONWITHIN, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  8485. }
  8486. }
  8487. }
  8488. else
  8489. {
  8490. if (!ListView_IsValidItemNumber(plv, i))
  8491. return FALSE;
  8492. // request selection state change
  8493. // and the selection state is new...
  8494. if ((mask & LVIS_SELECTED))
  8495. {
  8496. if (((plv->plvrangeSel->lpVtbl->IsSelected(plv->plvrangeSel, i) == S_OK) ? LVIS_SELECTED : 0) ^ (data & LVIS_SELECTED))
  8497. {
  8498. if (data & LVIS_SELECTED)
  8499. { // set selection
  8500. if ((plv->ci.style & LVS_SINGLESEL))
  8501. {
  8502. // in single selection mode, we need to deselect everything else
  8503. if (!ListView_OnSetItemState(plv, -1, 0, LVIS_SELECTED))
  8504. return FALSE;
  8505. }
  8506. // now select the new item
  8507. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, i, i)))
  8508. return FALSE;
  8509. }
  8510. else
  8511. { // clear selection
  8512. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, i, i)))
  8513. return FALSE;
  8514. }
  8515. // something actually changed (or else we wouldn't be in this
  8516. // if block
  8517. uOldData |= (LVIS_SELECTED & (mask ^ data));
  8518. }
  8519. else
  8520. {
  8521. // nothing changed... so make the uOldData be the same for this bit
  8522. // else make this the same as
  8523. uOldData |= (LVIS_SELECTED & (mask & data));
  8524. }
  8525. // Update our internal count to what the list thinks is the number selected...
  8526. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  8527. }
  8528. if ((mask & LVIS_CUT))
  8529. {
  8530. if (((plv->plvrangeCut->lpVtbl->IsSelected(plv->plvrangeCut, i) == S_OK) ? LVIS_CUT : 0) ^ (data & LVIS_CUT))
  8531. {
  8532. if (data & LVIS_CUT)
  8533. {
  8534. // now select the new item
  8535. if (FAILED(plv->plvrangeCut->lpVtbl->IncludeRange(plv->plvrangeCut, i, i)))
  8536. return FALSE;
  8537. }
  8538. else
  8539. { // clear selection
  8540. if (FAILED(plv->plvrangeCut->lpVtbl->ExcludeRange(plv->plvrangeCut, i, i)))
  8541. return FALSE;
  8542. }
  8543. // something actually changed (or else we wouldn't be in this
  8544. // if block
  8545. uOldData |= (LVIS_CUT & (mask ^ data));
  8546. rdwFlags |= RDW_ERASE;
  8547. }
  8548. else
  8549. {
  8550. // nothing changed... so make the uOldData be the same for this bit
  8551. // else make this the same as
  8552. uOldData |= (LVIS_CUT & (mask & data));
  8553. }
  8554. }
  8555. // request focus state change
  8556. if (mask & LVIS_FOCUSED)
  8557. {
  8558. int iOldFocus = plv->iFocus;
  8559. if (data & LVIS_FOCUSED)
  8560. { // set focus
  8561. if (i != plv->iFocus)
  8562. {
  8563. // we didn't have the focus before
  8564. plv->iFocus = i;
  8565. if (plv->iMark == -1)
  8566. plv->iMark = i;
  8567. if (iOldFocus != -1)
  8568. {
  8569. // we're stealing it from someone
  8570. // notify of the change
  8571. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), iOldFocus, LVIS_FOCUSED, 0);
  8572. ListView_SendChange(plv, iOldFocus, 0, LVN_ITEMCHANGED, LVIS_FOCUSED, 0, LVIF_STATE, 0);
  8573. }
  8574. }
  8575. else
  8576. {
  8577. // we DID have the focus before
  8578. uOldData |= LVIS_FOCUSED;
  8579. }
  8580. }
  8581. else
  8582. { // clear focus
  8583. if (i == plv->iFocus)
  8584. {
  8585. plv->iFocus = -1;
  8586. uOldData |= LVIS_FOCUSED;
  8587. }
  8588. }
  8589. }
  8590. // request focus state change
  8591. if (mask & LVIS_DROPHILITED)
  8592. {
  8593. int iOldDropHilite = plv->iDropHilite;
  8594. if (data & LVIS_DROPHILITED)
  8595. { // set Drop Hilite
  8596. if (i != plv->iDropHilite)
  8597. {
  8598. // we didn't have the Drop Hilite before
  8599. plv->iDropHilite = i;
  8600. if (iOldDropHilite != -1)
  8601. {
  8602. // we're stealing it from someone
  8603. // notify of the change
  8604. ListView_SendChange(plv, iOldDropHilite, 0, LVN_ITEMCHANGED, LVIS_DROPHILITED, 0, LVIF_STATE, 0);
  8605. ListView_InvalidateFoldedItem(plv, iOldDropHilite, TRUE, RDW_INVALIDATE |RDW_ERASE);
  8606. }
  8607. }
  8608. else
  8609. {
  8610. // we DID have the Drop Hilite before
  8611. uOldData |= LVIS_DROPHILITED;
  8612. }
  8613. }
  8614. else
  8615. { // clear Drop Hilite
  8616. if (i == plv->iDropHilite)
  8617. {
  8618. plv->iDropHilite = -1;
  8619. uOldData |= LVIS_DROPHILITED;
  8620. }
  8621. }
  8622. }
  8623. // invalidate and notify if there was a change
  8624. if (uOldData ^ (data & mask))
  8625. {
  8626. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), i, uOldData, data);
  8627. ListView_SendChange(plv, i, 0, LVN_ITEMCHANGED, uOldData, data, LVIF_STATE, 0);
  8628. ListView_InvalidateItem(plv, i, TRUE, rdwFlags);
  8629. // Kill the tooltip if focus moves, it causes us headaches otherwise!
  8630. if ((uOldData ^ (data & mask)) & LVIS_FOCUSED)
  8631. {
  8632. ListView_PopBubble(plv);
  8633. ListView_NotifyFocusEvent(plv);
  8634. }
  8635. // Tell accessibility about the changes
  8636. if (mask & LVIS_SELECTED)
  8637. {
  8638. UINT event;
  8639. if (data & LVIS_SELECTED)
  8640. {
  8641. if (plv->nSelected == 1)
  8642. event = EVENT_OBJECT_SELECTION; // this object is the entire selection
  8643. else
  8644. event = EVENT_OBJECT_SELECTIONADD; // this object is selected
  8645. }
  8646. else
  8647. event = EVENT_OBJECT_SELECTIONREMOVE; // this object is unselected
  8648. NotifyWinEvent(event, plv->ci.hwnd, OBJID_CLIENT, i + 1);
  8649. }
  8650. }
  8651. }
  8652. }
  8653. else
  8654. {
  8655. if (i != -1)
  8656. {
  8657. return ListView_OnSetItem(plv, &lvi);
  8658. }
  8659. else
  8660. {
  8661. UINT flags = LVNI_ALL;
  8662. if (data == 0)
  8663. {
  8664. switch (mask)
  8665. {
  8666. case LVIS_SELECTED:
  8667. flags = LVNI_SELECTED;
  8668. break;
  8669. case LVIS_CUT:
  8670. flags = LVNI_CUT;
  8671. break;
  8672. }
  8673. }
  8674. else if ((plv->ci.style & LVS_SINGLESEL) && (mask == LVIS_SELECTED))
  8675. return FALSE; /* can't select all in single-select listview */
  8676. else if ((mask & data) & LVIS_FOCUSED)
  8677. {
  8678. return FALSE; // can't set focus to everything
  8679. }
  8680. //
  8681. // Now iterate over all of the items that match our criteria and
  8682. // set their new value.
  8683. //
  8684. while ((lvi.iItem = ListView_OnGetNextItem(plv, lvi.iItem, flags)) != -1)
  8685. {
  8686. ListView_OnSetItem(plv, &lvi);
  8687. }
  8688. }
  8689. }
  8690. return TRUE;
  8691. }
  8692. //
  8693. // Returns TRUE if the label of an item is not truncated (is unfolded) and FALSE
  8694. // otherwise. If FALSE is returned, it also fills the Unfolding text in pszText.
  8695. // If TRUE is returned, pszText is set to empty string.
  8696. //
  8697. BOOL ListView_IsItemUnfolded2(LV* plv, int iItem, int iSubItem, LPTSTR pszText, int cchTextMax)
  8698. {
  8699. BOOL bItemUnfolded = ListView_IsItemUnfolded(plv, iItem);
  8700. if (pszText && cchTextMax > 0) // Sanity checks on input params.
  8701. {
  8702. pszText[0] = 0;
  8703. if (!bItemUnfolded)
  8704. {
  8705. RECT rcLabel = {0};
  8706. LV_ITEM item;
  8707. item.iItem = iItem;
  8708. item.iSubItem = iSubItem;
  8709. item.mask = LVIF_TEXT | LVIF_PARAM;
  8710. if (ListView_IsTileView(plv))
  8711. {
  8712. TCalculateSubItemRect(plv, NULL, NULL, iItem, iSubItem, NULL, NULL, &bItemUnfolded);
  8713. if (!bItemUnfolded)
  8714. {
  8715. // Need to supply text.
  8716. item.pszText = pszText;
  8717. item.cchTextMax = cchTextMax;
  8718. ListView_OnGetItem(plv, &item);
  8719. }
  8720. }
  8721. else if (!ListView_IsIconView(plv))
  8722. {
  8723. if (ListView_IsLabelTip(plv) || ListView_IsInfoTip(plv))
  8724. {
  8725. BOOL fSuccess;
  8726. rcLabel.left = LVIR_LABEL;
  8727. if (iSubItem)
  8728. {
  8729. rcLabel.top = iSubItem;
  8730. fSuccess = ListView_OnGetSubItemRect(plv, iItem, &rcLabel);
  8731. }
  8732. else
  8733. {
  8734. fSuccess = ListView_OnGetItemRect(plv, iItem, &rcLabel);
  8735. }
  8736. if (fSuccess)
  8737. {
  8738. TCHAR szText[INFOTIPSIZE];
  8739. item.pszText = szText;
  8740. item.cchTextMax = min(ARRAYSIZE(szText), cchTextMax);
  8741. if (ListView_OnGetItem(plv, &item) && item.pszText != LPSTR_TEXTCALLBACK)
  8742. {
  8743. SIZE siz;
  8744. LVFAKEDRAW lvfd;
  8745. int cx;
  8746. HRESULT hr = E_FAIL;
  8747. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  8748. ListView_BeginFakeItemDraw(&lvfd);
  8749. // ---------Label width----------- ---Client width---
  8750. cx = min(rcLabel.right - g_cxLabelMargin, plv->sizeClient.cx);
  8751. hr = GetTextExtentPoint32(lvfd.nmcd.nmcd.hdc, item.pszText, lstrlen(item.pszText), &siz) ?
  8752. S_OK : E_FAIL;
  8753. if (SUCCEEDED(hr) &&
  8754. (rcLabel.left + g_cxLabelMargin + siz.cx) > cx)
  8755. {
  8756. StringCchCopy(pszText, item.cchTextMax, item.pszText);
  8757. }
  8758. else
  8759. {
  8760. // Not truncated after all
  8761. bItemUnfolded = TRUE;
  8762. }
  8763. ListView_EndFakeItemDraw(&lvfd);
  8764. ListView_EndFakeCustomDraw(&lvfd);
  8765. }
  8766. }
  8767. }
  8768. }
  8769. else
  8770. {
  8771. // Large icon view is the only one that folds
  8772. if (ListView_GetUnfoldedRect(plv, iItem, &rcLabel))
  8773. {
  8774. item.pszText = pszText;
  8775. item.cchTextMax = cchTextMax;
  8776. ListView_OnGetItem(plv, &item);
  8777. }
  8778. else
  8779. {
  8780. // Item was never folded
  8781. bItemUnfolded = TRUE;
  8782. }
  8783. }
  8784. }
  8785. }
  8786. return bItemUnfolded;
  8787. }
  8788. // Rather than thunking to ListView_OnGetItemText, we let ListView_GetItemA
  8789. // do the work.
  8790. int ListView_OnGetItemTextA(LV* plv, int i, LV_ITEMA *plvi)
  8791. {
  8792. if (!plvi)
  8793. return 0;
  8794. RIPMSG(plvi->pszText != NULL, "LVM_GETITEMTEXT null string pointer");
  8795. plvi->mask = LVIF_TEXT;
  8796. plvi->iItem = i;
  8797. if (!ListView_OnGetItemA(plv, plvi))
  8798. return 0;
  8799. return lstrlenA(plvi->pszText);
  8800. }
  8801. int ListView_OnGetItemText(LV* plv, int i, LV_ITEM *plvi)
  8802. {
  8803. if (!plvi)
  8804. return 0;
  8805. RIPMSG(plvi->pszText != NULL, "LVM_GETITEMTEXT null string pointer");
  8806. plvi->mask = LVIF_TEXT;
  8807. plvi->iItem = i;
  8808. if (!ListView_OnGetItem(plv, plvi))
  8809. return 0;
  8810. return lstrlen(plvi->pszText);
  8811. }
  8812. BOOL WINAPI ListView_OnSetItemTextA(LV* plv, int i, int iSubItem, LPCSTR pszText)
  8813. {
  8814. LPWSTR pszW = NULL;
  8815. BOOL fRet;
  8816. // Let ListView_OnSetItemText() handle owner-data validation
  8817. if (pszText != NULL)
  8818. {
  8819. pszW = ProduceWFromA(plv->ci.uiCodePage, pszText);
  8820. if (pszW == NULL)
  8821. {
  8822. return FALSE;
  8823. }
  8824. }
  8825. fRet = ListView_OnSetItemText(plv, i, iSubItem, pszW);
  8826. FreeProducedString(pszW);
  8827. return fRet;
  8828. }
  8829. BOOL WINAPI ListView_OnSetItemText(LV* plv, int i, int iSubItem, LPCTSTR pszText)
  8830. {
  8831. LV_ITEM lvi;
  8832. if (ListView_IsOwnerData(plv))
  8833. {
  8834. RIPMSG(0, "LVM_SETITEMTEXT: Invalid for owner-data listview");
  8835. return FALSE;
  8836. }
  8837. ListView_InvalidateTTLastHit(plv, i);
  8838. lvi.mask = LVIF_TEXT;
  8839. lvi.pszText = (LPTSTR)pszText;
  8840. lvi.iItem = i;
  8841. lvi.iSubItem = iSubItem;
  8842. return ListView_OnSetItem(plv, &lvi);
  8843. }
  8844. VOID CALLBACK ImgCtxCallback(void * pvImgCtx, void * pvArg)
  8845. {
  8846. LV *plv = (LV *)pvArg;
  8847. ULONG ulState;
  8848. SIZE sizeImg;
  8849. IImgCtx *pImgCtx = plv->pImgCtx;
  8850. IImgCtx_GetStateInfo(pImgCtx, &ulState, &sizeImg, TRUE);
  8851. if (ulState & (IMGLOAD_STOPPED | IMGLOAD_ERROR))
  8852. {
  8853. TraceMsg(TF_BKIMAGE, "Listview ImageCallback: Error!");
  8854. plv->fImgCtxComplete = FALSE;
  8855. }
  8856. else if (ulState & IMGCHG_COMPLETE)
  8857. {
  8858. TraceMsg(TF_BKIMAGE, "Listview ImageCallback: Complete!");
  8859. plv->fImgCtxComplete = TRUE;
  8860. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  8861. }
  8862. }
  8863. void ListView_ReleaseBkImage(LV *plv)
  8864. {
  8865. if (plv->pImgCtx)
  8866. {
  8867. IImgCtx_Release(plv->pImgCtx);
  8868. plv->pImgCtx = NULL;
  8869. if (plv->hpalHalftone)
  8870. {
  8871. // No need to delete the half tone palette since we really
  8872. // share it with the image context and it will clean up.
  8873. plv->hpalHalftone = NULL;
  8874. }
  8875. }
  8876. if (plv->hbmBkImage)
  8877. {
  8878. DeleteObject(plv->hbmBkImage);
  8879. plv->hbmBkImage = NULL;
  8880. }
  8881. if (plv->pszBkImage)
  8882. {
  8883. LocalFree(plv->pszBkImage);
  8884. plv->pszBkImage = NULL;
  8885. }
  8886. }
  8887. BOOL WINAPI ListView_OnSetBkImage(LV* plv, LPLVBKIMAGE pbi)
  8888. {
  8889. BOOL fRet = FALSE;
  8890. if (!pbi)
  8891. return FALSE;
  8892. if (pbi->ulFlags & LVBKIF_TYPE_WATERMARK)
  8893. {
  8894. BITMAP bm;
  8895. if (pbi->ulFlags & ~LVBKIF_TYPE_WATERMARK)
  8896. return FALSE; // We don't support anything else with a watermark
  8897. if (plv->hbmpWatermark)
  8898. {
  8899. DeleteObject(plv->hbmpWatermark);
  8900. plv->hbmpWatermark = NULL;
  8901. }
  8902. if (pbi->hbm && GetObject(pbi->hbm, sizeof(bm), &bm))
  8903. {
  8904. plv->hbmpWatermark = pbi->hbm;
  8905. plv->szWatermark.cx = bm.bmWidth;
  8906. plv->szWatermark.cy = bm.bmHeight;
  8907. fRet = TRUE;
  8908. }
  8909. }
  8910. else
  8911. {
  8912. LPCTSTR pszImage = pbi->pszImage;
  8913. long fl;
  8914. switch (pbi->ulFlags & LVBKIF_SOURCE_MASK)
  8915. {
  8916. case LVBKIF_SOURCE_NONE:
  8917. TraceMsg(TF_BKIMAGE, "LV SetBkImage to none");
  8918. ListView_ReleaseBkImage(plv);
  8919. break;
  8920. case LVBKIF_SOURCE_HBITMAP:
  8921. TraceMsg(TF_BKIMAGE, "LV SetBkImage to hBitmap %08lX", pbi->hbm);
  8922. ListView_ReleaseBkImage(plv);
  8923. if (pbi->hbm &&
  8924. (plv->pImgCtx = CBitmapImgCtx_Create(pbi->hbm)) != NULL)
  8925. {
  8926. plv->hbmBkImage = pbi->hbm;
  8927. }
  8928. else
  8929. {
  8930. pbi->ulFlags &= ~LVBKIF_SOURCE_HBITMAP;
  8931. }
  8932. break;
  8933. case LVBKIF_SOURCE_URL:
  8934. TraceMsg(TF_BKIMAGE, "LV SetBkImage to URL");
  8935. ListView_ReleaseBkImage(plv);
  8936. if (pszImage && pszImage[0])
  8937. {
  8938. HRESULT (*pfnCoCreateInstance)(REFCLSID, IUnknown *, DWORD, REFIID, void * *);
  8939. HRESULT hr;
  8940. HMODULE hmodOLE;
  8941. DWORD cchBkImage = lstrlen(pszImage)+1;
  8942. plv->pszBkImage = LocalAlloc(LPTR, cchBkImage * sizeof(TCHAR));
  8943. if (plv->pszBkImage == NULL)
  8944. {
  8945. TraceMsg(TF_BKIMAGE, "Wow, could not allocate memory for string!");
  8946. return FALSE;
  8947. }
  8948. StringCchCopy(plv->pszBkImage, cchBkImage, pszImage);
  8949. if (((hmodOLE = GetModuleHandle(TEXT("OLE32"))) == NULL) ||
  8950. ((pfnCoCreateInstance = (HRESULT (*)(REFCLSID, IUnknown *, DWORD, REFIID, void * *))GetProcAddress(hmodOLE, "CoCreateInstance")) == NULL))
  8951. {
  8952. TraceMsg(TF_BKIMAGE, "Could not find CoCreateInstance!");
  8953. TraceMsg(TF_BKIMAGE, "Did the caller remember to call CoInitialize?");
  8954. return FALSE;
  8955. }
  8956. hr = pfnCoCreateInstance(&CLSID_IImgCtx, NULL, CLSCTX_INPROC_SERVER,
  8957. &IID_IImgCtx, (void * *)&plv->pImgCtx);
  8958. if (FAILED(hr))
  8959. {
  8960. TraceMsg(TF_BKIMAGE, "Could not create a pImgCtx!");
  8961. TraceMsg(TF_BKIMAGE, "Did you remember to register IEIMAGE.DLL?");
  8962. return FALSE;
  8963. }
  8964. //
  8965. // Mirror the downloaded image if the listview window is RTL mirrored,
  8966. // so that it would be displayed as is. [samera]
  8967. //
  8968. fl = ((IS_WINDOW_RTL_MIRRORED(plv->ci.hwnd)) ? DWN_MIRRORIMAGE : 0);
  8969. hr = IImgCtx_Load(plv->pImgCtx, pszImage, fl);
  8970. if (FAILED(hr))
  8971. {
  8972. IImgCtx_Release(plv->pImgCtx);
  8973. plv->pImgCtx = NULL;
  8974. TraceMsg(TF_BKIMAGE, "Could not init a pImgCtx!");
  8975. return FALSE;
  8976. }
  8977. }
  8978. else
  8979. {
  8980. pbi->ulFlags &= ~LVBKIF_SOURCE_URL;
  8981. }
  8982. break;
  8983. default:
  8984. RIPMSG(0, "LVM_SETBKIMAGE: Unsupported image type %d", pbi->ulFlags & LVBKIF_SOURCE_MASK);
  8985. return FALSE;
  8986. }
  8987. plv->ulBkImageFlags = pbi->ulFlags;
  8988. plv->xOffsetPercent = pbi->xOffsetPercent;
  8989. plv->yOffsetPercent = pbi->yOffsetPercent;
  8990. //
  8991. // If we actually created a pImgCtx, initialize it here.
  8992. //
  8993. if (plv->pImgCtx)
  8994. {
  8995. if (plv->hpalHalftone == NULL)
  8996. {
  8997. IImgCtx_GetPalette(plv->pImgCtx, &plv->hpalHalftone);
  8998. }
  8999. plv->fImgCtxComplete = FALSE;
  9000. IImgCtx_SetCallback(plv->pImgCtx, ImgCtxCallback, plv);
  9001. IImgCtx_SelectChanges(plv->pImgCtx, IMGCHG_COMPLETE, 0, TRUE);
  9002. TraceMsg(TF_BKIMAGE, " SUCCESS!");
  9003. fRet = TRUE;
  9004. }
  9005. }
  9006. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9007. return fRet;
  9008. }
  9009. BOOL WINAPI ListView_OnSetBkImageA(LV* plv, LPLVBKIMAGEA pbiA)
  9010. {
  9011. BOOL fProducedString = FALSE;
  9012. BOOL fRet;
  9013. LVBKIMAGEW biW;
  9014. CopyMemory(&biW, pbiA, sizeof(LVBKIMAGE));
  9015. switch (biW.ulFlags & LVBKIF_SOURCE_MASK)
  9016. {
  9017. case LVBKIF_SOURCE_NONE:
  9018. case LVBKIF_SOURCE_HBITMAP:
  9019. break;
  9020. case LVBKIF_SOURCE_URL:
  9021. if (biW.pszImage != NULL)
  9022. {
  9023. biW.pszImage = ProduceWFromA(plv->ci.uiCodePage, (LPCSTR)biW.pszImage);
  9024. if (biW.pszImage == (LPARAM)NULL)
  9025. {
  9026. return FALSE;
  9027. }
  9028. fProducedString = TRUE;
  9029. }
  9030. break;
  9031. default:
  9032. // Let ListView_OnSetBkImage() complain about the invalid parameter
  9033. break;
  9034. }
  9035. fRet = ListView_OnSetBkImage(plv, &biW);
  9036. if (fProducedString)
  9037. {
  9038. FreeProducedString((void *)biW.pszImage);
  9039. }
  9040. return fRet;
  9041. }
  9042. BOOL WINAPI ListView_OnGetBkImage(LV* plv, LPLVBKIMAGE pbi)
  9043. {
  9044. BOOL fRet = FALSE;
  9045. if (!IsBadWritePtr(pbi, sizeof(*pbi)))
  9046. {
  9047. if (pbi->ulFlags & LVBKIF_TYPE_WATERMARK)
  9048. {
  9049. pbi->hbm = plv->hbmpWatermark;
  9050. fRet = TRUE;
  9051. }
  9052. else
  9053. {
  9054. pbi->ulFlags = plv->ulBkImageFlags;
  9055. switch (plv->ulBkImageFlags & LVBKIF_SOURCE_MASK)
  9056. {
  9057. case LVBKIF_SOURCE_NONE:
  9058. fRet = TRUE;
  9059. break;
  9060. case LVBKIF_SOURCE_HBITMAP:
  9061. pbi->hbm = plv->hbmBkImage;
  9062. fRet = TRUE;
  9063. break;
  9064. case LVBKIF_SOURCE_URL:
  9065. if (!IsBadWritePtr(pbi->pszImage, pbi->cchImageMax * sizeof(TCHAR)))
  9066. {
  9067. StringCchCopy(pbi->pszImage, pbi->cchImageMax, plv->pszBkImage);
  9068. fRet = TRUE;
  9069. }
  9070. break;
  9071. default:
  9072. RIPMSG(0, "ListView_OnGetBkImage: Invalid source");
  9073. break;
  9074. }
  9075. pbi->xOffsetPercent = plv->xOffsetPercent;
  9076. pbi->yOffsetPercent = plv->yOffsetPercent;
  9077. }
  9078. }
  9079. return fRet;
  9080. }
  9081. BOOL WINAPI ListView_OnGetBkImageA(LV* plv, LPLVBKIMAGEA pbiA)
  9082. {
  9083. BOOL fRet = FALSE;
  9084. if (!IsBadWritePtr(pbiA, sizeof(*pbiA)))
  9085. {
  9086. pbiA->ulFlags = plv->ulBkImageFlags;
  9087. switch (plv->ulBkImageFlags & LVBKIF_SOURCE_MASK)
  9088. {
  9089. case LVBKIF_SOURCE_NONE:
  9090. fRet = TRUE;
  9091. break;
  9092. case LVBKIF_SOURCE_HBITMAP:
  9093. pbiA->hbm = plv->hbmBkImage;
  9094. fRet = TRUE;
  9095. break;
  9096. case LVBKIF_SOURCE_URL:
  9097. if (!IsBadWritePtr(pbiA->pszImage, pbiA->cchImageMax))
  9098. {
  9099. ConvertWToAN(plv->ci.uiCodePage, pbiA->pszImage,
  9100. pbiA->cchImageMax, plv->pszBkImage, -1);
  9101. fRet = TRUE;
  9102. }
  9103. break;
  9104. default:
  9105. RIPMSG(0, "ListView_OnGetBkImage: Invalid source");
  9106. break;
  9107. }
  9108. pbiA->xOffsetPercent = plv->xOffsetPercent;
  9109. pbiA->yOffsetPercent = plv->yOffsetPercent;
  9110. }
  9111. return fRet;
  9112. }
  9113. void ListView_FreeSubItem(PLISTSUBITEM plsi)
  9114. {
  9115. if (plsi)
  9116. {
  9117. Str_Set(&plsi->pszText, NULL);
  9118. LocalFree(plsi);
  9119. }
  9120. }
  9121. int ListView_GetCxScrollbar(LV* plv)
  9122. {
  9123. int cx;
  9124. if (((plv->exStyle & LVS_EX_FLATSB) == 0) ||
  9125. !FlatSB_GetScrollProp(plv->ci.hwnd, WSB_PROP_CXVSCROLL, &cx))
  9126. {
  9127. cx = g_cxScrollbar;
  9128. }
  9129. return cx;
  9130. }
  9131. int ListView_GetCyScrollbar(LV* plv)
  9132. {
  9133. int cy;
  9134. if (((plv->exStyle & LVS_EX_FLATSB) == 0) ||
  9135. !FlatSB_GetScrollProp(plv->ci.hwnd, WSB_PROP_CYHSCROLL, &cy))
  9136. {
  9137. cy = g_cyScrollbar;
  9138. }
  9139. return cy;
  9140. }
  9141. DWORD ListView_GetWindowStyle(LV* plv)
  9142. {
  9143. DWORD dwStyle;
  9144. if (((plv->exStyle & LVS_EX_FLATSB) == 0) ||
  9145. !FlatSB_GetScrollProp(plv->ci.hwnd, WSB_PROP_WINSTYLE, (LPINT)&dwStyle))
  9146. {
  9147. dwStyle = GetWindowStyle(plv->ci.hwnd);
  9148. }
  9149. return dwStyle;
  9150. }
  9151. int ListView_SetScrollInfo(LV *plv, int fnBar, LPSCROLLINFO lpsi, BOOL fRedraw)
  9152. {
  9153. int iRc;
  9154. if (plv->exStyle & LVS_EX_FLATSB)
  9155. {
  9156. iRc = FlatSB_SetScrollInfo(plv->ci.hwnd, fnBar, lpsi, fRedraw);
  9157. }
  9158. else
  9159. {
  9160. iRc = SetScrollInfo(plv->ci.hwnd, fnBar, lpsi, fRedraw);
  9161. }
  9162. //
  9163. // You'd think we were finished, but in fact the game is only half over.
  9164. //
  9165. // Some apps (e.g., Font Folder) will do
  9166. //
  9167. // SetWindowLong(hwnd, GWL_STYLE, newStyle);
  9168. //
  9169. // where newStyle toggles the WS_HSCROLL and/or WS_VSCROLL bits.
  9170. // This causes USER's internal bookkeeping to go completely out
  9171. // of whack: The ScrollInfo says that there is a scrollbar, but
  9172. // the window style says there isn't, or vice versa. The result
  9173. // is that we get a scrollbar when we shouldn't or vice versa.
  9174. //
  9175. // So each time we tweak the scroll info in a manner that changes
  9176. // the range and page, we kick USER in the head to make sure USER's
  9177. // view of the world (via style bits) is the same as the scroll
  9178. // bar's view of the world (via SCROLLINFO).
  9179. //
  9180. //
  9181. // We should always change SIF_PAGE and SIF_RANGE at the same time.
  9182. //
  9183. ASSERT((lpsi->fMask & (SIF_PAGE | SIF_RANGE)) == 0 ||
  9184. (lpsi->fMask & (SIF_PAGE | SIF_RANGE)) == (SIF_PAGE | SIF_RANGE));
  9185. if ((lpsi->fMask & (SIF_PAGE | SIF_RANGE)) == (SIF_PAGE | SIF_RANGE))
  9186. {
  9187. BOOL fShow;
  9188. fShow = lpsi->nMax && (int)lpsi->nPage <= lpsi->nMax;
  9189. #ifdef DEBUG
  9190. {
  9191. DWORD dwStyle, dwScroll, dwWant;
  9192. dwScroll = (fnBar == SB_VERT) ? WS_VSCROLL : WS_HSCROLL;
  9193. //
  9194. // We can short-circuit some logic with secret knowledge about how
  9195. // ListView uses SetScrollInfo.
  9196. //
  9197. ASSERT(lpsi->nMin == 0);
  9198. dwWant = fShow ? dwScroll : 0;
  9199. dwStyle = ListView_GetWindowStyle(plv);
  9200. if ((dwStyle & dwScroll) != dwWant)
  9201. {
  9202. TraceMsg(TF_LISTVIEW, "ListView_SetScrollInfo: App twiddled WS_[VH]SCROLL");
  9203. }
  9204. }
  9205. #endif
  9206. if (plv->exStyle & LVS_EX_FLATSB)
  9207. FlatSB_ShowScrollBar(plv->ci.hwnd, fnBar, fShow);
  9208. else
  9209. ShowScrollBar(plv->ci.hwnd, fnBar, fShow);
  9210. }
  9211. return iRc;
  9212. }
  9213. // Add/remove/replace item
  9214. BOOL ListView_FreeItem(LV* plv, LISTITEM* pitem)
  9215. {
  9216. ASSERT(!ListView_IsOwnerData(plv));
  9217. if (pitem)
  9218. {
  9219. if ((pitem->puColumns) && (pitem->cColumns != I_COLUMNSCALLBACK))
  9220. LocalFree(pitem->puColumns);
  9221. Str_Set(&pitem->pszText, NULL);
  9222. if (pitem->hrgnIcon && pitem->hrgnIcon!=(HANDLE)-1)
  9223. DeleteObject(pitem->hrgnIcon);
  9224. // NOTE: We never remove items from the image list; that's
  9225. // the app's responsibility.
  9226. // REVIEW: Should we do this? Or should we just provide
  9227. // a message that will adjust image indices for the guy
  9228. // when one is removed?
  9229. //
  9230. ControlFree(plv->hheap, pitem);
  9231. }
  9232. return FALSE;
  9233. }
  9234. LISTITEM* ListView_CreateItem(LV* plv, const LV_ITEM* plvi)
  9235. {
  9236. LISTITEM* pitem = ControlAlloc(plv->hheap, sizeof(LISTITEM));
  9237. ASSERT(!ListView_IsOwnerData(plv));
  9238. if (pitem)
  9239. {
  9240. if (plvi->mask & LVIF_STATE)
  9241. {
  9242. if (plvi->state & ~LVIS_ALL)
  9243. {
  9244. DebugMsg(DM_ERROR, TEXT("ListView: Invalid state: %04x"), plvi->state);
  9245. return NULL;
  9246. }
  9247. // If adding a selected item to a single-select listview, deselect
  9248. // any other items.
  9249. if ((plv->ci.style & LVS_SINGLESEL) && (plvi->state & LVIS_SELECTED))
  9250. ListView_DeselectAll(plv, -1);
  9251. pitem->state = (plvi->state & ~(LVIS_FOCUSED | LVIS_SELECTED));
  9252. }
  9253. if (plvi->mask & LVIF_PARAM)
  9254. pitem->lParam = plvi->lParam;
  9255. if (plvi->mask & LVIF_IMAGE)
  9256. pitem->iImage = (short) plvi->iImage;
  9257. if (plvi->mask & LVIF_INDENT)
  9258. pitem->iIndent = (short) plvi->iIndent;
  9259. pitem->pt.x = pitem->pt.y = RECOMPUTE;
  9260. ListView_SetSRecompute(pitem);
  9261. pitem->pszText = NULL;
  9262. if (plvi->mask & LVIF_TEXT)
  9263. {
  9264. if (!Str_Set(&pitem->pszText, plvi->pszText))
  9265. {
  9266. ListView_FreeItem(plv, pitem);
  9267. return NULL;
  9268. }
  9269. }
  9270. if ((plvi->mask & LVIF_COLUMNS) && plvi->cColumns)
  9271. {
  9272. pitem->cColumns = plvi->cColumns;
  9273. if (plvi->cColumns != I_COLUMNSCALLBACK)
  9274. {
  9275. // Too many columns, or no column array? Then fail.
  9276. if ((plvi->cColumns > CCMAX_TILE_COLUMNS) || (plvi->puColumns == NULL))
  9277. {
  9278. ListView_FreeItem(plv, pitem);
  9279. return NULL;
  9280. }
  9281. pitem->puColumns = LocalAlloc(LPTR, sizeof(UINT) * pitem->cColumns);
  9282. if (pitem->puColumns == NULL)
  9283. {
  9284. ListView_FreeItem(plv, pitem);
  9285. return NULL;
  9286. }
  9287. CopyMemory(pitem->puColumns, plvi->puColumns, sizeof(UINT) * pitem->cColumns);
  9288. }
  9289. }
  9290. else
  9291. {
  9292. pitem->cColumns = 0;
  9293. pitem->puColumns = NULL;
  9294. }
  9295. pitem->dwId = plv->idNext++; // This may overflow. How to deal?
  9296. }
  9297. return pitem;
  9298. }
  9299. // HACK ALERT!! -- fSmoothScroll is an added parameter! It allows for smooth
  9300. // scrolling when deleting items. ListView_LRInvalidateBelow is only currently
  9301. // called from ListView_OnUpdate and ListView_OnDeleteItem. Both these calls
  9302. // have been modified to work correctly and be backwards compatible.
  9303. //
  9304. void ListView_LRInvalidateBelow(LV* plv, int i, int fSmoothScroll)
  9305. {
  9306. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  9307. {
  9308. RECT rcItem;
  9309. if (!ListView_RedrawEnabled(plv) ||
  9310. (ListView_IsReportView(plv) && (plv->pImgCtx != NULL)))
  9311. fSmoothScroll = FALSE;
  9312. if (i >= 0)// && i < ListView_Count(plv))
  9313. {
  9314. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, &rcItem, NULL);
  9315. }
  9316. else
  9317. {
  9318. rcItem.left = rcItem.top = 0;
  9319. rcItem.right = plv->sizeClient.cx;
  9320. rcItem.bottom = plv->sizeClient.cy;
  9321. }
  9322. // Don't try to scroll over the header part
  9323. if (ListView_IsReportView(plv) && rcItem.top < plv->yTop)
  9324. rcItem.top = plv->yTop;
  9325. // For both List and report view need to erase the item and
  9326. // below. Note: do simple test to see if there is anything
  9327. // to redraw
  9328. // we can't check for bottom/right > 0 because if we nuked something
  9329. // above or to the left of the view, it may affect us all
  9330. if ((rcItem.top <= plv->sizeClient.cy) &&
  9331. (rcItem.left <= plv->sizeClient.cx))
  9332. {
  9333. rcItem.bottom = plv->sizeClient.cy;
  9334. if (ListView_RedrawEnabled(plv))
  9335. {
  9336. if ((plv->clrBk == CLR_NONE) && (plv->pImgCtx == NULL))
  9337. {
  9338. LVSeeThruScroll(plv, &rcItem);
  9339. }
  9340. else if (ListView_IsReportView(plv) && fSmoothScroll)
  9341. {
  9342. SMOOTHSCROLLINFO si =
  9343. {
  9344. sizeof(si),
  9345. SSIF_MINSCROLL,
  9346. plv->ci.hwnd,
  9347. 0,
  9348. -(plv->cyItem),
  9349. &rcItem,
  9350. &rcItem,
  9351. NULL,
  9352. NULL,
  9353. SW_INVALIDATE|SW_ERASE,
  9354. SSI_DEFAULT,
  9355. 1,
  9356. 1,
  9357. };
  9358. SmoothScrollWindow(&si);
  9359. }
  9360. else
  9361. {
  9362. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  9363. }
  9364. }
  9365. else
  9366. {
  9367. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  9368. }
  9369. if (ListView_IsListView(plv))
  9370. {
  9371. RECT rcClient;
  9372. // For Listview we need to erase the other columns...
  9373. rcClient.left = rcItem.right;
  9374. rcClient.top = 0;
  9375. rcClient.bottom = plv->sizeClient.cy;
  9376. rcClient.right = plv->sizeClient.cx;
  9377. RedrawWindow(plv->ci.hwnd, &rcClient, NULL, RDW_INVALIDATE | RDW_ERASE);
  9378. }
  9379. }
  9380. }
  9381. }
  9382. // Used in Ownerdata Icon views to try to not invalidate the whole world...
  9383. void ListView_IInvalidateBelow(LV* plv, int i)
  9384. {
  9385. RECT rcItem;
  9386. if (i >= 0)
  9387. {
  9388. ListView_GetRects(plv, i, QUERY_DEFAULT, NULL, NULL, &rcItem, NULL);
  9389. }
  9390. else
  9391. {
  9392. rcItem.left = rcItem.top = 0;
  9393. rcItem.right = plv->sizeClient.cx;
  9394. rcItem.bottom = plv->sizeClient.cy;
  9395. }
  9396. // For Iconviews we need to invalidate everything to the right of us in this
  9397. // row and everything below the row...
  9398. // below. Note: do simple test to see if there is anything
  9399. // to redraw
  9400. if ((rcItem.top <= plv->sizeClient.cy) &&
  9401. (rcItem.left <= plv->sizeClient.cx))
  9402. {
  9403. rcItem.right = plv->sizeClient.cx;
  9404. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  9405. // Now erase everything below...
  9406. rcItem.top = rcItem.bottom;
  9407. rcItem.bottom = plv->sizeClient.cy;
  9408. rcItem.left = 0;
  9409. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  9410. }
  9411. }
  9412. void ListView_OnUpdate(LV* plv, int i)
  9413. {
  9414. // If in icon/small view, don't call InvalidateItem, since that'll force
  9415. // FindFreeSlot to get called, which is pig-like. Instead, just
  9416. // force a WM_PAINT message, which we'll catch and call Recompute with.
  9417. //
  9418. if (ListView_IsAutoArrangeView(plv))
  9419. {
  9420. ListView_ArrangeOrSnapToGrid(plv);
  9421. if (!(plv->ci.style & LVS_AUTOARRANGE))
  9422. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INTERNALPAINT | RDW_NOCHILDREN);
  9423. }
  9424. else
  9425. {
  9426. // HACK ALERT!! -- The third parameter is new. It allows for
  9427. // smooth scrolling when items are deleted in reportview.
  9428. // Passing 0, tells it NOT to scroll.
  9429. //
  9430. ListView_LRInvalidateBelow(plv, i, 0);
  9431. }
  9432. ListView_UpdateScrollBars(plv);
  9433. }
  9434. int ListView_OnInsertItemA(LV* plv, LV_ITEMA* plvi)
  9435. {
  9436. LPWSTR pszW = NULL;
  9437. LPSTR pszC = NULL;
  9438. int iRet;
  9439. //HACK ALERT -- this code assumes that LV_ITEMA is exactly the same
  9440. // as LV_ITEMW except for the pointer to the string.
  9441. COMPILETIME_ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW));
  9442. if (!plvi)
  9443. {
  9444. return -1;
  9445. }
  9446. if ((plvi->mask & LVIF_TEXT) && (plvi->pszText != NULL))
  9447. {
  9448. pszC = plvi->pszText;
  9449. pszW = ProduceWFromA(plv->ci.uiCodePage, pszC);
  9450. if (pszW == NULL)
  9451. return -1;
  9452. plvi->pszText = (LPSTR)pszW;
  9453. }
  9454. iRet = ListView_OnInsertItem(plv, (const LV_ITEM*) plvi);
  9455. if (pszW != NULL)
  9456. {
  9457. plvi->pszText = pszC;
  9458. FreeProducedString(pszW);
  9459. }
  9460. return iRet;
  9461. }
  9462. int ListView_OnInsertItem(LV* plv, const LV_ITEM* plvi)
  9463. {
  9464. int i;
  9465. ListView_InsertItemInternal(plv, plvi, &i);
  9466. return i;
  9467. }
  9468. LISTITEM* ListView_InsertItemInternal(LV* plv, const LV_ITEM* plvi, int* pi)
  9469. {
  9470. int iItem;
  9471. LISTITEM *pitem = NULL;
  9472. *pi = -1;
  9473. if (plvi == NULL)
  9474. {
  9475. RIPMSG(0, "ListView_InsertItem: Do not pass a NULL LV_ITEM.");
  9476. return NULL;
  9477. }
  9478. if (plvi->iSubItem != 0) // can only insert the 0th item
  9479. {
  9480. RIPMSG(0, "ListView_InsertItem: iSubItem must be 0 (app passed %d)", plvi->iSubItem);
  9481. return NULL;
  9482. }
  9483. // If sorted, then insert sorted.
  9484. //
  9485. if (plv->ci.style & (LVS_SORTASCENDING | LVS_SORTDESCENDING)
  9486. && !ListView_IsOwnerData(plv))
  9487. {
  9488. if (plvi->pszText == LPSTR_TEXTCALLBACK)
  9489. {
  9490. DebugMsg(DM_ERROR, TEXT("Don't use LPSTR_TEXTCALLBACK with LVS_SORTASCENDING or LVS_SORTDESCENDING"));
  9491. return NULL;
  9492. }
  9493. iItem = ListView_LookupString(plv, plvi->pszText, LVFI_SUBSTRING | LVFI_NEARESTXY, 0);
  9494. }
  9495. else
  9496. iItem = plvi->iItem;
  9497. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  9498. if (!ListView_IsOwnerData(plv))
  9499. {
  9500. int iZ;
  9501. static s_blah = 0;
  9502. UINT uSelMask = plvi->mask & LVIF_STATE ?
  9503. (plvi->state & (LVIS_FOCUSED | LVIS_SELECTED))
  9504. : 0;
  9505. UINT uSel = uSelMask;
  9506. pitem = ListView_CreateItem(plv, plvi);
  9507. if (!pitem)
  9508. return NULL;
  9509. iItem = DPA_InsertPtr(plv->hdpa, iItem, pitem);
  9510. if (iItem == -1)
  9511. {
  9512. ListView_FreeItem(plv, pitem);
  9513. return NULL;
  9514. }
  9515. plv->cTotalItems++;
  9516. if (plv->hdpaSubItems)
  9517. {
  9518. int iCol;
  9519. // slide all the colum DPAs down to match the location of the
  9520. // inserted item
  9521. //
  9522. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  9523. {
  9524. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  9525. if (hdpa) // this is optional, call backs don't have them
  9526. {
  9527. // insert a blank item (REVIEW: should this be callback?)
  9528. // since this can be a tail sparce array,
  9529. // we need to make sure enough items are there.
  9530. if (iItem >= DPA_GetPtrCount(hdpa))
  9531. DPA_SetPtr(hdpa, iItem, NULL);
  9532. else if (DPA_InsertPtr(hdpa, iItem, NULL) != iItem)
  9533. goto Failure;
  9534. // Bad assert since hdpa can be tail sparse
  9535. // ASSERT(ListView_Count(plv) == DPA_GetPtrCount(hdpa));
  9536. ASSERT(ListView_Count(plv) >= DPA_GetPtrCount(hdpa));
  9537. }
  9538. }
  9539. }
  9540. // Add item to end of z order
  9541. //
  9542. iZ = DPA_InsertPtr(plv->hdpaZOrder, ListView_Count(plv), IntToPtr(iItem));
  9543. if (iZ == -1)
  9544. {
  9545. Failure:
  9546. DebugMsg(TF_LISTVIEW, TEXT("ListView_OnInsertItem() failed"));
  9547. if (DPA_DeletePtr(plv->hdpa, iItem))
  9548. plv->cTotalItems--;
  9549. ListView_FreeItem(plv, pitem);
  9550. return NULL;
  9551. }
  9552. // if we inserted before the focus point, move the focus point up one
  9553. if (iItem <= plv->iFocus)
  9554. plv->iFocus++;
  9555. // do the same thing for the mark
  9556. if (iItem <= plv->iMark)
  9557. plv->iMark++;
  9558. // If the item was not added at the end of the list we need
  9559. // to update the other indexes in the list
  9560. if (iItem != ListView_Count(plv) - 1)
  9561. {
  9562. int i2;
  9563. for (i2 = iZ - 1; i2 >= 0; i2--)
  9564. {
  9565. int iItemZ = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, i2);
  9566. if (iItemZ >= iItem)
  9567. DPA_SetPtr(plv->hdpaZOrder, i2, (void *)(UINT_PTR)(iItemZ + 1));
  9568. }
  9569. }
  9570. if (ListView_CheckBoxes(plv))
  9571. {
  9572. uSelMask |= LVIS_STATEIMAGEMASK;
  9573. uSel |= INDEXTOSTATEIMAGEMASK(1);
  9574. }
  9575. if (uSelMask)
  9576. {
  9577. // we masked off these in the createitem above.
  9578. // because turning these on means more than setting the bits.
  9579. ListView_OnSetItemState(plv, iItem, uSel, uSelMask);
  9580. }
  9581. if (plvi->mask & LVIF_GROUPID)
  9582. {
  9583. int iGroupId = plvi->iGroupId;
  9584. if (iGroupId == I_GROUPIDNONE)
  9585. {
  9586. LISTITEM_SETASKEDFORGROUP(pitem);
  9587. }
  9588. else if (iGroupId != I_GROUPIDCALLBACK)
  9589. {
  9590. LISTGROUP* pgrp = ListView_FindGroupFromID(plv, iGroupId, NULL);
  9591. if (!pgrp)
  9592. {
  9593. ListView_FreeItem(plv, pitem);
  9594. return NULL;
  9595. }
  9596. pitem->pGroup = pgrp;
  9597. DPA_AppendPtr(pgrp->hdpa, pitem);
  9598. }
  9599. }
  9600. else
  9601. {
  9602. LISTITEM_SETHASNOTASKEDFORGROUP(pitem);
  9603. }
  9604. if (plv->fGroupView && (plv->flags & LVF_REDRAW))
  9605. {
  9606. _ListView_RecomputeEx(plv, NULL, 0, FALSE);
  9607. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9608. }
  9609. }
  9610. else
  9611. {
  9612. //
  9613. // simply adjust selection and count
  9614. //
  9615. if ((iItem >= 0) && (iItem <= MAX_LISTVIEWITEMS))
  9616. {
  9617. if (FAILED(plv->plvrangeSel->lpVtbl->InsertItem(plv->plvrangeSel, iItem)))
  9618. {
  9619. return NULL;
  9620. }
  9621. plv->cTotalItems++;
  9622. plv->rcView.left = RECOMPUTE;
  9623. ListView_Recompute(plv);
  9624. if (!ListView_IsReportView(plv) && !ListView_IsListView(plv))
  9625. {
  9626. // We need to erase the background so that we don't leave
  9627. // turds from wrapped labels in large icon mode. This could
  9628. // be optimized by only invalidating to the right of and
  9629. // below the inserted item.
  9630. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9631. }
  9632. // if we inserted before the focus point, move the focus point up
  9633. if (iItem <= plv->iFocus)
  9634. plv->iFocus++;
  9635. // do the same thing for the mark
  9636. if (iItem <= plv->iMark)
  9637. plv->iMark++;
  9638. }
  9639. }
  9640. if (!ListView_IsOwnerData(plv))
  9641. {
  9642. ASSERT(ListView_Count(plv) == DPA_GetPtrCount(plv->hdpaZOrder));
  9643. }
  9644. if (ListView_RedrawEnabled(plv))
  9645. {
  9646. // Update region
  9647. ListView_RecalcRegion(plv, TRUE, TRUE);
  9648. // The Maybe resize colmns may resize things in which case the next call
  9649. // to Update is not needed.
  9650. if (!ListView_MaybeResizeListColumns(plv, iItem, iItem))
  9651. ListView_OnUpdate(plv, iItem);
  9652. // this trick makes inserting lots of items cheap
  9653. // even if redraw is enabled.... don't calc or position items
  9654. // until this postmessage comes around
  9655. if (!plv->uUnplaced)
  9656. {
  9657. PostMessage(plv->ci.hwnd, LVMI_PLACEITEMS, 0, 0);
  9658. }
  9659. plv->uUnplaced++;
  9660. }
  9661. else
  9662. {
  9663. //
  9664. // Special case code to make using SetRedraw work reasonably well
  9665. // for adding items to a listview which is in a non layout mode...
  9666. //
  9667. if ((plv->iFirstChangedNoRedraw == -1) ||
  9668. (iItem < plv->iFirstChangedNoRedraw))
  9669. plv->iFirstChangedNoRedraw = iItem;
  9670. }
  9671. // Nuke insertmark... it may be invalid now that an item has been added.
  9672. {
  9673. LVINSERTMARK lvim = {0};
  9674. lvim.cbSize = sizeof(LVINSERTMARK);
  9675. lvim.iItem = -1;
  9676. ListView_OnSetInsertMark(plv, (LPLVINSERTMARK)&lvim);
  9677. }
  9678. ListView_Notify(plv, iItem, 0, LVN_INSERTITEM);
  9679. NotifyWinEvent(EVENT_OBJECT_CREATE, plv->ci.hwnd, OBJID_CLIENT, iItem+1);
  9680. *pi = iItem;
  9681. return pitem;
  9682. }
  9683. BOOL ListView_OnDeleteItem(LV* plv, int iItem)
  9684. {
  9685. int iCount = ListView_Count(plv);
  9686. if (!ListView_IsValidItemNumber(plv, iItem))
  9687. return FALSE; // out of range
  9688. NotifyWinEvent(EVENT_OBJECT_DESTROY, plv->ci.hwnd, OBJID_CLIENT, iItem+1);
  9689. ListView_DismissEdit(plv, TRUE); // cancel edits
  9690. ListView_OnSetItemState(plv, iItem, 0, LVIS_SELECTED);
  9691. if (plv->iFocus == iItem)
  9692. ListView_OnSetItemState(plv, (iItem == iCount - 1 ? iItem - 1 : iItem + 1), LVIS_FOCUSED, LVIS_FOCUSED);
  9693. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  9694. if (!ListView_IsOwnerData(plv))
  9695. {
  9696. LISTITEM* pitem = ListView_FastGetItemPtr(plv, iItem);
  9697. int iZ;
  9698. if ((plv->rcView.left != RECOMPUTE) && ListView_IsSlotView(plv))
  9699. {
  9700. if (LV_IsItemOnViewEdge(plv, pitem))
  9701. {
  9702. plv->rcView.left = RECOMPUTE;
  9703. }
  9704. }
  9705. ListView_RemoveItemFromItsGroup(plv, pitem);
  9706. // We don't need to invalidate the item in report view because we
  9707. // will be scrolling on top of it.
  9708. //
  9709. if (!ListView_IsReportView(plv))
  9710. ListView_InvalidateItem(plv, iItem, FALSE, RDW_INVALIDATE | RDW_ERASE);
  9711. // this notify must be done AFTER the Invalidate because some items need callbacks
  9712. // to calculate the rect, but the notify might free it out
  9713. ListView_Notify(plv, iItem, 0, LVN_DELETEITEM);
  9714. // During the notify, the app might've done something to the listview
  9715. // so revalidate the item number pointer so we don't fault
  9716. #ifdef DEBUG
  9717. // Validate internally because DPA_DeletePtr will ASSERT if you ask it
  9718. // to delete something that doesn't exist.
  9719. if (!ListView_IsValidItemNumber(plv, iItem))
  9720. pitem = NULL;
  9721. else
  9722. #endif
  9723. pitem = DPA_DeletePtr(plv->hdpa, iItem);
  9724. if (!pitem)
  9725. {
  9726. RIPMSG(0, "Something strange happened during LVN_DELETEITEM; abandoning LVM_DELETEITEM");
  9727. return FALSE;
  9728. }
  9729. plv->cTotalItems = DPA_GetPtrCount(plv->hdpa);
  9730. // remove from the z-order, this is a lisearch to find this!
  9731. DPA_DeletePtr(plv->hdpaZOrder, ListView_ZOrderIndex(plv, iItem));
  9732. //
  9733. // As the Z-order hdpa is a set of indexes we also need to decrement
  9734. // all indexes that exceed the one we are deleting.
  9735. //
  9736. for (iZ = ListView_Count(plv) - 1; iZ >= 0; iZ--)
  9737. {
  9738. int iItemZ = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, iZ);
  9739. if (iItemZ > iItem)
  9740. DPA_SetPtr(plv->hdpaZOrder, iZ, IntToPtr(iItemZ - 1));
  9741. }
  9742. // remove from sub item DPAs if necessary
  9743. if (plv->hdpaSubItems)
  9744. {
  9745. int iCol;
  9746. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  9747. {
  9748. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  9749. if (hdpa)
  9750. { // this is optional, call backs don't have them
  9751. PLISTSUBITEM plsi;
  9752. // These DPAs are tail sparse, so don't get upset if we
  9753. // try to delete something that's past the end of the list
  9754. #ifdef DEBUG
  9755. plsi = iItem < DPA_GetPtrCount(hdpa) ? DPA_DeletePtr(hdpa, iItem) : NULL;
  9756. #else
  9757. plsi = DPA_DeletePtr(hdpa, iItem);
  9758. #endif
  9759. ListView_FreeSubItem(plsi);
  9760. }
  9761. }
  9762. }
  9763. ListView_FreeItem(plv, pitem); // ... finaly the item pointer
  9764. if (plv->fGroupView && (plv->flags & LVF_REDRAW))
  9765. {
  9766. _ListView_RecomputeEx(plv, NULL, 0, TRUE);
  9767. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9768. }
  9769. }
  9770. else
  9771. {
  9772. //
  9773. // simply notify and then fixup selection state and count
  9774. //
  9775. if ((iItem >= 0) && (iItem <= MAX_LISTVIEWITEMS))
  9776. {
  9777. ListView_Notify(plv, iItem, 0, LVN_DELETEITEM);
  9778. if (FAILED(plv->plvrangeSel->lpVtbl->RemoveItem(plv->plvrangeSel, iItem)))
  9779. {
  9780. SetLastError(ERROR_OUTOFMEMORY);
  9781. return FALSE;
  9782. }
  9783. plv->cTotalItems--;
  9784. plv->rcView.left = RECOMPUTE;
  9785. ListView_Recompute(plv);
  9786. if (!ListView_IsReportView(plv) && !ListView_IsListView(plv))
  9787. {
  9788. // We need to erase the background so that the last item gets
  9789. // erased in both icon modes and so that we don't leave turds
  9790. // from wrapped labels in large icon mode. This could be
  9791. // optimized by only invalidating to the right of and below
  9792. // the deleted item.
  9793. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9794. }
  9795. }
  9796. else
  9797. {
  9798. return FALSE;
  9799. }
  9800. }
  9801. iCount = ListView_Count(plv); // regrab count incase someone updated item...
  9802. if (!ListView_IsOwnerData(plv))
  9803. {
  9804. ASSERT(ListView_Count(plv) == DPA_GetPtrCount(plv->hdpaZOrder));
  9805. }
  9806. if (plv->iFocus == iItem)
  9807. {
  9808. if (plv->iFocus >= iCount)
  9809. {
  9810. plv->iFocus = iCount - 1;
  9811. }
  9812. }
  9813. if (plv->iFocus > iItem)
  9814. {
  9815. plv->iFocus--; // slide the focus index down
  9816. }
  9817. // same with the mark
  9818. if (plv->iMark == iItem)
  9819. {
  9820. // deleted the mark item
  9821. if (plv->iMark >= iCount) // did we nuke the last item?
  9822. plv->iMark = iCount - 1;
  9823. }
  9824. else if (plv->iMark > iItem)
  9825. plv->iMark--; // slide the mark index down
  9826. // Free up the hot item
  9827. if (plv->iHot == iItem)
  9828. plv->iHot = -1;
  9829. // Deleting an icon invalidates the icon positioning cache
  9830. plv->iFreeSlot = -1;
  9831. // HACK ALERT!! -- This construct with ReportView steals code from
  9832. // ListView_OnUpdate. Currently, it will work exactly the same as before,
  9833. // EXCEPT, that it won't call ListView_OnUpdate. This is to allow us to
  9834. // send a flag to ListView_LRUpdateBelow to tell it we're scrolling up.
  9835. //
  9836. if (ListView_IsReportView(plv))
  9837. {
  9838. // if the new count is zero and we will be showing empty text, simply invalidate the
  9839. // rect and redraw, else go through the invalidate below code...
  9840. // we don't know if we are going to show empty text if pszEmptyText is NULL, or not
  9841. // because we may get one through notify, so if iCount is 0 invalidate everything
  9842. if (iCount == 0)
  9843. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  9844. else
  9845. ListView_LRInvalidateBelow(plv,iItem,1);
  9846. if (ListView_RedrawEnabled(plv))
  9847. ListView_UpdateScrollBars(plv);
  9848. else {
  9849. //
  9850. // Special case code to make using SetRedraw work reasonably well
  9851. // for adding items to a listview which is in a non layout mode...
  9852. //
  9853. if ((plv->iFirstChangedNoRedraw != -1) && (iItem < plv->iFirstChangedNoRedraw))
  9854. plv->iFirstChangedNoRedraw--;
  9855. }
  9856. }
  9857. else
  9858. {
  9859. if (ListView_RedrawEnabled(plv))
  9860. ListView_OnUpdate(plv, iItem);
  9861. else
  9862. {
  9863. ListView_LRInvalidateBelow(plv, iItem, 0);
  9864. //
  9865. // Special case code to make using SetRedraw work reasonably well
  9866. // for adding items to a listview which is in a non layout mode...
  9867. //
  9868. if ((plv->iFirstChangedNoRedraw != -1) && (iItem < plv->iFirstChangedNoRedraw))
  9869. plv->iFirstChangedNoRedraw--;
  9870. }
  9871. }
  9872. ListView_RecalcRegion(plv, TRUE, TRUE);
  9873. return TRUE;
  9874. }
  9875. void ListView_DeleteAllGroupItems(LV* plv)
  9876. {
  9877. if (plv->hdpaGroups)
  9878. {
  9879. int iGroup, cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  9880. for (iGroup = 0; iGroup < cGroups; iGroup++)
  9881. {
  9882. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  9883. DPA_Destroy(pgrp->hdpa);
  9884. pgrp->hdpa = DPA_Create(5);
  9885. }
  9886. }
  9887. }
  9888. BOOL ListView_OnDeleteAllItems(LV* plv)
  9889. {
  9890. int i;
  9891. BOOL bAlreadyNotified;
  9892. BOOL fHasItemData = !ListView_IsOwnerData(plv);
  9893. ListView_DismissEdit(plv, TRUE); // cancel edits
  9894. ListView_DeleteAllGroupItems(plv);
  9895. // Must neutralize the focus because some apps will call
  9896. // ListView_OnGetNextItem(LVNI_FOCUSED) during delete notifications,
  9897. // so we need to make sure the focus is in a safe place.
  9898. // May as well neutralize the mark, too.
  9899. plv->iMark = plv->iFocus = -1;
  9900. // Also nuke the icon positioning cache
  9901. plv->iFreeSlot = -1;
  9902. // Since we delete all items, There is no insertion slot!
  9903. plv->iInsertItem = -1;
  9904. bAlreadyNotified = (BOOL)ListView_Notify(plv, -1, 0, LVN_DELETEALLITEMS);
  9905. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  9906. if (fHasItemData || !bAlreadyNotified)
  9907. {
  9908. for (i = ListView_Count(plv) - 1; i >= 0; i--)
  9909. {
  9910. if (!bAlreadyNotified)
  9911. ListView_Notify(plv, i, 0, LVN_DELETEITEM);
  9912. if (fHasItemData)
  9913. {
  9914. ListView_FreeItem(plv, ListView_FastGetItemPtr(plv, i));
  9915. //
  9916. // CAREFUL! Applications such as NT Backup call back
  9917. // into ListView during the LVN_DELETEITEM notification,
  9918. // so we need to kill this item or we will fault at the
  9919. // next iteration because everybody relies on
  9920. // ListView_Count for validation.
  9921. //
  9922. DPA_FastDeleteLastPtr(plv->hdpa);
  9923. plv->cTotalItems--;
  9924. }
  9925. }
  9926. }
  9927. if (ListView_IsOwnerData(plv))
  9928. {
  9929. if (FAILED(plv->plvrangeSel->lpVtbl->Clear(plv->plvrangeSel)))
  9930. {
  9931. SetLastError(ERROR_OUTOFMEMORY);
  9932. }
  9933. plv->cTotalItems = 0;
  9934. }
  9935. else
  9936. {
  9937. DPA_DeleteAllPtrs(plv->hdpa);
  9938. DPA_DeleteAllPtrs(plv->hdpaZOrder);
  9939. plv->cTotalItems = 0;
  9940. if (plv->hdpaSubItems)
  9941. {
  9942. int iCol;
  9943. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  9944. {
  9945. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  9946. if (hdpa)
  9947. {
  9948. DPA_EnumCallback(hdpa, ListView_FreeColumnData, 0);
  9949. DPA_DeleteAllPtrs(hdpa);
  9950. }
  9951. }
  9952. }
  9953. }
  9954. plv->rcView.left = RECOMPUTE;
  9955. plv->xOrigin = 0;
  9956. plv->nSelected = 0;
  9957. plv->ptlRptOrigin.x = 0;
  9958. plv->ptlRptOrigin.y = 0;
  9959. // reset the cxItem width
  9960. if (!(plv->flags & LVF_COLSIZESET))
  9961. {
  9962. plv->cxItem = ListView_ComputeCXItemSize(plv);
  9963. }
  9964. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  9965. ListView_UpdateScrollBars(plv);
  9966. return TRUE;
  9967. }
  9968. int ListView_IFindNearestItem(LV* plv, int left, int top, UINT vk)
  9969. {
  9970. int iMin = -1;
  9971. if (ListView_IsOwnerData(plv))
  9972. {
  9973. POINT pt;
  9974. int cSlots;
  9975. int iWidth = 0, iHeight = 0;
  9976. ASSERT(!ListView_IsReportView(plv) && !ListView_IsListView(plv));
  9977. pt.x = left + plv->ptOrigin.x;
  9978. pt.y = top + plv->ptOrigin.y;
  9979. cSlots = ListView_GetSlotCount(plv, TRUE, &iWidth, &iHeight);
  9980. iMin = ListView_CalcHitSlot(plv, pt, cSlots, iWidth, iHeight);
  9981. switch(vk)
  9982. {
  9983. case VK_HOME:
  9984. iMin = 0;
  9985. break;
  9986. case VK_END:
  9987. iMin = ListView_Count(plv) - 1;
  9988. break;
  9989. case VK_LEFT:
  9990. if (iMin % cSlots)
  9991. iMin -= 1;
  9992. break;
  9993. case VK_RIGHT:
  9994. if ((iMin + 1) % cSlots)
  9995. iMin += 1;
  9996. break;
  9997. case VK_UP:
  9998. if (iMin >= cSlots)
  9999. iMin -= cSlots;
  10000. break;
  10001. case VK_DOWN:
  10002. if (iMin + cSlots < ListView_Count(plv))
  10003. iMin += cSlots;
  10004. break;
  10005. default: ;
  10006. }
  10007. iMin = max(0, iMin);
  10008. iMin = min(ListView_Count(plv) - 1, iMin);
  10009. }
  10010. else
  10011. {
  10012. ULONGLONG dMin = 0;
  10013. int cyItem;
  10014. int yEnd = 0, yLimit = 0, xEnd = 0;
  10015. int iCount;
  10016. int i;
  10017. if (ListView_UseLargeIcons(plv))
  10018. {
  10019. cyItem = plv->cyIcon;
  10020. }
  10021. else
  10022. {
  10023. cyItem = plv->cyItem;
  10024. }
  10025. iCount = ListView_Count(plv);
  10026. if (iCount == 1)
  10027. return 0;
  10028. if (vk == VK_HOME)
  10029. {
  10030. yEnd = yLimit = plv->rcView.bottom;
  10031. xEnd = plv->rcView.right;
  10032. }
  10033. else if (vk == VK_END)
  10034. {
  10035. yEnd = yLimit = plv->rcView.top;
  10036. xEnd = plv->rcView.left;
  10037. }
  10038. for (i = 0; i < iCount; i++)
  10039. {
  10040. RECT rc;
  10041. int dx;
  10042. ULONGLONG dxAbs, dyAbs, dOffset;
  10043. int dy;
  10044. ListView_GetRects(plv, i, QUERY_DEFAULT, &rc, NULL, NULL, NULL);
  10045. dx = rc.left - left;
  10046. dxAbs = (ULONGLONG)(dx < 0 ? -dx : dx);
  10047. dy = rc.top - top;
  10048. dyAbs = (ULONGLONG)(dy < 0 ? -dy : dy);
  10049. if ((vk == VK_LEFT) && (dxAbs < dyAbs || dx >= 0))
  10050. continue;
  10051. else if ((vk == VK_RIGHT) && (dxAbs < dyAbs || dx <= 0))
  10052. continue;
  10053. else if ((vk == VK_UP) && (dxAbs > dyAbs || dy >= 0))
  10054. continue;
  10055. else if ((vk == VK_DOWN) && (dxAbs > dyAbs || dy <= 0))
  10056. continue;
  10057. if (vk == VK_HOME || vk == VK_END)
  10058. {
  10059. // home is not the nearest to the top corner, it's the leftmost of the top row.
  10060. // ditto (reversed) for end. thus we can't use the stuff below. bummer
  10061. if (vk == VK_HOME)
  10062. {
  10063. if ((rc.top + cyItem < yEnd) || // if it's fully above the highest line so, take it!
  10064. ((rc.top < yLimit) && // if it's on the same row as the top item to date
  10065. (rc.left < xEnd)))
  10066. {
  10067. iMin = i;
  10068. xEnd = rc.left;
  10069. yEnd = rc.top;
  10070. if (rc.top + cyItem < yLimit)
  10071. yLimit = rc.top + cyItem;
  10072. }
  10073. }
  10074. else
  10075. {
  10076. if ((rc.top > yEnd) || //if it's full below the lowest row
  10077. ((rc.top + cyItem > yLimit) && // if it's on the same row
  10078. (rc.right > xEnd)))
  10079. {
  10080. iMin = i;
  10081. xEnd = rc.right;
  10082. yEnd = rc.top;
  10083. if (rc.top > yLimit)
  10084. yLimit = rc.top;
  10085. }
  10086. }
  10087. }
  10088. else
  10089. {
  10090. dOffset = ((dxAbs * dxAbs) + (dyAbs * dyAbs));
  10091. if (iMin == -1 || (dMin > dOffset))
  10092. {
  10093. dMin = dOffset;
  10094. iMin = i;
  10095. }
  10096. }
  10097. }
  10098. }
  10099. return iMin;
  10100. }
  10101. int ListView_Arrow(LV* plv, int iStart, UINT vk)
  10102. {
  10103. RECT rcFocus;
  10104. int i;
  10105. int dx;
  10106. int iCount;
  10107. //
  10108. // The algorithm to find which item depends if we are in a view
  10109. // that is arrange(layout) oriented or a sorted (list) view.
  10110. // For the sorted views we will use some optimizations to make
  10111. // it faster
  10112. //
  10113. iCount = ListView_Count(plv);
  10114. if ((ListView_IsReportView(plv) || ListView_IsListView(plv)) && !plv->fGroupView)
  10115. {
  10116. //
  10117. // For up and down arrows, simply increment or decrement the
  10118. // index. Note: in listview this will cause it to wrap columns
  10119. // which is fine as it is compatible with the file manager
  10120. //
  10121. // Assumes only one of these flags is set...
  10122. switch (vk)
  10123. {
  10124. case VK_LEFT:
  10125. if (ListView_IsReportView(plv))
  10126. {
  10127. ListView_ROnScroll(plv, (GetAsyncKeyState(VK_CONTROL) < 0) ? SB_PAGELEFT : SB_LINELEFT, 0, SB_HORZ);
  10128. }
  10129. else
  10130. iStart -= plv->cItemCol;
  10131. break;
  10132. case VK_RIGHT:
  10133. if (ListView_IsReportView(plv))
  10134. {
  10135. // Make this horizontally scroll the report view
  10136. ListView_ROnScroll(plv, (GetAsyncKeyState(VK_CONTROL) < 0) ? SB_PAGERIGHT : SB_LINERIGHT, 0, SB_HORZ);
  10137. }
  10138. else
  10139. iStart += plv->cItemCol;
  10140. break;
  10141. case VK_UP:
  10142. iStart--;
  10143. break;
  10144. case VK_DOWN:
  10145. iStart++;
  10146. break;
  10147. case VK_HOME:
  10148. iStart = 0;
  10149. break;
  10150. case VK_END:
  10151. iStart = iCount -1;
  10152. break;
  10153. case VK_NEXT:
  10154. if (ListView_IsReportView(plv))
  10155. {
  10156. i = iStart; // save away to make sure we dont go wrong way!
  10157. // First go to end of page...
  10158. iStart = (int)(((LONG)(plv->sizeClient.cy - (plv->cyItem)
  10159. - plv->yTop) + plv->ptlRptOrigin.y) / plv->cyItem);
  10160. // If Same item, increment by page size.
  10161. if (iStart <= i)
  10162. iStart = i + max(
  10163. (plv->sizeClient.cy - plv->yTop)/ plv->cyItem - 1,
  10164. 1);
  10165. if (iStart >= iCount)
  10166. iStart = iCount - 1;
  10167. }
  10168. else
  10169. {
  10170. // multiply by 2/3 to give a good feel.. when the item is mostly shown
  10171. // you want to go to the next column
  10172. dx = (plv->sizeClient.cx + (plv->cxItem*2)/3) / plv->cxItem;
  10173. if (!dx)
  10174. dx = 1;
  10175. iStart += plv->cItemCol * dx;
  10176. if (plv->cItemCol)
  10177. {
  10178. while (iStart >= iCount)
  10179. iStart -= plv->cItemCol;
  10180. }
  10181. }
  10182. break;
  10183. case VK_PRIOR:
  10184. if (ListView_IsReportView(plv))
  10185. {
  10186. i = iStart; // save away to make sure we dont go wrong way!
  10187. // First go to end of page...
  10188. iStart = (int)(plv->ptlRptOrigin.y / plv->cyItem);
  10189. // If Same item, increment by page size.
  10190. if (iStart >= i)
  10191. iStart = i - max(
  10192. (plv->sizeClient.cy - plv->yTop)/ plv->cyItem - 1,
  10193. 1);
  10194. if (iStart < 0)
  10195. iStart = 0;
  10196. }
  10197. else
  10198. {
  10199. dx = (plv->sizeClient.cx + (plv->cxItem*2)/3) / plv->cxItem;
  10200. if (!dx)
  10201. dx = 1;
  10202. iStart -= plv->cItemCol * dx;
  10203. if (plv->cItemCol)
  10204. {
  10205. while (iStart < 0)
  10206. iStart += plv->cItemCol;
  10207. }
  10208. }
  10209. break;
  10210. default:
  10211. return -1; // Out of range
  10212. }
  10213. // Make sure it is in range!.
  10214. if ((iStart >= 0) && (iStart < iCount))
  10215. return iStart;
  10216. else if (iCount == 1)
  10217. return 0;
  10218. else
  10219. return -1;
  10220. }
  10221. else
  10222. {
  10223. //
  10224. // Layout type view. we need to use the position of the items
  10225. // to figure out the next item
  10226. //
  10227. if (ListView_IsOwnerData(plv))
  10228. {
  10229. iStart = max(0, iStart);
  10230. // if it does not matches any of the entries in the case statement below
  10231. // this is done to skip the call back by the GetRects
  10232. //
  10233. if (vk != VK_LEFT &&
  10234. vk != VK_RIGHT &&
  10235. vk != VK_UP &&
  10236. vk != VK_DOWN &&
  10237. vk != VK_HOME &&
  10238. vk != VK_END &&
  10239. vk != VK_NEXT &&
  10240. vk != VK_PRIOR)
  10241. {
  10242. return -1;
  10243. }
  10244. ListView_GetRects(plv, iStart, QUERY_DEFAULT, &rcFocus, NULL, NULL, NULL);
  10245. }
  10246. else
  10247. {
  10248. if (iStart != -1)
  10249. {
  10250. ListView_GetRects(plv, iStart, QUERY_DEFAULT, &rcFocus, NULL, NULL, NULL);
  10251. }
  10252. }
  10253. switch (vk)
  10254. {
  10255. // For standard arrow keys just fall out of here.
  10256. case VK_LEFT:
  10257. case VK_RIGHT:
  10258. case VK_UP:
  10259. case VK_DOWN:
  10260. if (ListView_IsOwnerData(plv))
  10261. {
  10262. break;
  10263. }
  10264. else
  10265. {
  10266. if (iStart != -1)
  10267. {
  10268. // all keys map to VK_HOME except VK_END
  10269. break;
  10270. }
  10271. // Fall through
  10272. vk = VK_HOME;
  10273. }
  10274. case VK_HOME:
  10275. rcFocus.left = - plv->ptOrigin.x;
  10276. rcFocus.top = - plv->ptOrigin.y;
  10277. break;
  10278. case VK_END:
  10279. rcFocus.left = plv->rcView.right;
  10280. rcFocus.top = plv->rcView.bottom;
  10281. break;
  10282. case VK_NEXT:
  10283. rcFocus.top += plv->sizeClient.cy;
  10284. vk = VK_UP;
  10285. break;
  10286. case VK_PRIOR:
  10287. vk = VK_DOWN;
  10288. rcFocus.top -= plv->sizeClient.cy;
  10289. break;
  10290. default:
  10291. return -1; // Out of range
  10292. }
  10293. return ListView_IFindNearestItem(plv, rcFocus.left, rcFocus.top, vk);
  10294. }
  10295. }
  10296. int ListView_OnGetNextItem(LV* plv, int i, UINT flags)
  10297. {
  10298. int iStart = i;
  10299. int cItemMax = ListView_Count(plv);
  10300. // Note that -1 is a valid starting point
  10301. if (i < -1 || i >= cItemMax)
  10302. return -1;
  10303. if (ListView_IsOwnerData(plv))
  10304. {
  10305. if (flags & (LVNI_CUT | LVNI_DROPHILITED | LVNI_PREVIOUS))
  10306. {
  10307. return -1;
  10308. }
  10309. }
  10310. if (flags & LVNI_FOCUSED)
  10311. {
  10312. // we know which item is focused, jump right to it.
  10313. // but we have to mimick the code below exactly for compat:
  10314. // if directional bits are set, they take precedence.
  10315. if (!(flags & (LVNI_ABOVE | LVNI_BELOW | LVNI_TORIGHT | LVNI_TOLEFT)))
  10316. {
  10317. // there are no more focused items after iFocus
  10318. if (i >= plv->iFocus)
  10319. return -1;
  10320. // subtract one here -- we increment it below
  10321. i = plv->iFocus - 1;
  10322. }
  10323. }
  10324. while (TRUE)
  10325. {
  10326. if (flags & (LVNI_ABOVE | LVNI_BELOW | LVNI_TORIGHT | LVNI_TOLEFT))
  10327. {
  10328. UINT vk;
  10329. if (flags & LVNI_ABOVE)
  10330. vk = VK_UP;
  10331. else if (flags & LVNI_BELOW)
  10332. vk = VK_DOWN;
  10333. else if (flags & LVNI_TORIGHT)
  10334. vk = VK_RIGHT;
  10335. else
  10336. vk = VK_LEFT;
  10337. if (i != -1)
  10338. i = ListView_Arrow(plv, i, vk);
  10339. if (i == -1)
  10340. return i;
  10341. }
  10342. else
  10343. {
  10344. i++;
  10345. if (i == cItemMax)
  10346. return -1;
  10347. }
  10348. // See if any other restrictions are set
  10349. if (flags & ~(LVNI_ABOVE | LVNI_BELOW | LVNI_TORIGHT | LVNI_TOLEFT))
  10350. {
  10351. WORD wItemState;
  10352. if (ListView_IsOwnerData(plv))
  10353. {
  10354. if (flags & LVNI_FOCUSED)
  10355. {
  10356. // we check LVNI_FOCUSED before the loop, so i == iFocus
  10357. ASSERT(i == plv->iFocus && i != -1);
  10358. if (flags & LVNI_SELECTED)
  10359. {
  10360. if (plv->plvrangeSel->lpVtbl->IsSelected(plv->plvrangeSel, i) != S_OK)
  10361. {
  10362. i = -1;
  10363. }
  10364. }
  10365. }
  10366. else if (flags & LVNI_SELECTED)
  10367. {
  10368. i = max(i, 0);
  10369. plv->plvrangeSel->lpVtbl->NextSelected(plv->plvrangeSel, i, &i);
  10370. }
  10371. else
  10372. {
  10373. i = -1;
  10374. }
  10375. }
  10376. else
  10377. {
  10378. {
  10379. LISTITEM* pitem = ListView_FastGetItemPtr(plv, i);
  10380. wItemState = pitem->state;
  10381. }
  10382. // for LVNI_FOCUSED, we start at the LVIS_FOCUSED element, if we're
  10383. // not on that element, one of the below continues was hit, so
  10384. // we'll never find the element. bail out early.
  10385. if ((flags & LVNI_FOCUSED) && !(wItemState & LVIS_FOCUSED))
  10386. {
  10387. ASSERT(i == plv->iFocus || i == plv->iFocus+1);
  10388. return -1;
  10389. }
  10390. if (((flags & LVNI_SELECTED) && !(wItemState & LVIS_SELECTED)) ||
  10391. ((flags & LVNI_CUT) && !(wItemState & LVIS_CUT)) ||
  10392. ((flags & LVNI_DROPHILITED) && !(wItemState & LVIS_DROPHILITED)))
  10393. {
  10394. if (i != iStart)
  10395. continue;
  10396. else
  10397. {
  10398. // we've looped and we can't find anything to fit this criteria
  10399. return -1;
  10400. }
  10401. }
  10402. }
  10403. }
  10404. return i;
  10405. }
  10406. }
  10407. int ListView_CompareString(LV* plv, int i, LPCTSTR pszFind, UINT flags, int iLen)
  10408. {
  10409. // REARCHITECT: non protected globals
  10410. int cb;
  10411. TCHAR ach[CCHLABELMAX];
  10412. LV_ITEM item;
  10413. ASSERT(!ListView_IsOwnerData(plv));
  10414. ASSERT(pszFind);
  10415. item.iItem = i;
  10416. item.iSubItem = 0;
  10417. item.mask = LVIF_TEXT;
  10418. item.pszText = ach;
  10419. item.cchTextMax = ARRAYSIZE(ach);
  10420. ListView_OnGetItem(plv, &item);
  10421. if (!(flags & (LVFI_PARTIAL | LVFI_SUBSTRING)))
  10422. return lstrcmpi(item.pszText, pszFind);
  10423. // FEATURE: LVFI_SUBSTRING is not really implemented yet.
  10424. cb = lstrlen(pszFind);
  10425. if (iLen && (cb > iLen))
  10426. {
  10427. cb = iLen;
  10428. }
  10429. //
  10430. // If the sub strings not equal then return the ordering based
  10431. // on the entire string.
  10432. //
  10433. return IntlStrEqNI(item.pszText, pszFind, cb) ? 0 : lstrcmp(item.pszText, pszFind);
  10434. }
  10435. int ListView_OnFindItemA(LV* plv, int iStart, LV_FINDINFOA * plvfi)
  10436. {
  10437. LPWSTR pszW = NULL;
  10438. LPCSTR pszC = NULL;
  10439. int iRet;
  10440. //HACK ALERT -- this code assumes that LV_FINDINFOA is exactly the same
  10441. // as LV_FINDINFOW except for the pointer to the string.
  10442. COMPILETIME_ASSERT(sizeof(LV_FINDINFOA) == sizeof(LV_FINDINFOW));
  10443. if (!plvfi)
  10444. return -1;
  10445. if (!(plvfi->flags & LVFI_PARAM) && !(plvfi->flags & LVFI_NEARESTXY))
  10446. {
  10447. pszC = plvfi->psz;
  10448. if ((pszW = ProduceWFromA(plv->ci.uiCodePage, pszC)) == NULL)
  10449. return -1;
  10450. plvfi->psz = (LPSTR)pszW;
  10451. }
  10452. iRet = ListView_OnFindItem(plv, iStart, (const LV_FINDINFO *)plvfi);
  10453. if (pszW != NULL)
  10454. {
  10455. plvfi->psz = pszC;
  10456. FreeProducedString(pszW);
  10457. }
  10458. return iRet;
  10459. }
  10460. int ListView_OnFindItem(LV* plv, int iStart, const LV_FINDINFO* plvfi)
  10461. {
  10462. int i;
  10463. int j;
  10464. int cItem;
  10465. UINT flags;
  10466. if (!plvfi)
  10467. return -1;
  10468. if (plvfi->flags & LVFI_NEARESTXY)
  10469. {
  10470. if (ListView_IsSlotView(plv))
  10471. {
  10472. return ListView_IFindNearestItem(plv, plvfi->pt.x, plvfi->pt.y, plvfi->vkDirection);
  10473. }
  10474. else
  10475. return -1;
  10476. }
  10477. // Note that -1 is a valid starting point
  10478. if (iStart < -1 || iStart >= ListView_Count(plv))
  10479. return -1;
  10480. if (ListView_IsOwnerData(plv))
  10481. {
  10482. // call back to owner for search
  10483. return (int) ListView_RequestFindItem(plv, plvfi, iStart + 1);
  10484. }
  10485. else
  10486. {
  10487. flags = plvfi->flags;
  10488. i = iStart;
  10489. cItem = ListView_Count(plv);
  10490. if (flags & LVFI_PARAM)
  10491. {
  10492. LPARAM lParam = plvfi->lParam;
  10493. // Lisearch with wraparound...
  10494. //
  10495. for (j = cItem; j-- != 0;)
  10496. {
  10497. ++i;
  10498. if (i == cItem)
  10499. {
  10500. if (flags & LVFI_WRAP)
  10501. i = 0;
  10502. else
  10503. break;
  10504. }
  10505. if (ListView_FastGetItemPtr(plv, i)->lParam == lParam)
  10506. return i;
  10507. }
  10508. }
  10509. else // if (flags & (LVFI_STRING | LVFI_SUBSTRING | LVFI_PARTIAL))
  10510. {
  10511. LPCTSTR pszFind = plvfi->psz;
  10512. if (!pszFind)
  10513. return -1;
  10514. if (plv->ci.style & (LVS_SORTASCENDING | LVS_SORTDESCENDING))
  10515. return ListView_LookupString(plv, pszFind, flags, i + 1);
  10516. for (j = cItem; j-- != 0;)
  10517. {
  10518. ++i;
  10519. if (i == cItem)
  10520. {
  10521. if (flags & LVFI_WRAP)
  10522. i = 0;
  10523. else
  10524. break;
  10525. }
  10526. if (ListView_CompareString(plv,
  10527. i,
  10528. pszFind,
  10529. (flags & (LVFI_PARTIAL | LVFI_SUBSTRING)), 0) == 0)
  10530. {
  10531. return i;
  10532. }
  10533. }
  10534. }
  10535. }
  10536. return -1;
  10537. }
  10538. BOOL ListView_OnGetItemRect(LV* plv, int i, RECT* prc)
  10539. {
  10540. LPRECT pRects[LVIR_MAX];
  10541. // validate parameters
  10542. if (!ListView_IsValidItemNumber(plv, i))
  10543. {
  10544. RIPMSG(0, "LVM_GETITEMRECT: invalid index %d", i);
  10545. return FALSE;
  10546. }
  10547. if (!prc || prc->left >= LVIR_MAX || prc->left < 0)
  10548. {
  10549. RIPMSG(0, "LVM_GETITEMRECT: invalid rect pointer");
  10550. return FALSE;
  10551. }
  10552. pRects[0] = NULL;
  10553. pRects[1] = NULL;
  10554. pRects[2] = NULL;
  10555. pRects[3] = NULL;
  10556. pRects[prc->left] = prc;
  10557. ListView_GetRects(plv, i, QUERY_DEFAULT, pRects[LVIR_ICON], pRects[LVIR_LABEL],
  10558. pRects[LVIR_BOUNDS], pRects[LVIR_SELECTBOUNDS]);
  10559. return TRUE;
  10560. }
  10561. //
  10562. // in:
  10563. // plv
  10564. // iItem MUST be a valid item index (in range)
  10565. // out:
  10566. // prcIcon icon bounding rect
  10567. // prcLabel label text bounding rect, for details this is the first column
  10568. // prcBounds entire item (all text and icon), including columns in details
  10569. // prcSelectionBounds union of icon and label rects, does NOT include columns
  10570. // in details view
  10571. // REARCHITECT raymondc - Need to pass an HDC parameter for measurement
  10572. // since sometimes we do this while painting
  10573. // This returns rects in Window Coordinates
  10574. void ListView_GetRects(LV* plv, int iItem, UINT fQueryLabelRects,
  10575. RECT* prcIcon, RECT* prcLabel, RECT* prcBounds,
  10576. RECT* prcSelectBounds)
  10577. {
  10578. ASSERT(plv);
  10579. if (ListView_IsReportView(plv))
  10580. {
  10581. ListView_RGetRects(plv, iItem, prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10582. }
  10583. else if (ListView_IsListView(plv))
  10584. {
  10585. ListView_LGetRects(plv, iItem, prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10586. }
  10587. else
  10588. {
  10589. if (ListView_IsOwnerData(plv))
  10590. {
  10591. RECT rcIcon;
  10592. RECT rcTextBounds;
  10593. LISTITEM item;
  10594. if (ListView_IsIconView(plv))
  10595. ListView_IGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, &item, FALSE);
  10596. else if (ListView_IsSmallView(plv))
  10597. ListView_SGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, &item, FALSE);
  10598. else if (ListView_IsTileView(plv))
  10599. ListView_TGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, &item, FALSE);
  10600. if (prcIcon)
  10601. *prcIcon = rcIcon;
  10602. if (prcLabel)
  10603. *prcLabel = rcTextBounds;
  10604. if (prcBounds)
  10605. UnionRect(prcBounds, &rcIcon, &rcTextBounds);
  10606. if (prcSelectBounds)
  10607. UnionRect(prcSelectBounds, &rcIcon, &rcTextBounds);
  10608. }
  10609. else
  10610. {
  10611. if (iItem >= ListView_Count(plv))
  10612. {
  10613. return;
  10614. }
  10615. else
  10616. {
  10617. LISTITEM *pitem = ListView_FastGetItemPtr(plv, iItem);
  10618. if (pitem->cyFoldedLabel == SRECOMPUTE)
  10619. {
  10620. _ListView_RecomputeLabelSize(plv, pitem, iItem, NULL, FALSE);
  10621. }
  10622. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem, fQueryLabelRects,
  10623. prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10624. }
  10625. }
  10626. }
  10627. }
  10628. void ListView_GetRectsOwnerData(LV* plv, int iItem,
  10629. RECT* prcIcon, RECT* prcLabel, RECT* prcBounds,
  10630. RECT* prcSelectBounds, LISTITEM* pitem)
  10631. {
  10632. ASSERT(plv);
  10633. ASSERT(ListView_IsOwnerData(plv));
  10634. if (ListView_IsReportView(plv))
  10635. {
  10636. ListView_RGetRects(plv, iItem, prcIcon, prcLabel, prcBounds,
  10637. prcSelectBounds);
  10638. }
  10639. else if (ListView_IsListView(plv))
  10640. {
  10641. ListView_LGetRects(plv, iItem, prcIcon, prcLabel, prcBounds,
  10642. prcSelectBounds);
  10643. }
  10644. else
  10645. {
  10646. RECT rcIcon;
  10647. RECT rcTextBounds;
  10648. if (ListView_IsIconView(plv))
  10649. ListView_IGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, pitem, TRUE);
  10650. else if (ListView_IsSmallView(plv))
  10651. ListView_SGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, pitem, TRUE);
  10652. else if (ListView_IsTileView(plv))
  10653. ListView_TGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, pitem, TRUE);
  10654. // Don't need to check for folding here, as will have been handled in user data
  10655. // rectangle fetching functions.
  10656. if (prcIcon)
  10657. *prcIcon = rcIcon;
  10658. if (prcLabel)
  10659. *prcLabel = rcTextBounds;
  10660. if (prcBounds)
  10661. UnionRect(prcBounds, &rcIcon, &rcTextBounds);
  10662. if (prcSelectBounds)
  10663. UnionRect(prcSelectBounds, &rcIcon, &rcTextBounds);
  10664. }
  10665. }
  10666. BOOL ListView_OnRedrawItems(LV* plv, int iFirst, int iLast)
  10667. {
  10668. int iCount = ListView_Count(plv);
  10669. if (iFirst < iCount)
  10670. {
  10671. if (iLast >= iCount)
  10672. iLast = iCount - 1;
  10673. while (iFirst <= iLast)
  10674. ListView_InvalidateItem(plv, iFirst++, FALSE, RDW_INVALIDATE | RDW_ERASE);
  10675. }
  10676. return TRUE;
  10677. }
  10678. // fSelectionOnly use the selection bounds only, ie. don't include
  10679. // columns in invalidation if in details view
  10680. //
  10681. void ListView_InvalidateItemEx(LV* plv, int iItem, BOOL fSelectionOnly,
  10682. UINT fRedraw, UINT maskChanged)
  10683. {
  10684. RECT rc;
  10685. LPRECT prcIcon;
  10686. LPRECT prcLabel;
  10687. LPRECT prcBounds;
  10688. LPRECT prcSelectBounds;
  10689. LISTITEM* pitem = NULL;
  10690. if (iItem == -1)
  10691. return;
  10692. // Ok if NULL
  10693. if (plv->hdpa)
  10694. pitem = ListView_GetItemPtr(plv, iItem);
  10695. prcIcon = prcLabel = prcBounds = prcSelectBounds = NULL;
  10696. // if we're in owner draw mode, and there's been a new font,
  10697. // we don't really know what the selection bounds is, so always use the bounds
  10698. // in that case... unless we're in fullrowselect mode
  10699. if (ListView_IsOwnerData(plv) && plv->flags & LVF_CUSTOMFONT &&
  10700. !ListView_FullRowSelect(plv))
  10701. {
  10702. fSelectionOnly = FALSE;
  10703. }
  10704. // if we're owner draw, there's no such thing as selection only
  10705. if (plv->ci.style & LVS_OWNERDRAWFIXED)
  10706. fSelectionOnly = FALSE;
  10707. if (fSelectionOnly)
  10708. {
  10709. // In report mode non-fullrowselect,
  10710. // we have to use the full label rectangle rather
  10711. // than just the selection bounds, since the stuff outside the
  10712. // rectangle might need redrawing, too.
  10713. if (ListView_IsReportView(plv) && !ListView_FullRowSelect(plv))
  10714. prcLabel = &rc;
  10715. else
  10716. prcSelectBounds = &rc;
  10717. }
  10718. else
  10719. {
  10720. // if _only_the_text_ or _only_the_image_ changed then limit the redraw
  10721. switch (maskChanged)
  10722. {
  10723. case LVIF_IMAGE:
  10724. prcIcon = &rc;
  10725. break;
  10726. case LVIF_TEXT:
  10727. prcLabel = &rc;
  10728. break;
  10729. default:
  10730. prcBounds = &rc;
  10731. break;
  10732. }
  10733. }
  10734. if (ListView_RedrawEnabled(plv))
  10735. {
  10736. ListView_GetRects(plv, iItem, QUERY_DEFAULT,
  10737. prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10738. if (RECTS_IN_SIZE(plv->sizeClient, rc))
  10739. {
  10740. if (ListView_IsBorderSelect(plv))
  10741. {
  10742. InflateRect(&rc, 4 + g_cxIconMargin, 4 + g_cyIconMargin); // account for selection border and seperation since drawing otside of icon
  10743. fRedraw |= RDW_ERASE;
  10744. }
  10745. // Affects only allowed if dubble buffering
  10746. if (ListView_IsDoubleBuffer(plv))
  10747. {
  10748. if ((pitem && (pitem->state & LVIS_GLOW)))
  10749. {
  10750. InflateRect(&rc, GLOW_EXPAND, GLOW_EXPAND);
  10751. fRedraw |= RDW_ERASE;
  10752. }
  10753. }
  10754. ListView_DebugDrawInvalidRegion(plv, &rc, NULL);
  10755. RedrawWindow(plv->ci.hwnd, &rc, NULL, fRedraw);
  10756. }
  10757. }
  10758. else
  10759. {
  10760. // if we're not visible, we'll get a full
  10761. // erase bk when we do become visible, so only do this stuff when
  10762. // we're on setredraw false
  10763. if (!(plv->flags & LVF_REDRAW))
  10764. {
  10765. // if we're invalidating that's new (thus hasn't been painted yet)
  10766. // blow it off
  10767. if ((plv->iFirstChangedNoRedraw != -1) &&
  10768. (iItem >= plv->iFirstChangedNoRedraw))
  10769. {
  10770. return;
  10771. }
  10772. ListView_GetRects(plv, iItem, QUERY_DEFAULT,
  10773. prcIcon, prcLabel, prcBounds, prcSelectBounds);
  10774. // Affects only allowed if dubble buffering
  10775. if (ListView_IsDoubleBuffer(plv))
  10776. {
  10777. if (pitem && (pitem->state & LVIS_GLOW))
  10778. {
  10779. InflateRect(&rc, GLOW_EXPAND, GLOW_EXPAND);
  10780. fRedraw |= RDW_ERASE;
  10781. }
  10782. }
  10783. if (ListView_IsBorderSelect(plv))
  10784. {
  10785. InflateRect(&rc, 4 + g_cxIconMargin, 4 + g_cyIconMargin); // account for selection border and seperation since drawing otside of icon
  10786. fRedraw |= RDW_ERASE;
  10787. }
  10788. // if it had the erase bit, add it to our region
  10789. if (RECTS_IN_SIZE(plv->sizeClient, rc))
  10790. {
  10791. HRGN hrgn = CreateRectRgnIndirect(&rc);
  10792. ListView_InvalidateRegion(plv, hrgn);
  10793. if (fRedraw & RDW_ERASE)
  10794. plv->flags |= LVF_ERASE;
  10795. }
  10796. }
  10797. }
  10798. }
  10799. // this returns BF_* flags to indicate which if any edge the item I is touching
  10800. // or crossing...
  10801. UINT LV_IsItemOnViewEdge(LV* plv, LISTITEM* pitem)
  10802. {
  10803. RECT rcItem;
  10804. UINT uRet = 0;
  10805. // as far as rcView goes, unfolded label rects determine edge-ness
  10806. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem, QUERY_RCVIEW|QUERY_UNFOLDED,
  10807. NULL, NULL, &rcItem, NULL);
  10808. // translate from window coordinates to listview coordinate
  10809. OffsetRect(&rcItem, plv->ptOrigin.x, plv->ptOrigin.y);
  10810. // include the rcView buffer
  10811. ListView_AddViewRectBuffer(plv, &rcItem);
  10812. if (rcItem.right >= plv->rcView.right)
  10813. uRet |= BF_RIGHT;
  10814. if (rcItem.left <= plv->rcView.left)
  10815. uRet |= BF_LEFT;
  10816. if (rcItem.top <= plv->rcView.top)
  10817. uRet |= BF_TOP;
  10818. if (rcItem.bottom >= plv->rcView.bottom)
  10819. uRet |= BF_BOTTOM;
  10820. return uRet;
  10821. }
  10822. // Move pitem to x,y
  10823. // Update rcView to accomodate this if we can, or mark rcView for recomputation
  10824. void LV_AdjustViewRectOnMove(LV* plv, LISTITEM *pitem, int x, int y)
  10825. {
  10826. plv->iFreeSlot = -1; // The "free slot" cache is no good once an item moves
  10827. // if we have to recompute anyways, don't bother
  10828. if (!ListView_IsOwnerData(plv))
  10829. {
  10830. if ((plv->rcView.left != RECOMPUTE) &&
  10831. x != RECOMPUTE && y != RECOMPUTE &&
  10832. pitem->cyFoldedLabel != SRECOMPUTE)
  10833. {
  10834. RECT rcClient, rcAfter;
  10835. RECT rcView = plv->rcView;
  10836. // Our optimized move-adjust-rcView must maintain this, make sure it's true before we even start:
  10837. ASSERT(ListView_ValidatercView(plv, &plv->rcView, FALSE));
  10838. ListView_GetClientRect(plv, &rcClient, TRUE, NULL);
  10839. ASSERT(ListView_ValidateScrollPositions(plv, &rcClient));
  10840. if (pitem->pt.x != RECOMPUTE)
  10841. {
  10842. UINT uEdges;
  10843. uEdges = LV_IsItemOnViewEdge(plv, pitem);
  10844. pitem->pt.x = x;
  10845. pitem->pt.y = y;
  10846. // before and after the move, they need to be touching the
  10847. // same edges or not at all
  10848. if (uEdges != LV_IsItemOnViewEdge(plv, pitem))
  10849. {
  10850. goto FullRecompute;
  10851. }
  10852. }
  10853. else
  10854. {
  10855. // if the position wasn't set before
  10856. // we just need to find out what it is afterwards and
  10857. // enlarge the view... we don't need to shrink it
  10858. pitem->pt.x = x;
  10859. pitem->pt.y = y;
  10860. }
  10861. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem, QUERY_RCVIEW|QUERY_UNFOLDED,
  10862. NULL, NULL, &rcAfter, NULL);
  10863. // translate from window coordinates to listview coordinates
  10864. OffsetRect(&rcAfter, plv->ptOrigin.x, plv->ptOrigin.y);
  10865. // include the rcView buffer
  10866. ListView_AddViewRectBuffer(plv, &rcAfter);
  10867. // if we make it here, we just have to make sure the new view rect
  10868. // encompases this new item
  10869. UnionRect(&rcView, &rcView, &rcAfter);
  10870. DebugMsg(TF_LISTVIEW, TEXT("Score! (%d %d %d %d) was (%d %d %d %d)"),
  10871. rcView.left, rcView.top, rcView.right, rcView.bottom,
  10872. plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
  10873. // Our optimized move-adjust-rcView must maintain this:
  10874. ASSERT(ListView_ValidatercView(plv, &rcView, FALSE));
  10875. plv->rcView = rcView;
  10876. // make sure our scroll positions are correct
  10877. if (ListView_IsIScrollView(plv))
  10878. ListView_FixIScrollPositions(plv, FALSE, &rcClient);
  10879. ASSERT(ListView_ValidateScrollPositions(plv, &rcClient));
  10880. }
  10881. else
  10882. {
  10883. FullRecompute:
  10884. plv->rcView.left = RECOMPUTE;
  10885. }
  10886. }
  10887. DebugMsg(TF_LISTVIEW, TEXT("LV -- AdjustViewRect pitem %d -- (%x, %x)"),
  10888. pitem,
  10889. pitem->pt.x, pitem->pt.y);
  10890. pitem->pt.x = x;
  10891. pitem->pt.y = y;
  10892. // Compute the workarea of this item if applicable
  10893. ListView_FindWorkArea(plv, pitem->pt, &(pitem->iWorkArea));
  10894. }
  10895. BOOL ListView_OnSetItemPosition(LV* plv, int i, int x, int y)
  10896. {
  10897. LISTITEM* pitem;
  10898. if (plv->fGroupView)
  10899. return FALSE;
  10900. if (ListView_IsListView(plv))
  10901. return FALSE;
  10902. if (ListView_IsOwnerData(plv))
  10903. {
  10904. RIPMSG(0, "LVM_SETITEMPOSITION: Invalid for owner-data listview");
  10905. return FALSE;
  10906. }
  10907. pitem = ListView_GetItemPtr(plv, i);
  10908. if (!pitem)
  10909. return FALSE;
  10910. //
  10911. // this is a hack to fix a bug in OLE drag/drop loop
  10912. //
  10913. if (x >= 0xF000 && x < 0x10000)
  10914. {
  10915. DebugMsg(TF_LISTVIEW, TEXT("LV -- On SetItemPosition fixing truncated negative number 0x%08X"), x);
  10916. x = x - 0x10000;
  10917. }
  10918. if (y >= 0xF000 && y < 0x10000)
  10919. {
  10920. DebugMsg(TF_LISTVIEW, TEXT("LV -- On SetItemPosition fixing truncated negative number 0x%08X"), y);
  10921. y = y - 0x10000;
  10922. }
  10923. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  10924. if (pitem->cyFoldedLabel == SRECOMPUTE)
  10925. {
  10926. _ListView_RecomputeLabelSize(plv, pitem, i, NULL, FALSE);
  10927. }
  10928. // erase old
  10929. if (y != pitem->pt.y || x != pitem->pt.x)
  10930. {
  10931. // Don't invalidate if it hasn't got a position yet
  10932. if (pitem->pt.y != RECOMPUTE)
  10933. {
  10934. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
  10935. }
  10936. else if (plv->uUnplaced)
  10937. {
  10938. // this means an unplaced item got placed
  10939. plv->uUnplaced--;
  10940. if (!plv->uUnplaced)
  10941. {
  10942. MSG msg;
  10943. // if this is now 0, pull out the postmessage
  10944. PeekMessage(&msg, plv->ci.hwnd, LVMI_PLACEITEMS, LVMI_PLACEITEMS, PM_REMOVE);
  10945. }
  10946. }
  10947. if (y == RECOMPUTE)
  10948. {
  10949. // if they're setting the new position to be a "any open spot" post that we
  10950. // need to calc this later
  10951. if (!plv->uUnplaced)
  10952. {
  10953. PostMessage(plv->ci.hwnd, LVMI_PLACEITEMS, 0, 0);
  10954. }
  10955. plv->uUnplaced++;
  10956. }
  10957. }
  10958. DebugMsg(TF_LISTVIEW, TEXT("LV -- On SetItemPosition %d %d %d %d -- (%x, %x)"),
  10959. plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom,
  10960. pitem->pt.x, pitem->pt.y);
  10961. LV_AdjustViewRectOnMove(plv, pitem, x, y);
  10962. // and draw at new position
  10963. ListView_RecalcRegion(plv, FALSE, TRUE);
  10964. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE);
  10965. // If autoarrange is turned on, do it now...
  10966. if (ListView_RedrawEnabled(plv))
  10967. {
  10968. ListView_ArrangeOrSnapToGrid(plv);
  10969. if (!(plv->ci.style & LVS_AUTOARRANGE))
  10970. ListView_UpdateScrollBars(plv);
  10971. }
  10972. if (!(plv->ci.style & LVS_AUTOARRANGE))
  10973. {
  10974. plv->fIconsPositioned = TRUE;
  10975. }
  10976. return TRUE;
  10977. }
  10978. BOOL ListView_OnGetItemPosition(LV* plv, int i, POINT* ppt)
  10979. {
  10980. LISTITEM* pitem;
  10981. //
  10982. // This needs to handle all views as it is used to figure out
  10983. // where the item is during drag and drop and the like
  10984. //
  10985. if (!ppt)
  10986. {
  10987. RIPMSG(0, "LVM_GETITEMPOSITION: Invalid ppt = NULL");
  10988. return FALSE;
  10989. }
  10990. if (ListView_IsListView(plv) || ListView_IsReportView(plv)
  10991. || ListView_IsOwnerData(plv))
  10992. {
  10993. RECT rcIcon;
  10994. ListView_GetRects(plv, i, QUERY_DEFAULT, &rcIcon, NULL, NULL, NULL);
  10995. ppt->x = rcIcon.left;
  10996. ppt->y = rcIcon.top;
  10997. }
  10998. else
  10999. {
  11000. pitem = ListView_GetItemPtr(plv, i);
  11001. if (!pitem)
  11002. return FALSE;
  11003. if (pitem->pt.x == RECOMPUTE)
  11004. ListView_Recompute(plv);
  11005. ppt->x = pitem->pt.x;
  11006. ppt->y = pitem->pt.y;
  11007. }
  11008. return TRUE;
  11009. }
  11010. BOOL ListView_OnGetOrigin(LV* plv, POINT* ppt)
  11011. {
  11012. if (!ppt)
  11013. {
  11014. DebugMsg(DM_ERROR, TEXT("ListView_OnGetOrigin: ppt is NULL"));
  11015. return FALSE;
  11016. }
  11017. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  11018. return FALSE;
  11019. *ppt = plv->ptOrigin;
  11020. return TRUE;
  11021. }
  11022. int ListView_OnGetStringWidthA(LV* plv, LPCSTR psz, HDC hdc)
  11023. {
  11024. LPWSTR pszW = NULL;
  11025. int iRet;
  11026. if (!psz)
  11027. return 0;
  11028. if ((psz != NULL) && (pszW = ProduceWFromA(plv->ci.uiCodePage, psz)) == NULL)
  11029. return 0;
  11030. iRet = ListView_OnGetStringWidth(plv, pszW, hdc);
  11031. FreeProducedString(pszW);
  11032. return iRet;
  11033. }
  11034. int ListView_OnGetStringWidth(LV* plv, LPCTSTR psz, HDC hdc)
  11035. {
  11036. SIZE siz;
  11037. HDC hdcFree = NULL;
  11038. HFONT hfontPrev;
  11039. if (!psz || psz == LPSTR_TEXTCALLBACK)
  11040. return 0;
  11041. if (!hdc)
  11042. {
  11043. hdcFree = hdc = GetDC(plv->ci.hwnd);
  11044. }
  11045. hfontPrev = SelectFont(hdc, plv->hfontLabel);
  11046. GetTextExtentPoint(hdc, psz, lstrlen(psz), &siz);
  11047. SelectFont(hdc, hfontPrev);
  11048. if (hdcFree)
  11049. {
  11050. ReleaseDC(plv->ci.hwnd, hdcFree);
  11051. }
  11052. return siz.cx;
  11053. }
  11054. int ListView_OnGetColumnWidth(LV* plv, int iCol)
  11055. {
  11056. if (ListView_IsReportView(plv))
  11057. return ListView_RGetColumnWidth(plv, iCol);
  11058. else if (ListView_IsListView(plv))
  11059. return plv->cxItem;
  11060. return 0;
  11061. }
  11062. BOOL ListView_ISetColumnWidth(LV* plv, int iCol, int cx, BOOL fExplicit)
  11063. {
  11064. if (ListView_IsListView(plv))
  11065. {
  11066. if (iCol != 0 || cx <= 0)
  11067. return FALSE;
  11068. // if it's different and this is an explicit set, or we've never set it explicitly
  11069. if (plv->cxItem != cx && (fExplicit || !(plv->flags & LVF_COLSIZESET)))
  11070. {
  11071. // REVIEW: Should optimize what gets invalidated here...
  11072. plv->cxItem = cx;
  11073. if (fExplicit)
  11074. plv->flags |= LVF_COLSIZESET; // Set the fact that we explictly set size!.
  11075. if (ListView_IsLabelTip(plv))
  11076. {
  11077. // A truncated label may have been exposed or vice versa.
  11078. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  11079. }
  11080. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  11081. ListView_UpdateScrollBars(plv);
  11082. }
  11083. return TRUE;
  11084. }
  11085. else if (ListView_IsReportView(plv))
  11086. {
  11087. if (ListView_IsLabelTip(plv))
  11088. {
  11089. // A truncated label may have been exposed or vice versa.
  11090. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  11091. }
  11092. return ListView_RSetColumnWidth(plv, iCol, cx);
  11093. }
  11094. else
  11095. {
  11096. if (cx && plv->cxItem != cx && (fExplicit || !(plv->flags & LVF_COLSIZESET)))
  11097. {
  11098. // REVIEW: Should optimize what gets invalidated here...
  11099. plv->cxItem = cx;
  11100. if (fExplicit)
  11101. plv->flags |= LVF_COLSIZESET; // Set the fact that we explictly set size!.
  11102. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  11103. ListView_UpdateScrollBars(plv);
  11104. }
  11105. // BUG-FOR-BUG COMPATIBILITY: IE4 accidentally returned FALSE here.
  11106. }
  11107. return FALSE;
  11108. }
  11109. void DrawGradiantLine(HDC hdc, RECT* prcText, RECT* prcGroup)
  11110. {
  11111. COLORREF cr1 = GetSysColor(COLOR_GRADIENTACTIVECAPTION);
  11112. COLORREF cr2 = GetSysColor(COLOR_WINDOW);
  11113. TRIVERTEX pt[2];
  11114. GRADIENT_RECT gr;
  11115. RECT rc = {prcGroup->left, prcText->bottom-1, prcGroup->left + GRADIENT_WIDTH, prcText->bottom};
  11116. pt[0].x = rc.left;
  11117. pt[0].y = rc.top;
  11118. pt[1].x = rc.right;
  11119. pt[1].y = rc.bottom;
  11120. pt[0].Red = GetRValue(cr1) << 8;
  11121. pt[0].Green = GetGValue(cr1) << 8;
  11122. pt[0].Blue = GetBValue(cr1) << 8;
  11123. pt[0].Alpha = 0xFF00;
  11124. pt[1].Red = GetRValue(cr2) << 8;
  11125. pt[1].Green = GetGValue(cr2) << 8;
  11126. pt[1].Blue = GetBValue(cr2) << 8;
  11127. pt[1].Alpha = 0x0000;
  11128. gr.UpperLeft = 0;
  11129. gr.LowerRight = 1;
  11130. GdiGradientFill(hdc, pt, 2, &gr, 1, GRADIENT_FILL_RECT_H);
  11131. }
  11132. void ListView_Redraw(LV* plv, HDC hdc, RECT* prcClip)
  11133. {
  11134. int i = 0;
  11135. int cItem = ListView_Count(plv);
  11136. NMCUSTOMDRAW nmcd;
  11137. LVDRAWITEM lvdi = {0};
  11138. SetBkMode(hdc, TRANSPARENT);
  11139. SelectFont(hdc, plv->hfontLabel);
  11140. nmcd.hdc = hdc;
  11141. nmcd.rc = *prcClip;
  11142. plv->ci.dwCustom = CICustomDrawNotify(&plv->ci, CDDS_PREPAINT, &nmcd);
  11143. if (!(plv->ci.dwCustom & CDRF_SKIPDEFAULT))
  11144. {
  11145. int cGroups;
  11146. // Just before doing any painting, see if the region is up to date...
  11147. ListView_RecalcRegion(plv, FALSE, TRUE);
  11148. //
  11149. // For list view and report view, we can save a lot of time
  11150. // by calculating the index of the first item that may need
  11151. // painting...
  11152. //
  11153. switch (plv->wView)
  11154. {
  11155. case LV_VIEW_DETAILS:
  11156. if (!plv->fGroupView)
  11157. {
  11158. i = ListView_RYHitTest(plv, prcClip->top);
  11159. cItem = ListView_RYHitTest(plv, prcClip->bottom) + 1;
  11160. }
  11161. break;
  11162. case LV_VIEW_LIST:
  11163. i = ListView_LCalcViewItem(plv, prcClip->left, prcClip->top);
  11164. cItem = ListView_LCalcViewItem(plv, prcClip->right, prcClip->bottom) + 1;
  11165. break;
  11166. default:
  11167. if (ListView_IsOwnerData(plv))
  11168. {
  11169. ListView_CalcMinMaxIndex(plv, prcClip, &i, &cItem);
  11170. break;
  11171. }
  11172. }
  11173. if (i < 0)
  11174. i = 0;
  11175. cItem = min(ListView_Count(plv), cItem);
  11176. if (ListView_IsOwnerData(plv) && (cItem > i))
  11177. {
  11178. ListView_NotifyCacheHint(plv, i, cItem-1);
  11179. ListView_LazyCreateWinEvents(plv, i, cItem-1);
  11180. }
  11181. lvdi.plv = plv;
  11182. lvdi.nmcd.nmcd.hdc = hdc;
  11183. lvdi.prcClip = prcClip;
  11184. lvdi.pitem = NULL;
  11185. if (plv->hdpaGroups)
  11186. {
  11187. cGroups = DPA_GetPtrCount(plv->hdpaGroups);
  11188. if (plv->fGroupView && cGroups > 0 && ListView_IsGroupedView(plv))
  11189. {
  11190. int iGroup;
  11191. RECT rcClient;
  11192. GetClientRect(plv->ci.hwnd, &rcClient);
  11193. for (iGroup = 0; iGroup < cGroups; iGroup++)
  11194. {
  11195. LISTGROUP* pgrp = DPA_FastGetPtr(plv->hdpaGroups, iGroup);
  11196. int cItems = DPA_GetPtrCount(pgrp->hdpa);
  11197. if (cItems > 0)
  11198. {
  11199. RECT rcT;
  11200. RECT rc;
  11201. SetRect(&rc, 0,
  11202. pgrp->rc.top - LISTGROUP_HEIGHT(plv, pgrp),
  11203. rcClient.right,
  11204. pgrp->rc.bottom + plv->rcBorder.bottom + plv->paddingBottom);
  11205. if (ListView_IsReportView(plv))
  11206. {
  11207. OffsetRect(&rc, -plv->ptlRptOrigin.x, -plv->ptlRptOrigin.y + plv->yTop);
  11208. }
  11209. else
  11210. {
  11211. OffsetRect(&rc, -plv->ptOrigin.x, -plv->ptOrigin.y);
  11212. }
  11213. if (IntersectRect(&rcT, &rc, prcClip))
  11214. {
  11215. NMLVCUSTOMDRAW nmcdGroup = {0};
  11216. DWORD dwCust;
  11217. UINT uAlign = LVCFMT_LEFT;
  11218. HFONT hfontOld;
  11219. RECT rcBorder = plv->rcBorder;
  11220. rcBorder.top = max(pgrp->cyTitle + 6, plv->rcBorder.top);
  11221. nmcdGroup.nmcd.hdc = hdc;
  11222. nmcdGroup.nmcd.rc = rc;
  11223. nmcdGroup.nmcd.dwItemSpec = pgrp->iGroupId;
  11224. nmcdGroup.dwItemType = LVCDI_GROUP;
  11225. nmcdGroup.rcText.left = rc.left + plv->paddingLeft;
  11226. nmcdGroup.rcText.top = rc.top;
  11227. nmcdGroup.rcText.bottom = rc.top + max(pgrp->cyTitle + 6, plv->rcBorder.top);
  11228. nmcdGroup.rcText.right = rc.right;
  11229. nmcdGroup.uAlign = pgrp->uAlign;
  11230. nmcdGroup.clrText = plv->crHeader;
  11231. dwCust = CICustomDrawNotify(&plv->ci, CDDS_PREPAINT, &nmcdGroup.nmcd);
  11232. if (!(dwCust & CDRF_SKIPDEFAULT))
  11233. {
  11234. RECT rcHeader = {0};
  11235. if (!(LVCDRF_NOGROUPFRAME & dwCust))
  11236. {
  11237. DrawGradiantLine(hdc, &nmcdGroup.rcText, &nmcdGroup.nmcd.rc);
  11238. }
  11239. if (!(dwCust & CDRF_NEWFONT))
  11240. {
  11241. hfontOld = SelectObject(hdc, plv->hfontGroup);
  11242. }
  11243. if (nmcdGroup.uAlign & LVGA_HEADER_CENTER)
  11244. uAlign = LVCFMT_CENTER;
  11245. else if (nmcdGroup.uAlign & LVGA_HEADER_RIGHT)
  11246. uAlign = LVCFMT_RIGHT;
  11247. SHDrawText(hdc, pgrp->pszHeader,
  11248. &nmcdGroup.rcText, uAlign, SHDT_VCENTER | SHDT_LEFT,
  11249. plv->cyLabelChar, plv->cxEllipses,
  11250. nmcdGroup.clrText, CLR_NONE);
  11251. // Need do do this before we unselect so that we get the right font...
  11252. DrawText(hdc, pgrp->pszHeader, -1, &rcHeader, DT_LV | DT_CALCRECT);
  11253. if (!(dwCust & CDRF_NEWFONT))
  11254. {
  11255. SelectObject(hdc, hfontOld);
  11256. }
  11257. }
  11258. dwCust = CICustomDrawNotify(&plv->ci, CDDS_POSTPAINT, &nmcdGroup.nmcd);
  11259. }
  11260. }
  11261. }
  11262. }
  11263. }
  11264. cItem = min(ListView_Count(plv), cItem);
  11265. for (; i < cItem; i++)
  11266. {
  11267. BOOL bSuccess;
  11268. int i2;
  11269. if (ListView_IsRearrangeableView(plv) &&
  11270. !ListView_IsOwnerData(plv))
  11271. {
  11272. LISTITEM *pitem;
  11273. // Icon views: Draw back-to-front mapped through
  11274. // Z-order array for proper Z order appearance - If autoarrange
  11275. // is on, we don't need to do this as our arrange code is setup
  11276. // to not overlap items!
  11277. //
  11278. // For the cases where we might have overlap, we sped this up,
  11279. // by converting the hdpaZorder into a list of indexes instead
  11280. // of pointers. This ovoids the costly convert pointer to
  11281. // index call.
  11282. //
  11283. i2 = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, (cItem - 1) -i);
  11284. //
  11285. // do a fast clip check on the item so we dont even try to
  11286. // draw it unless it is visible
  11287. //
  11288. // for small icon view we cant clip on the left without
  11289. // getting the text
  11290. //
  11291. // for large icon view we cant clip on the top without
  11292. // getting the text
  11293. //
  11294. // for large icon view in NOLABELWRAP mode, we can't clip
  11295. // on the top without getting the text, nor can we clip to
  11296. // the left or right in case the text is long.
  11297. //
  11298. // we can always clip to the bottom
  11299. //
  11300. pitem = ListView_FastGetItemPtr(plv, i2);
  11301. if (pitem && pitem->pt.x != RECOMPUTE)
  11302. {
  11303. int yBias = 0;
  11304. if (ListView_IsBorderSelect(plv))
  11305. yBias = BORDERSELECT_THICKNESS;
  11306. if (pitem->pt.y - yBias - plv->ptOrigin.y > prcClip->bottom)
  11307. continue;
  11308. if (plv->wView == LV_VIEW_SMALLICON)
  11309. {
  11310. if (pitem->pt.x - plv->ptOrigin.x - plv->cxState > prcClip->right)
  11311. continue;
  11312. if (pitem->pt.y + yBias + plv->cyItem - plv->ptOrigin.y < prcClip->top)
  11313. continue;
  11314. }
  11315. else if (!(plv->ci.style & LVS_NOLABELWRAP))
  11316. {
  11317. if (plv->wView == LV_VIEW_TILE)
  11318. {
  11319. if (pitem->pt.x - plv->sizeTile.cx - plv->ptOrigin.x > prcClip->right)
  11320. continue;
  11321. if (pitem->pt.x + yBias + plv->sizeTile.cx - plv->ptOrigin.x < prcClip->left)
  11322. continue;
  11323. }
  11324. else // LV_VIEW_ICON
  11325. {
  11326. if (pitem->pt.x - plv->cxIconSpacing - plv->ptOrigin.x > prcClip->right)
  11327. continue;
  11328. if (pitem->pt.x + yBias + plv->cxIconSpacing - plv->ptOrigin.x < prcClip->left)
  11329. continue;
  11330. }
  11331. }
  11332. }
  11333. if (plv->fGroupView &&
  11334. !LISTITEM_HASGROUP(pitem))
  11335. {
  11336. continue; // Don't paint items not in a group.
  11337. }
  11338. }
  11339. else
  11340. i2 = i;
  11341. plv->iItemDrawing = i2;
  11342. lvdi.nmcd.nmcd.dwItemSpec = i2;
  11343. // these may get changed
  11344. lvdi.lpptOrg = NULL;
  11345. lvdi.flags = 0;
  11346. lvdi.nmcd.clrText = plv->clrText;
  11347. lvdi.nmcd.clrTextBk = plv->clrTextBk;
  11348. lvdi.nmcd.clrFace = plv->clrBk;
  11349. lvdi.nmcd.iIconEffect = ILD_NORMAL;
  11350. lvdi.nmcd.iIconPhase = 0;
  11351. bSuccess = ListView_DrawItem(&lvdi);
  11352. if (!bSuccess)
  11353. {
  11354. break;
  11355. }
  11356. }
  11357. if (ListView_IsRearrangeableView(plv) &&
  11358. (ListView_IsOwnerData(plv)) &&
  11359. plv->iFocus != -1)
  11360. {
  11361. // since there's no zorder in ownerdata, we explicitly draw the focus guy last (again)
  11362. // so that it'll appear on top
  11363. // we may potentially want to do this for all items that are selected
  11364. plv->iItemDrawing = plv->iFocus;
  11365. lvdi.nmcd.nmcd.dwItemSpec = plv->iItemDrawing;
  11366. // these may get changed
  11367. lvdi.lpptOrg = NULL;
  11368. lvdi.flags = 0;
  11369. lvdi.nmcd.clrText = plv->clrText;
  11370. lvdi.nmcd.clrTextBk = plv->clrTextBk;
  11371. ListView_DrawItem(&lvdi);
  11372. }
  11373. // this is an NT5/Memphis feature.
  11374. if (ListView_Count(plv) == 0)
  11375. {
  11376. // there're no items in this view
  11377. // check if we need to display some text in this case.
  11378. if (ListView_GetEmptyText(plv))
  11379. {
  11380. RECT rcClip;
  11381. UINT flags = 0;
  11382. // Put some edging between the text and the border of the
  11383. // window so we don't slam up against the border.
  11384. // This keeps DBCS from looking horrid.
  11385. rcClip.left = g_cxEdge;
  11386. rcClip.top = g_cyEdge;
  11387. if (plv->dwExStyle & WS_EX_RTLREADING)
  11388. flags |= SHDT_RTLREADING;
  11389. // if its a report view && we have a header then move the text down
  11390. if (ListView_IsReportView(plv) && (!(plv->ci.style & LVS_NOCOLUMNHEADER)))
  11391. rcClip.top += plv->cyItem;
  11392. // Note: Use the full sizeClient.cx as the right margin
  11393. // in case pszEmptyText is wider than the client rectangle.
  11394. rcClip.left -= (int)plv->ptlRptOrigin.x;
  11395. rcClip.right = plv->sizeClient.cx;
  11396. rcClip.bottom = rcClip.top + plv->cyItem;
  11397. SHDrawText(hdc, plv->pszEmptyText,
  11398. &rcClip, LVCFMT_LEFT, flags,
  11399. plv->cyLabelChar, plv->cxEllipses,
  11400. plv->clrText, plv->clrBk);
  11401. }
  11402. }
  11403. plv->iItemDrawing = -1;
  11404. // post painting.... this is to do any extra (non item) painting
  11405. // such a grid lines
  11406. switch (plv->wView)
  11407. {
  11408. case LV_VIEW_DETAILS:
  11409. ListView_RAfterRedraw(plv, hdc);
  11410. break;
  11411. }
  11412. // Insert mark
  11413. {
  11414. RECT rcInsertMark;
  11415. if (ListView_OnGetInsertMarkRect(plv, &rcInsertMark))
  11416. {
  11417. OffsetRect(&rcInsertMark, -plv->ptOrigin.x, -plv->ptOrigin.y);
  11418. CCDrawInsertMark(hdc,
  11419. &rcInsertMark,
  11420. ((plv->ci.style & LVS_ALIGNMASK) == LVS_ALIGNTOP),
  11421. ListView_OnGetInsertMarkColor(plv));
  11422. }
  11423. }
  11424. // notify parent afterwards if they want us to
  11425. if (plv->ci.dwCustom & CDRF_NOTIFYPOSTPAINT)
  11426. {
  11427. CICustomDrawNotify(&plv->ci, CDDS_POSTPAINT, &nmcd);
  11428. }
  11429. }
  11430. }
  11431. BOOL ListView_DrawItem(PLVDRAWITEM plvdi)
  11432. {
  11433. BOOL fAllowHotSelection = FALSE;
  11434. BOOL bRet = TRUE;
  11435. UINT state;
  11436. if (!ListView_IsOwnerData(plvdi->plv) && (!plvdi->plv->hdpa || plvdi->nmcd.nmcd.dwItemSpec > (UINT)DPA_GetPtrCount(plvdi->plv->hdpa)))
  11437. return FALSE;
  11438. if (!ListView_IsOwnerData(plvdi->plv))
  11439. {
  11440. plvdi->pitem = ListView_FastGetItemPtr(plvdi->plv, plvdi->nmcd.nmcd.dwItemSpec);
  11441. }
  11442. // notify on custom draw then do it!
  11443. plvdi->nmcd.nmcd.uItemState = 0;
  11444. plvdi->nmcd.nmcd.lItemlParam = (plvdi->pitem)? plvdi->pitem->lParam : 0;
  11445. if (!(plvdi->flags & LVDI_NOWAYFOCUS))
  11446. {
  11447. if (plvdi->plv->flags & LVF_FOCUSED)
  11448. {
  11449. // if we're ownerdraw or asked to callback, go
  11450. // fetch the state
  11451. if (!plvdi->pitem || (plvdi->plv->stateCallbackMask & (LVIS_SELECTED | LVIS_FOCUSED)))
  11452. {
  11453. state = (WORD) ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec,
  11454. LVIS_SELECTED | LVIS_FOCUSED);
  11455. }
  11456. else
  11457. {
  11458. state = plvdi->pitem->state;
  11459. }
  11460. if (state & LVIS_FOCUSED)
  11461. {
  11462. plvdi->nmcd.nmcd.uItemState |= CDIS_FOCUS;
  11463. }
  11464. if (state & LVIS_SELECTED)
  11465. {
  11466. plvdi->nmcd.nmcd.uItemState |= CDIS_SELECTED;
  11467. }
  11468. }
  11469. // NOTE: This is a bug. We should set CDIS_SELECTED only if the item
  11470. // really is selected. But this bug has existed forever so who knows
  11471. // what apps are relying on it. Standard workaround is for the client
  11472. // to do a GetItemState and reconfirm the LVIS_SELECTED flag.
  11473. // That's what we do in ListView_DrawImageEx.
  11474. if (plvdi->plv->ci.style & LVS_SHOWSELALWAYS)
  11475. {
  11476. plvdi->nmcd.nmcd.uItemState |= CDIS_SELECTED;
  11477. }
  11478. }
  11479. if (!(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS))
  11480. {
  11481. plvdi->nmcd.nmcd.uItemState |= CDIS_SHOWKEYBOARDCUES;
  11482. }
  11483. plvdi->nmcd.clrText = plvdi->plv->clrText;
  11484. plvdi->nmcd.clrTextBk = (plvdi->plv->ci.style & WS_DISABLED ? plvdi->plv->clrBk : plvdi->plv->clrTextBk);
  11485. if ((plvdi->plv->exStyle & LVS_EX_UNDERLINEHOT) &&
  11486. plvdi->plv->iHot == (int)plvdi->nmcd.nmcd.dwItemSpec &&
  11487. (plvdi->plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  11488. ((plvdi->plv->exStyle & LVS_EX_TWOCLICKACTIVATE) &&
  11489. ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec, LVIS_SELECTED)))
  11490. {
  11491. fAllowHotSelection = TRUE;
  11492. // Handle the HOT case
  11493. if (plvdi->plv->clrHotlight != CLR_DEFAULT)
  11494. {
  11495. plvdi->nmcd.clrText = plvdi->plv->clrHotlight;
  11496. }
  11497. else
  11498. {
  11499. plvdi->nmcd.clrText = GetSysColor(COLOR_HOTLIGHT);
  11500. }
  11501. // if hotlight color is the same as the background
  11502. // color you don't see the text -- slam to a visible color in this case.
  11503. if (plvdi->nmcd.clrText == plvdi->nmcd.clrTextBk)
  11504. {
  11505. if (COLORISLIGHT(plvdi->nmcd.clrTextBk))
  11506. plvdi->nmcd.clrText = 0x000000; // black
  11507. else
  11508. plvdi->nmcd.clrText = 0xFFFFFF; // white
  11509. }
  11510. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hFontHot);
  11511. plvdi->nmcd.nmcd.uItemState |= CDIS_HOT;
  11512. }
  11513. else if ((plvdi->plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  11514. ((plvdi->plv->exStyle & LVS_EX_TWOCLICKACTIVATE) &&
  11515. ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec, LVIS_SELECTED)))
  11516. {
  11517. // Handle the non-hot webview case
  11518. if ((plvdi->plv->exStyle & LVS_EX_UNDERLINECOLD) &&
  11519. plvdi->plv->hFontHot)
  11520. {
  11521. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hFontHot);
  11522. }
  11523. else
  11524. {
  11525. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  11526. }
  11527. }
  11528. else
  11529. {
  11530. // Handle the non-webview case
  11531. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  11532. }
  11533. plvdi->dwCustom = CICustomDrawNotify(&plvdi->plv->ci, CDDS_ITEMPREPAINT, &plvdi->nmcd.nmcd);
  11534. plvdi->flags &= ~(LVDI_FOCUS | LVDI_SELECTED);
  11535. if (plvdi->nmcd.nmcd.uItemState & CDIS_FOCUS)
  11536. plvdi->flags |= LVDI_FOCUS;
  11537. if (plvdi->nmcd.nmcd.uItemState & CDIS_SELECTED)
  11538. {
  11539. if (plvdi->plv->flags & LVF_FOCUSED)
  11540. plvdi->flags |= LVDI_SELECTED;
  11541. else
  11542. plvdi->flags |= LVDI_SELECTNOFOCUS;
  11543. if (plvdi->plv->iHot == (int)plvdi->nmcd.nmcd.dwItemSpec && fAllowHotSelection)
  11544. plvdi->flags |= LVDI_HOTSELECTED;
  11545. }
  11546. if (!(plvdi->dwCustom & CDRF_SKIPDEFAULT))
  11547. {
  11548. if (!ListView_IsOwnerData(plvdi->plv))
  11549. {
  11550. if (plvdi->dwCustom & CDRF_NEWFONT)
  11551. {
  11552. _ListView_RecomputeLabelSize(plvdi->plv, plvdi->pitem, (int) plvdi->nmcd.nmcd.dwItemSpec, plvdi->nmcd.nmcd.hdc, FALSE);
  11553. }
  11554. }
  11555. bRet = _ListView_DrawItem(plvdi);
  11556. if (plvdi->dwCustom & CDRF_NOTIFYPOSTPAINT)
  11557. {
  11558. plvdi->nmcd.iSubItem = 0;
  11559. CICustomDrawNotify(&plvdi->plv->ci, CDDS_ITEMPOSTPAINT, &plvdi->nmcd.nmcd);
  11560. }
  11561. if (plvdi->dwCustom & CDRF_NEWFONT)
  11562. {
  11563. SelectObject(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  11564. plvdi->plv->flags |= LVF_CUSTOMFONT;
  11565. }
  11566. }
  11567. return bRet;
  11568. }
  11569. void WINAPI SHThemeDrawText(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCTSTR pszText, RECT* prc, int fmt,
  11570. UINT flags, int cyChar, int cxEllipses, COLORREF clrText, COLORREF clrTextBk)
  11571. {
  11572. int cchText;
  11573. COLORREF clrSave = GetTextColor(hdc), clrSaveBk = 0;
  11574. RECT rc;
  11575. UINT uETOFlags = 0;
  11576. BOOL fForeOnly = FALSE;
  11577. TCHAR ach[CCHLABELMAX + CCHELLIPSES];
  11578. int align;
  11579. BOOL fUseShadowedText = (flags & SHDT_SHADOWTEXT) && (!g_fHighContrast);
  11580. if (!pszText)
  11581. return;
  11582. if (IsRectEmpty(prc))
  11583. return;
  11584. if (flags & SHDT_RTLREADING)
  11585. {
  11586. align = GetTextAlign(hdc);
  11587. SetTextAlign(hdc, align | TA_RTLREADING);
  11588. }
  11589. rc = *prc;
  11590. if (fUseShadowedText)
  11591. {
  11592. if (!AreAllMonitorsAtLeast(16))
  11593. fUseShadowedText = FALSE;
  11594. }
  11595. // If needed, add in a little extra margin...
  11596. //
  11597. if (flags & SHDT_EXTRAMARGIN)
  11598. {
  11599. rc.left += g_cxLabelMargin * 3;
  11600. rc.right -= g_cxLabelMargin * 3;
  11601. }
  11602. else if (!(flags & SHDT_NOMARGIN))
  11603. {
  11604. rc.left += g_cxLabelMargin;
  11605. rc.right -= g_cxLabelMargin;
  11606. }
  11607. if ((rc.left >= rc.right) && !(flags & (SHDT_SELECTED | SHDT_DESELECTED | SHDT_SELECTNOFOCUS)))
  11608. return;
  11609. if ((flags & SHDT_ELLIPSES) &&
  11610. ListView_NeedsEllipses(hdc, pszText, &rc, &cchText, cxEllipses))
  11611. {
  11612. // In some cases cchText was comming back bigger than
  11613. // ARRYASIZE(ach), so we need to make sure we don't overflow the buffer
  11614. // if cchText is too big for the buffer, truncate it down to size
  11615. if (cchText >= ARRAYSIZE(ach) - CCHELLIPSES)
  11616. {
  11617. cchText = ARRAYSIZE(ach) - CCHELLIPSES - 1;
  11618. }
  11619. memcpy(ach, pszText, cchText * sizeof(TCHAR));
  11620. StringCchCopy(ach+cchText, ARRAYSIZE(ach)-cchText, c_szEllipses);
  11621. pszText = ach;
  11622. // Left-justify, in case there's no room for all of ellipses
  11623. //
  11624. fmt = LVCFMT_LEFT;
  11625. cchText += CCHELLIPSES;
  11626. }
  11627. else
  11628. {
  11629. cchText = lstrlen(pszText);
  11630. }
  11631. if (((clrTextBk == CLR_NONE) && !(flags & (SHDT_SELECTED | SHDT_SELECTNOFOCUS))) || (flags & SHDT_TRANSPARENT))
  11632. {
  11633. fForeOnly = TRUE;
  11634. clrSave = SetTextColor(hdc, (flags & SHDT_TRANSPARENT) ? 0 : clrText);
  11635. }
  11636. else if (!hTheme || clrTextBk != CLR_NONE)
  11637. {
  11638. HBRUSH hbrUse = NULL;
  11639. HBRUSH hbrDelete = NULL;
  11640. uETOFlags |= ETO_OPAQUE;
  11641. if ((flags & SHDT_SELECTED || flags & SHDT_SELECTNOFOCUS) && !(flags & SHDT_NOSELECTED))
  11642. {
  11643. fUseShadowedText = FALSE;
  11644. if (flags & SHDT_SELECTNOFOCUS)
  11645. {
  11646. clrText = g_clrBtnText;
  11647. clrTextBk = g_clrBtnFace;
  11648. if (flags & SHDT_DRAWTEXT)
  11649. {
  11650. hbrUse = g_hbrBtnFace;
  11651. }
  11652. }
  11653. else
  11654. {
  11655. clrText = g_clrHighlightText;
  11656. if (flags & SHDT_HOTSELECTED)
  11657. clrTextBk = GetSysColor(COLOR_HOTLIGHT);
  11658. else
  11659. clrTextBk = g_clrHighlight;
  11660. if (flags & SHDT_DRAWTEXT)
  11661. hbrUse = (flags & SHDT_HOTSELECTED)?GetSysColorBrush(COLOR_HOTLIGHT): g_hbrHighlight;
  11662. }
  11663. }
  11664. else if (clrText == CLR_DEFAULT && clrTextBk == CLR_DEFAULT)
  11665. {
  11666. fUseShadowedText = FALSE;
  11667. clrText = g_clrWindowText;
  11668. clrTextBk = g_clrWindow;
  11669. if ((flags & (SHDT_DRAWTEXT | SHDT_DESELECTED)) ==
  11670. (SHDT_DRAWTEXT | SHDT_DESELECTED))
  11671. {
  11672. hbrUse = g_hbrWindow;
  11673. }
  11674. }
  11675. else
  11676. {
  11677. if (clrText == CLR_DEFAULT)
  11678. clrText = g_clrWindowText;
  11679. if (clrTextBk == CLR_DEFAULT)
  11680. clrTextBk = g_clrWindow;
  11681. if (fUseShadowedText == FALSE &&
  11682. ((flags & (SHDT_DRAWTEXT | SHDT_DESELECTED)) ==
  11683. (SHDT_DRAWTEXT | SHDT_DESELECTED) || hTheme))
  11684. {
  11685. hbrUse = CreateSolidBrush(GetNearestColor(hdc, clrTextBk));
  11686. if (hbrUse)
  11687. {
  11688. hbrDelete = hbrUse;
  11689. }
  11690. else
  11691. hbrUse = GetStockObject(WHITE_BRUSH);
  11692. }
  11693. }
  11694. // now set it
  11695. clrSave = SetTextColor(hdc, clrText);
  11696. clrSaveBk = SetBkColor(hdc, clrTextBk);
  11697. if (hbrUse)
  11698. {
  11699. FillRect(hdc, prc, hbrUse);
  11700. if (hbrDelete)
  11701. DeleteObject(hbrDelete);
  11702. }
  11703. }
  11704. // If we want the item to display as if it was depressed, we will
  11705. // offset the text rectangle down and to the left
  11706. if (flags & SHDT_DEPRESSED)
  11707. OffsetRect(&rc, g_cxBorder, g_cyBorder);
  11708. if (flags & SHDT_DRAWTEXT || hTheme)
  11709. {
  11710. HRESULT hr = E_FAIL;
  11711. UINT uDTFlags;
  11712. if (flags & SHDT_DRAWTEXT)
  11713. {
  11714. uDTFlags= DT_LVWRAP | DT_END_ELLIPSIS;
  11715. }
  11716. else
  11717. {
  11718. uDTFlags = DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER;
  11719. if (fmt & LVCFMT_CENTER)
  11720. uDTFlags |= DT_CENTER;
  11721. else if (fmt & LVCFMT_RIGHT)
  11722. uDTFlags |= DT_RIGHT;
  11723. }
  11724. if (flags & SHDT_DTELLIPSIS)
  11725. uDTFlags |= DT_WORD_ELLIPSIS;
  11726. if (!(flags & SHDT_CLIPPED))
  11727. uDTFlags |= DT_NOCLIP;
  11728. if (flags & SHDT_NODBCSBREAK)
  11729. uDTFlags |= DT_NOFULLWIDTHCHARBREAK;
  11730. if (flags & SHDT_VCENTER)
  11731. uDTFlags |= DT_VCENTER | DT_SINGLELINE;
  11732. if (flags & SHDT_LEFT)
  11733. uDTFlags = DT_LEFT | uDTFlags & ~DT_CENTER;
  11734. if (fUseShadowedText)
  11735. {
  11736. DrawShadowText(hdc, pszText, cchText, &rc, uDTFlags, RGB(255,255,255), RGB(0,0,0), 2, 2);
  11737. }
  11738. else
  11739. {
  11740. if (hTheme)
  11741. hr = DrawThemeText(hTheme, hdc, iPartId, iStateId, pszText, -1, uDTFlags, 0, &rc);
  11742. if (FAILED(hr))
  11743. DrawText(hdc, pszText, cchText, &rc, uDTFlags);
  11744. }
  11745. }
  11746. else
  11747. {
  11748. if (fmt != LVCFMT_LEFT)
  11749. {
  11750. SIZE siz;
  11751. GetTextExtentPoint(hdc, pszText, cchText, &siz);
  11752. if (fmt == LVCFMT_CENTER)
  11753. rc.left = (rc.left + rc.right - siz.cx) / 2;
  11754. else // fmt == LVCFMT_RIGHT
  11755. rc.left = rc.right - siz.cx;
  11756. }
  11757. // Center vertically in case the bitmap (to the left) is larger than
  11758. // the height of one line
  11759. rc.top += (rc.bottom - rc.top - cyChar) / 2;
  11760. if (flags & SHDT_CLIPPED)
  11761. uETOFlags |= ETO_CLIPPED;
  11762. // HACK: ExtTextOut() has an off-by-one bug in its rendering of RTL
  11763. // text. We need this hack to render properly (RAID 439915).
  11764. // Note that this bug is NOT present in the DrawText() API.
  11765. if (flags & SHDT_RTLREADING)
  11766. rc.left--;
  11767. ExtTextOut(hdc, rc.left, rc.top, uETOFlags, prc, pszText, cchText, NULL);
  11768. }
  11769. if (flags & (SHDT_SELECTED | SHDT_DESELECTED | SHDT_TRANSPARENT))
  11770. {
  11771. SetTextColor(hdc, clrSave);
  11772. if (!fForeOnly)
  11773. SetBkColor(hdc, clrSaveBk);
  11774. }
  11775. if (flags & SHDT_RTLREADING)
  11776. SetTextAlign(hdc, align);
  11777. }
  11778. void WINAPI SHDrawText(HDC hdc, LPCTSTR pszText, RECT* prc, int fmt,
  11779. UINT flags, int cyChar, int cxEllipses, COLORREF clrText, COLORREF clrTextBk)
  11780. {
  11781. SHThemeDrawText(NULL, hdc, 0, 0, pszText, prc, fmt,
  11782. flags, cyChar, cxEllipses, clrText, clrTextBk);
  11783. }
  11784. /*----------------------------------------------------------------
  11785. ** Create an imagelist to be used for dragging.
  11786. **
  11787. ** 1) create mask and image bitmap matching the select bounds size
  11788. ** 2) draw the text to both bitmaps (in black for now)
  11789. ** 3) create an imagelist with these bitmaps
  11790. ** 4) make a dithered copy of the image onto the new imagelist
  11791. **----------------------------------------------------------------*/
  11792. HIMAGELIST ListView_OnCreateDragImage(LV *plv, int iItem, LPPOINT lpptUpLeft)
  11793. {
  11794. HWND hwndLV = plv->ci.hwnd;
  11795. RECT rcBounds, rcImage, rcLabel;
  11796. HDC hdcMem = NULL;
  11797. HBITMAP hbmImage = NULL;
  11798. HBITMAP hbmMask = NULL;
  11799. HBITMAP hbmOld;
  11800. HIMAGELIST himl = NULL;
  11801. int dx, dy;
  11802. HIMAGELIST himlSrc;
  11803. LV_ITEM item;
  11804. POINT ptOrg;
  11805. LVDRAWITEM lvdi = {0};
  11806. RECT rcSelBounds;
  11807. BOOL bMirroredWnd = (plv->ci.dwExStyle&RTL_MIRRORED_WINDOW);
  11808. int iImageList;
  11809. if (!lpptUpLeft)
  11810. return NULL;
  11811. if (iItem >= ListView_Count(plv))
  11812. return NULL;
  11813. if (plv->iHot == iItem)
  11814. {
  11815. ListView_OnSetHotItem(plv, -1);
  11816. UpdateWindow(plv->ci.hwnd);
  11817. }
  11818. ListView_GetRects(plv, iItem, QUERY_DEFAULT, &rcImage, &rcLabel, &rcBounds, &rcSelBounds);
  11819. if (ListView_IsIconView(plv))
  11820. {
  11821. ListView_UnfoldRects(plv, iItem, &rcImage, &rcLabel,
  11822. &rcBounds, &rcSelBounds);
  11823. InflateRect(&rcImage, -g_cxIconMargin, -g_cyIconMargin);
  11824. }
  11825. // chop off any extra filler above icon
  11826. ptOrg.x = rcBounds.left - rcSelBounds.left;
  11827. ptOrg.y = rcBounds.top - rcImage.top;
  11828. dx = rcSelBounds.right - rcSelBounds.left;
  11829. dy = rcSelBounds.bottom - rcImage.top;
  11830. lpptUpLeft->x = rcSelBounds.left;
  11831. lpptUpLeft->y = rcImage.top;
  11832. if (!(hdcMem = CreateCompatibleDC(NULL)))
  11833. goto CDI_Exit;
  11834. if (!(hbmImage = CreateColorBitmap(dx, dy)))
  11835. goto CDI_Exit;
  11836. if (!(hbmMask = CreateMonoBitmap(dx, dy)))
  11837. goto CDI_Exit;
  11838. //
  11839. // Mirror the memory DC so that the transition from
  11840. // mirrored(memDC)->non-mirrored(imagelist DCs)->mirrored(screenDC)
  11841. // is consistent. [samera]
  11842. //
  11843. if (bMirroredWnd)
  11844. {
  11845. SET_DC_RTL_MIRRORED(hdcMem);
  11846. }
  11847. // prepare for drawing the item
  11848. SelectObject(hdcMem, plv->hfontLabel);
  11849. SetBkMode(hdcMem, TRANSPARENT);
  11850. lvdi.plv = plv;
  11851. lvdi.nmcd.nmcd.dwItemSpec = iItem;
  11852. lvdi.pitem = NULL; // make sure it is null as Owner data uses this to trigger things...
  11853. lvdi.nmcd.nmcd.hdc = hdcMem;
  11854. lvdi.lpptOrg = &ptOrg;
  11855. lvdi.prcClip = NULL;
  11856. lvdi.flags = LVDI_NOIMAGE | LVDI_TRANSTEXT | LVDI_NOWAYFOCUS | LVDI_UNFOLDED;
  11857. lvdi.nmcd.clrFace = CLR_NONE;
  11858. /*
  11859. ** draw the text to both bitmaps
  11860. */
  11861. hbmOld = SelectObject(hdcMem, hbmImage);
  11862. // fill image with black for transparency
  11863. PatBlt(hdcMem, 0, 0, dx, dy, BLACKNESS);
  11864. ListView_DrawItem(&lvdi);
  11865. if (bMirroredWnd)
  11866. MirrorBitmapInDC(hdcMem, hbmImage);
  11867. lvdi.flags = LVDI_NOIMAGE | LVDI_TRANSTEXT | LVDI_NOWAYFOCUS | LVDI_UNFOLDED;
  11868. SelectObject(hdcMem, hbmMask);
  11869. // fill mask with white for transparency
  11870. PatBlt(hdcMem, 0, 0, dx, dy, WHITENESS);
  11871. ListView_DrawItem(&lvdi);
  11872. if (bMirroredWnd)
  11873. MirrorBitmapInDC(hdcMem, hbmMask);
  11874. // unselect objects that we used
  11875. SelectObject(hdcMem, hbmOld);
  11876. SelectObject(hdcMem, g_hfontSystem);
  11877. if (ListView_IsIconView(plv) || ListView_IsTileView(plv))
  11878. iImageList = LVSIL_NORMAL;
  11879. else
  11880. iImageList = LVSIL_SMALL;
  11881. himlSrc = ListView_OnGetImageList(plv, iImageList);
  11882. /*
  11883. ** make an image list that for now only has the text
  11884. ** we use ImageList_Clone so we get a imagelist that
  11885. ** the same color depth as our own imagelist
  11886. */
  11887. if (!(himl = ImageList_Clone(himlSrc, dx, dy, ILC_MASK, 1, 0)))
  11888. goto CDI_Exit;
  11889. ImageList_SetBkColor(himl, CLR_NONE);
  11890. ImageList_Add(himl, hbmImage, hbmMask);
  11891. /*
  11892. ** make a dithered copy of the image part onto our bitmaps
  11893. ** (need both bitmap and mask to be dithered)
  11894. */
  11895. if (himlSrc)
  11896. {
  11897. item.iItem = iItem;
  11898. item.iSubItem = 0;
  11899. item.mask = LVIF_IMAGE |LVIF_STATE;
  11900. item.stateMask = LVIS_OVERLAYMASK;
  11901. ListView_OnGetItem(plv, &item);
  11902. ImageList_CopyDitherImage(himl, 0, rcImage.left - rcSelBounds.left, 0, himlSrc, item.iImage, ((plv->ci.dwExStyle & dwExStyleRTLMirrorWnd) ? ILD_MIRROR : 0L) | (item.state & LVIS_OVERLAYMASK));
  11903. }
  11904. CDI_Exit:
  11905. if (hdcMem)
  11906. DeleteObject(hdcMem);
  11907. if (hbmImage)
  11908. DeleteObject(hbmImage);
  11909. if (hbmMask)
  11910. DeleteObject(hbmMask);
  11911. return himl;
  11912. }
  11913. // ListView_OnGetTopIndex -- Gets the index of the first visible item
  11914. // For list view and report view this calculates the actual index
  11915. // for iconic views it alway returns 0
  11916. //
  11917. int ListView_OnGetTopIndex(LV* plv)
  11918. {
  11919. if (ListView_IsReportView(plv) && !plv->fGroupView)
  11920. return (int)((plv->ptlRptOrigin.y) / plv->cyItem);
  11921. else if (ListView_IsListView(plv))
  11922. return (plv->xOrigin / plv->cxItem) * plv->cItemCol;
  11923. else
  11924. return 0;
  11925. }
  11926. // ListView_OnGetCountPerPage -- Gets the count of items that will fit
  11927. // on a page For list view and report view this calculates the
  11928. // count depending on the size of the window and for Iconic views it
  11929. // will always return the count of items in the list view.
  11930. //
  11931. int ListView_OnGetCountPerPage(LV* plv)
  11932. {
  11933. if (ListView_IsReportView(plv))
  11934. return (plv->sizeClient.cy - plv->yTop) / plv->cyItem;
  11935. else if (ListView_IsListView(plv))
  11936. return ((plv->sizeClient.cx)/ plv->cxItem)
  11937. * plv->cItemCol;
  11938. else
  11939. return (ListView_Count(plv));
  11940. }
  11941. /* Purpose:
  11942. / Provides support for invalidating items within list views.
  11943. /
  11944. / Notes:
  11945. / Copes with invalidating the extra region in the list view that requires
  11946. / us to erase the background. Design to optimise out the ERASURE of the
  11947. / background.
  11948. /
  11949. / For details on the API see ListView_InvalidateItem.
  11950. /
  11951. / In:
  11952. / plv->ListView structure to work with
  11953. / iItem = item number
  11954. / bSrelectionOnly = refesh the selection
  11955. / fRedraw = Flags for RedrawWindow
  11956. / Out:
  11957. / -
  11958. */
  11959. void ListView_InvalidateFoldedItem(LV* plv, int iItem, BOOL fSelectionOnly, UINT fRedraw)
  11960. {
  11961. ListView_InvalidateItem(plv, iItem, fSelectionOnly, fRedraw);
  11962. if (ListView_IsIconView(plv) &&
  11963. (!ListView_IsItemUnfolded(plv, iItem) || (fRedraw & RDW_ERASE)))
  11964. {
  11965. RECT rcLabel;
  11966. if (ListView_GetUnfoldedRect(plv, iItem, &rcLabel))
  11967. {
  11968. RedrawWindow(plv->ci.hwnd, &rcLabel, NULL, fRedraw|RDW_ERASE);
  11969. }
  11970. }
  11971. }
  11972. /*
  11973. / Purpose:
  11974. / Having previously called get rects, then call this function to ensure
  11975. / that they are correctly unfolded.
  11976. /
  11977. / Notes:
  11978. / -
  11979. /
  11980. / In:
  11981. / plv-> list view to unfold on
  11982. / iItem = item number
  11983. / prcIcon -> icon bounding box
  11984. / prcLabel -> rectangle for the label structure
  11985. / prcBounds -> bounds rectangle / == NULL for none / These are currently the same for large icons
  11986. / prcSelectBounds -> selection bounds / == NULL /
  11987. / Out: TRUE if unfolding the item was worth anything
  11988. / -
  11989. */
  11990. BOOL ListView_UnfoldRects(LV* plv, int iItem,
  11991. RECT * prcIcon, RECT * prcLabel,
  11992. RECT * prcBounds, RECT * prcSelectBounds)
  11993. {
  11994. LISTITEM item;
  11995. LISTITEM* pitem = &item;
  11996. BOOL fRc = FALSE;
  11997. if (!ListView_IsIconView(plv))
  11998. return fRc;
  11999. // If we have a label pointer then expand as required
  12000. // nb - different paths for owner data
  12001. if (prcLabel)
  12002. {
  12003. if (!ListView_IsOwnerData(plv))
  12004. {
  12005. pitem = ListView_GetItemPtr(plv, iItem);
  12006. if (!EVAL(pitem))
  12007. {
  12008. // DavidShi was able to get us into here with an invalid
  12009. // item number during a delete notification. So if the
  12010. // item number is invalid, just return a blank rectangle
  12011. // instead of faulting.
  12012. SetRectEmpty(prcLabel);
  12013. goto doneLabel;
  12014. }
  12015. }
  12016. else
  12017. {
  12018. _ListView_RecomputeLabelSize(plv, pitem, iItem, NULL, FALSE);
  12019. }
  12020. if (prcLabel->bottom != prcLabel->top + max(pitem->cyUnfoldedLabel, pitem->cyFoldedLabel))
  12021. fRc = TRUE;
  12022. // In HideLabel mode it's always "worthwhile" to "unfold" the rects because the label is not shown
  12023. // by default. By returning TRUE we cause the item's label to display in a tooltip where the label
  12024. // would normally be.
  12025. if (ListView_HideLabels(plv))
  12026. fRc = TRUE;
  12027. prcLabel->bottom = prcLabel->top + pitem->cyUnfoldedLabel;
  12028. }
  12029. doneLabel:
  12030. // Build the unions if required
  12031. if (prcBounds && prcIcon && prcLabel)
  12032. {
  12033. ListView_CalcBounds(plv, QUERY_UNFOLDED, prcIcon, prcLabel, prcBounds);
  12034. }
  12035. if (prcSelectBounds && prcIcon && prcLabel)
  12036. {
  12037. if (ListView_HideLabels(plv))
  12038. *prcBounds = *prcIcon;
  12039. else
  12040. UnionRect(prcSelectBounds, prcIcon, prcLabel);
  12041. }
  12042. return fRc;
  12043. }
  12044. void ListView_InvalidateMark(LV* plv)
  12045. {
  12046. RECT rc;
  12047. if (ListView_OnGetInsertMarkRect(plv, &rc))
  12048. {
  12049. OffsetRect(&rc, -plv->ptOrigin.x, -plv->ptOrigin.y);
  12050. InvalidateRect(plv->ci.hwnd, &rc, TRUE);
  12051. }
  12052. }
  12053. // Returns the insertmark rect in listview coordinates. Returns FALSE if there is no insertmarkrect
  12054. BOOL ListView_OnGetInsertMarkRect(LV* plv, LPRECT prc)
  12055. {
  12056. BOOL fVert;
  12057. RECT rcSlot;
  12058. LISTITEM *pitem;
  12059. if (plv->iInsertItem == -1)
  12060. return FALSE;
  12061. pitem = ListView_GetItemPtr(plv, plv->iInsertItem);
  12062. if (!pitem)
  12063. {
  12064. return FALSE;
  12065. }
  12066. fVert = !((plv->ci.style & LVS_ALIGNMASK) == LVS_ALIGNTOP);
  12067. ListView_CalcItemSlotAndRect(plv, pitem, NULL, &rcSlot);
  12068. if (fVert)
  12069. {
  12070. int iY;
  12071. prc->left = rcSlot.left;
  12072. prc->right = rcSlot.right;
  12073. iY = (plv->fInsertAfter) ? rcSlot.bottom : rcSlot.top;
  12074. prc->top = iY - INSERTMARKSIZE/2;
  12075. prc->bottom = iY + INSERTMARKSIZE/2 + 1;
  12076. }
  12077. else
  12078. {
  12079. int iX;
  12080. prc->top = rcSlot.top;
  12081. prc->bottom = rcSlot.bottom;
  12082. iX = (plv->fInsertAfter) ? rcSlot.right : rcSlot.left;
  12083. prc->left = iX - INSERTMARKSIZE/2;
  12084. prc->right = iX + INSERTMARKSIZE/2 + 1;
  12085. }
  12086. return TRUE;
  12087. }
  12088. COLORREF ListView_OnGetInsertMarkColor(LV* plv)
  12089. {
  12090. if (plv->clrim == CLR_DEFAULT)
  12091. return plv->clrText;
  12092. else
  12093. return plv->clrim;
  12094. }