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

11351 lines
349 KiB

  1. #include "ctlspriv.h"
  2. #include "listview.h"
  3. #include "image.h"
  4. #include <mlang.h>
  5. #include <inetreg.h>
  6. #define __IOleControl_INTERFACE_DEFINED__ // There is a conflict with the IOleControl's def of CONTROLINFO
  7. #include "shlobj.h"
  8. #define IE_SETTINGS TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced")
  9. #define USE_DBL_CLICK_TIMER TEXT("UseDoubleClickTimer")
  10. int g_bUseDblClickTimer;
  11. #define LVMP_WINDOWPOSCHANGED (WM_USER + 1)
  12. HRESULT WINAPI UninitializeFlatSB(HWND hwnd);
  13. #define COLORISLIGHT(clr) ((5*GetGValue((clr)) + 2*GetRValue((clr)) + GetBValue((clr))) > 8*128)
  14. void NEAR ListView_HandleMouse(LV* plv, BOOL fDoubleClick, int x, int y, UINT keyFlags, BOOL bMouseWheel);
  15. /// function table setup
  16. const PFNLISTVIEW_DRAWITEM pfnListView_DrawItem[4] = {
  17. ListView_IDrawItem,
  18. ListView_RDrawItem,
  19. ListView_IDrawItem,
  20. ListView_LDrawItem,
  21. };
  22. void ListView_HandleStateIconClick(LV* plv, int iItem);
  23. DWORD ListView_IApproximateViewRect(LV* ,int, int, int);
  24. DWORD ListView_RApproximateViewRect(LV* ,int, int, int);
  25. DWORD ListView_LApproximateViewRect(LV* ,int, int, int);
  26. const PFNLISTVIEW_APPROXIMATEVIEWRECT pfnListView_ApproximateViewRect[4] = {
  27. ListView_IApproximateViewRect,
  28. ListView_RApproximateViewRect,
  29. ListView_IApproximateViewRect,
  30. ListView_LApproximateViewRect,
  31. };
  32. const PFNLISTVIEW_UPDATESCROLLBARS pfnListView_UpdateScrollBars[4] = {
  33. ListView_IUpdateScrollBars,
  34. ListView_RUpdateScrollBars,
  35. ListView_IUpdateScrollBars,
  36. ListView_LUpdateScrollBars,
  37. };
  38. const PFNLISTVIEW_ITEMHITTEST pfnListView_ItemHitTest[4] = {
  39. ListView_IItemHitTest,
  40. ListView_RItemHitTest,
  41. ListView_SItemHitTest,
  42. ListView_LItemHitTest,
  43. };
  44. const PFNLISTVIEW_ONSCROLL pfnListView_OnScroll[4] = {
  45. ListView_IOnScroll,
  46. ListView_ROnScroll,
  47. ListView_IOnScroll,
  48. ListView_LOnScroll,
  49. };
  50. const PFNLISTVIEW_SCROLL2 pfnListView_Scroll2[4] = {
  51. ListView_IScroll2,
  52. ListView_RScroll2,
  53. ListView_IScroll2,
  54. ListView_LScroll2,
  55. };
  56. const PFNLISTVIEW_GETSCROLLUNITSPERLINE pfnListView_GetScrollUnitsPerLine[4] = {
  57. ListView_IGetScrollUnitsPerLine,
  58. ListView_RGetScrollUnitsPerLine,
  59. ListView_IGetScrollUnitsPerLine,
  60. ListView_LGetScrollUnitsPerLine,
  61. };
  62. // redefine to trace at most calls to ListView_SendChange
  63. #define DM_LVSENDCHANGE 0
  64. // BUGBUG -- penwin.h is messed up; define local stuff for now
  65. #define HN_BEGINDIALOG 40 // Lens/EditText/garbage detection dialog is about
  66. // to come up on this hedit/bedit
  67. #define HN_ENDDIALOG 41 // Lens/EditText/garbage detection dialog has
  68. // just been destroyed
  69. //---------------------------------------------------------
  70. // no way am I gonna make TWO function calls where I can do FOUR comparisons!
  71. //
  72. #define RECTS_IN_SIZE( sz, r2 ) (!RECTS_NOT_IN_SIZE( sz, r2 ))
  73. #define RECTS_NOT_IN_SIZE( sz, r2 ) (\
  74. ( (sz).cx <= (r2).left ) ||\
  75. ( 0 >= (r2).right ) ||\
  76. ( (sz).cy <= (r2).top ) ||\
  77. ( 0 >= (r2).bottom ) )
  78. //---------------------------------------------------------
  79. void NEAR ListView_OnUpdate(LV* plv, int i);
  80. void NEAR ListView_OnDestroy(LV* plv);
  81. BOOL NEAR PASCAL ListView_ValidateScrollParams(LV* plv, int FAR * dx, int FAR *dy);
  82. UINT LV_IsItemOnViewEdge(LV* plv, LISTITEM *pitem);
  83. void PASCAL ListView_ButtonSelect(LV* plv, int iItem, UINT keyFlags, BOOL bSelected);
  84. void NEAR ListView_DeselectAll(LV* plv, int iDontDeselect);
  85. void ListView_LRInvalidateBelow(LV* plv, int i, int fSmoothScroll);
  86. void ListView_IInvalidateBelow(LV* plv, int i);
  87. void NEAR ListView_InvalidateFoldedItem(LV* plv, int iItem, BOOL fSelectionOnly, UINT fRedraw);
  88. void ListView_ReleaseBkImage(LV *plv);
  89. void ListView_RecalcRegion(LV *plv, BOOL fForce, BOOL fRedraw);
  90. BOOL g_fSlowMachine = -1;
  91. #pragma code_seg(CODESEG_INIT)
  92. BOOL FAR ListView_Init(HINSTANCE hinst)
  93. {
  94. WNDCLASS wc;
  95. if (!GetClassInfo(hinst, c_szListViewClass, &wc)) {
  96. #ifndef WIN32
  97. LRESULT CALLBACK _ListView_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  98. wc.lpfnWndProc = _ListView_WndProc;
  99. #else
  100. wc.lpfnWndProc = ListView_WndProc;
  101. #endif
  102. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  103. wc.hIcon = NULL;
  104. wc.lpszMenuName = NULL;
  105. wc.hInstance = hinst;
  106. wc.lpszClassName = c_szListViewClass;
  107. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // NULL;
  108. wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
  109. wc.cbWndExtra = sizeof(LV*);
  110. wc.cbClsExtra = 0;
  111. return RegisterClass(&wc);
  112. }
  113. return TRUE;
  114. }
  115. #pragma code_seg()
  116. BOOL NEAR ListView_GetRegIASetting(BOOL *pb)
  117. {
  118. HKEY hkey;
  119. BOOL bRet = FALSE;
  120. BOOL bValue = TRUE;
  121. if (RegOpenKeyEx(HKEY_CURRENT_USER, IE_SETTINGS, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
  122. {
  123. DWORD dwType;
  124. DWORD dwValue;
  125. DWORD cbValue = sizeof(DWORD);
  126. if (RegQueryValueEx(hkey, (LPTSTR)USE_DBL_CLICK_TIMER, 0, &dwType, (LPBYTE)&dwValue, &cbValue) == ERROR_SUCCESS)
  127. {
  128. bValue = (BOOL)dwValue;
  129. bRet = TRUE;
  130. }
  131. RegCloseKey(hkey);
  132. }
  133. *pb = bValue;
  134. return bRet;
  135. }
  136. BOOL NEAR ListView_NotifyCacheHint(LV* plv, int iFrom, int iTo)
  137. {
  138. NM_CACHEHINT nm;
  139. ASSERT( iFrom <= iTo );
  140. if (iFrom <= iTo)
  141. {
  142. nm.iFrom = iFrom;
  143. nm.iTo = iTo;
  144. return !(BOOL)CCSendNotify(&plv->ci, LVN_ODCACHEHINT, &nm.hdr);
  145. }
  146. return FALSE;
  147. }
  148. void NEAR ListView_LazyCreateObjects(LV *plv, int iMin, int iMax)
  149. {
  150. for ( ; iMin < iMax; iMin++)
  151. MyNotifyWinEvent(EVENT_OBJECT_CREATE, plv->ci.hwnd, OBJID_CLIENT, 1 + iMin);
  152. }
  153. //
  154. // Owner-data causes MSAA lots of grief, because there is no way to tell
  155. // MSAA "I just created 25 million items". You have to tell it one at a
  156. // time. Instead of sending out 25 million "add item" notifications, we
  157. // just send them out as they scroll into view.
  158. //
  159. // plv->iMSAAMin and plv->iMSAAMax are the range of items we most
  160. // recently told MSAA about. MSAAMax is *exclusive*, just like RECTs.
  161. // It makes the math easier.
  162. //
  163. // We use iMSAAMin and iMSAAMax to avoid sending blatantly redundant
  164. // notifications, which would other happen very frequently.
  165. //
  166. void NEAR ListView_LazyCreateWinEvents(LV *plv, int iFrom, int iTo)
  167. {
  168. int iMin = iFrom;
  169. int iMax = iTo+1; // Convert from [From,To] to [Min,Max)
  170. #ifdef LVDEBUG
  171. DebugMsg(TF_LISTVIEW, TEXT("lv.CWE old: [%d,%d), new=[%d,%d)"),
  172. plv->iMSAAMin, plv->iMSAAMax,
  173. iMin, iMax);
  174. #endif
  175. //
  176. // If the incoming range is entirely contained within the existing
  177. // range, then there is nothing to do. This happens a lot.
  178. //
  179. if (iMin >= plv->iMSAAMin && iMax <= plv->iMSAAMax)
  180. return;
  181. //
  182. // If the incoming range is adjacent to or overlaps the low end
  183. // of the existing range... (This happens when scrolling backwards.)
  184. //
  185. if (iMin <= plv->iMSAAMin && iMax >= plv->iMSAAMin) {
  186. // Notify the low end.
  187. ListView_LazyCreateObjects(plv, iMin, plv->iMSAAMin);
  188. // Extend the list of things we've notified.
  189. plv->iMSAAMin = iMin;
  190. // Remove it from the things left to be notified.
  191. iMin = plv->iMSAAMax;
  192. }
  193. //
  194. // Now do the same thing to the top end.
  195. // (This happens when scrolling forwards.)
  196. //
  197. if (iMax >= plv->iMSAAMax && iMin <= plv->iMSAAMax) {
  198. // Notify the top end.
  199. ListView_LazyCreateObjects(plv, plv->iMSAAMax, iMax);
  200. // Extend the list of things we've notified.
  201. plv->iMSAAMax = iMax;
  202. // Remove it from the things left to be notified.
  203. iMax = plv->iMSAAMin;
  204. }
  205. //
  206. // If there are still things to be notified, then it means that the
  207. // incoming range isn't contiguous with the previous range, so throw
  208. // away the old range and just set it to the current range.
  209. // (This happens when you grab the scrollbar and jump to a completely
  210. // unrelated part of the listview.)
  211. //
  212. if (iMin < iMax) {
  213. plv->iMSAAMin = iMin;
  214. plv->iMSAAMax = iMax;
  215. ListView_LazyCreateObjects(plv, iMin, iMax);
  216. }
  217. #ifdef LVDEBUG
  218. DebugMsg(TF_LISTVIEW, TEXT("lv.CWE aft: [%d,%d)"), plv->iMSAAMin, plv->iMSAAMax);
  219. #endif
  220. }
  221. LRESULT NEAR ListView_RequestFindItem(LV* plv, CONST LV_FINDINFO* plvfi, int iStart)
  222. {
  223. NM_FINDITEM nm;
  224. nm.lvfi = *plvfi;
  225. nm.iStart = iStart;
  226. return CCSendNotify(&plv->ci, LVN_ODFINDITEM, &nm.hdr);
  227. }
  228. BOOL NEAR ListView_SendChange(LV* plv, int i, int iSubItem, int code, UINT oldState, UINT newState,
  229. UINT changed, LPARAM lParam)
  230. {
  231. NM_LISTVIEW nm;
  232. nm.iItem = i;
  233. nm.iSubItem = iSubItem;
  234. nm.uNewState = newState;
  235. nm.uOldState = oldState;
  236. nm.uChanged = changed;
  237. nm.ptAction.x = 0;
  238. nm.ptAction.y = 0;
  239. nm.lParam = lParam;
  240. return !CCSendNotify(&plv->ci, code, &nm.hdr);
  241. }
  242. void NEAR ListView_SendODChangeAndInvalidate(LV* plv, int iFrom, int iTo, UINT oldState,
  243. UINT newState)
  244. {
  245. NM_ODSTATECHANGE nm;
  246. nm.iFrom = iFrom;
  247. nm.iTo = iTo;
  248. nm.uNewState = newState;
  249. nm.uOldState = oldState;
  250. CCSendNotify(&plv->ci, LVN_ODSTATECHANGED, &nm.hdr);
  251. // Tell accessibility, "Selection changed in a complex way"
  252. MyNotifyWinEvent(EVENT_OBJECT_SELECTIONWITHIN, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  253. // considerable speed increase less than 100 to do this method
  254. // while over 100, the other method works faster
  255. if ((iTo - iFrom) > 100)
  256. {
  257. InvalidateRect( plv->ci.hwnd, NULL, FALSE );
  258. }
  259. else
  260. {
  261. while (iFrom <= iTo)
  262. {
  263. ListView_InvalidateItem(plv, iFrom, TRUE, RDW_INVALIDATE);
  264. iFrom++;
  265. }
  266. }
  267. }
  268. BOOL NEAR ListView_Notify(LV* plv, int i, int iSubItem, int code)
  269. {
  270. NM_LISTVIEW nm;
  271. nm.iItem = i;
  272. nm.iSubItem = iSubItem;
  273. nm.uNewState = nm.uOldState = 0;
  274. nm.uChanged = 0;
  275. nm.lParam = 0;
  276. if (!ListView_IsOwnerData( plv )) {
  277. if (code == LVN_DELETEITEM) {
  278. LISTITEM FAR * pItem = ListView_GetItemPtr(plv, i);
  279. if (pItem) {
  280. nm.lParam = pItem->lParam;
  281. }
  282. }
  283. }
  284. return (BOOL)CCSendNotify(&plv->ci, code, &nm.hdr);
  285. }
  286. BOOL NEAR ListView_GetEmptyText(LV* plv)
  287. {
  288. NMLVDISPINFO nm;
  289. BOOL ret;
  290. TCHAR szText[80];
  291. if (plv->fNoEmptyText)
  292. return FALSE;
  293. if (plv->pszEmptyText)
  294. return TRUE;
  295. // For each listview control, we will only send this notify
  296. // once if necessary.
  297. memset(&nm, 0, SIZEOF(NMLVDISPINFO));
  298. nm.item.mask = LVIF_TEXT;
  299. nm.item.cchTextMax = ARRAYSIZE(szText);
  300. nm.item.pszText = szText;
  301. szText[0] = TEXT('\0');
  302. ret = (BOOL)CCSendNotify(&plv->ci, LVN_GETEMPTYTEXT, &nm.hdr);
  303. if (ret)
  304. // save the text so we don't notify again.
  305. Str_Set(&plv->pszEmptyText, szText);
  306. else
  307. // set a flag so we don't notify again.
  308. plv->fNoEmptyText = TRUE;
  309. return ret;
  310. }
  311. void NEAR ListView_NotifyFocusEvent(LV *plv)
  312. {
  313. if (plv->iFocus != -1 && IsWindowVisible(plv->ci.hwnd) && GetFocus() == plv->ci.hwnd)
  314. MyNotifyWinEvent(EVENT_OBJECT_FOCUS, plv->ci.hwnd, OBJID_CLIENT,
  315. plv->iFocus+1);
  316. }
  317. //
  318. // Call this function when the listview has changed in a radical manner.
  319. // It notifies MSAA that "Whoa, things are completely different now."
  320. //
  321. void NEAR ListView_NotifyRecreate(LV *plv)
  322. {
  323. MyNotifyWinEvent(EVENT_OBJECT_DESTROY, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  324. MyNotifyWinEvent(EVENT_OBJECT_CREATE, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  325. plv->iMSAAMin = plv->iMSAAMax = 0;
  326. }
  327. int NEAR ListView_OnSetItemCount(LV *plv, int iItems, DWORD dwFlags)
  328. {
  329. BOOL frt = TRUE;
  330. // For compatability we assume 0 for flags implies old (Athena) type of functionality and
  331. // does a Invalidate all otherwise if low bit is set we try to be a bit smarter. First pass
  332. // If the first added item is visible invalidate all. Yes we can do better...
  333. if (ListView_IsOwnerData( plv )) {
  334. int iItem;
  335. int cTotalItemsOld = plv->cTotalItems;
  336. BOOL fInvalidateAll = ((dwFlags & LVSICF_NOINVALIDATEALL) == 0);
  337. if ((iItems >= 0) && (iItems <= MAX_LISTVIEWITEMS)) {
  338. plv->cTotalItems = iItems;
  339. // check focus
  340. if (plv->iFocus >= iItems)
  341. plv->iFocus = -1;
  342. if (plv->iDropHilite >= iItems)
  343. plv->iDropHilite = -1;
  344. // check mark
  345. if (plv->iMark >= iItems)
  346. plv->iMark = -1;
  347. // make sure no selections above number of items
  348. plv->plvrangeCut->lpVtbl->ExcludeRange(plv->plvrangeCut, iItems, SELRANGE_MAXVALUE );
  349. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, iItems, SELRANGE_MAXVALUE ))) {
  350. //BUGBUG: Return low memory status
  351. //MemoryLowDlg( plv->ci.hwnd );
  352. return FALSE;
  353. }
  354. plv->rcView.left = RECOMPUTE; // recompute view rect
  355. if ( ListView_IsSmallView(plv) || ListView_IsIconView(plv) ) {
  356. // Call off to the arrange function.
  357. ListView_OnArrange(plv, LVA_DEFAULT);
  358. if (!fInvalidateAll)
  359. {
  360. // Try to be smart and invalidate only what we need to.
  361. // Add a little logic to erase any message like no items found when
  362. // the view was previously empty...
  363. if (cTotalItemsOld < iItems)
  364. iItem = cTotalItemsOld;
  365. else
  366. iItem = iItems - 1; // Get the index
  367. if ((iItem >= 0) && (cTotalItemsOld > 0))
  368. ListView_IInvalidateBelow(plv, iItem);
  369. else
  370. fInvalidateAll = TRUE;
  371. }
  372. } else {
  373. ListView_Recompute(plv);
  374. // if we have empty text and old count was zero... then we should redraw all
  375. if (plv->pszEmptyText && (cTotalItemsOld == 0) && (iItems > 0))
  376. fInvalidateAll = TRUE;
  377. // Try to do smart invalidates...
  378. if (!fInvalidateAll)
  379. {
  380. // Try to be smart and invalidate only what we need to.
  381. if (cTotalItemsOld < iItems)
  382. iItem = cTotalItemsOld;
  383. else
  384. iItem = iItems - 1; // Get the index
  385. if (iItem >= 0)
  386. ListView_LRInvalidateBelow(plv, iItem, FALSE);
  387. }
  388. // We may try to resize the column
  389. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  390. // For compatability we assume 0 for flags implies old type
  391. // of functionality and scrolls the important item into view.
  392. // If second bit is set, we leave the scroll position alone.
  393. if ((dwFlags & LVSICF_NOSCROLL) == 0) {
  394. // what is the important item
  395. iItem = (plv->iFocus >= 0) ?
  396. plv->iFocus :
  397. ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  398. iItem = max(0, iItem);
  399. // make important item visable
  400. ListView_OnEnsureVisible(plv, iItem, FALSE);
  401. }
  402. }
  403. if (fInvalidateAll)
  404. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  405. ListView_UpdateScrollBars(plv);
  406. ListView_NotifyRecreate(plv);
  407. ListView_NotifyFocusEvent(plv);
  408. } else {
  409. frt = FALSE;
  410. }
  411. } else {
  412. if (plv->hdpaSubItems)
  413. {
  414. int iCol;
  415. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  416. {
  417. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  418. if (hdpa) // this is optional, call backs don't have them
  419. DPA_Grow(hdpa, iItems);
  420. }
  421. }
  422. DPA_Grow(plv->hdpa, iItems);
  423. DPA_Grow(plv->hdpaZOrder, iItems);
  424. }
  425. return frt;
  426. }
  427. typedef struct _LVSortInfo
  428. {
  429. LV* plv;
  430. BOOL fSortIndices;
  431. PFNLVCOMPARE pfnCompare;
  432. LPARAM lParam;
  433. BOOL bPassLP;
  434. } LVSortInfo;
  435. int CALLBACK ListView_SortCallback(LPVOID dw1, LPVOID dw2, LPARAM lParam)
  436. {
  437. LISTITEM FAR *pitem1;
  438. LISTITEM FAR *pitem2;
  439. LVSortInfo FAR *pSortInfo = (LVSortInfo FAR *)lParam;
  440. ASSERT(!ListView_IsOwnerData(pSortInfo->plv));
  441. // determine whether dw1 and dw2 are indices or the real items
  442. // and assign pitem? accordingly
  443. if (pSortInfo->fSortIndices) {
  444. pitem1 = ListView_GetItemPtr(pSortInfo->plv, PtrToUlong(dw1));
  445. pitem2 = ListView_GetItemPtr(pSortInfo->plv, PtrToUlong(dw2));
  446. } else {
  447. pitem1 = (LISTITEM FAR *)dw1;
  448. pitem2 = (LISTITEM FAR *)dw2;
  449. }
  450. if (!pSortInfo->pfnCompare) {
  451. // Treat NULL pszText like null string.
  452. LPCTSTR pszText1 = pitem1->pszText ? pitem1->pszText : c_szNULL;
  453. LPCTSTR pszText2 = pitem2->pszText ? pitem2->pszText : c_szNULL;
  454. // bugbug, should allow callbacks in text
  455. if (pszText1 != LPSTR_TEXTCALLBACK &&
  456. pszText2 != LPSTR_TEXTCALLBACK )
  457. {
  458. return lstrcmpi(pitem1->pszText, pitem2->pszText);
  459. }
  460. RIPMSG(0, "LVM_SORTITEM(EX): Cannot combine NULL callback with LPSTR_TEXTCALLBACK");
  461. return -1;
  462. } else
  463. {
  464. if (pSortInfo->bPassLP)
  465. return(pSortInfo->pfnCompare(pitem1->lParam, pitem2->lParam, pSortInfo->lParam));
  466. else
  467. {
  468. if (pSortInfo->fSortIndices)
  469. return(pSortInfo->pfnCompare((LPARAM)dw1, (LPARAM)dw2, pSortInfo->lParam));
  470. else
  471. {
  472. // we want to sort by the indices, but all we've got are pointers to the items
  473. // and there is no way to get back from that pointer to an index
  474. ASSERT(0);
  475. return -1;
  476. }
  477. }
  478. }
  479. ASSERT(0);
  480. return -1;
  481. }
  482. VOID ListView_InvalidateTTLastHit(LV* plv, int iNewHit)
  483. {
  484. if (plv->iTTLastHit == iNewHit)
  485. {
  486. plv->iTTLastHit = -1;
  487. if (plv->pszTip && plv->pszTip != LPSTR_TEXTCALLBACK)
  488. {
  489. plv->pszTip[0] = 0;
  490. }
  491. }
  492. }
  493. BOOL NEAR PASCAL ListView_SortAllColumns(LV* plv, LVSortInfo FAR * psi)
  494. {
  495. ASSERT(!ListView_IsOwnerData(plv));
  496. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  497. // don't do this optimization if we will need the indices to sort by
  498. if (psi->bPassLP && ((!plv->hdpaSubItems) || !DPA_GetPtrCount(plv->hdpaSubItems))) {
  499. psi->fSortIndices = FALSE;
  500. return (DPA_Sort(plv->hdpa, ListView_SortCallback, (LPARAM)psi));
  501. } else {
  502. // if we need to sort several hdpa's, create one DPA of just indices
  503. // and sort that, then fix up all the dpa's
  504. BOOL fReturn = FALSE;
  505. HDPA hdpa;
  506. int i;
  507. int iMax;
  508. void FAR * FAR * ph;
  509. void FAR * FAR *pNewIndices;
  510. // initialize the hdpa with indices
  511. hdpa = DPA_Clone(plv->hdpa, NULL);
  512. if (hdpa) {
  513. ASSERT(DPA_GetPtrCount(plv->hdpa) == DPA_GetPtrCount(hdpa));
  514. ph = pNewIndices = DPA_GetPtrPtr(hdpa);
  515. iMax = DPA_GetPtrCount(hdpa);
  516. for( i = 0; i < iMax; ph++, i++) {
  517. *ph = IntToPtr(i);
  518. }
  519. psi->fSortIndices = TRUE;
  520. if (DPA_Sort(hdpa, ListView_SortCallback, (LPARAM)psi)) {
  521. #ifdef WIN32
  522. ph = LocalAlloc(LPTR, sizeof(LPVOID) * iMax);
  523. #else
  524. ph = Alloc(sizeof(LPVOID) * iMax);
  525. #endif
  526. if (ph) {
  527. int j;
  528. void FAR * FAR *pSubItems;
  529. // we could get here because bPassLP is false, even if we don't have subitems
  530. if (plv->hdpaSubItems && DPA_GetPtrCount(plv->hdpaSubItems))
  531. {
  532. for (i = DPA_GetPtrCount(plv->hdpaSubItems) - 1; i >= 0; i--) {
  533. HDPA hdpaSubItem = ListView_GetSubItemDPA(plv, i);
  534. if (hdpaSubItem) {
  535. // make sure it's of the right size
  536. while (DPA_GetPtrCount(hdpaSubItem) < iMax) {
  537. if (DPA_InsertPtr(hdpaSubItem, iMax, NULL) == -1)
  538. goto Bail;
  539. }
  540. // actually copy across the dpa with the new indices
  541. pSubItems = DPA_GetPtrPtr(hdpaSubItem);
  542. for (j = 0; j < iMax; j++) {
  543. ph[j] = pSubItems[PtrToUlong(pNewIndices[j])];
  544. }
  545. // finally, copy it all back to the pSubItems;
  546. hmemcpy(pSubItems, ph, sizeof(LPVOID) * iMax);
  547. }
  548. }
  549. }
  550. // now do the main hdpa
  551. pSubItems = DPA_GetPtrPtr(plv->hdpa);
  552. for (j = 0; j < iMax; j++) {
  553. ph[j] = pSubItems[PtrToUlong(pNewIndices[j])];
  554. }
  555. // finally, copy it all back to the pSubItems;
  556. hmemcpy(pSubItems, ph, sizeof(LPVOID) * iMax);
  557. fReturn = TRUE;
  558. Bail:
  559. #ifdef WIN32
  560. LocalFree(ph);
  561. #else
  562. Free(ph);
  563. #endif
  564. }
  565. }
  566. DPA_Destroy(hdpa);
  567. }
  568. return fReturn;
  569. }
  570. }
  571. DWORD ListView_OnApproximateViewRect(LV* plv, int iCount, int iWidth, int iHeight)
  572. {
  573. if (iCount == -1)
  574. iCount = ListView_Count(plv);
  575. if (iWidth == -1)
  576. iWidth = plv->sizeClient.cx;
  577. if (iHeight == -1)
  578. iHeight = plv->sizeClient.cy;
  579. return _ListView_ApproximateViewRect(plv, iCount, iWidth, iHeight);
  580. }
  581. DWORD ListView_OnSetLVRangeObject(LV* plv, int iWhich, ILVRange *plvrange)
  582. {
  583. ILVRange **pplvrange;
  584. switch (iWhich)
  585. {
  586. case LVSR_SELECTION:
  587. pplvrange = &plv->plvrangeSel;
  588. break;
  589. case LVSR_CUT:
  590. pplvrange = &plv->plvrangeCut;
  591. break;
  592. default:
  593. return FALSE;
  594. }
  595. if (*pplvrange)
  596. {
  597. // Release the old one
  598. (*pplvrange)->lpVtbl->Release(*pplvrange);
  599. }
  600. *pplvrange = plvrange;
  601. // Hold onto the pointer...
  602. if (plvrange)
  603. plvrange->lpVtbl->AddRef(plvrange);
  604. return TRUE;
  605. }
  606. BOOL NEAR PASCAL ListView_OnSortItems(LV *plv, LPARAM lParam, PFNLVCOMPARE pfnCompare, BOOL bPassLP)
  607. {
  608. LVSortInfo SortInfo;
  609. LISTITEM FAR *pitemFocused;
  610. SortInfo.pfnCompare = pfnCompare;
  611. SortInfo.lParam = lParam;
  612. SortInfo.plv = plv;
  613. SortInfo.bPassLP = bPassLP;
  614. if (ListView_IsOwnerData(plv)) {
  615. RIPMSG(0, "LVM_SORTITEMS: Invalid for owner-data listview");
  616. return FALSE;
  617. }
  618. ListView_DismissEdit(plv, TRUE); // cancel edits
  619. // we're going to mess with the indices, so stash away the pointer to the
  620. // focused item.
  621. if (plv->iFocus != -1) {
  622. pitemFocused = ListView_GetItemPtr(plv, plv->iFocus);
  623. } else
  624. pitemFocused = NULL;
  625. if (ListView_SortAllColumns(plv, &SortInfo)) {
  626. // restore the focused item.
  627. if (pitemFocused) {
  628. int i;
  629. for (i = ListView_Count(plv) - 1; i >= 0 ; i--) {
  630. if (ListView_GetItemPtr(plv, i) == pitemFocused) {
  631. plv->iFocus = i;
  632. plv->iMark = i;
  633. }
  634. }
  635. }
  636. if (ListView_IsSmallView(plv) || ListView_IsIconView(plv))
  637. {
  638. ListView_CommonArrange(plv, LVA_DEFAULT, plv->hdpa);
  639. }
  640. else if (ListView_IsReportView(plv) || ListView_IsListView(plv))
  641. {
  642. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  643. }
  644. // The items in the view have moved around; let apps know
  645. MyNotifyWinEvent(EVENT_OBJECT_REORDER, plv->ci.hwnd, OBJID_CLIENT, 0);
  646. return(TRUE);
  647. }
  648. return FALSE;
  649. }
  650. void PASCAL ListView_EnableWindow(LV* plv, BOOL wParam)
  651. {
  652. if (wParam) {
  653. if (plv->ci.style & WS_DISABLED) {
  654. plv->ci.style &= ~WS_DISABLED; // enabled
  655. ListView_OnSetBkColor(plv, plv->clrBkSave);
  656. }
  657. } else {
  658. if (!(plv->ci.style & WS_DISABLED)) {
  659. plv->clrBkSave = plv->clrBk;
  660. plv->ci.style |= WS_DISABLED; // disabled
  661. ListView_OnSetBkColor(plv, g_clrBtnFace);
  662. }
  663. }
  664. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  665. }
  666. BOOL NEAR ListView_IsItemVisible(LV* plv, int i)
  667. // Assumes parmss ok etc for speed. Called inside region calc code.
  668. {
  669. RECT rcBounds;
  670. RECT rc, rcScratch;
  671. // get bounding rect of item
  672. ListView_GetRects(plv, i, NULL, NULL, &rcBounds, NULL);
  673. // setup rect for listview client. Should perf this up for
  674. // multimonitor case where there are dead zones in work area...
  675. rc.left = 0;
  676. rc.top = 0;
  677. rc.right = plv->sizeClient.cx;
  678. rc.bottom= plv->sizeClient.cy;
  679. return IntersectRect(&rcScratch, &rc, &rcBounds);
  680. }
  681. // Helper for ListView_RecalcRegion
  682. #define BitOn(lpbits, x, y, cx) (*((BYTE *)(lpbits + ((y * cx) + (x / 8)))) & (0x80 >> (x % 8)))
  683. void ListView_RecalcRegion(LV* plv, BOOL fForce, BOOL fRedraw)
  684. {
  685. HRGN hrgnUnion = NULL;
  686. HRGN hrgn = NULL;
  687. int i;
  688. HDC hdc = NULL;
  689. BYTE * lpBits = NULL;
  690. HBITMAP hbmp = NULL, hbmpOld = NULL;
  691. RECT rc, rcIcon;
  692. LISTITEM FAR * pitem;
  693. BITMAP bm;
  694. // Bail out if we don't need to do any work
  695. if (!(plv->exStyle & LVS_EX_REGIONAL) || !ListView_RedrawEnabled(plv) ||
  696. (plv->flags & LVF_INRECALCREGION))
  697. return;
  698. // To prevent recursion
  699. plv->flags |= LVF_INRECALCREGION;
  700. if ((ListView_Count(plv) > 0)) {
  701. int cxIcon, cyIcon;
  702. int dxOffset, dyOffset;
  703. // Run through first to see if anything changed - bail if not!
  704. if (!fForce) {
  705. for (i = 0; i < ListView_Count(plv); i++) {
  706. pitem = ListView_FastGetItemPtr(plv, i);
  707. if (!ListView_IsItemVisible(plv, i))
  708. {
  709. if (pitem->hrgnIcon == (HANDLE)-1 || !pitem->hrgnIcon)
  710. // Item was invisible and still is. Nothing changed.
  711. continue;
  712. if (pitem->hrgnIcon)
  713. {
  714. // Item was visible and now is invisible... Something
  715. // changed.
  716. pitem->ptRgn.x = RECOMPUTE;
  717. pitem->ptRgn.y = RECOMPUTE;
  718. DeleteObject(pitem->hrgnIcon);
  719. pitem->hrgnIcon = NULL;
  720. }
  721. }
  722. ListView_GetRects(plv, i, NULL, &rc, NULL, NULL);
  723. // If the location of the icon or the text rectangle have
  724. // changed, then we need to continue so that we can recalculate
  725. // the region.
  726. if ((pitem->pt.x != pitem->ptRgn.x) ||
  727. (pitem->pt.y != pitem->ptRgn.y) ||
  728. (!pitem->hrgnIcon) ||
  729. !EqualRect((CONST RECT *)&pitem->rcTextRgn, (CONST RECT *)&rc))
  730. goto changed;
  731. }
  732. // If we go through all the items and nothing changed, then
  733. // we can return without doing any work!
  734. ASSERT(i == ListView_Count(plv));
  735. goto exit;
  736. changed:;
  737. }
  738. // Figure out the dimensions of the Icon rectangle - assumes
  739. // each Icon rectangle is the same size.
  740. ListView_GetRects(plv, 0, &rcIcon, NULL, NULL, NULL);
  741. // Center the icon in the rectangle
  742. ImageList_GetIconSize(plv->himl, &cxIcon, &cyIcon);
  743. dxOffset = (rcIcon.right - rcIcon.left - cxIcon) / 2;
  744. dyOffset = (rcIcon.bottom - rcIcon.top - cyIcon) / 2;
  745. cxIcon = rcIcon.right - rcIcon.left;
  746. cyIcon = rcIcon.bottom - rcIcon.top;
  747. if (!(hdc = CreateCompatibleDC(NULL)) ||
  748. (!(hbmp = CreateBitmap(cxIcon, cyIcon, 1, 1, NULL)))) {
  749. goto BailOut;
  750. }
  751. GetObject(hbmp, SIZEOF(bm), &bm);
  752. if (!(lpBits = (BYTE *)GlobalAlloc(GPTR, bm.bmWidthBytes * bm.bmHeight)))
  753. goto BailOut;
  754. hbmpOld = SelectObject(hdc, hbmp);
  755. PatBlt(hdc, 0, 0, cxIcon, cyIcon, WHITENESS);
  756. if (hrgnUnion = CreateRectRgn(0, 0, 0, 0)) {
  757. for (i = 0; i < ListView_Count(plv); i++)
  758. {
  759. int x, y, iResult;
  760. BOOL fStarted = FALSE;
  761. LPRECT lprc;
  762. pitem = ListView_FastGetItemPtr(plv, i);
  763. if (pitem->pt.y == RECOMPUTE)
  764. continue;
  765. if (!ListView_IsItemVisible(plv, i))
  766. {
  767. // ignore invisible items
  768. if (pitem->hrgnIcon && pitem->hrgnIcon!=(HANDLE)-1)
  769. {
  770. pitem->ptRgn.x = RECOMPUTE;
  771. pitem->ptRgn.y = RECOMPUTE;
  772. DeleteObject(pitem->hrgnIcon);
  773. pitem->hrgnIcon = (HANDLE)-1;
  774. }
  775. continue;
  776. }
  777. // Add the region for the icon text first
  778. ListView_GetRects(plv, i, &rcIcon, &rc, NULL, NULL);
  779. // If we're in edit mode always use rcTextRgn
  780. if (i == plv->iEdit)
  781. lprc = &pitem->rcTextRgn;
  782. else
  783. lprc = &rc;
  784. if (!(hrgn = CreateRectRgnIndirect(lprc)))
  785. goto Error;
  786. iResult = CombineRgn(hrgnUnion, hrgn, hrgnUnion, RGN_OR);
  787. DeleteObject(hrgn);
  788. if (iResult == ERROR) {
  789. // Error case - out of memory. Just select in a NULL region.
  790. Error:
  791. DeleteObject(hrgnUnion);
  792. hrgnUnion = NULL;
  793. break;
  794. }
  795. // Succeeded, copy the rectangle to rcTextRgn so we
  796. // can test against it in the future. Don't copy over
  797. // it if we are in edit mode, the rectangle is used to
  798. // store the edit window in that case.
  799. if (plv->iEdit != i)
  800. CopyRect(&pitem->rcTextRgn, (CONST RECT *)&rc);
  801. // Now create a region for the icon mask - or use the cached one
  802. if (!pitem->hrgnIcon || pitem->hrgnIcon == (HANDLE)-1) {
  803. // (pitem->pt.x != pitem->ptRgn.x) ||
  804. // (pitem->pt.y != pitem->ptRgn.y)) {
  805. HRGN hrgnIcon = NULL;
  806. // On slow machines, we'll just wrap the icon with a rectangle. But on
  807. // faster machines, we'll build a region that corresponds to the
  808. // mask for the icon so it looks sweet.
  809. if (g_fSlowMachine) {
  810. // Modify the rectangle slightly so it looks better
  811. // Glue the icon and text rectangles together
  812. rcIcon.bottom = rc.top;
  813. // Shrink the width of the rectangle so it's only as big as the icon itself
  814. InflateRect(&rcIcon, -dxOffset, 0);
  815. hrgnIcon = CreateRectRgnIndirect(&rcIcon);
  816. } else {
  817. // If the image isn't around, get it now.
  818. if (pitem->iImage == I_IMAGECALLBACK) {
  819. LV_ITEM item;
  820. item.iItem = i;
  821. item.iSubItem = 0;
  822. item.mask = LVIF_IMAGE;
  823. item.stateMask = LVIS_ALL;
  824. item.pszText = NULL;
  825. item.cchTextMax = 0;
  826. // BOGUS - do we need to worry about our state
  827. // getting messed up during the callback?
  828. ListView_OnGetItem(plv, &item);
  829. }
  830. ImageList_Draw(plv->himl, pitem->iImage, hdc, 0, 0, ILD_MASK | (pitem->state & LVIS_OVERLAYMASK));
  831. GetBitmapBits(hbmp, bm.bmWidthBytes * bm.bmHeight, (LPVOID)lpBits);
  832. for (y = 0; y < cyIcon; y++) {
  833. for (x = 0; x < cxIcon; x++) {
  834. if (!fStarted && !BitOn(lpBits, x, y, bm.bmWidthBytes)) {
  835. rc.left = x;
  836. rc.top = y;
  837. rc.bottom = y + 1;
  838. fStarted = TRUE;
  839. if (x == (cxIcon - 1)) {
  840. x++;
  841. goto AddIt;
  842. } else {
  843. continue;
  844. }
  845. }
  846. if (fStarted && BitOn(lpBits, x, y, bm.bmWidthBytes)) {
  847. AddIt:
  848. rc.right = x;
  849. //
  850. // Mirror the region so that the icons get displayed ok. [samera]
  851. //
  852. if (plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  853. {
  854. int iLeft = rc.left;
  855. rc.left = (cxIcon - (rc.right+1));
  856. rc.right = (cxIcon - (iLeft+1));
  857. OffsetRect(&rc, rcIcon.left - dxOffset, rcIcon.top + dyOffset);
  858. }
  859. else
  860. OffsetRect(&rc, rcIcon.left + dxOffset, rcIcon.top + dyOffset);
  861. if (hrgn = CreateRectRgnIndirect(&rc)) {
  862. if (hrgnIcon || (hrgnIcon = CreateRectRgn(0, 0, 0, 0)))
  863. iResult = CombineRgn(hrgnIcon, hrgn, hrgnIcon, RGN_OR);
  864. else
  865. iResult = ERROR;
  866. DeleteObject(hrgn);
  867. }
  868. if (!hrgn || (iResult == ERROR)) {
  869. if (hrgnIcon)
  870. DeleteObject(hrgnIcon);
  871. goto Error;
  872. }
  873. fStarted = FALSE;
  874. }
  875. }
  876. }
  877. }
  878. if (hrgnIcon) {
  879. // Cache it since it takes a long time to build it
  880. if (pitem->hrgnIcon && pitem->hrgnIcon != (HANDLE)-1)
  881. DeleteObject(pitem->hrgnIcon);
  882. pitem->hrgnIcon = hrgnIcon;
  883. pitem->ptRgn = pitem->pt;
  884. // Add it to the accumulated window region
  885. if (ERROR == CombineRgn(hrgnUnion, hrgnIcon, hrgnUnion, RGN_OR))
  886. goto Error;
  887. }
  888. } else {
  889. OffsetRgn(pitem->hrgnIcon, pitem->pt.x - pitem->ptRgn.x, pitem->pt.y - pitem->ptRgn.y);
  890. pitem->ptRgn = pitem->pt;
  891. if (ERROR == CombineRgn(hrgnUnion, pitem->hrgnIcon, hrgnUnion, RGN_OR))
  892. goto Error;
  893. }
  894. }
  895. }
  896. }
  897. BailOut:
  898. if (lpBits)
  899. GlobalFree((HGLOBAL)lpBits);
  900. if (hbmp) {
  901. SelectObject(hdc, hbmpOld);
  902. DeleteObject(hbmp);
  903. }
  904. if (hdc)
  905. DeleteDC(hdc);
  906. // Windows takes ownership of the region when we select it in to the window
  907. SetWindowRgn(plv->ci.hwnd, hrgnUnion, fRedraw);
  908. exit:
  909. plv->flags &= ~LVF_INRECALCREGION;
  910. }
  911. HIMAGELIST CreateCheckBoxImagelist(HIMAGELIST himl, BOOL fTree, BOOL fUseColorKey, BOOL fMirror)
  912. {
  913. int cxImage, cyImage;
  914. HBITMAP hbm;
  915. HBITMAP hbmTemp;
  916. COLORREF clrMask;
  917. HDC hdcDesk = GetDC(NULL);
  918. HDC hdc;
  919. RECT rc;
  920. int nImages = fTree ? 3 : 2;
  921. if (!hdcDesk)
  922. return NULL;
  923. hdc = CreateCompatibleDC(hdcDesk);
  924. ReleaseDC(NULL, hdcDesk);
  925. if (!hdc)
  926. return NULL;
  927. // Must protect against ImageList_GetIconSize failing in case app
  928. // gave us a bad himl
  929. if (himl && ImageList_GetIconSize(himl, &cxImage, &cyImage)) {
  930. // cxImage and cyImage are okay
  931. } else {
  932. cxImage = g_cxSmIcon;
  933. cyImage = g_cySmIcon;
  934. }
  935. himl = ImageList_Create(cxImage, cyImage, ILC_MASK, 0, nImages);
  936. hbm = CreateColorBitmap(cxImage * nImages, cyImage);
  937. if (fUseColorKey)
  938. {
  939. clrMask = RGB(255,000,255); // magenta
  940. if (clrMask == g_clrWindow)
  941. clrMask = RGB(000,000,255); // blue
  942. }
  943. else
  944. {
  945. clrMask = g_clrWindow;
  946. }
  947. // fill
  948. hbmTemp = SelectObject(hdc, hbm);
  949. rc.left = rc.top = 0;
  950. rc.bottom = cyImage;
  951. rc.right = cxImage * nImages;
  952. FillRectClr(hdc, &rc, clrMask);
  953. rc.right = cxImage;
  954. // now draw the real controls on
  955. InflateRect(&rc, -g_cxEdge, -g_cyEdge);
  956. rc.right++;
  957. rc.bottom++;
  958. if (fTree)
  959. OffsetRect(&rc, cxImage, 0);
  960. DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT |
  961. (fUseColorKey? 0 : DFCS_TRANSPARENT));
  962. OffsetRect(&rc, cxImage, 0);
  963. // [msadek]; For the mirrored case, there is an off-by-one somewhere in MirrorIcon() or System API.
  964. // Since I will not be touching MirrorIcon() by any mean and no chance to fix a system API,
  965. // let's compensate for it here.
  966. if(fMirror)
  967. {
  968. OffsetRect(&rc, -1, 0);
  969. }
  970. DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | DFCS_CHECKED |
  971. (fUseColorKey? 0 : DFCS_TRANSPARENT));
  972. SelectObject(hdc, hbmTemp);
  973. if (fUseColorKey)
  974. {
  975. ImageList_AddMasked(himl, hbm, clrMask);
  976. }
  977. else
  978. {
  979. ImageList_Add(himl, hbm, NULL);
  980. }
  981. if(fMirror)
  982. {
  983. HICON hIcon = ImageList_ExtractIcon(0, himl, nImages-1);
  984. MirrorIcon(&hIcon, NULL);
  985. ImageList_ReplaceIcon(himl, nImages-1, hIcon);
  986. }
  987. DeleteDC(hdc);
  988. DeleteObject( hbm );
  989. return himl;
  990. }
  991. void ListView_InitCheckBoxes(LV* plv, BOOL fInitializeState)
  992. {
  993. HIMAGELIST himlCopy = (plv->himlSmall ? plv->himlSmall : plv->himl);
  994. HIMAGELIST himl;
  995. BOOL fNoColorKey = FALSE; // Backwards: If Cleartype is turned on, then we don't use colorkey.
  996. BOOL bMirror = FALSE;
  997. if (g_bRunOnNT5)
  998. {
  999. #ifdef CLEARTYPE // Don't use SPI_CLEARTYPE because it's defined because of APIThk, but not in NT.
  1000. SystemParametersInfo(SPI_GETCLEARTYPE, 0, &fNoColorKey, 0);
  1001. #endif
  1002. }
  1003. // [msadek], CheckBoxed need not to be mirrored.
  1004. // mirroer it during imagelist creation time so that it displays correctly
  1005. himl = CreateCheckBoxImagelist(himlCopy, FALSE, !fNoColorKey, IS_WINDOW_RTL_MIRRORED(plv->ci.hwnd));
  1006. ImageList_SetBkColor(himl, fNoColorKey ? (CLR_NONE) : (plv->clrBk));
  1007. ListView_OnSetImageList(plv, himl, LVSIL_STATE);
  1008. if (fInitializeState)
  1009. ListView_OnSetItemState(plv, -1, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK);
  1010. }
  1011. void ListView_PopBubble(LV *plv)
  1012. {
  1013. if (plv->hwndToolTips)
  1014. SendMessage( plv->hwndToolTips, TTM_POP, 0, 0 );
  1015. }
  1016. DWORD NEAR PASCAL ListView_ExtendedStyleChange(LV* plv, DWORD dwNewStyle, DWORD dwExMask)
  1017. {
  1018. DWORD dwOldStyle = plv->exStyle;
  1019. // this messge didn't come till 3, so version is at least 3
  1020. if (plv->ci.iVersion < 3) {
  1021. plv->ci.iVersion = 3;
  1022. // this will change the listview report size and painting algorithm
  1023. // because of the leading edge, so need to re-update scroll bars
  1024. // and repaint everything
  1025. if (ListView_IsReportView(plv))
  1026. {
  1027. ListView_RUpdateScrollBars(plv);
  1028. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1029. }
  1030. }
  1031. // Change of styles may also changes tooltip policy, so pop it
  1032. ListView_PopBubble(plv);
  1033. if (dwExMask)
  1034. dwNewStyle = (plv->exStyle & ~ dwExMask) | (dwNewStyle & dwExMask);
  1035. // Currently, the LVS_EX_REGIONAL style is only supported for large icon view
  1036. if (!ListView_IsIconView(plv)) {
  1037. dwNewStyle &= ~(LVS_EX_REGIONAL | LVS_EX_MULTIWORKAREAS);
  1038. }
  1039. // LVS_EX_REGIONAL is not supported for ownerdata
  1040. if (ListView_IsOwnerData(plv)) {
  1041. dwNewStyle &= ~LVS_EX_REGIONAL;
  1042. }
  1043. plv->exStyle = dwNewStyle;
  1044. // do any invalidation or whatever is needed here.
  1045. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_GRIDLINES) {
  1046. if (ListView_IsReportView(plv)) {
  1047. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1048. }
  1049. }
  1050. if ((dwOldStyle ^ dwNewStyle) & (LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD |
  1051. LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE |
  1052. LVS_EX_SUBITEMIMAGES)) {
  1053. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1054. }
  1055. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_CHECKBOXES) {
  1056. if (dwNewStyle & LVS_EX_CHECKBOXES) {
  1057. ListView_InitCheckBoxes(plv, TRUE);
  1058. } else {
  1059. // destroy the check boxes!
  1060. HIMAGELIST himl = ListView_OnSetImageList(plv, NULL, LVSIL_STATE);
  1061. if (himl)
  1062. ImageList_Destroy(himl);
  1063. }
  1064. }
  1065. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_FLATSB) {
  1066. if (dwNewStyle & LVS_EX_FLATSB) {
  1067. InitializeFlatSB(plv->ci.hwnd);
  1068. } else {
  1069. UninitializeFlatSB(plv->ci.hwnd);
  1070. }
  1071. }
  1072. if ((dwOldStyle ^ dwNewStyle) & LVS_EX_REGIONAL) {
  1073. if (g_fSlowMachine == -1) {
  1074. #ifdef NEVER
  1075. // Because some Alpha machines and faster pentiums were detected
  1076. // as slow machines (bug #30972 in IE4 database), it was decided
  1077. // to turn off this code.
  1078. SYSTEM_INFO si;
  1079. GetSystemInfo(&si);
  1080. g_fSlowMachine = (BOOL)((si.dwProcessorType == PROCESSOR_INTEL_386) ||
  1081. (si.dwProcessorType == PROCESSOR_INTEL_486));
  1082. #else
  1083. g_fSlowMachine = FALSE;
  1084. #endif
  1085. }
  1086. if (dwNewStyle & LVS_EX_REGIONAL) {
  1087. ListView_RecalcRegion(plv, TRUE, TRUE);
  1088. } else {
  1089. int i;
  1090. LISTITEM FAR * pitem;
  1091. // Delete all the cached regions, then NULL out our selected region.
  1092. for (i = 0; i < ListView_Count(plv); i++) {
  1093. pitem = ListView_FastGetItemPtr(plv, i);
  1094. if (pitem->hrgnIcon && pitem->hrgnIcon!=(HANDLE)-1) {
  1095. DeleteObject(pitem->hrgnIcon);
  1096. }
  1097. pitem->hrgnIcon = NULL;
  1098. }
  1099. SetWindowRgn(plv->ci.hwnd, (HRGN)NULL, TRUE);
  1100. }
  1101. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1102. }
  1103. return dwOldStyle;
  1104. }
  1105. // BUGBUG raymondc v6.0: Doesn't detect WM_WINDOWPOSCHANGING as a way
  1106. // of being shown. NT5 defview has to hack around it pretty grossly.
  1107. // Fix for v6.0.
  1108. void NEAR PASCAL LV_OnShowWindow(LV* plv, BOOL fShow)
  1109. {
  1110. if (fShow) {
  1111. if (!(plv->flags & LVF_VISIBLE)) {
  1112. plv->flags |= LVF_VISIBLE;
  1113. ListView_UpdateScrollBars(plv);
  1114. }
  1115. } else
  1116. plv->flags &= ~LVF_VISIBLE;
  1117. }
  1118. LRESULT NEAR PASCAL ListView_OnHelp(LV* plv, LPHELPINFO lpHelpInfo)
  1119. {
  1120. // If we're seeing WM_HELP because of our child header control, then
  1121. // munge the HELPINFO structure to use the ListView's control id.
  1122. // win\core\user\combo.c has similiar code to handle the child edit
  1123. // control of a combo box.
  1124. if ((lpHelpInfo != NULL) && ((plv->ci.style & LVS_TYPEMASK) == LVS_REPORT) &&
  1125. (lpHelpInfo->iCtrlId == LVID_HEADER)) {
  1126. lpHelpInfo->hItemHandle = plv->ci.hwnd;
  1127. lpHelpInfo->iCtrlId = GetWindowID(plv->ci.hwnd);
  1128. // Shouldn't have to do this: USER would have filled in the appropriate
  1129. // context id by walking up the parent hwnd chain.
  1130. //lpHelpInfo->dwContextId = GetContextHelpId(hwnd);
  1131. }
  1132. return DefWindowProc(plv->ci.hwnd, WM_HELP, 0, (LPARAM)lpHelpInfo);
  1133. }
  1134. DWORD NEAR PASCAL ListView_OnSetIconSpacing(LV* plv, LPARAM lParam)
  1135. {
  1136. DWORD dwOld = MAKELONG(plv->cxIconSpacing, plv->cyIconSpacing);
  1137. if (lParam == (LPARAM)-1) {
  1138. // go back to using defaults
  1139. plv->flags &= ~LVF_ICONSPACESET;
  1140. plv->cxIconSpacing = (plv->cxIcon + (g_cxIconSpacing - g_cxIcon));
  1141. plv->cyIconSpacing = (plv->cyIcon + (g_cyIconSpacing - g_cyIcon));
  1142. } else {
  1143. if (LOWORD(lParam))
  1144. plv->cxIconSpacing = LOWORD(lParam);
  1145. if (HIWORD(lParam))
  1146. plv->cyIconSpacing = HIWORD(lParam);
  1147. plv->flags |= LVF_ICONSPACESET;
  1148. }
  1149. plv->iFreeSlot = -1;
  1150. return dwOld;
  1151. }
  1152. BOOL ListView_OnSetCursorMsg(LV* plv)
  1153. {
  1154. if (plv->exStyle & (LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE)) {
  1155. if (plv->iHot != -1) {
  1156. if (((plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK)) ||
  1157. ListView_OnGetItemState(plv, plv->iHot, LVIS_SELECTED)) {
  1158. if (!plv->hCurHot)
  1159. plv->hCurHot = LoadHandCursor(0);
  1160. SetCursor(plv->hCurHot);
  1161. return TRUE;
  1162. }
  1163. }
  1164. }
  1165. return FALSE;
  1166. }
  1167. void ListView_OnSetHotItem(LV* plv, int iItem)
  1168. {
  1169. UINT maskChanged;
  1170. if (iItem != plv->iHot) {
  1171. BOOL fSelectOnly;
  1172. UINT fRedraw = RDW_INVALIDATE;
  1173. #ifndef DONT_UNDERLINE
  1174. if (plv->clrTextBk == CLR_NONE)
  1175. fRedraw |= RDW_ERASE;
  1176. #endif
  1177. fSelectOnly = ListView_FullRowSelect(plv);
  1178. maskChanged = (plv->exStyle & LVS_EX_BORDERSELECT) ? LVIF_TEXT | LVIF_IMAGE : LVIF_TEXT;
  1179. ListView_InvalidateItemEx(plv, plv->iHot, fSelectOnly, fRedraw, maskChanged);
  1180. ListView_InvalidateItemEx(plv, iItem, fSelectOnly, RDW_INVALIDATE, maskChanged);
  1181. plv->iHot = iItem;
  1182. }
  1183. }
  1184. /// Usability test prototype
  1185. // CHEEBUGBUG
  1186. BOOL fShouldFirstClickActivate()
  1187. {
  1188. static BOOL fInited = FALSE;
  1189. static BOOL fActivate = TRUE;
  1190. if (!fInited) {
  1191. long cb = 0;
  1192. if (RegQueryValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\NoFirstClickActivate"),
  1193. NULL, &cb) == ERROR_SUCCESS)
  1194. fActivate = FALSE;
  1195. fInited = TRUE;
  1196. }
  1197. return fActivate;
  1198. }
  1199. BOOL ChildOfDesktop(HWND hwnd)
  1200. {
  1201. return IsChild(GetShellWindow(), hwnd);
  1202. }
  1203. void ListView_OnMouseMove(LV* plv, int x, int y, UINT uFlags)
  1204. {
  1205. if (plv->exStyle & (LVS_EX_TRACKSELECT|LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE)
  1206. // CHEEBUGBUG: for usability testing
  1207. && (ChildOfActiveWindow(plv->ci.hwnd) || fShouldFirstClickActivate() ||
  1208. ChildOfDesktop(plv->ci.hwnd))
  1209. ) {
  1210. int iItem;
  1211. LV_HITTESTINFO ht;
  1212. NMLISTVIEW nm;
  1213. ht.pt.x = x;
  1214. ht.pt.y = y;
  1215. iItem = ListView_OnSubItemHitTest(plv, &ht);
  1216. if (ht.iSubItem != 0) {
  1217. // if we're not in full row select,
  1218. // hitting on a subitem is like hitting on nowhere
  1219. // also, in win95, ownerdraw fixed effectively had full row select
  1220. if (!ListView_FullRowSelect(plv) &&
  1221. !(plv->ci.style & LVS_OWNERDRAWFIXED)) {
  1222. iItem = -1;
  1223. ht.flags = LVHT_NOWHERE;
  1224. }
  1225. }
  1226. if (ht.flags & LVHT_NOWHERE ||
  1227. ht.flags & LVHT_ONITEMSTATEICON) {
  1228. iItem = -1; // this is possible in the list mode
  1229. }
  1230. nm.iItem = iItem;
  1231. nm.iSubItem = ht.iSubItem;
  1232. nm.uChanged = 0;
  1233. nm.ptAction.x = x;
  1234. nm.ptAction.y = y;
  1235. if (!CCSendNotify(&plv->ci, LVN_HOTTRACK, &nm.hdr)) {
  1236. #ifdef DEBUG
  1237. if ((nm.iItem != -1) && nm.iSubItem != 0)
  1238. nm.iItem = -1;
  1239. #endif
  1240. ListView_OnSetHotItem(plv, nm.iItem);
  1241. // Ensure our cursor is correct now since the WM_SETCURSOR
  1242. // message was already generated for this mouse event.
  1243. ListView_OnSetCursorMsg(plv);
  1244. // this lets us know when we've left an item
  1245. // and can then reselect/toggle it on hover events
  1246. if (iItem != plv->iNoHover) {
  1247. plv->iNoHover = -1;
  1248. }
  1249. }
  1250. }
  1251. }
  1252. BOOL EditBoxHasFocus()
  1253. {
  1254. HWND hwndFocus = GetFocus();
  1255. if (hwndFocus) {
  1256. if (SendMessage(hwndFocus, WM_GETDLGCODE, 0, 0) & DLGC_HASSETSEL)
  1257. return TRUE;
  1258. }
  1259. return FALSE;
  1260. }
  1261. void ListView_OnMouseHover(LV* plv, int x, int y, UINT uFlags)
  1262. {
  1263. int iItem;
  1264. BOOL bSelected;
  1265. LV_HITTESTINFO ht;
  1266. BOOL fControl;
  1267. BOOL fShift;
  1268. BOOL fNotifyReturn = FALSE;
  1269. if (GetCapture() || !ChildOfActiveWindow(plv->ci.hwnd) ||
  1270. EditBoxHasFocus())
  1271. return; // ignore hover while editing or any captured (d/d) operation
  1272. if (CCSendNotify(&plv->ci, NM_HOVER, NULL)) {
  1273. return;
  1274. }
  1275. // REVIEW: right button implies no shift or control stuff
  1276. // Single selection style also implies no modifiers
  1277. //if (RIGHTBUTTON(keyFlags) || (plv->ci.style & LVS_SINGLESEL))
  1278. if ((plv->ci.style & LVS_SINGLESEL)) {
  1279. fControl = FALSE;
  1280. fShift = FALSE;
  1281. } else {
  1282. fControl = GetAsyncKeyState(VK_CONTROL) < 0;
  1283. fShift = GetAsyncKeyState(VK_SHIFT) < 0;
  1284. }
  1285. ht.pt.x = x;
  1286. ht.pt.y = y;
  1287. iItem = ListView_OnHitTest(plv, &ht);
  1288. if (iItem == -1 ||
  1289. iItem == plv->iNoHover)
  1290. return;
  1291. //before we hover select we launch any pending item
  1292. //this prevents clicking on one item and hover selecting other before
  1293. //the timer goes off which result in wrong item being launched
  1294. if (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickHappened && plv->fOneClickOK)
  1295. {
  1296. HWND hwnd = plv->ci.hwnd;
  1297. KillTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED);
  1298. plv->fOneClickHappened = FALSE;
  1299. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  1300. if (!IsWindow(hwnd))
  1301. return;
  1302. }
  1303. plv->iNoHover = iItem;
  1304. bSelected = ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  1305. if (ht.flags & (LVHT_ONITEMLABEL | LVHT_ONITEMICON))
  1306. {
  1307. UINT keyFlags = 0;
  1308. if (fShift)
  1309. keyFlags |= MK_SHIFT;
  1310. if (fControl)
  1311. keyFlags |= MK_CONTROL;
  1312. if (!bSelected) {
  1313. // if it wasn't selected, we're about to select it... play
  1314. // a little ditty for us...
  1315. CCPlaySound(c_szSelect);
  1316. }
  1317. ListView_ButtonSelect(plv, iItem, keyFlags, bSelected);
  1318. if (fControl)
  1319. {
  1320. ListView_SetFocusSel(plv, iItem, !fShift, FALSE, !fShift);
  1321. }
  1322. if (!fShift)
  1323. plv->iMark = iItem;
  1324. ListView_OnSetCursorMsg(plv);
  1325. SetFocus(plv->ci.hwnd); // activate this window
  1326. }
  1327. }
  1328. BOOL EqualRects(LPRECT prcNew, LPRECT prcOld, int nRects)
  1329. {
  1330. int i;
  1331. for (i = 0; i < nRects; i++)
  1332. if (!EqualRect(&prcNew[i], &prcOld[i]))
  1333. return FALSE;
  1334. return TRUE;
  1335. }
  1336. BOOL ListView_FindWorkArea(LV * plv, POINT pt, short * piWorkArea)
  1337. {
  1338. int iWork;
  1339. for (iWork = 0; iWork < plv->nWorkAreas; iWork++)
  1340. {
  1341. if (PtInRect(&plv->prcWorkAreas[iWork], pt))
  1342. {
  1343. *piWorkArea = (short)iWork;
  1344. return TRUE;
  1345. }
  1346. }
  1347. // (dli) default case is the primary work area
  1348. *piWorkArea = 0;
  1349. return FALSE;
  1350. }
  1351. void ListView_BullyIconsOnWorkarea(LV * plv, HDPA hdpaLostItems)
  1352. {
  1353. int ihdpa;
  1354. int iFree = -1; // the last free slot number
  1355. LVFAKEDRAW lvfd;
  1356. LV_ITEM item;
  1357. // Caller should've filtered this case out
  1358. ASSERT(DPA_GetPtrCount(hdpaLostItems) > 0);
  1359. // Set up in case caller is customdraw
  1360. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  1361. item.mask = LVIF_PARAM;
  1362. item.iSubItem = 0;
  1363. // Go through my hdpa list of lost icons and try to place them within bound
  1364. for (ihdpa = 0; ihdpa < DPA_GetPtrCount(hdpaLostItems); ihdpa++)
  1365. {
  1366. POINT ptNew, pt;
  1367. RECT rcBound;
  1368. int cxBound, cyBound;
  1369. int iWidth, iHeight;
  1370. int iItem;
  1371. LISTITEM FAR * pitem;
  1372. iItem = PtrToUlong(DPA_GetPtr(hdpaLostItems, ihdpa));
  1373. pitem = ListView_FastGetItemPtr(plv, iItem);
  1374. pt = pitem->pt;
  1375. iWidth = RECTWIDTH(plv->prcWorkAreas[pitem->iWorkArea]);
  1376. iHeight = RECTHEIGHT(plv->prcWorkAreas[pitem->iWorkArea]);
  1377. ListView_GetRects(plv, iItem, NULL, NULL, &rcBound, NULL);
  1378. cxBound = RECTWIDTH(rcBound);
  1379. cyBound = RECTHEIGHT(rcBound);
  1380. pt.x -= plv->prcWorkAreas[pitem->iWorkArea].left;
  1381. pt.y -= plv->prcWorkAreas[pitem->iWorkArea].top;
  1382. if (pt.x < (-cxBound / 2)) {
  1383. ptNew.x = 0;
  1384. } else if (pt.x > (iWidth - (cxBound / 2))) {
  1385. ptNew.x = iWidth - cxBound;
  1386. } else
  1387. ptNew.x = pt.x;
  1388. if (pt.y < (-cyBound/2)) {
  1389. ptNew.y = 0;
  1390. } else if (pt.y > (iHeight - (cyBound / 2))) {
  1391. ptNew.y = iHeight - cyBound;
  1392. } else
  1393. ptNew.y = pt.y;
  1394. if ((ptNew.x != pt.x) || (ptNew.y != pt.y))
  1395. {
  1396. BOOL fUpdate;
  1397. RECT rcTest;
  1398. ptNew.x += plv->prcWorkAreas[pitem->iWorkArea].left;
  1399. ptNew.y += plv->prcWorkAreas[pitem->iWorkArea].top;
  1400. // See if the potential rectangle intersects other items.
  1401. rcTest.left = ptNew.x - plv->ptOrigin.x;
  1402. rcTest.top = ptNew.y - plv->ptOrigin.y;
  1403. rcTest.right = rcTest.left + cxBound;
  1404. rcTest.bottom = rcTest.top + cyBound;
  1405. item.iItem = iItem;
  1406. item.lParam = pitem->lParam;
  1407. ListView_BeginFakeItemDraw(&lvfd);
  1408. if (!ListView_IsCleanRect(plv, &rcTest, iItem, &fUpdate, lvfd.nmcd.nmcd.hdc))
  1409. {
  1410. // doh! We hit another item, let's try to find an available location
  1411. // for this item
  1412. BOOL fUpdateSB;
  1413. BOOL fAppendAtEnd = FALSE;
  1414. int cSlots = ListView_GetSlotCountEx(plv, FALSE, pitem->iWorkArea);
  1415. iFree = ListView_FindFreeSlot(plv, iItem, iFree + 1, cSlots, &fUpdateSB, &fAppendAtEnd, lvfd.nmcd.nmcd.hdc);
  1416. if (iFree == -1)
  1417. goto SetFirstGuess;
  1418. ListView_SetIconPos(plv, pitem, iFree, cSlots);
  1419. ListView_EndFakeItemDraw(&lvfd);
  1420. continue;
  1421. }
  1422. SetFirstGuess:
  1423. ListView_EndFakeItemDraw(&lvfd);
  1424. ListView_OnSetItemPosition(plv, iItem, ptNew.x, ptNew.y);
  1425. }
  1426. }
  1427. ListView_EndFakeCustomDraw(&lvfd);
  1428. }
  1429. #define DPA_LAST 0x7fffffff
  1430. //
  1431. // ListView_OnSetWorkAreas
  1432. //
  1433. // set the "work areas" for the list view.
  1434. // the "work areas" are a group of sub rectanges of the list view client rect
  1435. // where icons are aranged, and parked by default.
  1436. //
  1437. void NEAR ListView_OnSetWorkAreas(LV* plv, int nWorkAreas, LPRECT prc)
  1438. {
  1439. int nOldWorkAreas;
  1440. int iWork;
  1441. BOOL bAutoArrange;
  1442. HDPA hdpaLostItems = NULL;
  1443. RECT rcOldWorkAreas[LV_MAX_WORKAREAS];
  1444. bAutoArrange = plv->ci.style & LVS_AUTOARRANGE;
  1445. nOldWorkAreas = plv->nWorkAreas;
  1446. if (nOldWorkAreas > 0)
  1447. {
  1448. ASSERT(plv->prcWorkAreas != NULL);
  1449. hmemcpy(&rcOldWorkAreas[0], &plv->prcWorkAreas[0], SIZEOF(RECT) * nOldWorkAreas);
  1450. }
  1451. // for the mirrored case, the coordinates are reversed. IsRectEmpty() will always succeed
  1452. if (nWorkAreas == 0 || prc == NULL || ((IsRectEmpty(prc)) && !(plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)))
  1453. plv->nWorkAreas = 0;
  1454. else
  1455. {
  1456. plv->nWorkAreas = min(nWorkAreas, LV_MAX_WORKAREAS);
  1457. if (plv->prcWorkAreas == NULL)
  1458. plv->prcWorkAreas = (LPRECT)LocalAlloc(LPTR, SIZEOF(RECT) * LV_MAX_WORKAREAS);
  1459. if (plv->prcWorkAreas == NULL)
  1460. return;
  1461. //BUGBUG: Should we check if they intersect? This problem is sort of
  1462. // solved (or made more confusing) by ListView_GetFreeSlot since it checks all of the icons for
  1463. // intersection instead of just the ones in the workarea.
  1464. for (iWork = 0; iWork < plv->nWorkAreas; iWork++)
  1465. CopyRect(&plv->prcWorkAreas[iWork], &prc[iWork]);
  1466. }
  1467. // We don't support workareas for owner-data because our icon placement
  1468. // algorithm (ListView_IGetRectsOwnerData) completely ignores workareas
  1469. // and just dumps the icons in a rectangular array starting at (0,0).
  1470. if (!ListView_IsOwnerData(plv) &&
  1471. plv->nWorkAreas > 0 &&
  1472. ((plv->nWorkAreas != nOldWorkAreas) ||
  1473. (!EqualRects(&plv->prcWorkAreas[0], &rcOldWorkAreas[0], nOldWorkAreas))))
  1474. {
  1475. int iItem;
  1476. LISTITEM FAR * pitem;
  1477. //
  1478. // Subtle - ListView_Recompute cleans up all the RECOMPUTE icons,
  1479. // but in order to do that, it needs to have valid work area
  1480. // rectangles. So the call must happen after the CopyRect but before
  1481. // the loop that checks the icon positions.
  1482. //
  1483. ListView_Recompute(plv);
  1484. for (iItem = 0; iItem < ListView_Count(plv); iItem++)
  1485. {
  1486. pitem = ListView_FastGetItemPtr(plv, iItem);
  1487. if (pitem->pt.x == RECOMPUTE || pitem->pt.y == RECOMPUTE)
  1488. {
  1489. // ListView_Recompute should've fixed these if we were in
  1490. // an iconical view.
  1491. ASSERT(!(ListView_IsIconView(plv) || ListView_IsSmallView(plv)));
  1492. continue;
  1493. }
  1494. // Try to move me to the same location relative to the same workarea.
  1495. // This will give the cool shift effect when tools bars take the border areas.
  1496. // And we only want to do this for the workareas that changed
  1497. // Don't bully the icons on the workareas, Autoarrange will do the work for us
  1498. if (nOldWorkAreas > 0)
  1499. {
  1500. int iOldWorkArea;
  1501. iOldWorkArea = pitem->iWorkArea;
  1502. if (iOldWorkArea >= plv->nWorkAreas)
  1503. {
  1504. // My workarea is gone, put me on the primary workarea i.e. #0
  1505. pitem->iWorkArea = 0;
  1506. if (!bAutoArrange)
  1507. {
  1508. // If this item point location is already in the new primary workarea,
  1509. // move it out, and let ListView_BullyIconsOnWorkarea arrange it to the
  1510. // right place. NOTE: this could happen in the case the old secondary monitor
  1511. // is to the left of the old primary monitor, and user kills the secondary monitor
  1512. if (PtInRect(&plv->prcWorkAreas[0], pitem->pt))
  1513. {
  1514. pitem->pt.x = plv->prcWorkAreas[0].right + 1;
  1515. plv->iFreeSlot = -1; // an item moved -- old slot info is invalid
  1516. }
  1517. goto InsertLostItemsArray;
  1518. }
  1519. }
  1520. else if ((!bAutoArrange) && (!EqualRect(&plv->prcWorkAreas[iOldWorkArea], &rcOldWorkAreas[iOldWorkArea])))
  1521. {
  1522. RECT rcBound;
  1523. POINT ptCenter;
  1524. pitem->pt.x += plv->prcWorkAreas[iOldWorkArea].left - rcOldWorkAreas[iOldWorkArea].left;
  1525. pitem->pt.y += plv->prcWorkAreas[iOldWorkArea].top - rcOldWorkAreas[iOldWorkArea].top;
  1526. // Use the center of this icon to determine whether it's out of bound
  1527. ListView_GetRects(plv, iItem, NULL, NULL, &rcBound, NULL);
  1528. ptCenter.x = pitem->pt.x + RECTWIDTH(rcBound) / 2;
  1529. ptCenter.y = pitem->pt.y + RECTHEIGHT(rcBound) / 2;
  1530. // If this shifted me out of bounds, register to be bullied on the workarea
  1531. if (!PtInRect(&plv->prcWorkAreas[iOldWorkArea], ptCenter))
  1532. {
  1533. InsertLostItemsArray:
  1534. if (!hdpaLostItems)
  1535. {
  1536. hdpaLostItems = DPA_Create(4);
  1537. if (!hdpaLostItems)
  1538. // we ran out of memory
  1539. ASSERT(0);
  1540. }
  1541. if (hdpaLostItems)
  1542. DPA_InsertPtr(hdpaLostItems, DPA_LAST, IntToPtr(iItem));
  1543. }
  1544. }
  1545. }
  1546. else
  1547. {
  1548. // My first time in a multi-workarea system, so find out my workarea
  1549. if (!ListView_FindWorkArea(plv, pitem->pt, &(pitem->iWorkArea)) && !bAutoArrange)
  1550. goto InsertLostItemsArray;
  1551. }
  1552. if ((plv->exStyle & LVS_EX_REGIONAL) && (pitem->hrgnIcon))
  1553. {
  1554. if (pitem->hrgnIcon != (HANDLE)-1)
  1555. DeleteObject(pitem->hrgnIcon);
  1556. pitem->hrgnIcon = NULL;
  1557. }
  1558. }
  1559. if (hdpaLostItems)
  1560. {
  1561. ASSERT(!bAutoArrange);
  1562. if (DPA_GetPtrCount(hdpaLostItems) > 0)
  1563. ListView_BullyIconsOnWorkarea(plv, hdpaLostItems);
  1564. DPA_Destroy(hdpaLostItems);
  1565. }
  1566. if (plv->exStyle & LVS_EX_REGIONAL)
  1567. ListView_RecalcRegion(plv, TRUE, TRUE);
  1568. if ((plv->ci.style & LVS_AUTOARRANGE) &&
  1569. (ListView_IsSmallView(plv) || ListView_IsIconView(plv)))
  1570. ListView_OnArrange(plv, LVA_DEFAULT);
  1571. }
  1572. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  1573. }
  1574. void NEAR ListView_OnGetNumberOfWorkAreas(LV* plv, int * pnWorkAreas)
  1575. {
  1576. if (pnWorkAreas)
  1577. *pnWorkAreas = plv->nWorkAreas;
  1578. }
  1579. void NEAR ListView_OnGetWorkAreas(LV* plv, int nWorkAreas, LPRECT prc)
  1580. {
  1581. int i;
  1582. ASSERT(prc);
  1583. for (i = 0; i < min(plv->nWorkAreas, nWorkAreas); i++)
  1584. {
  1585. if (i < plv->nWorkAreas)
  1586. CopyRect(&prc[i], &plv->prcWorkAreas[i]);
  1587. else
  1588. // Set the workareas to all zeros if we don't have it.
  1589. ZeroMemory(&prc[i], sizeof(RECT));
  1590. }
  1591. }
  1592. // test an item to see if it is unfolded (because it is focused)
  1593. BOOL ListView_IsItemUnfolded(LV *plv, int item)
  1594. {
  1595. return plv && (item >= 0) && ListView_IsIconView(plv) &&
  1596. (plv->flags & LVF_UNFOLDED) && (plv->iFocus == item);
  1597. }
  1598. BOOL ListView_IsItemUnfoldedPtr(LV *plv, LISTITEM *pitem)
  1599. {
  1600. return plv && pitem && ListView_IsIconView(plv) &&
  1601. (plv->flags & LVF_UNFOLDED) && (pitem->state & LVIS_FOCUSED);
  1602. }
  1603. // Returns TRUE if unfolding the item will be worthwhile
  1604. BOOL ListView_GetUnfoldedRect(LV* plv, int iItem, RECT *prc)
  1605. {
  1606. ListView_GetRects(plv, iItem, NULL, prc, NULL, NULL);
  1607. return ListView_UnfoldRects(plv, iItem, NULL, prc, NULL, NULL);
  1608. }
  1609. LRESULT ListView_OnNotify(LV* plv, WPARAM wParam, LPNMHDR pnmh)
  1610. {
  1611. // we can't switch on the control ID because the tooltip is a WS_POPUP window
  1612. // and does not have a control ID. (header and tooltip both have 0 as ID)
  1613. if (plv->hwndHdr && (plv->hwndHdr == pnmh->hwndFrom))
  1614. {
  1615. // this is a notify for the header, deal with it as needed
  1616. return ListView_HeaderNotify(plv, (HD_NOTIFY *)pnmh);
  1617. }
  1618. else if (plv->hwndToolTips && (plv->hwndToolTips == pnmh->hwndFrom))
  1619. {
  1620. // implement unfolding the text for items as well as info tip support
  1621. switch (pnmh->code)
  1622. {
  1623. case TTN_NEEDTEXT:
  1624. {
  1625. POINT pt;
  1626. UINT uFlags;
  1627. int iNewHit;
  1628. int iNewSubHit;
  1629. NMTTDISPINFO *pttt = (NMTTDISPINFO *)pnmh;
  1630. GetMessagePosClient(plv->ci.hwnd, &pt);
  1631. iNewHit = _ListView_ItemHitTest(plv, pt.x, pt.y, &uFlags, &iNewSubHit);
  1632. if (iNewHit != plv->iTTLastHit || iNewSubHit != plv->iTTLastSubHit)
  1633. {
  1634. plv->fPlaceTooltip = FALSE; // Set it to TRUE only if Unfolding tip is set
  1635. Str_Set(&plv->pszTip, NULL); // clear the old tip
  1636. plv->iTTLastHit = iNewHit;
  1637. plv->iTTLastSubHit = iNewSubHit;
  1638. if ((iNewHit >= 0) && (plv->iEdit == -1))
  1639. {
  1640. TCHAR szBuf[INFOTIPSIZE], szBuf2[INFOTIPSIZE];
  1641. BOOL bItemUnfolded;
  1642. BOOL fInfoTip = FALSE;
  1643. LPTSTR pszTip = szBuf; // Use this one first
  1644. szBuf[0] = 0;
  1645. szBuf2[0] = 0;
  1646. // preload the tip text for folder items. this
  1647. // may be overridden by callback below
  1648. bItemUnfolded = ListView_IsItemUnfolded2(plv, plv->iTTLastHit, plv->iTTLastSubHit, szBuf, ARRAYSIZE(szBuf));
  1649. lstrcpyn(szBuf2, szBuf, ARRAYSIZE(szBuf2)); // Backup the unfolding text
  1650. if (ListView_IsInfoTip(plv) && iNewSubHit == 0)
  1651. {
  1652. NMLVGETINFOTIP git;
  1653. git.dwFlags = bItemUnfolded ? LVGIT_UNFOLDED : 0;
  1654. git.pszText = szBuf;
  1655. git.cchTextMax = ARRAYSIZE(szBuf);
  1656. git.iItem = plv->iTTLastHit;
  1657. git.iSubItem = 0;
  1658. git.lParam = 0;
  1659. // for folded items pszText is prepopulated with the
  1660. // item text, clients should append to this
  1661. CCSendNotify(&plv->ci, LVN_GETINFOTIP, &git.hdr);
  1662. // Sometimes defview gets confused and nulls out the
  1663. // buffer instead of leaving it alone (sigh)
  1664. if (szBuf[0] == TEXT('\0'))
  1665. {
  1666. pszTip = szBuf2; // Use the original text
  1667. }
  1668. else if (lstrcmp(szBuf, szBuf2) != 0)
  1669. {
  1670. // App changed something - there is a real infotip
  1671. fInfoTip = TRUE;
  1672. }
  1673. }
  1674. //
  1675. // Set the margins now before the TTN_SHOW because it will be too late then.
  1676. //
  1677. // We want fat margins if we're an infotip, thin margins if we're an
  1678. // in-place tooltip.
  1679. //
  1680. if (fInfoTip)
  1681. {
  1682. static const RECT rcMargin = {4, 4, 4, 4};
  1683. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  1684. CCSetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  1685. }
  1686. else
  1687. {
  1688. static const RECT rcMargin = {0, 0, 0, 0};
  1689. plv->fPlaceTooltip = TRUE;
  1690. SendMessage(plv->hwndToolTips, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  1691. CCResetInfoTipWidth(plv->ci.hwnd, plv->hwndToolTips);
  1692. }
  1693. Str_Set(&plv->pszTip, pszTip);
  1694. }
  1695. }
  1696. pttt->lpszText = plv->pszTip; // here it is...
  1697. }
  1698. break;
  1699. // Handle custom draw as we want the tooltip painted as a multi-line that
  1700. // matches the formatting used by the list view.
  1701. case NM_CUSTOMDRAW:
  1702. {
  1703. LPNMTTCUSTOMDRAW pnm = (LPNMTTCUSTOMDRAW) pnmh;
  1704. if (plv->fPlaceTooltip &&
  1705. (pnm->nmcd.dwDrawStage == CDDS_PREPAINT ||
  1706. pnm->nmcd.dwDrawStage == CDDS_ITEMPREPAINT))
  1707. {
  1708. DWORD dwCustom = 0;
  1709. //
  1710. // Set up the customdraw DC to match the font of the LV item.
  1711. //
  1712. if (plv->iTTLastHit != -1)
  1713. {
  1714. LVFAKEDRAW lvfd;
  1715. LV_ITEM item;
  1716. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  1717. item.iItem = plv->iTTLastHit;
  1718. item.iSubItem = plv->iTTLastSubHit;
  1719. item.mask = LVIF_PARAM;
  1720. ListView_OnGetItem(plv, &item);
  1721. dwCustom = ListView_BeginFakeItemDraw(&lvfd);
  1722. // If client changed the font, then transfer the font
  1723. // from our private hdc into the tooltip's HDC. We use
  1724. // a private HDC because we only want to let the app change
  1725. // the font, not the colors or anything else.
  1726. if (dwCustom & CDRF_NEWFONT)
  1727. {
  1728. SelectObject(pnm->nmcd.hdc, GetCurrentObject(lvfd.nmcd.nmcd.hdc, OBJ_FONT));
  1729. }
  1730. ListView_EndFakeItemDraw(&lvfd);
  1731. ListView_EndFakeCustomDraw(&lvfd);
  1732. }
  1733. //
  1734. // The Large Icon tooltip needs to be drawn specially.
  1735. //
  1736. if (ListView_IsIconView(plv))
  1737. {
  1738. pnm->uDrawFlags &= ~(DT_SINGLELINE|DT_LEFT);
  1739. pnm->uDrawFlags |= DT_CENTER|DT_LVWRAP;
  1740. if ( pnm->uDrawFlags & DT_CALCRECT )
  1741. {
  1742. pnm->nmcd.rc.right = pnm->nmcd.rc.left + (lv_cxIconSpacing - g_cxLabelMargin * 2);
  1743. pnm->nmcd.rc.bottom = pnm->nmcd.rc.top + 0x10000; // big number, no limit!
  1744. }
  1745. }
  1746. // Don't return other wacky flags to TT, since all we
  1747. // did was change the font (if even that)
  1748. return dwCustom & CDRF_NEWFONT;
  1749. }
  1750. }
  1751. break;
  1752. case TTN_SHOW:
  1753. if (plv->iTTLastHit != -1)
  1754. {
  1755. if (plv->fPlaceTooltip)
  1756. {
  1757. LPNMTTSHOWINFO psi = (LPNMTTSHOWINFO)pnmh;
  1758. RECT rcLabel;
  1759. // In case we're doing subitem hit-testing
  1760. rcLabel.top = plv->iTTLastSubHit;
  1761. rcLabel.left = LVIR_LABEL;
  1762. // reposition to allign with the text rect and
  1763. // set it to topmost
  1764. if (plv->iTTLastSubHit && ListView_OnGetSubItemRect(plv, plv->iTTLastHit, &rcLabel)) {
  1765. LV_ITEM item;
  1766. // we got the subitem rect. When we draw subitems, we give
  1767. // them SHDT_EXTRAMARGIN, so we have to also
  1768. rcLabel.left += g_cxLabelMargin * 3;
  1769. rcLabel.right -= g_cxLabelMargin * 3;
  1770. // And take the image into account, too.
  1771. // ListView_OnGetItem will worry about LVS_EX_SUBITEMIMAGES.
  1772. item.mask = LVIF_IMAGE;
  1773. item.iImage = -1;
  1774. item.iItem = plv->iTTLastHit;
  1775. item.iSubItem = plv->iTTLastSubHit;
  1776. ListView_OnGetItem(plv, &item);
  1777. if (item.iImage != -1)
  1778. rcLabel.left += plv->cxSmIcon;
  1779. } else { // a tip from subitem zero
  1780. ListView_GetUnfoldedRect(plv, plv->iTTLastHit, &rcLabel);
  1781. // SHDrawText actually leaves a g_cxLabelMargin margin
  1782. rcLabel.left += g_cxLabelMargin;
  1783. rcLabel.right -= g_cxLabelMargin;
  1784. }
  1785. // In report and list views, SHDrawText does vertical
  1786. // centering (without consulting the custom-draw client,
  1787. // even, so it just centers by a random amount).
  1788. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  1789. {
  1790. rcLabel.top += (rcLabel.bottom - rcLabel.top - plv->cyLabelChar) / 2;
  1791. }
  1792. SendMessage(plv->hwndToolTips, TTM_ADJUSTRECT, TRUE, (LPARAM)&rcLabel);
  1793. MapWindowRect(plv->ci.hwnd, HWND_DESKTOP, &rcLabel);
  1794. if (!ListView_IsIconView(plv))
  1795. {
  1796. // In non-large-icon view, the label size may be greater than the rect returned by ListView_GetUnfoldedRect.
  1797. // So don't specify the size
  1798. SetWindowPos(plv->hwndToolTips, HWND_TOP,
  1799. rcLabel.left, rcLabel.top,
  1800. 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW);
  1801. }
  1802. else
  1803. {
  1804. SetWindowPos(plv->hwndToolTips, HWND_TOP,
  1805. rcLabel.left, rcLabel.top,
  1806. (rcLabel.right - rcLabel.left), (rcLabel.bottom - rcLabel.top),
  1807. SWP_NOACTIVATE | SWP_HIDEWINDOW);
  1808. }
  1809. // This is an inplace tooltip, so disable animation.
  1810. psi->dwStyle |= TTS_NOANIMATE;
  1811. return TRUE;
  1812. }
  1813. }
  1814. break;
  1815. }
  1816. }
  1817. return 0;
  1818. }
  1819. // Pass the focus to the given window, and then check to see if it exists.
  1820. // Passing focus can cause the window to be destroyed (by the Explorer
  1821. // when renaming).
  1822. BOOL NEAR ListView_SetFocus(HWND hwnd)
  1823. {
  1824. SetFocus(hwnd);
  1825. return IsWindow(hwnd);
  1826. }
  1827. void NEAR ListView_Realize(LV* plv, HDC hdcParam, BOOL fBackground, BOOL fForceRepaint)
  1828. {
  1829. if (plv->hpalHalftone)
  1830. {
  1831. HDC hdc = hdcParam ? hdcParam : GetDC(plv->ci.hwnd);
  1832. if (hdc)
  1833. {
  1834. BOOL fRepaint;
  1835. SelectPalette(hdc, plv->hpalHalftone, fBackground);
  1836. fRepaint = RealizePalette(hdc) || fForceRepaint;
  1837. if (!hdcParam)
  1838. ReleaseDC(plv->ci.hwnd, hdc);
  1839. if (fRepaint)
  1840. {
  1841. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  1842. }
  1843. }
  1844. }
  1845. }
  1846. BOOL RectInRect(const RECT* prcOuter, const RECT* prcInner)
  1847. {
  1848. #ifdef COMPLETELYINRECT
  1849. return (prcOuter->left <= prcInner->left ) &&
  1850. (prcOuter->top <= prcInner->top ) &&
  1851. (prcOuter->right >= prcInner->right ) &&
  1852. (prcOuter->bottom >= prcInner->bottom);
  1853. #else
  1854. RECT rcDummy;
  1855. return IntersectRect(&rcDummy, prcOuter, prcInner);
  1856. #endif
  1857. }
  1858. LRESULT LVGenerateDragImage(LV* plv, SHDRAGIMAGE* pshdi)
  1859. {
  1860. LRESULT lRet = 0;
  1861. int iNumSelected = plv->nSelected;
  1862. int iIndex;
  1863. int iSelectedItem;
  1864. RECT rc = {0, 0, 0, 0};
  1865. RECT rcVisRect;
  1866. HBITMAP hbmpOld = NULL;
  1867. HDC hdcDragImage;
  1868. // First loop through can get the selection rect
  1869. if (ListView_IsOwnerData( plv ))
  1870. {
  1871. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &iNumSelected);
  1872. }
  1873. if (iNumSelected == 0)
  1874. return FALSE;
  1875. GetClientRect(plv->ci.hwnd, &rcVisRect);
  1876. // Loop Through and calculate the enclosing rect.
  1877. for (iIndex = iNumSelected - 1, iSelectedItem = -1; iIndex >= 0; iIndex--)
  1878. {
  1879. iSelectedItem = ListView_OnGetNextItem(plv, iSelectedItem, LVNI_SELECTED);
  1880. if (iSelectedItem != -1)
  1881. {
  1882. RECT rcItemBounds;
  1883. // Make sure this is in the visible region
  1884. if (ListView_GetItemRect(plv->ci.hwnd, iSelectedItem, &rcItemBounds, LVIR_SELECTBOUNDS) &&
  1885. RectInRect(&rcVisRect, &rcItemBounds))
  1886. {
  1887. UnionRect(&rc, &rc, &rcItemBounds);
  1888. }
  1889. }
  1890. }
  1891. hdcDragImage = CreateCompatibleDC(NULL);
  1892. if (!hdcDragImage)
  1893. return 0;
  1894. // After this rc contains the bounds of all the items in Client Coordinates.
  1895. //
  1896. // Mirror the the DC, if the listview is mirrored.
  1897. //
  1898. if (plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  1899. {
  1900. SET_DC_RTL_MIRRORED(hdcDragImage);
  1901. }
  1902. #define MAX_DRAG_RECT_WIDTH 300
  1903. #define MAX_DRAG_RECT_HEIGHT 300
  1904. // If this rect is too big, fix it.
  1905. if (RECTWIDTH(rc) > MAX_DRAG_RECT_WIDTH)
  1906. {
  1907. int iLeft = MAX_DRAG_RECT_WIDTH / 2;
  1908. int iRight = MAX_DRAG_RECT_WIDTH /2;
  1909. int iRectOriginalLeft = rc.left;
  1910. // Is the left boundry outside the visible rect?
  1911. if (rc.left < plv->ptCapture.x - iLeft)
  1912. {
  1913. // Yes, then we have to clip it.
  1914. rc.left = plv->ptCapture.x - iLeft;
  1915. }
  1916. else
  1917. {
  1918. // No? Well then shift the visible rect to the right, so that we have
  1919. // more room.
  1920. iRight += rc.left - (plv->ptCapture.x - iLeft);
  1921. }
  1922. // Is the right boundry outside the visible rect?
  1923. if (rc.right > plv->ptCapture.x + iRight)
  1924. {
  1925. // Yes, then we have to clip it.
  1926. rc.right = plv->ptCapture.x + iRight;
  1927. }
  1928. else
  1929. {
  1930. // No? Then try and add it to the left
  1931. if (rc.left > iRectOriginalLeft)
  1932. {
  1933. rc.left -= iRight - (rc.right - plv->ptCapture.x);
  1934. if (rc.left < iRectOriginalLeft)
  1935. rc.left = iRectOriginalLeft;
  1936. }
  1937. }
  1938. }
  1939. if (RECTHEIGHT(rc) > MAX_DRAG_RECT_HEIGHT)
  1940. {
  1941. // same for top and bottom:
  1942. // Is the top boundry outside the visible rect?
  1943. int iTop = MAX_DRAG_RECT_HEIGHT / 2;
  1944. int iBottom = MAX_DRAG_RECT_HEIGHT /2;
  1945. int iRectOriginalTop = rc.top;
  1946. if (rc.top < plv->ptCapture.y - iTop)
  1947. {
  1948. // Yes, then we have to clip it.
  1949. rc.top = plv->ptCapture.y - iTop;
  1950. }
  1951. else
  1952. {
  1953. // No? Well then shift the visible rect to the right, so that we have
  1954. // more room.
  1955. iBottom += rc.top - (plv->ptCapture.y - iTop);
  1956. }
  1957. // Is the right boundry outside the visible rect?
  1958. if (rc.bottom > plv->ptCapture.y + iBottom)
  1959. {
  1960. // Yes, then we have to clip it.
  1961. rc.bottom = plv->ptCapture.y + iBottom;
  1962. }
  1963. else
  1964. {
  1965. // No? Then try and add it to the top
  1966. if (rc.top > iRectOriginalTop)
  1967. {
  1968. rc.top -= iBottom - (rc.bottom - plv->ptCapture.y);
  1969. if (rc.top < iRectOriginalTop)
  1970. rc.top = iRectOriginalTop;
  1971. }
  1972. }
  1973. }
  1974. pshdi->sizeDragImage.cx = RECTWIDTH(rc);
  1975. pshdi->sizeDragImage.cy = RECTHEIGHT(rc);
  1976. pshdi->hbmpDragImage = CreateBitmap( pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy,
  1977. GetDeviceCaps(hdcDragImage, PLANES), GetDeviceCaps(hdcDragImage, BITSPIXEL),
  1978. NULL);
  1979. if (pshdi->hbmpDragImage)
  1980. {
  1981. LVDRAWITEM lvdi;
  1982. DWORD dwType;
  1983. int cItem;
  1984. RECT rcImage = {0, 0, pshdi->sizeDragImage.cx, pshdi->sizeDragImage.cy};
  1985. hbmpOld = SelectObject(hdcDragImage, pshdi->hbmpDragImage);
  1986. pshdi->crColorKey = RGB(0xFF, 0x00, 0x55);
  1987. FillRectClr(hdcDragImage, &rcImage, pshdi->crColorKey);
  1988. pshdi->crColorKey = GetPixel(hdcDragImage, 0, 0);
  1989. // Calculate the offset... The cursor should be in the bitmap rect.
  1990. if (plv->ci.dwExStyle & RTL_MIRRORED_WINDOW)
  1991. pshdi->ptOffset.x = rc.right - plv->ptCapture.x;
  1992. else
  1993. pshdi->ptOffset.x = plv->ptCapture.x - rc.left;
  1994. pshdi->ptOffset.y = plv->ptCapture.y - rc.top;
  1995. lvdi.prcClip = NULL;
  1996. lvdi.plv = plv;
  1997. lvdi.nmcd.nmcd.hdc = hdcDragImage;
  1998. lvdi.pitem = NULL;
  1999. dwType = plv->ci.style & LVS_TYPEMASK;
  2000. cItem = ListView_Count(plv);
  2001. // Now loop through again for the paint cycle
  2002. for (iIndex = cItem - 1, iSelectedItem = -1; iIndex >= 0; iIndex--)
  2003. {
  2004. if (ListView_IsOwnerData( plv ))
  2005. {
  2006. iSelectedItem++;
  2007. plv->plvrangeSel->lpVtbl->NextSelected(plv->plvrangeSel, iSelectedItem, &iSelectedItem);
  2008. }
  2009. else
  2010. {
  2011. LISTITEM FAR* pitem;
  2012. iSelectedItem = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, iIndex);
  2013. pitem = ListView_FastGetItemPtr(plv, iSelectedItem);
  2014. if (!(pitem->state & LVIS_SELECTED))
  2015. iSelectedItem = -1;
  2016. }
  2017. if (iSelectedItem != -1)
  2018. {
  2019. int iOldItemDrawing;
  2020. COLORREF crSave;
  2021. POINT ptOrigin = {-rc.left, -rc.top}; //Offset the rects by...
  2022. RECT rcItemBounds;
  2023. RECT rcTemp;
  2024. iOldItemDrawing = plv->iItemDrawing;
  2025. plv->iItemDrawing = iSelectedItem;
  2026. lvdi.nmcd.nmcd.dwItemSpec = iSelectedItem;
  2027. ListView_GetRects(plv, iSelectedItem, NULL, NULL, &rcItemBounds, NULL);
  2028. // Make sure this is in the visible region
  2029. if (IntersectRect(&rcTemp, &rcVisRect, &rcItemBounds))
  2030. {
  2031. ptOrigin.x += rcItemBounds.left;
  2032. ptOrigin.y += rcItemBounds.top;
  2033. // these may get changed
  2034. lvdi.lpptOrg = &ptOrigin;
  2035. lvdi.flags = 0;
  2036. lvdi.nmcd.clrText = plv->clrText;
  2037. lvdi.nmcd.clrTextBk = plv->clrTextBk;
  2038. // Save the Background color!
  2039. crSave = plv->clrBk;
  2040. plv->clrBk = pshdi->crColorKey;
  2041. ListView_DrawItem(&lvdi);
  2042. plv->clrBk = crSave;
  2043. }
  2044. plv->iItemDrawing = iOldItemDrawing;
  2045. }
  2046. }
  2047. SelectObject(hdcDragImage, hbmpOld);
  2048. DeleteDC(hdcDragImage);
  2049. // We're passing back the created HBMP.
  2050. return 1;
  2051. }
  2052. return lRet;
  2053. }
  2054. LRESULT CALLBACK ListView_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2055. {
  2056. LV* plv = ListView_GetPtr(hwnd);
  2057. if (plv == NULL)
  2058. {
  2059. if (uMsg == WM_NCCREATE)
  2060. {
  2061. plv = (LV*)NearAlloc(sizeof(LV));
  2062. if (!plv)
  2063. {
  2064. DebugMsg(DM_ERROR, TEXT("ListView: Out of near memory"));
  2065. return 0L; // fail the window create
  2066. }
  2067. plv->ci.hwnd = hwnd;
  2068. plv->flags = LVF_REDRAW; // assume that redrawing enabled!
  2069. plv->iFocus = -1; // no focus
  2070. plv->iMark = -1;
  2071. plv->iSelCol = -1;
  2072. plv->iDropHilite = -1; // Assume no item has drop hilite...
  2073. plv->cyItem = plv->cyItemSave = 1; // never let these be zero, not even for a moment
  2074. #ifdef WIN32
  2075. plv->hheap = GetProcessHeap();
  2076. #else
  2077. // plv->hheap = NULL; // not used in 16 bits...
  2078. #endif
  2079. ListView_SetPtr(hwnd, plv);
  2080. }
  2081. goto DoDefault;
  2082. }
  2083. if ((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) {
  2084. if (plv->exStyle & (LVS_EX_TRACKSELECT|LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE)) {
  2085. TRACKMOUSEEVENT tme;
  2086. tme.cbSize = sizeof(tme);
  2087. tme.hwndTrack = plv->ci.hwnd;
  2088. tme.dwHoverTime = plv->dwHoverTime;
  2089. tme.dwFlags = TME_LEAVE | TME_HOVER | TME_QUERY;
  2090. // see what's set
  2091. TrackMouseEvent(&tme);
  2092. tme.dwFlags &= TME_HOVER | TME_LEAVE;
  2093. // set these bits if they aren't already set
  2094. tme.dwFlags ^= TME_LEAVE;
  2095. if (plv->exStyle & LVS_EX_TRACKSELECT) {
  2096. tme.dwFlags ^= TME_HOVER;
  2097. }
  2098. tme.cbSize = sizeof(tme);
  2099. tme.hwndTrack = plv->ci.hwnd;
  2100. tme.dwHoverTime = plv->dwHoverTime;
  2101. // set it if there's anything to set
  2102. if (tme.dwFlags & (TME_HOVER | TME_LEAVE)) {
  2103. TrackMouseEvent(&tme);
  2104. }
  2105. }
  2106. }
  2107. if (uMsg == g_uDragImages)
  2108. {
  2109. return LVGenerateDragImage(plv, (SHDRAGIMAGE*)lParam);
  2110. }
  2111. switch (uMsg)
  2112. {
  2113. HANDLE_MSG(plv, WM_CREATE, ListView_OnCreate);
  2114. HANDLE_MSG(plv, WM_DESTROY, ListView_OnDestroy);
  2115. HANDLE_MSG(plv, WM_ERASEBKGND, ListView_OnEraseBkgnd);
  2116. HANDLE_MSG(plv, WM_COMMAND, ListView_OnCommand);
  2117. HANDLE_MSG(plv, WM_SETFOCUS, ListView_OnSetFocus);
  2118. HANDLE_MSG(plv, WM_KILLFOCUS, ListView_OnKillFocus);
  2119. HANDLE_MSG(plv, WM_HSCROLL, ListView_OnHScroll);
  2120. HANDLE_MSG(plv, WM_VSCROLL, ListView_OnVScroll);
  2121. HANDLE_MSG(plv, WM_GETDLGCODE, ListView_OnGetDlgCode);
  2122. HANDLE_MSG(plv, WM_SETFONT, ListView_OnSetFont);
  2123. HANDLE_MSG(plv, WM_GETFONT, ListView_OnGetFont);
  2124. HANDLE_MSG(plv, WM_TIMER, ListView_OnTimer);
  2125. HANDLE_MSG(plv, WM_SETREDRAW, ListView_OnSetRedraw);
  2126. HANDLE_MSG(plv, WM_NCDESTROY, ListView_OnNCDestroy);
  2127. case WM_SETCURSOR:
  2128. if (ListView_OnSetCursorMsg(plv))
  2129. return TRUE;
  2130. break;
  2131. case WM_PALETTECHANGED:
  2132. if ((HWND)wParam == hwnd)
  2133. break;
  2134. case WM_QUERYNEWPALETTE:
  2135. // Want to pass FALSE if WM_QUERYNEWPALETTE...
  2136. ListView_Realize(plv, NULL, uMsg == WM_PALETTECHANGED, uMsg == WM_PALETTECHANGED);
  2137. return TRUE;
  2138. case LVMP_WINDOWPOSCHANGED:
  2139. case WM_WINDOWPOSCHANGED:
  2140. HANDLE_WM_WINDOWPOSCHANGED(plv, wParam, lParam, ListView_OnWindowPosChanged);
  2141. break;
  2142. case WM_MBUTTONDOWN:
  2143. if (ListView_SetFocus(hwnd) && plv->hwndToolTips)
  2144. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  2145. break;
  2146. case WM_LBUTTONDBLCLK:
  2147. case WM_RBUTTONDBLCLK:
  2148. if (plv->hwndToolTips)
  2149. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  2150. ListView_OnButtonDown(plv, TRUE, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  2151. break;
  2152. case WM_LBUTTONDOWN:
  2153. case WM_RBUTTONDOWN:
  2154. if (plv->hwndToolTips)
  2155. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  2156. ListView_OnButtonDown(plv, FALSE, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  2157. break;
  2158. case WM_LBUTTONUP:
  2159. case WM_RBUTTONUP:
  2160. case WM_MBUTTONUP:
  2161. case WM_NCMOUSEMOVE:
  2162. if (plv->hwndToolTips)
  2163. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  2164. break;
  2165. case WM_PRINTCLIENT:
  2166. case WM_PAINT:
  2167. ListView_OnPaint(plv, (HDC)wParam);
  2168. return(0);
  2169. case WM_SHOWWINDOW:
  2170. LV_OnShowWindow(plv, BOOLFROMPTR(wParam));
  2171. break;
  2172. case WM_MOUSEHOVER:
  2173. ListView_OnMouseHover(plv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  2174. break;
  2175. case WM_MOUSELEAVE:
  2176. ListView_OnSetHotItem(plv, -1);
  2177. plv->iNoHover = -1;
  2178. break;
  2179. case WM_KEYUP:
  2180. plv->iScrollCount = 0;
  2181. break;
  2182. case WM_KEYDOWN:
  2183. HANDLE_WM_KEYDOWN(plv, wParam, lParam, ListView_OnKey);
  2184. break;
  2185. #if defined(FE_IME) || !defined(WINNT)
  2186. case WM_IME_COMPOSITION:
  2187. // Now only Korean version is interested in incremental search with composition string.
  2188. if (g_fDBCSInputEnabled) {
  2189. if (((ULONG_PTR)GetKeyboardLayout(0L) & 0xF000FFFFL) == 0xE0000412L)
  2190. {
  2191. if (ListView_OnImeComposition(plv, wParam, lParam))
  2192. {
  2193. lParam &= ~GCS_RESULTSTR;
  2194. break;
  2195. }
  2196. else
  2197. return 0;
  2198. }
  2199. }
  2200. break;
  2201. #endif
  2202. case WM_CHAR:
  2203. if (plv->iPuntChar) {
  2204. plv->iPuntChar--;
  2205. return TRUE;
  2206. } else {
  2207. return HANDLE_WM_CHAR(plv, wParam, lParam, ListView_OnChar);
  2208. }
  2209. case WM_WININICHANGE:
  2210. ListView_OnWinIniChange(plv, wParam, lParam);
  2211. break;
  2212. case WM_NOTIFYFORMAT:
  2213. return CIHandleNotifyFormat(&plv->ci, lParam);
  2214. case WM_ENABLE:
  2215. // HACK: we don't get WM_STYLECHANGE on EnableWindow()
  2216. ListView_EnableWindow(plv, BOOLFROMPTR(wParam));
  2217. break;
  2218. case WM_SYSCOLORCHANGE:
  2219. InitGlobalColors();
  2220. if (plv->ci.style & WS_DISABLED)
  2221. {
  2222. if (!(plv->flags & LVF_USERBKCLR))
  2223. plv->clrBkSave = g_clrWindow;
  2224. ListView_OnSetBkColor(plv, g_clrBtnFace);
  2225. }
  2226. else if (!(plv->flags & LVF_USERBKCLR))
  2227. {
  2228. ListView_OnSetBkColor(plv, g_clrWindow);
  2229. }
  2230. if (plv->exStyle & LVS_EX_CHECKBOXES)
  2231. {
  2232. ListView_InitCheckBoxes(plv, FALSE);
  2233. }
  2234. // 98/11/19 #249967 vtan: Always invalidate the list view
  2235. // rectangle so that the color change causes a refresh.
  2236. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2237. break;
  2238. // don't use HANDLE_MSG because this needs to go to the default handler
  2239. case WM_SYSKEYDOWN:
  2240. HANDLE_WM_SYSKEYDOWN(plv, wParam, lParam, ListView_OnKey);
  2241. break;
  2242. #ifdef KEYBOARDCUES
  2243. case WM_UPDATEUISTATE:
  2244. {
  2245. DWORD dwUIStateMask = MAKEWPARAM(0xFFFF, UISF_HIDEFOCUS);
  2246. // we care only about focus not accel, and redraw only if changed
  2247. if (CCOnUIState(&(plv->ci), WM_UPDATEUISTATE, wParam & dwUIStateMask, lParam))
  2248. {
  2249. if(plv->iFocus >= 0)
  2250. {
  2251. // an item has the focus, invalidate it
  2252. ListView_InvalidateItem(plv, plv->iFocus, FALSE, RDW_INVALIDATE | RDW_ERASE);
  2253. }
  2254. }
  2255. goto DoDefault;
  2256. }
  2257. #endif
  2258. #ifdef UNICODE
  2259. case LVM_GETITEMA:
  2260. return (LRESULT)ListView_OnGetItemA(plv, (LV_ITEMA *)lParam);
  2261. case LVM_SETITEMA:
  2262. return (LRESULT)ListView_OnSetItemA(plv, (LV_ITEMA *)lParam);
  2263. case LVM_INSERTITEMA:
  2264. return (LRESULT)ListView_OnInsertItemA(plv, (LV_ITEMA *)lParam);
  2265. case LVM_FINDITEMA:
  2266. return (LRESULT)ListView_OnFindItemA(plv, (int)wParam, (LV_FINDINFOA *)lParam);
  2267. case LVM_GETSTRINGWIDTHA:
  2268. return (LRESULT)ListView_OnGetStringWidthA(plv, (LPCSTR)lParam, NULL);
  2269. case LVM_GETCOLUMNA:
  2270. return (LRESULT)ListView_OnGetColumnA(plv, (int)wParam, (LV_COLUMNA *)lParam);
  2271. case LVM_SETCOLUMNA:
  2272. return (LRESULT)ListView_OnSetColumnA(plv, (int)wParam, (LV_COLUMNA *)lParam);
  2273. case LVM_INSERTCOLUMNA:
  2274. return (LRESULT)ListView_OnInsertColumnA(plv, (int)wParam, (LV_COLUMNA *)lParam);
  2275. case LVM_GETITEMTEXTA:
  2276. return (LRESULT)ListView_OnGetItemTextA(plv, (int)wParam, (LV_ITEMA FAR *)lParam);
  2277. case LVM_SETITEMTEXTA:
  2278. if (!lParam)
  2279. return FALSE;
  2280. return (LRESULT)ListView_OnSetItemTextA(plv, (int)wParam,
  2281. ((LV_ITEMA *)lParam)->iSubItem,
  2282. (LPCSTR)((LV_ITEMA FAR *)lParam)->pszText);
  2283. case LVM_GETBKIMAGEA:
  2284. return (LRESULT)ListView_OnGetBkImageA(plv, (LPLVBKIMAGEA)lParam);
  2285. case LVM_SETBKIMAGEA:
  2286. return (LRESULT)ListView_OnSetBkImageA(plv, (LPLVBKIMAGEA)lParam);
  2287. #else
  2288. #ifdef DEBUG
  2289. case LVM_GETITEMW:
  2290. case LVM_SETITEMW:
  2291. case LVM_INSERTITEMW:
  2292. case LVM_FINDITEMW:
  2293. case LVM_GETSTRINGWIDTHW:
  2294. case LVM_GETCOLUMNW:
  2295. case LVM_SETCOLUMNW:
  2296. case LVM_INSERTCOLUMNW:
  2297. case LVM_GETITEMTEXTW:
  2298. case LVM_SETITEMTEXTW:
  2299. case LVM_GETBKIMAGEW:
  2300. case LVM_SETBKIMAGEW:
  2301. case LVM_GETISEARCHSTRINGW:
  2302. case LVM_EDITLABELW:
  2303. break;
  2304. #endif
  2305. #endif
  2306. case WM_STYLECHANGING:
  2307. ListView_OnStyleChanging(plv, (UINT)wParam, (LPSTYLESTRUCT)lParam);
  2308. return 0;
  2309. case WM_STYLECHANGED:
  2310. ListView_OnStyleChanged(plv, (UINT) wParam, (LPSTYLESTRUCT)lParam);
  2311. return 0L;
  2312. case WM_HELP:
  2313. return ListView_OnHelp(plv, (LPHELPINFO)lParam);
  2314. case LVM_GETIMAGELIST:
  2315. return (LRESULT)(UINT_PTR)(ListView_OnGetImageList(plv, (int)wParam));
  2316. case LVM_SETIMAGELIST:
  2317. return (LRESULT)(UINT_PTR)ListView_OnSetImageList(plv, (HIMAGELIST)lParam, (int)wParam);
  2318. case LVM_GETBKCOLOR:
  2319. return (LRESULT)(plv->ci.style & WS_DISABLED ? plv->clrBkSave : plv->clrBk);
  2320. case LVM_SETBKCOLOR:
  2321. plv->flags |= LVF_USERBKCLR;
  2322. if (plv->ci.style & WS_DISABLED) {
  2323. plv->clrBkSave = (COLORREF)lParam;
  2324. return TRUE;
  2325. } else {
  2326. return (LRESULT)ListView_OnSetBkColor(plv, (COLORREF)lParam);
  2327. }
  2328. case LVM_GETTEXTCOLOR:
  2329. return (LRESULT)plv->clrText;
  2330. case LVM_SETTEXTCOLOR:
  2331. plv->clrText = (COLORREF)lParam;
  2332. return TRUE;
  2333. case LVM_GETTEXTBKCOLOR:
  2334. return (LRESULT)plv->clrTextBk;
  2335. case LVM_SETTEXTBKCOLOR:
  2336. plv->clrTextBk = (COLORREF)lParam;
  2337. return TRUE;
  2338. case LVM_GETHOTLIGHTCOLOR:
  2339. return (LRESULT)plv->clrHotlight;
  2340. case LVM_SETHOTLIGHTCOLOR:
  2341. plv->clrHotlight = (COLORREF)lParam;
  2342. return(TRUE);
  2343. case LVM_GETITEMCOUNT:
  2344. if (ListView_IsOwnerData(plv))
  2345. return((LRESULT)plv->cTotalItems);
  2346. else if (!plv->hdpa)
  2347. return(0);
  2348. else
  2349. return((LRESULT)DPA_GetPtrCount(plv->hdpa));
  2350. break;
  2351. case LVM_GETITEM:
  2352. return (LRESULT)ListView_OnGetItem(plv, (LV_ITEM FAR*)lParam);
  2353. case LVM_GETITEMSTATE:
  2354. return (LRESULT)ListView_OnGetItemState(plv, (int)wParam, (UINT)lParam);
  2355. case LVM_SETITEMSTATE:
  2356. if (!lParam)
  2357. return FALSE;
  2358. return (LRESULT)ListView_OnSetItemState(plv, (int)wParam,
  2359. ((LV_ITEM FAR *)lParam)->state,
  2360. ((LV_ITEM FAR *)lParam)->stateMask);
  2361. case LVM_SETITEMTEXT:
  2362. if (!lParam)
  2363. return FALSE;
  2364. return (LRESULT)ListView_OnSetItemText(plv, (int)wParam,
  2365. ((LV_ITEM FAR *)lParam)->iSubItem,
  2366. (LPCTSTR)((LV_ITEM FAR *)lParam)->pszText);
  2367. case LVM_GETITEMTEXT:
  2368. return (LRESULT)ListView_OnGetItemText(plv, (int)wParam, (LV_ITEM FAR *)lParam);
  2369. case LVM_GETBKIMAGE:
  2370. return (LRESULT)ListView_OnGetBkImage(plv, (LPLVBKIMAGE)lParam);
  2371. case LVM_SETBKIMAGE:
  2372. return (LRESULT)ListView_OnSetBkImage(plv, (LPLVBKIMAGE)lParam);
  2373. case LVM_SETITEM:
  2374. return (LRESULT)ListView_OnSetItem(plv, (const LV_ITEM FAR*)lParam);
  2375. case LVM_INSERTITEM:
  2376. return (LRESULT)ListView_OnInsertItem(plv, (const LV_ITEM FAR*)lParam);
  2377. case LVM_DELETEITEM:
  2378. return (LRESULT)ListView_OnDeleteItem(plv, (int)wParam);
  2379. case LVM_UPDATE:
  2380. ListView_OnUpdate(plv, (int)wParam);
  2381. UpdateWindow(plv->ci.hwnd);
  2382. return TRUE;
  2383. case LVM_DELETEALLITEMS:
  2384. lParam = (LRESULT)ListView_OnDeleteAllItems(plv);
  2385. // Optimization: Instead of sending out a zillion EVENT_OBJECT_DESTROY's,
  2386. // we send out a destroy of ourselves followed by a fresh create.
  2387. // For compatibility with IE4, we still send out the REORDER notification.
  2388. MyNotifyWinEvent(EVENT_OBJECT_REORDER, hwnd, OBJID_CLIENT, 0);
  2389. ListView_NotifyRecreate(plv);
  2390. return(lParam);
  2391. case LVM_GETITEMRECT:
  2392. return (LRESULT)ListView_OnGetItemRect(plv, (int)wParam, (RECT FAR*)lParam);
  2393. case LVM_GETSUBITEMRECT:
  2394. return (LRESULT)ListView_OnGetSubItemRect(plv, (int)wParam, (LPRECT)lParam);
  2395. case LVM_SUBITEMHITTEST:
  2396. return (LRESULT)ListView_OnSubItemHitTest(plv, (LPLVHITTESTINFO)lParam);
  2397. #ifdef UNICODE
  2398. case LVM_GETISEARCHSTRINGA:
  2399. if (GetFocus() == plv->ci.hwnd)
  2400. return (LRESULT)GetIncrementSearchStringA(&plv->is, plv->ci.uiCodePage, (LPSTR)lParam);
  2401. else
  2402. return 0;
  2403. #endif
  2404. case LVM_GETISEARCHSTRING:
  2405. if (GetFocus() == plv->ci.hwnd)
  2406. return (LRESULT)GetIncrementSearchString(&plv->is, (LPTSTR)lParam);
  2407. else
  2408. return 0;
  2409. case LVM_GETITEMSPACING:
  2410. if (wParam)
  2411. return MAKELONG(plv->cxItem, plv->cyItem);
  2412. else
  2413. return MAKELONG(lv_cxIconSpacing, lv_cyIconSpacing);
  2414. case LVM_GETNEXTITEM:
  2415. return (LRESULT)ListView_OnGetNextItem(plv, (int)wParam, (UINT)lParam);
  2416. case LVM_FINDITEM:
  2417. return (LRESULT)ListView_OnFindItem(plv, (int)wParam, (const LV_FINDINFO FAR*)lParam);
  2418. case LVM_SETSELECTIONMARK:
  2419. {
  2420. int iOldMark = plv->iMark;
  2421. int iNewMark = (int)lParam;
  2422. if (iNewMark == -1 || ListView_IsValidItemNumber(plv, iNewMark)) {
  2423. plv->iMark = iNewMark;
  2424. }
  2425. return iOldMark;
  2426. }
  2427. case LVM_GETSELECTIONMARK:
  2428. return plv->iMark;
  2429. case LVM_GETITEMPOSITION:
  2430. return (LRESULT)ListView_OnGetItemPosition(plv, (int)wParam,
  2431. (POINT FAR*)lParam);
  2432. case LVM_SETITEMPOSITION:
  2433. return (LRESULT)ListView_OnSetItemPosition(plv, (int)wParam,
  2434. GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2435. case LVM_SETITEMPOSITION32:
  2436. if (!lParam)
  2437. return FALSE;
  2438. return (LRESULT)ListView_OnSetItemPosition(plv, (int)wParam,
  2439. ((LPPOINT)lParam)->x, ((LPPOINT)lParam)->y);
  2440. case LVM_SCROLL:
  2441. {
  2442. int dx = (int)wParam;
  2443. int dy = (int)lParam;
  2444. return (LRESULT)
  2445. (ListView_ValidateScrollParams(plv, &dx, &dy) &&
  2446. ListView_OnScroll(plv, dx, dy));
  2447. }
  2448. case LVM_ENSUREVISIBLE:
  2449. return (LRESULT)ListView_OnEnsureVisible(plv, (int)wParam, BOOLFROMPTR(lParam));
  2450. case LVM_REDRAWITEMS:
  2451. return (LRESULT)ListView_OnRedrawItems(plv, (int)wParam, (int)lParam);
  2452. case LVM_ARRANGE:
  2453. return (LRESULT)ListView_OnArrange(plv, (UINT)wParam);
  2454. case LVM_GETEDITCONTROL:
  2455. return (LRESULT)(UINT_PTR)plv->hwndEdit;
  2456. #ifdef UNICODE
  2457. case LVM_EDITLABELA:
  2458. {
  2459. LPWSTR lpEditString = NULL;
  2460. HWND hRet;
  2461. if (lParam) {
  2462. lpEditString = ProduceWFromA(plv->ci.uiCodePage, (LPSTR)lParam);
  2463. }
  2464. hRet = ListView_OnEditLabel(plv, (int)wParam, lpEditString);
  2465. if (lpEditString) {
  2466. FreeProducedString(lpEditString);
  2467. }
  2468. return (LRESULT)hRet;
  2469. }
  2470. #endif
  2471. case LVM_EDITLABEL:
  2472. return (LRESULT)(UINT_PTR)ListView_OnEditLabel(plv, (int)wParam, (LPTSTR)lParam);
  2473. case LVM_HITTEST:
  2474. return (LRESULT)ListView_OnHitTest(plv, (LV_HITTESTINFO FAR*)lParam);
  2475. case LVM_GETSTRINGWIDTH:
  2476. return (LRESULT)ListView_OnGetStringWidth(plv, (LPCTSTR)lParam, NULL);
  2477. case LVM_GETCOLUMN:
  2478. return (LRESULT)ListView_OnGetColumn(plv, (int)wParam, (LV_COLUMN FAR*)lParam);
  2479. case LVM_SETCOLUMN:
  2480. return (LRESULT)ListView_OnSetColumn(plv, (int)wParam, (const LV_COLUMN FAR*)lParam);
  2481. case LVM_SETCOLUMNORDERARRAY:
  2482. return SendMessage(plv->hwndHdr, HDM_SETORDERARRAY, wParam, lParam);
  2483. case LVM_GETCOLUMNORDERARRAY:
  2484. return SendMessage(plv->hwndHdr, HDM_GETORDERARRAY, wParam, lParam);
  2485. case LVM_GETHEADER:
  2486. {
  2487. HWND hwndOld = plv->hwndHdr;
  2488. if (lParam && IsWindow((HWND)lParam)) {
  2489. plv->hwndHdr = (HWND)lParam;
  2490. }
  2491. return (LRESULT)hwndOld;
  2492. }
  2493. case LVM_INSERTCOLUMN:
  2494. return (LRESULT)ListView_OnInsertColumn(plv, (int)wParam, (const LV_COLUMN FAR*)lParam);
  2495. case LVM_DELETECOLUMN:
  2496. return (LRESULT)ListView_OnDeleteColumn(plv, (int)wParam);
  2497. case LVM_CREATEDRAGIMAGE:
  2498. return (LRESULT)(UINT_PTR)ListView_OnCreateDragImage(plv, (int)wParam, (LPPOINT)lParam);
  2499. case LVMI_PLACEITEMS:
  2500. if (plv->uUnplaced) {
  2501. ListView_Recompute(plv);
  2502. ListView_UpdateScrollBars(plv);
  2503. }
  2504. return 0;
  2505. case LVM_GETVIEWRECT:
  2506. if (!lParam)
  2507. return FALSE;
  2508. ListView_GetViewRect2(plv, (RECT FAR*)lParam, plv->sizeClient.cx, plv->sizeClient.cy);
  2509. return (LPARAM)TRUE;
  2510. case LVM_GETCOLUMNWIDTH:
  2511. return (LPARAM)ListView_OnGetColumnWidth(plv, (int)wParam);
  2512. case LVM_SETCOLUMNWIDTH:
  2513. return (LPARAM)ListView_ISetColumnWidth(plv, (int)wParam,
  2514. GET_X_LPARAM(lParam), TRUE);
  2515. case LVM_SETCALLBACKMASK:
  2516. plv->stateCallbackMask = (UINT)wParam;
  2517. return (LPARAM)TRUE;
  2518. case LVM_GETCALLBACKMASK:
  2519. return (LPARAM)(UINT)plv->stateCallbackMask;
  2520. case LVM_GETTOPINDEX:
  2521. return (LPARAM)ListView_OnGetTopIndex(plv);
  2522. case LVM_GETCOUNTPERPAGE:
  2523. return (LPARAM)ListView_OnGetCountPerPage(plv);
  2524. case LVM_GETORIGIN:
  2525. return (LPARAM)ListView_OnGetOrigin(plv, (POINT FAR*)lParam);
  2526. case LVM_SETITEMCOUNT:
  2527. return ListView_OnSetItemCount(plv, (int)wParam, (DWORD)lParam);
  2528. case LVM_GETSELECTEDCOUNT:
  2529. if (ListView_IsOwnerData( plv )) {
  2530. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  2531. }
  2532. return plv->nSelected;
  2533. case LVM_SORTITEMS:
  2534. return ListView_OnSortItems(plv, (LPARAM)wParam, (PFNLVCOMPARE)lParam, TRUE);
  2535. case LVM_SORTITEMSEX:
  2536. return ListView_OnSortItems(plv, (LPARAM)wParam, (PFNLVCOMPARE)lParam, FALSE);
  2537. case LVM_SETEXTENDEDLISTVIEWSTYLE:
  2538. return ListView_ExtendedStyleChange(plv, (DWORD) lParam, (DWORD) wParam);
  2539. case LVM_GETEXTENDEDLISTVIEWSTYLE:
  2540. return plv->exStyle;
  2541. case LVM_GETHOVERTIME:
  2542. return plv->dwHoverTime;
  2543. case LVM_SETHOVERTIME:
  2544. {
  2545. DWORD dwRet = plv->dwHoverTime;
  2546. plv->dwHoverTime = (DWORD)lParam;
  2547. return dwRet;
  2548. }
  2549. case LVM_GETTOOLTIPS:
  2550. return (LRESULT)plv->hwndToolTips;
  2551. case LVM_SETTOOLTIPS:
  2552. {
  2553. HWND hwndToolTips = plv->hwndToolTips;
  2554. plv->hwndToolTips = (HWND)wParam;
  2555. return (LRESULT)hwndToolTips;
  2556. }
  2557. case LVM_SETICONSPACING:
  2558. {
  2559. DWORD dwRet = ListView_OnSetIconSpacing(plv, lParam);
  2560. // rearrange as necessary
  2561. if (ListView_RedrawEnabled(plv) &&
  2562. ((plv->ci.style & LVS_AUTOARRANGE) &&
  2563. (ListView_IsSmallView(plv) || ListView_IsIconView(plv))))
  2564. {
  2565. // Call off to the arrange function.
  2566. ListView_OnArrange(plv, LVA_DEFAULT);
  2567. }
  2568. return dwRet;
  2569. }
  2570. case LVM_SETHOTITEM:
  2571. {
  2572. int iOld = plv->iHot;
  2573. int iNew = (int)wParam;
  2574. if (iNew == -1 || ListView_IsValidItemNumber(plv, iNew)) {
  2575. ListView_OnSetHotItem(plv, (int)wParam);
  2576. }
  2577. return iOld;
  2578. }
  2579. case LVM_GETHOTITEM:
  2580. return plv->iHot;
  2581. // hCurHot is used iff LVS_EX_TRACKSELECT
  2582. case LVM_SETHOTCURSOR:
  2583. {
  2584. HCURSOR hCurOld = plv->hCurHot;
  2585. plv->hCurHot = (HCURSOR)lParam;
  2586. return (LRESULT)hCurOld;
  2587. }
  2588. case LVM_GETHOTCURSOR:
  2589. if (!plv->hCurHot)
  2590. plv->hCurHot = LoadHandCursor(0);
  2591. return (LRESULT)plv->hCurHot;
  2592. case LVM_APPROXIMATEVIEWRECT:
  2593. return ListView_OnApproximateViewRect(plv, (int)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2594. case LVM_SETLVRANGEOBJECT:
  2595. return ListView_OnSetLVRangeObject(plv, (int)wParam, (ILVRange *)lParam);
  2596. #ifdef LVM_SETWORKAREAS // until the headers are in ssync.....
  2597. case LVM_SETWORKAREAS:
  2598. ListView_OnSetWorkAreas(plv, (int)wParam, (RECT FAR *)lParam);
  2599. return 0;
  2600. case LVM_GETWORKAREAS:
  2601. ListView_OnGetWorkAreas(plv, (int)wParam, (RECT FAR *)lParam);
  2602. return 0;
  2603. case LVM_GETNUMBEROFWORKAREAS:
  2604. ListView_OnGetNumberOfWorkAreas(plv, (int *)lParam);
  2605. return 0;
  2606. case LVM_RESETEMPTYTEXT:
  2607. plv->fNoEmptyText = FALSE;
  2608. Str_Set(&plv->pszEmptyText, NULL);
  2609. if (ListView_Count(plv) == 0)
  2610. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2611. return 1;
  2612. #endif
  2613. case WM_SIZE:
  2614. if (plv)
  2615. {
  2616. if (plv->hwndToolTips) {
  2617. TOOLINFO ti;
  2618. if (ListView_IsLabelTip(plv))
  2619. {
  2620. // A truncated label may have been exposed or vice versa.
  2621. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  2622. }
  2623. ti.cbSize = sizeof(ti);
  2624. ti.hwnd = plv->ci.hwnd;
  2625. ti.uId = 0;
  2626. // Resize the tooltip control so that it covers the entire
  2627. // area of the window when its parent gets resized.
  2628. GetClientRect( plv->ci.hwnd, &ti.rect );
  2629. SendMessage( plv->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM) &ti );
  2630. }
  2631. // if we're supposed to center the image,
  2632. // we need to do a full redraw on each size
  2633. if ((plv->ulBkImageFlags & LVBKIF_SOURCE_MASK) &&
  2634. (plv->xOffsetPercent || plv->yOffsetPercent)) {
  2635. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  2636. }
  2637. }
  2638. break;
  2639. case WM_NOTIFY:
  2640. return ListView_OnNotify(plv, wParam, (LPNMHDR)lParam);
  2641. case WM_MOUSEMOVE:
  2642. if (plv->hwndToolTips)
  2643. {
  2644. UINT uFlags;
  2645. int iHit, iSubHit;
  2646. RelayToToolTips(plv->hwndToolTips, hwnd, uMsg, wParam, lParam);
  2647. // check that we are still on the hit item, pop it!
  2648. iHit = _ListView_ItemHitTest( plv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &uFlags, &iSubHit );
  2649. if (iHit != plv->iTTLastHit || iSubHit != plv->iTTLastSubHit)
  2650. ListView_PopBubble(plv);
  2651. }
  2652. ListView_OnMouseMove(plv, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), (UINT) wParam);
  2653. break;
  2654. case WM_GETOBJECT:
  2655. if( lParam == OBJID_QUERYCLASSNAMEIDX )
  2656. return MSAA_CLASSNAMEIDX_LISTVIEW;
  2657. break;
  2658. default:
  2659. {
  2660. LRESULT lres;
  2661. if (CCWndProc(&plv->ci, uMsg, wParam, lParam, &lres))
  2662. return lres;
  2663. }
  2664. // Special handling of magellan mouse message
  2665. if (uMsg == g_msgMSWheel) {
  2666. BOOL fScroll;
  2667. BOOL fDataZoom;
  2668. DWORD dwStyle;
  2669. int sb;
  2670. SCROLLINFO si;
  2671. int cScrollUnitsPerLine;
  2672. int cPage;
  2673. int cLinesPerDetant;
  2674. int cDetants;
  2675. int dPos;
  2676. int iWheelDelta;
  2677. if (g_bRunOnNT || g_bRunOnMemphis)
  2678. {
  2679. iWheelDelta = (int)(short)HIWORD(wParam);
  2680. fScroll = !(wParam & (MK_SHIFT | MK_CONTROL));
  2681. fDataZoom = (BOOL) (wParam & MK_SHIFT);
  2682. }
  2683. else
  2684. {
  2685. iWheelDelta = (int)wParam;
  2686. fDataZoom = (GetKeyState(VK_SHIFT) < 0);
  2687. fScroll = !fDataZoom && GetKeyState(VK_CONTROL) >= 0;
  2688. }
  2689. // Update count of scroll amount
  2690. gcWheelDelta -= iWheelDelta;
  2691. cDetants = gcWheelDelta / WHEEL_DELTA;
  2692. if (cDetants != 0) {
  2693. gcWheelDelta %= WHEEL_DELTA;
  2694. }
  2695. if (fScroll) {
  2696. if (g_ucScrollLines > 0 &&
  2697. cDetants != 0 &&
  2698. ((WS_VSCROLL | WS_HSCROLL) & (dwStyle = ListView_GetWindowStyle(plv)))) {
  2699. sb = (dwStyle & WS_VSCROLL) ? SB_VERT : SB_HORZ;
  2700. // Get the scroll amount of one line
  2701. cScrollUnitsPerLine = _ListView_GetScrollUnitsPerLine(plv, sb);
  2702. ASSERT(cScrollUnitsPerLine > 0);
  2703. si.cbSize = sizeof(SCROLLINFO);
  2704. si.fMask = SIF_PAGE | SIF_POS;
  2705. if (!ListView_GetScrollInfo(plv, sb, &si))
  2706. return 1;
  2707. // The size of a page is at least one line, and
  2708. // leaves one line of overlap
  2709. cPage = (max(cScrollUnitsPerLine, (int)si.nPage - cScrollUnitsPerLine)) / cScrollUnitsPerLine;
  2710. // Don't scroll more than one page per detant
  2711. cLinesPerDetant = (int) min((ULONG) cPage, (ULONG) g_ucScrollLines);
  2712. dPos = cLinesPerDetant * cDetants * cScrollUnitsPerLine;
  2713. ListView_DismissEdit(plv, FALSE);
  2714. ListView_ComOnScroll(
  2715. plv, SB_THUMBTRACK, si.nPos + dPos, sb, cScrollUnitsPerLine, - 1);
  2716. ListView_UpdateScrollBars(plv);
  2717. // After scrolling, the tooltip might need to change
  2718. // so send the tooltip a fake mousemove message to force
  2719. // a recompute. We use WM_NCMOUSEMOVE since our lParam
  2720. // is in screen coordinates, not client coordinates.
  2721. ListView_PopBubble(plv);
  2722. RelayToToolTips(plv->hwndToolTips, plv->ci.hwnd,
  2723. WM_NCMOUSEMOVE, HTCLIENT, lParam);
  2724. }
  2725. return 1;
  2726. } else if (fDataZoom) {
  2727. LV_HITTESTINFO ht;
  2728. ht.pt.x = GET_X_LPARAM(lParam);
  2729. ht.pt.y = GET_Y_LPARAM(lParam);
  2730. ScreenToClient(hwnd, &(ht.pt));
  2731. // If we are rolling forward and we hit an item then navigate
  2732. // into that item (simulate dblclk which will open it). Otherwise
  2733. // just fall through so it isn't handled. In that case if we
  2734. // are being hosted in explorer it will do a backwards
  2735. // history navigation.
  2736. if ((iWheelDelta > 0) && (ListView_OnSubItemHitTest(plv, &ht) >= 0) &&
  2737. (ht.flags & LVHT_ONITEM) && cDetants != 0) {
  2738. BYTE aKeyState[256];
  2739. // This is a bit yucky but when ListView_HandleMouse sends the
  2740. // notification to the listview owner we need to make sure that
  2741. // it doesn't think the shift key is down. Otherwise it may
  2742. // perform some "alternate" action but in this case we always
  2743. // want it to perform the default open action.
  2744. //
  2745. // Strip the high bit of VK_SHIFT so that the shift key is
  2746. // not down.
  2747. GetKeyboardState(aKeyState);
  2748. aKeyState[VK_SHIFT] &= 0x7f;
  2749. SetKeyboardState(aKeyState);
  2750. ListView_HandleMouse(plv, FALSE, ht.pt.x, ht.pt.y, 0, TRUE);
  2751. ListView_HandleMouse(plv, TRUE, ht.pt.x, ht.pt.y, 0, TRUE);
  2752. return 1;
  2753. }
  2754. // else fall through
  2755. }
  2756. }
  2757. break;
  2758. }
  2759. DoDefault:
  2760. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  2761. }
  2762. void NEAR ListView_OnWinIniChange(LV* plv, WPARAM wParam, LPARAM lParam)
  2763. {
  2764. // BUGBUG: will this also catch sysparametersinfo?
  2765. // we need a general way of handling this, not
  2766. // just relying on the listview.
  2767. InitGlobalMetrics(wParam);
  2768. switch (wParam) {
  2769. case 0:
  2770. case SPI_SETNONCLIENTMETRICS:
  2771. case SPI_SETICONTITLELOGFONT:
  2772. case SPI_SETICONMETRICS:
  2773. // If wParam is 0, only reload settings if lParam is 0 too. This catches the wild-card scenario
  2774. // (like the old plus tab which does WM_WININICHANGE, 0, 0) but allows us to ignore wParam = 0
  2775. // and lParam = lpszSectionName. Reduces unecessary flashing.
  2776. if (wParam || !lParam)
  2777. {
  2778. if (!(plv->flags & LVF_ICONSPACESET))
  2779. ListView_OnSetIconSpacing(plv, (LPARAM)-1);
  2780. if (plv->flags & LVF_FONTCREATED)
  2781. ListView_OnSetFont(plv, NULL, TRUE);
  2782. // Force a recalc of all the icon regions by stripping and
  2783. // then adding back the LVS_EX_REGIONAL bit.
  2784. if (plv->exStyle & LVS_EX_REGIONAL) {
  2785. ListView_ExtendedStyleChange(plv, 0, LVS_EX_REGIONAL);
  2786. ListView_ExtendedStyleChange(plv, LVS_EX_REGIONAL, LVS_EX_REGIONAL);
  2787. }
  2788. }
  2789. break;
  2790. default:
  2791. break;
  2792. }
  2793. // If we are in an Iconic view and the user is in autoarrange mode,
  2794. // then we need to arrange the items.
  2795. //
  2796. if ((ListView_IsOwnerData( plv ) || (plv->ci.style & LVS_AUTOARRANGE)) &&
  2797. (ListView_IsSmallView(plv) || ListView_IsIconView(plv)))
  2798. {
  2799. // Call off to the arrange function.
  2800. ListView_OnArrange(plv, LVA_DEFAULT);
  2801. }
  2802. }
  2803. BOOL NEAR ListView_OnCreate(LV* plv, CREATESTRUCT FAR* lpCreateStruct)
  2804. {
  2805. CCCreateWindow();
  2806. #ifdef WINNT
  2807. InitDitherBrush();
  2808. #endif
  2809. CIInitialize(&plv->ci, plv->ci.hwnd, lpCreateStruct);
  2810. #ifdef DEBUG
  2811. if (GetAsyncKeyState(VK_SHIFT) < 0 &&
  2812. GetAsyncKeyState(VK_CONTROL) < 0) {
  2813. //plv->exStyle |= LVS_EX_SUBITEMIMAGES;
  2814. plv->exStyle |= LVS_EX_FULLROWSELECT;
  2815. plv->ci.style |= LVS_SHOWSELALWAYS;
  2816. SetWindowLong(plv->ci.hwnd, GWL_STYLE, plv->ci.style);
  2817. }
  2818. #endif
  2819. plv->dwExStyle = lpCreateStruct->dwExStyle;
  2820. if (plv->ci.style & WS_VISIBLE)
  2821. plv->flags |= LVF_VISIBLE;
  2822. ListView_GetRegIASetting(&g_bUseDblClickTimer);
  2823. if (ListView_IsOwnerData(plv))
  2824. {
  2825. // ownerdata initialization
  2826. plv->plvrangeSel = LVRange_Create();
  2827. if (NULL == plv->plvrangeSel)
  2828. goto error0;
  2829. plv->plvrangeCut = LVRange_Create();
  2830. if (NULL == plv->plvrangeCut)
  2831. goto error0;
  2832. }
  2833. else
  2834. {
  2835. ASSERT(plv->plvrangeSel == NULL);
  2836. ASSERT(plv->plvrangeCut == NULL);
  2837. plv->hdpa = DPA_CreateEx(LV_HDPA_GROW, plv->hheap);
  2838. if (!plv->hdpa)
  2839. goto error0;
  2840. plv->hdpaZOrder = DPA_CreateEx(LV_HDPA_GROW, plv->hheap);
  2841. if (!plv->hdpaZOrder)
  2842. goto error1;
  2843. }
  2844. ASSERT(plv->nWorkAreas == 0);
  2845. ASSERT(plv->prcWorkAreas == NULL);
  2846. plv->iNoHover = -1;
  2847. plv->dwHoverTime = HOVER_DEFAULT;
  2848. plv->iHot = -1;
  2849. plv->iEdit = -1;
  2850. plv->iFocus = -1;
  2851. plv->iDrag = -1;
  2852. plv->iTTLastHit = -1;
  2853. plv->iFreeSlot = -1;
  2854. plv->rcView.left = RECOMPUTE;
  2855. ASSERT(plv->iMSAAMin == plv->iMSAAMax);
  2856. plv->sizeClient.cx = lpCreateStruct->cx;
  2857. plv->sizeClient.cy = lpCreateStruct->cy;
  2858. // Setup flag to say if positions are in small or large view
  2859. if (ListView_IsSmallView(plv))
  2860. plv->flags |= LVF_ICONPOSSML;
  2861. // force calculation of listview metrics
  2862. ListView_OnSetFont(plv, NULL, FALSE);
  2863. plv->cxItem = 16 * plv->cxLabelChar + plv->cxSmIcon;
  2864. // if we're in ownerdraw report mode, the size got saved to cyItemSave
  2865. // at creation time, both need to have this
  2866. if ((plv->ci.style & LVS_OWNERDRAWFIXED) && ListView_IsReportView(plv))
  2867. plv->cyItem = plv->cyItemSave;
  2868. else
  2869. plv->cyItemSave = plv->cyItem;
  2870. ListView_OnSetIconSpacing(plv, (LPARAM)-1);
  2871. ListView_UpdateScrollBars(plv); // sets plv->cItemCol
  2872. plv->clrBk = CLR_NONE;
  2873. plv->clrText = CLR_DEFAULT;
  2874. plv->clrTextBk = CLR_DEFAULT;
  2875. plv->clrHotlight = CLR_DEFAULT;
  2876. // create the bk brush, and set the imagelists colors if needed
  2877. ListView_OnSetBkColor(plv, g_clrWindow);
  2878. // Initialize report view fields
  2879. plv->xTotalColumnWidth = RECOMPUTE;
  2880. if (ListView_IsReportView(plv))
  2881. ListView_RInitialize(plv, FALSE);
  2882. if (plv->ci.style & WS_DISABLED) {
  2883. plv->ci.style &= ~WS_DISABLED;
  2884. ListView_EnableWindow(plv, FALSE);
  2885. }
  2886. // tooltip for unfolding name lables
  2887. plv->hwndToolTips = CreateWindow(TOOLTIPS_CLASS, NULL,
  2888. WS_POPUP|TTS_NOPREFIX, 0, 0, 0, 0,
  2889. NULL, NULL, g_hinst, NULL);
  2890. if ( plv->hwndToolTips )
  2891. {
  2892. TOOLINFO ti;
  2893. ti.cbSize = sizeof(ti);
  2894. ti.uFlags = TTF_TRANSPARENT;
  2895. ti.hwnd = plv->ci.hwnd;
  2896. ti.uId = 0;
  2897. ti.hinst = NULL;
  2898. ti.lpszText = LPSTR_TEXTCALLBACK;
  2899. GetClientRect( plv->ci.hwnd, &ti.rect );
  2900. SendMessage( plv->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM) &ti );
  2901. /* Ensure that the tooltips use the same font as the view */
  2902. FORWARD_WM_SETFONT(plv->hwndToolTips, plv->hfontLabel, FALSE, SendMessage);
  2903. }
  2904. ASSERT(plv->hwndToolTips);
  2905. ASSERT(FALSE == plv->fOneClickOK);
  2906. SetTimer(plv->ci.hwnd, IDT_ONECLICKOK, GetDoubleClickTime(), NULL);
  2907. return TRUE;
  2908. error1:
  2909. DPA_Destroy(plv->hdpa);
  2910. error0:
  2911. if ( plv->plvrangeSel )
  2912. plv->plvrangeSel->lpVtbl->Release( plv->plvrangeSel );
  2913. if ( plv->plvrangeCut)
  2914. plv->plvrangeCut->lpVtbl->Release( plv->plvrangeCut );
  2915. return FALSE;
  2916. }
  2917. void NEAR PASCAL ListView_DeleteHrgnInval(LV* plv)
  2918. {
  2919. if (plv->hrgnInval && plv->hrgnInval != (HRGN)ENTIRE_REGION)
  2920. DeleteObject(plv->hrgnInval);
  2921. plv->hrgnInval = NULL;
  2922. }
  2923. void NEAR ListView_OnDestroy(LV* plv)
  2924. {
  2925. //
  2926. // The tooltip window may or may not exist at this point. It
  2927. // depends if the owning window of the tips is also being destroy.
  2928. // If so, then the tips are gone already.
  2929. //
  2930. if (IsWindow(plv->hwndToolTips))
  2931. DestroyWindow(plv->hwndToolTips);
  2932. if (plv->hCurHot)
  2933. DestroyCursor(plv->hCurHot);
  2934. plv->hwndToolTips = NULL;
  2935. Str_Set(&plv->pszTip, NULL);
  2936. Str_Set(&plv->pszEmptyText, NULL);
  2937. #ifdef WINNT
  2938. TerminateDitherBrush();
  2939. #endif
  2940. if (!ListView_IsOwnerData(plv)) {
  2941. // Make sure to notify the app
  2942. ListView_OnDeleteAllItems(plv);
  2943. }
  2944. if ((plv->flags & LVF_FONTCREATED) && plv->hfontLabel) {
  2945. DeleteObject(plv->hfontLabel);
  2946. // plv->flags &= ~LVF_FONTCREATED;
  2947. // plv->hwfontLabel = NULL;
  2948. }
  2949. if (plv->hFontHot)
  2950. DeleteObject(plv->hFontHot);
  2951. ListView_DeleteHrgnInval(plv);
  2952. if (plv->prcWorkAreas)
  2953. {
  2954. // This assert is bogus: If the app created work areas then deleted
  2955. // them, nWorkAreas will be 0 but prcWorkAreas will be non-NULL.
  2956. // ASSERT(plv->nWorkAreas > 0);
  2957. LocalFree(plv->prcWorkAreas);
  2958. }
  2959. }
  2960. void NEAR ListView_OnNCDestroy(LV* plv)
  2961. {
  2962. CCDestroyWindow();
  2963. if ((!(plv->ci.style & LVS_SHAREIMAGELISTS)) || ListView_CheckBoxes(plv)) {
  2964. if (plv->himlState &&
  2965. (plv->himlState != plv->himl) &&
  2966. (plv->himlState != plv->himlSmall))
  2967. {
  2968. ImageList_Destroy(plv->himlState);
  2969. }
  2970. }
  2971. if (!(plv->ci.style & LVS_SHAREIMAGELISTS))
  2972. {
  2973. if (plv->himl)
  2974. ImageList_Destroy(plv->himl);
  2975. if (plv->himlSmall)
  2976. ImageList_Destroy(plv->himlSmall);
  2977. }
  2978. if (ListView_IsOwnerData(plv)) {
  2979. plv->plvrangeSel->lpVtbl->Release( plv->plvrangeSel );
  2980. plv->plvrangeCut->lpVtbl->Release( plv->plvrangeCut );
  2981. plv->cTotalItems = 0;
  2982. }
  2983. ListView_ReleaseBkImage(plv);
  2984. if (plv->hbrBk)
  2985. DeleteBrush(plv->hbrBk);
  2986. if (plv->hdpa)
  2987. DPA_Destroy(plv->hdpa);
  2988. if (plv->hdpaZOrder)
  2989. DPA_Destroy(plv->hdpaZOrder);
  2990. ListView_RDestroy(plv);
  2991. IncrementSearchFree(&plv->is);
  2992. ListView_SetPtr(plv->ci.hwnd, NULL);
  2993. NearFree(plv);
  2994. }
  2995. // sets the background color for the listview
  2996. //
  2997. // this creats the brush for drawing the background as well
  2998. // as sets the imagelists background color if needed
  2999. BOOL NEAR ListView_OnSetBkColor(LV* plv, COLORREF clrBk)
  3000. {
  3001. if (plv->clrBk != clrBk)
  3002. {
  3003. if (plv->hbrBk)
  3004. {
  3005. DeleteBrush(plv->hbrBk);
  3006. plv->hbrBk = NULL;
  3007. }
  3008. if (clrBk != CLR_NONE)
  3009. {
  3010. plv->hbrBk = CreateSolidBrush(clrBk);
  3011. if (!plv->hbrBk)
  3012. return FALSE;
  3013. }
  3014. // don't mess with the imagelist color if things are shared
  3015. if (!(plv->ci.style & LVS_SHAREIMAGELISTS)) {
  3016. if (plv->himl)
  3017. ImageList_SetBkColor(plv->himl, clrBk);
  3018. if (plv->himlSmall)
  3019. ImageList_SetBkColor(plv->himlSmall, clrBk);
  3020. if (plv->himlState)
  3021. ImageList_SetBkColor(plv->himlState, clrBk);
  3022. }
  3023. plv->clrBk = clrBk;
  3024. }
  3025. return TRUE;
  3026. }
  3027. void PASCAL InitBrushOrg(LV* plv, HDC hdc)
  3028. {
  3029. int x;
  3030. if (ListView_IsSmallView(plv) || ListView_IsIconView(plv)) {
  3031. x = plv->ptOrigin.x;
  3032. } else if (ListView_IsListView(plv)) {
  3033. x = plv->xOrigin;
  3034. } else {
  3035. x = (int)plv->ptlRptOrigin.x;
  3036. }
  3037. SetBrushOrgEx(hdc, -x, 0, NULL);
  3038. }
  3039. void NEAR PASCAL ListView_InvalidateRegion(LV* plv, HRGN hrgn)
  3040. {
  3041. if (hrgn) {
  3042. if (plv->hrgnInval == NULL) {
  3043. plv->hrgnInval = hrgn;
  3044. } else {
  3045. // union it in if the entire region isn't marked for invalidate
  3046. if (plv->hrgnInval != (HRGN)ENTIRE_REGION) {
  3047. UnionRgn(plv->hrgnInval, plv->hrgnInval, hrgn);
  3048. }
  3049. DeleteObject(hrgn);
  3050. }
  3051. }
  3052. }
  3053. // ----------------------------------------------------------------------------
  3054. //
  3055. // LVSeeThruScroll
  3056. //
  3057. // Used when a watermark is the listview's background (detected via clrTextBk
  3058. // being CLR_NONE) to perform a flicker-free scroll of the client area, using
  3059. // an offscreen bitmap
  3060. //
  3061. // potential perf issue -- caching DC and/or bitmap instead of create/destroy
  3062. // on each call
  3063. //
  3064. // jeffbog 2/29/96
  3065. //
  3066. // ----------------------------------------------------------------------------
  3067. void LVSeeThruScroll(LV *plv, LPRECT lprcUpdate)
  3068. {
  3069. HDC hdcOff;
  3070. HBITMAP hbmpOff;
  3071. int x,y,cx,cy;
  3072. HDC hdc = GetDC(plv->ci.hwnd);
  3073. if (!lprcUpdate)
  3074. {
  3075. x = y = 0;
  3076. cx = plv->sizeClient.cx;
  3077. cy = plv->sizeClient.cy;
  3078. }
  3079. else
  3080. {
  3081. x = lprcUpdate->left;
  3082. y = lprcUpdate->top;
  3083. cx = lprcUpdate->right - x;
  3084. cy = lprcUpdate->bottom - y;
  3085. }
  3086. hdcOff = CreateCompatibleDC(hdc);
  3087. hbmpOff = CreateCompatibleBitmap(hdc, plv->sizeClient.cx, plv->sizeClient.cy);
  3088. SelectObject(hdcOff, hbmpOff);
  3089. SendMessage(plv->ci.hwnd, WM_PRINT, (WPARAM)hdcOff, PRF_CLIENT | PRF_ERASEBKGND);
  3090. BitBlt(hdc, x, y, cx, cy, hdcOff, x, y, SRCCOPY);
  3091. ReleaseDC(plv->ci.hwnd, hdc);
  3092. DeleteDC(hdcOff);
  3093. DeleteObject(hbmpOff);
  3094. }
  3095. void NEAR ListView_OnPaint(LV* plv, HDC hdc)
  3096. {
  3097. PAINTSTRUCT ps;
  3098. RECT rcUpdate;
  3099. // Before handling WM_PAINT, go ensure everything's recomputed...
  3100. //
  3101. if (plv->rcView.left == RECOMPUTE)
  3102. ListView_Recompute(plv);
  3103. // If we're in report view, update the header window: it looks
  3104. // better this way...
  3105. //
  3106. if (ListView_IsReportView(plv) && plv->hwndHdr)
  3107. UpdateWindow(plv->hwndHdr);
  3108. // If nothing to do (i.e., we recieved a WM_PAINT because
  3109. // of an RDW_INTERNALPAINT, and we didn't invalidate anything)
  3110. // don't bother with the Begin/EndPaint.
  3111. //
  3112. if (hdc || GetUpdateRect(plv->ci.hwnd, &rcUpdate, FALSE))
  3113. {
  3114. if (!(plv->flags & LVF_VISIBLE)) {
  3115. plv->flags |= LVF_VISIBLE;
  3116. // We may try to resize the column
  3117. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  3118. ListView_UpdateScrollBars(plv);
  3119. }
  3120. // this needs to be done before the beginpaint because it clears
  3121. // out the update region
  3122. if (!(plv->flags & LVF_REDRAW)) {
  3123. // add this region to our local invalidate region
  3124. HRGN hrgn = CreateRectRgn(0, 0, 0,0);
  3125. if (hrgn) {
  3126. // ok if GetUpdateRgn fails... then hrgn will still be
  3127. // and empty region..
  3128. GetUpdateRgn(plv->ci.hwnd, hrgn, FALSE);
  3129. ListView_InvalidateRegion(plv, hrgn);
  3130. }
  3131. }
  3132. if (hdc)
  3133. {
  3134. InitBrushOrg(plv, hdc);
  3135. SetRect(&ps.rcPaint, 0, 0, plv->sizeClient.cx, plv->sizeClient.cy);
  3136. if (ListView_RedrawEnabled(plv))
  3137. ListView_Redraw(plv, hdc, &ps.rcPaint);
  3138. }
  3139. else
  3140. {
  3141. hdc = BeginPaint(plv->ci.hwnd, &ps);
  3142. InitBrushOrg(plv, hdc);
  3143. if (ListView_RedrawEnabled(plv))
  3144. ListView_Redraw(plv, hdc, &ps.rcPaint);
  3145. EndPaint(plv->ci.hwnd, &ps);
  3146. }
  3147. }
  3148. }
  3149. void ListView_DrawSimpleBackground(LV *plv, HDC hdc, RECT *prcClip)
  3150. {
  3151. if (plv->clrBk != CLR_NONE)
  3152. {
  3153. //
  3154. // We just have a simple background color.
  3155. //
  3156. FillRect(hdc, prcClip, plv->hbrBk);
  3157. }
  3158. else
  3159. {
  3160. //
  3161. // Parent HWND draws the background for us.
  3162. //
  3163. SendMessage(plv->ci.hwndParent, WM_ERASEBKGND, (WPARAM)hdc, 0);
  3164. }
  3165. }
  3166. void ListView_DrawBackground(LV *plv, HDC hdc, RECT *prcClip)
  3167. {
  3168. HRGN hrgnClipSave;
  3169. RECT rcClip;
  3170. // Optimize the common/simple case
  3171. if (!(plv->pImgCtx && plv->fImgCtxComplete))
  3172. {
  3173. ListView_DrawSimpleBackground(plv, hdc, prcClip);
  3174. return;
  3175. }
  3176. //
  3177. // Save the old clipping region,
  3178. // since we whack on it a lot.
  3179. //
  3180. hrgnClipSave = CreateRectRgnIndirect(prcClip);
  3181. if (hrgnClipSave)
  3182. {
  3183. if (GetClipRgn(hdc, hrgnClipSave) <= 0)
  3184. {
  3185. DeleteObject(hrgnClipSave);
  3186. hrgnClipSave = NULL;
  3187. }
  3188. }
  3189. //
  3190. // Clip the clipping region to the caller's rectangle,
  3191. // and save the final clipping rectangle in rcClip.
  3192. //
  3193. if (prcClip != NULL)
  3194. {
  3195. IntersectClipRect(hdc, prcClip->left, prcClip->top,
  3196. prcClip->right, prcClip->bottom);
  3197. }
  3198. GetClipBox(hdc, &rcClip);
  3199. //
  3200. // If we have an image to draw, go draw it and
  3201. // exclue it from the clipping region.
  3202. //
  3203. if (plv->pImgCtx && plv->fImgCtxComplete)
  3204. {
  3205. RECT rcImage, rcClient;
  3206. ULONG ulState;
  3207. SIZE sizeImg;
  3208. POINT ptBackOrg;
  3209. //
  3210. // Compute ptBackOrg (aka scrolling offset), based on view style.
  3211. //
  3212. switch (plv->ci.style & LVS_TYPEMASK)
  3213. {
  3214. case LVS_LIST:
  3215. ptBackOrg.x = -plv->xOrigin;
  3216. ptBackOrg.y = 0;
  3217. break;
  3218. case LVS_REPORT:
  3219. ptBackOrg.x = -plv->ptlRptOrigin.x;
  3220. ptBackOrg.y = -plv->ptlRptOrigin.y + plv->yTop;
  3221. break;
  3222. default:
  3223. ptBackOrg.x = -plv->ptOrigin.x;
  3224. ptBackOrg.y = -plv->ptOrigin.y;
  3225. break;
  3226. }
  3227. ListView_Realize(plv, hdc, TRUE, FALSE);
  3228. switch (plv->ulBkImageFlags & LVBKIF_STYLE_MASK)
  3229. {
  3230. case LVBKIF_STYLE_TILE:
  3231. IImgCtx_Tile(plv->pImgCtx, hdc, &ptBackOrg, prcClip, NULL);
  3232. ExcludeClipRect(hdc, prcClip->left, prcClip->top,
  3233. prcClip->right, prcClip->bottom);
  3234. break;
  3235. #if 0
  3236. case LVBKIF_STYLE_STRETCH:
  3237. //
  3238. // Stretch the image to the extents of the client & view areas.
  3239. //
  3240. GetClientRect(plv->ci.hwnd, &rcImage);
  3241. UnionRect(&rcImage, &rcImage, &plv->rcView);
  3242. //
  3243. // Adjust for ptBackOrg (scrolling offset).
  3244. //
  3245. rcImage.left += ptBackOrg.x;
  3246. rcImage.top += ptBackOrg.y;
  3247. rcImage.right += ptBackOrg.x;
  3248. rcImage.bottom += ptBackOrg.y;
  3249. //
  3250. // Draw the image, if necessary.
  3251. //
  3252. if (RectVisible(hdc, &rcImage))
  3253. {
  3254. IImgCtx_Draw(plv->pImgCtx, hdc, &rcImage);
  3255. ExcludeClipRect(hdc, prcClip->left, prcClip->top,
  3256. prcClip->right, prcClip->bottom);
  3257. }
  3258. break;
  3259. #endif
  3260. case LVBKIF_STYLE_NORMAL:
  3261. //
  3262. // Start with the base image.
  3263. //
  3264. IImgCtx_GetStateInfo(plv->pImgCtx, &ulState, &sizeImg, FALSE);
  3265. rcImage.left = 0;
  3266. rcImage.top = 0;
  3267. rcImage.right = sizeImg.cx;
  3268. rcImage.bottom = sizeImg.cy;
  3269. //
  3270. // Adjust for caller offsets.
  3271. //
  3272. GetClientRect(plv->ci.hwnd, &rcClient);
  3273. if (plv->xOffsetPercent)
  3274. {
  3275. LONG dx = plv->xOffsetPercent * (rcClient.right - sizeImg.cx) / 100;
  3276. rcImage.left += dx;
  3277. rcImage.right += dx;
  3278. }
  3279. if (plv->yOffsetPercent)
  3280. {
  3281. LONG dy = plv->yOffsetPercent * (rcClient.bottom - sizeImg.cy) / 100;
  3282. rcImage.top += dy;
  3283. rcImage.bottom += dy;
  3284. }
  3285. //
  3286. // Adjust for ptBackOrg (scrolling offset).
  3287. //
  3288. rcImage.left += ptBackOrg.x;
  3289. rcImage.top += ptBackOrg.y;
  3290. rcImage.right += ptBackOrg.x;
  3291. rcImage.bottom += ptBackOrg.y;
  3292. //
  3293. // Draw the image, if necessary.
  3294. //
  3295. if (RectVisible(hdc, &rcImage))
  3296. {
  3297. IImgCtx_Draw(plv->pImgCtx, hdc, &rcImage);
  3298. ExcludeClipRect(hdc, rcImage.left, rcImage.top,
  3299. rcImage.right, rcImage.bottom);
  3300. }
  3301. break;
  3302. }
  3303. }
  3304. //
  3305. // Now draw the rest of the background.
  3306. //
  3307. if (RectVisible(hdc, prcClip))
  3308. {
  3309. ListView_DrawSimpleBackground(plv, hdc, prcClip);
  3310. }
  3311. //
  3312. // Restore old clipping region.
  3313. //
  3314. SelectClipRgn(hdc, hrgnClipSave);
  3315. if (hrgnClipSave)
  3316. {
  3317. DeleteObject(hrgnClipSave);
  3318. }
  3319. }
  3320. BOOL NEAR ListView_OnEraseBkgnd(LV *plv, HDC hdc)
  3321. {
  3322. RECT rcClip;
  3323. // Regional listviews only need to erase if we're on a slow machine
  3324. if (!(plv->exStyle & LVS_EX_REGIONAL) || g_fSlowMachine) {
  3325. //
  3326. // We draw our own background, erase with it.
  3327. //
  3328. GetClipBox(hdc, &rcClip);
  3329. ListView_DrawBackground(plv, hdc, &rcClip);
  3330. }
  3331. return TRUE;
  3332. }
  3333. void NEAR ListView_OnCommand(LV* plv, int id, HWND hwndCtl, UINT codeNotify)
  3334. {
  3335. if (hwndCtl == plv->hwndEdit)
  3336. {
  3337. switch (codeNotify)
  3338. {
  3339. case EN_UPDATE:
  3340. #if defined(FE_IME) || !defined(WINNT)
  3341. // We don't want flicker during replacing current selection
  3342. // as we use selection for IME composition.
  3343. //
  3344. if ((g_fDBCSInputEnabled) && (plv->flags & LVF_INSERTINGCOMP))
  3345. break;
  3346. #endif
  3347. // We will use the ID of the window as a Dirty flag...
  3348. if (IsWindowVisible(plv->hwndEdit)) {
  3349. SetWindowID(plv->hwndEdit, 1);
  3350. ListView_SetEditSize(plv);
  3351. }
  3352. break;
  3353. case EN_KILLFOCUS:
  3354. // We lost focus, so dismiss edit and save changes
  3355. // (Note that the owner might reject the change and restart
  3356. // edit mode, which traps the user. Owners need to give the
  3357. // user a way to get out.)
  3358. //
  3359. #if 0 // BUGBUG raymondc v6.0
  3360. //
  3361. // Fix horrible undocumented hanging problem: LVN_ENDLABELEDIT
  3362. // is sent in response to EN_KILLFOCUS, which is send in response
  3363. // to WM_KILLFOCUS, and it is undocumented that you cannot display
  3364. // UI during WM_KILLFOCUS when a journal record hook is active,
  3365. // because the presence of a hook forces serialization of activation,
  3366. // and so when you put up UI, you generate activation changes, which
  3367. // get stuck because you haven't finished responding to the previous
  3368. // WM_KILLFOCUS message yet.
  3369. //
  3370. // See NT bug 414634.
  3371. //
  3372. if (InSendMessage())
  3373. ReplyMessage(0);
  3374. #endif
  3375. if (!ListView_DismissEdit(plv, FALSE))
  3376. return;
  3377. break;
  3378. case HN_BEGINDIALOG: // pen windows is bringing up a dialog
  3379. ASSERT(GetSystemMetrics(SM_PENWINDOWS)); // only on a pen system
  3380. plv->fNoDismissEdit = TRUE;
  3381. break;
  3382. case HN_ENDDIALOG: // pen windows has destroyed dialog
  3383. ASSERT(GetSystemMetrics(SM_PENWINDOWS)); // only on a pen system
  3384. plv->fNoDismissEdit = FALSE;
  3385. break;
  3386. }
  3387. // Forward edit control notifications up to parent
  3388. //
  3389. if (IsWindow(hwndCtl))
  3390. FORWARD_WM_COMMAND(plv->ci.hwndParent, id, hwndCtl, codeNotify, SendMessage);
  3391. }
  3392. }
  3393. void NEAR ListView_OnWindowPosChanged(LV* plv, const WINDOWPOS FAR* lpwpos)
  3394. {
  3395. if (!lpwpos || !(lpwpos->flags & SWP_NOSIZE))
  3396. {
  3397. RECT rc;
  3398. int iOldSlots;
  3399. if (ListView_IsOwnerData(plv) &&
  3400. (ListView_IsSmallView(plv) || ListView_IsIconView(plv)))
  3401. {
  3402. iOldSlots = ListView_GetSlotCount(plv, TRUE);
  3403. }
  3404. GetClientRect(plv->ci.hwnd, &rc);
  3405. plv->sizeClient.cx = rc.right;
  3406. plv->sizeClient.cy = rc.bottom;
  3407. if ((plv->ci.style & LVS_AUTOARRANGE) &&
  3408. (ListView_IsSmallView(plv) || ListView_IsIconView(plv)))
  3409. {
  3410. // Call off to the arrange function.
  3411. ListView_OnArrange(plv, LVA_DEFAULT);
  3412. }
  3413. if (ListView_IsOwnerData(plv))
  3414. {
  3415. plv->rcView.left = RECOMPUTE;
  3416. ListView_Recompute(plv);
  3417. ListView_DismissEdit(plv, FALSE);
  3418. if (ListView_IsSmallView(plv) || ListView_IsIconView(plv))
  3419. {
  3420. // Uses the
  3421. int iNewSlots = ListView_GetSlotCount(plv, TRUE);
  3422. if ((iNewSlots != iOldSlots) && (ListView_Count(plv) > min(iNewSlots, iOldSlots)))
  3423. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  3424. }
  3425. }
  3426. ListView_RInitialize(plv, TRUE);
  3427. // Always make sure the scrollbars are updated to the new size
  3428. ListView_UpdateScrollBars(plv);
  3429. }
  3430. }
  3431. void ListView_InvalidateSelectedOrCutOwnerData(LV* plv, ILVRange *plvrangeSel)
  3432. {
  3433. UINT rdwFlags = RDW_INVALIDATE;
  3434. int cItem = ListView_Count( plv );
  3435. DWORD dwType = plv->ci.style & LVS_TYPEMASK;
  3436. int i;
  3437. RECT rcView;
  3438. ASSERT(ListView_IsOwnerData(plv));
  3439. ASSERT(plv );
  3440. GetClientRect( plv->ci.hwnd, &rcView );
  3441. if (plv->clrTextBk == CLR_NONE
  3442. || (plv->himl && (plv->clrBk != ImageList_GetBkColor(plv->himl)))) {
  3443. // always do an erase, otherwise the text background won't paint right
  3444. rdwFlags |= RDW_ERASE;
  3445. }
  3446. // calculate start of items and end of items visible on the view
  3447. //
  3448. switch (dwType)
  3449. {
  3450. case LVS_REPORT:
  3451. i = ListView_RYHitTest(plv, rcView.top);
  3452. cItem = ListView_RYHitTest(plv, rcView.bottom) + 1;
  3453. break;
  3454. case LVS_LIST:
  3455. i = ListView_LCalcViewItem(plv, rcView.left, rcView.top );
  3456. cItem = ListView_LCalcViewItem( plv, rcView.right, rcView.bottom ) + 1;
  3457. break;
  3458. default:
  3459. ListView_CalcMinMaxIndex( plv, &rcView, &i, &cItem );
  3460. break;
  3461. }
  3462. i = max( i, 0 );
  3463. cItem = min( ListView_Count( plv ), cItem );
  3464. if (cItem > i)
  3465. {
  3466. ListView_NotifyCacheHint( plv, i, cItem-1 );
  3467. }
  3468. for (; i < cItem; i++)
  3469. {
  3470. if (plvrangeSel->lpVtbl->IsSelected(plvrangeSel, i ) == S_OK)
  3471. {
  3472. ListView_InvalidateItem(plv, i, FALSE, rdwFlags);
  3473. }
  3474. }
  3475. }
  3476. void NEAR ListView_RedrawSelection(LV* plv)
  3477. {
  3478. if (ListView_IsOwnerData(plv)) {
  3479. ListView_InvalidateSelectedOrCutOwnerData( plv, plv->plvrangeSel );
  3480. } else {
  3481. int i = -1;
  3482. while ((i = ListView_OnGetNextItem(plv, i, LVNI_SELECTED)) != -1) {
  3483. ListView_InvalidateItem(plv, i, TRUE, RDW_INVALIDATE);
  3484. }
  3485. if (ListView_IsReportView(plv)) {
  3486. int iEnd = ListView_RYHitTest(plv, plv->sizeClient.cy) + 1;
  3487. iEnd = min(iEnd, ListView_Count(plv));
  3488. // if we're in report mode, sub items may have selection focus
  3489. for (i = ListView_RYHitTest(plv, 0); i < iEnd; i++) {
  3490. int iCol;
  3491. for (iCol = 1; iCol < plv->cCol; iCol++) {
  3492. LISTSUBITEM lsi;
  3493. ListView_GetSubItem(plv, i, iCol, &lsi);
  3494. if (lsi.state & LVIS_SELECTED) {
  3495. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE);
  3496. }
  3497. break;
  3498. }
  3499. }
  3500. }
  3501. }
  3502. UpdateWindow( plv->ci.hwnd );
  3503. }
  3504. void NEAR ListView_OnSetFocus(LV* plv, HWND hwndOldFocus)
  3505. {
  3506. ASSERT(gcWheelDelta == 0);
  3507. // due to the way listview call SetFocus on themselves on buttondown,
  3508. // the window can get a strange sequence of focus messages: first
  3509. // set, then kill, and then set again. since these are not really
  3510. // focus changes, ignore them and only handle "real" cases.
  3511. //
  3512. // But still send out the accessibility notification because USER
  3513. // has already pushed focus back to the listview instead of to the
  3514. // focus item.
  3515. if (hwndOldFocus == plv->ci.hwnd)
  3516. {
  3517. ListView_NotifyFocusEvent(plv);
  3518. return;
  3519. }
  3520. plv->flags |= LVF_FOCUSED | LVF_UNFOLDED;
  3521. if (IsWindowVisible(plv->ci.hwnd))
  3522. {
  3523. if (plv->iFocus != -1)
  3524. {
  3525. ListView_InvalidateItem(plv, plv->iFocus, TRUE, RDW_INVALIDATE);
  3526. ListView_NotifyFocusEvent(plv);
  3527. }
  3528. ListView_RedrawSelection(plv);
  3529. }
  3530. // Let the parent window know that we are getting the focus.
  3531. CCSendNotify(&plv->ci, NM_SETFOCUS, NULL);
  3532. }
  3533. void NEAR ListView_OnKillFocus(LV* plv, HWND hwndNewFocus)
  3534. {
  3535. // Reset wheel scroll amount
  3536. gcWheelDelta = 0;
  3537. // due to the way listview call SetFocus on themselves on buttondown,
  3538. // the window can get a strange sequence of focus messages: first
  3539. // set, then kill, and then set again. since these are not really
  3540. // focus changes, ignore them and only handle "real" cases.
  3541. if (!plv || hwndNewFocus == plv->ci.hwnd)
  3542. return;
  3543. plv->flags &= ~(LVF_FOCUSED|LVF_UNFOLDED);
  3544. // Blow this off if we are not currently visible (being destroyed!)
  3545. if (IsWindowVisible(plv->ci.hwnd))
  3546. {
  3547. if (plv->iFocus != -1)
  3548. {
  3549. UINT fRedraw = RDW_INVALIDATE;
  3550. if (plv->clrTextBk == CLR_NONE)
  3551. fRedraw |= RDW_ERASE;
  3552. ListView_InvalidateFoldedItem( plv, plv->iFocus, TRUE, fRedraw );
  3553. }
  3554. ListView_RedrawSelection(plv);
  3555. }
  3556. // Let the parent window know that we are losing the focus.
  3557. CCSendNotify(&plv->ci, NM_KILLFOCUS, NULL);
  3558. IncrementSearchString(&plv->is, 0, NULL);
  3559. }
  3560. void NEAR ListView_DeselectAll(LV* plv, int iDontDeselect)
  3561. {
  3562. int i = -1;
  3563. int nSkipped = 0;
  3564. BOOL fWasSelected = FALSE;
  3565. if (iDontDeselect != -1) {
  3566. if (ListView_OnGetItemState(plv, iDontDeselect, LVIS_SELECTED))
  3567. fWasSelected = TRUE;
  3568. }
  3569. if (ListView_IsOwnerData(plv)) {
  3570. // if there's only one item selected, and that item is the iDontDeselect
  3571. // then our work is done...
  3572. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  3573. if (plv->nSelected == 1 && fWasSelected)
  3574. return;
  3575. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeSel);
  3576. ListView_OnSetItemState(plv, -1, 0, LVIS_SELECTED);
  3577. if (fWasSelected) {
  3578. ListView_OnSetItemState(plv, iDontDeselect, LVIS_SELECTED, LVIS_SELECTED);
  3579. nSkipped = 1;
  3580. }
  3581. } else {
  3582. if (iDontDeselect != plv->iFocus) {
  3583. ListView_OnSetItemState(plv, plv->iFocus, 0, LVIS_SELECTED);
  3584. }
  3585. while ((plv->nSelected - nSkipped) && (i = ListView_OnGetNextItem(plv, i, LVNI_SELECTED)) != -1) {
  3586. if (i != iDontDeselect) {
  3587. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  3588. } else {
  3589. if (fWasSelected) {
  3590. nSkipped++;
  3591. }
  3592. }
  3593. }
  3594. }
  3595. ASSERT((plv->nSelected - nSkipped) == 0);
  3596. plv->nSelected = nSkipped;
  3597. }
  3598. // toggle the selection state of an item
  3599. void NEAR ListView_ToggleSelection(LV* plv, int iItem)
  3600. {
  3601. UINT cur_state;
  3602. if (iItem != -1) {
  3603. cur_state = ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  3604. ListView_OnSetItemState(plv, iItem, cur_state ^ LVIS_SELECTED, LVIS_SELECTED);
  3605. }
  3606. }
  3607. // Selects (or toggles) a range of items in the list.
  3608. // The curent iFocus is the starting location
  3609. // iItem - is the ending item
  3610. // fToggle - Well set all of the selection state of all of the items to
  3611. // inverse the starting location
  3612. //
  3613. void NEAR ListView_SelectRangeTo(LV* plv, int iItem, BOOL fResetRest)
  3614. {
  3615. int iMin, iMax;
  3616. int i = -1;
  3617. UINT uSelVal = LVIS_SELECTED;
  3618. if (plv->iMark == -1)
  3619. {
  3620. ListView_SetFocusSel(plv, iItem, TRUE, TRUE, FALSE);
  3621. return;
  3622. }
  3623. if (!fResetRest)
  3624. uSelVal = ListView_OnGetItemState(plv, plv->iMark, LVIS_SELECTED);
  3625. // If we are in report view or list view we simply walk through the
  3626. // indexes to see which items to select or deselect. otherwise it
  3627. // is is based off of the location of the objects being within the
  3628. // rectangle that is defined by
  3629. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  3630. {
  3631. iMin = min(iItem, plv->iMark);
  3632. iMax = max(iItem, plv->iMark);
  3633. if (ListView_IsOwnerData( plv )) {
  3634. if (fResetRest)
  3635. {
  3636. ListView_DeselectAll( plv, -1 );
  3637. }
  3638. if (iMax > iMin)
  3639. {
  3640. if (LVIS_SELECTED & uSelVal)
  3641. {
  3642. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, iMin, iMax )))
  3643. return;
  3644. }
  3645. else
  3646. {
  3647. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, iMin, iMax )))
  3648. return;
  3649. }
  3650. ListView_SendODChangeAndInvalidate(plv, iMin, iMax, uSelVal ^ LVIS_SELECTED, uSelVal);
  3651. }
  3652. else
  3653. {
  3654. ListView_OnSetItemState(plv, iMin, uSelVal, LVIS_SELECTED);
  3655. }
  3656. }
  3657. else
  3658. {
  3659. if (fResetRest)
  3660. {
  3661. while ((i = ListView_OnGetNextItem(plv, i, LVNI_SELECTED)) != -1)
  3662. {
  3663. if (i < iMin || i > iMax)
  3664. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  3665. }
  3666. }
  3667. while (iMin <= iMax)
  3668. {
  3669. ListView_OnSetItemState(plv, iMin, uSelVal, LVIS_SELECTED);
  3670. iMin++;
  3671. }
  3672. }
  3673. }
  3674. else
  3675. {
  3676. // Iconic views first calculate the bounding rectangle of the two
  3677. // items.
  3678. RECT rcTemp;
  3679. RECT rcTemp2;
  3680. RECT rcBounding;
  3681. POINT pt; //
  3682. ListView_GetRects(plv, plv->iMark, NULL, NULL, NULL, &rcTemp);
  3683. ListView_GetRects(plv, iItem, NULL, NULL, NULL, &rcTemp2);
  3684. UnionRect(&rcBounding, &rcTemp, &rcTemp2);
  3685. // since ownerdata icon views are always arranged, we can assume that
  3686. // all items are in order and we can search only those between the
  3687. // indexes found using the bouding rect
  3688. //
  3689. if (ListView_IsOwnerData( plv ))
  3690. {
  3691. ListView_CalcMinMaxIndex( plv, &rcBounding, &iMin, &iMax );
  3692. if (fResetRest)
  3693. {
  3694. ListView_DeselectAll( plv, -1 );
  3695. }
  3696. iMax = min( iMax, ListView_Count( plv ) );
  3697. iMin = max( iMin, 0 );
  3698. }
  3699. else
  3700. {
  3701. iMax = ListView_Count(plv);
  3702. iMin = 0;
  3703. }
  3704. if (ListView_IsOwnerData(plv) && (iMax > iMin))
  3705. {
  3706. if (LVIS_SELECTED & uSelVal)
  3707. {
  3708. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, iMin, iMax - 1 )))
  3709. return;
  3710. }
  3711. else
  3712. {
  3713. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, iMin, iMax - 1 )))
  3714. return;
  3715. }
  3716. ListView_SendODChangeAndInvalidate(plv, iMin, iMax, uSelVal ^ LVIS_SELECTED, uSelVal);
  3717. } else {
  3718. for (i = iMin; i < iMax; i++)
  3719. {
  3720. ListView_GetRects(plv, i, NULL, NULL, NULL, &rcTemp2);
  3721. pt.x = (rcTemp2.right + rcTemp2.left) / 2; // center of item
  3722. pt.y = (rcTemp2.bottom + rcTemp2.top) / 2;
  3723. if (PtInRect(&rcBounding, pt))
  3724. {
  3725. int iZ;
  3726. if (!ListView_IsOwnerData( plv ))
  3727. {
  3728. iZ = ListView_ZOrderIndex(plv, i);
  3729. if (iZ > 0)
  3730. DPA_InsertPtr(plv->hdpaZOrder, 0, DPA_DeletePtr(plv->hdpaZOrder, iZ));
  3731. }
  3732. ListView_OnSetItemState(plv, i, uSelVal, LVIS_SELECTED);
  3733. }
  3734. else if (fResetRest)
  3735. ListView_OnSetItemState(plv, i, 0, LVIS_SELECTED);
  3736. }
  3737. }
  3738. }
  3739. }
  3740. // makes an item the focused item and optionally selects it
  3741. //
  3742. // in:
  3743. // iItem item to get the focus
  3744. // fSelectAlso select this item as well as set it as the focus
  3745. // fDeselectAll deselect all items first
  3746. // fToggleSel toggle the selection state of the item
  3747. //
  3748. // returns:
  3749. // index of focus item (if focus change was refused)
  3750. // Bugbug::this is getting to have a lot of parameters
  3751. int NEAR ListView_SetFocusSel(LV* plv, int iItem, BOOL fSelectAlso,
  3752. BOOL fDeselectAll, BOOL fToggleSel)
  3753. {
  3754. int iFocus = plv->iFocus;
  3755. // if we're single sel mode, don't bother with this because
  3756. // the set item will do it for us
  3757. if (!(plv->ci.style & LVS_SINGLESEL) && (fDeselectAll))
  3758. ListView_DeselectAll(plv, -1);
  3759. if (iItem != plv->iFocus)
  3760. {
  3761. // remove the old focus
  3762. if (plv->iFocus != -1)
  3763. {
  3764. // If he refuses to give up the focus, bail out.
  3765. if (!ListView_OnSetItemState(plv, plv->iFocus, 0, LVIS_FOCUSED))
  3766. return plv->iFocus;
  3767. }
  3768. }
  3769. if (!ListView_IsOwnerData( plv )) {
  3770. if (fSelectAlso)
  3771. {
  3772. if (ListView_IsIconView(plv) || ListView_IsSmallView(plv))
  3773. {
  3774. int iZ = ListView_ZOrderIndex(plv, iItem);
  3775. if (iZ > 0)
  3776. DPA_InsertPtr(plv->hdpaZOrder, 0, DPA_DeletePtr(plv->hdpaZOrder, iZ));
  3777. }
  3778. }
  3779. }
  3780. /* Ensure that when moving focus that we refresh the previous focus
  3781. owner properly. */
  3782. if (iFocus != -1 && iFocus != plv->iFocus && ( plv->flags & LVF_UNFOLDED ) )
  3783. ListView_InvalidateFoldedItem( plv, iFocus, FALSE, RDW_INVALIDATE );
  3784. if (plv->iMark == -1)
  3785. plv->iMark = iItem;
  3786. SetTimer(plv->ci.hwnd, IDT_SCROLLWAIT, GetDoubleClickTime(), NULL);
  3787. plv->flags |= LVF_SCROLLWAIT;
  3788. if (fToggleSel)
  3789. {
  3790. ListView_ToggleSelection(plv, iItem);
  3791. ListView_OnSetItemState(plv, iItem, LVIS_FOCUSED, LVIS_FOCUSED);
  3792. }
  3793. else
  3794. {
  3795. UINT flags;
  3796. flags = ((fSelectAlso || plv->ci.style & LVS_SINGLESEL) ?
  3797. (LVIS_SELECTED | LVIS_FOCUSED) : LVIS_FOCUSED);
  3798. ListView_OnSetItemState(plv, iItem, flags, flags);
  3799. }
  3800. return iItem;
  3801. }
  3802. UINT GetLVKeyFlags()
  3803. {
  3804. UINT uFlags = 0;
  3805. if (GetKeyState(VK_MENU) < 0)
  3806. uFlags |= LVKF_ALT;
  3807. if (GetKeyState(VK_CONTROL) < 0)
  3808. uFlags |= LVKF_CONTROL;
  3809. if (GetKeyState(VK_SHIFT) < 0)
  3810. uFlags |= LVKF_SHIFT;
  3811. return uFlags;
  3812. }
  3813. void NEAR ListView_OnKey(LV* plv, UINT vk, BOOL fDown, int cRepeat, UINT flags)
  3814. {
  3815. UINT lvni = 0;
  3816. int iNewFocus;
  3817. BOOL fCtlDown;
  3818. BOOL fShiftDown;
  3819. LV_KEYDOWN nm;
  3820. HWND hwnd = plv->ci.hwnd;
  3821. if (!fDown)
  3822. return;
  3823. // Swap the left and right arrow key if the control is mirrored.
  3824. vk = RTLSwapLeftRightArrows(&plv->ci, vk);
  3825. //prevent any change in selected items before the dbl click timer goes off
  3826. //so that we don't launch wrong item(s)
  3827. if (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickHappened && plv->fOneClickOK)
  3828. {
  3829. //if a key is pressed with a mouse click with one click activate and double click
  3830. //timer, we end up setting up a timer and then processing the keydown
  3831. //this causes an item to be launched right away (from this code) and in case
  3832. //of return being pressed it causes double activation
  3833. //prevent these cases:
  3834. if (vk == VK_SHIFT || vk == VK_CONTROL || vk == VK_MENU || vk == VK_RETURN)
  3835. return;
  3836. KillTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED);
  3837. plv->fOneClickHappened = FALSE;
  3838. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  3839. if (!IsWindow(hwnd))
  3840. return;
  3841. }
  3842. // Notify
  3843. nm.wVKey = (WORD) vk;
  3844. nm.flags = flags;
  3845. if (CCSendNotify(&plv->ci, LVN_KEYDOWN, &nm.hdr)) {
  3846. plv->iPuntChar++;
  3847. return;
  3848. } else if (plv->iPuntChar) {
  3849. // this is tricky... if we want to punt the char, just increment the
  3850. // count. if we do NOT, then we must clear the queue of WM_CHAR's
  3851. // this is to preserve the iPuntChar to mean "punt the next n WM_CHAR messages
  3852. MSG msg;
  3853. while(plv->iPuntChar && PeekMessage(&msg, plv->ci.hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)) {
  3854. plv->iPuntChar--;
  3855. }
  3856. ASSERT(!plv->iPuntChar);
  3857. }
  3858. if (ListView_Count(plv) == 0) // don't blow up on empty list
  3859. return;
  3860. fCtlDown = GetKeyState(VK_CONTROL) < 0;
  3861. fShiftDown = GetKeyState(VK_SHIFT) < 0;
  3862. switch (vk)
  3863. {
  3864. case VK_SPACE:
  3865. #ifdef DEBUG
  3866. if (fCtlDown && fShiftDown) {
  3867. SendMessage(plv->ci.hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0,
  3868. (SendMessage(plv->ci.hwnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0) ^ LVS_EX_GRIDLINES) | LVS_EX_CHECKBOXES);
  3869. }
  3870. #endif
  3871. // If shift (extend) or control (disjoint) select,
  3872. // then toggle selection state of focused item.
  3873. if (fCtlDown)
  3874. {
  3875. plv->iMark = plv->iFocus;
  3876. ListView_ToggleSelection(plv, plv->iFocus);
  3877. plv->iPuntChar++;
  3878. }
  3879. // BUGBUG: Implement me
  3880. if ( fShiftDown) {
  3881. ListView_SelectRangeTo(plv, plv->iFocus, TRUE);
  3882. }
  3883. if (ListView_CheckBoxes(plv)) {
  3884. if (plv->iFocus != -1)
  3885. ListView_HandleStateIconClick(plv, plv->iFocus);
  3886. }
  3887. #ifdef KEYBOARDCUES
  3888. //notify of navigation key usage
  3889. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  3890. #endif
  3891. return;
  3892. case VK_RETURN:
  3893. #ifdef DEBUG
  3894. if (fCtlDown && fShiftDown) {
  3895. if (plv->iFocus != -1) {
  3896. LV_ITEM lvi;
  3897. lvi.iSubItem = 1;
  3898. lvi.iItem = plv->iFocus;
  3899. lvi.iImage = 3;
  3900. lvi.state = LVIS_SELECTED;
  3901. lvi.stateMask = LVIS_SELECTED;
  3902. lvi.mask = LVIF_STATE | LVIF_IMAGE;
  3903. SendMessage(plv->ci.hwnd, LVM_SETITEM, 0, (LPARAM)&lvi);
  3904. }
  3905. return;
  3906. }
  3907. #endif
  3908. CCSendNotify(&plv->ci, NM_RETURN, NULL);
  3909. /// some (comdlg32 for example) destroy on double click
  3910. // we need to bail if that happens because plv is no longer valid
  3911. if (!IsWindow(hwnd))
  3912. return;
  3913. {
  3914. NMITEMACTIVATE nm;
  3915. nm.iItem = plv->iFocus;
  3916. nm.iSubItem = 0;
  3917. nm.uChanged = 0;
  3918. nm.ptAction.x = -1;
  3919. nm.ptAction.y = -1;
  3920. nm.uKeyFlags = GetLVKeyFlags();
  3921. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &nm.hdr);
  3922. if (!IsWindow(hwnd))
  3923. return;
  3924. }
  3925. #ifdef KEYBOARDCUES
  3926. //notify of navigation key usage
  3927. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  3928. #endif
  3929. return;
  3930. case VK_ADD:
  3931. if (ListView_IsReportView(plv) && (GetKeyState(VK_CONTROL) < 0))
  3932. {
  3933. HCURSOR hcurPrev;
  3934. int i;
  3935. hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  3936. for (i=0; i < plv->cCol; i++)
  3937. {
  3938. ListView_RSetColumnWidth(plv, i, -1);
  3939. }
  3940. SetCursor(hcurPrev);
  3941. #ifdef KEYBOARDCUES
  3942. //notify of navigation key usage
  3943. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  3944. #endif
  3945. return;
  3946. }
  3947. }
  3948. if (GetKeyState(VK_MENU) < 0)
  3949. return;
  3950. // For a single selection listview, disable extending the selection
  3951. // by turning off the keyboard modifiers.
  3952. if (plv->ci.style & LVS_SINGLESEL) {
  3953. fCtlDown = FALSE;
  3954. fShiftDown = FALSE;
  3955. }
  3956. //
  3957. // Let the Arrow function attempt to process the key.
  3958. //
  3959. iNewFocus = ListView_Arrow(plv, plv->iFocus, vk);
  3960. // If control (disjoint) selection, don't change selection.
  3961. // If shift (extend) or control selection, don't deselect all.
  3962. //
  3963. if (iNewFocus != -1) {
  3964. if (fShiftDown)
  3965. {
  3966. ListView_SelectRangeTo(plv, iNewFocus, TRUE);
  3967. ListView_SetFocusSel(plv, iNewFocus, FALSE, FALSE, FALSE);
  3968. }
  3969. else {
  3970. if (!fCtlDown)
  3971. plv->iMark = iNewFocus;
  3972. ListView_SetFocusSel(plv, iNewFocus, !fCtlDown, !fShiftDown && !fCtlDown, FALSE);
  3973. }
  3974. IncrementSearchString(&plv->is, 0, NULL);
  3975. CCPlaySound(c_szSelect);
  3976. }
  3977. // on keyboard movement, scroll immediately.
  3978. if (ListView_CancelScrollWait(plv)) {
  3979. ListView_OnEnsureVisible(plv, plv->iFocus, FALSE);
  3980. UpdateWindow(plv->ci.hwnd);
  3981. }
  3982. #ifdef KEYBOARDCUES
  3983. //notify of navigation key usage
  3984. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  3985. #endif
  3986. }
  3987. //
  3988. // LVN_INCREMENTALSEARCH gives the app the opportunity to customize
  3989. // incremental search. For example, if the items are numeric,
  3990. // the app can do numerical search instead of string search.
  3991. //
  3992. // App sets pnmfi->lvfi.lParam to the result of the incremental search,
  3993. // or to -2 to fai the search and just beep.
  3994. //
  3995. // App can return 2 to indicate that all processing should stop, if
  3996. // app wants to take over incremental search completely.
  3997. //
  3998. BOOL ListView_IncrementalSearch(LV *plv, int iStartFrom, LPNMLVFINDITEM pnmfi, int *pi)
  3999. {
  4000. INT_PTR fRc;
  4001. ASSERT(!(pnmfi->lvfi.flags & LVFI_PARAM));
  4002. pnmfi->lvfi.lParam = -1;
  4003. fRc = CCSendNotify(&plv->ci, LVN_INCREMENTALSEARCH, &pnmfi->hdr);
  4004. *pi = (int)pnmfi->lvfi.lParam;
  4005. // Cannot just return fRc because some apps return 1 to all WM_NOTIFY's
  4006. return fRc == 2;
  4007. }
  4008. #if defined(FE_IME)
  4009. // Now only Korean version is interested in incremental search with composition string.
  4010. LPTSTR GET_COMP_STRING(HIMC hImc, DWORD dwFlags)
  4011. {
  4012. LONG iNumComp;
  4013. PTSTR pszCompStr;
  4014. iNumComp = ImmGetCompositionString(hImc, dwFlags, NULL, 0);
  4015. pszCompStr = (PTSTR)LocalAlloc(LPTR, sizeof(TCHAR)*(iNumComp+1));
  4016. if (pszCompStr)
  4017. {
  4018. if (iNumComp)
  4019. ImmGetCompositionString(hImc, dwFlags, pszCompStr, iNumComp+1);
  4020. pszCompStr[iNumComp] = TEXT('\0');
  4021. }
  4022. return pszCompStr;
  4023. }
  4024. #define FREE_COMP_STRING(pszCompStr) LocalFree((HLOCAL)(pszCompStr))
  4025. BOOL NEAR ListView_OnImeComposition(LV* plv, WPARAM wParam, LPARAM lParam)
  4026. {
  4027. LPTSTR lpsz;
  4028. NMLVFINDITEM nmfi;
  4029. int i;
  4030. int iStartFrom = -1;
  4031. int iLen;
  4032. int iCount;
  4033. HIMC hImc;
  4034. TCHAR *pszCompStr;
  4035. BOOL fRet = TRUE;
  4036. iCount = ListView_Count(plv);
  4037. if (!iCount || plv->iFocus == -1)
  4038. return fRet;
  4039. if (hImc = ImmGetContext(plv->ci.hwnd))
  4040. {
  4041. if (lParam & GCS_RESULTSTR)
  4042. {
  4043. fRet = FALSE;
  4044. pszCompStr = GET_COMP_STRING(hImc, GCS_RESULTSTR);
  4045. if (pszCompStr)
  4046. {
  4047. IncrementSearchImeCompStr(&plv->is, FALSE, pszCompStr, &lpsz);
  4048. FREE_COMP_STRING(pszCompStr);
  4049. }
  4050. }
  4051. if (lParam & GCS_COMPSTR)
  4052. {
  4053. fRet = TRUE;
  4054. pszCompStr = GET_COMP_STRING(hImc, GCS_COMPSTR);
  4055. if (pszCompStr)
  4056. {
  4057. if (IncrementSearchImeCompStr(&plv->is, TRUE, pszCompStr, &lpsz))
  4058. iStartFrom = plv->iFocus;
  4059. else
  4060. iStartFrom = ((plv->iFocus - 1) + iCount)% iCount;
  4061. nmfi.lvfi.flags = LVFI_SUBSTRING | LVFI_STRING | LVFI_WRAP;
  4062. nmfi.lvfi.psz = lpsz;
  4063. iLen = lstrlen(lpsz);
  4064. // special case space as the first character
  4065. if ((iLen == 1) && (*lpsz == TEXT(' '))) {
  4066. if (plv->iFocus != -1) {
  4067. ListView_OnSetItemState(plv, plv->iFocus, LVIS_SELECTED, LVIS_SELECTED);
  4068. IncrementSearchString(&plv->is, 0, NULL);
  4069. }
  4070. #ifdef KEYBOARDCUES
  4071. //notify of navigation key usage
  4072. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  4073. #endif
  4074. return fRet;
  4075. }
  4076. // Give caller full string in case they want to do something custom
  4077. if (ListView_IncrementalSearch(plv, iStartFrom, &nmfi, &i))
  4078. return fRet;
  4079. #ifdef UNICODE
  4080. if (iLen > 0 && SameChars(lpsz, lpsz[0])) {
  4081. #else
  4082. if (iLen > 1 && SameDBCSChars(lpsz, (WORD)((BYTE)lpsz[0] << 8 | (BYTE)lpsz[1]))) {
  4083. #endif
  4084. //
  4085. // The user has been typing the same char over and over again.
  4086. // Switch from incremental search to Windows 3.1 style search.
  4087. //
  4088. iStartFrom = plv->iFocus;
  4089. nmfi.lvfi.psz = lpsz + iLen - 1;
  4090. }
  4091. if (i == -1)
  4092. i = ListView_OnFindItem(plv, iStartFrom, &nmfi.lvfi);
  4093. if (!ListView_IsValidItemNumber(plv, i)) {
  4094. i = -1;
  4095. }
  4096. #ifdef LVDEBUG
  4097. DebugMsg(TF_LISTVIEW, TEXT("CIme listsearch %d %s %d"), (LPTSTR)lpsz, (LPTSTR)lpsz, i);
  4098. #endif
  4099. if (i != -1) {
  4100. ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
  4101. plv->iMark = i;
  4102. if (ListView_CancelScrollWait(plv))
  4103. ListView_OnEnsureVisible(plv, i, FALSE);
  4104. } else {
  4105. // Don't beep on spaces, we use it for selection.
  4106. IncrementSearchBeep(&plv->is);
  4107. }
  4108. #ifdef KEYBOARDCUES
  4109. //notify of navigation key usage
  4110. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  4111. #endif
  4112. FREE_COMP_STRING(pszCompStr);
  4113. }
  4114. }
  4115. ImmReleaseContext(plv->ci.hwnd, hImc);
  4116. }
  4117. return fRet;
  4118. }
  4119. #ifndef UNICODE
  4120. BOOL FAR PASCAL SameDBCSChars(LPSTR lpsz, WORD w)
  4121. {
  4122. while (*lpsz) {
  4123. if (IsDBCSLeadByte(*lpsz) == FALSE)
  4124. return FALSE;
  4125. if ((WORD)((BYTE)*lpsz++ << 8 | (BYTE)*lpsz++) != w)
  4126. return FALSE;
  4127. }
  4128. return TRUE;
  4129. }
  4130. #endif
  4131. #endif FE_IME
  4132. // REVIEW: We will want to reset ichCharBuf to 0 on certain conditions,
  4133. // such as: focus change, ENTER, arrow key, mouse click, etc.
  4134. //
  4135. void NEAR ListView_OnChar(LV* plv, UINT ch, int cRepeat)
  4136. {
  4137. LPTSTR lpsz;
  4138. NMLVFINDITEM nmfi;
  4139. int i;
  4140. int iStartFrom = -1;
  4141. int iLen;
  4142. int iCount;
  4143. iCount = ListView_Count(plv);
  4144. if (!iCount || plv->iFocus == -1)
  4145. return;
  4146. // Don't search for chars that cannot be in a file name (like ENTER and TAB)
  4147. // BUGBUG raymondc fix for v6: The Polish keyboard layout uses CTRL+ALT to
  4148. // enter some normal letters, so don't punt if the CTRL key is down or
  4149. // people in Poland are in trouble!
  4150. if (ch < TEXT(' ') || GetKeyState(VK_CONTROL) < 0)
  4151. {
  4152. IncrementSearchString(&plv->is, 0, NULL);
  4153. return;
  4154. }
  4155. #ifdef UNICODE_WIN9x
  4156. if (g_fDBCSEnabled && (IsDBCSLeadByteEx(plv->ci.uiCodePage, (BYTE)ch) || plv->uDBCSChar))
  4157. {
  4158. WCHAR wch;
  4159. if (!plv->uDBCSChar)
  4160. {
  4161. // Save DBCS LeadByte character
  4162. plv->uDBCSChar = ch & 0x00ff;
  4163. return;
  4164. }
  4165. else
  4166. {
  4167. // Combine DBCS characters
  4168. plv->uDBCSChar |= ((ch & 0x00ff) << 8);
  4169. // Convert to UNICODE
  4170. if (MultiByteToWideChar(plv->ci.uiCodePage, MB_ERR_INVALID_CHARS, (LPCSTR)&plv->uDBCSChar, 2, &wch, 1))
  4171. {
  4172. ch = wch;
  4173. }
  4174. plv->uDBCSChar = 0;
  4175. }
  4176. }
  4177. else
  4178. {
  4179. if (ch >= 0x80) // no need conversion for low ansi character
  4180. {
  4181. WCHAR wch;
  4182. if (MultiByteToWideChar(plv->ci.uiCodePage, 0, (LPCSTR)&ch, 1, &wch, 1))
  4183. ch = wch;
  4184. }
  4185. }
  4186. #endif
  4187. if (IncrementSearchString(&plv->is, ch, &lpsz))
  4188. iStartFrom = plv->iFocus;
  4189. else
  4190. iStartFrom = ((plv->iFocus - 1) + iCount)% iCount;
  4191. nmfi.lvfi.flags = LVFI_SUBSTRING | LVFI_STRING | LVFI_WRAP;
  4192. nmfi.lvfi.psz = lpsz;
  4193. iLen = lstrlen(lpsz);
  4194. // special case space as the first character
  4195. if ((iLen == 1) && (*lpsz == ' ')) {
  4196. if (plv->iFocus != -1) {
  4197. ListView_OnSetItemState(plv, plv->iFocus, LVIS_SELECTED, LVIS_SELECTED);
  4198. IncrementSearchString(&plv->is, 0, NULL);
  4199. }
  4200. #ifdef KEYBOARDCUES
  4201. //notify of navigation key usage
  4202. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  4203. #endif
  4204. return;
  4205. }
  4206. // Give caller full string in case they want to do something custom
  4207. if (ListView_IncrementalSearch(plv, iStartFrom, &nmfi, &i))
  4208. return;
  4209. if (iLen > 0 && SameChars(lpsz, lpsz[0])) {
  4210. //
  4211. // The user has been typing the same char over and over again.
  4212. // Switch from incremental search to Windows 3.1 style search.
  4213. //
  4214. iStartFrom = plv->iFocus;
  4215. nmfi.lvfi.psz = lpsz + iLen - 1;
  4216. }
  4217. if (i == -1)
  4218. i = ListView_OnFindItem(plv, iStartFrom, &nmfi.lvfi);
  4219. if (!ListView_IsValidItemNumber(plv, i)) {
  4220. i = -1;
  4221. }
  4222. #ifdef LVDEBUG
  4223. DebugMsg(TF_LISTVIEW, TEXT("listsearch %d %s %d"), (LPTSTR)lpsz, (LPTSTR)lpsz, i);
  4224. #endif
  4225. if (i != -1) {
  4226. ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
  4227. plv->iMark = i;
  4228. if (ListView_CancelScrollWait(plv))
  4229. ListView_OnEnsureVisible(plv, i, FALSE);
  4230. } else {
  4231. // Don't beep on spaces, we use it for selection.
  4232. IncrementSearchBeep(&plv->is);
  4233. }
  4234. #ifdef KEYBOARDCUES
  4235. //notify of navigation key usage
  4236. CCNotifyNavigationKeyUsage(&(plv->ci), UISF_HIDEFOCUS);
  4237. #endif
  4238. }
  4239. BOOL FAR PASCAL SameChars(LPTSTR lpsz, TCHAR c)
  4240. {
  4241. while (*lpsz) {
  4242. if (*lpsz++ != c)
  4243. return FALSE;
  4244. }
  4245. return TRUE;
  4246. }
  4247. UINT NEAR ListView_OnGetDlgCode(LV* plv, MSG FAR* lpmsg)
  4248. {
  4249. return DLGC_WANTARROWS | DLGC_WANTCHARS;
  4250. }
  4251. int ListView_ComputeCYItemSize(LV* plv)
  4252. {
  4253. int cy;
  4254. cy = max(plv->cyLabelChar, plv->cySmIcon);
  4255. if (plv->himlState)
  4256. cy = max(cy, plv->cyState);
  4257. cy += g_cyBorder;
  4258. ASSERT(cy);
  4259. return cy;
  4260. }
  4261. void NEAR ListView_InvalidateCachedLabelSizes(LV* plv)
  4262. {
  4263. int i;
  4264. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  4265. // Label wrapping has changed, so we need to invalidate the
  4266. // size of the items, such that they will be recomputed.
  4267. //
  4268. if (!ListView_IsOwnerData( plv ))
  4269. {
  4270. for (i = ListView_Count(plv) - 1; i >= 0; i--)
  4271. {
  4272. LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, i);
  4273. ListView_SetSRecompute(pitem);
  4274. }
  4275. }
  4276. plv->rcView.left = RECOMPUTE;
  4277. if ((plv->ci.style & LVS_OWNERDRAWFIXED) && ListView_IsReportView(plv))
  4278. plv->cyItemSave = ListView_ComputeCYItemSize(plv);
  4279. else {
  4280. plv->cyItem = ListView_ComputeCYItemSize(plv);
  4281. }
  4282. }
  4283. int LV_GetNewColWidth(LV* plv, int iFirst, int iLast);
  4284. void NEAR ListView_OnStyleChanging(LV* plv, UINT gwl, LPSTYLESTRUCT pinfo)
  4285. {
  4286. if (gwl == GWL_STYLE) {
  4287. // Don't allow LVS_OWNERDATA to change after creation
  4288. DWORD stylePreserve = LVS_OWNERDATA;
  4289. // Don't allow a LVS_EX_REGIONAL listview to change type, since
  4290. // it must be LVS_ICON
  4291. if (plv->exStyle & LVS_EX_REGIONAL)
  4292. stylePreserve |= LVS_TYPEMASK;
  4293. // Preserve the bits that must be preserved
  4294. pinfo->styleNew ^= (pinfo->styleNew ^ pinfo->styleOld) & stylePreserve;
  4295. }
  4296. }
  4297. void NEAR ListView_OnStyleChanged(LV* plv, UINT gwl, LPSTYLESTRUCT pinfo)
  4298. {
  4299. // Style changed: redraw everything...
  4300. //
  4301. // try to do this smartly, avoiding unnecessary redraws
  4302. if (gwl == GWL_STYLE)
  4303. {
  4304. BOOL fRedraw = FALSE, fShouldScroll = FALSE;
  4305. DWORD changeFlags, styleOld;
  4306. ListView_DismissEdit(plv, FALSE); // BUGBUG: FALSE == apply edits. Is this correct?
  4307. changeFlags = plv->ci.style ^ pinfo->styleNew;
  4308. styleOld = plv->ci.style;
  4309. // (dli) Setting the small icon width here and only in the case when we go
  4310. // from large icon view to some other view because of three reasons:
  4311. // 1. According to chee, we want to set this before we change the style bit in
  4312. // plv or after we scale.
  4313. // 2. We don't want to do it after we scale because we want to set the width to
  4314. // the maximum value so that the items in this listview do not cover each other
  4315. // 3. we do it from large icon view because large icon view has fixed width for
  4316. // each item, small icon view width can be scaled.
  4317. //
  4318. if ((changeFlags & LVS_TYPEMASK) && ((styleOld & LVS_TYPEMASK) == LVS_ICON))
  4319. ListView_ISetColumnWidth(plv, 0,
  4320. LV_GetNewColWidth(plv, 0, ListView_Count(plv)-1), FALSE);
  4321. plv->ci.style = pinfo->styleNew; // change our version
  4322. if (changeFlags & (WS_BORDER | WS_CAPTION | WS_THICKFRAME)) {
  4323. // the changing of these bits affect the size of the window
  4324. // but not until after this message is handled
  4325. // so post ourself a message.
  4326. PostMessage(plv->ci.hwnd, LVMP_WINDOWPOSCHANGED, 0, 0);
  4327. }
  4328. if (changeFlags & LVS_NOCOLUMNHEADER) {
  4329. if (plv->hwndHdr) {
  4330. SetWindowBits(plv->hwndHdr, GWL_STYLE, HDS_HIDDEN,
  4331. (plv->ci.style & LVS_NOCOLUMNHEADER) ? HDS_HIDDEN : 0);
  4332. fRedraw = TRUE;
  4333. fShouldScroll = TRUE;
  4334. }
  4335. }
  4336. if (changeFlags & LVS_NOLABELWRAP)
  4337. {
  4338. ListView_InvalidateCachedLabelSizes(plv);
  4339. fShouldScroll = TRUE;
  4340. fRedraw = TRUE;
  4341. }
  4342. if (changeFlags & LVS_TYPEMASK)
  4343. {
  4344. ListView_TypeChange(plv, styleOld);
  4345. fShouldScroll = TRUE;
  4346. fRedraw = TRUE;
  4347. }
  4348. if ((changeFlags & LVS_AUTOARRANGE) && (plv->ci.style & LVS_AUTOARRANGE))
  4349. {
  4350. ListView_OnArrange(plv, LVA_DEFAULT);
  4351. fRedraw = TRUE;
  4352. }
  4353. // bugbug, previously, this was the else to
  4354. // (changeFlags & LVS_AUTOARRANGE && (plv->ci.style & LVS_AUTOARRANGE))
  4355. // I'm not sure that was really the right thing..
  4356. if (fShouldScroll)
  4357. {
  4358. // Else we would like to make the most important item to still
  4359. // be visible. So first we will look for a cursorered item
  4360. // if this fails, we will look for the first selected item,
  4361. // else we will simply ask for the first item (assuming the
  4362. // count > 0
  4363. //
  4364. int i;
  4365. // And make sure the scrollbars are up to date Note this
  4366. // also updates some variables that some views need
  4367. ListView_UpdateScrollBars(plv);
  4368. i = (plv->iFocus >= 0) ? plv->iFocus : ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  4369. if ((i == -1) && (ListView_Count(plv) > 0))
  4370. i = 0;
  4371. if (i != -1)
  4372. ListView_OnEnsureVisible(plv, i, TRUE);
  4373. }
  4374. if (fRedraw)
  4375. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  4376. }
  4377. else if (gwl == GWL_EXSTYLE)
  4378. {
  4379. //
  4380. // If the RTL_MIRROR extended style bit had changed, let's
  4381. // repaint the control window.
  4382. //
  4383. if ((plv->ci.dwExStyle&RTL_MIRRORED_WINDOW) != (pinfo->styleNew&RTL_MIRRORED_WINDOW))
  4384. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  4385. //
  4386. // Save the new ex-style bits
  4387. //
  4388. plv->ci.dwExStyle = pinfo->styleNew;
  4389. }
  4390. // Change of styles also changes tooltip policy, so pop it
  4391. ListView_PopBubble(plv);
  4392. }
  4393. void NEAR ListView_TypeChange(LV* plv, DWORD styleOld)
  4394. {
  4395. RECT rc;
  4396. //
  4397. // Invalidate all cached string metrics because customdraw clients
  4398. // may draw differently depending on the type. This happens more
  4399. // often than you might think, not on purpose, but because apps are
  4400. // buggy.
  4401. //
  4402. // APP COMPAT! You'd think this was completely safe. After all,
  4403. // all we're doing is invalidating our cache so we ask the parent
  4404. // afresh the next time we need the strings. But noooooooo,
  4405. // Outlook98 will FAULT if you ask it for information that it thinks
  4406. // you by all rights already know. Sigh. So guard this with a v5.
  4407. //
  4408. if (plv->ci.iVersion >= 5 && !ListView_IsOwnerData(plv))
  4409. {
  4410. int i;
  4411. for (i = 0; i < ListView_Count(plv); i++)
  4412. {
  4413. LISTITEM *pitem = ListView_FastGetItemPtr(plv, i);
  4414. ListView_SetSRecompute(pitem);
  4415. }
  4416. }
  4417. switch (styleOld & LVS_TYPEMASK)
  4418. {
  4419. case LVS_REPORT:
  4420. ShowWindow(plv->hwndHdr, SW_HIDE);
  4421. if (styleOld & LVS_OWNERDRAWFIXED) {
  4422. // swap cyItem and cyFixed;
  4423. int temp = plv->cyItem;
  4424. plv->cyItem = plv->cyItemSave;
  4425. plv->cyItemSave = temp;
  4426. }
  4427. break;
  4428. default:
  4429. break;
  4430. }
  4431. // Now handle any special setup needed for the new view
  4432. switch (plv->ci.style & LVS_TYPEMASK)
  4433. {
  4434. case (UINT)LVS_ICON:
  4435. ListView_ScaleIconPositions(plv, FALSE);
  4436. break;
  4437. case (UINT)LVS_SMALLICON:
  4438. ListView_ScaleIconPositions(plv, TRUE);
  4439. break;
  4440. case (UINT)LVS_LIST:
  4441. // We may need to resize the columns
  4442. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  4443. break;
  4444. case (UINT)LVS_REPORT:
  4445. // if it's owner draw fixed, we may have to do funky stuff
  4446. if ((styleOld & LVS_TYPEMASK) != LVS_REPORT) {
  4447. plv->cyItemSave = plv->cyItem;
  4448. }
  4449. ListView_RInitialize(plv, FALSE);
  4450. break;
  4451. default:
  4452. break;
  4453. }
  4454. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  4455. GetClientRect(plv->ci.hwnd, &rc);
  4456. plv->sizeClient.cx = rc.right;
  4457. plv->sizeClient.cy = rc.bottom;
  4458. }
  4459. int NEAR ListView_OnHitTest(LV* plv, LV_HITTESTINFO FAR* pinfo)
  4460. {
  4461. UINT flags;
  4462. int x, y;
  4463. if (!pinfo) return -1;
  4464. x = pinfo->pt.x;
  4465. y = pinfo->pt.y;
  4466. pinfo->iItem = -1;
  4467. flags = 0;
  4468. if (x < 0)
  4469. flags |= LVHT_TOLEFT;
  4470. else if (x >= plv->sizeClient.cx)
  4471. flags |= LVHT_TORIGHT;
  4472. if (y < 0)
  4473. flags |= LVHT_ABOVE;
  4474. else if (y >= plv->sizeClient.cy)
  4475. flags |= LVHT_BELOW;
  4476. if (flags == 0)
  4477. {
  4478. pinfo->iItem = _ListView_ItemHitTest(plv, x, y, &flags, NULL);
  4479. }
  4480. pinfo->flags = flags;
  4481. if (pinfo->iItem >= ListView_Count(plv)) {
  4482. pinfo->iItem = -1;
  4483. pinfo->flags = LVHT_NOWHERE;
  4484. }
  4485. return pinfo->iItem;
  4486. }
  4487. int NEAR ScrollAmount(int large, int iSmall, int unit)
  4488. {
  4489. return (((large - iSmall) + (unit - 1)) / unit) * unit;
  4490. }
  4491. // NOTE: this is duplicated in shell32.dll
  4492. //
  4493. // checks to see if we are at the end position of a scroll bar
  4494. // to avoid scrolling when not needed (avoid flashing)
  4495. //
  4496. // in:
  4497. // code SB_VERT or SB_HORZ
  4498. // bDown FALSE is up or left
  4499. // TRUE is down or right
  4500. BOOL NEAR PASCAL CanScroll(LV* plv, int code, BOOL bDown)
  4501. {
  4502. SCROLLINFO si;
  4503. si.cbSize = sizeof(SCROLLINFO);
  4504. si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
  4505. if (ListView_GetScrollInfo(plv, code, &si))
  4506. {
  4507. if (bDown)
  4508. {
  4509. if (si.nPage)
  4510. si.nMax -= si.nPage - 1;
  4511. return si.nPos < si.nMax;
  4512. }
  4513. else
  4514. {
  4515. return si.nPos > si.nMin;
  4516. }
  4517. }
  4518. else
  4519. {
  4520. return FALSE;
  4521. }
  4522. }
  4523. // detect if we should auto scroll the window
  4524. //
  4525. // in:
  4526. // pt cursor pos in hwnd's client coords
  4527. // out:
  4528. // pdx, pdy ammount scrolled in x and y
  4529. //
  4530. // REVIEW, this should make sure a certain amount of time has passed
  4531. // before scrolling.
  4532. void NEAR ScrollDetect(LV* plv, POINT pt, int FAR *pdx, int FAR *pdy)
  4533. {
  4534. int dx, dy;
  4535. *pdx = *pdy = 0;
  4536. if (!(plv->ci.style & (WS_HSCROLL | WS_VSCROLL)))
  4537. return;
  4538. dx = dy = plv->cyIcon / 16;
  4539. if (ListView_IsReportView(plv)) {
  4540. dy = plv->cyItem; // we scroll in units of items...
  4541. if (!dx)
  4542. dx = plv->cxSmIcon;
  4543. }
  4544. if (ListView_IsListView(plv))
  4545. dx = plv->cxItem;
  4546. if (!dx)
  4547. dx = 1;
  4548. if (!dy)
  4549. dy = 1;
  4550. // we need to check if we can scroll before acutally doing it
  4551. // since the selection rect is adjusted based on how much
  4552. // we scroll by
  4553. if (plv->ci.style & WS_VSCROLL) { // scroll vertically?
  4554. if (pt.y >= plv->sizeClient.cy) {
  4555. if (CanScroll(plv, SB_VERT, TRUE))
  4556. *pdy = ScrollAmount(pt.y, plv->sizeClient.cy, dy); // down
  4557. } else if (pt.y <= 0) {
  4558. if (CanScroll(plv, SB_VERT, FALSE))
  4559. *pdy = -ScrollAmount(0, pt.y, dy); // up
  4560. }
  4561. }
  4562. if (plv->ci.style & WS_HSCROLL) { // horizontally
  4563. if (pt.x >= plv->sizeClient.cx) {
  4564. if (CanScroll(plv, SB_HORZ, TRUE))
  4565. *pdx = ScrollAmount(pt.x, plv->sizeClient.cx, dx); // right
  4566. } else if (pt.x <= 0) {
  4567. if (CanScroll(plv, SB_HORZ, FALSE))
  4568. *pdx = -ScrollAmount(0, pt.x, dx); // left
  4569. }
  4570. }
  4571. // BUGBUG: this will potentially scroll outside the bounds of the
  4572. // listview. we should bound the scroll amount in CanScroll()
  4573. // or ScrollAmount().
  4574. if (*pdx || *pdy) {
  4575. ListView_ValidateScrollParams(plv, pdx, pdy);
  4576. }
  4577. }
  4578. #define swap(pi1, pi2) {int i = *(pi1) ; *(pi1) = *(pi2) ; *(pi2) = i ;}
  4579. void NEAR OrderRect(RECT FAR *prc)
  4580. {
  4581. if (prc->left > prc->right)
  4582. swap(&prc->left, &prc->right);
  4583. if (prc->bottom < prc->top)
  4584. swap(&prc->bottom, &prc->top);
  4585. }
  4586. // in:
  4587. // x, y starting point in client coords
  4588. #define SCROLL_FREQ (GetDoubleClickTime()/2) // 1/5 of a second between scrolls
  4589. //----------------------------------------------------------------------------
  4590. BOOL ShouldScroll(LV* plv, LPPOINT ppt, LPRECT lprc)
  4591. {
  4592. ASSERT(ppt);
  4593. if (plv->ci.style & WS_VSCROLL)
  4594. {
  4595. if (ppt->y >= lprc->bottom)
  4596. {
  4597. if (CanScroll(plv, SB_VERT, TRUE))
  4598. return TRUE;
  4599. }
  4600. else if (ppt->y <= lprc->top)
  4601. {
  4602. if (CanScroll(plv, SB_VERT, FALSE))
  4603. return TRUE;
  4604. }
  4605. }
  4606. if (plv->ci.style & WS_HSCROLL)
  4607. {
  4608. if (ppt->x >= lprc->right)
  4609. {
  4610. if (CanScroll(plv, SB_HORZ, TRUE))
  4611. return TRUE;
  4612. }
  4613. else if (ppt->x <= lprc->left)
  4614. {
  4615. if (CanScroll(plv, SB_HORZ, FALSE))
  4616. return TRUE;
  4617. }
  4618. }
  4619. return FALSE;
  4620. }
  4621. #ifdef WIN95MARQUEE
  4622. #define DrawFocusRectClip(a, b, c) DrawFocusRect(a, b)
  4623. #else
  4624. BOOL DrawFocusRectClip(HDC hdc, CONST RECT * prc, CONST RECT * prcClip)
  4625. {
  4626. RECT rc;
  4627. IntersectRect(&rc, prc, prcClip);
  4628. return DrawFocusRect(hdc, &rc);
  4629. }
  4630. #endif
  4631. //----------------------------------------------------------------------------
  4632. void NEAR ListView_DragSelect(LV *plv, int x, int y)
  4633. {
  4634. RECT rc, rcWindow, rcOld, rcUnion, rcTemp2, rcClip;
  4635. POINT pt;
  4636. MSG32 msg32;
  4637. HDC hdc;
  4638. HWND hwnd = plv->ci.hwnd;
  4639. int i, iEnd, dx, dy;
  4640. BOOL bInOld, bInNew = FALSE, bLocked = FALSE;
  4641. DWORD dwTime, dwNewTime;
  4642. HRGN hrgnUpdate = NULL, hrgnLV = NULL;
  4643. rc.left = rc.right = x;
  4644. rc.top = rc.bottom = y;
  4645. rcOld = rc;
  4646. UpdateWindow(plv->ci.hwnd);
  4647. if (plv->exStyle & LVS_EX_REGIONAL) {
  4648. if ((hrgnUpdate = CreateRectRgn(0,0,0,0)) &&
  4649. (hrgnLV = CreateRectRgn(0,0,0,0)) &&
  4650. (LockWindowUpdate(GetParent(hwnd)))) {
  4651. hdc = GetDCEx(hwnd, NULL, DCX_PARENTCLIP | DCX_LOCKWINDOWUPDATE);
  4652. bLocked = TRUE;
  4653. } else {
  4654. goto BailOut;
  4655. }
  4656. } else {
  4657. hdc = GetDC(hwnd);
  4658. }
  4659. SetCapture(hwnd);
  4660. DrawFocusRect(hdc, &rc);
  4661. GetClientRect(hwnd, &rcClip);
  4662. GetWindowRect(hwnd, &rcWindow);
  4663. dwTime = GetTickCount();
  4664. for (;;)
  4665. {
  4666. // WM_CANCELMODE messages will unset the capture, in that
  4667. // case I want to exit this loop
  4668. if (GetCapture() != hwnd)
  4669. {
  4670. break;
  4671. }
  4672. if (!PeekMessage32(&msg32, NULL, 0, 0, PM_REMOVE, TRUE)) {
  4673. // if the cursor is outside of the window rect
  4674. // we need to generate messages to make autoscrolling
  4675. // keep going
  4676. if (!PtInRect(&rcWindow, msg32.pt))
  4677. {
  4678. // If we may be able to scroll, generate a mouse move.
  4679. if (ShouldScroll(plv, &msg32.pt, &rcWindow))
  4680. SetCursorPos(msg32.pt.x, msg32.pt.y);
  4681. }
  4682. else
  4683. {
  4684. WaitMessage();
  4685. }
  4686. continue;
  4687. }
  4688. // See if the application wants to process the message...
  4689. if (CallMsgFilter32(&msg32, MSGF_COMMCTRL_DRAGSELECT, TRUE) != 0)
  4690. continue;
  4691. switch (msg32.message)
  4692. {
  4693. case WM_LBUTTONUP:
  4694. case WM_RBUTTONUP:
  4695. case WM_LBUTTONDOWN:
  4696. case WM_MBUTTONDOWN:
  4697. case WM_MBUTTONUP:
  4698. case WM_RBUTTONDOWN:
  4699. CCReleaseCapture(&plv->ci);
  4700. goto EndOfLoop;
  4701. case WM_TIMER:
  4702. if (msg32.wParam != IDT_MARQUEE)
  4703. goto DoDefault;
  4704. // else fall through
  4705. case WM_MOUSEMOVE:
  4706. {
  4707. int dMax = -1;
  4708. pt = msg32.pt;
  4709. ScreenToClient(hwnd, &pt);
  4710. dwNewTime = GetTickCount();
  4711. // if (1 || (dwNewTime - dwTime) > SCROLL_FREQ)
  4712. // {
  4713. dwTime = dwNewTime; // reset scroll timer
  4714. ScrollDetect(plv, pt, &dx, &dy);
  4715. // }
  4716. // else
  4717. // {
  4718. // dx = dy = 0;
  4719. // }
  4720. //SetTimer(plv->ci.hwnd, IDT_MARQUEE, SCROLL_FREQ, NULL);
  4721. y -= dy; // scroll up/down
  4722. x -= dx; // scroll left/right
  4723. rc.left = x;
  4724. rc.top = y;
  4725. rc.right = pt.x;
  4726. rc.bottom = pt.y;
  4727. // clip drag rect to the window
  4728. //
  4729. if (rc.right > rcClip.right)
  4730. rc.right = rcClip.right;
  4731. if (rc.right < rcClip.left)
  4732. rc.right = rcClip.left;
  4733. if (rc.bottom > rcClip.bottom)
  4734. rc.bottom = rcClip.bottom;
  4735. if (rc.bottom < rcClip.top)
  4736. rc.bottom = rcClip.top;
  4737. OrderRect(&rc);
  4738. if (EqualRect(&rc, &rcOld))
  4739. break;
  4740. // move the old rect
  4741. DrawFocusRect(hdc, &rcOld); // erase old
  4742. if (dx || dy)
  4743. ListView_OnScroll(plv, dx, dy);
  4744. OffsetRect(&rcOld, -dx, -dy);
  4745. //
  4746. // For Report and List view, we can speed things up by
  4747. // only searching through those items that are visible. We
  4748. // use the hittest to calculate the first item to paint.
  4749. // BUGBUG:: We are using state specific info here...
  4750. //
  4751. UnionRect(&rcUnion, &rc, &rcOld);
  4752. if (ListView_IsReportView(plv))
  4753. {
  4754. i = (int)((plv->ptlRptOrigin.y + rcUnion.top - plv->yTop)
  4755. / plv->cyItem);
  4756. iEnd = (int)((plv->ptlRptOrigin.y + rcUnion.bottom - plv->yTop)
  4757. / plv->cyItem) + 1;
  4758. }
  4759. else if (ListView_IsListView(plv))
  4760. {
  4761. i = ((plv->xOrigin + rcUnion.left)/ plv->cxItem)
  4762. * plv->cItemCol + rcUnion.top / plv->cyItem;
  4763. iEnd = ((plv->xOrigin + rcUnion.right)/ plv->cxItem)
  4764. * plv->cItemCol + rcUnion.bottom / plv->cyItem + 1;
  4765. }
  4766. else
  4767. {
  4768. if (ListView_IsOwnerData( plv ))
  4769. {
  4770. ListView_CalcMinMaxIndex( plv, &rcUnion, &i, &iEnd );
  4771. }
  4772. else
  4773. {
  4774. i = 0;
  4775. iEnd = ListView_Count(plv);
  4776. }
  4777. }
  4778. // make sure our endpoint is in range.
  4779. if (iEnd > ListView_Count(plv))
  4780. iEnd = ListView_Count(plv);
  4781. if (i < 0)
  4782. i = 0;
  4783. if (ListView_IsOwnerData(plv) && (i < iEnd)) {
  4784. ListView_NotifyCacheHint(plv, i, iEnd-1);
  4785. }
  4786. if (bInNew && !(msg32.wParam & (MK_CONTROL | MK_SHIFT))) {
  4787. plv->iMark = -1;
  4788. }
  4789. for (; i < iEnd; i++) {
  4790. RECT dummy;
  4791. ListView_GetRects(plv, i, NULL, NULL, NULL, &rcTemp2);
  4792. // don't do this infaltion if we're in report&full row mode
  4793. // in that case, just touching is good enough
  4794. if (!(ListView_IsReportView(plv) && ListView_FullRowSelect(plv))) {
  4795. int cxInflate = (rcTemp2.right - rcTemp2.left) / 4;
  4796. if (ListView_IsListView(plv)) {
  4797. cxInflate = min(cxInflate, plv->cxSmIcon);
  4798. }
  4799. InflateRect(&rcTemp2, -cxInflate, -(rcTemp2.bottom - rcTemp2.top) / 4);
  4800. }
  4801. bInOld = (IntersectRect(&dummy, &rcOld, &rcTemp2) != 0);
  4802. bInNew = (IntersectRect(&dummy, &rc, &rcTemp2) != 0);
  4803. if (msg32.wParam & MK_CONTROL) {
  4804. if (bInOld != bInNew) {
  4805. ListView_ToggleSelection(plv, i);
  4806. }
  4807. } else {
  4808. // was there a change?
  4809. if (bInOld != bInNew) {
  4810. ListView_OnSetItemState(plv, i, bInOld ? 0 : LVIS_SELECTED, LVIS_SELECTED);
  4811. }
  4812. // if no alternate keys are down.. set the mark to
  4813. // the item furthest from the cursor
  4814. if (bInNew && !(msg32.wParam & (MK_CONTROL | MK_SHIFT))) {
  4815. int dItem;
  4816. dItem = (rcTemp2.left - pt.x) * (rcTemp2.left - pt.x) +
  4817. (rcTemp2.top - pt.y) * (rcTemp2.top - pt.y);
  4818. // if it's further away, set this as the mark
  4819. //DebugMsg(TF_LISTVIEW, "dItem = %d, dMax = %d", dItem, dMax);
  4820. if (dItem > dMax) {
  4821. //DebugMsg(TF_LISTVIEW, "taking dItem .. iMark = %d", i);
  4822. dMax = dItem;
  4823. plv->iMark = i;
  4824. }
  4825. }
  4826. }
  4827. }
  4828. //DebugMsg(TF_LISTVIEW, "Final iMark = %d", plv->iMark);
  4829. if (bLocked) {
  4830. if (GetUpdateRgn(plv->ci.hwnd, hrgnUpdate, FALSE) > NULLREGION) {
  4831. ValidateRect(plv->ci.hwnd, NULL);
  4832. GetWindowRgn(plv->ci.hwnd, hrgnLV);
  4833. CombineRgn(hrgnUpdate, hrgnUpdate, hrgnLV, RGN_AND);
  4834. SelectClipRgn(hdc, hrgnUpdate);
  4835. SendMessage(plv->ci.hwnd, WM_PRINTCLIENT, (WPARAM)hdc, 0);
  4836. SelectClipRgn(hdc, NULL);
  4837. }
  4838. } else {
  4839. UpdateWindow(plv->ci.hwnd); // make selection draw
  4840. }
  4841. DrawFocusRect(hdc, &rc);
  4842. rcOld = rc;
  4843. break;
  4844. }
  4845. case WM_KEYDOWN:
  4846. switch (msg32.wParam) {
  4847. case VK_ESCAPE:
  4848. ListView_DeselectAll(plv, -1);
  4849. goto EndOfLoop;
  4850. }
  4851. case WM_CHAR:
  4852. case WM_KEYUP:
  4853. // don't process thay keyboard stuff during marquee
  4854. break;
  4855. default:
  4856. // don't process mouse wheel stuff
  4857. if (msg32.message == g_msgMSWheel)
  4858. break;
  4859. DoDefault:
  4860. TranslateMessage32(&msg32, TRUE);
  4861. DispatchMessage32(&msg32, TRUE);
  4862. }
  4863. }
  4864. EndOfLoop:
  4865. DrawFocusRect(hdc, &rcOld); // erase old
  4866. ReleaseDC(hwnd, hdc);
  4867. BailOut:
  4868. if (hrgnUpdate)
  4869. DeleteObject(hrgnUpdate);
  4870. if (hrgnLV)
  4871. DeleteObject(hrgnLV);
  4872. if (bLocked)
  4873. LockWindowUpdate(NULL);
  4874. }
  4875. #define SHIFT_DOWN(keyFlags) (keyFlags & MK_SHIFT)
  4876. #define CONTROL_DOWN(keyFlags) (keyFlags & MK_CONTROL)
  4877. #define RIGHTBUTTON(keyFlags) (keyFlags & MK_RBUTTON)
  4878. void PASCAL ListView_ButtonSelect(LV* plv, int iItem, UINT keyFlags, BOOL bSelected)
  4879. {
  4880. if (SHIFT_DOWN(keyFlags))
  4881. {
  4882. ListView_SelectRangeTo(plv, iItem, !CONTROL_DOWN(keyFlags));
  4883. ListView_SetFocusSel(plv, iItem, TRUE, FALSE, FALSE);
  4884. }
  4885. else if (!CONTROL_DOWN(keyFlags))
  4886. {
  4887. ListView_SetFocusSel(plv, iItem, TRUE, !bSelected, FALSE);
  4888. }
  4889. }
  4890. void ListView_HandleStateIconClick(LV* plv, int iItem)
  4891. {
  4892. int iState =
  4893. ListView_OnGetItemState(plv, iItem, LVIS_STATEIMAGEMASK);
  4894. iState = STATEIMAGEMASKTOINDEX(iState) -1;
  4895. iState++;
  4896. iState %= ImageList_GetImageCount(plv->himlState);
  4897. iState++;
  4898. ListView_OnSetItemState(plv, iItem, INDEXTOSTATEIMAGEMASK(iState), LVIS_STATEIMAGEMASK);
  4899. }
  4900. BOOL ListView_RBeginMarquee(LV* plv, int x, int y, LPLVHITTESTINFO plvhti)
  4901. {
  4902. if (ListView_FullRowSelect(plv) &&
  4903. ListView_IsReportView(plv) &&
  4904. !(plv->ci.style & LVS_SINGLESEL) &&
  4905. !ListView_OwnerDraw(plv) &&
  4906. plvhti->iSubItem == 0) {
  4907. // can only begin marquee in column 0.
  4908. if (plvhti->flags == LVHT_ONITEM) {
  4909. return TRUE;
  4910. }
  4911. }
  4912. return FALSE;
  4913. }
  4914. void NEAR ListView_HandleMouse(LV* plv, BOOL fDoubleClick, int x, int y, UINT keyFlags, BOOL bMouseWheel)
  4915. {
  4916. LV_HITTESTINFO ht;
  4917. NMITEMACTIVATE nm;
  4918. int iItem, click, drag;
  4919. BOOL bSelected, fHadFocus, fNotifyReturn = FALSE;
  4920. BOOL fActive;
  4921. HWND hwnd = plv->ci.hwnd;
  4922. if (plv->fButtonDown)
  4923. return;
  4924. plv->fButtonDown = TRUE;
  4925. if (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickHappened && plv->fOneClickOK)
  4926. {
  4927. KillTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED);
  4928. plv->fOneClickHappened = FALSE;
  4929. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  4930. if (!IsWindow(hwnd))
  4931. return;
  4932. }
  4933. fHadFocus = (GetFocus() == plv->ci.hwnd);
  4934. click = RIGHTBUTTON(keyFlags) ? NM_RCLICK : NM_CLICK;
  4935. drag = RIGHTBUTTON(keyFlags) ? LVN_BEGINRDRAG : LVN_BEGINDRAG;
  4936. fActive = ChildOfActiveWindow(plv->ci.hwnd) || fShouldFirstClickActivate() ||
  4937. ChildOfDesktop(plv->ci.hwnd);
  4938. #ifdef LVDEBUG
  4939. DebugMsg(TF_LISTVIEW, TEXT("ListView_OnButtonDown %d"), fDoubleClick);
  4940. #endif
  4941. SetCapture(plv->ci.hwnd);
  4942. plv->ptCapture.x = x;
  4943. plv->ptCapture.y = y;
  4944. if (!ListView_DismissEdit(plv, FALSE) && GetCapture() != plv->ci.hwnd)
  4945. goto EndButtonDown;
  4946. CCReleaseCapture(&plv->ci);
  4947. // REVIEW: right button implies no shift or control stuff
  4948. // Single selection style also implies no modifiers
  4949. //if (RIGHTBUTTON(keyFlags) || (plv->ci.style & LVS_SINGLESEL))
  4950. if ((plv->ci.style & LVS_SINGLESEL))
  4951. keyFlags &= ~(MK_SHIFT | MK_CONTROL);
  4952. ht.pt.x = x;
  4953. ht.pt.y = y;
  4954. iItem = ListView_OnSubItemHitTest(plv, &ht);
  4955. if (ht.iSubItem != 0) {
  4956. // if we're not in full row select,
  4957. // hitting on a subitem is like hitting on nowhere
  4958. // also, in win95, ownerdraw fixed effectively had full row select
  4959. if (!ListView_FullRowSelect(plv) &&
  4960. !(plv->ci.style & LVS_OWNERDRAWFIXED)) {
  4961. iItem = -1;
  4962. ht.flags = LVHT_NOWHERE;
  4963. }
  4964. }
  4965. nm.iItem = iItem;
  4966. nm.iSubItem = ht.iSubItem;
  4967. nm.uChanged = 0;
  4968. nm.ptAction.x = x;
  4969. nm.ptAction.y = y;
  4970. nm.uKeyFlags = GetLVKeyFlags();
  4971. // FProt Profesional assumed that if the notification structure pointer + 14h bytes
  4972. // had a value 2 that it was a displayinfo structure and they then used offset +2c as lparam...
  4973. nm.uNewState = 0;
  4974. plv->iNoHover = iItem;
  4975. bSelected = (iItem >= 0) && ListView_OnGetItemState(plv, iItem, LVIS_SELECTED);
  4976. if (fDoubleClick)
  4977. {
  4978. //
  4979. // Cancel any name editing that might happen.
  4980. //
  4981. ListView_CancelPendingEdit(plv);
  4982. KillTimer(plv->ci.hwnd, IDT_SCROLLWAIT);
  4983. if (ht.flags & LVHT_NOWHERE) {
  4984. // this would have been done in the first click in win95 except
  4985. // now we blow off the first click on focus change
  4986. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags))
  4987. ListView_DeselectAll(plv, -1);
  4988. }
  4989. click = RIGHTBUTTON(keyFlags) ? NM_RDBLCLK : NM_DBLCLK ;
  4990. if (CCSendNotify(&plv->ci, click, &nm.hdr))
  4991. goto EndButtonDown;
  4992. /// some (comdlg32 for example) destroy on double click
  4993. // we need to bail if that happens because plv is no longer valid
  4994. if (!IsWindow(hwnd))
  4995. return;
  4996. if (click == NM_DBLCLK)
  4997. {
  4998. // these shift control flags are to mirror when we don't send out the activate on the single click,
  4999. // but are in the oneclick activate mode (see below)
  5000. if (ht.flags & (LVHT_ONITEMLABEL | LVHT_ONITEMICON))
  5001. {
  5002. // possible scenarios below:
  5003. // 1) we're using classic windows style so double click => launch
  5004. // 2) we're using single click activate
  5005. // a) shift is down and item is selected => launch
  5006. // this implies that the first click selected it
  5007. // b) control is down => launch
  5008. // the first click toggled the selection so if the item was
  5009. // the only item selected and we double clicked on it
  5010. // the first click deselects it and no item is selected
  5011. // so nothing will be launched - this is win95 behavior
  5012. if (!(plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK) ||
  5013. (plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK &&
  5014. (SHIFT_DOWN(keyFlags) || CONTROL_DOWN(keyFlags))))
  5015. {
  5016. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &nm.hdr);
  5017. }
  5018. }
  5019. // Double-click on checkbox state icon cycles it just like single click
  5020. else if ((ht.flags & LVHT_ONITEMSTATEICON) && ListView_CheckBoxes(plv)) {
  5021. ListView_HandleStateIconClick(plv, iItem);
  5022. }
  5023. }
  5024. if (!IsWindow(hwnd))
  5025. return;
  5026. goto EndButtonDown;
  5027. }
  5028. if (ht.flags & (LVHT_ONITEMLABEL | LVHT_ONITEMICON))
  5029. {
  5030. // if it wasn't selected, we're about to select it... play
  5031. // a little ditty for us...
  5032. CCPlaySound(c_szSelect);
  5033. if (!RIGHTBUTTON(keyFlags) || (!CONTROL_DOWN(keyFlags) && !SHIFT_DOWN(keyFlags)))
  5034. ListView_ButtonSelect(plv, iItem, keyFlags, bSelected);
  5035. // handle full row select
  5036. // If single-select listview, disable marquee selection.
  5037. //
  5038. // Careful - CheckForDragBegin yields and the app may have
  5039. // destroyed the item we were thinking about dragging!
  5040. //
  5041. if (!bMouseWheel && CheckForDragBegin(plv->ci.hwnd, x, y))
  5042. {
  5043. // should we do a marquee?
  5044. if (ListView_RBeginMarquee(plv, x, y, &ht) &&
  5045. !CCSendNotify(&plv->ci, LVN_MARQUEEBEGIN, &nm.hdr))
  5046. {
  5047. ListView_DragSelect(plv, x, y);
  5048. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  5049. }
  5050. else
  5051. {
  5052. // Before we start dragging, make it sure that it is
  5053. // selected and has the focus.
  5054. ListView_SetFocusSel(plv, iItem, TRUE, FALSE, FALSE);
  5055. if (!SHIFT_DOWN(keyFlags))
  5056. plv->iMark = iItem;
  5057. // Then, we need to update the window before start dragging
  5058. // to show the selection chagne.
  5059. UpdateWindow(plv->ci.hwnd);
  5060. CCSendNotify(&plv->ci, drag, &nm.hdr);
  5061. goto EndButtonDown;
  5062. }
  5063. }
  5064. // CheckForDragBegin yields, so revalidate before continuing
  5065. else if (IsWindow(hwnd))
  5066. {
  5067. // button came up and we are not dragging
  5068. if (!RIGHTBUTTON(keyFlags))
  5069. {
  5070. if (CONTROL_DOWN(keyFlags))
  5071. {
  5072. // do this on the button up so that ctrl-dragging a range
  5073. // won't toggle the select.
  5074. if (SHIFT_DOWN(keyFlags))
  5075. ListView_SetFocusSel(plv, iItem, FALSE, FALSE, FALSE);
  5076. else
  5077. {
  5078. ListView_SetFocusSel(plv, iItem, TRUE, FALSE, TRUE);
  5079. }
  5080. }
  5081. }
  5082. if (!SHIFT_DOWN(keyFlags))
  5083. plv->iMark = iItem;
  5084. if (!ListView_SetFocus(plv->ci.hwnd)) // activate this window
  5085. return;
  5086. // now do the deselect stuff
  5087. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags) && !RIGHTBUTTON(keyFlags))
  5088. {
  5089. ListView_DeselectAll(plv, iItem);
  5090. if ((ht.flags & LVHT_ONITEMLABEL) && bSelected &&
  5091. !(plv->exStyle & (LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE)))
  5092. {
  5093. // doing this check for ownerdrawfixed is for compatability.
  5094. // we don't want to go into edit mode if the user just happened to click
  5095. // to this window when a different one had focus,
  5096. // but ms hammer relied upon the notification being sent (and we
  5097. // don't go into edit mode anyways for ownerdraw)
  5098. if (fHadFocus ||
  5099. (plv->ci.style & LVS_OWNERDRAWFIXED)) {
  5100. // Click on item label. It was selected and
  5101. // no modifier keys were pressed and no drag operation
  5102. // So setup for name edit mode. Still need to wait
  5103. // to make sure user is not doing double click.
  5104. //
  5105. ListView_SetupPendingNameEdit(plv);
  5106. }
  5107. }
  5108. }
  5109. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  5110. if (!IsWindow(hwnd))
  5111. return;
  5112. if (plv->exStyle & (LVS_EX_ONECLICKACTIVATE|LVS_EX_TWOCLICKACTIVATE))
  5113. {
  5114. if (!RIGHTBUTTON(keyFlags))
  5115. {
  5116. // We don't ItemActivate within one double-click time of creating
  5117. // this listview. This is a common occurence for people used to
  5118. // double-clicking. The first click pops up a new window which
  5119. // receives the second click and ItemActivates the item...
  5120. //
  5121. if ((plv->exStyle & LVS_EX_ONECLICKACTIVATE && plv->fOneClickOK) || bSelected)
  5122. {
  5123. if (fActive)
  5124. {
  5125. // condition: if we're in a single click activate mode
  5126. // don't launch if control or shift keys are pressed
  5127. BOOL bCond = plv->exStyle & LVS_EX_ONECLICKACTIVATE && !CONTROL_DOWN(keyFlags) && !SHIFT_DOWN(keyFlags);
  5128. if ((bSelected && plv->exStyle & LVS_EX_TWOCLICKACTIVATE) ||
  5129. (bCond && !g_bUseDblClickTimer))
  5130. {
  5131. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &nm.hdr);
  5132. if (!IsWindow(hwnd))
  5133. return;
  5134. }
  5135. else if (bCond && g_bUseDblClickTimer)
  5136. {
  5137. plv->fOneClickHappened = TRUE;
  5138. plv->nmOneClickHappened = nm;
  5139. SetTimer(plv->ci.hwnd, IDT_ONECLICKHAPPENED, GetDoubleClickTime(), NULL);
  5140. }
  5141. }
  5142. }
  5143. }
  5144. }
  5145. }
  5146. else
  5147. {
  5148. // IsWindow() failed. Bail.
  5149. return;
  5150. }
  5151. }
  5152. else if (ht.flags & LVHT_ONITEMSTATEICON)
  5153. {
  5154. // Should activate window and send notificiation to parent...
  5155. if (!ListView_SetFocus(plv->ci.hwnd)) // activate this window
  5156. return;
  5157. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  5158. if (fNotifyReturn && ListView_CheckBoxes(plv)) {
  5159. ListView_HandleStateIconClick(plv, iItem);
  5160. }
  5161. }
  5162. else if (ht.flags & LVHT_NOWHERE)
  5163. {
  5164. if (!ListView_SetFocus(plv->ci.hwnd)) // activate this window
  5165. return;
  5166. // If single-select listview, disable marquee selection.
  5167. if (!(plv->ci.style & LVS_SINGLESEL) && CheckForDragBegin(plv->ci.hwnd, x, y) &&
  5168. !CCSendNotify(&plv->ci, LVN_MARQUEEBEGIN, &nm.hdr))
  5169. {
  5170. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags))
  5171. ListView_DeselectAll(plv, -1);
  5172. ListView_DragSelect(plv, x, y);
  5173. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  5174. } else if (IsWindow(hwnd)) {
  5175. // if we didn't have focus and aren't showing selection always,
  5176. // make the first click just set focus
  5177. BOOL fDoFirstClickSelection = (fHadFocus || plv->ci.style & LVS_SHOWSELALWAYS ||
  5178. CONTROL_DOWN(keyFlags) || SHIFT_DOWN(keyFlags) ||
  5179. RIGHTBUTTON(keyFlags));
  5180. if (fDoFirstClickSelection && fActive) {
  5181. if (!SHIFT_DOWN(keyFlags) && !CONTROL_DOWN(keyFlags))
  5182. ListView_DeselectAll(plv, -1);
  5183. fNotifyReturn = !CCSendNotify(&plv->ci, click, &nm.hdr);
  5184. }
  5185. }
  5186. else
  5187. {
  5188. // IsWindow() failed. Bail.
  5189. return;
  5190. }
  5191. }
  5192. // re-check the key state so we don't get confused by multiple clicks
  5193. // this needs to check the GetKeyState stuff only when we've gone into
  5194. // a modal loop waiting for the rbutton up.
  5195. if (fNotifyReturn && (click == NM_RCLICK)) // && (GetKeyState(VK_RBUTTON)>=0))
  5196. {
  5197. POINT pt = { x, y };
  5198. ClientToScreen(plv->ci.hwnd, &pt);
  5199. FORWARD_WM_CONTEXTMENU(plv->ci.hwnd, plv->ci.hwnd, pt.x, pt.y, SendMessage);
  5200. }
  5201. EndButtonDown:
  5202. if (IsWindow(hwnd))
  5203. plv->fButtonDown = FALSE;
  5204. }
  5205. void NEAR ListView_OnButtonDown(LV* plv, BOOL fDoubleClick, int x, int y, UINT keyFlags)
  5206. {
  5207. ListView_HandleMouse(plv, fDoubleClick, x, y, keyFlags, FALSE);
  5208. }
  5209. #define ListView_CancelPendingEdit(plv) ListView_CancelPendingTimer(plv, LVF_NMEDITPEND, IDT_NAMEEDIT)
  5210. #define ListView_CancelScrollWait(plv) ListView_CancelPendingTimer(plv, LVF_SCROLLWAIT, IDT_SCROLLWAIT)
  5211. BOOL NEAR ListView_CancelPendingTimer(LV* plv, UINT fFlags, int idTimer)
  5212. {
  5213. if (plv->flags & fFlags)
  5214. {
  5215. KillTimer(plv->ci.hwnd, idTimer);
  5216. plv->flags &= ~fFlags;
  5217. return TRUE;
  5218. }
  5219. return FALSE;
  5220. }
  5221. //
  5222. // ListView_OnTimer:
  5223. // process the WM_TIMER message. If the timer id is thta
  5224. // of the name editing, we should then start the name editing mode.
  5225. //
  5226. void NEAR ListView_OnTimer(LV* plv, UINT id)
  5227. {
  5228. KillTimer(plv->ci.hwnd, id);
  5229. if (id == IDT_NAMEEDIT)
  5230. {
  5231. // Kill the timer as we wont need any more messages from it.
  5232. if (ListView_CancelPendingEdit(plv)) {
  5233. // And start name editing mode.
  5234. if (!ListView_OnEditLabel(plv, plv->iFocus, NULL))
  5235. {
  5236. ListView_DismissEdit(plv, FALSE);
  5237. ListView_SetFocusSel(plv, plv->iFocus, TRUE, TRUE, FALSE);
  5238. }
  5239. }
  5240. } else if (id == IDT_SCROLLWAIT) {
  5241. if (ListView_CancelScrollWait(plv)) {
  5242. ListView_OnEnsureVisible(plv, plv->iFocus, TRUE);
  5243. }
  5244. } else if (id == IDT_ONECLICKOK) {
  5245. plv->fOneClickOK = TRUE;
  5246. } else if (id == IDT_ONECLICKHAPPENED) {
  5247. //if (!g_bUseDblClickTimer)
  5248. //{
  5249. //// EnableWindow(plv->ci.hwnd, TRUE);
  5250. // SetWindowBits(plv->ci.hwnd, GWL_STYLE, WS_DISABLED, 0);
  5251. // plv->fOneClickHappened = FALSE;
  5252. //}
  5253. // check the bit just in case they double-clicked
  5254. //else
  5255. if (plv->fOneClickHappened)
  5256. {
  5257. plv->fOneClickHappened = FALSE;
  5258. CCSendNotify(&plv->ci, LVN_ITEMACTIVATE, &(plv->nmOneClickHappened.hdr));
  5259. }
  5260. }
  5261. }
  5262. //
  5263. // ListView_SetupPendingNameEdit:
  5264. // Sets up a timer to begin name editing at a delayed time. This
  5265. // will allow the user to double click on the already selected item
  5266. // without going into name editing mode, which is especially important
  5267. // in those views that only show a small icon.
  5268. //
  5269. void NEAR ListView_SetupPendingNameEdit(LV* plv)
  5270. {
  5271. SetTimer(plv->ci.hwnd, IDT_NAMEEDIT, GetDoubleClickTime(), NULL);
  5272. plv->flags |= LVF_NMEDITPEND;
  5273. }
  5274. void NEAR PASCAL ListView_OnHVScroll(LV* plv, UINT code, int pos, int sb)
  5275. {
  5276. int iScrollCount = 0;
  5277. #ifdef SIF_TRACKPOS
  5278. SCROLLINFO si;
  5279. si.cbSize = sizeof(SCROLLINFO);
  5280. si.fMask = SIF_TRACKPOS;
  5281. // if we're in 32bits, don't trust the pos since it's only 16bit's worth
  5282. if (ListView_GetScrollInfo(plv, sb, &si))
  5283. pos = (int)si.nTrackPos;
  5284. #endif
  5285. ListView_DismissEdit(plv, FALSE);
  5286. _ListView_OnScroll(plv, code, pos, sb);
  5287. switch (code) {
  5288. case SB_PAGELEFT:
  5289. case SB_PAGERIGHT:
  5290. if (plv->iScrollCount < SMOOTHSCROLLLIMIT)
  5291. plv->iScrollCount += 3;
  5292. break;
  5293. case SB_LINELEFT:
  5294. case SB_LINERIGHT:
  5295. if (plv->iScrollCount < SMOOTHSCROLLLIMIT)
  5296. plv->iScrollCount++;
  5297. break;
  5298. case SB_ENDSCROLL:
  5299. plv->iScrollCount = 0;
  5300. break;
  5301. }
  5302. }
  5303. void NEAR ListView_OnVScroll(LV* plv, HWND hwndCtl, UINT code, int pos)
  5304. {
  5305. ListView_OnHVScroll(plv, code, pos, SB_VERT);
  5306. }
  5307. void NEAR ListView_OnHScroll(LV* plv, HWND hwndCtl, UINT code, int pos)
  5308. {
  5309. ListView_OnHVScroll(plv, code, pos, SB_HORZ);
  5310. }
  5311. int ListView_ValidateOneScrollParam(LV* plv, int iDirection, int dx)
  5312. {
  5313. SCROLLINFO si;
  5314. si.cbSize = sizeof(SCROLLINFO);
  5315. si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  5316. if (!ListView_GetScrollInfo(plv, iDirection, &si))
  5317. return 0;
  5318. si.nMax -= (si.nPage - 1);
  5319. si.nPos += dx;
  5320. if (si.nPos < si.nMin) {
  5321. dx += (int)(si.nMin - si.nPos);
  5322. } else if (si.nPos > si.nMax) {
  5323. dx -= (int)(si.nPos - si.nMax);
  5324. }
  5325. return dx;
  5326. }
  5327. BOOL NEAR PASCAL ListView_ValidateScrollParams(LV* plv, int FAR * pdx, int FAR *pdy)
  5328. {
  5329. int dx = *pdx;
  5330. int dy = *pdy;
  5331. if (plv->ci.style & LVS_NOSCROLL)
  5332. return FALSE;
  5333. if (ListView_IsListView(plv))
  5334. {
  5335. ListView_MaybeResizeListColumns(plv, 0, ListView_Count(plv)-1);
  5336. #ifdef COLUMN_VIEW
  5337. if (dx < 0)
  5338. dx = (dx - plv->cxItem - 1) / plv->cxItem;
  5339. else
  5340. dx = (dx + plv->cxItem - 1) / plv->cxItem;
  5341. if (dy)
  5342. return FALSE;
  5343. #else
  5344. if (dy < 0)
  5345. dy = (dy - plv->cyItem - 1) / plv->cyItem;
  5346. else
  5347. dy = (dy + plv->cyItem - 1) / plv->cyItem;
  5348. if (dx)
  5349. return FALSE;
  5350. #endif
  5351. }
  5352. else if (ListView_IsReportView(plv))
  5353. {
  5354. //
  5355. // Note: This function expects that dy is in number of lines
  5356. // and we are working with pixels so do a conversion use some
  5357. // rounding up and down to make it right
  5358. if (dy > 0)
  5359. dy = (dy + plv->cyItem/2) / plv->cyItem;
  5360. else
  5361. dy = (dy - plv->cyItem/2) / plv->cyItem;
  5362. }
  5363. if (dy) {
  5364. dy = ListView_ValidateOneScrollParam(plv, SB_VERT, dy);
  5365. if (ListView_IsReportView(plv)
  5366. #ifndef COLUMN_VIEW
  5367. || ListView_IsListView(plv)
  5368. #endif
  5369. ) {
  5370. // convert back to pixels
  5371. dy *= plv->cyItem;
  5372. }
  5373. *pdy = dy;
  5374. }
  5375. if (dx) {
  5376. dx = ListView_ValidateOneScrollParam(plv, SB_HORZ, dx);
  5377. #ifdef COLUMN_VIEW
  5378. if (ListView_IsListView(plv)) {
  5379. dx *= plv->cxItem;
  5380. }
  5381. #endif
  5382. *pdx = dx;
  5383. }
  5384. return TRUE;
  5385. }
  5386. BOOL NEAR ListView_OnScroll(LV* plv, int dx, int dy)
  5387. {
  5388. if (plv->ci.style & LVS_NOSCROLL)
  5389. return FALSE;
  5390. if (ListView_IsListView(plv))
  5391. {
  5392. // Scale pixel count to column count
  5393. //
  5394. #ifdef COLUMN_VIEW
  5395. if (dx < 0)
  5396. dx -= plv->cxItem - 1;
  5397. else
  5398. dx += plv->cxItem - 1;
  5399. dx = dx / plv->cxItem;
  5400. if (dy)
  5401. return FALSE;
  5402. #else
  5403. if (dy < 0)
  5404. dy -= plv->cyItem - 1;
  5405. else
  5406. dy += plv->cyItem - 1;
  5407. dy = dy / plv->cyItem;
  5408. if (dx)
  5409. return FALSE;
  5410. #endif
  5411. }
  5412. else if (ListView_IsReportView(plv))
  5413. {
  5414. //
  5415. // Note: This function expects that dy is in number of lines
  5416. // and we are working with pixels so do a conversion use some
  5417. // rounding up and down to make it right
  5418. if (dy > 0)
  5419. dy = (dy + plv->cyItem/2) / plv->cyItem;
  5420. else
  5421. dy = (dy - plv->cyItem/2) / plv->cyItem;
  5422. }
  5423. _ListView_Scroll2(plv, dx, dy, 0);
  5424. ListView_UpdateScrollBars(plv);
  5425. return TRUE;
  5426. }
  5427. BOOL NEAR ListView_OnEnsureVisible(LV* plv, int i, BOOL fPartialOK)
  5428. {
  5429. RECT rcBounds;
  5430. RECT rc;
  5431. int dx, dy;
  5432. if (!ListView_IsValidItemNumber(plv, i) || plv->ci.style & LVS_NOSCROLL)
  5433. return FALSE;
  5434. // we need to do this again inside because some callers don't do it.
  5435. // other callers that do this need to do it outside so that
  5436. // they can know not to call us if there's not wait pending
  5437. ListView_CancelScrollWait(plv);
  5438. if (ListView_IsReportView(plv))
  5439. return ListView_ROnEnsureVisible(plv, i, fPartialOK);
  5440. ListView_GetRects(plv, i, &rc, NULL, &rcBounds, NULL);
  5441. if (!fPartialOK)
  5442. rc = rcBounds;
  5443. // If any part of rc is outside of rcClient, then
  5444. // scroll so that all of rcBounds is visible.
  5445. //
  5446. dx = 0;
  5447. if (rc.left < 0 || rc.right >= plv->sizeClient.cx)
  5448. {
  5449. dx = rcBounds.left - 0;
  5450. if (dx >= 0)
  5451. {
  5452. dx = rcBounds.right - plv->sizeClient.cx;
  5453. if (dx <= 0)
  5454. dx = 0;
  5455. else if ((rcBounds.left - dx) < 0)
  5456. dx = rcBounds.left - 0; // Not all fits...
  5457. }
  5458. }
  5459. dy = 0;
  5460. if (rc.top < 0 || rc.bottom >= plv->sizeClient.cy)
  5461. {
  5462. dy = rcBounds.top - 0;
  5463. if (dy >= 0)
  5464. {
  5465. dy = rcBounds.bottom - plv->sizeClient.cy;
  5466. if (dy < 0)
  5467. dy = 0;
  5468. }
  5469. }
  5470. if (dx | dy)
  5471. return ListView_OnScroll(plv, dx, dy);
  5472. return TRUE;
  5473. }
  5474. void NEAR ListView_UpdateScrollBars(LV* plv)
  5475. {
  5476. RECT rc;
  5477. DWORD dwStyle;
  5478. if ((plv->ci.style & LVS_NOSCROLL) ||
  5479. (!(ListView_RedrawEnabled(plv))))
  5480. return;
  5481. _ListView_UpdateScrollBars(plv);
  5482. GetClientRect(plv->ci.hwnd, &rc);
  5483. plv->sizeClient.cx = rc.right;
  5484. plv->sizeClient.cy = rc.bottom;
  5485. dwStyle = ListView_GetWindowStyle(plv);
  5486. plv->ci.style = (plv->ci.style & ~(WS_HSCROLL | WS_VSCROLL)) | (dwStyle & WS_HSCROLL | WS_VSCROLL);
  5487. }
  5488. #ifndef WINNT
  5489. #pragma optimize ("gle", off)
  5490. // Crappy hack for Sage, which passes unitialized memory to SetWindowPlacement.
  5491. // They used to get lucky and get zeros for the max position, but now they end
  5492. // up with non-zero stack trash that causes bad things to happen when sage is
  5493. // maximized. Thus, zero a bunch of stack to save their tail...
  5494. void ZeroSomeStackForSage()
  5495. {
  5496. BYTE aByte[1024];
  5497. memset(aByte, 0, sizeof(aByte));
  5498. aByte;
  5499. }
  5500. #pragma optimize ("", on)
  5501. #endif
  5502. void NEAR ListView_OnSetFont(LV* plv, HFONT hfont, BOOL fRedraw)
  5503. {
  5504. HDC hdc;
  5505. SIZE siz;
  5506. LOGFONT lf;
  5507. HFONT hfontPrev;
  5508. if ((plv->flags & LVF_FONTCREATED) && plv->hfontLabel) {
  5509. DeleteObject(plv->hfontLabel);
  5510. plv->flags &= ~LVF_FONTCREATED;
  5511. }
  5512. if (hfont == NULL) {
  5513. SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
  5514. #ifdef WINNT
  5515. // B#210235 - because NT4 initializes icontitle logfont with Ansi charset
  5516. // no matter what font is selected, following A/W conversion would fail
  5517. // on non US environment if we use this logfont to get codepage.
  5518. // The ACP is guaranteed to work with any Ansi apps because these apps
  5519. // assume ACP to be matching to their desired codepage.
  5520. if (staticIsOS(OS_NT4ORGREATER) && !staticIsOS(OS_WIN2000ORGREATER))
  5521. {
  5522. CHARSETINFO csi;
  5523. TranslateCharsetInfo(IntToPtr_(DWORD *, g_uiACP), &csi, TCI_SRCCODEPAGE);
  5524. lf.lfCharSet = (BYTE)csi.ciCharset;
  5525. }
  5526. #endif
  5527. hfont = CreateFontIndirect(&lf);
  5528. plv->flags |= LVF_FONTCREATED;
  5529. }
  5530. hdc = GetDC(HWND_DESKTOP);
  5531. hfontPrev = SelectFont(hdc, hfont);
  5532. GetTextExtentPoint(hdc, TEXT("0"), 1, &siz);
  5533. plv->cyLabelChar = siz.cy;
  5534. plv->cxLabelChar = siz.cx;
  5535. GetTextExtentPoint(hdc, c_szEllipses, CCHELLIPSES, &siz);
  5536. plv->cxEllipses = siz.cx;
  5537. SelectFont(hdc, hfontPrev);
  5538. ReleaseDC(HWND_DESKTOP, hdc);
  5539. plv->hfontLabel = hfont;
  5540. plv->ci.uiCodePage = GetCodePageForFont(hfont);
  5541. ListView_InvalidateCachedLabelSizes(plv);
  5542. /* Ensure that our tooltip control uses the same font as the list view is using, therefore
  5543. / avoiding any nasty formatting problems. */
  5544. if ( plv->hwndToolTips )
  5545. {
  5546. FORWARD_WM_SETFONT( plv->hwndToolTips, plv->hfontLabel, FALSE, SendMessage );
  5547. }
  5548. // If we have a header window, we need to forward this to it also
  5549. // as we have destroyed the hfont that they are using...
  5550. if (plv->hwndHdr) {
  5551. FORWARD_WM_SETFONT(plv->hwndHdr, plv->hfontLabel, FALSE, SendMessage);
  5552. ListView_UpdateScrollBars(plv);
  5553. }
  5554. if (plv->hFontHot) {
  5555. DeleteObject(plv->hFontHot);
  5556. plv->hFontHot = NULL;
  5557. }
  5558. CCGetHotFont(plv->hfontLabel, &plv->hFontHot);
  5559. plv->iFreeSlot = -1;
  5560. if (fRedraw)
  5561. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  5562. #ifndef WINNT
  5563. ZeroSomeStackForSage();
  5564. #endif
  5565. }
  5566. HFONT NEAR ListView_OnGetFont(LV* plv)
  5567. {
  5568. return plv->hfontLabel;
  5569. }
  5570. // This function process the WM_SETREDRAW message by setting or clearing
  5571. // a bit in the listview structure, which several places in the code will
  5572. // check...
  5573. //
  5574. // REVIEW: Should probably forward to DefWindowProc()
  5575. //
  5576. void NEAR ListView_OnSetRedraw(LV* plv, BOOL fRedraw)
  5577. {
  5578. if (fRedraw)
  5579. {
  5580. BOOL fChanges = FALSE;
  5581. // Only do work if we're turning redraw back on...
  5582. //
  5583. if (!(plv->flags & LVF_REDRAW))
  5584. {
  5585. plv->flags |= LVF_REDRAW;
  5586. // deal with any accumulated invalid regions
  5587. if (plv->hrgnInval)
  5588. {
  5589. UINT fRedraw = (plv->flags & LVF_ERASE) ? RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW : RDW_UPDATENOW|RDW_INVALIDATE;
  5590. if (plv->hrgnInval == (HRGN)ENTIRE_REGION)
  5591. plv->hrgnInval = NULL;
  5592. RedrawWindow(plv->ci.hwnd, NULL, plv->hrgnInval, fRedraw);
  5593. ListView_DeleteHrgnInval(plv);
  5594. fChanges = TRUE;
  5595. }
  5596. plv->flags &= ~LVF_ERASE;
  5597. // now deal with the optimized stuff
  5598. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  5599. {
  5600. if (plv->iFirstChangedNoRedraw != -1)
  5601. {
  5602. // We may try to resize the column
  5603. if (!ListView_MaybeResizeListColumns(plv, plv->iFirstChangedNoRedraw,
  5604. ListView_Count(plv)-1))
  5605. ListView_OnUpdate(plv, plv->iFirstChangedNoRedraw);
  5606. }
  5607. else
  5608. ListView_UpdateScrollBars(plv);
  5609. } else {
  5610. int iCount;
  5611. if (plv->iFirstChangedNoRedraw != -1) {
  5612. for (iCount = ListView_Count(plv) ; plv->iFirstChangedNoRedraw < iCount; plv->iFirstChangedNoRedraw++) {
  5613. ListView_InvalidateItem(plv, plv->iFirstChangedNoRedraw, FALSE, RDW_INVALIDATE);
  5614. }
  5615. fChanges = TRUE;
  5616. }
  5617. if (fChanges)
  5618. ListView_RecalcRegion(plv, TRUE, TRUE);
  5619. if ((plv->ci.style & LVS_AUTOARRANGE) && fChanges) {
  5620. ListView_OnUpdate(plv, plv->iFirstChangedNoRedraw);
  5621. } else {
  5622. ListView_UpdateScrollBars(plv);
  5623. }
  5624. }
  5625. }
  5626. }
  5627. else
  5628. {
  5629. plv->iFirstChangedNoRedraw = -1;
  5630. plv->flags &= ~LVF_REDRAW;
  5631. }
  5632. }
  5633. HIMAGELIST NEAR ListView_OnGetImageList(LV* plv, int iImageList)
  5634. {
  5635. switch (iImageList)
  5636. {
  5637. case LVSIL_NORMAL:
  5638. return plv->himl;
  5639. case LVSIL_SMALL:
  5640. return plv->himlSmall;
  5641. case LVSIL_STATE:
  5642. return plv->himlState;
  5643. }
  5644. ASSERT(0);
  5645. return NULL;
  5646. }
  5647. HIMAGELIST NEAR ListView_OnSetImageList(LV* plv, HIMAGELIST himl, int iImageList)
  5648. {
  5649. HIMAGELIST hImageOld = NULL;
  5650. switch (iImageList)
  5651. {
  5652. case LVSIL_NORMAL:
  5653. hImageOld = plv->himl;
  5654. plv->himl = himl;
  5655. if (himl) {
  5656. ImageList_GetIconSize(himl, &plv->cxIcon , &plv->cyIcon);
  5657. if (!(plv->flags & LVF_ICONSPACESET)) {
  5658. ListView_OnSetIconSpacing(plv, (LPARAM)-1);
  5659. }
  5660. }
  5661. break;
  5662. case LVSIL_SMALL:
  5663. hImageOld = plv->himlSmall;
  5664. plv->himlSmall = himl;
  5665. if (himl)
  5666. ImageList_GetIconSize(himl, &plv->cxSmIcon , &plv->cySmIcon);
  5667. plv->cxItem = 16 * plv->cxLabelChar + plv->cxSmIcon;
  5668. plv->cyItem = ListView_ComputeCYItemSize(plv);
  5669. if (plv->hwndHdr)
  5670. SendMessage(plv->hwndHdr, HDM_SETIMAGELIST, 0, (LPARAM)himl);
  5671. break;
  5672. case LVSIL_STATE:
  5673. if (himl) {
  5674. ImageList_GetIconSize(himl, &plv->cxState , &plv->cyState);
  5675. } else {
  5676. plv->cxState = 0;
  5677. }
  5678. hImageOld = plv->himlState;
  5679. plv->himlState = himl;
  5680. plv->cyItem = ListView_ComputeCYItemSize(plv);
  5681. break;
  5682. default:
  5683. #ifdef LVDEBUG
  5684. DebugMsg(TF_LISTVIEW, TEXT("sh TR - LVM_SETIMAGELIST: unrecognized iImageList"));
  5685. #endif
  5686. break;
  5687. }
  5688. if (himl && !(plv->ci.style & LVS_SHAREIMAGELISTS))
  5689. ImageList_SetBkColor(himl, plv->clrBk);
  5690. if (ListView_Count(plv) > 0)
  5691. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  5692. return hImageOld;
  5693. }
  5694. #ifdef UNICODE
  5695. BOOL NEAR ListView_OnGetItemA(LV* plv, LV_ITEMA *plvi) {
  5696. LPWSTR pszW = NULL;
  5697. LPSTR pszC = NULL;
  5698. BOOL fRet;
  5699. //HACK ALERT -- this code assumes that LV_ITEMA is exactly the same
  5700. // as LV_ITEMW except for the pointer to the string.
  5701. ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW))
  5702. if (!plvi)
  5703. return FALSE;
  5704. if ((plvi->mask & LVIF_TEXT) && (plvi->pszText != NULL)) {
  5705. pszC = plvi->pszText;
  5706. pszW = LocalAlloc(LMEM_FIXED, plvi->cchTextMax * sizeof(WCHAR));
  5707. if (pszW == NULL)
  5708. return FALSE;
  5709. plvi->pszText = (LPSTR)pszW;
  5710. }
  5711. fRet = ListView_OnGetItem(plv, (LV_ITEM *) plvi);
  5712. if (pszW)
  5713. {
  5714. if (plvi->pszText != LPSTR_TEXTCALLBACKA)
  5715. {
  5716. if (fRet && plvi->cchTextMax)
  5717. ConvertWToAN(plv->ci.uiCodePage, pszC, plvi->cchTextMax, (LPWSTR)plvi->pszText, -1);
  5718. plvi->pszText = pszC;
  5719. }
  5720. LocalFree(pszW);
  5721. }
  5722. return fRet;
  5723. }
  5724. #endif
  5725. BOOL NEAR ListView_OnGetItem(LV* plv, LV_ITEM FAR* plvi)
  5726. {
  5727. UINT mask;
  5728. LISTITEM FAR* pitem = NULL;
  5729. LV_DISPINFO nm;
  5730. if (!plvi)
  5731. {
  5732. RIPMSG(0, "LVM_GET(ITEM|ITEMTEXT): Invalid pitem = NULL");
  5733. return FALSE;
  5734. }
  5735. if (!ListView_IsValidItemNumber(plv, plvi->iItem))
  5736. {
  5737. #ifdef DEBUG
  5738. // owner data views (e.g. docfind) may change the number of items in listview
  5739. // while we are doing something, thus hitting this rip
  5740. if (!ListView_IsOwnerData(plv))
  5741. RIPMSG(0, "LVM_GET(ITEM|ITEMTEXT|ITEMSTATE): item=%d does not exist", plvi->iItem);
  5742. #endif
  5743. return FALSE;
  5744. }
  5745. nm.item.mask = 0;
  5746. mask = plvi->mask;
  5747. if (!ListView_IsOwnerData(plv))
  5748. {
  5749. // Standard listviews
  5750. pitem = ListView_FastGetItemPtr(plv, plvi->iItem);
  5751. ASSERT(pitem);
  5752. // Handle sub-item cases for report view
  5753. //
  5754. if (plvi->iSubItem != 0)
  5755. {
  5756. LISTSUBITEM lsi;
  5757. ListView_GetSubItem(plv, plvi->iItem, plvi->iSubItem, &lsi);
  5758. if (mask & LVIF_TEXT)
  5759. {
  5760. if (lsi.pszText != LPSTR_TEXTCALLBACK)
  5761. {
  5762. Str_GetPtr0(lsi.pszText, plvi->pszText, plvi->cchTextMax);
  5763. } else {
  5764. // if this is LVIF_NORECOMPUTE we will update pszText later
  5765. nm.item.mask |= LVIF_TEXT;
  5766. }
  5767. }
  5768. if ((mask & LVIF_IMAGE) && (plv->exStyle & LVS_EX_SUBITEMIMAGES))
  5769. {
  5770. plvi->iImage = lsi.iImage;
  5771. if (lsi.iImage == I_IMAGECALLBACK)
  5772. nm.item.mask |= LVIF_IMAGE;
  5773. }
  5774. if (mask & LVIF_STATE) {
  5775. if (ListView_FullRowSelect(plv)) {
  5776. // if we're in full row select,
  5777. // the state bit for select and focus follows column 0.
  5778. lsi.state |= pitem->state & (LVIS_SELECTED | LVIS_FOCUSED | LVIS_DROPHILITED);
  5779. }
  5780. plvi->state = lsi.state & plvi->stateMask;
  5781. if (plv->stateCallbackMask)
  5782. {
  5783. nm.item.stateMask = (plvi->stateMask & plv->stateCallbackMask);
  5784. if (nm.item.stateMask)
  5785. {
  5786. nm.item.mask |= LVIF_STATE;
  5787. nm.item.state = 0;
  5788. }
  5789. }
  5790. }
  5791. } else {
  5792. if (mask & LVIF_TEXT)
  5793. {
  5794. if (pitem->pszText != LPSTR_TEXTCALLBACK)
  5795. {
  5796. Str_GetPtr0(pitem->pszText, plvi->pszText, plvi->cchTextMax);
  5797. } else {
  5798. // if this is LVIF_NORECOMPUTE we will update pszText later
  5799. nm.item.mask |= LVIF_TEXT;
  5800. }
  5801. }
  5802. if (mask & LVIF_IMAGE)
  5803. {
  5804. plvi->iImage = pitem->iImage;
  5805. if (pitem->iImage == I_IMAGECALLBACK)
  5806. nm.item.mask |= LVIF_IMAGE;
  5807. }
  5808. if (mask & LVIF_INDENT)
  5809. {
  5810. plvi->iIndent = pitem->iIndent;
  5811. if (pitem->iIndent == I_INDENTCALLBACK)
  5812. nm.item.mask |= LVIF_INDENT;
  5813. }
  5814. if (mask & LVIF_STATE)
  5815. {
  5816. plvi->state = (pitem->state & plvi->stateMask);
  5817. if (plv->stateCallbackMask)
  5818. {
  5819. nm.item.stateMask = (plvi->stateMask & plv->stateCallbackMask);
  5820. if (nm.item.stateMask)
  5821. {
  5822. nm.item.mask |= LVIF_STATE;
  5823. nm.item.state = 0;
  5824. }
  5825. }
  5826. }
  5827. }
  5828. if (mask & LVIF_PARAM)
  5829. plvi->lParam = pitem->lParam;
  5830. }
  5831. else
  5832. {
  5833. // Complete call back for info...
  5834. // Handle sub-item cases for report view
  5835. //
  5836. if (plvi->iSubItem != 0)
  5837. {
  5838. // if there are no subitem images, don't query for them
  5839. if (!(plv->exStyle & LVS_EX_SUBITEMIMAGES))
  5840. mask &= ~LVIF_IMAGE;
  5841. // don't allow indent on the non-0th column
  5842. mask &= ~LVIF_INDENT;
  5843. }
  5844. if (mask & LVIF_PARAM)
  5845. plvi->lParam = 0L; // Dont have any to return now...
  5846. if (mask & LVIF_STATE)
  5847. {
  5848. plvi->state = 0;
  5849. if ((plvi->iSubItem == 0) || ListView_FullRowSelect(plv))
  5850. {
  5851. if (plvi->iItem == plv->iFocus)
  5852. plvi->state |= LVIS_FOCUSED;
  5853. if (plv->plvrangeSel->lpVtbl->IsSelected(plv->plvrangeSel, plvi->iItem) == S_OK)
  5854. plvi->state |= LVIS_SELECTED;
  5855. if (plv->plvrangeCut->lpVtbl->IsSelected(plv->plvrangeCut, plvi->iItem) == S_OK)
  5856. plvi->state |= LVIS_CUT;
  5857. if (plvi->iItem == plv->iDropHilite)
  5858. plvi->state |= LVIS_DROPHILITED;
  5859. plvi->state &= plvi->stateMask;
  5860. }
  5861. if (plv->stateCallbackMask)
  5862. {
  5863. nm.item.stateMask = (plvi->stateMask & plv->stateCallbackMask);
  5864. if (nm.item.stateMask)
  5865. {
  5866. nm.item.mask |= LVIF_STATE;
  5867. nm.item.state = 0;
  5868. }
  5869. }
  5870. }
  5871. nm.item.mask |= (mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_INDENT));
  5872. }
  5873. if (mask & LVIF_NORECOMPUTE)
  5874. {
  5875. if (nm.item.mask & LVIF_TEXT)
  5876. plvi->pszText = LPSTR_TEXTCALLBACK;
  5877. }
  5878. else if (nm.item.mask)
  5879. {
  5880. nm.item.iItem = plvi->iItem;
  5881. nm.item.iSubItem = plvi->iSubItem;
  5882. if (ListView_IsOwnerData( plv ))
  5883. nm.item.lParam = 0L;
  5884. else
  5885. nm.item.lParam = pitem->lParam;
  5886. // just in case LVIF_IMAGE is set and callback doesn't fill it in
  5887. // ... we'd rather have a -1 than whatever garbage is on the stack
  5888. nm.item.iImage = -1;
  5889. nm.item.iIndent = 0;
  5890. if (nm.item.mask & LVIF_TEXT)
  5891. {
  5892. RIPMSG(plvi->pszText != NULL, "LVM_GET(ITEM|ITEMTEXT) null string pointer");
  5893. if (plvi->pszText) {
  5894. nm.item.pszText = plvi->pszText;
  5895. nm.item.cchTextMax = plvi->cchTextMax;
  5896. // Make sure the buffer is zero terminated...
  5897. if (nm.item.cchTextMax)
  5898. *nm.item.pszText = 0;
  5899. } else {
  5900. // Don't make caller smash null pointer
  5901. nm.item.mask &= ~LVIF_TEXT;
  5902. }
  5903. }
  5904. CCSendNotify(&plv->ci, LVN_GETDISPINFO, &nm.hdr);
  5905. // use nm.item.mask to give the app a chance to change values
  5906. if (nm.item.mask & LVIF_INDENT)
  5907. plvi->iIndent = nm.item.iIndent;
  5908. if (nm.item.mask & LVIF_STATE)
  5909. plvi->state ^= ((plvi->state ^ nm.item.state) & nm.item.stateMask);
  5910. if (nm.item.mask & LVIF_IMAGE)
  5911. plvi->iImage = nm.item.iImage;
  5912. if (nm.item.mask & LVIF_TEXT)
  5913. if (mask & LVIF_TEXT)
  5914. plvi->pszText = CCReturnDispInfoText(nm.item.pszText, plvi->pszText, plvi->cchTextMax);
  5915. else
  5916. plvi->pszText = nm.item.pszText;
  5917. if (pitem && (nm.item.mask & LVIF_DI_SETITEM))
  5918. {
  5919. // BUGBUG HACKHACK
  5920. //
  5921. // The SendNotify above can set about a terrible series of events
  5922. // whereby asking for DISPINFO causes the shell to look around
  5923. // (call peekmessage) to see if its got a new async icon for the
  5924. // listview. This lets other messages be delivered, such as an
  5925. // UPDATEIMAGE of Index == -1 (if the user is changing icon sizing
  5926. // at the same time). This causes a re-enumeration of the desktop
  5927. // and hence this very listview is torn down and rebuilt while
  5928. // we're sitting here for the DISPINFO to finish. Thus, as a cheap
  5929. // and dirty solution, I check to see if the item I think I have
  5930. // is the same one I had when I made the notify, and if not, I
  5931. // bail. Don't blame me, I'm just cleaning up the mess.
  5932. if (!EVAL(pitem == ListView_GetItemPtr(plv, plvi->iItem)))
  5933. {
  5934. return FALSE;
  5935. }
  5936. if (nm.item.iSubItem == 0)
  5937. {
  5938. //DebugMsg(TF_LISTVIEW, "SAVING ITEMS!");
  5939. if (nm.item.mask & LVIF_IMAGE)
  5940. pitem->iImage = (short) nm.item.iImage;
  5941. if (nm.item.mask & LVIF_INDENT)
  5942. pitem->iIndent = (short) nm.item.iIndent;
  5943. if (nm.item.mask & LVIF_TEXT)
  5944. if (nm.item.pszText) {
  5945. Str_Set(&pitem->pszText, nm.item.pszText);
  5946. }
  5947. if (nm.item.mask & LVIF_STATE)
  5948. pitem->state ^= ((pitem->state ^ nm.item.state) & nm.item.stateMask);
  5949. }
  5950. else
  5951. {
  5952. ListView_SetSubItem(plv, &nm.item);
  5953. }
  5954. }
  5955. }
  5956. return TRUE;
  5957. }
  5958. #ifdef UNICODE
  5959. BOOL NEAR ListView_OnSetItemA(LV* plv, LV_ITEMA FAR* plvi) {
  5960. LPWSTR pszW = NULL;
  5961. LPSTR pszC = NULL;
  5962. BOOL fRet;
  5963. // Let ListView_OnSetItem() handle owner-data validation
  5964. //HACK ALERT -- this code assumes that LV_ITEMA is exactly the same
  5965. // as LV_ITEMW except for the pointer to the string.
  5966. ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW));
  5967. if (!plvi)
  5968. return FALSE;
  5969. if ((plvi->mask & LVIF_TEXT) && (plvi->pszText != NULL)) {
  5970. pszC = plvi->pszText;
  5971. pszW = ProduceWFromA(plv->ci.uiCodePage, pszC);
  5972. if (pszW == NULL)
  5973. return FALSE;
  5974. plvi->pszText = (LPSTR)pszW;
  5975. }
  5976. fRet = ListView_OnSetItem(plv, (const LV_ITEM FAR*) plvi);
  5977. if (pszW != NULL) {
  5978. plvi->pszText = pszC;
  5979. FreeProducedString(pszW);
  5980. }
  5981. return fRet;
  5982. }
  5983. #endif
  5984. BOOL NEAR ListView_OnSetItem(LV* plv, const LV_ITEM FAR* plvi)
  5985. {
  5986. LISTITEM FAR* pitem = NULL;
  5987. UINT mask;
  5988. UINT maskChanged;
  5989. UINT rdwFlags=RDW_INVALIDATE;
  5990. int i;
  5991. UINT stateOld, stateNew;
  5992. BOOL fFocused = FALSE;
  5993. BOOL fSelected = FALSE;
  5994. BOOL fStateImageChanged = FALSE;
  5995. if (ListView_IsOwnerData(plv)) {
  5996. RIPMSG(0, "LVM_SETITEM: Invalid for owner-data listview");
  5997. return FALSE;
  5998. }
  5999. if (!plvi)
  6000. return FALSE;
  6001. ASSERT(plvi->iSubItem >= 0);
  6002. if (plv->himl && (plv->clrBk != ImageList_GetBkColor(plv->himl)))
  6003. rdwFlags |= RDW_ERASE;
  6004. mask = plvi->mask;
  6005. if (!mask)
  6006. return TRUE;
  6007. // If we're setting a subitem, handle it elsewhere...
  6008. //
  6009. if (plvi->iSubItem > 0)
  6010. return ListView_SetSubItem(plv, plvi);
  6011. i = plvi->iItem;
  6012. ListView_InvalidateTTLastHit(plv, i);
  6013. pitem = ListView_GetItemPtr(plv, i);
  6014. if (!pitem)
  6015. return FALSE;
  6016. //REVIEW: This is a BOGUS HACK, and should be fixed.
  6017. //This incorrectly calculates the old state (since we may
  6018. // have to send LVN_GETDISPINFO to get it).
  6019. //
  6020. stateOld = stateNew = 0;
  6021. if (mask & LVIF_STATE)
  6022. {
  6023. stateOld = pitem->state & plvi->stateMask;
  6024. stateNew = plvi->state & plvi->stateMask;
  6025. }
  6026. // Prevent multiple selections in a single-select listview.
  6027. if ((plv->ci.style & LVS_SINGLESEL) && (mask & LVIF_STATE) && (stateNew & LVIS_SELECTED)) {
  6028. ListView_DeselectAll(plv, i);
  6029. // Refresh the old state information
  6030. stateOld = pitem->state & plvi->stateMask;
  6031. }
  6032. if (!ListView_SendChange(plv, i, 0, LVN_ITEMCHANGING, stateOld, stateNew, mask, pitem->lParam))
  6033. return FALSE;
  6034. maskChanged = 0;
  6035. if (mask & LVIF_STATE)
  6036. {
  6037. UINT change = (pitem->state ^ plvi->state) & plvi->stateMask;
  6038. if (change)
  6039. {
  6040. pitem->state ^= change;
  6041. maskChanged |= LVIF_STATE;
  6042. // the selection state has changed.. update selected count
  6043. if (change & LVIS_SELECTED)
  6044. {
  6045. fSelected = TRUE;
  6046. if (pitem->state & LVIS_SELECTED) {
  6047. plv->nSelected++;
  6048. } else {
  6049. if (plv->nSelected > 0)
  6050. plv->nSelected--;
  6051. }
  6052. }
  6053. // For some bits we can only invert the label area...
  6054. // fSelectOnlyChange = ((change & ~(LVIS_SELECTED | LVIS_FOCUSED | LVIS_DROPHILITED)) == 0);
  6055. // fEraseItem = ((change & ~(LVIS_SELECTED | LVIS_DROPHILITED)) != 0);
  6056. // try to steal focus from the previous guy.
  6057. if (change & LVIS_FOCUSED)
  6058. {
  6059. BOOL fUnfolded = ListView_IsItemUnfolded(plv, plv->iFocus);
  6060. int iOldFocus = plv->iFocus;
  6061. RECT rcLabel;
  6062. fFocused = TRUE;
  6063. if (plv->iFocus != i) {
  6064. if ((plv->iFocus == -1) || ListView_OnSetItemState(plv, plv->iFocus, 0, LVIS_FOCUSED)) {
  6065. ASSERT(pitem->state & LVIS_FOCUSED);
  6066. plv->iFocus = i;
  6067. if (plv->iMark == -1)
  6068. plv->iMark = i;
  6069. } else {
  6070. fFocused = FALSE;
  6071. pitem->state &= ~LVIS_FOCUSED;
  6072. }
  6073. } else {
  6074. ASSERT(!(pitem->state & LVIS_FOCUSED));
  6075. plv->iFocus = -1;
  6076. }
  6077. // If we were previously unfolded and we move the focus we must
  6078. // attempt to refresh the previous focus owner to referect this change.
  6079. if (fUnfolded && !ListView_IsItemUnfolded(plv, iOldFocus) && (plv->iItemDrawing != iOldFocus))
  6080. {
  6081. ListView_GetUnfoldedRect(plv, iOldFocus, &rcLabel);
  6082. RedrawWindow(plv->ci.hwnd, &rcLabel, NULL, RDW_INVALIDATE|RDW_ERASE);
  6083. }
  6084. // Kill the tooltip if focus moves, it causes us headaches otherwise!
  6085. ListView_PopBubble(plv);
  6086. }
  6087. if (change & LVIS_CUT ||
  6088. plv->clrTextBk == CLR_NONE)
  6089. rdwFlags |= RDW_ERASE;
  6090. if (change & LVIS_OVERLAYMASK) {
  6091. // Overlay changed, so need to blow away icon region cache
  6092. if (pitem->hrgnIcon) {
  6093. if (pitem->hrgnIcon != (HANDLE) -1)
  6094. DeleteObject(pitem->hrgnIcon);
  6095. pitem->hrgnIcon = NULL;
  6096. }
  6097. }
  6098. fStateImageChanged = (change & LVIS_STATEIMAGEMASK);
  6099. }
  6100. }
  6101. if (mask & LVIF_TEXT)
  6102. {
  6103. // need to do this now because we're changing the text
  6104. // so we need to get the rect of the thing before the text changes
  6105. // but don't redraw the item we are currently painting
  6106. if (plv->iItemDrawing != i)
  6107. {
  6108. ListView_InvalidateItemEx(plv, i, FALSE,
  6109. RDW_INVALIDATE | RDW_ERASE, LVIF_TEXT);
  6110. }
  6111. if (!Str_Set(&pitem->pszText, plvi->pszText))
  6112. return FALSE;
  6113. plv->rcView.left = RECOMPUTE;
  6114. ListView_SetSRecompute(pitem);
  6115. maskChanged |= LVIF_TEXT;
  6116. }
  6117. if (mask & LVIF_INDENT) {
  6118. if (pitem->iIndent != plvi->iIndent)
  6119. {
  6120. pitem->iIndent = (short) plvi->iIndent;
  6121. maskChanged |= LVIF_INDENT;
  6122. if (ListView_IsReportView(plv))
  6123. rdwFlags |= RDW_ERASE;
  6124. }
  6125. }
  6126. if (mask & LVIF_IMAGE)
  6127. {
  6128. if (pitem->iImage != plvi->iImage)
  6129. {
  6130. pitem->iImage = (short) plvi->iImage;
  6131. maskChanged |= LVIF_IMAGE;
  6132. if (pitem->hrgnIcon) {
  6133. if (pitem->hrgnIcon != (HANDLE) -1)
  6134. DeleteObject(pitem->hrgnIcon);
  6135. pitem->hrgnIcon = NULL;
  6136. }
  6137. // erase if there was a set image
  6138. if (pitem->iImage != I_IMAGECALLBACK)
  6139. rdwFlags |= RDW_ERASE;
  6140. }
  6141. }
  6142. if (mask & LVIF_PARAM)
  6143. {
  6144. if (pitem->lParam != plvi->lParam)
  6145. {
  6146. pitem->lParam = plvi->lParam;
  6147. maskChanged |= LVIF_PARAM;
  6148. }
  6149. }
  6150. if (maskChanged)
  6151. {
  6152. // don't redraw the item we are currently painting
  6153. if (plv->iItemDrawing != i)
  6154. ListView_InvalidateItemEx(plv, i, FALSE, rdwFlags, maskChanged);
  6155. DebugMsg(DM_LVSENDCHANGE, TEXT("LV - SendChange %d %d %d %d"), i, stateOld, stateNew, maskChanged);
  6156. ListView_SendChange(plv, i, 0, LVN_ITEMCHANGED, stateOld, stateNew, maskChanged, pitem->lParam);
  6157. if (maskChanged & LVIF_TEXT)
  6158. MyNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, plv->ci.hwnd, OBJID_CLIENT, i+1);
  6159. if (maskChanged & LVIF_STATE)
  6160. {
  6161. if (fFocused)
  6162. ListView_NotifyFocusEvent(plv);
  6163. if (fSelected)
  6164. {
  6165. if (stateNew & LVIS_SELECTED)
  6166. {
  6167. MyNotifyWinEvent((plv->nSelected == 1) ? EVENT_OBJECT_SELECTION :
  6168. EVENT_OBJECT_SELECTIONADD, plv->ci.hwnd, OBJID_CLIENT, i+1);
  6169. }
  6170. else
  6171. {
  6172. MyNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE, plv->ci.hwnd, OBJID_CLIENT, i+1);
  6173. }
  6174. }
  6175. if (fStateImageChanged)
  6176. MyNotifyWinEvent(EVENT_OBJECT_STATECHANGE, plv->ci.hwnd, OBJID_CLIENT, i+1);
  6177. }
  6178. }
  6179. return TRUE;
  6180. }
  6181. UINT NEAR PASCAL ListView_OnGetItemState(LV* plv, int i, UINT mask)
  6182. {
  6183. LV_ITEM lvi;
  6184. lvi.mask = LVIF_STATE;
  6185. lvi.stateMask = mask;
  6186. lvi.iItem = i;
  6187. lvi.iSubItem = 0;
  6188. if (!ListView_OnGetItem(plv, &lvi))
  6189. return 0;
  6190. return lvi.state;
  6191. }
  6192. BOOL NEAR PASCAL ListView_OnSetItemState(LV* plv, int i, UINT data, UINT mask)
  6193. {
  6194. UINT rdwFlags = RDW_INVALIDATE;
  6195. LV_ITEM lvi;
  6196. lvi.mask = LVIF_STATE;
  6197. lvi.state = data;
  6198. lvi.stateMask = mask;
  6199. lvi.iItem = i;
  6200. lvi.iSubItem = 0;
  6201. // if the item is -1, we will do it for all items. We special case
  6202. // a few cases here as to speed it up. For example if the mask is
  6203. // LVIS_SELECTED and data is zero it implies that we will deselect
  6204. // all items...
  6205. //
  6206. if (ListView_IsOwnerData( plv ))
  6207. {
  6208. UINT uOldData = 0;
  6209. // these are the only two we handled
  6210. mask &= (LVIS_SELECTED | LVIS_FOCUSED | LVIS_CUT | LVIS_DROPHILITED);
  6211. if (!mask)
  6212. return TRUE;
  6213. if (plv->clrTextBk == CLR_NONE
  6214. || (plv->himl && (plv->clrBk != ImageList_GetBkColor(plv->himl))))
  6215. {
  6216. rdwFlags |= RDW_ERASE;
  6217. }
  6218. if (i == -1)
  6219. {
  6220. // request selection state change for all
  6221. if (mask & LVIS_SELECTED)
  6222. {
  6223. if (data & LVIS_SELECTED)
  6224. { // set selection
  6225. if ((plv->ci.style & LVS_SINGLESEL))
  6226. { // cant make multiple selections in a single-select listview.
  6227. return( FALSE );
  6228. }
  6229. if (plv->cTotalItems)
  6230. {
  6231. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, 0, plv->cTotalItems - 1)))
  6232. return( FALSE );
  6233. }
  6234. RedrawWindow( plv->ci.hwnd, NULL, NULL, rdwFlags );
  6235. }
  6236. else
  6237. { // clear selection
  6238. if (plv->nSelected > 0) {
  6239. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeSel);
  6240. if (FAILED(plv->plvrangeSel->lpVtbl->Clear( plv->plvrangeSel )))
  6241. return FALSE;
  6242. } else {
  6243. // if nothing was selected, then there's nothing to clear
  6244. // no change.
  6245. mask &= ~ LVIS_SELECTED;
  6246. }
  6247. }
  6248. uOldData |= (LVIS_SELECTED & (mask ^ data));
  6249. // Update our internal count to what the list thinks is the number selected...
  6250. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  6251. }
  6252. // can maybe combine with above code...
  6253. if (mask & LVIS_CUT)
  6254. {
  6255. if (data & LVIS_CUT)
  6256. { // set selection
  6257. if (plv->cTotalItems)
  6258. if (FAILED(plv->plvrangeCut->lpVtbl->IncludeRange(plv->plvrangeCut, 0, plv->cTotalItems - 1)))
  6259. return( FALSE );
  6260. RedrawWindow( plv->ci.hwnd, NULL, NULL, rdwFlags );
  6261. }
  6262. else
  6263. { // clear selection
  6264. if (plv->plvrangeCut->lpVtbl->IsEmpty(plv->plvrangeCut) != S_OK) {
  6265. ListView_InvalidateSelectedOrCutOwnerData(plv, plv->plvrangeCut);
  6266. if (FAILED(plv->plvrangeCut->lpVtbl->Clear( plv->plvrangeCut)))
  6267. return( FALSE );
  6268. } else {
  6269. // if nothing was selected, then there's nothing to clear
  6270. // no change.
  6271. mask &= ~ LVIS_CUT;
  6272. }
  6273. }
  6274. uOldData |= (LVIS_CUT & (mask ^ data));
  6275. }
  6276. // request focus state change
  6277. if (mask & LVIS_FOCUSED)
  6278. {
  6279. if (data & LVIS_FOCUSED)
  6280. { // cant set focus to all
  6281. return( FALSE );
  6282. }
  6283. else if (plv->iFocus != -1)
  6284. {
  6285. int iOldFocus = plv->iFocus;
  6286. // clear focus
  6287. uOldData |= (LVIS_FOCUSED & (mask ^ data));
  6288. plv->iFocus = -1;
  6289. // notify that the old focus is being lost
  6290. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), iOldFocus, LVIS_FOCUSED, 0);
  6291. ListView_SendChange(plv, iOldFocus, 0, LVN_ITEMCHANGED, LVIS_FOCUSED, 0, LVIF_STATE, 0);
  6292. ListView_InvalidateFoldedItem(plv, iOldFocus, TRUE, RDW_INVALIDATE |RDW_ERASE);
  6293. }
  6294. }
  6295. if (mask & LVIS_DROPHILITED)
  6296. {
  6297. if (data & LVIS_DROPHILITED)
  6298. { // cant set focus to all
  6299. return( FALSE );
  6300. }
  6301. else if (plv->iDropHilite != -1)
  6302. {
  6303. int iOldDropHilite = plv->iDropHilite;
  6304. // clear focus
  6305. uOldData |= (LVIS_FOCUSED & (mask ^ data));
  6306. plv->iDropHilite = -1;
  6307. // notify that the old focus is being lost
  6308. ListView_SendChange(plv, iOldDropHilite, 0, LVN_ITEMCHANGED, LVIS_DROPHILITED, 0, LVIF_STATE, 0);
  6309. ListView_InvalidateFoldedItem(plv, iOldDropHilite, TRUE, RDW_INVALIDATE |RDW_ERASE);
  6310. }
  6311. }
  6312. // invalidate and notify if there was a change
  6313. if (uOldData ^ (data & mask)) {
  6314. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), i, uOldData, data);
  6315. ListView_SendChange(plv, i, 0, LVN_ITEMCHANGED, uOldData, data, LVIF_STATE, 0);
  6316. if (mask & LVIS_SELECTED)
  6317. {
  6318. // Tell accessibility, "Selection changed in a complex way"
  6319. // (There is no "select all" or "select none" notification)
  6320. MyNotifyWinEvent(EVENT_OBJECT_SELECTIONWITHIN, plv->ci.hwnd, OBJID_CLIENT, CHILDID_SELF);
  6321. }
  6322. }
  6323. }
  6324. else
  6325. {
  6326. if (!ListView_IsValidItemNumber(plv, i))
  6327. return (FALSE);
  6328. // request selection state change
  6329. // and the selection state is new...
  6330. if ((mask & LVIS_SELECTED)) {
  6331. if (((plv->plvrangeSel->lpVtbl->IsSelected(plv->plvrangeSel, i) == S_OK) ? LVIS_SELECTED : 0) ^ (data & LVIS_SELECTED))
  6332. {
  6333. if (data & LVIS_SELECTED)
  6334. { // set selection
  6335. if ((plv->ci.style & LVS_SINGLESEL))
  6336. {
  6337. // in single selection mode, we need to deselect everything else
  6338. if (!ListView_OnSetItemState(plv, -1, 0, LVIS_SELECTED))
  6339. return FALSE;
  6340. }
  6341. // now select the new item
  6342. if (FAILED(plv->plvrangeSel->lpVtbl->IncludeRange(plv->plvrangeSel, i, i)))
  6343. return FALSE;
  6344. }
  6345. else
  6346. { // clear selection
  6347. if (FAILED(plv->plvrangeSel->lpVtbl->ExcludeRange(plv->plvrangeSel, i, i )))
  6348. return( FALSE );
  6349. }
  6350. // something actually changed (or else we wouldn't be in this
  6351. // if block
  6352. uOldData |= (LVIS_SELECTED & (mask ^ data));
  6353. } else {
  6354. // nothing changed... so make the uOldData be the same for this bit
  6355. // else make this the same as
  6356. uOldData |= (LVIS_SELECTED & (mask & data));
  6357. }
  6358. // Update our internal count to what the list thinks is the number selected...
  6359. plv->plvrangeSel->lpVtbl->CountIncluded(plv->plvrangeSel, &plv->nSelected);
  6360. }
  6361. if ((mask & LVIS_CUT)) {
  6362. if (((plv->plvrangeCut->lpVtbl->IsSelected(plv->plvrangeCut, i) == S_OK) ? LVIS_CUT : 0) ^ (data & LVIS_CUT))
  6363. {
  6364. if (data & LVIS_CUT)
  6365. {
  6366. // now select the new item
  6367. if (FAILED(plv->plvrangeCut->lpVtbl->IncludeRange(plv->plvrangeCut, i, i )))
  6368. return FALSE;
  6369. }
  6370. else
  6371. { // clear selection
  6372. if (FAILED(plv->plvrangeCut->lpVtbl->ExcludeRange(plv->plvrangeCut, i, i )))
  6373. return( FALSE );
  6374. }
  6375. // something actually changed (or else we wouldn't be in this
  6376. // if block
  6377. uOldData |= (LVIS_CUT & (mask ^ data));
  6378. rdwFlags |= RDW_ERASE;
  6379. } else {
  6380. // nothing changed... so make the uOldData be the same for this bit
  6381. // else make this the same as
  6382. uOldData |= (LVIS_CUT & (mask & data));
  6383. }
  6384. }
  6385. // request focus state change
  6386. if (mask & LVIS_FOCUSED)
  6387. {
  6388. int iOldFocus = plv->iFocus;
  6389. if (data & LVIS_FOCUSED)
  6390. { // set focus
  6391. if (i != plv->iFocus)
  6392. {
  6393. // we didn't have the focus before
  6394. plv->iFocus = i;
  6395. if (plv->iMark == -1)
  6396. plv->iMark = i;
  6397. if (iOldFocus != -1) {
  6398. // we're stealing it from someone
  6399. // notify of the change
  6400. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), iOldFocus, LVIS_FOCUSED, 0);
  6401. ListView_SendChange(plv, iOldFocus, 0, LVN_ITEMCHANGED, LVIS_FOCUSED, 0, LVIF_STATE, 0);
  6402. }
  6403. } else {
  6404. // we DID have the focus before
  6405. uOldData |= LVIS_FOCUSED;
  6406. }
  6407. }
  6408. else
  6409. { // clear focus
  6410. if (i == plv->iFocus)
  6411. {
  6412. plv->iFocus = -1;
  6413. uOldData |= LVIS_FOCUSED;
  6414. }
  6415. }
  6416. }
  6417. // request focus state change
  6418. if (mask & LVIS_DROPHILITED)
  6419. {
  6420. int iOldDropHilite = plv->iDropHilite;
  6421. if (data & LVIS_DROPHILITED)
  6422. { // set Drop Hilite
  6423. if (i != plv->iDropHilite)
  6424. {
  6425. // we didn't have the Drop Hilite before
  6426. plv->iDropHilite = i;
  6427. if (iOldDropHilite != -1) {
  6428. // we're stealing it from someone
  6429. // notify of the change
  6430. ListView_SendChange(plv, iOldDropHilite, 0, LVN_ITEMCHANGED, LVIS_DROPHILITED, 0, LVIF_STATE, 0);
  6431. ListView_InvalidateFoldedItem(plv, iOldDropHilite, TRUE, RDW_INVALIDATE |RDW_ERASE);
  6432. }
  6433. } else {
  6434. // we DID have the Drop Hilite before
  6435. uOldData |= LVIS_DROPHILITED;
  6436. }
  6437. }
  6438. else
  6439. { // clear Drop Hilite
  6440. if (i == plv->iDropHilite)
  6441. {
  6442. plv->iDropHilite = -1;
  6443. uOldData |= LVIS_DROPHILITED;
  6444. }
  6445. }
  6446. }
  6447. // invalidate and notify if there was a change
  6448. if (uOldData ^ (data & mask)) {
  6449. DebugMsg(DM_LVSENDCHANGE, TEXT("VLV: LVN_ITEMCHANGED: %d %d %d"), i, uOldData, data);
  6450. ListView_SendChange(plv, i, 0, LVN_ITEMCHANGED, uOldData, data, LVIF_STATE, 0);
  6451. ListView_InvalidateItem(plv, i, TRUE, rdwFlags);
  6452. // Kill the tooltip if focus moves, it causes us headaches otherwise!
  6453. if ((uOldData ^ (data & mask)) & LVIS_FOCUSED)
  6454. {
  6455. ListView_PopBubble(plv);
  6456. ListView_NotifyFocusEvent(plv);
  6457. }
  6458. // Tell accessibility about the changes
  6459. if (mask & LVIS_SELECTED) {
  6460. UINT event;
  6461. if (data & LVIS_SELECTED) {
  6462. if (plv->nSelected == 1)
  6463. event = EVENT_OBJECT_SELECTION; // this object is the entire selection
  6464. else
  6465. event = EVENT_OBJECT_SELECTIONADD; // this object is selected
  6466. } else
  6467. event = EVENT_OBJECT_SELECTIONREMOVE; // this object is unselected
  6468. MyNotifyWinEvent(event, plv->ci.hwnd, OBJID_CLIENT, i + 1);
  6469. }
  6470. }
  6471. }
  6472. } else {
  6473. if (i != -1) {
  6474. return ListView_OnSetItem(plv, &lvi);
  6475. } else {
  6476. UINT flags = LVNI_ALL;
  6477. if (data == 0)
  6478. {
  6479. switch (mask)
  6480. {
  6481. case LVIS_SELECTED:
  6482. flags = LVNI_SELECTED;
  6483. break;
  6484. case LVIS_CUT:
  6485. flags = LVNI_CUT;
  6486. break;
  6487. }
  6488. }
  6489. else if ((plv->ci.style & LVS_SINGLESEL) && (mask == LVIS_SELECTED))
  6490. return FALSE; /* can't select all in single-select listview */
  6491. else if ((mask & data) & LVIS_FOCUSED) {
  6492. return FALSE; // can't set focus to everything
  6493. }
  6494. //
  6495. // Now iterate over all of the items that match our criteria and
  6496. // set their new value.
  6497. //
  6498. while ((lvi.iItem = ListView_OnGetNextItem(plv, lvi.iItem,
  6499. flags)) != -1) {
  6500. ListView_OnSetItem(plv, &lvi);
  6501. }
  6502. }
  6503. }
  6504. return(TRUE);
  6505. }
  6506. //
  6507. // Returns TRUE if the label of an item is not truncated (is unfolded) and FALSE
  6508. // otherwise. If FALSE is returned, it also fills the Unfolding text in pszText.
  6509. // If TRUE is returned, pszText is set to empty string.
  6510. //
  6511. BOOL ListView_IsItemUnfolded2(LV* plv, int iItem, int iSubItem, LPTSTR pszText, int cchTextMax)
  6512. {
  6513. BOOL bItemUnfolded = ListView_IsItemUnfolded(plv, iItem);
  6514. if (pszText && cchTextMax > 0) // Sanity checks on input params.
  6515. {
  6516. pszText[0] = 0;
  6517. if (!bItemUnfolded)
  6518. {
  6519. RECT rcLabel;
  6520. LV_ITEM item;
  6521. item.iItem = iItem;
  6522. item.iSubItem = iSubItem;
  6523. item.mask = LVIF_TEXT | LVIF_PARAM;
  6524. if (!ListView_IsIconView(plv))
  6525. {
  6526. if (ListView_IsLabelTip(plv) || ListView_IsInfoTip(plv))
  6527. {
  6528. BOOL fSuccess;
  6529. rcLabel.left = LVIR_LABEL;
  6530. if (iSubItem) {
  6531. rcLabel.top = iSubItem;
  6532. fSuccess = ListView_OnGetSubItemRect(plv, iItem, &rcLabel);
  6533. } else {
  6534. fSuccess = ListView_OnGetItemRect(plv, iItem, &rcLabel);
  6535. }
  6536. if (fSuccess)
  6537. {
  6538. TCHAR szText[INFOTIPSIZE];
  6539. item.pszText = szText;
  6540. item.cchTextMax = min(ARRAYSIZE(szText), cchTextMax);
  6541. if (ListView_OnGetItem(plv, &item) && item.pszText != LPSTR_TEXTCALLBACK)
  6542. {
  6543. SIZE siz;
  6544. LVFAKEDRAW lvfd;
  6545. int cx;
  6546. ListView_BeginFakeCustomDraw(plv, &lvfd, &item);
  6547. ListView_BeginFakeItemDraw(&lvfd);
  6548. // ---------Label width----------- ---Client width---
  6549. cx = min(rcLabel.right - g_cxLabelMargin, plv->sizeClient.cx);
  6550. if (GetTextExtentPoint32(lvfd.nmcd.nmcd.hdc, item.pszText, lstrlen(item.pszText), &siz) &&
  6551. (rcLabel.left + g_cxLabelMargin + siz.cx) > cx)
  6552. {
  6553. lstrcpyn(pszText, item.pszText, item.cchTextMax);
  6554. }
  6555. else
  6556. {
  6557. // Not truncated after all
  6558. bItemUnfolded = TRUE;
  6559. }
  6560. ListView_EndFakeItemDraw(&lvfd);
  6561. ListView_EndFakeCustomDraw(&lvfd);
  6562. }
  6563. }
  6564. }
  6565. }
  6566. else
  6567. {
  6568. // Large icon view is the only one that folds
  6569. if (ListView_GetUnfoldedRect(plv, iItem, &rcLabel))
  6570. {
  6571. item.pszText = pszText;
  6572. item.cchTextMax = cchTextMax;
  6573. ListView_OnGetItem(plv, &item);
  6574. }
  6575. else
  6576. {
  6577. // Item was never folded
  6578. bItemUnfolded = TRUE;
  6579. }
  6580. }
  6581. }
  6582. }
  6583. return bItemUnfolded;
  6584. }
  6585. #ifdef UNICODE
  6586. // Rather than thunking to ListView_OnGetItemText, we let ListView_GetItemA
  6587. // do the work.
  6588. int NEAR PASCAL ListView_OnGetItemTextA(LV* plv, int i, LV_ITEMA FAR *plvi)
  6589. {
  6590. if (!plvi)
  6591. return 0;
  6592. RIPMSG(plvi->pszText != NULL, "LVM_GETITEMTEXT null string pointer");
  6593. plvi->mask = LVIF_TEXT;
  6594. plvi->iItem = i;
  6595. if (!ListView_OnGetItemA(plv, plvi))
  6596. return 0;
  6597. return lstrlenA(plvi->pszText);
  6598. }
  6599. #endif
  6600. int NEAR PASCAL ListView_OnGetItemText(LV* plv, int i, LV_ITEM FAR *plvi)
  6601. {
  6602. if (!plvi)
  6603. return 0;
  6604. RIPMSG(plvi->pszText != NULL, "LVM_GETITEMTEXT null string pointer");
  6605. plvi->mask = LVIF_TEXT;
  6606. plvi->iItem = i;
  6607. if (!ListView_OnGetItem(plv, plvi))
  6608. return 0;
  6609. return lstrlen(plvi->pszText);
  6610. }
  6611. #ifdef UNICODE
  6612. BOOL WINAPI ListView_OnSetItemTextA(LV* plv, int i, int iSubItem, LPCSTR pszText) {
  6613. LPWSTR pszW = NULL;
  6614. BOOL fRet;
  6615. // Let ListView_OnSetItemText() handle owner-data validation
  6616. if (pszText != NULL) {
  6617. pszW = ProduceWFromA(plv->ci.uiCodePage, pszText);
  6618. if (pszW == NULL) {
  6619. return FALSE;
  6620. }
  6621. }
  6622. fRet = ListView_OnSetItemText(plv, i, iSubItem, pszW);
  6623. FreeProducedString(pszW);
  6624. return fRet;
  6625. }
  6626. #endif
  6627. BOOL WINAPI ListView_OnSetItemText(LV* plv, int i, int iSubItem, LPCTSTR pszText)
  6628. {
  6629. LV_ITEM lvi;
  6630. if (ListView_IsOwnerData(plv))
  6631. {
  6632. RIPMSG(0, "LVM_SETITEMTEXT: Invalid for owner-data listview");
  6633. return FALSE;
  6634. }
  6635. ListView_InvalidateTTLastHit(plv, i);
  6636. lvi.mask = LVIF_TEXT;
  6637. lvi.pszText = (LPTSTR)pszText;
  6638. lvi.iItem = i;
  6639. lvi.iSubItem = iSubItem;
  6640. return ListView_OnSetItem(plv, &lvi);
  6641. }
  6642. VOID CALLBACK ImgCtxCallback(void * pvImgCtx, void * pvArg)
  6643. {
  6644. LV *plv = (LV *)pvArg;
  6645. ULONG ulState;
  6646. SIZE sizeImg;
  6647. IImgCtx *pImgCtx = plv->pImgCtx;
  6648. IImgCtx_GetStateInfo(pImgCtx, &ulState, &sizeImg, TRUE);
  6649. if (ulState & (IMGLOAD_STOPPED | IMGLOAD_ERROR))
  6650. {
  6651. TraceMsg(TF_BKIMAGE, "Error!");
  6652. plv->fImgCtxComplete = FALSE;
  6653. }
  6654. else if (ulState & IMGCHG_COMPLETE)
  6655. {
  6656. TraceMsg(TF_BKIMAGE, "Complete!");
  6657. plv->fImgCtxComplete = TRUE;
  6658. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  6659. }
  6660. }
  6661. void ListView_ReleaseBkImage(LV *plv)
  6662. {
  6663. if (plv->pImgCtx)
  6664. {
  6665. IImgCtx_Release(plv->pImgCtx);
  6666. plv->pImgCtx = NULL;
  6667. if (plv->hpalHalftone)
  6668. {
  6669. // No need to delete the half tone palette since we really
  6670. // share it with the image context and it will clean up.
  6671. plv->hpalHalftone = NULL;
  6672. }
  6673. }
  6674. if (plv->hbmBkImage)
  6675. {
  6676. DeleteObject(plv->hbmBkImage);
  6677. plv->hbmBkImage = NULL;
  6678. }
  6679. if (plv->pszBkImage)
  6680. {
  6681. LocalFree(plv->pszBkImage);
  6682. plv->pszBkImage = NULL;
  6683. }
  6684. }
  6685. BOOL WINAPI ListView_OnSetBkImage(LV* plv, LPLVBKIMAGE pbi)
  6686. {
  6687. LPCTSTR pszImage = pbi->pszImage;
  6688. BOOL fRet = FALSE;
  6689. LONG fl;
  6690. switch (pbi->ulFlags & LVBKIF_SOURCE_MASK)
  6691. {
  6692. case LVBKIF_SOURCE_NONE:
  6693. TraceMsg(TF_BKIMAGE, "LV SetBkImage to none");
  6694. ListView_ReleaseBkImage(plv);
  6695. break;
  6696. case LVBKIF_SOURCE_HBITMAP:
  6697. TraceMsg(TF_BKIMAGE, "LV SetBkImage to hBitmap %08lX", pbi->hbm);
  6698. ListView_ReleaseBkImage(plv);
  6699. if (pbi->hbm)
  6700. {
  6701. plv->hbmBkImage = pbi->hbm;
  6702. ASSERT(0); // KenSy hasn't implemented init from bitmap yet...
  6703. }
  6704. else
  6705. {
  6706. pbi->ulFlags &= ~LVBKIF_SOURCE_HBITMAP;
  6707. }
  6708. break;
  6709. case LVBKIF_SOURCE_URL:
  6710. TraceMsg(TF_BKIMAGE, "LV SetBkImage to URL");
  6711. ListView_ReleaseBkImage(plv);
  6712. if (pszImage && pszImage[0])
  6713. {
  6714. HRESULT (*pfnCoCreateInstance)(REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID *);
  6715. HRESULT hr;
  6716. HMODULE hmodOLE;
  6717. plv->pszBkImage = LocalAlloc(LPTR, (lstrlen(pszImage) + 1) * sizeof(TCHAR));
  6718. if (plv->pszBkImage == NULL)
  6719. {
  6720. TraceMsg(TF_BKIMAGE, "Wow, could not allocate memory for string!");
  6721. return FALSE;
  6722. }
  6723. lstrcpy(plv->pszBkImage, pszImage);
  6724. if (((hmodOLE = GetModuleHandle(TEXT("OLE32"))) == NULL) ||
  6725. ((pfnCoCreateInstance = (HRESULT (*)(REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID *))GetProcAddress(hmodOLE, "CoCreateInstance")) == NULL))
  6726. {
  6727. TraceMsg(TF_BKIMAGE, "Could not find CoCreateInstance!");
  6728. TraceMsg(TF_BKIMAGE, "Did the caller remember to call CoInitialize?");
  6729. return FALSE;
  6730. }
  6731. hr = pfnCoCreateInstance(&CLSID_IImgCtx, NULL, CLSCTX_INPROC_SERVER,
  6732. &IID_IImgCtx, (LPVOID *)&plv->pImgCtx);
  6733. if (FAILED(hr))
  6734. {
  6735. TraceMsg(TF_BKIMAGE, "Could not create a pImgCtx!");
  6736. TraceMsg(TF_BKIMAGE, "Did you remember to register IEIMAGE.DLL?");
  6737. return FALSE;
  6738. }
  6739. //
  6740. // Mirror the downloaded image if the listview window is RTL mirrored,
  6741. // so that it would be displayed as is. [samera]
  6742. //
  6743. fl = ((IS_WINDOW_RTL_MIRRORED(plv->ci.hwnd)) ? DWN_MIRRORIMAGE : 0);
  6744. #ifdef UNICODE
  6745. hr = IImgCtx_Load(plv->pImgCtx, pszImage, fl);
  6746. #else
  6747. {
  6748. //
  6749. // Darn IImgCtx::Load only takes wide URLs, sigh
  6750. //
  6751. LPWSTR pwszImage;
  6752. pwszImage = ProduceWFromA(plv->ci.uiCodePage, pszImage);
  6753. if (!pwszImage)
  6754. {
  6755. IImgCtx_Release(plv->pImgCtx);
  6756. plv->pImgCtx = NULL;
  6757. TraceMsg(TF_BKIMAGE, "Could not convert URL to wide!");
  6758. return FALSE;
  6759. }
  6760. hr = IImgCtx_Load(plv->pImgCtx, pwszImage, fl);
  6761. FreeProducedString(pwszImage);
  6762. }
  6763. #endif
  6764. if (FAILED(hr))
  6765. {
  6766. IImgCtx_Release(plv->pImgCtx);
  6767. plv->pImgCtx = NULL;
  6768. TraceMsg(TF_BKIMAGE, "Could not init a pImgCtx!");
  6769. return FALSE;
  6770. }
  6771. }
  6772. else
  6773. {
  6774. pbi->ulFlags &= ~LVBKIF_SOURCE_URL;
  6775. }
  6776. break;
  6777. default:
  6778. RIPMSG(0, "LVM_SETBKIMAGE: Unsupported image type %d", pbi->ulFlags & LVBKIF_SOURCE_MASK);
  6779. return FALSE;
  6780. }
  6781. plv->ulBkImageFlags = pbi->ulFlags;
  6782. plv->xOffsetPercent = pbi->xOffsetPercent;
  6783. plv->yOffsetPercent = pbi->yOffsetPercent;
  6784. //
  6785. // If we actually created a pImgCtx, initialize it here.
  6786. //
  6787. if (plv->pImgCtx)
  6788. {
  6789. if (plv->hpalHalftone == NULL)
  6790. {
  6791. IImgCtx_GetPalette(plv->pImgCtx, &plv->hpalHalftone);
  6792. }
  6793. plv->fImgCtxComplete = FALSE;
  6794. IImgCtx_SetCallback(plv->pImgCtx, ImgCtxCallback, plv);
  6795. IImgCtx_SelectChanges(plv->pImgCtx, IMGCHG_COMPLETE, 0, TRUE);
  6796. TraceMsg(TF_BKIMAGE, " SUCCESS!");
  6797. fRet = TRUE;
  6798. }
  6799. InvalidateRect(plv->ci.hwnd, NULL, TRUE);
  6800. return fRet;
  6801. }
  6802. #ifdef UNICODE
  6803. BOOL WINAPI ListView_OnSetBkImageA(LV* plv, LPLVBKIMAGEA pbiA)
  6804. {
  6805. BOOL fProducedString = FALSE;
  6806. BOOL fRet;
  6807. LVBKIMAGEW biW;
  6808. CopyMemory(&biW, pbiA, SIZEOF(LVBKIMAGE));
  6809. switch (biW.ulFlags & LVBKIF_SOURCE_MASK)
  6810. {
  6811. case LVBKIF_SOURCE_NONE:
  6812. case LVBKIF_SOURCE_HBITMAP:
  6813. break;
  6814. case LVBKIF_SOURCE_URL:
  6815. if (biW.pszImage != NULL)
  6816. {
  6817. biW.pszImage = ProduceWFromA(plv->ci.uiCodePage, (LPCSTR)biW.pszImage);
  6818. if (biW.pszImage == (LPARAM)NULL)
  6819. {
  6820. return FALSE;
  6821. }
  6822. fProducedString = TRUE;
  6823. }
  6824. break;
  6825. default:
  6826. // Let ListView_OnSetBkImage() complain about the invalid parameter
  6827. break;
  6828. }
  6829. fRet = ListView_OnSetBkImage(plv, &biW);
  6830. if (fProducedString)
  6831. {
  6832. FreeProducedString((LPVOID)biW.pszImage);
  6833. }
  6834. return fRet;
  6835. }
  6836. #endif
  6837. BOOL WINAPI ListView_OnGetBkImage(LV* plv, LPLVBKIMAGE pbi)
  6838. {
  6839. BOOL fRet = FALSE;
  6840. if (!IsBadWritePtr(pbi, sizeof(*pbi)))
  6841. {
  6842. pbi->ulFlags = plv->ulBkImageFlags;
  6843. switch (plv->ulBkImageFlags & LVBKIF_SOURCE_MASK)
  6844. {
  6845. case LVBKIF_SOURCE_NONE:
  6846. fRet = TRUE;
  6847. break;
  6848. case LVBKIF_SOURCE_HBITMAP:
  6849. pbi->hbm = plv->hbmBkImage;
  6850. fRet = TRUE;
  6851. break;
  6852. case LVBKIF_SOURCE_URL:
  6853. if (!IsBadWritePtr(pbi->pszImage, pbi->cchImageMax * SIZEOF(TCHAR)))
  6854. {
  6855. lstrcpyn(pbi->pszImage, plv->pszBkImage, pbi->cchImageMax);
  6856. fRet = TRUE;
  6857. }
  6858. break;
  6859. default:
  6860. ASSERT(0);
  6861. break;
  6862. }
  6863. pbi->xOffsetPercent = plv->xOffsetPercent;
  6864. pbi->yOffsetPercent = plv->yOffsetPercent;
  6865. }
  6866. return fRet;
  6867. }
  6868. #ifdef UNICODE
  6869. BOOL WINAPI ListView_OnGetBkImageA(LV* plv, LPLVBKIMAGEA pbiA)
  6870. {
  6871. BOOL fRet = FALSE;
  6872. if (!IsBadWritePtr(pbiA, sizeof(*pbiA)))
  6873. {
  6874. pbiA->ulFlags = plv->ulBkImageFlags;
  6875. switch (plv->ulBkImageFlags & LVBKIF_SOURCE_MASK)
  6876. {
  6877. case LVBKIF_SOURCE_NONE:
  6878. fRet = TRUE;
  6879. break;
  6880. case LVBKIF_SOURCE_HBITMAP:
  6881. pbiA->hbm = plv->hbmBkImage;
  6882. fRet = TRUE;
  6883. break;
  6884. case LVBKIF_SOURCE_URL:
  6885. if (!IsBadWritePtr(pbiA->pszImage, pbiA->cchImageMax))
  6886. {
  6887. ConvertWToAN(plv->ci.uiCodePage, pbiA->pszImage,
  6888. pbiA->cchImageMax, plv->pszBkImage, -1);
  6889. fRet = TRUE;
  6890. }
  6891. break;
  6892. default:
  6893. ASSERT(0);
  6894. break;
  6895. }
  6896. pbiA->xOffsetPercent = plv->xOffsetPercent;
  6897. pbiA->yOffsetPercent = plv->yOffsetPercent;
  6898. }
  6899. return fRet;
  6900. }
  6901. #endif
  6902. void ListView_FreeSubItem(PLISTSUBITEM plsi)
  6903. {
  6904. if (plsi) {
  6905. Str_Set(&plsi->pszText, NULL);
  6906. LocalFree(plsi);
  6907. }
  6908. }
  6909. int NEAR ListView_GetCxScrollbar(LV* plv)
  6910. {
  6911. int cx;
  6912. if (((plv->exStyle & LVS_EX_FLATSB) == 0) ||
  6913. !FlatSB_GetScrollProp(plv->ci.hwnd, WSB_PROP_CXVSCROLL, &cx))
  6914. {
  6915. cx = g_cxScrollbar;
  6916. }
  6917. return cx;
  6918. }
  6919. int NEAR ListView_GetCyScrollbar(LV* plv)
  6920. {
  6921. int cy;
  6922. if (((plv->exStyle & LVS_EX_FLATSB) == 0) ||
  6923. !FlatSB_GetScrollProp(plv->ci.hwnd, WSB_PROP_CYHSCROLL, &cy))
  6924. {
  6925. cy = g_cyScrollbar;
  6926. }
  6927. return cy;
  6928. }
  6929. DWORD NEAR ListView_GetWindowStyle(LV* plv)
  6930. {
  6931. DWORD dwStyle;
  6932. if (((plv->exStyle & LVS_EX_FLATSB) == 0) ||
  6933. !FlatSB_GetScrollProp(plv->ci.hwnd, WSB_PROP_WINSTYLE, (LPINT)&dwStyle))
  6934. {
  6935. dwStyle = GetWindowStyle(plv->ci.hwnd);
  6936. }
  6937. return dwStyle;
  6938. }
  6939. int ListView_SetScrollInfo(LV *plv, int fnBar, LPSCROLLINFO lpsi, BOOL fRedraw)
  6940. {
  6941. int iRc;
  6942. if (plv->exStyle & LVS_EX_FLATSB)
  6943. {
  6944. iRc = FlatSB_SetScrollInfo(plv->ci.hwnd, fnBar, lpsi, fRedraw);
  6945. }
  6946. else
  6947. {
  6948. iRc = SetScrollInfo(plv->ci.hwnd, fnBar, lpsi, fRedraw);
  6949. }
  6950. //
  6951. // You'd think we were finished, but in fact the game is only half over.
  6952. //
  6953. // Some apps (e.g., Font Folder) will do
  6954. //
  6955. // SetWindowLong(hwnd, GWL_STYLE, newStyle);
  6956. //
  6957. // where newStyle toggles the WS_HSCROLL and/or WS_VSCROLL bits.
  6958. // This causes USER's internal bookkeeping to go completely out
  6959. // of whack: The ScrollInfo says that there is a scrollbar, but
  6960. // the window style says there isn't, or vice versa. The result
  6961. // is that we get a scrollbar when we shouldn't or vice versa.
  6962. //
  6963. // So each time we tweak the scroll info in a manner that changes
  6964. // the range and page, we kick USER in the head to make sure USER's
  6965. // view of the world (via style bits) is the same as the scroll
  6966. // bar's view of the world (via SCROLLINFO).
  6967. //
  6968. //
  6969. // We should always change SIF_PAGE and SIF_RANGE at the same time.
  6970. //
  6971. ASSERT((lpsi->fMask & (SIF_PAGE | SIF_RANGE)) == 0 ||
  6972. (lpsi->fMask & (SIF_PAGE | SIF_RANGE)) == (SIF_PAGE | SIF_RANGE));
  6973. if ((lpsi->fMask & (SIF_PAGE | SIF_RANGE)) == (SIF_PAGE | SIF_RANGE))
  6974. {
  6975. BOOL fShow;
  6976. fShow = lpsi->nMax && (int)lpsi->nPage <= lpsi->nMax;
  6977. #ifdef DEBUG
  6978. {
  6979. DWORD dwStyle, dwScroll, dwWant;
  6980. dwScroll = (fnBar == SB_VERT) ? WS_VSCROLL : WS_HSCROLL;
  6981. //
  6982. // We can short-circuit some logic with secret knowledge about how
  6983. // ListView uses SetScrollInfo.
  6984. //
  6985. ASSERT(lpsi->nMin == 0);
  6986. dwWant = fShow ? dwScroll : 0;
  6987. dwStyle = ListView_GetWindowStyle(plv);
  6988. if ((dwStyle & dwScroll) != dwWant)
  6989. {
  6990. TraceMsg(TF_LISTVIEW, "ListView_SetScrollInfo: App twiddled WS_[VH]SCROLL");
  6991. }
  6992. }
  6993. #endif
  6994. if (plv->exStyle & LVS_EX_FLATSB)
  6995. FlatSB_ShowScrollBar(plv->ci.hwnd, fnBar, fShow);
  6996. else
  6997. ShowScrollBar(plv->ci.hwnd, fnBar, fShow);
  6998. }
  6999. return iRc;
  7000. }
  7001. // Add/remove/replace item
  7002. BOOL NEAR ListView_FreeItem(LV* plv, LISTITEM FAR* pitem)
  7003. {
  7004. ASSERT( !ListView_IsOwnerData(plv));
  7005. if (pitem)
  7006. {
  7007. Str_Set(&pitem->pszText, NULL);
  7008. if (pitem->hrgnIcon && pitem->hrgnIcon!=(HANDLE)-1)
  7009. DeleteObject(pitem->hrgnIcon);
  7010. // NOTE: We never remove items from the image list; that's
  7011. // the app's responsibility.
  7012. // REVIEW: Should we do this? Or should we just provide
  7013. // a message that will adjust image indices for the guy
  7014. // when one is removed?
  7015. //
  7016. ControlFree(plv->hheap, pitem);
  7017. }
  7018. return FALSE;
  7019. }
  7020. LISTITEM FAR* NEAR ListView_CreateItem(LV* plv, const LV_ITEM FAR* plvi)
  7021. {
  7022. LISTITEM FAR* pitem = ControlAlloc(plv->hheap, sizeof(LISTITEM));
  7023. ASSERT(!ListView_IsOwnerData(plv));
  7024. if (pitem)
  7025. {
  7026. if (plvi->mask & LVIF_STATE) {
  7027. if (plvi->state & ~LVIS_ALL) {
  7028. DebugMsg(DM_ERROR, TEXT("ListView: Invalid state: %04x"), plvi->state);
  7029. return NULL;
  7030. }
  7031. // If adding a selected item to a single-select listview, deselect
  7032. // any other items.
  7033. if ((plv->ci.style & LVS_SINGLESEL) && (plvi->state & LVIS_SELECTED))
  7034. ListView_DeselectAll(plv, -1);
  7035. pitem->state = (plvi->state & ~(LVIS_FOCUSED | LVIS_SELECTED));
  7036. }
  7037. if (plvi->mask & LVIF_PARAM)
  7038. pitem->lParam = plvi->lParam;
  7039. if (plvi->mask & LVIF_IMAGE)
  7040. pitem->iImage = (short) plvi->iImage;
  7041. if (plvi->mask & LVIF_INDENT)
  7042. pitem->iIndent = (short) plvi->iIndent;
  7043. pitem->pt.x = pitem->pt.y = RECOMPUTE;
  7044. ListView_SetSRecompute(pitem);
  7045. pitem->pszText = NULL;
  7046. if (plvi->mask & LVIF_TEXT) {
  7047. if (!Str_Set(&pitem->pszText, plvi->pszText))
  7048. {
  7049. ListView_FreeItem(plv, pitem);
  7050. return NULL;
  7051. }
  7052. }
  7053. }
  7054. return pitem;
  7055. }
  7056. // HACK ALERT!! -- fSmoothScroll is an added parameter! It allows for smooth
  7057. // scrolling when deleting items. ListView_LRInvalidateBelow is only currently
  7058. // called from ListView_OnUpdate and ListView_OnDeleteItem. Both these calls
  7059. // have been modified to work correctly and be backwards compatible.
  7060. //
  7061. void ListView_LRInvalidateBelow(LV* plv, int i, int fSmoothScroll)
  7062. {
  7063. if (ListView_IsListView(plv) || ListView_IsReportView(plv)) {
  7064. RECT rcItem;
  7065. if (!ListView_RedrawEnabled(plv) ||
  7066. (ListView_IsReportView(plv) && (plv->pImgCtx != NULL)))
  7067. fSmoothScroll = FALSE;
  7068. if (i >= 0)
  7069. {
  7070. ListView_GetRects(plv, i, NULL, NULL, &rcItem, NULL);
  7071. }
  7072. else
  7073. {
  7074. rcItem.left = rcItem.top = 0;
  7075. rcItem.right = plv->sizeClient.cx;
  7076. rcItem.bottom = plv->sizeClient.cy;
  7077. }
  7078. // Don't try to scroll over the header part
  7079. if (ListView_IsReportView(plv) && rcItem.top < plv->yTop)
  7080. rcItem.top = plv->yTop;
  7081. // For both List and report view need to erase the item and
  7082. // below. Note: do simple test to see if there is anything
  7083. // to redraw
  7084. // we can't check for bottom/right > 0 because if we nuked something
  7085. // above or to the left of the view, it may affect us all
  7086. if ((rcItem.top <= plv->sizeClient.cy) &&
  7087. (rcItem.left <= plv->sizeClient.cx))
  7088. {
  7089. rcItem.bottom = plv->sizeClient.cy;
  7090. if (ListView_RedrawEnabled(plv)) {
  7091. if ((plv->clrBk == CLR_NONE) && (plv->pImgCtx == NULL))
  7092. {
  7093. LVSeeThruScroll(plv, &rcItem);
  7094. }
  7095. else if (ListView_IsReportView(plv) && fSmoothScroll)
  7096. {
  7097. #ifndef UNIX
  7098. SMOOTHSCROLLINFO si =
  7099. {
  7100. sizeof(si),
  7101. SSIF_MINSCROLL,
  7102. plv->ci.hwnd,
  7103. 0,
  7104. -(plv->cyItem),
  7105. &rcItem,
  7106. &rcItem,
  7107. NULL,
  7108. NULL,
  7109. SW_INVALIDATE|SW_ERASE,
  7110. SSI_DEFAULT,
  7111. 1,
  7112. 1,
  7113. };
  7114. #else
  7115. SMOOTHSCROLLINFO si;
  7116. si.cbSize = sizeof(si);
  7117. si.fMask = SSIF_MINSCROLL;
  7118. si.hwnd = plv->ci.hwnd;
  7119. si.dx = 0;
  7120. si.dy = -(plv->cyItem);
  7121. si.lprcSrc = &rcItem;
  7122. si.lprcClip = &rcItem;
  7123. si.hrgnUpdate = NULL;
  7124. si.lprcUpdate = NULL;
  7125. si.fuScroll = SW_INVALIDATE|SW_ERASE;
  7126. si.uMaxScrollTime = SSI_DEFAULT;
  7127. si.cxMinScroll = 1;
  7128. si.cyMinScroll = 1;
  7129. si.pfnScrollProc = NULL;
  7130. #endif
  7131. SmoothScrollWindow(&si);
  7132. } else {
  7133. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  7134. }
  7135. } else {
  7136. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  7137. }
  7138. if (ListView_IsListView(plv))
  7139. {
  7140. RECT rcClient;
  7141. // For Listview we need to erase the other columns...
  7142. rcClient.left = rcItem.right;
  7143. rcClient.top = 0;
  7144. rcClient.bottom = plv->sizeClient.cy;
  7145. rcClient.right = plv->sizeClient.cx;
  7146. RedrawWindow(plv->ci.hwnd, &rcClient, NULL, RDW_INVALIDATE | RDW_ERASE);
  7147. }
  7148. }
  7149. }
  7150. }
  7151. // Used in Ownerdata Icon views to try to not invalidate the whole world...
  7152. void ListView_IInvalidateBelow(LV* plv, int i)
  7153. {
  7154. RECT rcItem;
  7155. if (i >= 0)
  7156. {
  7157. ListView_GetRects(plv, i, NULL, NULL, &rcItem, NULL);
  7158. }
  7159. else
  7160. {
  7161. rcItem.left = rcItem.top = 0;
  7162. rcItem.right = plv->sizeClient.cx;
  7163. rcItem.bottom = plv->sizeClient.cy;
  7164. }
  7165. // For Iconviews we need to invalidate everything to the right of us in this
  7166. // row and everything below the row...
  7167. // below. Note: do simple test to see if there is anything
  7168. // to redraw
  7169. if ((rcItem.top <= plv->sizeClient.cy) &&
  7170. (rcItem.left <= plv->sizeClient.cx))
  7171. {
  7172. rcItem.right = plv->sizeClient.cx;
  7173. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  7174. // Now erase everything below...
  7175. rcItem.top = rcItem.bottom;
  7176. rcItem.bottom = plv->sizeClient.cy;
  7177. rcItem.left = 0;
  7178. RedrawWindow(plv->ci.hwnd, &rcItem, NULL, RDW_INVALIDATE | RDW_ERASE);
  7179. }
  7180. }
  7181. void NEAR ListView_OnUpdate(LV* plv, int i)
  7182. {
  7183. // If in icon/small view, don't call InvalidateItem, since that'll force
  7184. // FindFreeSlot to get called, which is pig-like. Instead, just
  7185. // force a WM_PAINT message, which we'll catch and call Recompute with.
  7186. //
  7187. if (ListView_IsIconView(plv) || ListView_IsSmallView(plv))
  7188. {
  7189. if (plv->ci.style & LVS_AUTOARRANGE)
  7190. ListView_OnArrange(plv, LVA_DEFAULT);
  7191. else
  7192. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INTERNALPAINT | RDW_NOCHILDREN);
  7193. }
  7194. else
  7195. {
  7196. // HACK ALERT!! -- The third parameter is new. It allows for
  7197. // smooth scrolling when items are deleted in reportview.
  7198. // Passing 0, tells it NOT to scroll.
  7199. //
  7200. ListView_LRInvalidateBelow(plv, i, 0);
  7201. }
  7202. ListView_UpdateScrollBars(plv);
  7203. }
  7204. #ifdef UNICODE
  7205. int NEAR ListView_OnInsertItemA(LV* plv, LV_ITEMA FAR* plvi) {
  7206. LPWSTR pszW = NULL;
  7207. LPSTR pszC = NULL;
  7208. int iRet;
  7209. //HACK ALERT -- this code assumes that LV_ITEMA is exactly the same
  7210. // as LV_ITEMW except for the pointer to the string.
  7211. ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW));
  7212. if (!plvi)
  7213. {
  7214. return -1;
  7215. }
  7216. if ((plvi->mask & LVIF_TEXT) && (plvi->pszText != NULL)) {
  7217. pszC = plvi->pszText;
  7218. pszW = ProduceWFromA(plv->ci.uiCodePage, pszC);
  7219. if (pszW == NULL)
  7220. return -1;
  7221. plvi->pszText = (LPSTR)pszW;
  7222. }
  7223. iRet = ListView_OnInsertItem(plv, (const LV_ITEM FAR*) plvi);
  7224. if (pszW != NULL) {
  7225. plvi->pszText = pszC;
  7226. FreeProducedString(pszW);
  7227. }
  7228. return iRet;
  7229. }
  7230. #endif
  7231. int NEAR ListView_OnInsertItem(LV* plv, const LV_ITEM FAR* plvi)
  7232. {
  7233. int iItem;
  7234. if (!plvi || (plvi->iSubItem != 0)) // can only insert the 0th item
  7235. {
  7236. RIPMSG(0, "ListView_InsertItem: iSubItem must be 0 (app passed %d)", plvi->iSubItem);
  7237. return -1;
  7238. }
  7239. // If sorted, then insert sorted.
  7240. //
  7241. if (plv->ci.style & (LVS_SORTASCENDING | LVS_SORTDESCENDING)
  7242. && !ListView_IsOwnerData( plv ))
  7243. {
  7244. if (plvi->pszText == LPSTR_TEXTCALLBACK)
  7245. {
  7246. DebugMsg(DM_ERROR, TEXT("Don't use LPSTR_TEXTCALLBACK with LVS_SORTASCENDING or LVS_SORTDESCENDING"));
  7247. return -1;
  7248. }
  7249. iItem = ListView_LookupString(plv, plvi->pszText, LVFI_SUBSTRING | LVFI_NEARESTXY, 0);
  7250. }
  7251. else
  7252. iItem = plvi->iItem;
  7253. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  7254. if ( !ListView_IsOwnerData(plv))
  7255. {
  7256. int iZ;
  7257. LISTITEM FAR *pitem = ListView_CreateItem(plv, plvi);
  7258. UINT uSelMask = plvi->mask & LVIF_STATE ?
  7259. (plvi->state & (LVIS_FOCUSED | LVIS_SELECTED))
  7260. : 0;
  7261. UINT uSel = uSelMask;
  7262. if (!pitem)
  7263. return -1;
  7264. iItem = DPA_InsertPtr(plv->hdpa, iItem, pitem);
  7265. if (iItem == -1)
  7266. {
  7267. ListView_FreeItem(plv, pitem);
  7268. return -1;
  7269. }
  7270. plv->cTotalItems++;
  7271. if (plv->hdpaSubItems)
  7272. {
  7273. int iCol;
  7274. // slide all the colum DPAs down to match the location of the
  7275. // inserted item
  7276. //
  7277. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  7278. {
  7279. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  7280. if (hdpa) // this is optional, call backs don't have them
  7281. {
  7282. // insert a blank item (REVIEW: should this be callback?)
  7283. // since this can be a tail sparce array,
  7284. // we need to make sure enough items are there.
  7285. if (iItem >= DPA_GetPtrCount(hdpa))
  7286. DPA_SetPtr(hdpa, iItem, NULL);
  7287. else if (DPA_InsertPtr(hdpa, iItem, NULL) != iItem)
  7288. goto Failure;
  7289. // Bad assert since hdpa can be tail sparse
  7290. // ASSERT(ListView_Count(plv) == DPA_GetPtrCount(hdpa));
  7291. ASSERT(ListView_Count(plv) >= DPA_GetPtrCount(hdpa));
  7292. }
  7293. }
  7294. }
  7295. // Add item to end of z order
  7296. //
  7297. iZ = DPA_InsertPtr(plv->hdpaZOrder, ListView_Count(plv), IntToPtr(iItem));
  7298. if (iZ == -1)
  7299. {
  7300. Failure:
  7301. DebugMsg(TF_LISTVIEW, TEXT("ListView_OnInsertItem() failed"));
  7302. if (DPA_DeletePtr(plv->hdpa, iItem))
  7303. plv->cTotalItems--;
  7304. ListView_FreeItem(plv, pitem);
  7305. return -1;
  7306. }
  7307. // if we inserted before the focus point, move the focus point up one
  7308. if (iItem <= plv->iFocus)
  7309. plv->iFocus++;
  7310. // do the same thing for the mark
  7311. if (iItem <= plv->iMark)
  7312. plv->iMark++;
  7313. // If the item was not added at the end of the list we need
  7314. // to update the other indexes in the list
  7315. if (iItem != ListView_Count(plv) - 1)
  7316. {
  7317. int i2;
  7318. for (i2 = iZ - 1; i2 >= 0; i2--)
  7319. {
  7320. int iItemZ = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, i2);
  7321. if (iItemZ >= iItem)
  7322. DPA_SetPtr(plv->hdpaZOrder, i2, (LPVOID)(UINT_PTR)(iItemZ + 1));
  7323. }
  7324. }
  7325. if (ListView_CheckBoxes(plv)) {
  7326. uSelMask |= LVIS_STATEIMAGEMASK;
  7327. uSel |= INDEXTOSTATEIMAGEMASK(1);
  7328. }
  7329. if (uSelMask) {
  7330. // we masked off these in the createitem above.
  7331. // because turning these on means more than setting the bits.
  7332. ListView_OnSetItemState(plv, iItem, uSel, uSelMask);
  7333. }
  7334. }
  7335. else
  7336. {
  7337. //
  7338. // simply adjust selection and count
  7339. //
  7340. if ((iItem >= 0) && (iItem <= MAX_LISTVIEWITEMS))
  7341. {
  7342. if (FAILED(plv->plvrangeSel->lpVtbl->InsertItem(plv->plvrangeSel, iItem )))
  7343. {
  7344. return( -1 );
  7345. }
  7346. plv->cTotalItems++;
  7347. plv->rcView.left = RECOMPUTE;
  7348. ListView_Recompute(plv);
  7349. if (!ListView_IsReportView(plv) && !ListView_IsListView(plv))
  7350. {
  7351. // We need to erase the background so that we don't leave
  7352. // turds from wrapped labels in large icon mode. This could
  7353. // be optimized by only invalidating to the right of and
  7354. // below the inserted item.
  7355. InvalidateRect( plv->ci.hwnd, NULL, TRUE );
  7356. }
  7357. // if we inserted before the focus point, move the focus point up
  7358. if (iItem <= plv->iFocus)
  7359. plv->iFocus++;
  7360. // do the same thing for the mark
  7361. if (iItem <= plv->iMark)
  7362. plv->iMark++;
  7363. }
  7364. }
  7365. if (!ListView_IsOwnerData(plv))
  7366. ASSERT(ListView_Count(plv) == DPA_GetPtrCount(plv->hdpaZOrder));
  7367. if (ListView_RedrawEnabled(plv))
  7368. {
  7369. // Update region
  7370. ListView_RecalcRegion(plv, TRUE, TRUE);
  7371. // The Maybe resize colmns may resize things in which case the next call
  7372. // to Update is not needed.
  7373. if (!ListView_MaybeResizeListColumns(plv, iItem, iItem))
  7374. ListView_OnUpdate(plv, iItem);
  7375. // this trick makes inserting lots of items cheap
  7376. // even if redraw is enabled.... don't calc or position items
  7377. // until this postmessage comes around
  7378. if (!plv->uUnplaced) {
  7379. PostMessage(plv->ci.hwnd, LVMI_PLACEITEMS, 0, 0);
  7380. }
  7381. plv->uUnplaced++;
  7382. }
  7383. else
  7384. {
  7385. //
  7386. // Special case code to make using SetRedraw work reasonably well
  7387. // for adding items to a listview which is in a non layout mode...
  7388. //
  7389. if ((plv->iFirstChangedNoRedraw == -1) ||
  7390. (iItem < plv->iFirstChangedNoRedraw))
  7391. plv->iFirstChangedNoRedraw = iItem;
  7392. }
  7393. ListView_Notify(plv, iItem, 0, LVN_INSERTITEM);
  7394. MyNotifyWinEvent(EVENT_OBJECT_CREATE, plv->ci.hwnd, OBJID_CLIENT, iItem+1);
  7395. return iItem;
  7396. }
  7397. BOOL NEAR ListView_OnDeleteItem(LV* plv, int iItem)
  7398. {
  7399. int iCount = ListView_Count(plv);
  7400. if (!ListView_IsValidItemNumber(plv, iItem))
  7401. return FALSE; // out of range
  7402. MyNotifyWinEvent(EVENT_OBJECT_DESTROY, plv->ci.hwnd, OBJID_CLIENT, iItem+1);
  7403. ListView_DismissEdit(plv, TRUE); // cancel edits
  7404. ListView_OnSetItemState(plv, iItem, 0, LVIS_SELECTED);
  7405. if (plv->iFocus == iItem)
  7406. ListView_OnSetItemState(plv, (iItem==iCount-1 ? iItem-1 : iItem+1), LVIS_FOCUSED, LVIS_FOCUSED);
  7407. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  7408. if (!ListView_IsOwnerData(plv))
  7409. {
  7410. LISTITEM FAR* pitem;
  7411. int iZ;
  7412. if ((plv->rcView.left != RECOMPUTE) && (ListView_IsIconView(plv) || ListView_IsSmallView(plv))) {
  7413. pitem = ListView_FastGetItemPtr(plv, iItem);
  7414. if (LV_IsItemOnViewEdge(plv, pitem)) {
  7415. plv->rcView.left = RECOMPUTE;
  7416. }
  7417. }
  7418. // We don't need to invalidate the item in report view because we
  7419. // will be scrolling on top of it.
  7420. //
  7421. if (!ListView_IsReportView(plv))
  7422. ListView_InvalidateItem(plv, iItem, FALSE, RDW_INVALIDATE | RDW_ERASE);
  7423. // this notify must be done AFTER the Invalidate because some items need callbacks
  7424. // to calculate the rect, but the notify might free it out
  7425. ListView_Notify(plv, iItem, 0, LVN_DELETEITEM);
  7426. // During the notify, the app might've done something to the listview
  7427. // so revalidate the item number pointer so we don't fault
  7428. #ifdef DEBUG
  7429. // Validate internally because DPA_DeletePtr will ASSERT if you ask it
  7430. // to delete something that doesn't exist.
  7431. if (!ListView_IsValidItemNumber(plv, iItem))
  7432. pitem = NULL;
  7433. else
  7434. #endif
  7435. pitem = DPA_DeletePtr(plv->hdpa, iItem);
  7436. if (!pitem)
  7437. {
  7438. RIPMSG(0, "Something strange happened during LVN_DELETEITEM; abandoning LVM_DELETEITEM");
  7439. return FALSE;
  7440. }
  7441. plv->cTotalItems = DPA_GetPtrCount(plv->hdpa);
  7442. // remove from the z-order, this is a linear search to find this!
  7443. DPA_DeletePtr(plv->hdpaZOrder, ListView_ZOrderIndex(plv, iItem));
  7444. //
  7445. // As the Z-order hdpa is a set of indexes we also need to decrement
  7446. // all indexes that exceed the one we are deleting.
  7447. //
  7448. for (iZ = ListView_Count(plv) - 1; iZ >= 0; iZ--)
  7449. {
  7450. int iItemZ = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, iZ);
  7451. if (iItemZ > iItem)
  7452. DPA_SetPtr(plv->hdpaZOrder, iZ, IntToPtr(iItemZ - 1));
  7453. }
  7454. // remove from sub item DPAs if necessary
  7455. if (plv->hdpaSubItems)
  7456. {
  7457. int iCol;
  7458. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  7459. {
  7460. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  7461. if (hdpa) { // this is optional, call backs don't have them
  7462. PLISTSUBITEM plsi;
  7463. // These DPAs are tail sparse, so don't get upset if we
  7464. // try to delete something that's past the end of the list
  7465. #ifdef DEBUG
  7466. plsi = iItem < DPA_GetPtrCount(hdpa) ? DPA_DeletePtr(hdpa, iItem) : NULL;
  7467. #else
  7468. plsi = DPA_DeletePtr(hdpa, iItem);
  7469. #endif
  7470. ListView_FreeSubItem(plsi);
  7471. }
  7472. }
  7473. }
  7474. ListView_FreeItem(plv, pitem); // ... finaly the item pointer
  7475. }
  7476. else
  7477. {
  7478. //
  7479. // simply notify and then fixup selection state and count
  7480. //
  7481. if ((iItem >= 0) && (iItem <= MAX_LISTVIEWITEMS))
  7482. {
  7483. ListView_Notify(plv, iItem, 0, LVN_DELETEITEM);
  7484. if (FAILED(plv->plvrangeSel->lpVtbl->RemoveItem(plv->plvrangeSel, iItem)))
  7485. {
  7486. // BUGBUG: return out of memory status
  7487. //MemoryLowDlg( plv->ci.hwnd );
  7488. return FALSE;
  7489. }
  7490. plv->cTotalItems--;
  7491. plv->rcView.left = RECOMPUTE;
  7492. ListView_Recompute(plv);
  7493. if (!ListView_IsReportView(plv) && !ListView_IsListView(plv))
  7494. {
  7495. // We need to erase the background so that the last item gets
  7496. // erased in both icon modes and so that we don't leave turds
  7497. // from wrapped labels in large icon mode. This could be
  7498. // optimized by only invalidating to the right of and below
  7499. // the deleted item.
  7500. InvalidateRect( plv->ci.hwnd, NULL, TRUE );
  7501. }
  7502. }
  7503. else
  7504. {
  7505. return FALSE;
  7506. }
  7507. }
  7508. iCount = ListView_Count(plv); // regrab count incase someone updated item...
  7509. if (!ListView_IsOwnerData(plv))
  7510. ASSERT(ListView_Count(plv) == DPA_GetPtrCount(plv->hdpaZOrder));
  7511. if (plv->iFocus == iItem) {
  7512. if (plv->iFocus >= iCount) {
  7513. plv->iFocus = iCount - 1;
  7514. }
  7515. } if (plv->iFocus > iItem) {
  7516. plv->iFocus--; // slide the focus index down
  7517. }
  7518. // same with the mark
  7519. if (plv->iMark == iItem) { // deleted the mark item
  7520. if (plv->iMark >= iCount) // did we nuke the last item?
  7521. plv->iMark = iCount - 1;
  7522. } else if (plv->iMark > iItem)
  7523. plv->iMark--; // slide the mark index down
  7524. // Deleting an icon invalidates the icon positioning cache
  7525. plv->iFreeSlot = -1;
  7526. // HACK ALERT!! -- This construct with ReportView steals code from
  7527. // ListView_OnUpdate. Currently, it will work exactly the same as before,
  7528. // EXCEPT, that it won't call ListView_OnUpdate. This is to allow us to
  7529. // send a flag to ListView_LRUpdateBelow to tell it we're scrolling up.
  7530. //
  7531. if (ListView_IsReportView(plv)) {
  7532. // if the new count is zero and we will be showing empty text, simply invalidate the
  7533. // rect and redraw, else go through the invalidate below code...
  7534. // we don't know if we are going to show empty text if pszEmptyText is NULL, or not
  7535. // because we may get one through notify, so if iCount is 0 invalidate everything
  7536. if (iCount == 0)
  7537. InvalidateRect( plv->ci.hwnd, NULL, TRUE );
  7538. else
  7539. ListView_LRInvalidateBelow(plv,iItem,1);
  7540. if (ListView_RedrawEnabled(plv))
  7541. ListView_UpdateScrollBars(plv);
  7542. else {
  7543. //
  7544. // Special case code to make using SetRedraw work reasonably well
  7545. // for adding items to a listview which is in a non layout mode...
  7546. //
  7547. if ((plv->iFirstChangedNoRedraw != -1) && (iItem < plv->iFirstChangedNoRedraw))
  7548. plv->iFirstChangedNoRedraw--;
  7549. }
  7550. }
  7551. else {
  7552. if (ListView_RedrawEnabled(plv))
  7553. ListView_OnUpdate(plv, iItem);
  7554. else
  7555. {
  7556. ListView_LRInvalidateBelow(plv, iItem, 0);
  7557. //
  7558. // Special case code to make using SetRedraw work reasonably well
  7559. // for adding items to a listview which is in a non layout mode...
  7560. //
  7561. if ((plv->iFirstChangedNoRedraw != -1) && (iItem < plv->iFirstChangedNoRedraw))
  7562. plv->iFirstChangedNoRedraw--;
  7563. }
  7564. }
  7565. ListView_RecalcRegion(plv, TRUE, TRUE);
  7566. return TRUE;
  7567. }
  7568. BOOL NEAR ListView_OnDeleteAllItems(LV* plv)
  7569. {
  7570. int i;
  7571. BOOL bAlreadyNotified;
  7572. BOOL fHasItemData;
  7573. fHasItemData = !ListView_IsOwnerData(plv);
  7574. ListView_DismissEdit(plv, TRUE); // cancel edits
  7575. // Must neutralize the focus because some apps will call
  7576. // ListView_OnGetNextItem(LVNI_FOCUSED) during delete notifications,
  7577. // so we need to make sure the focus is in a safe place.
  7578. // May as well neutralize the mark, too.
  7579. plv->iMark = plv->iFocus = -1;
  7580. // Also nuke the icon positioning cache
  7581. plv->iFreeSlot = -1;
  7582. bAlreadyNotified = (BOOL)ListView_Notify(plv, -1, 0, LVN_DELETEALLITEMS);
  7583. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  7584. if (fHasItemData || !bAlreadyNotified)
  7585. {
  7586. for (i = ListView_Count(plv) - 1; i >= 0; i--)
  7587. {
  7588. if (!bAlreadyNotified)
  7589. ListView_Notify(plv, i, 0, LVN_DELETEITEM);
  7590. if (fHasItemData)
  7591. {
  7592. ListView_FreeItem(plv, ListView_FastGetItemPtr(plv, i));
  7593. //
  7594. // CAREFUL! Applications such as NT Backup call back
  7595. // into ListView during the LVN_DELETEITEM notification,
  7596. // so we need to kill this item or we will fault at the
  7597. // next iteration because everybody relies on
  7598. // ListView_Count for validation.
  7599. //
  7600. DPA_FastDeleteLastPtr(plv->hdpa);
  7601. plv->cTotalItems--;
  7602. }
  7603. }
  7604. }
  7605. if (ListView_IsOwnerData( plv ))
  7606. {
  7607. if (FAILED(plv->plvrangeSel->lpVtbl->Clear( plv->plvrangeSel )))
  7608. {
  7609. // BUGBUG: return low memory status
  7610. //MemoryLowDlg( plv->ci.hwnd );
  7611. }
  7612. plv->cTotalItems = 0;
  7613. }
  7614. else
  7615. {
  7616. DPA_DeleteAllPtrs(plv->hdpa);
  7617. DPA_DeleteAllPtrs(plv->hdpaZOrder);
  7618. plv->cTotalItems = 0;
  7619. if (plv->hdpaSubItems)
  7620. {
  7621. int iCol;
  7622. for (iCol = plv->cCol - 1; iCol >= 0; iCol--)
  7623. {
  7624. HDPA hdpa = ListView_GetSubItemDPA(plv, iCol);
  7625. if (hdpa) {
  7626. DPA_EnumCallback(hdpa, ListView_FreeColumnData, 0);
  7627. DPA_DeleteAllPtrs(hdpa);
  7628. }
  7629. }
  7630. }
  7631. }
  7632. plv->rcView.left = RECOMPUTE;
  7633. plv->xOrigin = 0;
  7634. plv->nSelected = 0;
  7635. plv->ptlRptOrigin.x = 0;
  7636. plv->ptlRptOrigin.y = 0;
  7637. // reset the cxItem width
  7638. if (!(plv->flags & LVF_COLSIZESET))
  7639. plv->cxItem = 16 * plv->cxLabelChar + plv->cxSmIcon;
  7640. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  7641. ListView_UpdateScrollBars(plv);
  7642. return TRUE;
  7643. }
  7644. int PASCAL ListView_IFindNearestItem(LV* plv, int left, int top, UINT vk)
  7645. {
  7646. int iMin = -1;
  7647. if (ListView_IsOwnerData( plv ))
  7648. {
  7649. POINT pt;
  7650. int cSlots;
  7651. ASSERT( !ListView_IsReportView( plv ) && !ListView_IsListView( plv ) );
  7652. pt.x = left + plv->ptOrigin.x;
  7653. pt.y = top + plv->ptOrigin.y;
  7654. cSlots = ListView_GetSlotCount( plv, TRUE );
  7655. iMin = ListView_CalcHitSlot( plv, pt, cSlots );
  7656. switch( vk )
  7657. {
  7658. case VK_HOME:
  7659. iMin = 0;
  7660. break;
  7661. case VK_END:
  7662. iMin = ListView_Count( plv ) - 1;
  7663. break;
  7664. case VK_LEFT:
  7665. if (iMin % cSlots)
  7666. iMin -= 1;
  7667. break;
  7668. case VK_RIGHT:
  7669. if ((iMin + 1) % cSlots)
  7670. iMin += 1;
  7671. break;
  7672. case VK_UP:
  7673. if (iMin >= cSlots)
  7674. iMin -= cSlots;
  7675. break;
  7676. case VK_DOWN:
  7677. if (iMin + cSlots < ListView_Count( plv ))
  7678. iMin += cSlots;
  7679. break;
  7680. default: ;
  7681. }
  7682. iMin = max( 0, iMin );
  7683. iMin = min( ListView_Count( plv ) - 1, iMin );
  7684. }
  7685. else
  7686. {
  7687. DWORD dMin = 0;
  7688. int cyItem;
  7689. int yEnd = 0, yLimit = 0, xEnd = 0;
  7690. int iCount;
  7691. int i;
  7692. if (ListView_IsIconView(plv)) {
  7693. cyItem = plv->cyIcon;
  7694. } else {
  7695. cyItem = plv->cyItem;
  7696. }
  7697. iCount = ListView_Count(plv);
  7698. if (iCount == 1)
  7699. return 0;
  7700. if (vk == VK_HOME) {
  7701. yEnd = yLimit = plv->rcView.bottom;
  7702. xEnd = plv->rcView.right;
  7703. } else if (vk == VK_END) {
  7704. yEnd = yLimit = plv->rcView.top;
  7705. xEnd = plv->rcView.left;
  7706. }
  7707. for (i = 0; i < iCount; i++)
  7708. {
  7709. RECT rc;
  7710. int dx;
  7711. DWORD dxAbs, dyAbs;
  7712. int dy;
  7713. DWORD dOffset;
  7714. ListView_GetRects(plv, i, &rc, NULL, NULL, NULL);
  7715. dx = rc.left - left;
  7716. dxAbs = (DWORD)(dx < 0 ? -dx : dx);
  7717. dy = rc.top - top;
  7718. dyAbs = (DWORD)(dy < 0 ? -dy : dy);
  7719. if ((vk == VK_LEFT) && (dxAbs < dyAbs || dx >= 0))
  7720. continue;
  7721. else if ((vk == VK_RIGHT) && (dxAbs < dyAbs || dx <= 0))
  7722. continue;
  7723. else if ((vk == VK_UP) && (dxAbs > dyAbs || dy >= 0))
  7724. continue;
  7725. else if ((vk == VK_DOWN) && (dxAbs > dyAbs || dy <= 0))
  7726. continue;
  7727. if (vk == VK_HOME || vk == VK_END) {
  7728. // home is not the nearest to the top corner, it's the leftmost of the top row.
  7729. // ditto (reversed) for end. thus we can't use the stuff below. bummer
  7730. if (vk == VK_HOME) {
  7731. if ((rc.top + cyItem < yEnd) || // if it's fully above the highest line so far, take it!
  7732. ((rc.top < yLimit) && // if it's on the same row as the top item to date
  7733. (rc.left < xEnd))) {
  7734. iMin = i;
  7735. xEnd = rc.left;
  7736. yEnd = rc.top;
  7737. if (rc.top + cyItem < yLimit)
  7738. yLimit = rc.top + cyItem;
  7739. }
  7740. } else {
  7741. if ((rc.top > yEnd) || //if it's full below the lowest row
  7742. ((rc.top + cyItem > yLimit) && // if it's on the same row
  7743. (rc.right > xEnd))) {
  7744. iMin = i;
  7745. xEnd = rc.right;
  7746. yEnd = rc.top;
  7747. if (rc.top > yLimit)
  7748. yLimit = rc.top;
  7749. }
  7750. }
  7751. } else {
  7752. dOffset = ((dxAbs * dxAbs) + (dyAbs * dyAbs));
  7753. if (iMin == -1 || (dMin > dOffset))
  7754. {
  7755. dMin = dOffset;
  7756. iMin = i;
  7757. }
  7758. }
  7759. }
  7760. }
  7761. return iMin;
  7762. }
  7763. int NEAR ListView_Arrow(LV* plv, int iStart, UINT vk)
  7764. {
  7765. RECT rcFocus;
  7766. int i;
  7767. int dx;
  7768. int iCount;
  7769. //
  7770. // The algorithm to find which item depends if we are in a view
  7771. // that is arrange(layout) oriented or a sorted (list) view.
  7772. // For the sorted views we will use some optimizations to make
  7773. // it faster
  7774. //
  7775. iCount = ListView_Count(plv);
  7776. if (ListView_IsReportView(plv) || ListView_IsListView(plv))
  7777. {
  7778. //
  7779. // For up and down arrows, simply increment or decrement the
  7780. // index. Note: in listview this will cause it to wrap columns
  7781. // which is fine as it is compatible with the file manager
  7782. //
  7783. // Assumes only one of these flags is set...
  7784. switch (vk)
  7785. {
  7786. case VK_LEFT:
  7787. if (ListView_IsReportView(plv))
  7788. {
  7789. ListView_ROnScroll(plv, (GetAsyncKeyState(VK_CONTROL) < 0) ? SB_PAGELEFT : SB_LINELEFT, 0, SB_HORZ);
  7790. }
  7791. else
  7792. iStart -= plv->cItemCol;
  7793. break;
  7794. case VK_RIGHT:
  7795. if (ListView_IsReportView(plv))
  7796. {
  7797. // Make this horizontally scroll the report view
  7798. ListView_ROnScroll(plv, (GetAsyncKeyState(VK_CONTROL) < 0) ? SB_PAGERIGHT : SB_LINERIGHT, 0, SB_HORZ);
  7799. }
  7800. else
  7801. iStart += plv->cItemCol;
  7802. break;
  7803. case VK_UP:
  7804. iStart--;
  7805. break;
  7806. case VK_DOWN:
  7807. iStart++;
  7808. break;
  7809. case VK_HOME:
  7810. iStart = 0;
  7811. break;
  7812. case VK_END:
  7813. iStart = iCount -1;
  7814. break;
  7815. case VK_NEXT:
  7816. if (ListView_IsReportView(plv))
  7817. {
  7818. i = iStart; // save away to make sure we dont go wrong way!
  7819. // First go to end of page...
  7820. iStart = (int)(((LONG)(plv->sizeClient.cy - (plv->cyItem)
  7821. - plv->yTop) + plv->ptlRptOrigin.y) / plv->cyItem);
  7822. // If Same item, increment by page size.
  7823. if (iStart <= i)
  7824. iStart = i + max(
  7825. (plv->sizeClient.cy - plv->yTop)/ plv->cyItem - 1,
  7826. 1);
  7827. if (iStart >= iCount)
  7828. iStart = iCount - 1;
  7829. } else {
  7830. // multiply by 2/3 to give a good feel.. when the item is mostly shown
  7831. // you want to go to the next column
  7832. dx = (plv->sizeClient.cx + (plv->cxItem*2)/3) / plv->cxItem;
  7833. if (!dx)
  7834. dx = 1;
  7835. iStart += plv->cItemCol * dx;
  7836. if (plv->cItemCol) {
  7837. while (iStart >= iCount)
  7838. iStart -= plv->cItemCol;
  7839. }
  7840. }
  7841. break;
  7842. case VK_PRIOR:
  7843. if (ListView_IsReportView(plv))
  7844. {
  7845. i = iStart; // save away to make sure we dont go wrong way!
  7846. // First go to end of page...
  7847. iStart = (int)(plv->ptlRptOrigin.y / plv->cyItem);
  7848. // If Same item, increment by page size.
  7849. if (iStart >= i)
  7850. iStart = i - max(
  7851. (plv->sizeClient.cy - plv->yTop)/ plv->cyItem - 1,
  7852. 1);
  7853. if (iStart < 0)
  7854. iStart = 0;
  7855. } else {
  7856. dx = (plv->sizeClient.cx + (plv->cxItem*2)/3) / plv->cxItem;
  7857. if (!dx)
  7858. dx = 1;
  7859. iStart -= plv->cItemCol * dx;
  7860. if (plv->cItemCol) {
  7861. while (iStart < 0)
  7862. iStart += plv->cItemCol;
  7863. }
  7864. }
  7865. break;
  7866. default:
  7867. return -1; // Out of range
  7868. }
  7869. // Make sure it is in range!.
  7870. if ((iStart >= 0) && (iStart < iCount))
  7871. return iStart;
  7872. else if (iCount == 1)
  7873. return 0;
  7874. else
  7875. return -1;
  7876. }
  7877. else
  7878. {
  7879. //
  7880. // Layout type view. we need to use the position of the items
  7881. // to figure out the next item
  7882. //
  7883. if (ListView_IsOwnerData( plv ))
  7884. {
  7885. iStart = max( 0, iStart );
  7886. // if it does not matches any of the entries in the case statement below
  7887. // this is done to skip the call back by the GetRects
  7888. //
  7889. if ( vk != VK_LEFT &&
  7890. vk != VK_RIGHT &&
  7891. vk != VK_UP &&
  7892. vk != VK_DOWN &&
  7893. vk != VK_HOME &&
  7894. vk != VK_END &&
  7895. vk != VK_NEXT &&
  7896. vk != VK_PRIOR )
  7897. {
  7898. return -1;
  7899. }
  7900. ListView_GetRects(plv, iStart, &rcFocus, NULL, NULL, NULL);
  7901. }
  7902. else
  7903. {
  7904. if (iStart != -1) {
  7905. ListView_GetRects(plv, iStart, &rcFocus, NULL, NULL, NULL);
  7906. }
  7907. }
  7908. switch (vk)
  7909. {
  7910. // For standard arrow keys just fall out of here.
  7911. case VK_LEFT:
  7912. case VK_RIGHT:
  7913. case VK_UP:
  7914. case VK_DOWN:
  7915. if (ListView_IsOwnerData( plv ))
  7916. {
  7917. break;
  7918. }
  7919. else
  7920. {
  7921. if (iStart != -1) {
  7922. // all keys map to VK_HOME except VK_END
  7923. break;
  7924. }
  7925. // Fall through
  7926. vk = VK_HOME;
  7927. }
  7928. case VK_HOME:
  7929. rcFocus.left = - plv->ptOrigin.x;
  7930. rcFocus.top = - plv->ptOrigin.y;
  7931. break;
  7932. case VK_END:
  7933. rcFocus.left = plv->rcView.right;
  7934. rcFocus.top = plv->rcView.bottom;
  7935. break;
  7936. case VK_NEXT:
  7937. rcFocus.top += plv->sizeClient.cy;
  7938. vk = VK_UP;
  7939. break;
  7940. case VK_PRIOR:
  7941. vk = VK_DOWN;
  7942. rcFocus.top -= plv->sizeClient.cy;
  7943. break;
  7944. default:
  7945. return -1; // Out of range
  7946. }
  7947. return ListView_IFindNearestItem(plv, rcFocus.left, rcFocus.top, vk);
  7948. }
  7949. }
  7950. int NEAR ListView_OnGetNextItem(LV* plv, int i, UINT flags)
  7951. {
  7952. int iStart = i;
  7953. int cItemMax = ListView_Count(plv);
  7954. // Note that -1 is a valid starting point
  7955. if (i < -1 || i >= cItemMax)
  7956. return -1;
  7957. if (ListView_IsOwnerData( plv ))
  7958. {
  7959. if (flags & (LVNI_CUT | LVNI_DROPHILITED | LVNI_PREVIOUS))
  7960. {
  7961. return( -1 );
  7962. }
  7963. }
  7964. if (flags & LVNI_FOCUSED)
  7965. {
  7966. // we know which item is focused, jump right to it.
  7967. // but we have to mimick the code below exactly for compat:
  7968. // if directional bits are set, they take precedence.
  7969. if (!(flags & (LVNI_ABOVE | LVNI_BELOW | LVNI_TORIGHT | LVNI_TOLEFT)))
  7970. {
  7971. // there are no more focused items after iFocus
  7972. if (i >= plv->iFocus)
  7973. return -1;
  7974. // subtract one here -- we increment it below
  7975. i = plv->iFocus - 1;
  7976. }
  7977. }
  7978. while (TRUE)
  7979. {
  7980. // BUGBUG: does anyone call this now???
  7981. if (flags & (LVNI_ABOVE | LVNI_BELOW | LVNI_TORIGHT | LVNI_TOLEFT))
  7982. {
  7983. UINT vk;
  7984. if (flags & LVNI_ABOVE)
  7985. vk = VK_UP;
  7986. else if (flags & LVNI_BELOW)
  7987. vk = VK_DOWN;
  7988. else if (flags & LVNI_TORIGHT)
  7989. vk = VK_RIGHT;
  7990. else
  7991. vk = VK_LEFT;
  7992. if (i != -1)
  7993. i = ListView_Arrow(plv, i, vk);
  7994. if (i == -1)
  7995. return i;
  7996. }
  7997. else
  7998. {
  7999. i++;
  8000. if (i == cItemMax)
  8001. return -1;
  8002. }
  8003. // See if any other restrictions are set
  8004. if (flags & ~(LVNI_ABOVE | LVNI_BELOW | LVNI_TORIGHT | LVNI_TOLEFT))
  8005. {
  8006. WORD wItemState;
  8007. if (ListView_IsOwnerData( plv ))
  8008. {
  8009. if (flags & LVNI_FOCUSED)
  8010. {
  8011. // we check LVNI_FOCUSED before the loop, so i == iFocus
  8012. ASSERT(i == plv->iFocus && i != -1);
  8013. if (flags & LVNI_SELECTED)
  8014. {
  8015. if (plv->plvrangeSel->lpVtbl->IsSelected(plv->plvrangeSel, i ) != S_OK)
  8016. {
  8017. i = -1;
  8018. }
  8019. }
  8020. }
  8021. else if (flags & LVNI_SELECTED)
  8022. {
  8023. i = max( i, 0 );
  8024. plv->plvrangeSel->lpVtbl->NextSelected(plv->plvrangeSel, i, &i );
  8025. }
  8026. else
  8027. {
  8028. i = -1;
  8029. }
  8030. }
  8031. else
  8032. {
  8033. {
  8034. LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, i);
  8035. wItemState = pitem->state;
  8036. }
  8037. // for LVNI_FOCUSED, we start at the LVIS_FOCUSED element, if we're
  8038. // not on that element, one of the below continues was hit, so
  8039. // we'll never find the element. bail out early.
  8040. if ((flags & LVNI_FOCUSED) && !(wItemState & LVIS_FOCUSED))
  8041. {
  8042. ASSERT(i == plv->iFocus || i == plv->iFocus+1);
  8043. return(-1);
  8044. }
  8045. if (((flags & LVNI_SELECTED) && !(wItemState & LVIS_SELECTED)) ||
  8046. ((flags & LVNI_CUT) && !(wItemState & LVIS_CUT)) ||
  8047. ((flags & LVNI_DROPHILITED) && !(wItemState & LVIS_DROPHILITED))) {
  8048. if (i != iStart)
  8049. continue;
  8050. else {
  8051. // we've looped and we can't find anything to fit this criteria
  8052. return -1;
  8053. }
  8054. }
  8055. }
  8056. }
  8057. return i;
  8058. }
  8059. }
  8060. int NEAR ListView_CompareString(LV* plv, int i, LPCTSTR pszFind, UINT flags, int iLen)
  8061. {
  8062. // BUGBUG: non protected globals
  8063. int cb;
  8064. TCHAR ach[CCHLABELMAX];
  8065. LV_ITEM item;
  8066. ASSERT(!ListView_IsOwnerData(plv));
  8067. ASSERT(pszFind);
  8068. item.iItem = i;
  8069. item.iSubItem = 0;
  8070. item.mask = LVIF_TEXT;
  8071. item.pszText = ach;
  8072. item.cchTextMax = ARRAYSIZE(ach);
  8073. ListView_OnGetItem(plv, &item);
  8074. if (!(flags & (LVFI_PARTIAL | LVFI_SUBSTRING)))
  8075. return lstrcmpi(item.pszText, pszFind);
  8076. // REVIEW: LVFI_SUBSTRING is not really implemented yet.
  8077. cb = lstrlen(pszFind);
  8078. if (iLen && (cb > iLen)) {
  8079. cb = iLen;
  8080. }
  8081. //
  8082. // If the sub strings not equal then return the ordering based
  8083. // on the entire string.
  8084. //
  8085. #ifndef UNIX
  8086. return IntlStrEqNI(item.pszText, pszFind, cb) ? 0 : lstrcmp(item.pszText, pszFind);
  8087. #else
  8088. return IntlStrEqNI(item.pszText, pszFind, cb) ? 0 : lstrcmpi(item.pszText, pszFind);
  8089. #endif
  8090. }
  8091. #ifdef UNICODE
  8092. int NEAR ListView_OnFindItemA(LV* plv, int iStart, LV_FINDINFOA * plvfi) {
  8093. LPWSTR pszW = NULL;
  8094. LPCSTR pszC = NULL;
  8095. int iRet;
  8096. //HACK ALERT -- this code assumes that LV_FINDINFOA is exactly the same
  8097. // as LV_FINDINFOW except for the pointer to the string.
  8098. ASSERT(sizeof(LV_FINDINFOA) == sizeof(LV_FINDINFOW));
  8099. if (!plvfi)
  8100. return -1;
  8101. if (!(plvfi->flags & LVFI_PARAM) && !(plvfi->flags & LVFI_NEARESTXY)) {
  8102. pszC = plvfi->psz;
  8103. if ((pszW = ProduceWFromA(plv->ci.uiCodePage, pszC)) == NULL)
  8104. return -1;
  8105. plvfi->psz = (LPSTR)pszW;
  8106. }
  8107. iRet = ListView_OnFindItem(plv, iStart, (const LV_FINDINFO FAR *)plvfi);
  8108. if (pszW != NULL) {
  8109. plvfi->psz = pszC;
  8110. FreeProducedString(pszW);
  8111. }
  8112. return iRet;
  8113. }
  8114. #endif
  8115. int NEAR ListView_OnFindItem(LV* plv, int iStart, const LV_FINDINFO FAR* plvfi)
  8116. {
  8117. int i;
  8118. int j;
  8119. int cItem;
  8120. UINT flags;
  8121. if (!plvfi)
  8122. return -1;
  8123. if (plvfi->flags & LVFI_NEARESTXY) {
  8124. if (ListView_IsIconView(plv) || ListView_IsSmallView(plv)) {
  8125. return ListView_IFindNearestItem(plv, plvfi->pt.x, plvfi->pt.y, plvfi->vkDirection);
  8126. } else
  8127. return -1;
  8128. }
  8129. // Note that -1 is a valid starting point
  8130. if (iStart < -1 || iStart >= ListView_Count(plv))
  8131. return -1;
  8132. if (ListView_IsOwnerData( plv ))
  8133. {
  8134. // call back to owner for search
  8135. return( (int) ListView_RequestFindItem( plv, plvfi, iStart + 1 ) );
  8136. }
  8137. else
  8138. {
  8139. flags = plvfi->flags;
  8140. i = iStart;
  8141. cItem = ListView_Count(plv);
  8142. if (flags & LVFI_PARAM)
  8143. {
  8144. LPARAM lParam = plvfi->lParam;
  8145. // Linear search with wraparound...
  8146. //
  8147. for (j = cItem; j-- != 0; )
  8148. {
  8149. ++i;
  8150. if (i == cItem) {
  8151. if (flags & LVFI_WRAP)
  8152. i = 0;
  8153. else
  8154. break;
  8155. }
  8156. if (ListView_FastGetItemPtr(plv, i)->lParam == lParam)
  8157. return i;
  8158. }
  8159. }
  8160. else // if (flags & (LVFI_STRING | LVFI_SUBSTRING | LVFI_PARTIAL))
  8161. {
  8162. LPCTSTR pszFind = plvfi->psz;
  8163. if (!pszFind)
  8164. return -1;
  8165. if (plv->ci.style & (LVS_SORTASCENDING | LVS_SORTDESCENDING))
  8166. return ListView_LookupString(plv, pszFind, flags, i + 1);
  8167. for (j = cItem; j-- != 0; )
  8168. {
  8169. ++i;
  8170. if (i == cItem) {
  8171. if (flags & LVFI_WRAP)
  8172. i = 0;
  8173. else
  8174. break;
  8175. }
  8176. if (ListView_CompareString(plv,
  8177. i,
  8178. pszFind,
  8179. (flags & (LVFI_PARTIAL | LVFI_SUBSTRING)), 0) == 0)
  8180. {
  8181. return i;
  8182. }
  8183. }
  8184. }
  8185. }
  8186. return -1;
  8187. }
  8188. BOOL NEAR ListView_OnGetItemRect(LV* plv, int i, RECT FAR* prc)
  8189. {
  8190. #ifndef UNIX
  8191. LPRECT pRects[LVIR_MAX];
  8192. #else
  8193. volatile LPRECT pRects[LVIR_MAX]; /* this is used by the irix compiler */
  8194. #endif
  8195. // validate parameters
  8196. if (!ListView_IsValidItemNumber(plv, i))
  8197. {
  8198. RIPMSG(0, "LVM_GETITEMRECT: invalid index %d", i);
  8199. return FALSE;
  8200. }
  8201. if (!prc || prc->left >= LVIR_MAX || prc->left < 0)
  8202. {
  8203. RIPMSG(0, "LVM_GETITEMRECT: invalid rect pointer");
  8204. return FALSE;
  8205. }
  8206. pRects[0] = NULL;
  8207. pRects[1] = NULL;
  8208. pRects[2] = NULL;
  8209. pRects[3] = NULL;
  8210. pRects[prc->left] = prc;
  8211. ListView_GetRects(plv, i, pRects[LVIR_ICON], pRects[LVIR_LABEL],
  8212. pRects[LVIR_BOUNDS], pRects[LVIR_SELECTBOUNDS]);
  8213. return TRUE;
  8214. }
  8215. //
  8216. // in:
  8217. // plv
  8218. // iItem MUST be a valid item index (in range)
  8219. // out:
  8220. // prcIcon icon bounding rect
  8221. // prcLabel label text bounding rect, for details this is the first column
  8222. // prcBounds entire item (all text and icon), including columns in details
  8223. // prcSelectionBounds union of icon and label rects, does NOT include columns
  8224. // in details view
  8225. // BUGBUG raymondc - Need to pass an HDC parameter for measurement
  8226. // since sometimes we do this while painting
  8227. void NEAR ListView_GetRects(LV* plv, int iItem,
  8228. RECT FAR* prcIcon, RECT FAR* prcLabel, RECT FAR* prcBounds,
  8229. RECT FAR* prcSelectBounds)
  8230. {
  8231. ASSERT(plv);
  8232. if (ListView_IsReportView(plv))
  8233. {
  8234. ListView_RGetRects(plv, iItem, prcIcon, prcLabel, prcBounds, prcSelectBounds);
  8235. }
  8236. else if (ListView_IsListView(plv))
  8237. {
  8238. ListView_LGetRects(plv, iItem, prcIcon, prcLabel, prcBounds, prcSelectBounds);
  8239. }
  8240. else
  8241. {
  8242. if (ListView_IsOwnerData( plv ))
  8243. {
  8244. RECT rcIcon;
  8245. RECT rcTextBounds;
  8246. LISTITEM item;
  8247. if (ListView_IsIconView(plv))
  8248. ListView_IGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, &item, FALSE);
  8249. else if (ListView_IsSmallView(plv))
  8250. ListView_SGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, &item, FALSE);
  8251. if (prcIcon)
  8252. *prcIcon = rcIcon;
  8253. if (prcLabel)
  8254. *prcLabel = rcTextBounds;
  8255. if (prcBounds)
  8256. UnionRect(prcBounds, &rcIcon, &rcTextBounds);
  8257. if (prcSelectBounds)
  8258. UnionRect(prcSelectBounds, &rcIcon, &rcTextBounds);
  8259. }
  8260. else
  8261. {
  8262. if (iItem >= ListView_Count(plv))
  8263. {
  8264. return;
  8265. }
  8266. else
  8267. {
  8268. LISTITEM FAR *pitem = ListView_FastGetItemPtr(plv, iItem);
  8269. if (pitem->cyFoldedLabel == SRECOMPUTE)
  8270. {
  8271. ListView_RecomputeLabelSize(plv, pitem, iItem, NULL, FALSE);
  8272. }
  8273. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem,
  8274. prcIcon, prcLabel, prcBounds, prcSelectBounds);
  8275. }
  8276. }
  8277. }
  8278. }
  8279. void NEAR ListView_GetRectsOwnerData(LV* plv, int iItem,
  8280. RECT FAR* prcIcon, RECT FAR* prcLabel, RECT FAR* prcBounds,
  8281. RECT FAR* prcSelectBounds, LISTITEM* pitem)
  8282. {
  8283. ASSERT(plv);
  8284. ASSERT(ListView_IsOwnerData(plv));
  8285. if (ListView_IsReportView(plv))
  8286. {
  8287. ListView_RGetRects(plv, iItem, prcIcon, prcLabel, prcBounds,
  8288. prcSelectBounds);
  8289. }
  8290. else if (ListView_IsListView(plv))
  8291. {
  8292. ListView_LGetRects(plv, iItem, prcIcon, prcLabel, prcBounds,
  8293. prcSelectBounds);
  8294. }
  8295. else
  8296. {
  8297. RECT rcIcon;
  8298. RECT rcTextBounds;
  8299. if (ListView_IsIconView(plv))
  8300. ListView_IGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, pitem, TRUE);
  8301. else if (ListView_IsSmallView(plv))
  8302. ListView_SGetRectsOwnerData(plv, iItem, &rcIcon, &rcTextBounds, pitem, TRUE);
  8303. // Don't need to check for folding here, as will have been handled in user data
  8304. // rectangle fetching functions.
  8305. if (prcIcon)
  8306. *prcIcon = rcIcon;
  8307. if (prcLabel)
  8308. *prcLabel = rcTextBounds;
  8309. if (prcBounds)
  8310. UnionRect(prcBounds, &rcIcon, &rcTextBounds);
  8311. if (prcSelectBounds)
  8312. UnionRect(prcSelectBounds, &rcIcon, &rcTextBounds);
  8313. }
  8314. }
  8315. BOOL NEAR ListView_OnRedrawItems(LV* plv, int iFirst, int iLast)
  8316. {
  8317. int iCount = ListView_Count(plv);
  8318. if (iFirst < iCount) {
  8319. if (iLast >= iCount)
  8320. iLast = iCount - 1;
  8321. while (iFirst <= iLast)
  8322. ListView_InvalidateItem(plv, iFirst++, FALSE, RDW_INVALIDATE | RDW_ERASE);
  8323. }
  8324. return TRUE;
  8325. }
  8326. // fSelectionOnly use the selection bounds only, ie. don't include
  8327. // columns in invalidation if in details view
  8328. //
  8329. void NEAR ListView_InvalidateItemEx(LV* plv, int iItem, BOOL fSelectionOnly,
  8330. UINT fRedraw, UINT maskChanged)
  8331. {
  8332. RECT rc;
  8333. LPRECT prcIcon;
  8334. LPRECT prcLabel;
  8335. LPRECT prcBounds;
  8336. LPRECT prcSelectBounds;
  8337. if (iItem == -1)
  8338. return;
  8339. prcIcon = prcLabel = prcBounds = prcSelectBounds = NULL;
  8340. // if we're in owner draw mode, and there's been a new font,
  8341. // we don't really know what the selection bounds is, so always use the bounds
  8342. // in that case... unless we're in fullrowselect mode
  8343. if (ListView_IsOwnerData(plv) && plv->flags & LVF_CUSTOMFONT &&
  8344. !ListView_FullRowSelect(plv)) {
  8345. fSelectionOnly = FALSE;
  8346. }
  8347. // if we're owner draw, there's no such thing as selection only
  8348. if (plv->ci.style & LVS_OWNERDRAWFIXED)
  8349. fSelectionOnly = FALSE;
  8350. if (fSelectionOnly) {
  8351. // In report mode non-fullrowselect,
  8352. // we have to use the full label rectangle rather
  8353. // than just the selection bounds, since the stuff outside the
  8354. // rectangle might need redrawing, too.
  8355. if (ListView_IsReportView(plv) && !ListView_FullRowSelect(plv))
  8356. prcLabel = &rc;
  8357. else
  8358. prcSelectBounds = &rc;
  8359. } else {
  8360. // if _only_the_text_ or _only_the_image_ changed then limit the redraw
  8361. switch (maskChanged) {
  8362. case LVIF_IMAGE:
  8363. prcIcon = &rc;
  8364. break;
  8365. case LVIF_TEXT:
  8366. prcLabel = &rc;
  8367. break;
  8368. default:
  8369. prcBounds = &rc;
  8370. break;
  8371. }
  8372. }
  8373. if (ListView_RedrawEnabled(plv)) {
  8374. ListView_GetRects(plv, iItem,
  8375. prcIcon, prcLabel, prcBounds, prcSelectBounds);
  8376. if (RECTS_IN_SIZE(plv->sizeClient, rc))
  8377. {
  8378. if (plv->exStyle & LVS_EX_BORDERSELECT)
  8379. InflateRect(&rc, 4 + g_cxIconMargin, 4 + g_cyIconMargin); // account for selection border and seperation since drawing otside of icon
  8380. RedrawWindow(plv->ci.hwnd, &rc, NULL, fRedraw);
  8381. }
  8382. } else {
  8383. // if we're not visible, we'll get a full
  8384. // erase bk when we do become visible, so only do this stuff when
  8385. // we're on setredraw false
  8386. if (!(plv->flags & LVF_REDRAW)) {
  8387. // if we're invalidating that's new (thus hasn't been painted yet)
  8388. // blow it off
  8389. if ((plv->iFirstChangedNoRedraw != -1) &&
  8390. (iItem >= plv->iFirstChangedNoRedraw)) {
  8391. return;
  8392. }
  8393. ListView_GetRects(plv, iItem,
  8394. prcIcon, prcLabel, prcBounds, prcSelectBounds);
  8395. // if it had the erase bit, add it to our region
  8396. if (RECTS_IN_SIZE(plv->sizeClient, rc)) {
  8397. HRGN hrgn = CreateRectRgnIndirect(&rc);
  8398. ListView_InvalidateRegion(plv, hrgn);
  8399. if (fRedraw & RDW_ERASE)
  8400. plv->flags |= LVF_ERASE;
  8401. }
  8402. }
  8403. }
  8404. }
  8405. // this returns BF_* flags to indicate which if any edge the item I is touching
  8406. // or crossing...
  8407. UINT LV_IsItemOnViewEdge(LV* plv, LISTITEM* pitem)
  8408. {
  8409. RECT rcItem;
  8410. RECT rcView = plv->rcView;
  8411. UINT uRet = 0;
  8412. // the view rect is enlarged a bit to allow for a little space around
  8413. // the text (see ListView_Recompute())
  8414. rcView.bottom -= g_cyEdge;
  8415. rcView.right -= g_cxEdge;
  8416. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem,
  8417. NULL, NULL, &rcItem, NULL);
  8418. // translate from window coordinates to listview coordinate
  8419. OffsetRect(&rcItem, plv->ptOrigin.x, plv->ptOrigin.y);
  8420. if (rcItem.right >= rcView.right)
  8421. uRet |= BF_RIGHT;
  8422. if (rcItem.left <= rcView.left)
  8423. uRet |= BF_LEFT;
  8424. if (rcItem.top <= rcView.top)
  8425. uRet |= BF_TOP;
  8426. if (rcItem.bottom >= rcView.bottom)
  8427. uRet |= BF_BOTTOM;
  8428. return uRet;
  8429. }
  8430. void LV_AdjustViewRectOnMove(LV* plv, LISTITEM *pitem, int x, int y)
  8431. {
  8432. plv->iFreeSlot = -1; // The "free slot" cache is no good once an item moves
  8433. // if we have to recompute anyways, don't bother
  8434. if (!ListView_IsOwnerData( plv )) {
  8435. if ((plv->rcView.left != RECOMPUTE) &&
  8436. x != RECOMPUTE && y != RECOMPUTE &&
  8437. pitem->cyFoldedLabel != SRECOMPUTE) {
  8438. RECT rcAfter;
  8439. RECT rcView = plv->rcView;
  8440. // the view rect is enlarged a bit to allow for a little space around
  8441. // the text (see ListView_Recompute())
  8442. rcView.bottom -= g_cyEdge;
  8443. rcView.right -= g_cxEdge;
  8444. if (pitem->pt.x != RECOMPUTE) {
  8445. UINT uEdges;
  8446. uEdges = LV_IsItemOnViewEdge(plv, pitem);
  8447. pitem->pt.x = x;
  8448. pitem->pt.y = y;
  8449. // before and after the move, they need to be touching the
  8450. // same edges or not at all
  8451. if (uEdges != LV_IsItemOnViewEdge(plv, pitem)) {
  8452. goto FullRecompute;
  8453. }
  8454. } else {
  8455. // if the position wasn't set before
  8456. // we just need to find out what it is afterwards and
  8457. // enlarge the view... we don't need to shrink it
  8458. pitem->pt.x = x;
  8459. pitem->pt.y = y;
  8460. }
  8461. _ListView_GetRectsFromItem(plv, ListView_IsSmallView(plv), pitem,
  8462. NULL, NULL, &rcAfter, NULL);
  8463. // translate from window coordinates to listview coordinate
  8464. OffsetRect(&rcAfter, plv->ptOrigin.x, plv->ptOrigin.y);
  8465. // if we make it here, we just have to make sure the new view rect
  8466. // encompases this new item
  8467. UnionRect(&rcView, &rcView, &rcAfter);
  8468. rcView.right += g_cxEdge;
  8469. rcView.bottom += g_cyEdge;
  8470. DebugMsg(TF_LISTVIEW, TEXT("Score! (%d %d %d %d) was (%d %d %d %d)"),
  8471. rcView.left, rcView.top, rcView.right, rcView.bottom,
  8472. plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
  8473. plv->rcView = rcView;
  8474. } else {
  8475. FullRecompute:
  8476. plv->rcView.left = RECOMPUTE;
  8477. }
  8478. }
  8479. DebugMsg(TF_LISTVIEW, TEXT("LV -- AdjustViewRect pitem %d -- (%x, %x)"),
  8480. pitem,
  8481. pitem->pt.x, pitem->pt.y);
  8482. pitem->pt.x = x;
  8483. pitem->pt.y = y;
  8484. // Compute the workarea of this item if applicable
  8485. ListView_FindWorkArea(plv, pitem->pt, &(pitem->iWorkArea));
  8486. }
  8487. BOOL NEAR ListView_OnSetItemPosition(LV* plv, int i, int x, int y)
  8488. {
  8489. LISTITEM FAR* pitem;
  8490. if (ListView_IsListView(plv))
  8491. return FALSE;
  8492. if (ListView_IsOwnerData( plv ))
  8493. {
  8494. RIPMSG(0, "LVM_SETITEMPOSITION: Invalid for owner-data listview");
  8495. return FALSE;
  8496. }
  8497. pitem = ListView_GetItemPtr(plv, i);
  8498. if (!pitem)
  8499. return FALSE;
  8500. //
  8501. // this is a hack to fix a bug in OLE drag/drop loop
  8502. //
  8503. if (x >= 0xF000 && x < 0x10000)
  8504. {
  8505. DebugMsg(TF_LISTVIEW, TEXT("LV -- On SetItemPosition fixing truncated negative number 0x%08X"), x);
  8506. x = x - 0x10000;
  8507. }
  8508. if (y >= 0xF000 && y < 0x10000)
  8509. {
  8510. DebugMsg(TF_LISTVIEW, TEXT("LV -- On SetItemPosition fixing truncated negative number 0x%08X"), y);
  8511. y = y - 0x10000;
  8512. }
  8513. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  8514. if (pitem->cyFoldedLabel == SRECOMPUTE)
  8515. {
  8516. ListView_RecomputeLabelSize(plv, pitem, i, NULL, FALSE);
  8517. }
  8518. // erase old
  8519. if (y != pitem->pt.y || x != pitem->pt.x) {
  8520. // Don't invalidate if it hasn't got a position yet
  8521. if (pitem->pt.y != RECOMPUTE) {
  8522. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
  8523. } else if (plv->uUnplaced) {
  8524. // this means an unplaced item got placed
  8525. plv->uUnplaced--;
  8526. if (!plv->uUnplaced) {
  8527. MSG msg;
  8528. // if this is now 0, pull out the postmessage
  8529. PeekMessage(&msg, plv->ci.hwnd, LVMI_PLACEITEMS, LVMI_PLACEITEMS, PM_REMOVE);
  8530. }
  8531. }
  8532. if (y == RECOMPUTE) {
  8533. // if they're setting the new position to be a "any open spot" post that we
  8534. // need to calc this later
  8535. if (!plv->uUnplaced) {
  8536. PostMessage(plv->ci.hwnd, LVMI_PLACEITEMS, 0, 0);
  8537. }
  8538. plv->uUnplaced++;
  8539. }
  8540. }
  8541. DebugMsg(TF_LISTVIEW, TEXT("LV -- On SetItemPosition %d %d %d %d -- (%x, %x)"),
  8542. plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom,
  8543. pitem->pt.x, pitem->pt.y);
  8544. LV_AdjustViewRectOnMove(plv, pitem, x, y);
  8545. // and draw at new position
  8546. ListView_RecalcRegion(plv, FALSE, TRUE);
  8547. ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE);
  8548. // If autoarrange is turned on, do it now...
  8549. if (ListView_RedrawEnabled(plv)) {
  8550. if (plv->ci.style & LVS_AUTOARRANGE)
  8551. ListView_OnArrange(plv, LVA_DEFAULT);
  8552. else
  8553. ListView_UpdateScrollBars(plv);
  8554. }
  8555. return TRUE;
  8556. }
  8557. BOOL NEAR ListView_OnGetItemPosition(LV* plv, int i, POINT FAR* ppt)
  8558. {
  8559. LISTITEM FAR* pitem;
  8560. //
  8561. // This needs to handle all views as it is used to figure out
  8562. // where the item is during drag and drop and the like
  8563. //
  8564. if (!ppt)
  8565. {
  8566. RIPMSG(0, "LVM_GETITEMPOSITION: Invalid ppt = NULL");
  8567. return FALSE;
  8568. }
  8569. if (ListView_IsListView(plv) || ListView_IsReportView(plv)
  8570. || ListView_IsOwnerData( plv ))
  8571. {
  8572. RECT rcIcon;
  8573. ListView_GetRects(plv, i, &rcIcon, NULL, NULL, NULL);
  8574. ppt->x = rcIcon.left;
  8575. ppt->y = rcIcon.top;
  8576. } else {
  8577. pitem = ListView_GetItemPtr(plv, i);
  8578. if (!pitem)
  8579. return FALSE;
  8580. if (pitem->pt.x == RECOMPUTE)
  8581. ListView_Recompute(plv);
  8582. ppt->x = pitem->pt.x;
  8583. ppt->y = pitem->pt.y;
  8584. }
  8585. return TRUE;
  8586. }
  8587. BOOL NEAR ListView_OnGetOrigin(LV* plv, POINT FAR* ppt)
  8588. {
  8589. if (!ppt) {
  8590. DebugMsg(DM_ERROR, TEXT("ListView_OnGetOrigin: ppt is NULL"));
  8591. return FALSE;
  8592. }
  8593. if (ListView_IsListView(plv) || ListView_IsReportView(plv))
  8594. return FALSE;
  8595. *ppt = plv->ptOrigin;
  8596. return TRUE;
  8597. }
  8598. #ifdef UNICODE
  8599. int NEAR ListView_OnGetStringWidthA(LV* plv, LPCSTR psz, HDC hdc) {
  8600. LPWSTR pszW = NULL;
  8601. int iRet;
  8602. if (!psz)
  8603. return 0;
  8604. if ((psz != NULL) && (pszW = ProduceWFromA(plv->ci.uiCodePage, psz)) == NULL)
  8605. return 0;
  8606. iRet = ListView_OnGetStringWidth(plv, pszW, hdc);
  8607. FreeProducedString(pszW);
  8608. return iRet;
  8609. }
  8610. #endif
  8611. int NEAR ListView_OnGetStringWidth(LV* plv, LPCTSTR psz, HDC hdc)
  8612. {
  8613. SIZE siz;
  8614. HDC hdcFree = NULL;
  8615. HFONT hfontPrev;
  8616. if (!psz || psz == LPSTR_TEXTCALLBACK)
  8617. return 0;
  8618. if (!hdc) {
  8619. hdcFree = hdc = GetDC(plv->ci.hwnd);
  8620. hfontPrev = SelectFont(hdc, plv->hfontLabel);
  8621. }
  8622. GetTextExtentPoint(hdc, psz, lstrlen(psz), &siz);
  8623. if (hdcFree) {
  8624. SelectFont(hdc, hfontPrev);
  8625. ReleaseDC(plv->ci.hwnd, hdcFree);
  8626. }
  8627. return siz.cx;
  8628. }
  8629. int NEAR ListView_OnGetColumnWidth(LV* plv, int iCol)
  8630. {
  8631. if (ListView_IsReportView(plv))
  8632. return ListView_RGetColumnWidth(plv, iCol);
  8633. else if (ListView_IsListView(plv))
  8634. return plv->cxItem;
  8635. return 0;
  8636. }
  8637. BOOL FAR PASCAL ListView_ISetColumnWidth(LV* plv, int iCol, int cx, BOOL fExplicit)
  8638. {
  8639. if (ListView_IsListView(plv))
  8640. {
  8641. if (iCol != 0 || cx <= 0)
  8642. return FALSE;
  8643. // if it's different and this is an explicit set, or we've never set it explicitly
  8644. if (plv->cxItem != cx && (fExplicit || !(plv->flags & LVF_COLSIZESET)))
  8645. {
  8646. // REVIEW: Should optimize what gets invalidated here...
  8647. plv->cxItem = cx;
  8648. if (fExplicit)
  8649. plv->flags |= LVF_COLSIZESET; // Set the fact that we explictly set size!.
  8650. if (ListView_IsLabelTip(plv))
  8651. {
  8652. // A truncated label may have been exposed or vice versa.
  8653. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  8654. }
  8655. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  8656. ListView_UpdateScrollBars(plv);
  8657. }
  8658. return TRUE;
  8659. }
  8660. else if (ListView_IsReportView(plv))
  8661. {
  8662. if (ListView_IsLabelTip(plv))
  8663. {
  8664. // A truncated label may have been exposed or vice versa.
  8665. ListView_InvalidateTTLastHit(plv, plv->iTTLastHit);
  8666. }
  8667. return ListView_RSetColumnWidth(plv, iCol, cx);
  8668. } else {
  8669. if (cx && plv->cxItem != cx && (fExplicit || !(plv->flags & LVF_COLSIZESET)))
  8670. {
  8671. // REVIEW: Should optimize what gets invalidated here...
  8672. plv->cxItem = cx;
  8673. if (fExplicit)
  8674. plv->flags |= LVF_COLSIZESET; // Set the fact that we explictly set size!.
  8675. RedrawWindow(plv->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  8676. ListView_UpdateScrollBars(plv);
  8677. }
  8678. // BUG-FOR-BUG COMPATIBILITY: IE4 accidentally returned FALSE here.
  8679. }
  8680. return FALSE;
  8681. }
  8682. void NEAR ListView_Redraw(LV* plv, HDC hdc, RECT FAR* prcClip)
  8683. {
  8684. int i;
  8685. int cItem = ListView_Count(plv);
  8686. DWORD dwType = plv->ci.style & LVS_TYPEMASK;
  8687. NMCUSTOMDRAW nmcd;
  8688. LVDRAWITEM lvdi;
  8689. SetBkMode(hdc, TRANSPARENT);
  8690. SelectFont(hdc, plv->hfontLabel);
  8691. nmcd.hdc = hdc;
  8692. /// not implemented yet
  8693. //if (ptb->ci.hwnd == GetFocus())
  8694. //nmcd.uItemState = CDIS_FOCUS;
  8695. //else
  8696. nmcd.uItemState = 0;
  8697. nmcd.lItemlParam = 0;
  8698. nmcd.rc = *prcClip;
  8699. plv->ci.dwCustom = CICustomDrawNotify(&plv->ci, CDDS_PREPAINT, &nmcd);
  8700. if (!(plv->ci.dwCustom & CDRF_SKIPDEFAULT)) {
  8701. // Just before doing any painting, see if the region is up to date...
  8702. ListView_RecalcRegion(plv, FALSE, TRUE);
  8703. //
  8704. // For list view and report view, we can save a lot of time
  8705. // by calculating the index of the first item that may need
  8706. // painting...
  8707. //
  8708. switch (dwType) {
  8709. case LVS_REPORT:
  8710. i = ListView_RYHitTest(plv, prcClip->top);
  8711. cItem = ListView_RYHitTest(plv, prcClip->bottom) + 1;
  8712. break;
  8713. case LVS_LIST:
  8714. i = ListView_LCalcViewItem(plv, prcClip->left, prcClip->top );
  8715. cItem = ListView_LCalcViewItem( plv, prcClip->right, prcClip->bottom ) + 1;
  8716. break;
  8717. default:
  8718. if (ListView_IsOwnerData( plv ))
  8719. {
  8720. ListView_CalcMinMaxIndex( plv, prcClip, &i, &cItem );
  8721. break;
  8722. }
  8723. else
  8724. {
  8725. // REVIEW: we can keep a flag which tracks whether the view is
  8726. // presently in pre-arranged order and bypass Zorder when it is
  8727. i = 0; // Icon views no such hint
  8728. }
  8729. }
  8730. if (i < 0)
  8731. i = 0;
  8732. cItem = min( ListView_Count( plv ), cItem );
  8733. if (ListView_IsOwnerData( plv ) && (cItem > i))
  8734. {
  8735. ListView_NotifyCacheHint( plv, i, cItem-1 );
  8736. ListView_LazyCreateWinEvents( plv, i, cItem-1);
  8737. }
  8738. lvdi.plv = plv;
  8739. lvdi.nmcd.nmcd.hdc = hdc;
  8740. lvdi.prcClip = prcClip;
  8741. lvdi.pitem = NULL;
  8742. for (; i < cItem; i++)
  8743. {
  8744. BOOL bSuccess;
  8745. int i2;
  8746. if ((dwType == LVS_ICON || dwType == LVS_SMALLICON)
  8747. && (!ListView_IsOwnerData(plv)))
  8748. {
  8749. LISTITEM FAR *pitem;
  8750. // Icon views: Draw back-to-front mapped through
  8751. // Z-order array for proper Z order appearance - If autoarrange
  8752. // is on, we don't need to do this as our arrange code is setup
  8753. // to not overlap items!
  8754. //
  8755. // For the cases where we might have overlap, we sped this up,
  8756. // by converting the hdpaZorder into a list of indexes instead
  8757. // of pointers. This ovoids the costly convert pointer to
  8758. // index call.
  8759. //
  8760. i2 = (int)(UINT_PTR)DPA_FastGetPtr(plv->hdpaZOrder, (cItem - 1) -i);
  8761. //
  8762. // do a fast clip check on the item so we dont even try to
  8763. // draw it unless it is visible
  8764. //
  8765. // for small icon view we cant clip on the left without
  8766. // getting the text
  8767. //
  8768. // for large icon view we cant clip on the top without
  8769. // getting the text
  8770. //
  8771. // for large icon view in NOLABELWRAP mode, we can't clip
  8772. // on the top without getting the text, nor can we clip to
  8773. // the left or right in case the text is long.
  8774. //
  8775. // we can always clip to the bottom
  8776. //
  8777. pitem = ListView_FastGetItemPtr(plv, i2);
  8778. if (pitem->pt.x != RECOMPUTE)
  8779. {
  8780. if (pitem->pt.y - plv->ptOrigin.y > prcClip->bottom)
  8781. continue;
  8782. if (dwType == LVS_SMALLICON)
  8783. {
  8784. if (pitem->pt.x - plv->ptOrigin.x - plv->cxState > prcClip->right)
  8785. continue;
  8786. if (pitem->pt.y + plv->cyItem - plv->ptOrigin.y < prcClip->top)
  8787. continue;
  8788. }
  8789. else if (!(plv->ci.style & LVS_NOLABELWRAP))
  8790. {
  8791. if (pitem->pt.x - plv->cxIconSpacing - plv->ptOrigin.x > prcClip->right)
  8792. continue;
  8793. if (pitem->pt.x + plv->cxIconSpacing - plv->ptOrigin.x < prcClip->left)
  8794. continue;
  8795. }
  8796. }
  8797. }
  8798. else
  8799. i2 = i;
  8800. plv->iItemDrawing = i2;
  8801. lvdi.nmcd.nmcd.dwItemSpec = i2;
  8802. // these may get changed
  8803. lvdi.lpptOrg = NULL;
  8804. lvdi.flags = 0;
  8805. lvdi.nmcd.clrText = plv->clrText;
  8806. lvdi.nmcd.clrTextBk = plv->clrTextBk;
  8807. bSuccess = ListView_DrawItem(&lvdi);
  8808. if (!bSuccess) {
  8809. break;
  8810. }
  8811. }
  8812. if ((dwType == LVS_ICON || dwType == LVS_SMALLICON)
  8813. && (ListView_IsOwnerData(plv)) &&
  8814. plv->iFocus != -1) {
  8815. // since there's no zorder in ownerdata, we explicitly draw the focus guy last (again)
  8816. // so that it'll appear on top
  8817. // we may potentially want to do this for all items that are selected
  8818. plv->iItemDrawing = plv->iFocus;
  8819. lvdi.nmcd.nmcd.dwItemSpec = plv->iItemDrawing;
  8820. // these may get changed
  8821. lvdi.lpptOrg = NULL;
  8822. lvdi.flags = 0;
  8823. lvdi.nmcd.clrText = plv->clrText;
  8824. lvdi.nmcd.clrTextBk = plv->clrTextBk;
  8825. ListView_DrawItem(&lvdi);
  8826. }
  8827. // this is an NT5/Memphis feature.
  8828. if (ListView_Count(plv) == 0)
  8829. {
  8830. // there're no items in this view
  8831. // check if we need to display some text in this case.
  8832. if (ListView_GetEmptyText(plv))
  8833. {
  8834. RECT rcClip;
  8835. UINT flags = 0;
  8836. // Put some edging between the text and the border of the
  8837. // window so we don't slam up against the border.
  8838. // This keeps DBCS from looking horrid.
  8839. rcClip.left = g_cxEdge;
  8840. rcClip.top = g_cyEdge;
  8841. #ifdef WINDOWS_ME
  8842. if (plv->dwExStyle & WS_EX_RTLREADING)
  8843. flags |= SHDT_RTLREADING;
  8844. #endif
  8845. // if its a report view && we have a header then move the text down
  8846. if (ListView_IsReportView(plv) && (!(plv->ci.style & LVS_NOCOLUMNHEADER)))
  8847. rcClip.top += plv->cyItem;
  8848. // Note: Use the full sizeClient.cx as the right margin
  8849. // in case pszEmptyText is wider than the client rectangle.
  8850. rcClip.left -= (int)plv->ptlRptOrigin.x;
  8851. rcClip.right = plv->sizeClient.cx;
  8852. rcClip.bottom = rcClip.top + plv->cyItem;
  8853. SHDrawText(hdc, plv->pszEmptyText,
  8854. &rcClip, LVCFMT_LEFT, flags,
  8855. plv->cyLabelChar, plv->cxEllipses,
  8856. plv->clrText, plv->clrBk);
  8857. }
  8858. }
  8859. plv->iItemDrawing = -1;
  8860. // post painting.... this is to do any extra (non item) painting
  8861. // such a grid lines
  8862. switch (dwType) {
  8863. case LVS_REPORT:
  8864. ListView_RAfterRedraw(plv, hdc);
  8865. break;
  8866. }
  8867. // notify parent afterwards if they want us to
  8868. if (plv->ci.dwCustom & CDRF_NOTIFYPOSTPAINT) {
  8869. CICustomDrawNotify(&plv->ci, CDDS_POSTPAINT, &nmcd);
  8870. }
  8871. }
  8872. }
  8873. BOOL NEAR ListView_DrawItem(PLVDRAWITEM plvdi)
  8874. {
  8875. BOOL bRet = TRUE;
  8876. UINT state;
  8877. if (!ListView_IsOwnerData( plvdi->plv )) {
  8878. plvdi->pitem = ListView_FastGetItemPtr(plvdi->plv, plvdi->nmcd.nmcd.dwItemSpec);
  8879. }
  8880. // notify on custom draw then do it!
  8881. plvdi->nmcd.nmcd.uItemState = 0;
  8882. plvdi->nmcd.nmcd.lItemlParam = (plvdi->pitem)? plvdi->pitem->lParam : 0;
  8883. if (!(plvdi->flags & LVDI_NOWAYFOCUS))
  8884. {
  8885. if (plvdi->plv->flags & LVF_FOCUSED) {
  8886. // if we're ownerdraw or asked to callback, go
  8887. // fetch the state
  8888. if (!plvdi->pitem || (plvdi->plv->stateCallbackMask & (LVIS_SELECTED | LVIS_FOCUSED))) {
  8889. state = (WORD) ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec,
  8890. LVIS_SELECTED | LVIS_FOCUSED);
  8891. } else {
  8892. state = plvdi->pitem->state;
  8893. }
  8894. if (state & LVIS_FOCUSED) {
  8895. plvdi->nmcd.nmcd.uItemState |= CDIS_FOCUS;
  8896. }
  8897. if (state & LVIS_SELECTED) {
  8898. plvdi->nmcd.nmcd.uItemState |= CDIS_SELECTED;
  8899. }
  8900. }
  8901. // NOTE: This is a bug. We should set CDIS_SELECTED only if the item
  8902. // really is selected. But this bug has existed forever so who knows
  8903. // what apps are relying on it. Standard workaround is for the client
  8904. // to do a GetItemState and reconfirm the LVIS_SELECTED flag.
  8905. // That's what we do in ListView_DrawImageEx.
  8906. if (plvdi->plv->ci.style & LVS_SHOWSELALWAYS) {
  8907. plvdi->nmcd.nmcd.uItemState |= CDIS_SELECTED;
  8908. }
  8909. }
  8910. #ifdef KEYBOARDCUES
  8911. if (!(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS))
  8912. {
  8913. plvdi->nmcd.nmcd.uItemState |= CDIS_SHOWKEYBOARDCUES;
  8914. }
  8915. #endif
  8916. plvdi->nmcd.clrText = plvdi->plv->clrText;
  8917. plvdi->nmcd.clrTextBk = (plvdi->plv->ci.style & WS_DISABLED ? plvdi->plv->clrBk : plvdi->plv->clrTextBk);
  8918. // PERF: if we decide to keep LVIS_EX_TWOCLICKACTIVATE, then we can
  8919. // call ListView_OnGetItem for LVIF_TEXT | LVIF_IMAGE | LVIF_STATE
  8920. // and pass the intem info into _ListView_DrawItem below.
  8921. if (plvdi->plv->iHot == (int)plvdi->nmcd.nmcd.dwItemSpec) {
  8922. // Handle the HOT case
  8923. if(plvdi->plv->clrHotlight != CLR_DEFAULT)
  8924. plvdi->nmcd.clrText = plvdi->plv->clrHotlight;
  8925. else
  8926. plvdi->nmcd.clrText = GetSysColor(COLOR_HOTLIGHT);
  8927. // ie4 bug 47635: if hotlight color is the same as the background
  8928. // color you don't see the text -- slam to a visible color in this case.
  8929. if (plvdi->nmcd.clrText == plvdi->nmcd.clrTextBk)
  8930. {
  8931. if (COLORISLIGHT(plvdi->nmcd.clrTextBk))
  8932. plvdi->nmcd.clrText = 0x000000; // black
  8933. else
  8934. plvdi->nmcd.clrText = 0xFFFFFF; // white
  8935. }
  8936. if ((plvdi->plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  8937. ((plvdi->plv->exStyle & LVS_EX_TWOCLICKACTIVATE) &&
  8938. ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec, LVIS_SELECTED))) {
  8939. if ((plvdi->plv->exStyle & LVS_EX_UNDERLINEHOT) &&
  8940. (plvdi->plv->hFontHot))
  8941. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hFontHot);
  8942. else
  8943. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  8944. plvdi->nmcd.nmcd.uItemState |= CDIS_HOT;
  8945. }
  8946. } else if ((plvdi->plv->exStyle & LVS_EX_ONECLICKACTIVATE) ||
  8947. ((plvdi->plv->exStyle & LVS_EX_TWOCLICKACTIVATE) &&
  8948. ListView_OnGetItemState(plvdi->plv, (int) plvdi->nmcd.nmcd.dwItemSpec, LVIS_SELECTED))) {
  8949. // Handle the non-hot webview case
  8950. if ((plvdi->plv->exStyle & LVS_EX_UNDERLINECOLD) && (plvdi->plv->hFontHot))
  8951. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hFontHot);
  8952. else
  8953. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  8954. } else {
  8955. // Handle the non-webview case
  8956. SelectFont(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  8957. }
  8958. plvdi->dwCustom = CICustomDrawNotify(&plvdi->plv->ci, CDDS_ITEMPREPAINT, &plvdi->nmcd.nmcd);
  8959. plvdi->flags &= ~(LVDI_FOCUS | LVDI_SELECTED);
  8960. if (plvdi->nmcd.nmcd.uItemState & CDIS_FOCUS)
  8961. plvdi->flags |= LVDI_FOCUS;
  8962. if (plvdi->nmcd.nmcd.uItemState & CDIS_SELECTED) {
  8963. if (plvdi->plv->flags & LVF_FOCUSED)
  8964. plvdi->flags |= LVDI_SELECTED;
  8965. else
  8966. plvdi->flags |= LVDI_SELECTNOFOCUS;
  8967. if (plvdi->plv->iHot == (int)plvdi->nmcd.nmcd.dwItemSpec)
  8968. plvdi->flags |= LVDI_HOTSELECTED;
  8969. }
  8970. if (!(plvdi->dwCustom & CDRF_SKIPDEFAULT)) {
  8971. if (!ListView_IsOwnerData( plvdi->plv )) {
  8972. #ifdef DEBUG_NEWFONT
  8973. if ((plvdi->nmcd.nmcd.dwItemSpec % 3) == 0) {
  8974. plvdi->dwCustom |= CDRF_NEWFONT;
  8975. SelectObject(plvdi->nmcd.nmcd.hdc, GetStockObject(SYSTEM_FONT));
  8976. }
  8977. #endif
  8978. if (plvdi->dwCustom & CDRF_NEWFONT) {
  8979. ListView_RecomputeLabelSize(plvdi->plv, plvdi->pitem, (int) plvdi->nmcd.nmcd.dwItemSpec, plvdi->nmcd.nmcd.hdc, FALSE);
  8980. }
  8981. }
  8982. bRet = _ListView_DrawItem(plvdi);
  8983. if (plvdi->dwCustom & CDRF_NOTIFYPOSTPAINT) {
  8984. plvdi->nmcd.iSubItem = 0;
  8985. CICustomDrawNotify(&plvdi->plv->ci, CDDS_ITEMPOSTPAINT, &plvdi->nmcd.nmcd);
  8986. }
  8987. if (plvdi->dwCustom & CDRF_NEWFONT) {
  8988. SelectObject(plvdi->nmcd.nmcd.hdc, plvdi->plv->hfontLabel);
  8989. plvdi->plv->flags |= LVF_CUSTOMFONT;
  8990. }
  8991. }
  8992. return bRet;
  8993. }
  8994. // NOTE: this function requires a properly selected font.
  8995. //
  8996. void WINAPI SHDrawText(HDC hdc, LPCTSTR pszText, RECT FAR* prc, int fmt,
  8997. UINT flags, int cyChar, int cxEllipses, COLORREF clrText, COLORREF clrTextBk)
  8998. {
  8999. int cchText;
  9000. COLORREF clrSave, clrSaveBk = 0;
  9001. RECT rc;
  9002. UINT uETOFlags = 0;
  9003. BOOL fForeOnly = FALSE;
  9004. TCHAR ach[CCHLABELMAX + CCHELLIPSES];
  9005. #ifdef WINDOWS_ME
  9006. int align;
  9007. #endif
  9008. // REVIEW: Performance idea:
  9009. // We could cache the currently selected text color
  9010. // so we don't have to set and restore it each time
  9011. // when the color is the same.
  9012. //
  9013. if (!pszText)
  9014. return;
  9015. if (IsRectEmpty(prc))
  9016. return;
  9017. #ifdef WINDOWS_ME
  9018. if (flags & SHDT_RTLREADING) {
  9019. align = GetTextAlign(hdc);
  9020. SetTextAlign(hdc, align | TA_RTLREADING);
  9021. }
  9022. #endif
  9023. rc = *prc;
  9024. // If needed, add in a little extra margin...
  9025. //
  9026. if (flags & SHDT_EXTRAMARGIN)
  9027. {
  9028. rc.left += g_cxLabelMargin * 3;
  9029. rc.right -= g_cxLabelMargin * 3;
  9030. }
  9031. else
  9032. {
  9033. rc.left += g_cxLabelMargin;
  9034. rc.right -= g_cxLabelMargin;
  9035. }
  9036. if ((rc.left >= rc.right) && !(flags & (SHDT_SELECTED | SHDT_DESELECTED | SHDT_SELECTNOFOCUS)))
  9037. return;
  9038. if ((flags & SHDT_ELLIPSES) &&
  9039. ListView_NeedsEllipses(hdc, pszText, &rc, &cchText, cxEllipses))
  9040. {
  9041. // In some cases cchText was comming back bigger than
  9042. // ARRYASIZE(ach), so we need to make sure we don't overflow the buffer
  9043. // if cchText is too big for the buffer, truncate it down to size
  9044. if (cchText >= ARRAYSIZE(ach) - CCHELLIPSES)
  9045. cchText = ARRAYSIZE(ach) - CCHELLIPSES - 1;
  9046. hmemcpy(ach, pszText, cchText * sizeof(TCHAR));
  9047. lstrcpy(ach + cchText, c_szEllipses);
  9048. pszText = ach;
  9049. // Left-justify, in case there's no room for all of ellipses
  9050. //
  9051. fmt = LVCFMT_LEFT;
  9052. cchText += CCHELLIPSES;
  9053. }
  9054. else
  9055. {
  9056. cchText = lstrlen(pszText);
  9057. }
  9058. if (((clrTextBk == CLR_NONE) && !(flags & (SHDT_SELECTED | SHDT_SELECTNOFOCUS))) || (flags & SHDT_TRANSPARENT))
  9059. {
  9060. fForeOnly = TRUE;
  9061. clrSave = SetTextColor(hdc, (flags & SHDT_TRANSPARENT) ? 0 : clrText);
  9062. }
  9063. else
  9064. {
  9065. HBRUSH hbrUse = NULL;
  9066. HBRUSH hbrDelete = NULL;
  9067. uETOFlags |= ETO_OPAQUE;
  9068. if (flags & SHDT_SELECTED)
  9069. {
  9070. clrText = g_clrHighlightText;
  9071. clrTextBk = (flags & SHDT_HOTSELECTED) ? GetSysColor(COLOR_HOTLIGHT) : g_clrHighlight;
  9072. if (flags & SHDT_DRAWTEXT)
  9073. hbrUse = (flags & SHDT_HOTSELECTED) ? GetSysColorBrush(COLOR_HOTLIGHT) : g_hbrHighlight;
  9074. }
  9075. else if (flags & SHDT_SELECTNOFOCUS)
  9076. {
  9077. if ((clrTextBk == CLR_DEFAULT ? g_clrWindow : clrTextBk) == g_clrBtnFace)
  9078. {
  9079. // if the text background color in this mode is the same as the current
  9080. // background, use the color highlight text so that you can actually see somehting
  9081. clrText = g_clrHighlightText;
  9082. clrTextBk = g_clrHighlight;
  9083. if (flags & SHDT_DRAWTEXT)
  9084. hbrUse = g_hbrHighlight;
  9085. }
  9086. else
  9087. {
  9088. clrText = g_clrBtnText;
  9089. clrTextBk = g_clrBtnFace;
  9090. if (flags & SHDT_DRAWTEXT)
  9091. hbrUse = g_hbrBtnFace;
  9092. }
  9093. #ifdef LVDEBUG
  9094. if (GetAsyncKeyState(VK_CONTROL) < 0)
  9095. clrText = g_clrBtnHighlight;
  9096. #endif
  9097. }
  9098. else if (clrText == CLR_DEFAULT && clrTextBk == CLR_DEFAULT)
  9099. {
  9100. clrText = g_clrWindowText;
  9101. clrTextBk = g_clrWindow;
  9102. if ( ( flags & (SHDT_DRAWTEXT | SHDT_DESELECTED) ) ==
  9103. (SHDT_DRAWTEXT | SHDT_DESELECTED) )
  9104. {
  9105. hbrUse = g_hbrWindow;
  9106. }
  9107. }
  9108. else
  9109. {
  9110. if (clrText == CLR_DEFAULT)
  9111. clrText = g_clrWindowText;
  9112. if (clrTextBk == CLR_DEFAULT)
  9113. clrTextBk = g_clrWindow;
  9114. if ( ( flags & (SHDT_DRAWTEXT | SHDT_DESELECTED) ) ==
  9115. (SHDT_DRAWTEXT | SHDT_DESELECTED) )
  9116. {
  9117. hbrUse = CreateSolidBrush(GetNearestColor(hdc, clrTextBk));
  9118. if (hbrUse)
  9119. {
  9120. hbrDelete = hbrUse;
  9121. }
  9122. else
  9123. hbrUse = GetStockObject( WHITE_BRUSH );
  9124. }
  9125. }
  9126. // now set it
  9127. clrSave = SetTextColor(hdc, clrText);
  9128. clrSaveBk = SetBkColor(hdc, clrTextBk);
  9129. if (hbrUse) {
  9130. FillRect(hdc, prc, hbrUse);
  9131. if (hbrDelete)
  9132. DeleteObject(hbrDelete);
  9133. }
  9134. }
  9135. // If we want the item to display as if it was depressed, we will
  9136. // offset the text rectangle down and to the left
  9137. if (flags & SHDT_DEPRESSED)
  9138. OffsetRect(&rc, g_cxBorder, g_cyBorder);
  9139. if (flags & SHDT_DRAWTEXT)
  9140. {
  9141. UINT uDTFlags = DT_LVWRAP | DT_END_ELLIPSIS;
  9142. if (flags & SHDT_DTELLIPSIS)
  9143. uDTFlags |= DT_WORD_ELLIPSIS;
  9144. if ( !( flags & SHDT_CLIPPED ) )
  9145. uDTFlags |= DT_NOCLIP;
  9146. if (flags & SHDT_NODBCSBREAK)
  9147. uDTFlags |= DT_NOFULLWIDTHCHARBREAK;
  9148. DrawText(hdc, pszText, cchText, &rc, uDTFlags );
  9149. }
  9150. else
  9151. {
  9152. if (fmt != LVCFMT_LEFT)
  9153. {
  9154. SIZE siz;
  9155. GetTextExtentPoint(hdc, pszText, cchText, &siz);
  9156. if (fmt == LVCFMT_CENTER)
  9157. rc.left = (rc.left + rc.right - siz.cx) / 2;
  9158. else // fmt == LVCFMT_RIGHT
  9159. rc.left = rc.right - siz.cx;
  9160. }
  9161. // Center vertically in case the bitmap (to the left) is larger than
  9162. // the height of one line
  9163. rc.top += (rc.bottom - rc.top - cyChar) / 2;
  9164. if ( flags & SHDT_CLIPPED )
  9165. uETOFlags |= ETO_CLIPPED;
  9166. ExtTextOut(hdc, rc.left, rc.top, uETOFlags, prc, pszText, cchText, NULL);
  9167. }
  9168. if (flags & (SHDT_SELECTED | SHDT_DESELECTED | SHDT_TRANSPARENT))
  9169. {
  9170. SetTextColor(hdc, clrSave);
  9171. if (!fForeOnly)
  9172. SetBkColor(hdc, clrSaveBk);
  9173. }
  9174. #ifdef WINDOWS_ME
  9175. if (flags & SHDT_RTLREADING)
  9176. SetTextAlign(hdc, align);
  9177. #endif
  9178. }
  9179. /*----------------------------------------------------------------
  9180. ** Create an imagelist to be used for dragging.
  9181. **
  9182. ** 1) create mask and image bitmap matching the select bounds size
  9183. ** 2) draw the text to both bitmaps (in black for now)
  9184. ** 3) create an imagelist with these bitmaps
  9185. ** 4) make a dithered copy of the image onto the new imagelist
  9186. **----------------------------------------------------------------*/
  9187. HIMAGELIST NEAR ListView_OnCreateDragImage(LV *plv, int iItem, LPPOINT lpptUpLeft)
  9188. {
  9189. HWND hwndLV = plv->ci.hwnd;
  9190. RECT rcBounds, rcImage, rcLabel;
  9191. HDC hdcMem = NULL;
  9192. HBITMAP hbmImage = NULL;
  9193. HBITMAP hbmMask = NULL;
  9194. HBITMAP hbmOld;
  9195. HIMAGELIST himl = NULL;
  9196. int dx, dy;
  9197. HIMAGELIST himlSrc;
  9198. LV_ITEM item;
  9199. POINT ptOrg;
  9200. LVDRAWITEM lvdi;
  9201. RECT rcSelBounds;
  9202. BOOL bMirroredWnd = (plv->ci.dwExStyle&RTL_MIRRORED_WINDOW);
  9203. if (!lpptUpLeft)
  9204. return NULL;
  9205. if (iItem >= ListView_Count(plv))
  9206. return NULL;
  9207. if (plv->iHot == iItem) {
  9208. ListView_OnSetHotItem(plv, -1);
  9209. UpdateWindow(plv->ci.hwnd);
  9210. }
  9211. ListView_GetRects(plv, iItem, &rcImage, &rcLabel, &rcBounds, &rcSelBounds);
  9212. if (ListView_IsIconView(plv)) {
  9213. ListView_UnfoldRects(plv, iItem, &rcImage, &rcLabel,
  9214. &rcBounds, &rcSelBounds);
  9215. InflateRect(&rcImage, -g_cxIconMargin, -g_cyIconMargin);
  9216. }
  9217. // chop off any extra filler above icon
  9218. ptOrg.x = rcBounds.left - rcSelBounds.left;
  9219. ptOrg.y = rcBounds.top - rcImage.top;
  9220. dx = rcSelBounds.right - rcSelBounds.left;
  9221. dy = rcSelBounds.bottom - rcImage.top;
  9222. lpptUpLeft->x = rcSelBounds.left;
  9223. lpptUpLeft->y = rcImage.top;
  9224. if (!(hdcMem = CreateCompatibleDC(NULL)))
  9225. goto CDI_Exit;
  9226. if (!(hbmImage = CreateColorBitmap(dx, dy)))
  9227. goto CDI_Exit;
  9228. if (!(hbmMask = CreateMonoBitmap(dx, dy)))
  9229. goto CDI_Exit;
  9230. //
  9231. // Mirror the memory DC so that the transition from
  9232. // mirrored(memDC)->non-mirrored(imagelist DCs)->mirrored(screenDC)
  9233. // is consistent. [samera]
  9234. //
  9235. if (bMirroredWnd)
  9236. {
  9237. SET_DC_RTL_MIRRORED(hdcMem);
  9238. }
  9239. // prepare for drawing the item
  9240. SelectObject(hdcMem, plv->hfontLabel);
  9241. SetBkMode(hdcMem, TRANSPARENT);
  9242. lvdi.plv = plv;
  9243. lvdi.nmcd.nmcd.dwItemSpec = iItem;
  9244. lvdi.pitem = NULL; // make sure it is null as Owner data uses this to trigger things...
  9245. lvdi.nmcd.nmcd.hdc = hdcMem;
  9246. lvdi.lpptOrg = &ptOrg;
  9247. lvdi.prcClip = NULL;
  9248. lvdi.flags = LVDI_NOIMAGE | LVDI_TRANSTEXT | LVDI_NOWAYFOCUS | LVDI_UNFOLDED;
  9249. /*
  9250. ** draw the text to both bitmaps
  9251. */
  9252. hbmOld = SelectObject(hdcMem, hbmImage);
  9253. // fill image with black for transparency
  9254. PatBlt(hdcMem, 0, 0, dx, dy, BLACKNESS);
  9255. ListView_DrawItem(&lvdi);
  9256. if (bMirroredWnd)
  9257. MirrorBitmapInDC(hdcMem, hbmImage);
  9258. lvdi.flags = LVDI_NOIMAGE | LVDI_TRANSTEXT | LVDI_NOWAYFOCUS | LVDI_UNFOLDED;
  9259. SelectObject(hdcMem, hbmMask);
  9260. // fill mask with white for transparency
  9261. PatBlt(hdcMem, 0, 0, dx, dy, WHITENESS);
  9262. ListView_DrawItem(&lvdi);
  9263. if (bMirroredWnd)
  9264. MirrorBitmapInDC(hdcMem, hbmMask);
  9265. // unselect objects that we used
  9266. SelectObject(hdcMem, hbmOld);
  9267. SelectObject(hdcMem, g_hfontSystem);
  9268. himlSrc = ListView_OnGetImageList(plv, !(ListView_IsIconView(plv)));
  9269. /*
  9270. ** make an image list that for now only has the text
  9271. ** we use ImageList_Clone so we get a imagelist that
  9272. ** the same color depth as our own imagelist
  9273. */
  9274. if (!(himl = ImageList_Clone(himlSrc, dx, dy, ILC_MASK, 1, 0)))
  9275. goto CDI_Exit;
  9276. ImageList_SetBkColor(himl, CLR_NONE);
  9277. ImageList_Add(himl, hbmImage, hbmMask);
  9278. /*
  9279. ** make a dithered copy of the image part onto our bitmaps
  9280. ** (need both bitmap and mask to be dithered)
  9281. */
  9282. if (himlSrc)
  9283. {
  9284. item.iItem = iItem;
  9285. item.iSubItem = 0;
  9286. item.mask = LVIF_IMAGE |LVIF_STATE;
  9287. item.stateMask = LVIS_OVERLAYMASK;
  9288. ListView_OnGetItem(plv, &item);
  9289. ImageList_CopyDitherImage(himl, 0, rcImage.left - rcSelBounds.left, 0, himlSrc, item.iImage, ((plv->ci.dwExStyle & dwExStyleRTLMirrorWnd) ? ILD_MIRROR : 0L) | (item.state & LVIS_OVERLAYMASK) );
  9290. }
  9291. CDI_Exit:
  9292. if (hdcMem)
  9293. DeleteObject(hdcMem);
  9294. if (hbmImage)
  9295. DeleteObject(hbmImage);
  9296. if (hbmMask)
  9297. DeleteObject(hbmMask);
  9298. return himl;
  9299. }
  9300. //-------------------------------------------------------------------
  9301. // ListView_OnGetTopIndex -- Gets the index of the first visible item
  9302. // For list view and report view this calculates the actual index
  9303. // for iconic views it alway returns 0
  9304. //
  9305. int NEAR ListView_OnGetTopIndex(LV* plv)
  9306. {
  9307. if (ListView_IsReportView(plv))
  9308. return (int)((plv->ptlRptOrigin.y) / plv->cyItem);
  9309. else if (ListView_IsListView(plv))
  9310. return (plv->xOrigin / plv->cxItem) * plv->cItemCol;
  9311. else
  9312. return(0);
  9313. }
  9314. //-------------------------------------------------------------------
  9315. // ListView_OnGetCountPerPage -- Gets the count of items that will fit
  9316. // on a page For list view and report view this calculates the
  9317. // count depending on the size of the window and for Iconic views it
  9318. // will always return the count of items in the list view.
  9319. //
  9320. int NEAR ListView_OnGetCountPerPage(LV* plv)
  9321. {
  9322. if (ListView_IsReportView(plv))
  9323. return (plv->sizeClient.cy - plv->yTop) / plv->cyItem;
  9324. else if (ListView_IsListView(plv))
  9325. return ((plv->sizeClient.cx)/ plv->cxItem)
  9326. * plv->cItemCol;
  9327. else
  9328. return (ListView_Count(plv));
  9329. }
  9330. /*----------------------------------------------------------------------------
  9331. / ListView_InvalidateFoldedItem implementation
  9332. / -----------------------------
  9333. / Purpose:
  9334. / Provides support for invalidating items within list views.
  9335. /
  9336. / Notes:
  9337. / Copes with invalidating the extra region in the list view that requires
  9338. / us to erase the background. Design to optimise out the ERASURE of the
  9339. / background.
  9340. /
  9341. / For details on the API see ListView_InvalidateItem.
  9342. /
  9343. / In:
  9344. / plv->ListView structure to work with
  9345. / iItem = item number
  9346. / bSrelectionOnly = refesh the selection
  9347. / fRedraw = Flags for RedrawWindow
  9348. / Out:
  9349. / -
  9350. /----------------------------------------------------------------------------*/
  9351. void NEAR ListView_InvalidateFoldedItem(LV* plv, int iItem, BOOL fSelectionOnly, UINT fRedraw)
  9352. {
  9353. ListView_InvalidateItem( plv, iItem, fSelectionOnly, fRedraw );
  9354. if ( ListView_IsIconView(plv) &&
  9355. ( !ListView_IsItemUnfolded(plv, iItem) || (fRedraw & RDW_ERASE) ) )
  9356. {
  9357. RECT rcLabel;
  9358. if (ListView_GetUnfoldedRect(plv, iItem, &rcLabel))
  9359. {
  9360. RedrawWindow(plv->ci.hwnd, &rcLabel, NULL, fRedraw|RDW_ERASE);
  9361. }
  9362. }
  9363. }
  9364. /*----------------------------------------------------------------------------
  9365. / ListView_UnfoldedRects implementation
  9366. / ----------------------
  9367. / Purpose:
  9368. / Having previously called get rects, then call this function to ensure
  9369. / that they are correctly unfolded.
  9370. /
  9371. / Notes:
  9372. / -
  9373. /
  9374. / In:
  9375. / plv-> list view to unfold on
  9376. / iItem = item number
  9377. / prcIcon -> icon bounding box
  9378. / prcLabel -> rectangle for the label structure
  9379. / prcBounds -> bounds rectangle / == NULL for none / These are currently the same for large icons
  9380. / prcSelectBounds -> selection bounds / == NULL /
  9381. / Out: TRUE if unfolding the item was worth anything
  9382. / -
  9383. /----------------------------------------------------------------------------*/
  9384. BOOL NEAR ListView_UnfoldRects(LV* plv, int iItem,
  9385. RECT * prcIcon, RECT * prcLabel,
  9386. RECT * prcBounds, RECT * prcSelectBounds)
  9387. {
  9388. LISTITEM item;
  9389. LISTITEM FAR* pitem = &item;
  9390. BOOL fRc = FALSE;
  9391. if (!ListView_IsIconView(plv))
  9392. return fRc;
  9393. // If we have a label pointer then expand as required
  9394. // nb - different paths for owner data
  9395. if ( prcLabel )
  9396. {
  9397. if ( !ListView_IsOwnerData(plv) )
  9398. {
  9399. pitem = ListView_GetItemPtr(plv, iItem);
  9400. if (!EVAL(pitem)) {
  9401. // DavidShi was able to get us into here with an invalid
  9402. // item number during a delete notification. So if the
  9403. // item number is invalid, just return a blank rectangle
  9404. // instead of faulting.
  9405. SetRectEmpty(prcLabel);
  9406. goto doneLabel;
  9407. }
  9408. }
  9409. else
  9410. {
  9411. ListView_RecomputeLabelSize( plv, pitem, iItem, NULL, FALSE );
  9412. }
  9413. if (prcLabel->bottom != prcLabel->top + max(pitem->cyUnfoldedLabel, pitem->cyFoldedLabel))
  9414. fRc = TRUE;
  9415. prcLabel->bottom = prcLabel->top + pitem->cyUnfoldedLabel;
  9416. }
  9417. doneLabel:
  9418. // Build the unions if required
  9419. if ( prcBounds && prcIcon && prcLabel )
  9420. {
  9421. UnionRect( prcBounds, prcIcon, prcLabel );
  9422. }
  9423. if ( prcSelectBounds && prcIcon && prcLabel )
  9424. {
  9425. UnionRect( prcSelectBounds, prcIcon, prcLabel );
  9426. }
  9427. return fRc;
  9428. }