wined3d: Use a separate STATE_VDECL state handler in the GLSL pipeline.
[wine/multimedia.git] / dlls / comctl32 / listview.c
blob57f19e4588f5aa858c21a6de3fc0ec89e1b542a2
1 /*
2 * Listview control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
9 * Copyright 2009-2014 Nikolay Sivov
10 * Copyright 2009 Owen Rudge for CodeWeavers
11 * Copyright 2012-2013 Daniel Jelinski
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
29 * This code was audited for completeness against the documented features
30 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
32 * Unless otherwise noted, we believe this code to be complete, as per
33 * the specification mentioned above.
34 * If you discover missing features, or bugs, please note them below.
36 * TODO:
38 * Default Message Processing
39 * -- WM_CREATE: create the icon and small icon image lists at this point only if
40 * the LVS_SHAREIMAGELISTS style is not specified.
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
43 * -- WM_TIMER
44 * -- WM_WININICHANGE
46 * Features
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
49 * -- Tilemode support
50 * -- Groups support
52 * Bugs
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
58 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
60 * Speedups
61 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
62 * linear in the number of items in the list, and this is
63 * unacceptable for large lists.
64 * -- if list is sorted by item text LISTVIEW_InsertItemT could use
65 * binary search to calculate item index (e.g. DPA_Search()).
66 * This requires sorted state to be reliably tracked in item modifiers.
67 * -- we should keep an ordered array of coordinates in iconic mode
68 * this would allow to frame items (iterator_frameditems),
69 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
71 * Flags
72 * -- LVIF_COLUMNS
73 * -- LVIF_GROUPID
75 * States
76 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
77 * -- LVIS_DROPHILITED
79 * Styles
80 * -- LVS_NOLABELWRAP
81 * -- LVS_NOSCROLL (see Q137520)
82 * -- LVS_ALIGNTOP
84 * Extended Styles
85 * -- LVS_EX_BORDERSELECT
86 * -- LVS_EX_FLATSB
87 * -- LVS_EX_INFOTIP
88 * -- LVS_EX_LABELTIP
89 * -- LVS_EX_MULTIWORKAREAS
90 * -- LVS_EX_REGIONAL
91 * -- LVS_EX_SIMPLESELECT
92 * -- LVS_EX_TWOCLICKACTIVATE
93 * -- LVS_EX_UNDERLINECOLD
94 * -- LVS_EX_UNDERLINEHOT
96 * Notifications:
97 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
98 * -- LVN_GETINFOTIP
99 * -- LVN_HOTTRACK
100 * -- LVN_SETDISPINFO
102 * Messages:
103 * -- LVM_ENABLEGROUPVIEW
104 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
105 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
106 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
107 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
108 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
109 * -- LVM_GETINSERTMARKRECT
110 * -- LVM_GETNUMBEROFWORKAREAS
111 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
112 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
113 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
114 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
115 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
116 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
117 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
118 * -- LVM_INSERTGROUPSORTED
119 * -- LVM_INSERTMARKHITTEST
120 * -- LVM_ISGROUPVIEWENABLED
121 * -- LVM_MOVEGROUP
122 * -- LVM_MOVEITEMTOGROUP
123 * -- LVM_SETINFOTIP
124 * -- LVM_SETTILEWIDTH
125 * -- LVM_SORTGROUPS
127 * Macros:
128 * -- ListView_GetHoverTime, ListView_SetHoverTime
129 * -- ListView_GetISearchString
130 * -- ListView_GetNumberOfWorkAreas
131 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
133 * Functions:
134 * -- LVGroupComparE
137 #include "config.h"
138 #include "wine/port.h"
140 #include <assert.h>
141 #include <ctype.h>
142 #include <string.h>
143 #include <stdlib.h>
144 #include <stdarg.h>
145 #include <stdio.h>
147 #include "windef.h"
148 #include "winbase.h"
149 #include "winnt.h"
150 #include "wingdi.h"
151 #include "winuser.h"
152 #include "winnls.h"
153 #include "commctrl.h"
154 #include "comctl32.h"
155 #include "uxtheme.h"
157 #include "wine/debug.h"
158 #include "wine/unicode.h"
160 WINE_DEFAULT_DEBUG_CHANNEL(listview);
162 typedef struct tagCOLUMN_INFO
164 RECT rcHeader; /* tracks the header's rectangle */
165 INT fmt; /* same as LVCOLUMN.fmt */
166 INT cxMin;
167 } COLUMN_INFO;
169 typedef struct tagITEMHDR
171 LPWSTR pszText;
172 INT iImage;
173 } ITEMHDR, *LPITEMHDR;
175 typedef struct tagSUBITEM_INFO
177 ITEMHDR hdr;
178 INT iSubItem;
179 } SUBITEM_INFO;
181 typedef struct tagITEM_ID ITEM_ID;
183 typedef struct tagITEM_INFO
185 ITEMHDR hdr;
186 UINT state;
187 LPARAM lParam;
188 INT iIndent;
189 ITEM_ID *id;
190 } ITEM_INFO;
192 struct tagITEM_ID
194 UINT id; /* item id */
195 HDPA item; /* link to item data */
198 typedef struct tagRANGE
200 INT lower;
201 INT upper;
202 } RANGE;
204 typedef struct tagRANGES
206 HDPA hdpa;
207 } *RANGES;
209 typedef struct tagITERATOR
211 INT nItem;
212 INT nSpecial;
213 RANGE range;
214 RANGES ranges;
215 INT index;
216 } ITERATOR;
218 typedef struct tagDELAYED_ITEM_EDIT
220 BOOL fEnabled;
221 INT iItem;
222 } DELAYED_ITEM_EDIT;
224 typedef struct tagLISTVIEW_INFO
226 /* control window */
227 HWND hwndSelf;
228 RECT rcList; /* This rectangle is really the window
229 * client rectangle possibly reduced by the
230 * horizontal scroll bar and/or header - see
231 * LISTVIEW_UpdateSize. This rectangle offset
232 * by the LISTVIEW_GetOrigin value is in
233 * client coordinates */
235 /* notification window */
236 SHORT notifyFormat;
237 HWND hwndNotify;
238 BOOL bDoChangeNotify; /* send change notification messages? */
239 UINT uCallbackMask;
241 /* tooltips */
242 HWND hwndToolTip;
244 /* items */
245 INT nItemCount; /* the number of items in the list */
246 HDPA hdpaItems; /* array ITEM_INFO pointers */
247 HDPA hdpaItemIds; /* array of ITEM_ID pointers */
248 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
249 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
250 RANGES selectionRanges;
251 INT nSelectionMark; /* item to start next multiselection from */
252 INT nHotItem;
253 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
255 /* columns */
256 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
257 BOOL colRectsDirty; /* trigger column rectangles requery from header */
259 /* item metrics */
260 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
261 INT nItemHeight;
262 INT nItemWidth;
264 /* sorting */
265 PFNLVCOMPARE pfnCompare; /* sorting callback pointer */
266 LPARAM lParamSort;
268 /* style */
269 DWORD dwStyle; /* the cached window GWL_STYLE */
270 DWORD dwLvExStyle; /* extended listview style */
271 DWORD uView; /* current view available through LVM_[G,S]ETVIEW */
273 /* edit item */
274 HWND hwndEdit;
275 WNDPROC EditWndProc;
276 INT nEditLabelItem;
277 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
279 /* icons */
280 HIMAGELIST himlNormal;
281 HIMAGELIST himlSmall;
282 HIMAGELIST himlState;
283 SIZE iconSize;
284 BOOL autoSpacing;
285 SIZE iconSpacing;
286 SIZE iconStateSize;
287 POINT currIconPos; /* this is the position next icon will be placed */
289 /* header */
290 HWND hwndHeader;
291 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
293 /* marquee selection */
294 BOOL bMarqueeSelect; /* marquee selection/highlight underway */
295 BOOL bScrolling;
296 RECT marqueeRect; /* absolute coordinates of marquee selection */
297 RECT marqueeDrawRect; /* relative coordinates for drawing marquee */
298 POINT marqueeOrigin; /* absolute coordinates of marquee click origin */
300 /* focus drawing */
301 BOOL bFocus; /* control has focus */
302 INT nFocusedItem;
303 RECT rcFocus; /* focus bounds */
305 /* colors */
306 HBRUSH hBkBrush;
307 COLORREF clrBk;
308 COLORREF clrText;
309 COLORREF clrTextBk;
311 /* font */
312 HFONT hDefaultFont;
313 HFONT hFont;
314 INT ntmHeight; /* Some cached metrics of the font used */
315 INT ntmMaxCharWidth; /* by the listview to draw items */
316 INT nEllipsisWidth;
318 /* mouse operation */
319 BOOL bLButtonDown;
320 BOOL bDragging;
321 POINT ptClickPos; /* point where the user clicked */
322 INT nLButtonDownItem; /* tracks item to reset multiselection on WM_LBUTTONUP */
323 DWORD dwHoverTime;
324 HCURSOR hHotCursor;
325 INT cWheelRemainder;
327 /* keyboard operation */
328 DWORD lastKeyPressTimestamp;
329 WPARAM charCode;
330 INT nSearchParamLength;
331 WCHAR szSearchParam[ MAX_PATH ];
333 /* painting */
334 BOOL bIsDrawing; /* Drawing in progress */
335 INT nMeasureItemHeight; /* WM_MEASUREITEM result */
336 BOOL bRedraw; /* WM_SETREDRAW switch */
338 /* misc */
339 DWORD iVersion; /* CCM_[G,S]ETVERSION */
340 } LISTVIEW_INFO;
343 * constants
345 /* How many we debug buffer to allocate */
346 #define DEBUG_BUFFERS 20
347 /* The size of a single debug buffer */
348 #define DEBUG_BUFFER_SIZE 256
350 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
351 #define SB_INTERNAL -1
353 /* maximum size of a label */
354 #define DISP_TEXT_SIZE 260
356 /* padding for items in list and small icon display modes */
357 #define WIDTH_PADDING 12
359 /* padding for items in list, report and small icon display modes */
360 #define HEIGHT_PADDING 1
362 /* offset of items in report display mode */
363 #define REPORT_MARGINX 2
365 /* padding for icon in large icon display mode
366 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
367 * that HITTEST will see.
368 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
369 * ICON_TOP_PADDING - sum of the two above.
370 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
371 * LABEL_HOR_PADDING - between text and sides of box
372 * LABEL_VERT_PADDING - between bottom of text and end of box
374 * ICON_LR_PADDING - additional width above icon size.
375 * ICON_LR_HALF - half of the above value
377 #define ICON_TOP_PADDING_NOTHITABLE 2
378 #define ICON_TOP_PADDING_HITABLE 2
379 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
380 #define ICON_BOTTOM_PADDING 4
381 #define LABEL_HOR_PADDING 5
382 #define LABEL_VERT_PADDING 7
383 #define ICON_LR_PADDING 16
384 #define ICON_LR_HALF (ICON_LR_PADDING/2)
386 /* default label width for items in list and small icon display modes */
387 #define DEFAULT_LABEL_WIDTH 40
388 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
389 #define MAX_EMPTYTEXT_SELECT_WIDTH 80
391 /* default column width for items in list display mode */
392 #define DEFAULT_COLUMN_WIDTH 128
394 /* Size of "line" scroll for V & H scrolls */
395 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
397 /* Padding between image and label */
398 #define IMAGE_PADDING 2
400 /* Padding behind the label */
401 #define TRAILING_LABEL_PADDING 12
402 #define TRAILING_HEADER_PADDING 11
404 /* Border for the icon caption */
405 #define CAPTION_BORDER 2
407 /* Standard DrawText flags */
408 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
409 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
410 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
412 /* Image index from state */
413 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
415 /* The time in milliseconds to reset the search in the list */
416 #define KEY_DELAY 450
418 /* Dump the LISTVIEW_INFO structure to the debug channel */
419 #define LISTVIEW_DUMP(iP) do { \
420 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
421 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
422 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
423 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
424 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
425 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
426 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
427 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
428 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
429 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
430 } while(0)
432 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
435 * forward declarations
437 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
438 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
439 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
440 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
441 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
442 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
443 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
444 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
445 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
446 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
447 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
448 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
449 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
450 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT);
451 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT);
452 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
453 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
454 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
455 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *, BOOL, BOOL);
456 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *, INT, INT);
458 /******** Text handling functions *************************************/
460 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
461 * text string. The string may be ANSI or Unicode, in which case
462 * the boolean isW tells us the type of the string.
464 * The name of the function tell what type of strings it expects:
465 * W: Unicode, T: ANSI/Unicode - function of isW
468 static inline BOOL is_text(LPCWSTR text)
470 return text != NULL && text != LPSTR_TEXTCALLBACKW;
473 static inline int textlenT(LPCWSTR text, BOOL isW)
475 return !is_text(text) ? 0 :
476 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
479 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
481 if (isDestW)
482 if (isSrcW) lstrcpynW(dest, src, max);
483 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
484 else
485 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
486 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
489 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
491 LPWSTR wstr = (LPWSTR)text;
493 if (!isW && is_text(text))
495 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
496 wstr = Alloc(len * sizeof(WCHAR));
497 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
499 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
500 return wstr;
503 static inline void textfreeT(LPWSTR wstr, BOOL isW)
505 if (!isW && is_text(wstr)) Free (wstr);
509 * dest is a pointer to a Unicode string
510 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
512 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
514 BOOL bResult = TRUE;
516 if (src == LPSTR_TEXTCALLBACKW)
518 if (is_text(*dest)) Free(*dest);
519 *dest = LPSTR_TEXTCALLBACKW;
521 else
523 LPWSTR pszText = textdupTtoW(src, isW);
524 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
525 bResult = Str_SetPtrW(dest, pszText);
526 textfreeT(pszText, isW);
528 return bResult;
532 * compares a Unicode to a Unicode/ANSI text string
534 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
536 if (!aw) return bt ? -1 : 0;
537 if (!bt) return 1;
538 if (aw == LPSTR_TEXTCALLBACKW)
539 return bt == LPSTR_TEXTCALLBACKW ? 1 : -1;
540 if (bt != LPSTR_TEXTCALLBACKW)
542 LPWSTR bw = textdupTtoW(bt, isW);
543 int r = bw ? lstrcmpW(aw, bw) : 1;
544 textfreeT(bw, isW);
545 return r;
548 return 1;
551 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
553 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
554 return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n) - CSTR_EQUAL;
557 /******** Debugging functions *****************************************/
559 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
561 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
562 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
565 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
567 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
568 n = min(textlenT(text, isW), n);
569 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
572 static char* debug_getbuf(void)
574 static int index = 0;
575 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
576 return buffers[index++ % DEBUG_BUFFERS];
579 static inline const char* debugrange(const RANGE *lprng)
581 if (!lprng) return "(null)";
582 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
585 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
587 char* buf = debug_getbuf(), *text = buf;
588 int len, size = DEBUG_BUFFER_SIZE;
590 if (pScrollInfo == NULL) return "(null)";
591 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
592 if (len == -1) goto end; buf += len; size -= len;
593 if (pScrollInfo->fMask & SIF_RANGE)
594 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
595 else len = 0;
596 if (len == -1) goto end; buf += len; size -= len;
597 if (pScrollInfo->fMask & SIF_PAGE)
598 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
599 else len = 0;
600 if (len == -1) goto end; buf += len; size -= len;
601 if (pScrollInfo->fMask & SIF_POS)
602 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
603 else len = 0;
604 if (len == -1) goto end; buf += len; size -= len;
605 if (pScrollInfo->fMask & SIF_TRACKPOS)
606 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
607 else len = 0;
608 if (len == -1) goto end; buf += len;
609 goto undo;
610 end:
611 buf = text + strlen(text);
612 undo:
613 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
614 return text;
617 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
619 if (!plvnm) return "(null)";
620 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
621 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
622 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
623 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
626 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
628 char* buf = debug_getbuf(), *text = buf;
629 int len, size = DEBUG_BUFFER_SIZE;
631 if (lpLVItem == NULL) return "(null)";
632 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
633 if (len == -1) goto end; buf += len; size -= len;
634 if (lpLVItem->mask & LVIF_STATE)
635 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
636 else len = 0;
637 if (len == -1) goto end; buf += len; size -= len;
638 if (lpLVItem->mask & LVIF_TEXT)
639 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
640 else len = 0;
641 if (len == -1) goto end; buf += len; size -= len;
642 if (lpLVItem->mask & LVIF_IMAGE)
643 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
644 else len = 0;
645 if (len == -1) goto end; buf += len; size -= len;
646 if (lpLVItem->mask & LVIF_PARAM)
647 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
648 else len = 0;
649 if (len == -1) goto end; buf += len; size -= len;
650 if (lpLVItem->mask & LVIF_INDENT)
651 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
652 else len = 0;
653 if (len == -1) goto end; buf += len;
654 goto undo;
655 end:
656 buf = text + strlen(text);
657 undo:
658 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
659 return text;
662 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
664 char* buf = debug_getbuf(), *text = buf;
665 int len, size = DEBUG_BUFFER_SIZE;
667 if (lpColumn == NULL) return "(null)";
668 len = snprintf(buf, size, "{");
669 if (len == -1) goto end; buf += len; size -= len;
670 if (lpColumn->mask & LVCF_SUBITEM)
671 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
672 else len = 0;
673 if (len == -1) goto end; buf += len; size -= len;
674 if (lpColumn->mask & LVCF_FMT)
675 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
676 else len = 0;
677 if (len == -1) goto end; buf += len; size -= len;
678 if (lpColumn->mask & LVCF_WIDTH)
679 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
680 else len = 0;
681 if (len == -1) goto end; buf += len; size -= len;
682 if (lpColumn->mask & LVCF_TEXT)
683 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
684 else len = 0;
685 if (len == -1) goto end; buf += len; size -= len;
686 if (lpColumn->mask & LVCF_IMAGE)
687 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
688 else len = 0;
689 if (len == -1) goto end; buf += len; size -= len;
690 if (lpColumn->mask & LVCF_ORDER)
691 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
692 else len = 0;
693 if (len == -1) goto end; buf += len;
694 goto undo;
695 end:
696 buf = text + strlen(text);
697 undo:
698 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
699 return text;
702 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
704 if (!lpht) return "(null)";
706 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
707 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
710 /* Return the corresponding text for a given scroll value */
711 static inline LPCSTR debugscrollcode(int nScrollCode)
713 switch(nScrollCode)
715 case SB_LINELEFT: return "SB_LINELEFT";
716 case SB_LINERIGHT: return "SB_LINERIGHT";
717 case SB_PAGELEFT: return "SB_PAGELEFT";
718 case SB_PAGERIGHT: return "SB_PAGERIGHT";
719 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
720 case SB_THUMBTRACK: return "SB_THUMBTRACK";
721 case SB_ENDSCROLL: return "SB_ENDSCROLL";
722 case SB_INTERNAL: return "SB_INTERNAL";
723 default: return "unknown";
728 /******** Notification functions ************************************/
730 static int get_ansi_notification(UINT unicodeNotificationCode)
732 switch (unicodeNotificationCode)
734 case LVN_BEGINLABELEDITA:
735 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
736 case LVN_ENDLABELEDITA:
737 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
738 case LVN_GETDISPINFOA:
739 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
740 case LVN_SETDISPINFOA:
741 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
742 case LVN_ODFINDITEMA:
743 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
744 case LVN_GETINFOTIPA:
745 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
746 /* header forwards */
747 case HDN_TRACKA:
748 case HDN_TRACKW: return HDN_TRACKA;
749 case HDN_ENDTRACKA:
750 case HDN_ENDTRACKW: return HDN_ENDTRACKA;
751 case HDN_BEGINDRAG: return HDN_BEGINDRAG;
752 case HDN_ENDDRAG: return HDN_ENDDRAG;
753 case HDN_ITEMCHANGINGA:
754 case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
755 case HDN_ITEMCHANGEDA:
756 case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
757 case HDN_ITEMCLICKA:
758 case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
759 case HDN_DIVIDERDBLCLICKA:
760 case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
761 default: break;
763 FIXME("unknown notification %x\n", unicodeNotificationCode);
764 return unicodeNotificationCode;
767 /* forwards header notifications to listview parent */
768 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, NMHEADERW *lpnmhW)
770 LPCWSTR text = NULL, filter = NULL;
771 LRESULT ret;
772 NMHEADERA *lpnmh = (NMHEADERA*) lpnmhW;
774 /* on unicode format exit earlier */
775 if (infoPtr->notifyFormat == NFR_UNICODE)
776 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom,
777 (LPARAM)lpnmh);
779 /* header always supplies unicode notifications,
780 all we have to do is to convert strings to ANSI */
781 if (lpnmh->pitem)
783 /* convert item text */
784 if (lpnmh->pitem->mask & HDI_TEXT)
786 text = (LPCWSTR)lpnmh->pitem->pszText;
787 lpnmh->pitem->pszText = NULL;
788 Str_SetPtrWtoA(&lpnmh->pitem->pszText, text);
790 /* convert filter text */
791 if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
792 lpnmh->pitem->pvFilter)
794 filter = (LPCWSTR)((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText;
795 ((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = NULL;
796 Str_SetPtrWtoA(&((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText, filter);
799 lpnmh->hdr.code = get_ansi_notification(lpnmh->hdr.code);
801 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom,
802 (LPARAM)lpnmh);
804 /* cleanup */
805 if(text)
807 Free(lpnmh->pitem->pszText);
808 lpnmh->pitem->pszText = (LPSTR)text;
810 if(filter)
812 Free(((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText);
813 ((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = (LPSTR)filter;
816 return ret;
819 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
821 LRESULT result;
823 TRACE("(code=%d)\n", code);
825 pnmh->hwndFrom = infoPtr->hwndSelf;
826 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
827 pnmh->code = code;
828 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
830 TRACE(" <= %ld\n", result);
832 return result;
835 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
837 NMHDR nmh;
838 HWND hwnd = infoPtr->hwndSelf;
839 notify_hdr(infoPtr, code, &nmh);
840 return IsWindow(hwnd);
843 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
845 NMITEMACTIVATE nmia;
846 LVITEMW item;
848 if (htInfo) {
849 nmia.uNewState = 0;
850 nmia.uOldState = 0;
851 nmia.uChanged = 0;
852 nmia.uKeyFlags = 0;
854 item.mask = LVIF_PARAM|LVIF_STATE;
855 item.iItem = htInfo->iItem;
856 item.iSubItem = 0;
857 item.stateMask = (UINT)-1;
858 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
859 nmia.lParam = item.lParam;
860 nmia.uOldState = item.state;
861 nmia.uNewState = item.state | LVIS_ACTIVATING;
862 nmia.uChanged = LVIF_STATE;
865 nmia.iItem = htInfo->iItem;
866 nmia.iSubItem = htInfo->iSubItem;
867 nmia.ptAction = htInfo->pt;
869 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
870 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
871 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
873 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
876 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
878 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
879 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
882 /* Handles NM_DBLCLK, NM_CLICK, NM_RDBLCLK, NM_RCLICK. Only NM_RCLICK return value is used. */
883 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
885 NMITEMACTIVATE nmia;
886 LVITEMW item;
887 HWND hwnd = infoPtr->hwndSelf;
888 LRESULT ret;
890 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
891 ZeroMemory(&nmia, sizeof(nmia));
892 nmia.iItem = lvht->iItem;
893 nmia.iSubItem = lvht->iSubItem;
894 nmia.ptAction = lvht->pt;
895 item.mask = LVIF_PARAM;
896 item.iItem = lvht->iItem;
897 item.iSubItem = 0;
898 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
899 ret = notify_hdr(infoPtr, code, (NMHDR*)&nmia);
900 return IsWindow(hwnd) && (code == NM_RCLICK ? !ret : TRUE);
903 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
905 NMLISTVIEW nmlv;
906 LVITEMW item;
907 HWND hwnd = infoPtr->hwndSelf;
909 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
910 nmlv.iItem = nItem;
911 item.mask = LVIF_PARAM;
912 item.iItem = nItem;
913 item.iSubItem = 0;
914 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
915 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
916 return IsWindow(hwnd);
920 Send notification. depends on dispinfoW having same
921 structure as dispinfoA.
922 infoPtr : listview struct
923 code : *Unicode* notification code
924 pdi : dispinfo structure (can be unicode or ansi)
925 isW : TRUE if dispinfo is Unicode
927 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT code, LPNMLVDISPINFOW pdi, BOOL isW)
929 INT length = 0, ret_length;
930 LPWSTR buffer = NULL, ret_text;
931 BOOL return_ansi = FALSE;
932 BOOL return_unicode = FALSE;
933 BOOL ret;
935 if ((pdi->item.mask & LVIF_TEXT) && is_text(pdi->item.pszText))
937 return_unicode = ( isW && infoPtr->notifyFormat == NFR_ANSI);
938 return_ansi = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
941 ret_length = pdi->item.cchTextMax;
942 ret_text = pdi->item.pszText;
944 if (return_unicode || return_ansi)
946 if (code != LVN_GETDISPINFOW)
948 length = return_ansi ?
949 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
950 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
952 else
954 length = pdi->item.cchTextMax;
955 *pdi->item.pszText = 0; /* make sure we don't process garbage */
958 buffer = Alloc( (return_ansi ? sizeof(WCHAR) : sizeof(CHAR)) * length);
959 if (!buffer) return FALSE;
961 if (return_ansi)
962 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
963 buffer, length);
964 else
965 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer,
966 length, NULL, NULL);
968 pdi->item.pszText = buffer;
969 pdi->item.cchTextMax = length;
972 if (infoPtr->notifyFormat == NFR_ANSI)
973 code = get_ansi_notification(code);
975 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
976 ret = notify_hdr(infoPtr, code, &pdi->hdr);
977 TRACE(" resulting code=%d\n", pdi->hdr.code);
979 if (return_ansi || return_unicode)
981 if (return_ansi && (pdi->hdr.code == LVN_GETDISPINFOA))
983 strcpy((char*)ret_text, (char*)pdi->item.pszText);
985 else if (return_unicode && (pdi->hdr.code == LVN_GETDISPINFOW))
987 strcpyW(ret_text, pdi->item.pszText);
989 else if (return_ansi) /* note : pointer can be changed by app ! */
991 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) ret_text,
992 ret_length, NULL, NULL);
994 else
995 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
996 ret_text, ret_length);
998 pdi->item.pszText = ret_text; /* restores our buffer */
999 pdi->item.cchTextMax = ret_length;
1001 Free(buffer);
1002 return ret;
1005 /* if dipsinfo holder changed notification code then convert */
1006 if (!isW && (pdi->hdr.code == LVN_GETDISPINFOW) && (pdi->item.mask & LVIF_TEXT))
1008 length = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
1010 buffer = Alloc(length * sizeof(CHAR));
1011 if (!buffer) return FALSE;
1013 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer,
1014 ret_length, NULL, NULL);
1016 strcpy((LPSTR)pdi->item.pszText, (LPSTR)buffer);
1017 Free(buffer);
1020 return ret;
1023 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
1024 const RECT *rcBounds, const LVITEMW *lplvItem)
1026 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
1027 lpnmlvcd->nmcd.hdc = hdc;
1028 lpnmlvcd->nmcd.rc = *rcBounds;
1029 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
1030 lpnmlvcd->clrText = infoPtr->clrText;
1031 if (!lplvItem) return;
1032 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
1033 lpnmlvcd->iSubItem = lplvItem->iSubItem;
1034 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
1035 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
1036 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
1037 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
1040 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
1042 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
1043 DWORD result;
1045 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
1046 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
1047 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
1048 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
1049 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
1050 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
1051 return result;
1054 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
1056 COLORREF backcolor, textcolor;
1058 /* apparently, for selected items, we have to override the returned values */
1059 if (!SubItem)
1061 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
1063 if (infoPtr->bFocus)
1065 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
1066 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
1068 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
1070 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
1071 lpnmlvcd->clrText = comctl32_color.clrBtnText;
1076 backcolor = lpnmlvcd->clrTextBk;
1077 textcolor = lpnmlvcd->clrText;
1079 if (backcolor == CLR_DEFAULT)
1080 backcolor = comctl32_color.clrWindow;
1081 if (textcolor == CLR_DEFAULT)
1082 textcolor = comctl32_color.clrWindowText;
1084 /* Set the text attributes */
1085 if (backcolor != CLR_NONE)
1087 SetBkMode(hdc, OPAQUE);
1088 SetBkColor(hdc, backcolor);
1090 else
1091 SetBkMode(hdc, TRANSPARENT);
1092 SetTextColor(hdc, textcolor);
1095 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1097 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1100 /* returns TRUE when repaint needed, FALSE otherwise */
1101 static BOOL notify_measureitem(LISTVIEW_INFO *infoPtr)
1103 MEASUREITEMSTRUCT mis;
1104 mis.CtlType = ODT_LISTVIEW;
1105 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1106 mis.itemID = -1;
1107 mis.itemWidth = 0;
1108 mis.itemData = 0;
1109 mis.itemHeight= infoPtr->nItemHeight;
1110 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
1111 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
1113 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
1114 return TRUE;
1116 return FALSE;
1119 /******** Item iterator functions **********************************/
1121 static RANGES ranges_create(int count);
1122 static void ranges_destroy(RANGES ranges);
1123 static BOOL ranges_add(RANGES ranges, RANGE range);
1124 static BOOL ranges_del(RANGES ranges, RANGE range);
1125 static void ranges_dump(RANGES ranges);
1127 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1129 RANGE range = { nItem, nItem + 1 };
1131 return ranges_add(ranges, range);
1134 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1136 RANGE range = { nItem, nItem + 1 };
1138 return ranges_del(ranges, range);
1141 /***
1142 * ITERATOR DOCUMENTATION
1144 * The iterator functions allow for easy, and convenient iteration
1145 * over items of interest in the list. Typically, you create an
1146 * iterator, use it, and destroy it, as such:
1147 * ITERATOR i;
1149 * iterator_xxxitems(&i, ...);
1150 * while (iterator_{prev,next}(&i)
1152 * //code which uses i.nItem
1154 * iterator_destroy(&i);
1156 * where xxx is either: framed, or visible.
1157 * Note that it is important that the code destroys the iterator
1158 * after it's done with it, as the creation of the iterator may
1159 * allocate memory, which thus needs to be freed.
1161 * You can iterate both forwards, and backwards through the list,
1162 * by using iterator_next or iterator_prev respectively.
1164 * Lower numbered items are draw on top of higher number items in
1165 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1166 * items may overlap). So, to test items, you should use
1167 * iterator_next
1168 * which lists the items top to bottom (in Z-order).
1169 * For drawing items, you should use
1170 * iterator_prev
1171 * which lists the items bottom to top (in Z-order).
1172 * If you keep iterating over the items after the end-of-items
1173 * marker (-1) is returned, the iterator will start from the
1174 * beginning. Typically, you don't need to test for -1,
1175 * because iterator_{next,prev} will return TRUE if more items
1176 * are to be iterated over, or FALSE otherwise.
1178 * Note: the iterator is defined to be bidirectional. That is,
1179 * any number of prev followed by any number of next, or
1180 * five versa, should leave the iterator at the same item:
1181 * prev * n, next * n = next * n, prev * n
1183 * The iterator has a notion of an out-of-order, special item,
1184 * which sits at the start of the list. This is used in
1185 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1186 * which needs to be first, as it may overlap other items.
1188 * The code is a bit messy because we have:
1189 * - a special item to deal with
1190 * - simple range, or composite range
1191 * - empty range.
1192 * If you find bugs, or want to add features, please make sure you
1193 * always check/modify *both* iterator_prev, and iterator_next.
1196 /****
1197 * This function iterates through the items in increasing order,
1198 * but prefixed by the special item, then -1. That is:
1199 * special, 1, 2, 3, ..., n, -1.
1200 * Each item is listed only once.
1202 static inline BOOL iterator_next(ITERATOR* i)
1204 if (i->nItem == -1)
1206 i->nItem = i->nSpecial;
1207 if (i->nItem != -1) return TRUE;
1209 if (i->nItem == i->nSpecial)
1211 if (i->ranges) i->index = 0;
1212 goto pickarange;
1215 i->nItem++;
1216 testitem:
1217 if (i->nItem == i->nSpecial) i->nItem++;
1218 if (i->nItem < i->range.upper) return TRUE;
1220 pickarange:
1221 if (i->ranges)
1223 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1224 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1225 else goto end;
1227 else if (i->nItem >= i->range.upper) goto end;
1229 i->nItem = i->range.lower;
1230 if (i->nItem >= 0) goto testitem;
1231 end:
1232 i->nItem = -1;
1233 return FALSE;
1236 /****
1237 * This function iterates through the items in decreasing order,
1238 * followed by the special item, then -1. That is:
1239 * n, n-1, ..., 3, 2, 1, special, -1.
1240 * Each item is listed only once.
1242 static inline BOOL iterator_prev(ITERATOR* i)
1244 BOOL start = FALSE;
1246 if (i->nItem == -1)
1248 start = TRUE;
1249 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1250 goto pickarange;
1252 if (i->nItem == i->nSpecial)
1254 i->nItem = -1;
1255 return FALSE;
1258 testitem:
1259 i->nItem--;
1260 if (i->nItem == i->nSpecial) i->nItem--;
1261 if (i->nItem >= i->range.lower) return TRUE;
1263 pickarange:
1264 if (i->ranges)
1266 if (i->index > 0)
1267 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1268 else goto end;
1270 else if (!start && i->nItem < i->range.lower) goto end;
1272 i->nItem = i->range.upper;
1273 if (i->nItem > 0) goto testitem;
1274 end:
1275 return (i->nItem = i->nSpecial) != -1;
1278 static RANGE iterator_range(const ITERATOR *i)
1280 RANGE range;
1282 if (!i->ranges) return i->range;
1284 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1286 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1287 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1289 else range.lower = range.upper = 0;
1291 return range;
1294 /***
1295 * Releases resources associated with this iterator.
1297 static inline void iterator_destroy(const ITERATOR *i)
1299 ranges_destroy(i->ranges);
1302 /***
1303 * Create an empty iterator.
1305 static inline BOOL iterator_empty(ITERATOR* i)
1307 ZeroMemory(i, sizeof(*i));
1308 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1309 return TRUE;
1312 /***
1313 * Create an iterator over a range.
1315 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1317 iterator_empty(i);
1318 i->range = range;
1319 return TRUE;
1322 /***
1323 * Create an iterator over a bunch of ranges.
1324 * Please note that the iterator will take ownership of the ranges,
1325 * and will free them upon destruction.
1327 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1329 iterator_empty(i);
1330 i->ranges = ranges;
1331 return TRUE;
1334 /***
1335 * Creates an iterator over the items which intersect frame.
1336 * Uses absolute coordinates rather than compensating for the current offset.
1338 static BOOL iterator_frameditems_absolute(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *frame)
1340 RECT rcItem, rcTemp;
1342 /* in case we fail, we want to return an empty iterator */
1343 if (!iterator_empty(i)) return FALSE;
1345 TRACE("(frame=%s)\n", wine_dbgstr_rect(frame));
1347 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1349 INT nItem;
1351 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1353 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1354 if (IntersectRect(&rcTemp, &rcItem, frame))
1355 i->nSpecial = infoPtr->nFocusedItem;
1357 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1358 /* to do better here, we need to have PosX, and PosY sorted */
1359 TRACE("building icon ranges:\n");
1360 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1362 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1363 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1364 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1365 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1366 if (IntersectRect(&rcTemp, &rcItem, frame))
1367 ranges_additem(i->ranges, nItem);
1369 return TRUE;
1371 else if (infoPtr->uView == LV_VIEW_DETAILS)
1373 RANGE range;
1375 if (frame->left >= infoPtr->nItemWidth) return TRUE;
1376 if (frame->top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1378 range.lower = max(frame->top / infoPtr->nItemHeight, 0);
1379 range.upper = min((frame->bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1380 if (range.upper <= range.lower) return TRUE;
1381 if (!iterator_rangeitems(i, range)) return FALSE;
1382 TRACE(" report=%s\n", debugrange(&i->range));
1384 else
1386 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1387 INT nFirstRow = max(frame->top / infoPtr->nItemHeight, 0);
1388 INT nLastRow = min((frame->bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1389 INT nFirstCol;
1390 INT nLastCol;
1391 INT lower;
1392 RANGE item_range;
1393 INT nCol;
1395 if (infoPtr->nItemWidth)
1397 nFirstCol = max(frame->left / infoPtr->nItemWidth, 0);
1398 nLastCol = min((frame->right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1400 else
1402 nFirstCol = max(frame->left, 0);
1403 nLastCol = min(frame->right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1406 lower = nFirstCol * nPerCol + nFirstRow;
1408 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1409 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1411 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1413 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1414 TRACE("building list ranges:\n");
1415 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1417 item_range.lower = nCol * nPerCol + nFirstRow;
1418 if(item_range.lower >= infoPtr->nItemCount) break;
1419 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1420 TRACE(" list=%s\n", debugrange(&item_range));
1421 ranges_add(i->ranges, item_range);
1425 return TRUE;
1428 /***
1429 * Creates an iterator over the items which intersect lprc.
1431 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1433 RECT frame = *lprc;
1434 POINT Origin;
1436 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1438 LISTVIEW_GetOrigin(infoPtr, &Origin);
1439 OffsetRect(&frame, -Origin.x, -Origin.y);
1441 return iterator_frameditems_absolute(i, infoPtr, &frame);
1444 /***
1445 * Creates an iterator over the items which intersect the visible region of hdc.
1447 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1449 POINT Origin, Position;
1450 RECT rcItem, rcClip;
1451 INT rgntype;
1453 rgntype = GetClipBox(hdc, &rcClip);
1454 if (rgntype == NULLREGION) return iterator_empty(i);
1455 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1456 if (rgntype == SIMPLEREGION) return TRUE;
1458 /* first deal with the special item */
1459 if (i->nSpecial != -1)
1461 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1462 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1465 /* if we can't deal with the region, we'll just go with the simple range */
1466 LISTVIEW_GetOrigin(infoPtr, &Origin);
1467 TRACE("building visible range:\n");
1468 if (!i->ranges && i->range.lower < i->range.upper)
1470 if (!(i->ranges = ranges_create(50))) return TRUE;
1471 if (!ranges_add(i->ranges, i->range))
1473 ranges_destroy(i->ranges);
1474 i->ranges = 0;
1475 return TRUE;
1479 /* now delete the invisible items from the list */
1480 while(iterator_next(i))
1482 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1483 rcItem.left = (infoPtr->uView == LV_VIEW_DETAILS) ? Origin.x : Position.x + Origin.x;
1484 rcItem.top = Position.y + Origin.y;
1485 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1486 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1487 if (!RectVisible(hdc, &rcItem))
1488 ranges_delitem(i->ranges, i->nItem);
1490 /* the iterator should restart on the next iterator_next */
1491 TRACE("done\n");
1493 return TRUE;
1496 /* Remove common elements from two iterators */
1497 /* Passed iterators have to point on the first elements */
1498 static BOOL iterator_remove_common_items(ITERATOR *iter1, ITERATOR *iter2)
1500 if(!iter1->ranges || !iter2->ranges) {
1501 int lower, upper;
1503 if(iter1->ranges || iter2->ranges ||
1504 (iter1->range.lower<iter2->range.lower && iter1->range.upper>iter2->range.upper) ||
1505 (iter1->range.lower>iter2->range.lower && iter1->range.upper<iter2->range.upper)) {
1506 ERR("result is not a one range iterator\n");
1507 return FALSE;
1510 if(iter1->range.lower==-1 || iter2->range.lower==-1)
1511 return TRUE;
1513 lower = iter1->range.lower;
1514 upper = iter1->range.upper;
1516 if(lower < iter2->range.lower)
1517 iter1->range.upper = iter2->range.lower;
1518 else if(upper > iter2->range.upper)
1519 iter1->range.lower = iter2->range.upper;
1520 else
1521 iter1->range.lower = iter1->range.upper = -1;
1523 if(iter2->range.lower < lower)
1524 iter2->range.upper = lower;
1525 else if(iter2->range.upper > upper)
1526 iter2->range.lower = upper;
1527 else
1528 iter2->range.lower = iter2->range.upper = -1;
1530 return TRUE;
1533 iterator_next(iter1);
1534 iterator_next(iter2);
1536 while(1) {
1537 if(iter1->nItem==-1 || iter2->nItem==-1)
1538 break;
1540 if(iter1->nItem == iter2->nItem) {
1541 int delete = iter1->nItem;
1543 iterator_prev(iter1);
1544 iterator_prev(iter2);
1545 ranges_delitem(iter1->ranges, delete);
1546 ranges_delitem(iter2->ranges, delete);
1547 iterator_next(iter1);
1548 iterator_next(iter2);
1549 } else if(iter1->nItem > iter2->nItem)
1550 iterator_next(iter2);
1551 else
1552 iterator_next(iter1);
1555 iter1->nItem = iter1->range.lower = iter1->range.upper = -1;
1556 iter2->nItem = iter2->range.lower = iter2->range.upper = -1;
1557 return TRUE;
1560 /******** Misc helper functions ************************************/
1562 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1563 WPARAM wParam, LPARAM lParam, BOOL isW)
1565 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1566 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1569 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1571 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1572 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1575 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1577 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1578 if(state == 1 || state == 2)
1580 LVITEMW lvitem;
1581 state ^= 3;
1582 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1583 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1584 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1588 /* this should be called after window style got updated,
1589 it used to reset view state to match current window style */
1590 static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1592 switch (infoPtr->dwStyle & LVS_TYPEMASK)
1594 case LVS_ICON:
1595 infoPtr->uView = LV_VIEW_ICON;
1596 break;
1597 case LVS_REPORT:
1598 infoPtr->uView = LV_VIEW_DETAILS;
1599 break;
1600 case LVS_SMALLICON:
1601 infoPtr->uView = LV_VIEW_SMALLICON;
1602 break;
1603 case LVS_LIST:
1604 infoPtr->uView = LV_VIEW_LIST;
1608 /* computes next item id value */
1609 static DWORD get_next_itemid(const LISTVIEW_INFO *infoPtr)
1611 INT count = DPA_GetPtrCount(infoPtr->hdpaItemIds);
1613 if (count > 0)
1615 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, count - 1);
1616 return lpID->id + 1;
1618 return 0;
1621 /******** Internal API functions ************************************/
1623 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1625 static COLUMN_INFO mainItem;
1627 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1628 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1630 /* update cached column rectangles */
1631 if (infoPtr->colRectsDirty)
1633 COLUMN_INFO *info;
1634 LISTVIEW_INFO *Ptr = (LISTVIEW_INFO*)infoPtr;
1635 INT i;
1637 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) {
1638 info = DPA_GetPtr(infoPtr->hdpaColumns, i);
1639 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, i, (LPARAM)&info->rcHeader);
1641 Ptr->colRectsDirty = FALSE;
1644 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1647 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1649 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1650 HINSTANCE hInst;
1652 if (infoPtr->hwndHeader) return 0;
1654 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1656 /* setup creation flags */
1657 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1658 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1660 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1662 /* create header */
1663 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1664 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1665 if (!infoPtr->hwndHeader) return -1;
1667 /* set header unicode format */
1668 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1670 /* set header font */
1671 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, TRUE);
1673 /* set header image list */
1674 if (infoPtr->himlSmall)
1675 SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)infoPtr->himlSmall);
1677 LISTVIEW_UpdateSize(infoPtr);
1679 return 0;
1682 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1684 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1687 static inline BOOL LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO *infoPtr)
1689 return (infoPtr->uView == LV_VIEW_DETAILS ||
1690 infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) &&
1691 !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER);
1694 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1696 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1699 /* used to handle collapse main item column case */
1700 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1702 return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1703 DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1706 /* Listview invalidation functions: use _only_ these functions to invalidate */
1708 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1710 return infoPtr->bRedraw;
1713 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1715 if(!is_redrawing(infoPtr)) return;
1716 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1717 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1720 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1722 RECT rcBox;
1724 if(!is_redrawing(infoPtr)) return;
1725 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1726 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1729 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1731 POINT Origin, Position;
1732 RECT rcBox;
1734 if(!is_redrawing(infoPtr)) return;
1735 assert (infoPtr->uView == LV_VIEW_DETAILS);
1736 LISTVIEW_GetOrigin(infoPtr, &Origin);
1737 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1738 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1739 rcBox.top = 0;
1740 rcBox.bottom = infoPtr->nItemHeight;
1741 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1742 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1745 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1747 LISTVIEW_InvalidateRect(infoPtr, NULL);
1750 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1752 RECT rcCol;
1754 if(!is_redrawing(infoPtr)) return;
1755 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1756 rcCol.top = infoPtr->rcList.top;
1757 rcCol.bottom = infoPtr->rcList.bottom;
1758 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1761 /***
1762 * DESCRIPTION:
1763 * Retrieves the number of items that can fit vertically in the client area.
1765 * PARAMETER(S):
1766 * [I] infoPtr : valid pointer to the listview structure
1768 * RETURN:
1769 * Number of items per row.
1771 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1773 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1775 return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1778 /***
1779 * DESCRIPTION:
1780 * Retrieves the number of items that can fit horizontally in the client
1781 * area.
1783 * PARAMETER(S):
1784 * [I] infoPtr : valid pointer to the listview structure
1786 * RETURN:
1787 * Number of items per column.
1789 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1791 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1793 return max(nListHeight / infoPtr->nItemHeight, 1);
1797 /*************************************************************************
1798 * LISTVIEW_ProcessLetterKeys
1800 * Processes keyboard messages generated by pressing the letter keys
1801 * on the keyboard.
1802 * What this does is perform a case insensitive search from the
1803 * current position with the following quirks:
1804 * - If two chars or more are pressed in quick succession we search
1805 * for the corresponding string (e.g. 'abc').
1806 * - If there is a delay we wipe away the current search string and
1807 * restart with just that char.
1808 * - If the user keeps pressing the same character, whether slowly or
1809 * fast, so that the search string is entirely composed of this
1810 * character ('aaaaa' for instance), then we search for first item
1811 * that starting with that character.
1812 * - If the user types the above character in quick succession, then
1813 * we must also search for the corresponding string ('aaaaa'), and
1814 * go to that string if there is a match.
1816 * PARAMETERS
1817 * [I] hwnd : handle to the window
1818 * [I] charCode : the character code, the actual character
1819 * [I] keyData : key data
1821 * RETURNS
1823 * Zero.
1825 * BUGS
1827 * - The current implementation has a list of characters it will
1828 * accept and it ignores everything else. In particular it will
1829 * ignore accentuated characters which seems to match what
1830 * Windows does. But I'm not sure it makes sense to follow
1831 * Windows there.
1832 * - We don't sound a beep when the search fails.
1834 * SEE ALSO
1836 * TREEVIEW_ProcessLetterKeys
1838 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1840 WCHAR buffer[MAX_PATH];
1841 DWORD prevTime;
1842 LVITEMW item;
1843 int startidx;
1844 INT nItem;
1845 INT diff;
1847 /* simple parameter checking */
1848 if (!charCode || !keyData || infoPtr->nItemCount == 0) return 0;
1850 /* only allow the valid WM_CHARs through */
1851 if (!isalnumW(charCode) &&
1852 charCode != '.' && charCode != '`' && charCode != '!' &&
1853 charCode != '@' && charCode != '#' && charCode != '$' &&
1854 charCode != '%' && charCode != '^' && charCode != '&' &&
1855 charCode != '*' && charCode != '(' && charCode != ')' &&
1856 charCode != '-' && charCode != '_' && charCode != '+' &&
1857 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1858 charCode != '}' && charCode != '[' && charCode != '{' &&
1859 charCode != '/' && charCode != '?' && charCode != '>' &&
1860 charCode != '<' && charCode != ',' && charCode != '~')
1861 return 0;
1863 /* update the search parameters */
1864 prevTime = infoPtr->lastKeyPressTimestamp;
1865 infoPtr->lastKeyPressTimestamp = GetTickCount();
1866 diff = infoPtr->lastKeyPressTimestamp - prevTime;
1868 if (diff >= 0 && diff < KEY_DELAY)
1870 if (infoPtr->nSearchParamLength < MAX_PATH - 1)
1871 infoPtr->szSearchParam[infoPtr->nSearchParamLength++] = charCode;
1873 if (infoPtr->charCode != charCode)
1874 infoPtr->charCode = charCode = 0;
1876 else
1878 infoPtr->charCode = charCode;
1879 infoPtr->szSearchParam[0] = charCode;
1880 infoPtr->nSearchParamLength = 1;
1883 /* should start from next after focused item, so next item that matches
1884 will be selected, if there isn't any and focused matches it will be selected
1885 on second search stage from beginning of the list */
1886 if (infoPtr->nFocusedItem >= 0 && infoPtr->nItemCount > 1)
1888 /* with some accumulated search data available start with current focus, otherwise
1889 it's excluded from search */
1890 startidx = infoPtr->nSearchParamLength > 1 ? infoPtr->nFocusedItem : infoPtr->nFocusedItem + 1;
1891 if (startidx == infoPtr->nItemCount) startidx = 0;
1893 else
1894 startidx = 0;
1896 /* let application handle this for virtual listview */
1897 if (infoPtr->dwStyle & LVS_OWNERDATA)
1899 NMLVFINDITEMW nmlv;
1901 memset(&nmlv.lvfi, 0, sizeof(nmlv.lvfi));
1902 nmlv.lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1903 nmlv.lvfi.psz = infoPtr->szSearchParam;
1904 nmlv.iStart = startidx;
1906 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = 0;
1908 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1910 else
1912 int i = startidx, endidx;
1914 /* and search from the current position */
1915 nItem = -1;
1916 endidx = infoPtr->nItemCount;
1918 /* first search in [startidx, endidx), on failure continue in [0, startidx) */
1919 while (1)
1921 /* start from first item if not found with >= startidx */
1922 if (i == infoPtr->nItemCount && startidx > 0)
1924 endidx = startidx;
1925 startidx = 0;
1928 for (i = startidx; i < endidx; i++)
1930 /* retrieve text */
1931 item.mask = LVIF_TEXT;
1932 item.iItem = i;
1933 item.iSubItem = 0;
1934 item.pszText = buffer;
1935 item.cchTextMax = MAX_PATH;
1936 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1938 if (!lstrncmpiW(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength))
1940 nItem = i;
1941 break;
1943 /* this is used to find first char match when search string is not available yet,
1944 otherwise every WM_CHAR will search to next item by first char, ignoring that we're
1945 already waiting for user to complete a string */
1946 else if (nItem == -1 && infoPtr->nSearchParamLength == 1 && !lstrncmpiW(item.pszText, infoPtr->szSearchParam, 1))
1948 /* this would work but we must keep looking for a longer match */
1949 nItem = i;
1953 if ( nItem != -1 || /* found something */
1954 endidx != infoPtr->nItemCount || /* second search done */
1955 (startidx == 0 && endidx == infoPtr->nItemCount) /* full range for first search */ )
1956 break;
1960 if (nItem != -1)
1961 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1963 return 0;
1966 /*************************************************************************
1967 * LISTVIEW_UpdateHeaderSize [Internal]
1969 * Function to resize the header control
1971 * PARAMS
1972 * [I] hwnd : handle to a window
1973 * [I] nNewScrollPos : scroll pos to set
1975 * RETURNS
1976 * None.
1978 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1980 RECT winRect;
1981 POINT point[2];
1983 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1985 if (!infoPtr->hwndHeader) return;
1987 GetWindowRect(infoPtr->hwndHeader, &winRect);
1988 point[0].x = winRect.left;
1989 point[0].y = winRect.top;
1990 point[1].x = winRect.right;
1991 point[1].y = winRect.bottom;
1993 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1994 point[0].x = -nNewScrollPos;
1995 point[1].x += nNewScrollPos;
1997 SetWindowPos(infoPtr->hwndHeader,0,
1998 point[0].x,point[0].y,point[1].x,point[1].y,
1999 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
2000 SWP_NOZORDER | SWP_NOACTIVATE);
2003 /***
2004 * DESCRIPTION:
2005 * Update the scrollbars. This functions should be called whenever
2006 * the content, size or view changes.
2008 * PARAMETER(S):
2009 * [I] infoPtr : valid pointer to the listview structure
2011 * RETURN:
2012 * None
2014 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
2016 SCROLLINFO horzInfo, vertInfo;
2017 INT dx, dy;
2019 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
2021 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
2022 horzInfo.cbSize = sizeof(SCROLLINFO);
2023 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
2025 /* for now, we'll set info.nMax to the _count_, and adjust it later */
2026 if (infoPtr->uView == LV_VIEW_LIST)
2028 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
2029 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
2031 /* scroll by at least one column per page */
2032 if(horzInfo.nPage < infoPtr->nItemWidth)
2033 horzInfo.nPage = infoPtr->nItemWidth;
2035 if (infoPtr->nItemWidth)
2036 horzInfo.nPage /= infoPtr->nItemWidth;
2038 else if (infoPtr->uView == LV_VIEW_DETAILS)
2040 horzInfo.nMax = infoPtr->nItemWidth;
2042 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2044 RECT rcView;
2046 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
2049 if (LISTVIEW_IsHeaderEnabled(infoPtr))
2051 if (DPA_GetPtrCount(infoPtr->hdpaColumns))
2053 RECT rcHeader;
2054 INT index;
2056 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2057 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2059 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2060 horzInfo.nMax = rcHeader.right;
2061 TRACE("horzInfo.nMax=%d\n", horzInfo.nMax);
2065 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
2066 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
2067 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
2068 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
2069 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
2071 /* Setting the horizontal scroll can change the listview size
2072 * (and potentially everything else) so we need to recompute
2073 * everything again for the vertical scroll
2076 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
2077 vertInfo.cbSize = sizeof(SCROLLINFO);
2078 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
2080 if (infoPtr->uView == LV_VIEW_DETAILS)
2082 vertInfo.nMax = infoPtr->nItemCount;
2084 /* scroll by at least one page */
2085 if(vertInfo.nPage < infoPtr->nItemHeight)
2086 vertInfo.nPage = infoPtr->nItemHeight;
2088 if (infoPtr->nItemHeight > 0)
2089 vertInfo.nPage /= infoPtr->nItemHeight;
2091 else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2093 RECT rcView;
2095 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
2098 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
2099 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
2100 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
2101 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
2102 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
2104 /* Change of the range may have changed the scroll pos. If so move the content */
2105 if (dx != 0 || dy != 0)
2107 RECT listRect;
2108 listRect = infoPtr->rcList;
2109 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
2110 SW_ERASE | SW_INVALIDATE);
2113 /* Update the Header Control */
2114 if (infoPtr->hwndHeader)
2116 horzInfo.fMask = SIF_POS;
2117 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
2118 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
2123 /***
2124 * DESCRIPTION:
2125 * Shows/hides the focus rectangle.
2127 * PARAMETER(S):
2128 * [I] infoPtr : valid pointer to the listview structure
2129 * [I] fShow : TRUE to show the focus, FALSE to hide it.
2131 * RETURN:
2132 * None
2134 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
2136 HDC hdc;
2138 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
2140 if (infoPtr->nFocusedItem < 0) return;
2142 /* we need some gymnastics in ICON mode to handle large items */
2143 if (infoPtr->uView == LV_VIEW_ICON)
2145 RECT rcBox;
2147 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
2148 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
2150 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
2151 return;
2155 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
2157 /* for some reason, owner draw should work only in report mode */
2158 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
2160 DRAWITEMSTRUCT dis;
2161 LVITEMW item;
2163 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2164 HFONT hOldFont = SelectObject(hdc, hFont);
2166 item.iItem = infoPtr->nFocusedItem;
2167 item.iSubItem = 0;
2168 item.mask = LVIF_PARAM;
2169 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
2171 ZeroMemory(&dis, sizeof(dis));
2172 dis.CtlType = ODT_LISTVIEW;
2173 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
2174 dis.itemID = item.iItem;
2175 dis.itemAction = ODA_FOCUS;
2176 if (fShow) dis.itemState |= ODS_FOCUS;
2177 dis.hwndItem = infoPtr->hwndSelf;
2178 dis.hDC = hdc;
2179 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
2180 dis.itemData = item.lParam;
2182 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
2184 SelectObject(hdc, hOldFont);
2186 else
2188 LISTVIEW_DrawFocusRect(infoPtr, hdc);
2190 done:
2191 ReleaseDC(infoPtr->hwndSelf, hdc);
2194 /***
2195 * Invalidates all visible selected items.
2197 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
2199 ITERATOR i;
2201 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
2202 while(iterator_next(&i))
2204 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
2205 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
2207 iterator_destroy(&i);
2211 /***
2212 * DESCRIPTION: [INTERNAL]
2213 * Computes an item's (left,top) corner, relative to rcView.
2214 * That is, the position has NOT been made relative to the Origin.
2215 * This is deliberate, to avoid computing the Origin over, and
2216 * over again, when this function is called in a loop. Instead,
2217 * one can factor the computation of the Origin before the loop,
2218 * and offset the value returned by this function, on every iteration.
2220 * PARAMETER(S):
2221 * [I] infoPtr : valid pointer to the listview structure
2222 * [I] nItem : item number
2223 * [O] lpptOrig : item top, left corner
2225 * RETURN:
2226 * None.
2228 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
2230 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
2232 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
2234 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2235 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2237 else if (infoPtr->uView == LV_VIEW_LIST)
2239 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
2240 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
2241 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
2243 else /* LV_VIEW_DETAILS */
2245 lpptPosition->x = REPORT_MARGINX;
2246 /* item is always at zero indexed column */
2247 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2248 lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
2249 lpptPosition->y = nItem * infoPtr->nItemHeight;
2253 /***
2254 * DESCRIPTION: [INTERNAL]
2255 * Compute the rectangles of an item. This is to localize all
2256 * the computations in one place. If you are not interested in some
2257 * of these values, simply pass in a NULL -- the function is smart
2258 * enough to compute only what's necessary. The function computes
2259 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2260 * one, the BOX rectangle. This rectangle is very cheap to compute,
2261 * and is guaranteed to contain all the other rectangles. Computing
2262 * the ICON rect is also cheap, but all the others are potentially
2263 * expensive. This gives an easy and effective optimization when
2264 * searching (like point inclusion, or rectangle intersection):
2265 * first test against the BOX, and if TRUE, test against the desired
2266 * rectangle.
2267 * If the function does not have all the necessary information
2268 * to computed the requested rectangles, will crash with a
2269 * failed assertion. This is done so we catch all programming
2270 * errors, given that the function is called only from our code.
2272 * We have the following 'special' meanings for a few fields:
2273 * * If LVIS_FOCUSED is set, we assume the item has the focus
2274 * This is important in ICON mode, where it might get a larger
2275 * then usual rectangle
2277 * Please note that subitem support works only in REPORT mode.
2279 * PARAMETER(S):
2280 * [I] infoPtr : valid pointer to the listview structure
2281 * [I] lpLVItem : item to compute the measures for
2282 * [O] lprcBox : ptr to Box rectangle
2283 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2284 * [0] lprcSelectBox : ptr to select box rectangle
2285 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2286 * [O] lprcIcon : ptr to Icon rectangle
2287 * Same as LVM_GETITEMRECT with LVIR_ICON
2288 * [O] lprcStateIcon: ptr to State Icon rectangle
2289 * [O] lprcLabel : ptr to Label rectangle
2290 * Same as LVM_GETITEMRECT with LVIR_LABEL
2292 * RETURN:
2293 * None.
2295 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
2296 LPRECT lprcBox, LPRECT lprcSelectBox,
2297 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2299 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2300 RECT Box, SelectBox, Icon, Label;
2301 COLUMN_INFO *lpColumnInfo = NULL;
2302 SIZE labelSize = { 0, 0 };
2304 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2306 /* Be smart and try to figure out the minimum we have to do */
2307 if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2308 if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2310 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2311 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2313 if (lprcSelectBox) doSelectBox = TRUE;
2314 if (lprcLabel) doLabel = TRUE;
2315 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2316 if (doSelectBox)
2318 doIcon = TRUE;
2319 doLabel = TRUE;
2322 /************************************************************/
2323 /* compute the box rectangle (it should be cheap to do) */
2324 /************************************************************/
2325 if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2326 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2328 if (lpLVItem->iSubItem)
2330 Box = lpColumnInfo->rcHeader;
2332 else
2334 Box.left = 0;
2335 Box.right = infoPtr->nItemWidth;
2337 Box.top = 0;
2338 Box.bottom = infoPtr->nItemHeight;
2340 /******************************************************************/
2341 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2342 /******************************************************************/
2343 if (doIcon)
2345 LONG state_width = 0;
2347 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2348 state_width = infoPtr->iconStateSize.cx;
2350 if (infoPtr->uView == LV_VIEW_ICON)
2352 Icon.left = Box.left + state_width;
2353 if (infoPtr->himlNormal)
2354 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2355 Icon.top = Box.top + ICON_TOP_PADDING;
2356 Icon.right = Icon.left;
2357 Icon.bottom = Icon.top;
2358 if (infoPtr->himlNormal)
2360 Icon.right += infoPtr->iconSize.cx;
2361 Icon.bottom += infoPtr->iconSize.cy;
2364 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2366 Icon.left = Box.left + state_width;
2368 if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2370 /* we need the indent in report mode */
2371 assert(lpLVItem->mask & LVIF_INDENT);
2372 Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2375 Icon.top = Box.top;
2376 Icon.right = Icon.left;
2377 if (infoPtr->himlSmall &&
2378 (!lpColumnInfo || lpLVItem->iSubItem == 0 ||
2379 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2380 Icon.right += infoPtr->iconSize.cx;
2381 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2383 if(lprcIcon) *lprcIcon = Icon;
2384 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2386 /* TODO: is this correct? */
2387 if (lprcStateIcon)
2389 lprcStateIcon->left = Icon.left - state_width;
2390 lprcStateIcon->right = Icon.left;
2391 lprcStateIcon->top = Icon.top;
2392 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2393 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2396 else Icon.right = 0;
2398 /************************************************************/
2399 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2400 /************************************************************/
2401 if (doLabel)
2403 /* calculate how far to the right can the label stretch */
2404 Label.right = Box.right;
2405 if (infoPtr->uView == LV_VIEW_DETAILS)
2407 if (lpLVItem->iSubItem == 0)
2409 /* we need a zero based rect here */
2410 Label = lpColumnInfo->rcHeader;
2411 OffsetRect(&Label, -Label.left, 0);
2415 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2417 labelSize.cx = infoPtr->nItemWidth;
2418 labelSize.cy = infoPtr->nItemHeight;
2419 goto calc_label;
2422 /* we need the text in non owner draw mode */
2423 assert(lpLVItem->mask & LVIF_TEXT);
2424 if (is_text(lpLVItem->pszText))
2426 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2427 HDC hdc = GetDC(infoPtr->hwndSelf);
2428 HFONT hOldFont = SelectObject(hdc, hFont);
2429 UINT uFormat;
2430 RECT rcText;
2432 /* compute rough rectangle where the label will go */
2433 SetRectEmpty(&rcText);
2434 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2435 rcText.bottom = infoPtr->nItemHeight;
2436 if (infoPtr->uView == LV_VIEW_ICON)
2437 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2439 /* now figure out the flags */
2440 if (infoPtr->uView == LV_VIEW_ICON)
2441 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2442 else
2443 uFormat = LV_SL_DT_FLAGS;
2445 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2447 if (rcText.right != rcText.left)
2448 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2450 labelSize.cy = rcText.bottom - rcText.top;
2452 SelectObject(hdc, hOldFont);
2453 ReleaseDC(infoPtr->hwndSelf, hdc);
2456 calc_label:
2457 if (infoPtr->uView == LV_VIEW_ICON)
2459 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2460 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2461 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2462 Label.right = Label.left + labelSize.cx;
2463 Label.bottom = Label.top + infoPtr->nItemHeight;
2464 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2466 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2467 labelSize.cy /= infoPtr->ntmHeight;
2468 labelSize.cy = max(labelSize.cy, 1);
2469 labelSize.cy *= infoPtr->ntmHeight;
2471 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2473 else if (infoPtr->uView == LV_VIEW_DETAILS)
2475 Label.left = Icon.right;
2476 Label.top = Box.top;
2477 Label.right = lpLVItem->iSubItem ? lpColumnInfo->rcHeader.right :
2478 lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
2479 Label.bottom = Label.top + infoPtr->nItemHeight;
2481 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2483 Label.left = Icon.right;
2484 Label.top = Box.top;
2485 Label.right = min(Label.left + labelSize.cx, Label.right);
2486 Label.bottom = Label.top + infoPtr->nItemHeight;
2489 if (lprcLabel) *lprcLabel = Label;
2490 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2493 /************************************************************/
2494 /* compute SELECT bounding box */
2495 /************************************************************/
2496 if (doSelectBox)
2498 if (infoPtr->uView == LV_VIEW_DETAILS)
2500 SelectBox.left = Icon.left;
2501 SelectBox.top = Box.top;
2502 SelectBox.bottom = Box.bottom;
2504 if (labelSize.cx)
2505 SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2506 else
2507 SelectBox.right = min(Label.left + MAX_EMPTYTEXT_SELECT_WIDTH, Label.right);
2509 else
2511 UnionRect(&SelectBox, &Icon, &Label);
2513 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2514 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2517 /* Fix the Box if necessary */
2518 if (lprcBox)
2520 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2521 else *lprcBox = Box;
2523 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2526 /***
2527 * DESCRIPTION: [INTERNAL]
2529 * PARAMETER(S):
2530 * [I] infoPtr : valid pointer to the listview structure
2531 * [I] nItem : item number
2532 * [O] lprcBox : ptr to Box rectangle
2534 * RETURN:
2535 * None.
2537 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2539 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2540 POINT Position, Origin;
2541 LVITEMW lvItem;
2543 LISTVIEW_GetOrigin(infoPtr, &Origin);
2544 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2546 /* Be smart and try to figure out the minimum we have to do */
2547 lvItem.mask = 0;
2548 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2549 lvItem.mask |= LVIF_TEXT;
2550 lvItem.iItem = nItem;
2551 lvItem.iSubItem = 0;
2552 lvItem.pszText = szDispText;
2553 lvItem.cchTextMax = DISP_TEXT_SIZE;
2554 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2555 if (infoPtr->uView == LV_VIEW_ICON)
2557 lvItem.mask |= LVIF_STATE;
2558 lvItem.stateMask = LVIS_FOCUSED;
2559 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2561 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2563 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
2564 SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))
2566 OffsetRect(lprcBox, Origin.x, Position.y + Origin.y);
2568 else
2569 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2572 /* LISTVIEW_MapIdToIndex helper */
2573 static INT CALLBACK MapIdSearchCompare(LPVOID p1, LPVOID p2, LPARAM lParam)
2575 ITEM_ID *id1 = (ITEM_ID*)p1;
2576 ITEM_ID *id2 = (ITEM_ID*)p2;
2578 if (id1->id == id2->id) return 0;
2580 return (id1->id < id2->id) ? -1 : 1;
2583 /***
2584 * DESCRIPTION:
2585 * Returns the item index for id specified.
2587 * PARAMETER(S):
2588 * [I] infoPtr : valid pointer to the listview structure
2589 * [I] iID : item id to get index for
2591 * RETURN:
2592 * Item index, or -1 on failure.
2594 static INT LISTVIEW_MapIdToIndex(const LISTVIEW_INFO *infoPtr, UINT iID)
2596 ITEM_ID ID;
2597 INT index;
2599 TRACE("iID=%d\n", iID);
2601 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2602 if (infoPtr->nItemCount == 0) return -1;
2604 ID.id = iID;
2605 index = DPA_Search(infoPtr->hdpaItemIds, &ID, -1, MapIdSearchCompare, 0, DPAS_SORTED);
2607 if (index != -1)
2609 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, index);
2610 return DPA_GetPtrIndex(infoPtr->hdpaItems, lpID->item);
2613 return -1;
2616 /***
2617 * DESCRIPTION:
2618 * Returns the item id for index given.
2620 * PARAMETER(S):
2621 * [I] infoPtr : valid pointer to the listview structure
2622 * [I] iItem : item index to get id for
2624 * RETURN:
2625 * Item id.
2627 static DWORD LISTVIEW_MapIndexToId(const LISTVIEW_INFO *infoPtr, INT iItem)
2629 ITEM_INFO *lpItem;
2630 HDPA hdpaSubItems;
2632 TRACE("iItem=%d\n", iItem);
2634 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2635 if (iItem < 0 || iItem >= infoPtr->nItemCount) return -1;
2637 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, iItem);
2638 lpItem = DPA_GetPtr(hdpaSubItems, 0);
2640 return lpItem->id->id;
2643 /***
2644 * DESCRIPTION:
2645 * Returns the current icon position, and advances it along the top.
2646 * The returned position is not offset by Origin.
2648 * PARAMETER(S):
2649 * [I] infoPtr : valid pointer to the listview structure
2650 * [O] lpPos : will get the current icon position
2652 * RETURN:
2653 * None
2655 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2657 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2659 *lpPos = infoPtr->currIconPos;
2661 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2662 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2664 infoPtr->currIconPos.x = 0;
2665 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2669 /***
2670 * DESCRIPTION:
2671 * Returns the current icon position, and advances it down the left edge.
2672 * The returned position is not offset by Origin.
2674 * PARAMETER(S):
2675 * [I] infoPtr : valid pointer to the listview structure
2676 * [O] lpPos : will get the current icon position
2678 * RETURN:
2679 * None
2681 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2683 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2685 *lpPos = infoPtr->currIconPos;
2687 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2688 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2690 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2691 infoPtr->currIconPos.y = 0;
2695 /***
2696 * DESCRIPTION:
2697 * Moves an icon to the specified position.
2698 * It takes care of invalidating the item, etc.
2700 * PARAMETER(S):
2701 * [I] infoPtr : valid pointer to the listview structure
2702 * [I] nItem : the item to move
2703 * [I] lpPos : the new icon position
2704 * [I] isNew : flags the item as being new
2706 * RETURN:
2707 * Success: TRUE
2708 * Failure: FALSE
2710 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2712 POINT old;
2714 if (!isNew)
2716 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2717 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2719 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2720 LISTVIEW_InvalidateItem(infoPtr, nItem);
2723 /* Allocating a POINTER for every item is too resource intensive,
2724 * so we'll keep the (x,y) in different arrays */
2725 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2726 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2728 LISTVIEW_InvalidateItem(infoPtr, nItem);
2730 return TRUE;
2733 /***
2734 * DESCRIPTION:
2735 * Arranges listview items in icon display mode.
2737 * PARAMETER(S):
2738 * [I] infoPtr : valid pointer to the listview structure
2739 * [I] nAlignCode : alignment code
2741 * RETURN:
2742 * SUCCESS : TRUE
2743 * FAILURE : FALSE
2745 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2747 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2748 POINT pos;
2749 INT i;
2751 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2753 TRACE("nAlignCode=%d\n", nAlignCode);
2755 if (nAlignCode == LVA_DEFAULT)
2757 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2758 else nAlignCode = LVA_ALIGNTOP;
2761 switch (nAlignCode)
2763 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2764 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2765 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2766 default: return FALSE;
2769 infoPtr->bAutoarrange = TRUE;
2770 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2771 for (i = 0; i < infoPtr->nItemCount; i++)
2773 next_pos(infoPtr, &pos);
2774 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2777 return TRUE;
2780 /***
2781 * DESCRIPTION:
2782 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2783 * For LVS_REPORT always returns empty rectangle.
2785 * PARAMETER(S):
2786 * [I] infoPtr : valid pointer to the listview structure
2787 * [O] lprcView : bounding rectangle
2789 * RETURN:
2790 * SUCCESS : TRUE
2791 * FAILURE : FALSE
2793 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2795 INT i, x, y;
2797 SetRectEmpty(lprcView);
2799 switch (infoPtr->uView)
2801 case LV_VIEW_ICON:
2802 case LV_VIEW_SMALLICON:
2803 for (i = 0; i < infoPtr->nItemCount; i++)
2805 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2806 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2807 lprcView->right = max(lprcView->right, x);
2808 lprcView->bottom = max(lprcView->bottom, y);
2810 if (infoPtr->nItemCount > 0)
2812 lprcView->right += infoPtr->nItemWidth;
2813 lprcView->bottom += infoPtr->nItemHeight;
2815 break;
2817 case LV_VIEW_LIST:
2818 y = LISTVIEW_GetCountPerColumn(infoPtr);
2819 x = infoPtr->nItemCount / y;
2820 if (infoPtr->nItemCount % y) x++;
2821 lprcView->right = x * infoPtr->nItemWidth;
2822 lprcView->bottom = y * infoPtr->nItemHeight;
2823 break;
2827 /***
2828 * DESCRIPTION:
2829 * Retrieves the bounding rectangle of all the items.
2831 * PARAMETER(S):
2832 * [I] infoPtr : valid pointer to the listview structure
2833 * [O] lprcView : bounding rectangle
2835 * RETURN:
2836 * SUCCESS : TRUE
2837 * FAILURE : FALSE
2839 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2841 POINT ptOrigin;
2843 TRACE("(lprcView=%p)\n", lprcView);
2845 if (!lprcView) return FALSE;
2847 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2849 if (infoPtr->uView != LV_VIEW_DETAILS)
2851 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2852 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2855 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2857 return TRUE;
2860 /***
2861 * DESCRIPTION:
2862 * Retrieves the subitem pointer associated with the subitem index.
2864 * PARAMETER(S):
2865 * [I] hdpaSubItems : DPA handle for a specific item
2866 * [I] nSubItem : index of subitem
2868 * RETURN:
2869 * SUCCESS : subitem pointer
2870 * FAILURE : NULL
2872 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2874 SUBITEM_INFO *lpSubItem;
2875 INT i;
2877 /* we should binary search here if need be */
2878 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2880 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2881 if (lpSubItem->iSubItem == nSubItem)
2882 return lpSubItem;
2885 return NULL;
2889 /***
2890 * DESCRIPTION:
2891 * Calculates the desired item width.
2893 * PARAMETER(S):
2894 * [I] infoPtr : valid pointer to the listview structure
2896 * RETURN:
2897 * The desired item width.
2899 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2901 INT nItemWidth = 0;
2903 TRACE("uView=%d\n", infoPtr->uView);
2905 if (infoPtr->uView == LV_VIEW_ICON)
2906 nItemWidth = infoPtr->iconSpacing.cx;
2907 else if (infoPtr->uView == LV_VIEW_DETAILS)
2909 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2911 RECT rcHeader;
2912 INT index;
2914 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2915 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2917 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2918 nItemWidth = rcHeader.right;
2921 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2923 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2924 LVITEMW lvItem;
2925 INT i;
2927 lvItem.mask = LVIF_TEXT;
2928 lvItem.iSubItem = 0;
2930 for (i = 0; i < infoPtr->nItemCount; i++)
2932 lvItem.iItem = i;
2933 lvItem.pszText = szDispText;
2934 lvItem.cchTextMax = DISP_TEXT_SIZE;
2935 if (LISTVIEW_GetItemW(infoPtr, &lvItem))
2936 nItemWidth = max(LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE),
2937 nItemWidth);
2940 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2941 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2943 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2946 return nItemWidth;
2949 /***
2950 * DESCRIPTION:
2951 * Calculates the desired item height.
2953 * PARAMETER(S):
2954 * [I] infoPtr : valid pointer to the listview structure
2956 * RETURN:
2957 * The desired item height.
2959 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2961 INT nItemHeight;
2963 TRACE("uView=%d\n", infoPtr->uView);
2965 if (infoPtr->uView == LV_VIEW_ICON)
2966 nItemHeight = infoPtr->iconSpacing.cy;
2967 else
2969 nItemHeight = infoPtr->ntmHeight;
2970 if (infoPtr->himlState)
2971 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2972 if (infoPtr->himlSmall)
2973 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2974 nItemHeight += HEIGHT_PADDING;
2975 if (infoPtr->nMeasureItemHeight > 0)
2976 nItemHeight = infoPtr->nMeasureItemHeight;
2979 return max(nItemHeight, 1);
2982 /***
2983 * DESCRIPTION:
2984 * Updates the width, and height of an item.
2986 * PARAMETER(S):
2987 * [I] infoPtr : valid pointer to the listview structure
2989 * RETURN:
2990 * None.
2992 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2994 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2995 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2999 /***
3000 * DESCRIPTION:
3001 * Retrieves and saves important text metrics info for the current
3002 * Listview font.
3004 * PARAMETER(S):
3005 * [I] infoPtr : valid pointer to the listview structure
3008 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
3010 HDC hdc = GetDC(infoPtr->hwndSelf);
3011 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
3012 HFONT hOldFont = SelectObject(hdc, hFont);
3013 TEXTMETRICW tm;
3014 SIZE sz;
3016 if (GetTextMetricsW(hdc, &tm))
3018 infoPtr->ntmHeight = tm.tmHeight;
3019 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
3022 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
3023 infoPtr->nEllipsisWidth = sz.cx;
3025 SelectObject(hdc, hOldFont);
3026 ReleaseDC(infoPtr->hwndSelf, hdc);
3028 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
3031 /***
3032 * DESCRIPTION:
3033 * A compare function for ranges
3035 * PARAMETER(S)
3036 * [I] range1 : pointer to range 1;
3037 * [I] range2 : pointer to range 2;
3038 * [I] flags : flags
3040 * RETURNS:
3041 * > 0 : if range 1 > range 2
3042 * < 0 : if range 2 > range 1
3043 * = 0 : if range intersects range 2
3045 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
3047 INT cmp;
3049 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
3050 cmp = -1;
3051 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
3052 cmp = 1;
3053 else
3054 cmp = 0;
3056 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
3058 return cmp;
3061 #define ranges_check(ranges, desc) if (TRACE_ON(listview)) ranges_assert(ranges, desc, __FILE__, __LINE__)
3063 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *file, int line)
3065 INT i;
3066 RANGE *prev, *curr;
3068 TRACE("*** Checking %s:%d:%s ***\n", file, line, desc);
3069 assert (ranges);
3070 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
3071 ranges_dump(ranges);
3072 if (DPA_GetPtrCount(ranges->hdpa) > 0)
3074 prev = DPA_GetPtr(ranges->hdpa, 0);
3075 assert (prev->lower >= 0 && prev->lower < prev->upper);
3076 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
3078 curr = DPA_GetPtr(ranges->hdpa, i);
3079 assert (prev->upper <= curr->lower);
3080 assert (curr->lower < curr->upper);
3081 prev = curr;
3084 TRACE("--- Done checking---\n");
3087 static RANGES ranges_create(int count)
3089 RANGES ranges = Alloc(sizeof(struct tagRANGES));
3090 if (!ranges) return NULL;
3091 ranges->hdpa = DPA_Create(count);
3092 if (ranges->hdpa) return ranges;
3093 Free(ranges);
3094 return NULL;
3097 static void ranges_clear(RANGES ranges)
3099 INT i;
3101 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3102 Free(DPA_GetPtr(ranges->hdpa, i));
3103 DPA_DeleteAllPtrs(ranges->hdpa);
3107 static void ranges_destroy(RANGES ranges)
3109 if (!ranges) return;
3110 ranges_clear(ranges);
3111 DPA_Destroy(ranges->hdpa);
3112 Free(ranges);
3115 static RANGES ranges_clone(RANGES ranges)
3117 RANGES clone;
3118 INT i;
3120 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
3122 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3124 RANGE *newrng = Alloc(sizeof(RANGE));
3125 if (!newrng) goto fail;
3126 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
3127 DPA_SetPtr(clone->hdpa, i, newrng);
3129 return clone;
3131 fail:
3132 TRACE ("clone failed\n");
3133 ranges_destroy(clone);
3134 return NULL;
3137 static RANGES ranges_diff(RANGES ranges, RANGES sub)
3139 INT i;
3141 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
3142 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
3144 return ranges;
3147 static void ranges_dump(RANGES ranges)
3149 INT i;
3151 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3152 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
3155 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
3157 RANGE srchrng = { nItem, nItem + 1 };
3159 TRACE("(nItem=%d)\n", nItem);
3160 ranges_check(ranges, "before contain");
3161 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
3164 static INT ranges_itemcount(RANGES ranges)
3166 INT i, count = 0;
3168 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3170 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
3171 count += sel->upper - sel->lower;
3174 return count;
3177 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
3179 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
3180 INT index;
3182 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3183 if (index == -1) return TRUE;
3185 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
3187 chkrng = DPA_GetPtr(ranges->hdpa, index);
3188 if (chkrng->lower >= nItem)
3189 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
3190 if (chkrng->upper > nItem)
3191 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
3193 return TRUE;
3196 static BOOL ranges_add(RANGES ranges, RANGE range)
3198 RANGE srchrgn;
3199 INT index;
3201 TRACE("(%s)\n", debugrange(&range));
3202 ranges_check(ranges, "before add");
3204 /* try find overlapping regions first */
3205 srchrgn.lower = range.lower - 1;
3206 srchrgn.upper = range.upper + 1;
3207 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
3209 if (index == -1)
3211 RANGE *newrgn;
3213 TRACE("Adding new range\n");
3215 /* create the brand new range to insert */
3216 newrgn = Alloc(sizeof(RANGE));
3217 if(!newrgn) goto fail;
3218 *newrgn = range;
3220 /* figure out where to insert it */
3221 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3222 TRACE("index=%d\n", index);
3223 if (index == -1) index = 0;
3225 /* and get it over with */
3226 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3228 Free(newrgn);
3229 goto fail;
3232 else
3234 RANGE *chkrgn, *mrgrgn;
3235 INT fromindex, mergeindex;
3237 chkrgn = DPA_GetPtr(ranges->hdpa, index);
3238 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
3240 chkrgn->lower = min(range.lower, chkrgn->lower);
3241 chkrgn->upper = max(range.upper, chkrgn->upper);
3243 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
3245 /* merge now common ranges */
3246 fromindex = 0;
3247 srchrgn.lower = chkrgn->lower - 1;
3248 srchrgn.upper = chkrgn->upper + 1;
3252 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
3253 if (mergeindex == -1) break;
3254 if (mergeindex == index)
3256 fromindex = index + 1;
3257 continue;
3260 TRACE("Merge with index %i\n", mergeindex);
3262 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
3263 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
3264 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
3265 Free(mrgrgn);
3266 DPA_DeletePtr(ranges->hdpa, mergeindex);
3267 if (mergeindex < index) index --;
3268 } while(1);
3271 ranges_check(ranges, "after add");
3272 return TRUE;
3274 fail:
3275 ranges_check(ranges, "failed add");
3276 return FALSE;
3279 static BOOL ranges_del(RANGES ranges, RANGE range)
3281 RANGE *chkrgn;
3282 INT index;
3284 TRACE("(%s)\n", debugrange(&range));
3285 ranges_check(ranges, "before del");
3287 /* we don't use DPAS_SORTED here, since we need *
3288 * to find the first overlapping range */
3289 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
3290 while(index != -1)
3292 chkrgn = DPA_GetPtr(ranges->hdpa, index);
3294 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
3296 /* case 1: Same range */
3297 if ( (chkrgn->upper == range.upper) &&
3298 (chkrgn->lower == range.lower) )
3300 DPA_DeletePtr(ranges->hdpa, index);
3301 Free(chkrgn);
3302 break;
3304 /* case 2: engulf */
3305 else if ( (chkrgn->upper <= range.upper) &&
3306 (chkrgn->lower >= range.lower) )
3308 DPA_DeletePtr(ranges->hdpa, index);
3309 Free(chkrgn);
3311 /* case 3: overlap upper */
3312 else if ( (chkrgn->upper <= range.upper) &&
3313 (chkrgn->lower < range.lower) )
3315 chkrgn->upper = range.lower;
3317 /* case 4: overlap lower */
3318 else if ( (chkrgn->upper > range.upper) &&
3319 (chkrgn->lower >= range.lower) )
3321 chkrgn->lower = range.upper;
3322 break;
3324 /* case 5: fully internal */
3325 else
3327 RANGE *newrgn;
3329 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
3330 newrgn->lower = chkrgn->lower;
3331 newrgn->upper = range.lower;
3332 chkrgn->lower = range.upper;
3333 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3335 Free(newrgn);
3336 goto fail;
3338 break;
3341 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
3344 ranges_check(ranges, "after del");
3345 return TRUE;
3347 fail:
3348 ranges_check(ranges, "failed del");
3349 return FALSE;
3352 /***
3353 * DESCRIPTION:
3354 * Removes all selection ranges
3356 * Parameters(s):
3357 * [I] infoPtr : valid pointer to the listview structure
3358 * [I] toSkip : item range to skip removing the selection
3360 * RETURNS:
3361 * SUCCESS : TRUE
3362 * FAILURE : FALSE
3364 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
3366 LVITEMW lvItem;
3367 ITERATOR i;
3368 RANGES clone;
3370 TRACE("()\n");
3372 lvItem.state = 0;
3373 lvItem.stateMask = LVIS_SELECTED;
3375 /* need to clone the DPA because callbacks can change it */
3376 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
3377 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
3378 while(iterator_next(&i))
3379 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
3380 /* note that the iterator destructor will free the cloned range */
3381 iterator_destroy(&i);
3383 return TRUE;
3386 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
3388 RANGES toSkip;
3390 if (!(toSkip = ranges_create(1))) return FALSE;
3391 if (nItem != -1) ranges_additem(toSkip, nItem);
3392 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3393 ranges_destroy(toSkip);
3394 return TRUE;
3397 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3399 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3402 /***
3403 * DESCRIPTION:
3404 * Retrieves the number of items that are marked as selected.
3406 * PARAMETER(S):
3407 * [I] infoPtr : valid pointer to the listview structure
3409 * RETURN:
3410 * Number of items selected.
3412 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3414 INT nSelectedCount = 0;
3416 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3418 INT i;
3419 for (i = 0; i < infoPtr->nItemCount; i++)
3421 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3422 nSelectedCount++;
3425 else
3426 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3428 TRACE("nSelectedCount=%d\n", nSelectedCount);
3429 return nSelectedCount;
3432 /***
3433 * DESCRIPTION:
3434 * Manages the item focus.
3436 * PARAMETER(S):
3437 * [I] infoPtr : valid pointer to the listview structure
3438 * [I] nItem : item index
3440 * RETURN:
3441 * TRUE : focused item changed
3442 * FALSE : focused item has NOT changed
3444 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3446 INT oldFocus = infoPtr->nFocusedItem;
3447 LVITEMW lvItem;
3449 if (nItem == infoPtr->nFocusedItem) return FALSE;
3451 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3452 lvItem.stateMask = LVIS_FOCUSED;
3453 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3455 return oldFocus != infoPtr->nFocusedItem;
3458 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3460 if (nShiftItem < nItem) return nShiftItem;
3462 if (nShiftItem > nItem) return nShiftItem + direction;
3464 if (direction > 0) return nShiftItem + direction;
3466 return min(nShiftItem, infoPtr->nItemCount - 1);
3469 /* This function updates focus index.
3471 Parameters:
3472 focus : current focus index
3473 item : index of item to be added/removed
3474 direction : add/remove flag
3476 static void LISTVIEW_ShiftFocus(LISTVIEW_INFO *infoPtr, INT focus, INT item, INT direction)
3478 BOOL old_change = infoPtr->bDoChangeNotify;
3480 infoPtr->bDoChangeNotify = FALSE;
3481 focus = shift_item(infoPtr, focus, item, direction);
3482 if (focus != infoPtr->nFocusedItem)
3483 LISTVIEW_SetItemFocus(infoPtr, focus);
3484 infoPtr->bDoChangeNotify = old_change;
3488 * DESCRIPTION:
3489 * Updates the various indices after an item has been inserted or deleted.
3491 * PARAMETER(S):
3492 * [I] infoPtr : valid pointer to the listview structure
3493 * [I] nItem : item index
3494 * [I] direction : Direction of shift, +1 or -1.
3496 * RETURN:
3497 * None
3499 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3501 TRACE("Shifting %i, %i steps\n", nItem, direction);
3503 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3504 assert(abs(direction) == 1);
3505 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3507 /* But we are not supposed to modify nHotItem! */
3511 * DESCRIPTION:
3512 * Adds a block of selections.
3514 * PARAMETER(S):
3515 * [I] infoPtr : valid pointer to the listview structure
3516 * [I] nItem : item index
3518 * RETURN:
3519 * Whether the window is still valid.
3521 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3523 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3524 INT nLast = max(infoPtr->nSelectionMark, nItem);
3525 HWND hwndSelf = infoPtr->hwndSelf;
3526 NMLVODSTATECHANGE nmlv;
3527 LVITEMW item;
3528 BOOL bOldChange;
3529 INT i;
3531 /* Temporarily disable change notification
3532 * If the control is LVS_OWNERDATA, we need to send
3533 * only one LVN_ODSTATECHANGED notification.
3534 * See MSDN documentation for LVN_ITEMCHANGED.
3536 bOldChange = infoPtr->bDoChangeNotify;
3537 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3539 if (nFirst == -1) nFirst = nItem;
3541 item.state = LVIS_SELECTED;
3542 item.stateMask = LVIS_SELECTED;
3544 for (i = nFirst; i <= nLast; i++)
3545 LISTVIEW_SetItemState(infoPtr,i,&item);
3547 ZeroMemory(&nmlv, sizeof(nmlv));
3548 nmlv.iFrom = nFirst;
3549 nmlv.iTo = nLast;
3550 nmlv.uOldState = 0;
3551 nmlv.uNewState = item.state;
3553 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3554 if (!IsWindow(hwndSelf))
3555 return FALSE;
3556 infoPtr->bDoChangeNotify = bOldChange;
3557 return TRUE;
3561 /***
3562 * DESCRIPTION:
3563 * Sets a single group selection.
3565 * PARAMETER(S):
3566 * [I] infoPtr : valid pointer to the listview structure
3567 * [I] nItem : item index
3569 * RETURN:
3570 * None
3572 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3574 RANGES selection;
3575 LVITEMW item;
3576 ITERATOR i;
3577 BOOL bOldChange;
3579 if (!(selection = ranges_create(100))) return;
3581 item.state = LVIS_SELECTED;
3582 item.stateMask = LVIS_SELECTED;
3584 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3586 if (infoPtr->nSelectionMark == -1)
3588 infoPtr->nSelectionMark = nItem;
3589 ranges_additem(selection, nItem);
3591 else
3593 RANGE sel;
3595 sel.lower = min(infoPtr->nSelectionMark, nItem);
3596 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3597 ranges_add(selection, sel);
3600 else
3602 RECT rcItem, rcSel, rcSelMark;
3603 POINT ptItem;
3605 rcItem.left = LVIR_BOUNDS;
3606 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) {
3607 ranges_destroy (selection);
3608 return;
3610 rcSelMark.left = LVIR_BOUNDS;
3611 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) {
3612 ranges_destroy (selection);
3613 return;
3615 UnionRect(&rcSel, &rcItem, &rcSelMark);
3616 iterator_frameditems(&i, infoPtr, &rcSel);
3617 while(iterator_next(&i))
3619 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3620 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3622 iterator_destroy(&i);
3625 /* disable per item notifications on LVS_OWNERDATA style
3626 FIXME: single LVN_ODSTATECHANGED should be used */
3627 bOldChange = infoPtr->bDoChangeNotify;
3628 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3630 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3633 iterator_rangesitems(&i, selection);
3634 while(iterator_next(&i))
3635 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3636 /* this will also destroy the selection */
3637 iterator_destroy(&i);
3639 infoPtr->bDoChangeNotify = bOldChange;
3641 LISTVIEW_SetItemFocus(infoPtr, nItem);
3644 /***
3645 * DESCRIPTION:
3646 * Sets a single selection.
3648 * PARAMETER(S):
3649 * [I] infoPtr : valid pointer to the listview structure
3650 * [I] nItem : item index
3652 * RETURN:
3653 * None
3655 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3657 LVITEMW lvItem;
3659 TRACE("nItem=%d\n", nItem);
3661 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3663 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3664 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3665 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3667 infoPtr->nSelectionMark = nItem;
3670 /***
3671 * DESCRIPTION:
3672 * Set selection(s) with keyboard.
3674 * PARAMETER(S):
3675 * [I] infoPtr : valid pointer to the listview structure
3676 * [I] nItem : item index
3677 * [I] space : VK_SPACE code sent
3679 * RETURN:
3680 * SUCCESS : TRUE (needs to be repainted)
3681 * FAILURE : FALSE (nothing has changed)
3683 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3685 /* FIXME: pass in the state */
3686 WORD wShift = GetKeyState(VK_SHIFT) & 0x8000;
3687 WORD wCtrl = GetKeyState(VK_CONTROL) & 0x8000;
3688 BOOL bResult = FALSE;
3690 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3691 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3693 bResult = TRUE;
3695 if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3696 LISTVIEW_SetSelection(infoPtr, nItem);
3697 else
3699 if (wShift)
3700 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3701 else if (wCtrl)
3703 LVITEMW lvItem;
3704 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3705 lvItem.stateMask = LVIS_SELECTED;
3706 if (space)
3708 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3709 if (lvItem.state & LVIS_SELECTED)
3710 infoPtr->nSelectionMark = nItem;
3712 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3715 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3718 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3719 return bResult;
3722 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3724 LVHITTESTINFO lvHitTestInfo;
3726 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3727 lvHitTestInfo.pt.x = pt.x;
3728 lvHitTestInfo.pt.y = pt.y;
3730 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3732 lpLVItem->mask = LVIF_PARAM;
3733 lpLVItem->iItem = lvHitTestInfo.iItem;
3734 lpLVItem->iSubItem = 0;
3736 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3739 static inline BOOL LISTVIEW_IsHotTracking(const LISTVIEW_INFO *infoPtr)
3741 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3742 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3743 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3746 /***
3747 * DESCRIPTION:
3748 * Called when the mouse is being actively tracked and has hovered for a specified
3749 * amount of time
3751 * PARAMETER(S):
3752 * [I] infoPtr : valid pointer to the listview structure
3753 * [I] fwKeys : key indicator
3754 * [I] x,y : mouse position
3756 * RETURN:
3757 * 0 if the message was processed, non-zero if there was an error
3759 * INFO:
3760 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3761 * over the item for a certain period of time.
3764 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, INT x, INT y)
3766 NMHDR hdr;
3768 if (notify_hdr(infoPtr, NM_HOVER, &hdr)) return 0;
3770 if (LISTVIEW_IsHotTracking(infoPtr))
3772 LVITEMW item;
3773 POINT pt;
3775 pt.x = x;
3776 pt.y = y;
3778 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3779 LISTVIEW_SetSelection(infoPtr, item.iItem);
3781 SetFocus(infoPtr->hwndSelf);
3784 return 0;
3787 #define SCROLL_LEFT 0x1
3788 #define SCROLL_RIGHT 0x2
3789 #define SCROLL_UP 0x4
3790 #define SCROLL_DOWN 0x8
3792 /***
3793 * DESCRIPTION:
3794 * Utility routine to draw and highlight items within a marquee selection rectangle.
3796 * PARAMETER(S):
3797 * [I] infoPtr : valid pointer to the listview structure
3798 * [I] coords_orig : original co-ordinates of the cursor
3799 * [I] coords_offs : offsetted coordinates of the cursor
3800 * [I] offset : offset amount
3801 * [I] scroll : Bitmask of which directions we should scroll, if at all
3803 * RETURN:
3804 * None.
3806 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, const POINT *coords_orig,
3807 const POINT *coords_offs, const POINT *offset,
3808 INT scroll)
3810 BOOL controlDown = FALSE;
3811 LVITEMW item;
3812 ITERATOR old_elems, new_elems;
3813 RECT rect;
3815 if (coords_offs->x > infoPtr->marqueeOrigin.x)
3817 rect.left = infoPtr->marqueeOrigin.x;
3818 rect.right = coords_offs->x;
3820 else
3822 rect.left = coords_offs->x;
3823 rect.right = infoPtr->marqueeOrigin.x;
3826 if (coords_offs->y > infoPtr->marqueeOrigin.y)
3828 rect.top = infoPtr->marqueeOrigin.y;
3829 rect.bottom = coords_offs->y;
3831 else
3833 rect.top = coords_offs->y;
3834 rect.bottom = infoPtr->marqueeOrigin.y;
3837 /* Cancel out the old marquee rectangle and draw the new one */
3838 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
3840 /* Scroll by the appropriate distance if applicable - speed up scrolling as
3841 the cursor is further away */
3843 if ((scroll & SCROLL_LEFT) && (coords_orig->x <= 0))
3844 LISTVIEW_Scroll(infoPtr, coords_orig->x, 0);
3846 if ((scroll & SCROLL_RIGHT) && (coords_orig->x >= infoPtr->rcList.right))
3847 LISTVIEW_Scroll(infoPtr, (coords_orig->x - infoPtr->rcList.right), 0);
3849 if ((scroll & SCROLL_UP) && (coords_orig->y <= 0))
3850 LISTVIEW_Scroll(infoPtr, 0, coords_orig->y);
3852 if ((scroll & SCROLL_DOWN) && (coords_orig->y >= infoPtr->rcList.bottom))
3853 LISTVIEW_Scroll(infoPtr, 0, (coords_orig->y - infoPtr->rcList.bottom));
3855 iterator_frameditems_absolute(&old_elems, infoPtr, &infoPtr->marqueeRect);
3857 CopyRect(&infoPtr->marqueeRect, &rect);
3859 CopyRect(&infoPtr->marqueeDrawRect, &rect);
3860 OffsetRect(&infoPtr->marqueeDrawRect, offset->x, offset->y);
3862 iterator_frameditems_absolute(&new_elems, infoPtr, &infoPtr->marqueeRect);
3863 iterator_remove_common_items(&old_elems, &new_elems);
3865 /* Iterate over no longer selected items */
3866 while (iterator_next(&old_elems))
3868 if (old_elems.nItem > -1)
3870 if (LISTVIEW_GetItemState(infoPtr, old_elems.nItem, LVIS_SELECTED) == LVIS_SELECTED)
3871 item.state = 0;
3872 else
3873 item.state = LVIS_SELECTED;
3875 item.stateMask = LVIS_SELECTED;
3877 LISTVIEW_SetItemState(infoPtr, old_elems.nItem, &item);
3880 iterator_destroy(&old_elems);
3883 /* Iterate over newly selected items */
3884 if (GetKeyState(VK_CONTROL) & 0x8000)
3885 controlDown = TRUE;
3887 while (iterator_next(&new_elems))
3889 if (new_elems.nItem > -1)
3891 /* If CTRL is pressed, invert. If not, always select the item. */
3892 if ((controlDown) && (LISTVIEW_GetItemState(infoPtr, new_elems.nItem, LVIS_SELECTED)))
3893 item.state = 0;
3894 else
3895 item.state = LVIS_SELECTED;
3897 item.stateMask = LVIS_SELECTED;
3899 LISTVIEW_SetItemState(infoPtr, new_elems.nItem, &item);
3902 iterator_destroy(&new_elems);
3904 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
3907 /***
3908 * DESCRIPTION:
3909 * Called when we are in a marquee selection that involves scrolling the listview (ie,
3910 * the cursor is outside the bounds of the client area). This is a TIMERPROC.
3912 * PARAMETER(S):
3913 * [I] hwnd : Handle to the listview
3914 * [I] uMsg : WM_TIMER (ignored)
3915 * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3916 * [I] dwTimer : The elapsed time (ignored)
3918 * RETURN:
3919 * None.
3921 static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
3923 LISTVIEW_INFO *infoPtr;
3924 SCROLLINFO scrollInfo;
3925 POINT coords_orig;
3926 POINT coords_offs;
3927 POINT offset;
3928 INT scroll = 0;
3930 infoPtr = (LISTVIEW_INFO *) idEvent;
3932 if (!infoPtr)
3933 return;
3935 /* Get the current cursor position and convert to client coordinates */
3936 GetCursorPos(&coords_orig);
3937 ScreenToClient(hWnd, &coords_orig);
3939 /* Ensure coordinates are within client bounds */
3940 coords_offs.x = max(min(coords_orig.x, infoPtr->rcList.right), 0);
3941 coords_offs.y = max(min(coords_orig.y, infoPtr->rcList.bottom), 0);
3943 /* Get offset */
3944 LISTVIEW_GetOrigin(infoPtr, &offset);
3946 /* Offset coordinates by the appropriate amount */
3947 coords_offs.x -= offset.x;
3948 coords_offs.y -= offset.y;
3950 scrollInfo.cbSize = sizeof(SCROLLINFO);
3951 scrollInfo.fMask = SIF_ALL;
3953 /* Work out in which directions we can scroll */
3954 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3956 if (scrollInfo.nPos != scrollInfo.nMin)
3957 scroll |= SCROLL_UP;
3959 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3960 scroll |= SCROLL_DOWN;
3963 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3965 if (scrollInfo.nPos != scrollInfo.nMin)
3966 scroll |= SCROLL_LEFT;
3968 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3969 scroll |= SCROLL_RIGHT;
3972 if (((coords_orig.x <= 0) && (scroll & SCROLL_LEFT)) ||
3973 ((coords_orig.y <= 0) && (scroll & SCROLL_UP)) ||
3974 ((coords_orig.x >= infoPtr->rcList.right) && (scroll & SCROLL_RIGHT)) ||
3975 ((coords_orig.y >= infoPtr->rcList.bottom) && (scroll & SCROLL_DOWN)))
3977 LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, scroll);
3981 /***
3982 * DESCRIPTION:
3983 * Called whenever WM_MOUSEMOVE is received.
3985 * PARAMETER(S):
3986 * [I] infoPtr : valid pointer to the listview structure
3987 * [I] fwKeys : key indicator
3988 * [I] x,y : mouse position
3990 * RETURN:
3991 * 0 if the message is processed, non-zero if there was an error
3993 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3995 LVHITTESTINFO ht;
3996 RECT rect;
3997 POINT pt;
3999 if (!(fwKeys & MK_LBUTTON))
4000 infoPtr->bLButtonDown = FALSE;
4002 if (infoPtr->bLButtonDown)
4004 rect.left = rect.right = infoPtr->ptClickPos.x;
4005 rect.top = rect.bottom = infoPtr->ptClickPos.y;
4007 InflateRect(&rect, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
4009 if (infoPtr->bMarqueeSelect)
4011 POINT coords_orig;
4012 POINT coords_offs;
4013 POINT offset;
4015 coords_orig.x = x;
4016 coords_orig.y = y;
4018 /* Get offset */
4019 LISTVIEW_GetOrigin(infoPtr, &offset);
4021 /* Ensure coordinates are within client bounds */
4022 coords_offs.x = max(min(x, infoPtr->rcList.right), 0);
4023 coords_offs.y = max(min(y, infoPtr->rcList.bottom), 0);
4025 /* Offset coordinates by the appropriate amount */
4026 coords_offs.x -= offset.x;
4027 coords_offs.y -= offset.y;
4029 /* Enable the timer if we're going outside our bounds, in case the user doesn't
4030 move the mouse again */
4032 if ((x <= 0) || (y <= 0) || (x >= infoPtr->rcList.right) ||
4033 (y >= infoPtr->rcList.bottom))
4035 if (!infoPtr->bScrolling)
4037 infoPtr->bScrolling = TRUE;
4038 SetTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr, 1, LISTVIEW_ScrollTimer);
4041 else
4043 infoPtr->bScrolling = FALSE;
4044 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
4047 LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, 0);
4048 return 0;
4051 pt.x = x;
4052 pt.y = y;
4054 ht.pt = pt;
4055 LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
4057 /* reset item marker */
4058 if (infoPtr->nLButtonDownItem != ht.iItem)
4059 infoPtr->nLButtonDownItem = -1;
4061 if (!PtInRect(&rect, pt))
4063 /* this path covers the following:
4064 1. WM_LBUTTONDOWN over selected item (sets focus on it)
4065 2. change focus with keys
4066 3. move mouse over item from step 1 selects it and moves focus on it */
4067 if (infoPtr->nLButtonDownItem != -1 &&
4068 !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
4070 LVITEMW lvItem;
4072 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
4073 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
4075 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
4076 infoPtr->nLButtonDownItem = -1;
4079 if (!infoPtr->bDragging)
4081 ht.pt = infoPtr->ptClickPos;
4082 LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
4084 /* If the click is outside the range of an item, begin a
4085 highlight. If not, begin an item drag. */
4086 if (ht.iItem == -1)
4088 NMHDR hdr;
4090 /* If we're allowing multiple selections, send notification.
4091 If return value is non-zero, cancel. */
4092 if (!(infoPtr->dwStyle & LVS_SINGLESEL) && (notify_hdr(infoPtr, LVN_MARQUEEBEGIN, &hdr) == 0))
4094 /* Store the absolute coordinates of the click */
4095 POINT offset;
4096 LISTVIEW_GetOrigin(infoPtr, &offset);
4098 infoPtr->marqueeOrigin.x = infoPtr->ptClickPos.x - offset.x;
4099 infoPtr->marqueeOrigin.y = infoPtr->ptClickPos.y - offset.y;
4101 /* Begin selection and capture mouse */
4102 infoPtr->bMarqueeSelect = TRUE;
4103 SetCapture(infoPtr->hwndSelf);
4106 else
4108 NMLISTVIEW nmlv;
4110 ZeroMemory(&nmlv, sizeof(nmlv));
4111 nmlv.iItem = ht.iItem;
4112 nmlv.ptAction = infoPtr->ptClickPos;
4114 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
4115 infoPtr->bDragging = TRUE;
4119 return 0;
4123 /* see if we are supposed to be tracking mouse hovering */
4124 if (LISTVIEW_IsHotTracking(infoPtr)) {
4125 TRACKMOUSEEVENT trackinfo;
4127 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
4128 trackinfo.dwFlags = TME_QUERY;
4130 /* see if we are already tracking this hwnd */
4131 _TrackMouseEvent(&trackinfo);
4133 if(!(trackinfo.dwFlags & TME_HOVER) || trackinfo.hwndTrack != infoPtr->hwndSelf) {
4134 trackinfo.dwFlags = TME_HOVER;
4135 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
4136 trackinfo.hwndTrack = infoPtr->hwndSelf;
4138 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
4139 _TrackMouseEvent(&trackinfo);
4143 return 0;
4147 /***
4148 * Tests whether the item is assignable to a list with style lStyle
4150 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
4152 if ( (lpLVItem->mask & LVIF_TEXT) &&
4153 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
4154 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
4156 return TRUE;
4160 /***
4161 * DESCRIPTION:
4162 * Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes.
4164 * PARAMETER(S):
4165 * [I] infoPtr : valid pointer to the listview structure
4166 * [I] lpLVItem : valid pointer to new item attributes
4167 * [I] isNew : the item being set is being inserted
4168 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4169 * [O] bChanged : will be set to TRUE if the item really changed
4171 * RETURN:
4172 * SUCCESS : TRUE
4173 * FAILURE : FALSE
4175 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
4177 ITEM_INFO *lpItem;
4178 NMLISTVIEW nmlv;
4179 UINT uChanged = 0;
4180 LVITEMW item;
4181 /* stateMask is ignored for LVM_INSERTITEM */
4182 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
4184 TRACE("()\n");
4186 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
4188 if (lpLVItem->mask == 0) return TRUE;
4190 if (infoPtr->dwStyle & LVS_OWNERDATA)
4192 /* a virtual listview only stores selection and focus */
4193 if (lpLVItem->mask & ~LVIF_STATE)
4194 return FALSE;
4195 lpItem = NULL;
4197 else
4199 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4200 lpItem = DPA_GetPtr(hdpaSubItems, 0);
4201 assert (lpItem);
4204 /* we need to get the lParam and state of the item */
4205 item.iItem = lpLVItem->iItem;
4206 item.iSubItem = lpLVItem->iSubItem;
4207 item.mask = LVIF_STATE | LVIF_PARAM;
4208 item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
4210 item.state = 0;
4211 item.lParam = 0;
4212 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
4214 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
4215 /* determine what fields will change */
4216 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
4217 uChanged |= LVIF_STATE;
4219 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
4220 uChanged |= LVIF_IMAGE;
4222 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
4223 uChanged |= LVIF_PARAM;
4225 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
4226 uChanged |= LVIF_INDENT;
4228 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
4229 uChanged |= LVIF_TEXT;
4231 TRACE("change mask=0x%x\n", uChanged);
4233 memset(&nmlv, 0, sizeof(NMLISTVIEW));
4234 nmlv.iItem = lpLVItem->iItem;
4235 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
4236 nmlv.uOldState = item.state;
4237 nmlv.uChanged = uChanged ? uChanged : lpLVItem->mask;
4238 nmlv.lParam = item.lParam;
4240 /* Send LVN_ITEMCHANGING notification, if the item is not being inserted
4241 and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications
4242 are enabled. Even nothing really changed we still need to send this,
4243 in this case uChanged mask is just set to passed item mask. */
4244 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
4246 HWND hwndSelf = infoPtr->hwndSelf;
4248 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
4249 return FALSE;
4250 if (!IsWindow(hwndSelf))
4251 return FALSE;
4254 /* When item is inserted we need to shift existing focus index if new item has lower index. */
4255 if (isNew && (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) &&
4256 /* this means we won't hit a focus change path later */
4257 ((uChanged & LVIF_STATE) == 0 || (!(lpLVItem->state & LVIS_FOCUSED) && (infoPtr->nFocusedItem != lpLVItem->iItem))))
4259 if (infoPtr->nFocusedItem != -1 && (lpLVItem->iItem <= infoPtr->nFocusedItem))
4260 infoPtr->nFocusedItem++;
4263 if (!uChanged) return TRUE;
4264 *bChanged = TRUE;
4266 /* copy information */
4267 if (lpLVItem->mask & LVIF_TEXT)
4268 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
4270 if (lpLVItem->mask & LVIF_IMAGE)
4271 lpItem->hdr.iImage = lpLVItem->iImage;
4273 if (lpLVItem->mask & LVIF_PARAM)
4274 lpItem->lParam = lpLVItem->lParam;
4276 if (lpLVItem->mask & LVIF_INDENT)
4277 lpItem->iIndent = lpLVItem->iIndent;
4279 if (uChanged & LVIF_STATE)
4281 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
4283 lpItem->state &= ~stateMask;
4284 lpItem->state |= (lpLVItem->state & stateMask);
4286 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
4288 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
4289 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
4291 else if (stateMask & LVIS_SELECTED)
4293 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
4295 /* If we are asked to change focus, and we manage it, do it.
4296 It's important to have all new item data stored at this point,
4297 because changing existing focus could result in a redrawing operation,
4298 which in turn could ask for disp data, application should see all data
4299 for inserted item when processing LVN_GETDISPINFO.
4301 The way this works application will see nested item change notifications -
4302 changed item notifications interrupted by ones from item losing focus. */
4303 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
4305 if (lpLVItem->state & LVIS_FOCUSED)
4307 /* update selection mark */
4308 if (infoPtr->nFocusedItem == -1 && infoPtr->nSelectionMark == -1)
4309 infoPtr->nSelectionMark = lpLVItem->iItem;
4311 if (infoPtr->nFocusedItem != -1)
4313 /* remove current focus */
4314 item.mask = LVIF_STATE;
4315 item.state = 0;
4316 item.stateMask = LVIS_FOCUSED;
4318 /* recurse with redrawing an item */
4319 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
4322 infoPtr->nFocusedItem = lpLVItem->iItem;
4323 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST);
4325 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
4327 infoPtr->nFocusedItem = -1;
4332 /* if we're inserting the item, we're done */
4333 if (isNew) return TRUE;
4335 /* send LVN_ITEMCHANGED notification */
4336 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
4337 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
4339 return TRUE;
4342 /***
4343 * DESCRIPTION:
4344 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4346 * PARAMETER(S):
4347 * [I] infoPtr : valid pointer to the listview structure
4348 * [I] lpLVItem : valid pointer to new subitem attributes
4349 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4350 * [O] bChanged : will be set to TRUE if the item really changed
4352 * RETURN:
4353 * SUCCESS : TRUE
4354 * FAILURE : FALSE
4356 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
4358 HDPA hdpaSubItems;
4359 SUBITEM_INFO *lpSubItem;
4361 /* we do not support subitems for virtual listviews */
4362 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
4364 /* set subitem only if column is present */
4365 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4367 /* First do some sanity checks */
4368 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
4369 particularly useful. We currently do not actually do anything with
4370 the flag on subitems.
4372 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_DI_SETITEM)) return FALSE;
4373 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
4375 /* get the subitem structure, and create it if not there */
4376 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4377 assert (hdpaSubItems);
4379 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4380 if (!lpSubItem)
4382 SUBITEM_INFO *tmpSubItem;
4383 INT i;
4385 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
4386 if (!lpSubItem) return FALSE;
4387 /* we could binary search here, if need be...*/
4388 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4390 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
4391 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
4393 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
4395 Free(lpSubItem);
4396 return FALSE;
4398 lpSubItem->iSubItem = lpLVItem->iSubItem;
4399 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
4400 *bChanged = TRUE;
4403 if ((lpLVItem->mask & LVIF_IMAGE) && (lpSubItem->hdr.iImage != lpLVItem->iImage))
4405 lpSubItem->hdr.iImage = lpLVItem->iImage;
4406 *bChanged = TRUE;
4409 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW))
4411 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
4412 *bChanged = TRUE;
4415 return TRUE;
4418 /***
4419 * DESCRIPTION:
4420 * Sets item attributes.
4422 * PARAMETER(S):
4423 * [I] infoPtr : valid pointer to the listview structure
4424 * [I] lpLVItem : new item attributes
4425 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4427 * RETURN:
4428 * SUCCESS : TRUE
4429 * FAILURE : FALSE
4431 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
4433 HWND hwndSelf = infoPtr->hwndSelf;
4434 LPWSTR pszText = NULL;
4435 BOOL bResult, bChanged = FALSE;
4436 RECT oldItemArea;
4438 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4440 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4441 return FALSE;
4443 /* Store old item area */
4444 LISTVIEW_GetItemBox(infoPtr, lpLVItem->iItem, &oldItemArea);
4446 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4447 if ((lpLVItem->mask & LVIF_TEXT) && is_text(lpLVItem->pszText))
4449 pszText = lpLVItem->pszText;
4450 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
4453 /* actually set the fields */
4454 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
4456 if (lpLVItem->iSubItem)
4457 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
4458 else
4459 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
4460 if (!IsWindow(hwndSelf))
4461 return FALSE;
4463 /* redraw item, if necessary */
4464 if (bChanged && !infoPtr->bIsDrawing)
4466 /* this little optimization eliminates some nasty flicker */
4467 if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
4468 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
4469 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
4470 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
4471 else
4473 LISTVIEW_InvalidateRect(infoPtr, &oldItemArea);
4474 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
4477 /* restore text */
4478 if (pszText)
4480 textfreeT(lpLVItem->pszText, isW);
4481 lpLVItem->pszText = pszText;
4484 return bResult;
4487 /***
4488 * DESCRIPTION:
4489 * Retrieves the index of the item at coordinate (0, 0) of the client area.
4491 * PARAMETER(S):
4492 * [I] infoPtr : valid pointer to the listview structure
4494 * RETURN:
4495 * item index
4497 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
4499 INT nItem = 0;
4500 SCROLLINFO scrollInfo;
4502 scrollInfo.cbSize = sizeof(SCROLLINFO);
4503 scrollInfo.fMask = SIF_POS;
4505 if (infoPtr->uView == LV_VIEW_LIST)
4507 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4508 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
4510 else if (infoPtr->uView == LV_VIEW_DETAILS)
4512 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4513 nItem = scrollInfo.nPos;
4515 else
4517 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4518 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
4521 TRACE("nItem=%d\n", nItem);
4523 return nItem;
4527 /***
4528 * DESCRIPTION:
4529 * Erases the background of the given rectangle
4531 * PARAMETER(S):
4532 * [I] infoPtr : valid pointer to the listview structure
4533 * [I] hdc : device context handle
4534 * [I] lprcBox : clipping rectangle
4536 * RETURN:
4537 * Success: TRUE
4538 * Failure: FALSE
4540 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
4542 if (!infoPtr->hBkBrush) return FALSE;
4544 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
4546 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
4549 /* Draw main item or subitem */
4550 static void LISTVIEW_DrawItemPart(LISTVIEW_INFO *infoPtr, LVITEMW *item, const NMLVCUSTOMDRAW *nmlvcd, const POINT *pos)
4552 RECT rcSelect, rcLabel, rcBox, rcStateIcon, rcIcon;
4553 HIMAGELIST himl;
4554 UINT format;
4555 RECT *focus;
4557 /* now check if we need to update the focus rectangle */
4558 focus = infoPtr->bFocus && (item->state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4559 if (!focus) item->state &= ~LVIS_FOCUSED;
4561 LISTVIEW_GetItemMetrics(infoPtr, item, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
4562 OffsetRect(&rcBox, pos->x, pos->y);
4563 OffsetRect(&rcSelect, pos->x, pos->y);
4564 OffsetRect(&rcIcon, pos->x, pos->y);
4565 OffsetRect(&rcStateIcon, pos->x, pos->y);
4566 OffsetRect(&rcLabel, pos->x, pos->y);
4567 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
4568 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
4569 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
4571 /* FIXME: temporary hack */
4572 rcSelect.left = rcLabel.left;
4574 /* in icon mode, the label rect is really what we want to draw the
4575 * background for */
4576 /* in detail mode, we want to paint background for label rect when
4577 * item is not selected or listview has full row select; otherwise paint
4578 * background for text only */
4579 if ( infoPtr->uView == LV_VIEW_ICON ||
4580 (infoPtr->uView == LV_VIEW_DETAILS && (!(item->state & LVIS_SELECTED) ||
4581 (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))))
4582 rcSelect = rcLabel;
4584 if (nmlvcd->clrTextBk != CLR_NONE)
4585 ExtTextOutW(nmlvcd->nmcd.hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, NULL, 0, NULL);
4587 if (item->state & LVIS_FOCUSED)
4589 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4591 /* we have to update left focus bound too if item isn't in leftmost column
4592 and reduce right box bound */
4593 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4595 INT leftmost;
4597 if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0)))
4599 INT Originx = pos->x - LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
4600 INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4601 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4603 rcBox.right = LISTVIEW_GetColumnInfo(infoPtr, index)->rcHeader.right + Originx;
4604 rcSelect.left = LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left + Originx;
4608 rcSelect.right = rcBox.right;
4611 /* store new focus rectangle */
4612 infoPtr->rcFocus = rcSelect;
4615 /* state icons */
4616 if (infoPtr->himlState && STATEIMAGEINDEX(item->state) && (item->iSubItem == 0))
4618 UINT stateimage = STATEIMAGEINDEX(item->state);
4619 if (stateimage)
4621 TRACE("stateimage=%d\n", stateimage);
4622 ImageList_Draw(infoPtr->himlState, stateimage-1, nmlvcd->nmcd.hdc, rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
4626 /* item icons */
4627 himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
4628 if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon))
4630 UINT style;
4632 TRACE("iImage=%d\n", item->iImage);
4634 if (item->state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus)
4635 style = ILD_SELECTED;
4636 else
4637 style = ILD_NORMAL;
4639 ImageList_DrawEx(himl, item->iImage, nmlvcd->nmcd.hdc, rcIcon.left, rcIcon.top,
4640 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk,
4641 item->state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT,
4642 style | (item->state & LVIS_OVERLAYMASK));
4645 /* Don't bother painting item being edited */
4646 if (infoPtr->hwndEdit && item->iItem == infoPtr->nEditLabelItem && item->iSubItem == 0) return;
4648 /* figure out the text drawing flags */
4649 format = (infoPtr->uView == LV_VIEW_ICON ? (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
4650 if (infoPtr->uView == LV_VIEW_ICON)
4651 format = (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
4652 else if (item->iSubItem)
4654 switch (LISTVIEW_GetColumnInfo(infoPtr, item->iSubItem)->fmt & LVCFMT_JUSTIFYMASK)
4656 case LVCFMT_RIGHT: format |= DT_RIGHT; break;
4657 case LVCFMT_CENTER: format |= DT_CENTER; break;
4658 default: format |= DT_LEFT;
4661 if (!(format & (DT_RIGHT | DT_CENTER)))
4663 if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
4664 else rcLabel.left += LABEL_HOR_PADDING;
4666 else if (format & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4668 /* for GRIDLINES reduce the bottom so the text formats correctly */
4669 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4670 rcLabel.bottom--;
4672 DrawTextW(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format);
4675 /***
4676 * DESCRIPTION:
4677 * Draws an item.
4679 * PARAMETER(S):
4680 * [I] infoPtr : valid pointer to the listview structure
4681 * [I] hdc : device context handle
4682 * [I] nItem : item index
4683 * [I] nSubItem : subitem index
4684 * [I] pos : item position in client coordinates
4685 * [I] cdmode : custom draw mode
4687 * RETURN:
4688 * Success: TRUE
4689 * Failure: FALSE
4691 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERATOR *subitems, POINT pos, DWORD cdmode)
4693 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4694 static WCHAR callbackW[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4695 DWORD cdsubitemmode = CDRF_DODEFAULT;
4696 RECT *focus, rcBox;
4697 NMLVCUSTOMDRAW nmlvcd;
4698 LVITEMW lvItem;
4700 TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc, nItem, subitems, wine_dbgstr_point(&pos));
4702 /* get information needed for drawing the item */
4703 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
4704 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
4705 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
4706 lvItem.iItem = nItem;
4707 lvItem.iSubItem = 0;
4708 lvItem.state = 0;
4709 lvItem.lParam = 0;
4710 lvItem.cchTextMax = DISP_TEXT_SIZE;
4711 lvItem.pszText = szDispText;
4712 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4713 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
4714 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4716 /* now check if we need to update the focus rectangle */
4717 focus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4718 if (!focus) lvItem.state &= ~LVIS_FOCUSED;
4720 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, NULL, NULL, NULL);
4721 OffsetRect(&rcBox, pos.x, pos.y);
4723 /* Full custom draw stage sequence looks like this:
4725 LV_VIEW_DETAILS:
4727 - CDDS_ITEMPREPAINT
4728 - CDDS_ITEMPREPAINT|CDDS_SUBITEM | => sent n times, where n is number of subitems,
4729 CDDS_ITEMPOSTPAINT|CDDS_SUBITEM | including item iself
4730 - CDDS_ITEMPOSTPAINT
4732 other styles:
4734 - CDDS_ITEMPREPAINT
4735 - CDDS_ITEMPOSTPAINT
4738 /* fill in the custom draw structure */
4739 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
4740 if (cdmode & CDRF_NOTIFYITEMDRAW)
4741 cdsubitemmode = notify_customdraw(infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
4742 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4744 if (subitems)
4746 while (iterator_next(subitems))
4748 DWORD subitemstage = CDRF_DODEFAULT;
4750 /* We need to query for each subitem, item's data (subitem == 0) is already here at this point */
4751 if (subitems->nItem)
4753 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_INDENT;
4754 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
4755 lvItem.iItem = nItem;
4756 lvItem.iSubItem = subitems->nItem;
4757 lvItem.state = 0;
4758 lvItem.lParam = 0;
4759 lvItem.cchTextMax = DISP_TEXT_SIZE;
4760 lvItem.pszText = szDispText;
4761 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4762 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4763 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4764 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
4765 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4767 /* update custom draw data */
4768 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &nmlvcd.nmcd.rc, NULL, NULL, NULL, NULL);
4769 OffsetRect(&nmlvcd.nmcd.rc, pos.x, pos.y);
4770 nmlvcd.iSubItem = subitems->nItem;
4773 if (cdsubitemmode & CDRF_NOTIFYSUBITEMDRAW)
4774 subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
4775 else
4777 nmlvcd.clrTextBk = infoPtr->clrTextBk;
4778 nmlvcd.clrText = infoPtr->clrText;
4781 if (subitems->nItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
4782 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4783 else if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4784 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
4786 if (!(subitemstage & CDRF_SKIPDEFAULT))
4787 LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos);
4789 if (subitemstage & CDRF_NOTIFYPOSTPAINT)
4790 subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPOSTPAINT, &nmlvcd);
4793 else
4795 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4796 LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos);
4799 postpaint:
4800 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4802 nmlvcd.iSubItem = 0;
4803 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
4806 return TRUE;
4809 /***
4810 * DESCRIPTION:
4811 * Draws listview items when in owner draw mode.
4813 * PARAMETER(S):
4814 * [I] infoPtr : valid pointer to the listview structure
4815 * [I] hdc : device context handle
4817 * RETURN:
4818 * None
4820 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4822 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4823 DWORD cditemmode = CDRF_DODEFAULT;
4824 NMLVCUSTOMDRAW nmlvcd;
4825 POINT Origin, Position;
4826 DRAWITEMSTRUCT dis;
4827 LVITEMW item;
4829 TRACE("()\n");
4831 ZeroMemory(&dis, sizeof(dis));
4833 /* Get scroll info once before loop */
4834 LISTVIEW_GetOrigin(infoPtr, &Origin);
4836 /* iterate through the invalidated rows */
4837 while(iterator_next(i))
4839 item.iItem = i->nItem;
4840 item.iSubItem = 0;
4841 item.mask = LVIF_PARAM | LVIF_STATE;
4842 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4843 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4845 dis.CtlType = ODT_LISTVIEW;
4846 dis.CtlID = uID;
4847 dis.itemID = item.iItem;
4848 dis.itemAction = ODA_DRAWENTIRE;
4849 dis.itemState = 0;
4850 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4851 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4852 dis.hwndItem = infoPtr->hwndSelf;
4853 dis.hDC = hdc;
4854 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4855 dis.rcItem.left = Position.x + Origin.x;
4856 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4857 dis.rcItem.top = Position.y + Origin.y;
4858 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4859 dis.itemData = item.lParam;
4861 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4864 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4865 * structure for the rest. of the paint cycle
4867 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4868 if (cdmode & CDRF_NOTIFYITEMDRAW)
4869 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4871 if (!(cditemmode & CDRF_SKIPDEFAULT))
4873 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4874 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4877 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4878 notify_postpaint(infoPtr, &nmlvcd);
4882 /***
4883 * DESCRIPTION:
4884 * Draws listview items when in report display mode.
4886 * PARAMETER(S):
4887 * [I] infoPtr : valid pointer to the listview structure
4888 * [I] hdc : device context handle
4889 * [I] cdmode : custom draw mode
4891 * RETURN:
4892 * None
4894 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4896 INT rgntype;
4897 RECT rcClip, rcItem;
4898 POINT Origin, Position;
4899 RANGES colRanges;
4900 INT col, index;
4901 ITERATOR j;
4903 TRACE("()\n");
4905 /* figure out what to draw */
4906 rgntype = GetClipBox(hdc, &rcClip);
4907 if (rgntype == NULLREGION) return;
4909 /* Get scroll info once before loop */
4910 LISTVIEW_GetOrigin(infoPtr, &Origin);
4912 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4914 /* narrow down the columns we need to paint */
4915 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4917 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4919 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4920 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4921 ranges_additem(colRanges, index);
4923 iterator_rangesitems(&j, colRanges);
4925 /* in full row select, we _have_ to draw the main item */
4926 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4927 j.nSpecial = 0;
4929 /* iterate through the invalidated rows */
4930 while(iterator_next(i))
4932 RANGES subitems;
4933 ITERATOR k;
4935 SelectObject(hdc, infoPtr->hFont);
4936 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4937 Position.y += Origin.y;
4939 subitems = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4941 /* iterate through the invalidated columns */
4942 while(iterator_next(&j))
4944 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4945 Position.x = (j.nItem == 0) ? rcItem.left + Origin.x : Origin.x;
4947 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4949 rcItem.top = 0;
4950 rcItem.bottom = infoPtr->nItemHeight;
4951 OffsetRect(&rcItem, Origin.x, Position.y);
4952 if (!RectVisible(hdc, &rcItem)) continue;
4955 ranges_additem(subitems, j.nItem);
4958 iterator_rangesitems(&k, subitems);
4959 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, &k, Position, cdmode);
4960 iterator_destroy(&k);
4962 iterator_destroy(&j);
4965 /***
4966 * DESCRIPTION:
4967 * Draws the gridlines if necessary when in report display mode.
4969 * PARAMETER(S):
4970 * [I] infoPtr : valid pointer to the listview structure
4971 * [I] hdc : device context handle
4973 * RETURN:
4974 * None
4976 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4978 INT rgntype;
4979 INT y, itemheight;
4980 INT col, index;
4981 HPEN hPen, hOldPen;
4982 RECT rcClip, rcItem = {0};
4983 POINT Origin;
4984 RANGES colRanges;
4985 ITERATOR j;
4986 BOOL rmost = FALSE;
4988 TRACE("()\n");
4990 /* figure out what to draw */
4991 rgntype = GetClipBox(hdc, &rcClip);
4992 if (rgntype == NULLREGION) return;
4994 /* Get scroll info once before loop */
4995 LISTVIEW_GetOrigin(infoPtr, &Origin);
4997 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4999 /* narrow down the columns we need to paint */
5000 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
5002 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
5004 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5005 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
5006 ranges_additem(colRanges, index);
5009 /* is right most vertical line visible? */
5010 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
5012 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
5013 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5014 rmost = (rcItem.right + Origin.x < rcClip.right);
5017 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
5019 hOldPen = SelectObject ( hdc, hPen );
5021 /* draw the vertical lines for the columns */
5022 iterator_rangesitems(&j, colRanges);
5023 while(iterator_next(&j))
5025 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
5026 if (rcItem.left == 0) continue; /* skip leftmost column */
5027 rcItem.left += Origin.x;
5028 rcItem.right += Origin.x;
5029 rcItem.top = infoPtr->rcList.top;
5030 rcItem.bottom = infoPtr->rcList.bottom;
5031 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
5032 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
5033 LineTo (hdc, rcItem.left, rcItem.bottom);
5035 iterator_destroy(&j);
5036 /* draw rightmost grid line if visible */
5037 if (rmost)
5039 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
5040 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
5041 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5043 rcItem.right += Origin.x;
5045 MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL);
5046 LineTo (hdc, rcItem.right, infoPtr->rcList.bottom);
5049 /* draw the horizontal lines for the rows */
5050 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
5051 rcItem.left = infoPtr->rcList.left;
5052 rcItem.right = infoPtr->rcList.right;
5053 for(y = Origin.y > 1 ? Origin.y - 1 : itemheight - 1 + Origin.y % itemheight; y<=infoPtr->rcList.bottom; y+=itemheight)
5055 rcItem.bottom = rcItem.top = y;
5056 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
5057 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
5058 LineTo (hdc, rcItem.right, rcItem.top);
5061 SelectObject( hdc, hOldPen );
5062 DeleteObject( hPen );
5064 else
5065 ranges_destroy(colRanges);
5068 /***
5069 * DESCRIPTION:
5070 * Draws listview items when in list display mode.
5072 * PARAMETER(S):
5073 * [I] infoPtr : valid pointer to the listview structure
5074 * [I] hdc : device context handle
5075 * [I] cdmode : custom draw mode
5077 * RETURN:
5078 * None
5080 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
5082 POINT Origin, Position;
5084 /* Get scroll info once before loop */
5085 LISTVIEW_GetOrigin(infoPtr, &Origin);
5087 while(iterator_prev(i))
5089 SelectObject(hdc, infoPtr->hFont);
5090 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
5091 Position.x += Origin.x;
5092 Position.y += Origin.y;
5094 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, NULL, Position, cdmode);
5099 /***
5100 * DESCRIPTION:
5101 * Draws listview items.
5103 * PARAMETER(S):
5104 * [I] infoPtr : valid pointer to the listview structure
5105 * [I] hdc : device context handle
5106 * [I] prcErase : rect to be erased before refresh (may be NULL)
5108 * RETURN:
5109 * NoneX
5111 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
5113 COLORREF oldTextColor = 0, oldBkColor = 0;
5114 NMLVCUSTOMDRAW nmlvcd;
5115 HFONT hOldFont = 0;
5116 DWORD cdmode;
5117 INT oldBkMode = 0;
5118 RECT rcClient;
5119 ITERATOR i;
5120 HDC hdcOrig = hdc;
5121 HBITMAP hbmp = NULL;
5122 RANGE range;
5124 LISTVIEW_DUMP(infoPtr);
5126 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
5127 TRACE("double buffering\n");
5129 hdc = CreateCompatibleDC(hdcOrig);
5130 if (!hdc) {
5131 ERR("Failed to create DC for backbuffer\n");
5132 return;
5134 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
5135 infoPtr->rcList.bottom);
5136 if (!hbmp) {
5137 ERR("Failed to create bitmap for backbuffer\n");
5138 DeleteDC(hdc);
5139 return;
5142 SelectObject(hdc, hbmp);
5143 SelectObject(hdc, infoPtr->hFont);
5145 if(GetClipBox(hdcOrig, &rcClient))
5146 IntersectClipRect(hdc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
5147 } else {
5148 /* Save dc values we're gonna trash while drawing
5149 * FIXME: Should be done in LISTVIEW_DrawItem() */
5150 hOldFont = SelectObject(hdc, infoPtr->hFont);
5151 oldBkMode = GetBkMode(hdc);
5152 oldBkColor = GetBkColor(hdc);
5153 oldTextColor = GetTextColor(hdc);
5156 infoPtr->bIsDrawing = TRUE;
5158 if (prcErase) {
5159 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
5160 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
5161 /* If no erasing was done (usually because RedrawWindow was called
5162 * with RDW_INVALIDATE only) we need to copy the old contents into
5163 * the backbuffer before continuing. */
5164 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
5165 infoPtr->rcList.right - infoPtr->rcList.left,
5166 infoPtr->rcList.bottom - infoPtr->rcList.top,
5167 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
5170 GetClientRect(infoPtr->hwndSelf, &rcClient);
5171 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
5172 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
5173 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
5175 /* nothing to draw */
5176 if(infoPtr->nItemCount == 0) goto enddraw;
5178 /* figure out what we need to draw */
5179 iterator_visibleitems(&i, infoPtr, hdc);
5180 range = iterator_range(&i);
5182 /* send cache hint notification */
5183 if (infoPtr->dwStyle & LVS_OWNERDATA)
5185 NMLVCACHEHINT nmlv;
5187 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
5188 nmlv.iFrom = range.lower;
5189 nmlv.iTo = range.upper - 1;
5190 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
5193 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
5194 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
5195 else
5197 if (infoPtr->uView == LV_VIEW_DETAILS)
5198 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
5199 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
5200 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
5202 /* if we have a focus rect and it's visible, draw it */
5203 if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
5204 (range.upper - 1) >= infoPtr->nFocusedItem)
5205 LISTVIEW_DrawFocusRect(infoPtr, hdc);
5207 iterator_destroy(&i);
5209 enddraw:
5210 /* For LVS_EX_GRIDLINES go and draw lines */
5211 /* This includes the case where there were *no* items */
5212 if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
5213 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
5215 /* Draw marquee rectangle if appropriate */
5216 if (infoPtr->bMarqueeSelect)
5217 DrawFocusRect(hdc, &infoPtr->marqueeDrawRect);
5219 if (cdmode & CDRF_NOTIFYPOSTPAINT)
5220 notify_postpaint(infoPtr, &nmlvcd);
5222 if(hbmp) {
5223 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
5224 infoPtr->rcList.right - infoPtr->rcList.left,
5225 infoPtr->rcList.bottom - infoPtr->rcList.top,
5226 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
5228 DeleteObject(hbmp);
5229 DeleteDC(hdc);
5230 } else {
5231 SelectObject(hdc, hOldFont);
5232 SetBkMode(hdc, oldBkMode);
5233 SetBkColor(hdc, oldBkColor);
5234 SetTextColor(hdc, oldTextColor);
5237 infoPtr->bIsDrawing = FALSE;
5241 /***
5242 * DESCRIPTION:
5243 * Calculates the approximate width and height of a given number of items.
5245 * PARAMETER(S):
5246 * [I] infoPtr : valid pointer to the listview structure
5247 * [I] nItemCount : number of items
5248 * [I] wWidth : width
5249 * [I] wHeight : height
5251 * RETURN:
5252 * Returns a DWORD. The width in the low word and the height in high word.
5254 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
5255 WORD wWidth, WORD wHeight)
5257 DWORD dwViewRect = 0;
5259 if (nItemCount == -1)
5260 nItemCount = infoPtr->nItemCount;
5262 if (infoPtr->uView == LV_VIEW_LIST)
5264 INT nItemCountPerColumn = 1;
5265 INT nColumnCount = 0;
5267 if (wHeight == 0xFFFF)
5269 /* use current height */
5270 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5273 if (wHeight < infoPtr->nItemHeight)
5274 wHeight = infoPtr->nItemHeight;
5276 if (nItemCount > 0)
5278 if (infoPtr->nItemHeight > 0)
5280 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
5281 if (nItemCountPerColumn == 0)
5282 nItemCountPerColumn = 1;
5284 if (nItemCount % nItemCountPerColumn != 0)
5285 nColumnCount = nItemCount / nItemCountPerColumn;
5286 else
5287 nColumnCount = nItemCount / nItemCountPerColumn + 1;
5291 /* Microsoft padding magic */
5292 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
5293 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
5295 dwViewRect = MAKELONG(wWidth, wHeight);
5297 else if (infoPtr->uView == LV_VIEW_DETAILS)
5299 RECT rcBox;
5301 if (infoPtr->nItemCount > 0)
5303 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
5304 wWidth = rcBox.right - rcBox.left;
5305 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
5307 else
5309 /* use current height and width */
5310 if (wHeight == 0xffff)
5311 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5312 if (wWidth == 0xffff)
5313 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5316 dwViewRect = MAKELONG(wWidth, wHeight);
5318 else if (infoPtr->uView == LV_VIEW_ICON)
5320 UINT rows,cols;
5321 UINT nItemWidth;
5322 UINT nItemHeight;
5324 nItemWidth = infoPtr->iconSpacing.cx;
5325 nItemHeight = infoPtr->iconSpacing.cy;
5327 if (wWidth == 0xffff)
5328 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5330 if (wWidth < nItemWidth)
5331 wWidth = nItemWidth;
5333 cols = wWidth / nItemWidth;
5334 if (cols > nItemCount)
5335 cols = nItemCount;
5336 if (cols < 1)
5337 cols = 1;
5339 if (nItemCount)
5341 rows = nItemCount / cols;
5342 if (nItemCount % cols)
5343 rows++;
5345 else
5346 rows = 0;
5348 wHeight = (nItemHeight * rows)+2;
5349 wWidth = (nItemWidth * cols)+2;
5351 dwViewRect = MAKELONG(wWidth, wHeight);
5353 else if (infoPtr->uView == LV_VIEW_SMALLICON)
5354 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5356 return dwViewRect;
5359 /***
5360 * DESCRIPTION:
5361 * Cancel edit label with saving item text.
5363 * PARAMETER(S):
5364 * [I] infoPtr : valid pointer to the listview structure
5366 * RETURN:
5367 * Always returns TRUE.
5369 static LRESULT LISTVIEW_CancelEditLabel(LISTVIEW_INFO *infoPtr)
5371 if (infoPtr->hwndEdit)
5373 /* handle value will be lost after LISTVIEW_EndEditLabelT */
5374 HWND edit = infoPtr->hwndEdit;
5376 LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit));
5377 SendMessageW(edit, WM_CLOSE, 0, 0);
5380 return TRUE;
5383 /***
5384 * DESCRIPTION:
5385 * Create a drag image list for the specified item.
5387 * PARAMETER(S):
5388 * [I] infoPtr : valid pointer to the listview structure
5389 * [I] iItem : index of item
5390 * [O] lppt : Upper-left corner of the image
5392 * RETURN:
5393 * Returns a handle to the image list if successful, NULL otherwise.
5395 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
5397 RECT rcItem;
5398 SIZE size;
5399 POINT pos;
5400 HDC hdc, hdcOrig;
5401 HBITMAP hbmp, hOldbmp;
5402 HFONT hOldFont;
5403 HIMAGELIST dragList = 0;
5404 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
5406 if (iItem < 0 || iItem >= infoPtr->nItemCount || !lppt)
5407 return 0;
5409 rcItem.left = LVIR_BOUNDS;
5410 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
5411 return 0;
5413 lppt->x = rcItem.left;
5414 lppt->y = rcItem.top;
5416 size.cx = rcItem.right - rcItem.left;
5417 size.cy = rcItem.bottom - rcItem.top;
5419 hdcOrig = GetDC(infoPtr->hwndSelf);
5420 hdc = CreateCompatibleDC(hdcOrig);
5421 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
5422 hOldbmp = SelectObject(hdc, hbmp);
5423 hOldFont = SelectObject(hdc, infoPtr->hFont);
5425 rcItem.left = rcItem.top = 0;
5426 rcItem.right = size.cx;
5427 rcItem.bottom = size.cy;
5428 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
5430 pos.x = pos.y = 0;
5431 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, NULL, pos, CDRF_DODEFAULT))
5433 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
5434 SelectObject(hdc, hOldbmp);
5435 ImageList_Add(dragList, hbmp, 0);
5437 else
5438 SelectObject(hdc, hOldbmp);
5440 SelectObject(hdc, hOldFont);
5441 DeleteObject(hbmp);
5442 DeleteDC(hdc);
5443 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
5445 TRACE("ret=%p\n", dragList);
5447 return dragList;
5451 /***
5452 * DESCRIPTION:
5453 * Removes all listview items and subitems.
5455 * PARAMETER(S):
5456 * [I] infoPtr : valid pointer to the listview structure
5458 * RETURN:
5459 * SUCCESS : TRUE
5460 * FAILURE : FALSE
5462 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
5464 HDPA hdpaSubItems = NULL;
5465 BOOL suppress = FALSE;
5466 ITEMHDR *hdrItem;
5467 ITEM_INFO *lpItem;
5468 ITEM_ID *lpID;
5469 INT i, j;
5471 TRACE("()\n");
5473 /* we do it directly, to avoid notifications */
5474 ranges_clear(infoPtr->selectionRanges);
5475 infoPtr->nSelectionMark = -1;
5476 infoPtr->nFocusedItem = -1;
5477 SetRectEmpty(&infoPtr->rcFocus);
5478 /* But we are supposed to leave nHotItem as is! */
5480 /* send LVN_DELETEALLITEMS notification */
5481 if (!(infoPtr->dwStyle & LVS_OWNERDATA) || !destroy)
5483 NMLISTVIEW nmlv;
5485 memset(&nmlv, 0, sizeof(NMLISTVIEW));
5486 nmlv.iItem = -1;
5487 suppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
5490 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
5492 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5494 /* send LVN_DELETEITEM notification, if not suppressed
5495 and if it is not a virtual listview */
5496 if (!suppress) notify_deleteitem(infoPtr, i);
5497 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
5498 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5499 /* free id struct */
5500 j = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5501 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, j);
5502 DPA_DeletePtr(infoPtr->hdpaItemIds, j);
5503 Free(lpID);
5504 /* both item and subitem start with ITEMHDR header */
5505 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
5507 hdrItem = DPA_GetPtr(hdpaSubItems, j);
5508 if (is_text(hdrItem->pszText)) Free(hdrItem->pszText);
5509 Free(hdrItem);
5511 DPA_Destroy(hdpaSubItems);
5512 DPA_DeletePtr(infoPtr->hdpaItems, i);
5514 DPA_DeletePtr(infoPtr->hdpaPosX, i);
5515 DPA_DeletePtr(infoPtr->hdpaPosY, i);
5516 infoPtr->nItemCount --;
5519 if (!destroy)
5521 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5522 LISTVIEW_UpdateScroll(infoPtr);
5524 LISTVIEW_InvalidateList(infoPtr);
5526 return TRUE;
5529 /***
5530 * DESCRIPTION:
5531 * Scrolls, and updates the columns, when a column is changing width.
5533 * PARAMETER(S):
5534 * [I] infoPtr : valid pointer to the listview structure
5535 * [I] nColumn : column to scroll
5536 * [I] dx : amount of scroll, in pixels
5538 * RETURN:
5539 * None.
5541 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
5543 COLUMN_INFO *lpColumnInfo;
5544 RECT rcOld, rcCol;
5545 POINT ptOrigin;
5546 INT nCol;
5547 HDITEMW hdi;
5549 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
5550 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
5551 rcCol = lpColumnInfo->rcHeader;
5552 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
5553 rcCol.left = rcCol.right;
5555 /* adjust the other columns */
5556 hdi.mask = HDI_ORDER;
5557 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
5559 INT nOrder = hdi.iOrder;
5560 for (nCol = 0; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
5562 hdi.mask = HDI_ORDER;
5563 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nCol, (LPARAM)&hdi);
5564 if (hdi.iOrder >= nOrder) {
5565 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
5566 lpColumnInfo->rcHeader.left += dx;
5567 lpColumnInfo->rcHeader.right += dx;
5572 /* do not update screen if not in report mode */
5573 if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
5575 /* Need to reset the item width when inserting a new column */
5576 infoPtr->nItemWidth += dx;
5578 LISTVIEW_UpdateScroll(infoPtr);
5579 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
5581 /* scroll to cover the deleted column, and invalidate for redraw */
5582 rcOld = infoPtr->rcList;
5583 rcOld.left = ptOrigin.x + rcCol.left + dx;
5584 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5587 /***
5588 * DESCRIPTION:
5589 * Removes a column from the listview control.
5591 * PARAMETER(S):
5592 * [I] infoPtr : valid pointer to the listview structure
5593 * [I] nColumn : column index
5595 * RETURN:
5596 * SUCCESS : TRUE
5597 * FAILURE : FALSE
5599 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
5601 RECT rcCol;
5603 TRACE("nColumn=%d\n", nColumn);
5605 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
5606 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5608 /* While the MSDN specifically says that column zero should not be deleted,
5609 what actually happens is that the column itself is deleted but no items or subitems
5610 are removed.
5613 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
5615 if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
5616 return FALSE;
5618 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
5619 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
5621 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
5623 SUBITEM_INFO *lpSubItem, *lpDelItem;
5624 HDPA hdpaSubItems;
5625 INT nItem, nSubItem, i;
5627 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5629 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
5630 nSubItem = 0;
5631 lpDelItem = 0;
5632 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
5634 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
5635 if (lpSubItem->iSubItem == nColumn)
5637 nSubItem = i;
5638 lpDelItem = lpSubItem;
5640 else if (lpSubItem->iSubItem > nColumn)
5642 lpSubItem->iSubItem--;
5646 /* if we found our subitem, zap it */
5647 if (nSubItem > 0)
5649 /* free string */
5650 if (is_text(lpDelItem->hdr.pszText))
5651 Free(lpDelItem->hdr.pszText);
5653 /* free item */
5654 Free(lpDelItem);
5656 /* free dpa memory */
5657 DPA_DeletePtr(hdpaSubItems, nSubItem);
5662 /* update the other column info */
5663 LISTVIEW_UpdateItemSize(infoPtr);
5664 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
5665 LISTVIEW_InvalidateList(infoPtr);
5666 else
5667 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
5669 return TRUE;
5672 /***
5673 * DESCRIPTION:
5674 * Invalidates the listview after an item's insertion or deletion.
5676 * PARAMETER(S):
5677 * [I] infoPtr : valid pointer to the listview structure
5678 * [I] nItem : item index
5679 * [I] dir : -1 if deleting, 1 if inserting
5681 * RETURN:
5682 * None
5684 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
5686 INT nPerCol, nItemCol, nItemRow;
5687 RECT rcScroll;
5688 POINT Origin;
5690 /* if we don't refresh, what's the point of scrolling? */
5691 if (!is_redrawing(infoPtr)) return;
5693 assert (abs(dir) == 1);
5695 /* arrange icons if autoarrange is on */
5696 if (is_autoarrange(infoPtr))
5698 BOOL arrange = TRUE;
5699 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
5700 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
5701 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5704 /* scrollbars need updating */
5705 LISTVIEW_UpdateScroll(infoPtr);
5707 /* figure out the item's position */
5708 if (infoPtr->uView == LV_VIEW_DETAILS)
5709 nPerCol = infoPtr->nItemCount + 1;
5710 else if (infoPtr->uView == LV_VIEW_LIST)
5711 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
5712 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5713 return;
5715 nItemCol = nItem / nPerCol;
5716 nItemRow = nItem % nPerCol;
5717 LISTVIEW_GetOrigin(infoPtr, &Origin);
5719 /* move the items below up a slot */
5720 rcScroll.left = nItemCol * infoPtr->nItemWidth;
5721 rcScroll.top = nItemRow * infoPtr->nItemHeight;
5722 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
5723 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5724 OffsetRect(&rcScroll, Origin.x, Origin.y);
5725 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
5726 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5728 TRACE("Invalidating rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
5729 InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
5732 /* report has only that column, so we're done */
5733 if (infoPtr->uView == LV_VIEW_DETAILS) return;
5735 /* now for LISTs, we have to deal with the columns to the right */
5736 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
5737 rcScroll.top = 0;
5738 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
5739 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5740 OffsetRect(&rcScroll, Origin.x, Origin.y);
5741 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5742 InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
5745 /***
5746 * DESCRIPTION:
5747 * Removes an item from the listview control.
5749 * PARAMETER(S):
5750 * [I] infoPtr : valid pointer to the listview structure
5751 * [I] nItem : item index
5753 * RETURN:
5754 * SUCCESS : TRUE
5755 * FAILURE : FALSE
5757 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
5759 LVITEMW item;
5760 const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
5761 INT focus = infoPtr->nFocusedItem;
5763 TRACE("(nItem=%d)\n", nItem);
5765 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5767 /* remove selection, and focus */
5768 item.state = 0;
5769 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
5770 LISTVIEW_SetItemState(infoPtr, nItem, &item);
5772 /* send LVN_DELETEITEM notification. */
5773 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
5775 /* we need to do this here, because we'll be deleting stuff */
5776 if (is_icon)
5777 LISTVIEW_InvalidateItem(infoPtr, nItem);
5779 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5781 HDPA hdpaSubItems;
5782 ITEMHDR *hdrItem;
5783 ITEM_INFO *lpItem;
5784 ITEM_ID *lpID;
5785 INT i;
5787 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5788 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5790 /* free id struct */
5791 i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5792 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i);
5793 DPA_DeletePtr(infoPtr->hdpaItemIds, i);
5794 Free(lpID);
5795 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
5797 hdrItem = DPA_GetPtr(hdpaSubItems, i);
5798 if (is_text(hdrItem->pszText)) Free(hdrItem->pszText);
5799 Free(hdrItem);
5801 DPA_Destroy(hdpaSubItems);
5804 if (is_icon)
5806 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5807 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
5810 infoPtr->nItemCount--;
5811 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
5812 LISTVIEW_ShiftFocus(infoPtr, focus, nItem, -1);
5814 /* now is the invalidation fun */
5815 if (!is_icon)
5816 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
5817 return TRUE;
5821 /***
5822 * DESCRIPTION:
5823 * Callback implementation for editlabel control
5825 * PARAMETER(S):
5826 * [I] infoPtr : valid pointer to the listview structure
5827 * [I] storeText : store edit box text as item text
5828 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5830 * RETURN:
5831 * SUCCESS : TRUE
5832 * FAILURE : FALSE
5834 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
5836 HWND hwndSelf = infoPtr->hwndSelf;
5837 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5838 NMLVDISPINFOW dispInfo;
5839 INT editedItem = infoPtr->nEditLabelItem;
5840 BOOL same;
5841 WCHAR *pszText = NULL;
5842 BOOL res;
5844 if (storeText)
5846 DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit);
5848 if (len)
5850 if ((pszText = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
5852 if (isW) GetWindowTextW(infoPtr->hwndEdit, pszText, len+1);
5853 else GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len+1);
5858 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
5860 ZeroMemory(&dispInfo, sizeof(dispInfo));
5861 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5862 dispInfo.item.iItem = editedItem;
5863 dispInfo.item.iSubItem = 0;
5864 dispInfo.item.stateMask = ~0;
5865 dispInfo.item.pszText = szDispText;
5866 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5867 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW))
5869 res = FALSE;
5870 goto cleanup;
5873 if (isW)
5874 same = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
5875 else
5877 LPWSTR tmp = textdupTtoW(pszText, FALSE);
5878 same = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
5879 textfreeT(tmp, FALSE);
5882 /* add the text from the edit in */
5883 dispInfo.item.mask |= LVIF_TEXT;
5884 dispInfo.item.pszText = same ? NULL : pszText;
5885 dispInfo.item.cchTextMax = textlenT(dispInfo.item.pszText, isW);
5887 /* Do we need to update the Item Text */
5888 res = notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW);
5890 infoPtr->nEditLabelItem = -1;
5891 infoPtr->hwndEdit = 0;
5893 if (!res) goto cleanup;
5895 if (!IsWindow(hwndSelf))
5897 res = FALSE;
5898 goto cleanup;
5900 if (!pszText) return TRUE;
5901 if (same)
5903 res = TRUE;
5904 goto cleanup;
5907 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5909 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
5910 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
5911 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
5913 LISTVIEW_InvalidateItem(infoPtr, editedItem);
5914 res = TRUE;
5915 goto cleanup;
5919 ZeroMemory(&dispInfo, sizeof(dispInfo));
5920 dispInfo.item.mask = LVIF_TEXT;
5921 dispInfo.item.iItem = editedItem;
5922 dispInfo.item.iSubItem = 0;
5923 dispInfo.item.pszText = pszText;
5924 dispInfo.item.cchTextMax = textlenT(pszText, isW);
5925 res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
5927 cleanup:
5928 Free(pszText);
5930 return res;
5933 /***
5934 * DESCRIPTION:
5935 * Subclassed edit control windproc function
5937 * PARAMETER(S):
5938 * [I] hwnd : the edit window handle
5939 * [I] uMsg : the message that is to be processed
5940 * [I] wParam : first message parameter
5941 * [I] lParam : second message parameter
5942 * [I] isW : TRUE if input is Unicode
5944 * RETURN:
5945 * Zero.
5947 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
5949 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
5950 BOOL save = TRUE;
5952 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
5953 hwnd, uMsg, wParam, lParam, isW);
5955 switch (uMsg)
5957 case WM_GETDLGCODE:
5958 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
5960 case WM_DESTROY:
5962 WNDPROC editProc = infoPtr->EditWndProc;
5963 infoPtr->EditWndProc = 0;
5964 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
5965 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
5968 case WM_KEYDOWN:
5969 if (VK_ESCAPE == (INT)wParam)
5971 save = FALSE;
5972 break;
5974 else if (VK_RETURN == (INT)wParam)
5975 break;
5977 default:
5978 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
5981 /* kill the edit */
5982 if (infoPtr->hwndEdit)
5983 LISTVIEW_EndEditLabelT(infoPtr, save, isW);
5985 SendMessageW(hwnd, WM_CLOSE, 0, 0);
5986 return 0;
5989 /***
5990 * DESCRIPTION:
5991 * Subclassed edit control Unicode windproc function
5993 * PARAMETER(S):
5994 * [I] hwnd : the edit window handle
5995 * [I] uMsg : the message that is to be processed
5996 * [I] wParam : first message parameter
5997 * [I] lParam : second message parameter
5999 * RETURN:
6001 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
6003 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
6006 /***
6007 * DESCRIPTION:
6008 * Subclassed edit control ANSI windproc function
6010 * PARAMETER(S):
6011 * [I] hwnd : the edit window handle
6012 * [I] uMsg : the message that is to be processed
6013 * [I] wParam : first message parameter
6014 * [I] lParam : second message parameter
6016 * RETURN:
6018 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
6020 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
6023 /***
6024 * DESCRIPTION:
6025 * Creates a subclassed edit control
6027 * PARAMETER(S):
6028 * [I] infoPtr : valid pointer to the listview structure
6029 * [I] text : initial text for the edit
6030 * [I] style : the window style
6031 * [I] isW : TRUE if input is Unicode
6033 * RETURN:
6035 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, BOOL isW)
6037 static const DWORD style = WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER|WS_VISIBLE;
6038 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
6039 HWND hedit;
6041 TRACE("(%p, text=%s, isW=%d)\n", infoPtr, debugtext_t(text, isW), isW);
6043 /* window will be resized and positioned after LVN_BEGINLABELEDIT */
6044 if (isW)
6045 hedit = CreateWindowW(WC_EDITW, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
6046 else
6047 hedit = CreateWindowA(WC_EDITA, (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
6049 if (!hedit) return 0;
6051 infoPtr->EditWndProc = (WNDPROC)
6052 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
6053 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
6055 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
6056 SendMessageW(hedit, EM_SETLIMITTEXT, DISP_TEXT_SIZE-1, 0);
6058 return hedit;
6061 /***
6062 * DESCRIPTION:
6063 * Begin in place editing of specified list view item
6065 * PARAMETER(S):
6066 * [I] infoPtr : valid pointer to the listview structure
6067 * [I] nItem : item index
6068 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
6070 * RETURN:
6071 * SUCCESS : TRUE
6072 * FAILURE : FALSE
6074 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
6076 WCHAR disptextW[DISP_TEXT_SIZE] = { 0 };
6077 HWND hwndSelf = infoPtr->hwndSelf;
6078 NMLVDISPINFOW dispInfo;
6079 HFONT hOldFont = NULL;
6080 TEXTMETRICW tm;
6081 RECT rect;
6082 SIZE sz;
6083 HDC hdc;
6085 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
6087 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
6089 /* remove existing edit box */
6090 if (infoPtr->hwndEdit)
6092 SetFocus(infoPtr->hwndSelf);
6093 infoPtr->hwndEdit = 0;
6096 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6098 infoPtr->nEditLabelItem = nItem;
6100 LISTVIEW_SetSelection(infoPtr, nItem);
6101 LISTVIEW_SetItemFocus(infoPtr, nItem);
6102 LISTVIEW_InvalidateItem(infoPtr, nItem);
6104 rect.left = LVIR_LABEL;
6105 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
6107 ZeroMemory(&dispInfo, sizeof(dispInfo));
6108 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
6109 dispInfo.item.iItem = nItem;
6110 dispInfo.item.iSubItem = 0;
6111 dispInfo.item.stateMask = ~0;
6112 dispInfo.item.pszText = disptextW;
6113 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
6114 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
6116 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, isW);
6117 if (!infoPtr->hwndEdit) return 0;
6119 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
6121 if (!IsWindow(hwndSelf))
6122 return 0;
6123 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
6124 infoPtr->hwndEdit = 0;
6125 return 0;
6128 TRACE("disp text=%s\n", debugtext_t(dispInfo.item.pszText, isW));
6130 /* position and display edit box */
6131 hdc = GetDC(infoPtr->hwndSelf);
6133 /* select the font to get appropriate metric dimensions */
6134 if (infoPtr->hFont)
6135 hOldFont = SelectObject(hdc, infoPtr->hFont);
6137 /* use real edit box content, it could be altered during LVN_BEGINLABELEDIT notification */
6138 GetWindowTextW(infoPtr->hwndEdit, disptextW, DISP_TEXT_SIZE);
6139 TRACE("edit box text=%s\n", debugstr_w(disptextW));
6141 /* get string length in pixels */
6142 GetTextExtentPoint32W(hdc, disptextW, lstrlenW(disptextW), &sz);
6144 /* add extra spacing for the next character */
6145 GetTextMetricsW(hdc, &tm);
6146 sz.cx += tm.tmMaxCharWidth * 2;
6148 if (infoPtr->hFont)
6149 SelectObject(hdc, hOldFont);
6151 ReleaseDC(infoPtr->hwndSelf, hdc);
6153 sz.cy = rect.bottom - rect.top + 2;
6154 rect.left -= 2;
6155 rect.top -= 1;
6156 TRACE("moving edit=(%d,%d)-(%d,%d)\n", rect.left, rect.top, sz.cx, sz.cy);
6157 MoveWindow(infoPtr->hwndEdit, rect.left, rect.top, sz.cx, sz.cy, FALSE);
6158 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
6159 SetFocus(infoPtr->hwndEdit);
6160 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
6161 return infoPtr->hwndEdit;
6165 /***
6166 * DESCRIPTION:
6167 * Ensures the specified item is visible, scrolling into view if necessary.
6169 * PARAMETER(S):
6170 * [I] infoPtr : valid pointer to the listview structure
6171 * [I] nItem : item index
6172 * [I] bPartial : partially or entirely visible
6174 * RETURN:
6175 * SUCCESS : TRUE
6176 * FAILURE : FALSE
6178 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
6180 INT nScrollPosHeight = 0;
6181 INT nScrollPosWidth = 0;
6182 INT nHorzAdjust = 0;
6183 INT nVertAdjust = 0;
6184 INT nHorzDiff = 0;
6185 INT nVertDiff = 0;
6186 RECT rcItem, rcTemp;
6188 rcItem.left = LVIR_BOUNDS;
6189 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
6191 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
6193 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
6195 /* scroll left/right, but in LV_VIEW_DETAILS mode */
6196 if (infoPtr->uView == LV_VIEW_LIST)
6197 nScrollPosWidth = infoPtr->nItemWidth;
6198 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6199 nScrollPosWidth = 1;
6201 if (rcItem.left < infoPtr->rcList.left)
6203 nHorzAdjust = -1;
6204 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
6206 else
6208 nHorzAdjust = 1;
6209 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
6213 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
6215 /* scroll up/down, but not in LVS_LIST mode */
6216 if (infoPtr->uView == LV_VIEW_DETAILS)
6217 nScrollPosHeight = infoPtr->nItemHeight;
6218 else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
6219 nScrollPosHeight = 1;
6221 if (rcItem.top < infoPtr->rcList.top)
6223 nVertAdjust = -1;
6224 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
6226 else
6228 nVertAdjust = 1;
6229 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
6233 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
6235 if (nScrollPosWidth)
6237 INT diff = nHorzDiff / nScrollPosWidth;
6238 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
6239 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff);
6242 if (nScrollPosHeight)
6244 INT diff = nVertDiff / nScrollPosHeight;
6245 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
6246 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff);
6249 return TRUE;
6252 /***
6253 * DESCRIPTION:
6254 * Searches for an item with specific characteristics.
6256 * PARAMETER(S):
6257 * [I] hwnd : window handle
6258 * [I] nStart : base item index
6259 * [I] lpFindInfo : item information to look for
6261 * RETURN:
6262 * SUCCESS : index of item
6263 * FAILURE : -1
6265 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
6266 const LVFINDINFOW *lpFindInfo)
6268 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6269 BOOL bWrap = FALSE, bNearest = FALSE;
6270 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
6271 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
6272 POINT Position, Destination;
6273 LVITEMW lvItem;
6275 /* Search in virtual listviews should be done by application, not by
6276 listview control, so we just send LVN_ODFINDITEMW and return the result */
6277 if (infoPtr->dwStyle & LVS_OWNERDATA)
6279 NMLVFINDITEMW nmlv;
6281 nmlv.iStart = nStart;
6282 nmlv.lvfi = *lpFindInfo;
6283 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
6286 if (!lpFindInfo || nItem < 0) return -1;
6288 lvItem.mask = 0;
6289 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) ||
6290 lpFindInfo->flags & LVFI_SUBSTRING)
6292 lvItem.mask |= LVIF_TEXT;
6293 lvItem.pszText = szDispText;
6294 lvItem.cchTextMax = DISP_TEXT_SIZE;
6297 if (lpFindInfo->flags & LVFI_WRAP)
6298 bWrap = TRUE;
6300 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
6301 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
6303 POINT Origin;
6304 RECT rcArea;
6306 LISTVIEW_GetOrigin(infoPtr, &Origin);
6307 Destination.x = lpFindInfo->pt.x - Origin.x;
6308 Destination.y = lpFindInfo->pt.y - Origin.y;
6309 switch(lpFindInfo->vkDirection)
6311 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
6312 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
6313 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
6314 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
6315 case VK_HOME: Destination.x = Destination.y = 0; break;
6316 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
6317 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
6318 case VK_END:
6319 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
6320 Destination.x = rcArea.right;
6321 Destination.y = rcArea.bottom;
6322 break;
6323 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
6325 bNearest = TRUE;
6327 else Destination.x = Destination.y = 0;
6329 /* if LVFI_PARAM is specified, all other flags are ignored */
6330 if (lpFindInfo->flags & LVFI_PARAM)
6332 lvItem.mask |= LVIF_PARAM;
6333 bNearest = FALSE;
6334 lvItem.mask &= ~LVIF_TEXT;
6337 again:
6338 for (; nItem < nLast; nItem++)
6340 lvItem.iItem = nItem;
6341 lvItem.iSubItem = 0;
6342 lvItem.pszText = szDispText;
6343 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6345 if (lvItem.mask & LVIF_PARAM)
6347 if (lpFindInfo->lParam == lvItem.lParam)
6348 return nItem;
6349 else
6350 continue;
6353 if (lvItem.mask & LVIF_TEXT)
6355 if (lpFindInfo->flags & (LVFI_PARTIAL | LVFI_SUBSTRING))
6357 WCHAR *p = strstrW(lvItem.pszText, lpFindInfo->psz);
6358 if (!p || p != lvItem.pszText) continue;
6360 else
6362 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
6366 if (!bNearest) return nItem;
6368 /* This is very inefficient. To do a good job here,
6369 * we need a sorted array of (x,y) item positions */
6370 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6372 /* compute the distance^2 to the destination */
6373 xdist = Destination.x - Position.x;
6374 ydist = Destination.y - Position.y;
6375 dist = xdist * xdist + ydist * ydist;
6377 /* remember the distance, and item if it's closer */
6378 if (dist < mindist)
6380 mindist = dist;
6381 nNearestItem = nItem;
6385 if (bWrap)
6387 nItem = 0;
6388 nLast = min(nStart + 1, infoPtr->nItemCount);
6389 bWrap = FALSE;
6390 goto again;
6393 return nNearestItem;
6396 /***
6397 * DESCRIPTION:
6398 * Searches for an item with specific characteristics.
6400 * PARAMETER(S):
6401 * [I] hwnd : window handle
6402 * [I] nStart : base item index
6403 * [I] lpFindInfo : item information to look for
6405 * RETURN:
6406 * SUCCESS : index of item
6407 * FAILURE : -1
6409 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
6410 const LVFINDINFOA *lpFindInfo)
6412 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) ||
6413 lpFindInfo->flags & LVFI_SUBSTRING;
6414 LVFINDINFOW fiw;
6415 INT res;
6416 LPWSTR strW = NULL;
6418 memcpy(&fiw, lpFindInfo, sizeof(fiw));
6419 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
6420 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
6421 textfreeT(strW, FALSE);
6422 return res;
6425 /***
6426 * DESCRIPTION:
6427 * Retrieves column attributes.
6429 * PARAMETER(S):
6430 * [I] infoPtr : valid pointer to the listview structure
6431 * [I] nColumn : column index
6432 * [IO] lpColumn : column information
6433 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6434 * otherwise it is in fact a LPLVCOLUMNA
6436 * RETURN:
6437 * SUCCESS : TRUE
6438 * FAILURE : FALSE
6440 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
6442 COLUMN_INFO *lpColumnInfo;
6443 HDITEMW hdi;
6445 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6446 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6448 /* initialize memory */
6449 ZeroMemory(&hdi, sizeof(hdi));
6451 if (lpColumn->mask & LVCF_TEXT)
6453 hdi.mask |= HDI_TEXT;
6454 hdi.pszText = lpColumn->pszText;
6455 hdi.cchTextMax = lpColumn->cchTextMax;
6458 if (lpColumn->mask & LVCF_IMAGE)
6459 hdi.mask |= HDI_IMAGE;
6461 if (lpColumn->mask & LVCF_ORDER)
6462 hdi.mask |= HDI_ORDER;
6464 if (lpColumn->mask & LVCF_SUBITEM)
6465 hdi.mask |= HDI_LPARAM;
6467 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
6469 if (lpColumn->mask & LVCF_FMT)
6470 lpColumn->fmt = lpColumnInfo->fmt;
6472 if (lpColumn->mask & LVCF_WIDTH)
6473 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
6475 if (lpColumn->mask & LVCF_IMAGE)
6476 lpColumn->iImage = hdi.iImage;
6478 if (lpColumn->mask & LVCF_ORDER)
6479 lpColumn->iOrder = hdi.iOrder;
6481 if (lpColumn->mask & LVCF_SUBITEM)
6482 lpColumn->iSubItem = hdi.lParam;
6484 if (lpColumn->mask & LVCF_MINWIDTH)
6485 lpColumn->cxMin = lpColumnInfo->cxMin;
6487 return TRUE;
6491 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6493 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
6495 if (!lpiArray)
6496 return FALSE;
6498 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
6501 /***
6502 * DESCRIPTION:
6503 * Retrieves the column width.
6505 * PARAMETER(S):
6506 * [I] infoPtr : valid pointer to the listview structure
6507 * [I] int : column index
6509 * RETURN:
6510 * SUCCESS : column width
6511 * FAILURE : zero
6513 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
6515 INT nColumnWidth = 0;
6516 HDITEMW hdItem;
6518 TRACE("nColumn=%d\n", nColumn);
6520 /* we have a 'column' in LIST and REPORT mode only */
6521 switch(infoPtr->uView)
6523 case LV_VIEW_LIST:
6524 nColumnWidth = infoPtr->nItemWidth;
6525 break;
6526 case LV_VIEW_DETAILS:
6527 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6528 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6529 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6531 * TODO: should we do the same in LVM_GETCOLUMN?
6533 hdItem.mask = HDI_WIDTH;
6534 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
6536 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
6537 return 0;
6539 nColumnWidth = hdItem.cxy;
6540 break;
6543 TRACE("nColumnWidth=%d\n", nColumnWidth);
6544 return nColumnWidth;
6547 /***
6548 * DESCRIPTION:
6549 * In list or report display mode, retrieves the number of items that can fit
6550 * vertically in the visible area. In icon or small icon display mode,
6551 * retrieves the total number of visible items.
6553 * PARAMETER(S):
6554 * [I] infoPtr : valid pointer to the listview structure
6556 * RETURN:
6557 * Number of fully visible items.
6559 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
6561 switch (infoPtr->uView)
6563 case LV_VIEW_ICON:
6564 case LV_VIEW_SMALLICON:
6565 return infoPtr->nItemCount;
6566 case LV_VIEW_DETAILS:
6567 return LISTVIEW_GetCountPerColumn(infoPtr);
6568 case LV_VIEW_LIST:
6569 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
6571 assert(FALSE);
6572 return 0;
6575 /***
6576 * DESCRIPTION:
6577 * Retrieves an image list handle.
6579 * PARAMETER(S):
6580 * [I] infoPtr : valid pointer to the listview structure
6581 * [I] nImageList : image list identifier
6583 * RETURN:
6584 * SUCCESS : image list handle
6585 * FAILURE : NULL
6587 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
6589 switch (nImageList)
6591 case LVSIL_NORMAL: return infoPtr->himlNormal;
6592 case LVSIL_SMALL: return infoPtr->himlSmall;
6593 case LVSIL_STATE: return infoPtr->himlState;
6594 case LVSIL_GROUPHEADER:
6595 FIXME("LVSIL_GROUPHEADER not supported\n");
6596 break;
6597 default:
6598 WARN("got unknown imagelist index - %d\n", nImageList);
6600 return NULL;
6603 /* LISTVIEW_GetISearchString */
6605 /***
6606 * DESCRIPTION:
6607 * Retrieves item attributes.
6609 * PARAMETER(S):
6610 * [I] hwnd : window handle
6611 * [IO] lpLVItem : item info
6612 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6613 * if FALSE, then lpLVItem is a LPLVITEMA.
6615 * NOTE:
6616 * This is the internal 'GetItem' interface -- it tries to
6617 * be smart and avoid text copies, if possible, by modifying
6618 * lpLVItem->pszText to point to the text string. Please note
6619 * that this is not always possible (e.g. OWNERDATA), so on
6620 * entry you *must* supply valid values for pszText, and cchTextMax.
6621 * The only difference to the documented interface is that upon
6622 * return, you should use *only* the lpLVItem->pszText, rather than
6623 * the buffer pointer you provided on input. Most code already does
6624 * that, so it's not a problem.
6625 * For the two cases when the text must be copied (that is,
6626 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6628 * RETURN:
6629 * SUCCESS : TRUE
6630 * FAILURE : FALSE
6632 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6634 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
6635 NMLVDISPINFOW dispInfo;
6636 ITEM_INFO *lpItem;
6637 ITEMHDR* pItemHdr;
6638 HDPA hdpaSubItems;
6639 INT isubitem;
6641 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6643 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6644 return FALSE;
6646 if (lpLVItem->mask == 0) return TRUE;
6647 TRACE("mask=%x\n", lpLVItem->mask);
6649 /* make a local copy */
6650 isubitem = lpLVItem->iSubItem;
6652 /* a quick optimization if all we're asked is the focus state
6653 * these queries are worth optimising since they are common,
6654 * and can be answered in constant time, without the heavy accesses */
6655 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
6656 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
6658 lpLVItem->state = 0;
6659 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6660 lpLVItem->state |= LVIS_FOCUSED;
6661 return TRUE;
6664 ZeroMemory(&dispInfo, sizeof(dispInfo));
6666 /* if the app stores all the data, handle it separately */
6667 if (infoPtr->dwStyle & LVS_OWNERDATA)
6669 dispInfo.item.state = 0;
6671 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
6672 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
6673 ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
6675 UINT mask = lpLVItem->mask;
6677 /* NOTE: copy only fields which we _know_ are initialized, some apps
6678 * depend on the uninitialized fields being 0 */
6679 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
6680 dispInfo.item.iItem = lpLVItem->iItem;
6681 dispInfo.item.iSubItem = isubitem;
6682 if (lpLVItem->mask & LVIF_TEXT)
6684 if (lpLVItem->mask & LVIF_NORECOMPUTE)
6685 /* reset mask */
6686 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
6687 else
6689 dispInfo.item.pszText = lpLVItem->pszText;
6690 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6693 if (lpLVItem->mask & LVIF_STATE)
6694 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
6695 /* could be zeroed on LVIF_NORECOMPUTE case */
6696 if (dispInfo.item.mask)
6698 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6699 dispInfo.item.stateMask = lpLVItem->stateMask;
6700 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6702 /* full size structure expected - _WIN32IE >= 0x560 */
6703 *lpLVItem = dispInfo.item;
6705 else if (lpLVItem->mask & LVIF_INDENT)
6707 /* indent member expected - _WIN32IE >= 0x300 */
6708 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
6710 else
6712 /* minimal structure expected */
6713 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
6715 lpLVItem->mask = mask;
6716 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
6720 /* make sure lParam is zeroed out */
6721 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
6723 /* callback marked pointer required here */
6724 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
6725 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
6727 /* we store only a little state, so if we're not asked, we're done */
6728 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
6730 /* if focus is handled by us, report it */
6731 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6733 lpLVItem->state &= ~LVIS_FOCUSED;
6734 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6735 lpLVItem->state |= LVIS_FOCUSED;
6738 /* and do the same for selection, if we handle it */
6739 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6741 lpLVItem->state &= ~LVIS_SELECTED;
6742 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6743 lpLVItem->state |= LVIS_SELECTED;
6746 return TRUE;
6749 /* find the item and subitem structures before we proceed */
6750 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
6751 lpItem = DPA_GetPtr(hdpaSubItems, 0);
6752 assert (lpItem);
6754 if (isubitem)
6756 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
6757 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
6758 if (!lpSubItem)
6760 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
6761 isubitem = 0;
6764 else
6765 pItemHdr = &lpItem->hdr;
6767 /* Do we need to query the state from the app? */
6768 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
6770 dispInfo.item.mask |= LVIF_STATE;
6771 dispInfo.item.stateMask = infoPtr->uCallbackMask;
6774 /* Do we need to enquire about the image? */
6775 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
6776 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
6778 dispInfo.item.mask |= LVIF_IMAGE;
6779 dispInfo.item.iImage = I_IMAGECALLBACK;
6782 /* Only items support indentation */
6783 if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
6784 (isubitem == 0))
6786 dispInfo.item.mask |= LVIF_INDENT;
6787 dispInfo.item.iIndent = I_INDENTCALLBACK;
6790 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6791 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
6792 !is_text(pItemHdr->pszText))
6794 dispInfo.item.mask |= LVIF_TEXT;
6795 dispInfo.item.pszText = lpLVItem->pszText;
6796 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6797 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
6798 *dispInfo.item.pszText = '\0';
6801 /* If we don't have all the requested info, query the application */
6802 if (dispInfo.item.mask)
6804 dispInfo.item.iItem = lpLVItem->iItem;
6805 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
6806 dispInfo.item.lParam = lpItem->lParam;
6807 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6808 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
6811 /* we should not store values for subitems */
6812 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
6814 /* Now, handle the iImage field */
6815 if (dispInfo.item.mask & LVIF_IMAGE)
6817 lpLVItem->iImage = dispInfo.item.iImage;
6818 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
6819 pItemHdr->iImage = dispInfo.item.iImage;
6821 else if (lpLVItem->mask & LVIF_IMAGE)
6823 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
6824 lpLVItem->iImage = pItemHdr->iImage;
6825 else
6826 lpLVItem->iImage = 0;
6829 /* The pszText field */
6830 if (dispInfo.item.mask & LVIF_TEXT)
6832 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
6833 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
6835 lpLVItem->pszText = dispInfo.item.pszText;
6837 else if (lpLVItem->mask & LVIF_TEXT)
6839 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6840 if (isW || !is_text(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
6841 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
6844 /* Next is the lParam field */
6845 if (dispInfo.item.mask & LVIF_PARAM)
6847 lpLVItem->lParam = dispInfo.item.lParam;
6848 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
6849 lpItem->lParam = dispInfo.item.lParam;
6851 else if (lpLVItem->mask & LVIF_PARAM)
6852 lpLVItem->lParam = lpItem->lParam;
6854 /* if this is a subitem, we're done */
6855 if (isubitem) return TRUE;
6857 /* ... the state field (this one is different due to uCallbackmask) */
6858 if (lpLVItem->mask & LVIF_STATE)
6860 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
6861 if (dispInfo.item.mask & LVIF_STATE)
6863 lpLVItem->state &= ~dispInfo.item.stateMask;
6864 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
6866 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6868 lpLVItem->state &= ~LVIS_FOCUSED;
6869 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6870 lpLVItem->state |= LVIS_FOCUSED;
6872 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6874 lpLVItem->state &= ~LVIS_SELECTED;
6875 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6876 lpLVItem->state |= LVIS_SELECTED;
6880 /* and last, but not least, the indent field */
6881 if (dispInfo.item.mask & LVIF_INDENT)
6883 lpLVItem->iIndent = dispInfo.item.iIndent;
6884 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
6885 lpItem->iIndent = dispInfo.item.iIndent;
6887 else if (lpLVItem->mask & LVIF_INDENT)
6889 lpLVItem->iIndent = lpItem->iIndent;
6892 return TRUE;
6895 /***
6896 * DESCRIPTION:
6897 * Retrieves item attributes.
6899 * PARAMETER(S):
6900 * [I] hwnd : window handle
6901 * [IO] lpLVItem : item info
6902 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6903 * if FALSE, then lpLVItem is a LPLVITEMA.
6905 * NOTE:
6906 * This is the external 'GetItem' interface -- it properly copies
6907 * the text in the provided buffer.
6909 * RETURN:
6910 * SUCCESS : TRUE
6911 * FAILURE : FALSE
6913 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6915 LPWSTR pszText;
6916 BOOL bResult;
6918 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6919 return FALSE;
6921 pszText = lpLVItem->pszText;
6922 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
6923 if (bResult && (lpLVItem->mask & LVIF_TEXT) && lpLVItem->pszText != pszText)
6925 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6926 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
6927 else
6928 pszText = LPSTR_TEXTCALLBACKW;
6930 lpLVItem->pszText = pszText;
6932 return bResult;
6936 /***
6937 * DESCRIPTION:
6938 * Retrieves the position (upper-left) of the listview control item.
6939 * Note that for LVS_ICON style, the upper-left is that of the icon
6940 * and not the bounding box.
6942 * PARAMETER(S):
6943 * [I] infoPtr : valid pointer to the listview structure
6944 * [I] nItem : item index
6945 * [O] lpptPosition : coordinate information
6947 * RETURN:
6948 * SUCCESS : TRUE
6949 * FAILURE : FALSE
6951 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
6953 POINT Origin;
6955 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
6957 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6959 LISTVIEW_GetOrigin(infoPtr, &Origin);
6960 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
6962 if (infoPtr->uView == LV_VIEW_ICON)
6964 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6965 lpptPosition->y += ICON_TOP_PADDING;
6967 lpptPosition->x += Origin.x;
6968 lpptPosition->y += Origin.y;
6970 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
6971 return TRUE;
6975 /***
6976 * DESCRIPTION:
6977 * Retrieves the bounding rectangle for a listview control item.
6979 * PARAMETER(S):
6980 * [I] infoPtr : valid pointer to the listview structure
6981 * [I] nItem : item index
6982 * [IO] lprc : bounding rectangle coordinates
6983 * lprc->left specifies the portion of the item for which the bounding
6984 * rectangle will be retrieved.
6986 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
6987 * including the icon and label.
6989 * * For LVS_ICON
6990 * * Experiment shows that native control returns:
6991 * * width = min (48, length of text line)
6992 * * .left = position.x - (width - iconsize.cx)/2
6993 * * .right = .left + width
6994 * * height = #lines of text * ntmHeight + icon height + 8
6995 * * .top = position.y - 2
6996 * * .bottom = .top + height
6997 * * separation between items .y = itemSpacing.cy - height
6998 * * .x = itemSpacing.cx - width
6999 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
7001 * * For LVS_ICON
7002 * * Experiment shows that native control returns:
7003 * * width = iconSize.cx + 16
7004 * * .left = position.x - (width - iconsize.cx)/2
7005 * * .right = .left + width
7006 * * height = iconSize.cy + 4
7007 * * .top = position.y - 2
7008 * * .bottom = .top + height
7009 * * separation between items .y = itemSpacing.cy - height
7010 * * .x = itemSpacing.cx - width
7011 * LVIR_LABEL Returns the bounding rectangle of the item text.
7013 * * For LVS_ICON
7014 * * Experiment shows that native control returns:
7015 * * width = text length
7016 * * .left = position.x - width/2
7017 * * .right = .left + width
7018 * * height = ntmH * linecount + 2
7019 * * .top = position.y + iconSize.cy + 6
7020 * * .bottom = .top + height
7021 * * separation between items .y = itemSpacing.cy - height
7022 * * .x = itemSpacing.cx - width
7023 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
7024 * rectangles, but excludes columns in report view.
7026 * RETURN:
7027 * SUCCESS : TRUE
7028 * FAILURE : FALSE
7030 * NOTES
7031 * Note that the bounding rectangle of the label in the LVS_ICON view depends
7032 * upon whether the window has the focus currently and on whether the item
7033 * is the one with the focus. Ensure that the control's record of which
7034 * item has the focus agrees with the items' records.
7036 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
7038 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7039 BOOL doLabel = TRUE, oversizedBox = FALSE;
7040 POINT Position, Origin;
7041 LVITEMW lvItem;
7042 LONG mode;
7044 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
7046 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7048 LISTVIEW_GetOrigin(infoPtr, &Origin);
7049 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7051 /* Be smart and try to figure out the minimum we have to do */
7052 if (lprc->left == LVIR_ICON) doLabel = FALSE;
7053 if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
7054 if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
7055 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
7056 oversizedBox = TRUE;
7058 /* get what we need from the item before hand, so we make
7059 * only one request. This can speed up things, if data
7060 * is stored on the app side */
7061 lvItem.mask = 0;
7062 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7063 if (doLabel) lvItem.mask |= LVIF_TEXT;
7064 lvItem.iItem = nItem;
7065 lvItem.iSubItem = 0;
7066 lvItem.pszText = szDispText;
7067 lvItem.cchTextMax = DISP_TEXT_SIZE;
7068 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
7069 /* we got the state already up, simulate it here, to avoid a reget */
7070 if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
7072 lvItem.mask |= LVIF_STATE;
7073 lvItem.stateMask = LVIS_FOCUSED;
7074 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
7077 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
7078 lprc->left = LVIR_BOUNDS;
7080 mode = lprc->left;
7081 switch(lprc->left)
7083 case LVIR_ICON:
7084 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
7085 break;
7087 case LVIR_LABEL:
7088 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
7089 break;
7091 case LVIR_BOUNDS:
7092 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
7093 break;
7095 case LVIR_SELECTBOUNDS:
7096 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
7097 break;
7099 default:
7100 WARN("Unknown value: %d\n", lprc->left);
7101 return FALSE;
7104 if (infoPtr->uView == LV_VIEW_DETAILS)
7106 if (mode != LVIR_BOUNDS)
7107 OffsetRect(lprc, Origin.x + LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left,
7108 Position.y + Origin.y);
7109 else
7110 OffsetRect(lprc, Origin.x, Position.y + Origin.y);
7112 else
7113 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
7115 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
7117 return TRUE;
7120 /***
7121 * DESCRIPTION:
7122 * Retrieves the spacing between listview control items.
7124 * PARAMETER(S):
7125 * [I] infoPtr : valid pointer to the listview structure
7126 * [IO] lprc : rectangle to receive the output
7127 * on input, lprc->top = nSubItem
7128 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
7130 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
7131 * not only those of the first column.
7133 * RETURN:
7134 * TRUE: success
7135 * FALSE: failure
7137 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT item, LPRECT lprc)
7139 RECT rect = { 0, 0, 0, 0 };
7140 POINT origin;
7141 INT y;
7143 if (!lprc) return FALSE;
7145 TRACE("(item=%d, subitem=%d, type=%d)\n", item, lprc->top, lprc->left);
7146 /* Subitem of '0' means item itself, and this works for all control view modes */
7147 if (lprc->top == 0)
7148 return LISTVIEW_GetItemRect(infoPtr, item, lprc);
7150 if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
7152 LISTVIEW_GetOrigin(infoPtr, &origin);
7153 /* this works for any item index, no matter if it exists or not */
7154 y = item * infoPtr->nItemHeight + origin.y;
7156 if (infoPtr->hwndHeader && SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)&rect))
7158 rect.top = 0;
7159 rect.bottom = infoPtr->nItemHeight;
7161 else
7163 /* Native implementation is broken for this case and garbage is left for left and right fields,
7164 we zero them to get predictable output */
7165 lprc->left = lprc->right = lprc->top = 0;
7166 lprc->bottom = infoPtr->nItemHeight;
7167 OffsetRect(lprc, origin.x, y);
7168 TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
7169 return TRUE;
7172 switch (lprc->left)
7174 case LVIR_ICON:
7176 /* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */
7177 if (infoPtr->himlSmall)
7178 rect.right = rect.left + infoPtr->iconSize.cx;
7179 else
7180 rect.right = rect.left;
7182 rect.bottom = rect.top + infoPtr->iconSize.cy;
7183 break;
7185 case LVIR_LABEL:
7186 case LVIR_BOUNDS:
7187 break;
7189 default:
7190 ERR("Unknown bounds=%d\n", lprc->left);
7191 return FALSE;
7194 OffsetRect(&rect, origin.x, y);
7195 *lprc = rect;
7196 TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
7198 return TRUE;
7201 /***
7202 * DESCRIPTION:
7203 * Retrieves the spacing between listview control items.
7205 * PARAMETER(S):
7206 * [I] infoPtr : valid pointer to the listview structure
7207 * [I] bSmall : flag for small or large icon
7209 * RETURN:
7210 * Horizontal + vertical spacing
7212 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
7214 LONG lResult;
7216 if (!bSmall)
7218 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7220 else
7222 if (infoPtr->uView == LV_VIEW_ICON)
7223 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
7224 else
7225 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
7227 return lResult;
7230 /***
7231 * DESCRIPTION:
7232 * Retrieves the state of a listview control item.
7234 * PARAMETER(S):
7235 * [I] infoPtr : valid pointer to the listview structure
7236 * [I] nItem : item index
7237 * [I] uMask : state mask
7239 * RETURN:
7240 * State specified by the mask.
7242 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
7244 LVITEMW lvItem;
7246 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
7248 lvItem.iItem = nItem;
7249 lvItem.iSubItem = 0;
7250 lvItem.mask = LVIF_STATE;
7251 lvItem.stateMask = uMask;
7252 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
7254 return lvItem.state & uMask;
7257 /***
7258 * DESCRIPTION:
7259 * Retrieves the text of a listview control item or subitem.
7261 * PARAMETER(S):
7262 * [I] hwnd : window handle
7263 * [I] nItem : item index
7264 * [IO] lpLVItem : item information
7265 * [I] isW : TRUE if lpLVItem is Unicode
7267 * RETURN:
7268 * SUCCESS : string length
7269 * FAILURE : 0
7271 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7273 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
7275 lpLVItem->mask = LVIF_TEXT;
7276 lpLVItem->iItem = nItem;
7277 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
7279 return textlenT(lpLVItem->pszText, isW);
7282 /***
7283 * DESCRIPTION:
7284 * Searches for an item based on properties + relationships.
7286 * PARAMETER(S):
7287 * [I] infoPtr : valid pointer to the listview structure
7288 * [I] nItem : item index
7289 * [I] uFlags : relationship flag
7291 * RETURN:
7292 * SUCCESS : item index
7293 * FAILURE : -1
7295 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
7297 UINT uMask = 0;
7298 LVFINDINFOW lvFindInfo;
7299 INT nCountPerColumn;
7300 INT nCountPerRow;
7301 INT i;
7303 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
7304 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
7306 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
7308 if (uFlags & LVNI_CUT)
7309 uMask |= LVIS_CUT;
7311 if (uFlags & LVNI_DROPHILITED)
7312 uMask |= LVIS_DROPHILITED;
7314 if (uFlags & LVNI_FOCUSED)
7315 uMask |= LVIS_FOCUSED;
7317 if (uFlags & LVNI_SELECTED)
7318 uMask |= LVIS_SELECTED;
7320 /* if we're asked for the focused item, that's only one,
7321 * so it's worth optimizing */
7322 if (uFlags & LVNI_FOCUSED)
7324 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
7325 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
7328 if (uFlags & LVNI_ABOVE)
7330 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7332 while (nItem >= 0)
7334 nItem--;
7335 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7336 return nItem;
7339 else
7341 /* Special case for autoarrange - move 'til the top of a list */
7342 if (is_autoarrange(infoPtr))
7344 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7345 while (nItem - nCountPerRow >= 0)
7347 nItem -= nCountPerRow;
7348 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7349 return nItem;
7351 return -1;
7353 lvFindInfo.flags = LVFI_NEARESTXY;
7354 lvFindInfo.vkDirection = VK_UP;
7355 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7356 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7358 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7359 return nItem;
7363 else if (uFlags & LVNI_BELOW)
7365 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7367 while (nItem < infoPtr->nItemCount)
7369 nItem++;
7370 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7371 return nItem;
7374 else
7376 /* Special case for autoarrange - move 'til the bottom of a list */
7377 if (is_autoarrange(infoPtr))
7379 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7380 while (nItem + nCountPerRow < infoPtr->nItemCount )
7382 nItem += nCountPerRow;
7383 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7384 return nItem;
7386 return -1;
7388 lvFindInfo.flags = LVFI_NEARESTXY;
7389 lvFindInfo.vkDirection = VK_DOWN;
7390 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7391 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7393 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7394 return nItem;
7398 else if (uFlags & LVNI_TOLEFT)
7400 if (infoPtr->uView == LV_VIEW_LIST)
7402 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7403 while (nItem - nCountPerColumn >= 0)
7405 nItem -= nCountPerColumn;
7406 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7407 return nItem;
7410 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7412 /* Special case for autoarrange - move 'til the beginning of a row */
7413 if (is_autoarrange(infoPtr))
7415 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7416 while (nItem % nCountPerRow > 0)
7418 nItem --;
7419 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7420 return nItem;
7422 return -1;
7424 lvFindInfo.flags = LVFI_NEARESTXY;
7425 lvFindInfo.vkDirection = VK_LEFT;
7426 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7427 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7429 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7430 return nItem;
7434 else if (uFlags & LVNI_TORIGHT)
7436 if (infoPtr->uView == LV_VIEW_LIST)
7438 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7439 while (nItem + nCountPerColumn < infoPtr->nItemCount)
7441 nItem += nCountPerColumn;
7442 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7443 return nItem;
7446 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7448 /* Special case for autoarrange - move 'til the end of a row */
7449 if (is_autoarrange(infoPtr))
7451 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7452 while (nItem % nCountPerRow < nCountPerRow - 1 )
7454 nItem ++;
7455 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7456 return nItem;
7458 return -1;
7460 lvFindInfo.flags = LVFI_NEARESTXY;
7461 lvFindInfo.vkDirection = VK_RIGHT;
7462 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7463 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7465 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7466 return nItem;
7470 else
7472 nItem++;
7474 /* search by index */
7475 for (i = nItem; i < infoPtr->nItemCount; i++)
7477 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
7478 return i;
7482 return -1;
7485 /* LISTVIEW_GetNumberOfWorkAreas */
7487 /***
7488 * DESCRIPTION:
7489 * Retrieves the origin coordinates when in icon or small icon display mode.
7491 * PARAMETER(S):
7492 * [I] infoPtr : valid pointer to the listview structure
7493 * [O] lpptOrigin : coordinate information
7495 * RETURN:
7496 * None.
7498 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
7500 INT nHorzPos = 0, nVertPos = 0;
7501 SCROLLINFO scrollInfo;
7503 scrollInfo.cbSize = sizeof(SCROLLINFO);
7504 scrollInfo.fMask = SIF_POS;
7506 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
7507 nHorzPos = scrollInfo.nPos;
7508 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7509 nVertPos = scrollInfo.nPos;
7511 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
7513 lpptOrigin->x = infoPtr->rcList.left;
7514 lpptOrigin->y = infoPtr->rcList.top;
7515 if (infoPtr->uView == LV_VIEW_LIST)
7516 nHorzPos *= infoPtr->nItemWidth;
7517 else if (infoPtr->uView == LV_VIEW_DETAILS)
7518 nVertPos *= infoPtr->nItemHeight;
7520 lpptOrigin->x -= nHorzPos;
7521 lpptOrigin->y -= nVertPos;
7523 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
7526 /***
7527 * DESCRIPTION:
7528 * Retrieves the width of a string.
7530 * PARAMETER(S):
7531 * [I] hwnd : window handle
7532 * [I] lpszText : text string to process
7533 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7535 * RETURN:
7536 * SUCCESS : string width (in pixels)
7537 * FAILURE : zero
7539 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
7541 SIZE stringSize;
7543 stringSize.cx = 0;
7544 if (is_text(lpszText))
7546 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
7547 HDC hdc = GetDC(infoPtr->hwndSelf);
7548 HFONT hOldFont = SelectObject(hdc, hFont);
7550 if (isW)
7551 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
7552 else
7553 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
7554 SelectObject(hdc, hOldFont);
7555 ReleaseDC(infoPtr->hwndSelf, hdc);
7557 return stringSize.cx;
7560 /***
7561 * DESCRIPTION:
7562 * Determines which listview item is located at the specified position.
7564 * PARAMETER(S):
7565 * [I] infoPtr : valid pointer to the listview structure
7566 * [IO] lpht : hit test information
7567 * [I] subitem : fill out iSubItem.
7568 * [I] select : return the index only if the hit selects the item
7570 * NOTE:
7571 * (mm 20001022): We must not allow iSubItem to be touched, for
7572 * an app might pass only a structure with space up to iItem!
7573 * (MS Office 97 does that for instance in the file open dialog)
7575 * RETURN:
7576 * SUCCESS : item index
7577 * FAILURE : -1
7579 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
7581 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7582 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
7583 POINT Origin, Position, opt;
7584 BOOL is_fullrow;
7585 LVITEMW lvItem;
7586 ITERATOR i;
7587 INT iItem;
7589 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
7591 lpht->flags = 0;
7592 lpht->iItem = -1;
7593 if (subitem) lpht->iSubItem = 0;
7595 LISTVIEW_GetOrigin(infoPtr, &Origin);
7597 /* set whole list relation flags */
7598 if (subitem && infoPtr->uView == LV_VIEW_DETAILS)
7600 /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7601 if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x)
7602 lpht->flags |= LVHT_TOLEFT;
7604 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7605 opt.y = lpht->pt.y + infoPtr->rcList.top;
7606 else
7607 opt.y = lpht->pt.y;
7609 if (infoPtr->rcList.bottom < opt.y)
7610 lpht->flags |= LVHT_BELOW;
7612 else
7614 if (infoPtr->rcList.left > lpht->pt.x)
7615 lpht->flags |= LVHT_TOLEFT;
7616 else if (infoPtr->rcList.right < lpht->pt.x)
7617 lpht->flags |= LVHT_TORIGHT;
7619 if (infoPtr->rcList.top > lpht->pt.y)
7620 lpht->flags |= LVHT_ABOVE;
7621 else if (infoPtr->rcList.bottom < lpht->pt.y)
7622 lpht->flags |= LVHT_BELOW;
7625 /* even if item is invalid try to find subitem */
7626 if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
7628 RECT *pRect;
7629 INT j;
7631 opt.x = lpht->pt.x - Origin.x;
7633 lpht->iSubItem = -1;
7634 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
7636 pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
7638 if ((opt.x >= pRect->left) && (opt.x < pRect->right))
7640 lpht->iSubItem = j;
7641 break;
7644 TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
7646 /* if we're outside horizontal columns bounds there's nothing to test further */
7647 if (lpht->iSubItem == -1)
7649 lpht->iItem = -1;
7650 lpht->flags = LVHT_NOWHERE;
7651 return -1;
7655 TRACE("lpht->flags=0x%x\n", lpht->flags);
7656 if (lpht->flags) return -1;
7658 lpht->flags |= LVHT_NOWHERE;
7660 /* first deal with the large items */
7661 rcSearch.left = lpht->pt.x;
7662 rcSearch.top = lpht->pt.y;
7663 rcSearch.right = rcSearch.left + 1;
7664 rcSearch.bottom = rcSearch.top + 1;
7666 iterator_frameditems(&i, infoPtr, &rcSearch);
7667 iterator_next(&i); /* go to first item in the sequence */
7668 iItem = i.nItem;
7669 iterator_destroy(&i);
7671 TRACE("lpht->iItem=%d\n", iItem);
7672 if (iItem == -1) return -1;
7674 lvItem.mask = LVIF_STATE | LVIF_TEXT;
7675 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7676 lvItem.stateMask = LVIS_STATEIMAGEMASK;
7677 if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
7678 lvItem.iItem = iItem;
7679 lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
7680 lvItem.pszText = szDispText;
7681 lvItem.cchTextMax = DISP_TEXT_SIZE;
7682 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
7683 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
7685 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7686 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
7687 opt.x = lpht->pt.x - Position.x - Origin.x;
7689 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7690 opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top;
7691 else
7692 opt.y = lpht->pt.y - Position.y - Origin.y;
7694 if (infoPtr->uView == LV_VIEW_DETAILS)
7696 rcBounds = rcBox;
7697 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
7698 opt.x = lpht->pt.x - Origin.x;
7700 else
7702 UnionRect(&rcBounds, &rcIcon, &rcLabel);
7703 UnionRect(&rcBounds, &rcBounds, &rcState);
7705 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
7706 if (!PtInRect(&rcBounds, opt)) return -1;
7708 /* That's a special case - row rectangle is used as item rectangle and
7709 returned flags contain all item parts. */
7710 is_fullrow = (infoPtr->uView == LV_VIEW_DETAILS) && ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || (infoPtr->dwStyle & LVS_OWNERDRAWFIXED));
7712 if (PtInRect(&rcIcon, opt))
7713 lpht->flags |= LVHT_ONITEMICON;
7714 else if (PtInRect(&rcLabel, opt))
7715 lpht->flags |= LVHT_ONITEMLABEL;
7716 else if (infoPtr->himlState && PtInRect(&rcState, opt))
7717 lpht->flags |= LVHT_ONITEMSTATEICON;
7718 if (is_fullrow && !(lpht->flags & LVHT_ONITEM))
7720 lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
7722 if (lpht->flags & LVHT_ONITEM)
7723 lpht->flags &= ~LVHT_NOWHERE;
7724 TRACE("lpht->flags=0x%x\n", lpht->flags);
7726 if (select && !is_fullrow)
7728 if (infoPtr->uView == LV_VIEW_DETAILS)
7730 /* get main item bounds */
7731 lvItem.iSubItem = 0;
7732 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7733 UnionRect(&rcBounds, &rcIcon, &rcLabel);
7734 UnionRect(&rcBounds, &rcBounds, &rcState);
7736 if (!PtInRect(&rcBounds, opt)) iItem = -1;
7738 return lpht->iItem = iItem;
7741 /***
7742 * DESCRIPTION:
7743 * Inserts a new item in the listview control.
7745 * PARAMETER(S):
7746 * [I] infoPtr : valid pointer to the listview structure
7747 * [I] lpLVItem : item information
7748 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7750 * RETURN:
7751 * SUCCESS : new item index
7752 * FAILURE : -1
7754 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
7756 INT nItem;
7757 HDPA hdpaSubItems;
7758 NMLISTVIEW nmlv;
7759 ITEM_INFO *lpItem;
7760 ITEM_ID *lpID;
7761 BOOL is_sorted, has_changed;
7762 LVITEMW item;
7763 HWND hwndSelf = infoPtr->hwndSelf;
7765 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
7767 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
7769 /* make sure it's an item, and not a subitem; cannot insert a subitem */
7770 if (!lpLVItem || lpLVItem->iSubItem) return -1;
7772 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
7774 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
7776 /* insert item in listview control data structure */
7777 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
7778 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
7780 /* link with id struct */
7781 if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail;
7782 lpItem->id = lpID;
7783 lpID->item = hdpaSubItems;
7784 lpID->id = get_next_itemid(infoPtr);
7785 if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail;
7787 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
7788 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
7790 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
7792 /* calculate new item index */
7793 if (is_sorted)
7795 HDPA hItem;
7796 ITEM_INFO *item_s;
7797 INT i = 0, cmpv;
7799 while (i < infoPtr->nItemCount)
7801 hItem = DPA_GetPtr( infoPtr->hdpaItems, i);
7802 item_s = DPA_GetPtr(hItem, 0);
7804 cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, isW);
7805 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
7807 if (cmpv >= 0) break;
7808 i++;
7810 nItem = i;
7812 else
7813 nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
7815 TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
7816 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
7817 if (nItem == -1) goto fail;
7818 infoPtr->nItemCount++;
7820 /* shift indices first so they don't get tangled */
7821 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
7823 /* set the item attributes */
7824 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
7826 /* full size structure expected - _WIN32IE >= 0x560 */
7827 item = *lpLVItem;
7829 else if (lpLVItem->mask & LVIF_INDENT)
7831 /* indent member expected - _WIN32IE >= 0x300 */
7832 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
7834 else
7836 /* minimal structure expected */
7837 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
7839 item.iItem = nItem;
7840 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7842 item.mask |= LVIF_STATE;
7843 item.stateMask |= LVIS_STATEIMAGEMASK;
7844 item.state &= ~LVIS_STATEIMAGEMASK;
7845 item.state |= INDEXTOSTATEIMAGEMASK(1);
7848 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
7850 /* make room for the position, if we are in the right mode */
7851 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7853 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
7854 goto undo;
7855 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
7857 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
7858 goto undo;
7862 /* send LVN_INSERTITEM notification */
7863 memset(&nmlv, 0, sizeof(NMLISTVIEW));
7864 nmlv.iItem = nItem;
7865 nmlv.lParam = lpItem->lParam;
7866 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
7867 if (!IsWindow(hwndSelf))
7868 return -1;
7870 /* align items (set position of each item) */
7871 if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
7873 POINT pt;
7875 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
7876 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
7877 else
7878 LISTVIEW_NextIconPosTop(infoPtr, &pt);
7880 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
7883 /* now is the invalidation fun */
7884 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
7885 return nItem;
7887 undo:
7888 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
7889 LISTVIEW_ShiftFocus(infoPtr, infoPtr->nFocusedItem, nItem, -1);
7890 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
7891 infoPtr->nItemCount--;
7892 fail:
7893 DPA_DeletePtr(hdpaSubItems, 0);
7894 DPA_Destroy (hdpaSubItems);
7895 Free (lpItem);
7896 return -1;
7899 /***
7900 * DESCRIPTION:
7901 * Checks item visibility.
7903 * PARAMETER(S):
7904 * [I] infoPtr : valid pointer to the listview structure
7905 * [I] nFirst : item index to check for
7907 * RETURN:
7908 * Item visible : TRUE
7909 * Item invisible or failure : FALSE
7911 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem)
7913 POINT Origin, Position;
7914 RECT rcItem;
7915 HDC hdc;
7916 BOOL ret;
7918 TRACE("nItem=%d\n", nItem);
7920 if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE;
7922 LISTVIEW_GetOrigin(infoPtr, &Origin);
7923 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7924 rcItem.left = Position.x + Origin.x;
7925 rcItem.top = Position.y + Origin.y;
7926 rcItem.right = rcItem.left + infoPtr->nItemWidth;
7927 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
7929 hdc = GetDC(infoPtr->hwndSelf);
7930 if (!hdc) return FALSE;
7931 ret = RectVisible(hdc, &rcItem);
7932 ReleaseDC(infoPtr->hwndSelf, hdc);
7934 return ret;
7937 /***
7938 * DESCRIPTION:
7939 * Redraws a range of items.
7941 * PARAMETER(S):
7942 * [I] infoPtr : valid pointer to the listview structure
7943 * [I] nFirst : first item
7944 * [I] nLast : last item
7946 * RETURN:
7947 * SUCCESS : TRUE
7948 * FAILURE : FALSE
7950 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
7952 INT i;
7954 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
7955 max(nFirst, nLast) >= infoPtr->nItemCount)
7956 return FALSE;
7958 for (i = nFirst; i <= nLast; i++)
7959 LISTVIEW_InvalidateItem(infoPtr, i);
7961 return TRUE;
7964 /***
7965 * DESCRIPTION:
7966 * Scroll the content of a listview.
7968 * PARAMETER(S):
7969 * [I] infoPtr : valid pointer to the listview structure
7970 * [I] dx : horizontal scroll amount in pixels
7971 * [I] dy : vertical scroll amount in pixels
7973 * RETURN:
7974 * SUCCESS : TRUE
7975 * FAILURE : FALSE
7977 * COMMENTS:
7978 * If the control is in report view (LV_VIEW_DETAILS) the control can
7979 * be scrolled only in line increments. "dy" will be rounded to the
7980 * nearest number of pixels that are a whole line. Ex: if line height
7981 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7982 * is passed, then the scroll will be 0. (per MSDN 7/2002)
7984 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7986 switch(infoPtr->uView) {
7987 case LV_VIEW_DETAILS:
7988 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
7989 dy /= infoPtr->nItemHeight;
7990 break;
7991 case LV_VIEW_LIST:
7992 if (dy != 0) return FALSE;
7993 break;
7994 default: /* icon */
7995 break;
7998 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx);
7999 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy);
8001 return TRUE;
8004 /***
8005 * DESCRIPTION:
8006 * Sets the background color.
8008 * PARAMETER(S):
8009 * [I] infoPtr : valid pointer to the listview structure
8010 * [I] color : background color
8012 * RETURN:
8013 * SUCCESS : TRUE
8014 * FAILURE : FALSE
8016 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF color)
8018 TRACE("(color=%x)\n", color);
8020 if(infoPtr->clrBk != color) {
8021 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8022 infoPtr->clrBk = color;
8023 if (color == CLR_NONE)
8024 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
8025 else
8027 infoPtr->hBkBrush = CreateSolidBrush(color);
8028 infoPtr->dwLvExStyle &= ~LVS_EX_TRANSPARENTBKGND;
8032 return TRUE;
8035 /* LISTVIEW_SetBkImage */
8037 /*** Helper for {Insert,Set}ColumnT *only* */
8038 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
8039 const LVCOLUMNW *lpColumn, BOOL isW)
8041 if (lpColumn->mask & LVCF_FMT)
8043 /* format member is valid */
8044 lphdi->mask |= HDI_FORMAT;
8046 /* set text alignment (leftmost column must be left-aligned) */
8047 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8048 lphdi->fmt |= HDF_LEFT;
8049 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
8050 lphdi->fmt |= HDF_RIGHT;
8051 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
8052 lphdi->fmt |= HDF_CENTER;
8054 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
8055 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
8057 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
8059 lphdi->fmt |= HDF_IMAGE;
8060 lphdi->iImage = I_IMAGECALLBACK;
8063 if (lpColumn->fmt & LVCFMT_FIXED_WIDTH)
8064 lphdi->fmt |= HDF_FIXEDWIDTH;
8067 if (lpColumn->mask & LVCF_WIDTH)
8069 lphdi->mask |= HDI_WIDTH;
8070 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
8072 /* make it fill the remainder of the controls width */
8073 RECT rcHeader;
8074 INT item_index;
8076 for(item_index = 0; item_index < (nColumn - 1); item_index++)
8078 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
8079 lphdi->cxy += rcHeader.right - rcHeader.left;
8082 /* retrieve the layout of the header */
8083 GetClientRect(infoPtr->hwndSelf, &rcHeader);
8084 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
8086 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
8088 else
8089 lphdi->cxy = lpColumn->cx;
8092 if (lpColumn->mask & LVCF_TEXT)
8094 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
8095 lphdi->fmt |= HDF_STRING;
8096 lphdi->pszText = lpColumn->pszText;
8097 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
8100 if (lpColumn->mask & LVCF_IMAGE)
8102 lphdi->mask |= HDI_IMAGE;
8103 lphdi->iImage = lpColumn->iImage;
8106 if (lpColumn->mask & LVCF_ORDER)
8108 lphdi->mask |= HDI_ORDER;
8109 lphdi->iOrder = lpColumn->iOrder;
8114 /***
8115 * DESCRIPTION:
8116 * Inserts a new column.
8118 * PARAMETER(S):
8119 * [I] infoPtr : valid pointer to the listview structure
8120 * [I] nColumn : column index
8121 * [I] lpColumn : column information
8122 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
8124 * RETURN:
8125 * SUCCESS : new column index
8126 * FAILURE : -1
8128 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
8129 const LVCOLUMNW *lpColumn, BOOL isW)
8131 COLUMN_INFO *lpColumnInfo;
8132 INT nNewColumn;
8133 HDITEMW hdi;
8135 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
8137 if (!lpColumn || nColumn < 0) return -1;
8138 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
8140 ZeroMemory(&hdi, sizeof(HDITEMW));
8141 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
8144 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
8145 * (can be seen in SPY) otherwise column never gets added.
8147 if (!(lpColumn->mask & LVCF_WIDTH)) {
8148 hdi.mask |= HDI_WIDTH;
8149 hdi.cxy = 10;
8153 * when the iSubItem is available Windows copies it to the header lParam. It seems
8154 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
8156 if (lpColumn->mask & LVCF_SUBITEM)
8158 hdi.mask |= HDI_LPARAM;
8159 hdi.lParam = lpColumn->iSubItem;
8162 /* create header if not present */
8163 LISTVIEW_CreateHeader(infoPtr);
8164 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
8165 (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
8167 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8170 /* insert item in header control */
8171 nNewColumn = SendMessageW(infoPtr->hwndHeader,
8172 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
8173 nColumn, (LPARAM)&hdi);
8174 if (nNewColumn == -1) return -1;
8175 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
8177 /* create our own column info */
8178 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
8179 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
8181 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
8182 if (lpColumn->mask & LVCF_MINWIDTH) lpColumnInfo->cxMin = lpColumn->cxMin;
8183 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
8184 goto fail;
8186 /* now we have to actually adjust the data */
8187 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
8189 SUBITEM_INFO *lpSubItem;
8190 HDPA hdpaSubItems;
8191 INT nItem, i;
8192 LVITEMW item;
8193 BOOL changed;
8195 item.iSubItem = nNewColumn;
8196 item.mask = LVIF_TEXT | LVIF_IMAGE;
8197 item.iImage = I_IMAGECALLBACK;
8198 item.pszText = LPSTR_TEXTCALLBACKW;
8200 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
8202 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
8203 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
8205 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
8206 if (lpSubItem->iSubItem >= nNewColumn)
8207 lpSubItem->iSubItem++;
8210 /* add new subitem for each item */
8211 item.iItem = nItem;
8212 set_sub_item(infoPtr, &item, isW, &changed);
8216 /* make space for the new column */
8217 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8218 LISTVIEW_UpdateItemSize(infoPtr);
8220 return nNewColumn;
8222 fail:
8223 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
8224 if (lpColumnInfo)
8226 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
8227 Free(lpColumnInfo);
8229 return -1;
8232 /***
8233 * DESCRIPTION:
8234 * Sets the attributes of a header item.
8236 * PARAMETER(S):
8237 * [I] infoPtr : valid pointer to the listview structure
8238 * [I] nColumn : column index
8239 * [I] lpColumn : column attributes
8240 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
8242 * RETURN:
8243 * SUCCESS : TRUE
8244 * FAILURE : FALSE
8246 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
8247 const LVCOLUMNW *lpColumn, BOOL isW)
8249 HDITEMW hdi, hdiget;
8250 BOOL bResult;
8252 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
8254 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
8256 ZeroMemory(&hdi, sizeof(HDITEMW));
8257 if (lpColumn->mask & LVCF_FMT)
8259 hdi.mask |= HDI_FORMAT;
8260 hdiget.mask = HDI_FORMAT;
8261 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
8262 hdi.fmt = hdiget.fmt & HDF_STRING;
8264 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
8266 /* set header item attributes */
8267 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, nColumn, (LPARAM)&hdi);
8268 if (!bResult) return FALSE;
8270 if (lpColumn->mask & LVCF_FMT)
8272 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
8273 INT oldFmt = lpColumnInfo->fmt;
8275 lpColumnInfo->fmt = lpColumn->fmt;
8276 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
8278 if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
8282 if (lpColumn->mask & LVCF_MINWIDTH)
8283 LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin = lpColumn->cxMin;
8285 return TRUE;
8288 /***
8289 * DESCRIPTION:
8290 * Sets the column order array
8292 * PARAMETERS:
8293 * [I] infoPtr : valid pointer to the listview structure
8294 * [I] iCount : number of elements in column order array
8295 * [I] lpiArray : pointer to column order array
8297 * RETURN:
8298 * SUCCESS : TRUE
8299 * FAILURE : FALSE
8301 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
8303 TRACE("iCount %d lpiArray %p\n", iCount, lpiArray);
8305 if (!lpiArray || !IsWindow(infoPtr->hwndHeader)) return FALSE;
8307 infoPtr->colRectsDirty = TRUE;
8309 return SendMessageW(infoPtr->hwndHeader, HDM_SETORDERARRAY, iCount, (LPARAM)lpiArray);
8312 /***
8313 * DESCRIPTION:
8314 * Sets the width of a column
8316 * PARAMETERS:
8317 * [I] infoPtr : valid pointer to the listview structure
8318 * [I] nColumn : column index
8319 * [I] cx : column width
8321 * RETURN:
8322 * SUCCESS : TRUE
8323 * FAILURE : FALSE
8325 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
8327 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
8328 INT max_cx = 0;
8329 HDITEMW hdi;
8331 TRACE("(nColumn=%d, cx=%d)\n", nColumn, cx);
8333 /* set column width only if in report or list mode */
8334 if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
8336 /* take care of invalid cx values */
8337 if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE;
8338 else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE;
8340 /* resize all columns if in LV_VIEW_LIST mode */
8341 if(infoPtr->uView == LV_VIEW_LIST)
8343 infoPtr->nItemWidth = cx;
8344 LISTVIEW_InvalidateList(infoPtr);
8345 return TRUE;
8348 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
8350 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
8352 INT nLabelWidth;
8353 LVITEMW lvItem;
8355 lvItem.mask = LVIF_TEXT;
8356 lvItem.iItem = 0;
8357 lvItem.iSubItem = nColumn;
8358 lvItem.cchTextMax = DISP_TEXT_SIZE;
8359 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8361 lvItem.pszText = szDispText;
8362 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
8363 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
8364 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
8366 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
8367 max_cx += infoPtr->iconSize.cx;
8368 max_cx += TRAILING_LABEL_PADDING;
8371 /* autosize based on listview items width */
8372 if(cx == LVSCW_AUTOSIZE)
8373 cx = max_cx;
8374 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
8376 /* if iCol is the last column make it fill the remainder of the controls width */
8377 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
8379 RECT rcHeader;
8380 POINT Origin;
8382 LISTVIEW_GetOrigin(infoPtr, &Origin);
8383 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
8385 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
8387 else
8389 /* Despite what the MS docs say, if this is not the last
8390 column, then MS resizes the column to the width of the
8391 largest text string in the column, including headers
8392 and items. This is different from LVSCW_AUTOSIZE in that
8393 LVSCW_AUTOSIZE ignores the header string length. */
8394 cx = 0;
8396 /* retrieve header text */
8397 hdi.mask = HDI_TEXT;
8398 hdi.cchTextMax = DISP_TEXT_SIZE;
8399 hdi.pszText = szDispText;
8400 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
8402 HDC hdc = GetDC(infoPtr->hwndSelf);
8403 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
8404 SIZE size;
8406 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
8407 cx = size.cx + TRAILING_HEADER_PADDING;
8408 /* FIXME: Take into account the header image, if one is present */
8409 SelectObject(hdc, old_font);
8410 ReleaseDC(infoPtr->hwndSelf, hdc);
8412 cx = max (cx, max_cx);
8416 if (cx < 0) return FALSE;
8418 /* call header to update the column change */
8419 hdi.mask = HDI_WIDTH;
8420 hdi.cxy = max(cx, LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin);
8421 TRACE("hdi.cxy=%d\n", hdi.cxy);
8422 return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
8425 /***
8426 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
8429 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
8431 HDC hdc_wnd, hdc;
8432 HBITMAP hbm_im, hbm_mask, hbm_orig;
8433 RECT rc;
8434 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
8435 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
8436 HIMAGELIST himl;
8438 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
8439 ILC_COLOR | ILC_MASK, 2, 2);
8440 hdc_wnd = GetDC(infoPtr->hwndSelf);
8441 hdc = CreateCompatibleDC(hdc_wnd);
8442 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
8443 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
8444 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
8446 rc.left = rc.top = 0;
8447 rc.right = GetSystemMetrics(SM_CXSMICON);
8448 rc.bottom = GetSystemMetrics(SM_CYSMICON);
8450 hbm_orig = SelectObject(hdc, hbm_mask);
8451 FillRect(hdc, &rc, hbr_white);
8452 InflateRect(&rc, -2, -2);
8453 FillRect(hdc, &rc, hbr_black);
8455 SelectObject(hdc, hbm_im);
8456 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
8457 SelectObject(hdc, hbm_orig);
8458 ImageList_Add(himl, hbm_im, hbm_mask);
8460 SelectObject(hdc, hbm_im);
8461 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
8462 SelectObject(hdc, hbm_orig);
8463 ImageList_Add(himl, hbm_im, hbm_mask);
8465 DeleteObject(hbm_mask);
8466 DeleteObject(hbm_im);
8467 DeleteDC(hdc);
8469 return himl;
8472 /***
8473 * DESCRIPTION:
8474 * Sets the extended listview style.
8476 * PARAMETERS:
8477 * [I] infoPtr : valid pointer to the listview structure
8478 * [I] dwMask : mask
8479 * [I] dwStyle : style
8481 * RETURN:
8482 * SUCCESS : previous style
8483 * FAILURE : 0
8485 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD mask, DWORD ex_style)
8487 DWORD old_ex_style = infoPtr->dwLvExStyle;
8489 TRACE("mask=0x%08x, ex_style=0x%08x\n", mask, ex_style);
8491 /* set new style */
8492 if (mask)
8493 infoPtr->dwLvExStyle = (old_ex_style & ~mask) | (ex_style & mask);
8494 else
8495 infoPtr->dwLvExStyle = ex_style;
8497 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_CHECKBOXES)
8499 HIMAGELIST himl = 0;
8500 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8502 LVITEMW item;
8503 item.mask = LVIF_STATE;
8504 item.stateMask = LVIS_STATEIMAGEMASK;
8505 item.state = INDEXTOSTATEIMAGEMASK(1);
8506 LISTVIEW_SetItemState(infoPtr, -1, &item);
8508 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
8509 if(!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8510 ImageList_Destroy(infoPtr->himlState);
8512 himl = LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
8513 /* checkbox list replaces previous custom list or... */
8514 if(((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) &&
8515 !(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) ||
8516 /* ...previous was checkbox list */
8517 (old_ex_style & LVS_EX_CHECKBOXES))
8518 ImageList_Destroy(himl);
8521 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERDRAGDROP)
8523 DWORD style;
8525 /* if not already created */
8526 LISTVIEW_CreateHeader(infoPtr);
8528 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
8529 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
8530 style |= HDS_DRAGDROP;
8531 else
8532 style &= ~HDS_DRAGDROP;
8533 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style);
8536 /* GRIDLINES adds decoration at top so changes sizes */
8537 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_GRIDLINES)
8539 LISTVIEW_CreateHeader(infoPtr);
8540 LISTVIEW_UpdateSize(infoPtr);
8543 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_FULLROWSELECT)
8545 LISTVIEW_CreateHeader(infoPtr);
8548 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_TRANSPARENTBKGND)
8550 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
8551 LISTVIEW_SetBkColor(infoPtr, CLR_NONE);
8554 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERINALLVIEWS)
8556 if (infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
8557 LISTVIEW_CreateHeader(infoPtr);
8558 else
8559 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8560 LISTVIEW_UpdateSize(infoPtr);
8561 LISTVIEW_UpdateScroll(infoPtr);
8564 LISTVIEW_InvalidateList(infoPtr);
8565 return old_ex_style;
8568 /***
8569 * DESCRIPTION:
8570 * Sets the new hot cursor used during hot tracking and hover selection.
8572 * PARAMETER(S):
8573 * [I] infoPtr : valid pointer to the listview structure
8574 * [I] hCursor : the new hot cursor handle
8576 * RETURN:
8577 * Returns the previous hot cursor
8579 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
8581 HCURSOR oldCursor = infoPtr->hHotCursor;
8583 infoPtr->hHotCursor = hCursor;
8585 return oldCursor;
8589 /***
8590 * DESCRIPTION:
8591 * Sets the hot item index.
8593 * PARAMETERS:
8594 * [I] infoPtr : valid pointer to the listview structure
8595 * [I] iIndex : index
8597 * RETURN:
8598 * SUCCESS : previous hot item index
8599 * FAILURE : -1 (no hot item)
8601 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
8603 INT iOldIndex = infoPtr->nHotItem;
8605 infoPtr->nHotItem = iIndex;
8607 return iOldIndex;
8611 /***
8612 * DESCRIPTION:
8613 * Sets the amount of time the cursor must hover over an item before it is selected.
8615 * PARAMETER(S):
8616 * [I] infoPtr : valid pointer to the listview structure
8617 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8619 * RETURN:
8620 * Returns the previous hover time
8622 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
8624 DWORD oldHoverTime = infoPtr->dwHoverTime;
8626 infoPtr->dwHoverTime = dwHoverTime;
8628 return oldHoverTime;
8631 /***
8632 * DESCRIPTION:
8633 * Sets spacing for icons of LVS_ICON style.
8635 * PARAMETER(S):
8636 * [I] infoPtr : valid pointer to the listview structure
8637 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8638 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8640 * RETURN:
8641 * MAKELONG(oldcx, oldcy)
8643 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
8645 INT iconWidth = 0, iconHeight = 0;
8646 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
8648 TRACE("requested=(%d,%d)\n", cx, cy);
8650 /* set to defaults, if instructed to */
8651 if (cx == -1 && cy == -1)
8653 infoPtr->autoSpacing = TRUE;
8654 if (infoPtr->himlNormal)
8655 ImageList_GetIconSize(infoPtr->himlNormal, &iconWidth, &iconHeight);
8656 cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON) + iconWidth;
8657 cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON) + iconHeight;
8659 else
8660 infoPtr->autoSpacing = FALSE;
8662 /* if 0 then keep width */
8663 if (cx != 0)
8664 infoPtr->iconSpacing.cx = cx;
8666 /* if 0 then keep height */
8667 if (cy != 0)
8668 infoPtr->iconSpacing.cy = cy;
8670 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8671 LOWORD(oldspacing), HIWORD(oldspacing), infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
8672 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
8673 infoPtr->ntmHeight);
8675 /* these depend on the iconSpacing */
8676 LISTVIEW_UpdateItemSize(infoPtr);
8678 return oldspacing;
8681 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
8683 INT cx, cy;
8685 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
8687 size->cx = cx;
8688 size->cy = cy;
8690 else
8692 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
8693 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
8697 /***
8698 * DESCRIPTION:
8699 * Sets image lists.
8701 * PARAMETER(S):
8702 * [I] infoPtr : valid pointer to the listview structure
8703 * [I] nType : image list type
8704 * [I] himl : image list handle
8706 * RETURN:
8707 * SUCCESS : old image list
8708 * FAILURE : NULL
8710 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
8712 INT oldHeight = infoPtr->nItemHeight;
8713 HIMAGELIST himlOld = 0;
8715 TRACE("(nType=%d, himl=%p)\n", nType, himl);
8717 switch (nType)
8719 case LVSIL_NORMAL:
8720 himlOld = infoPtr->himlNormal;
8721 infoPtr->himlNormal = himl;
8722 if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
8723 if (infoPtr->autoSpacing)
8724 LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
8725 break;
8727 case LVSIL_SMALL:
8728 himlOld = infoPtr->himlSmall;
8729 infoPtr->himlSmall = himl;
8730 if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
8731 if (infoPtr->hwndHeader)
8732 SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)himl);
8733 break;
8735 case LVSIL_STATE:
8736 himlOld = infoPtr->himlState;
8737 infoPtr->himlState = himl;
8738 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
8739 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
8740 break;
8742 default:
8743 ERR("Unknown icon type=%d\n", nType);
8744 return NULL;
8747 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
8748 if (infoPtr->nItemHeight != oldHeight)
8749 LISTVIEW_UpdateScroll(infoPtr);
8751 return himlOld;
8754 /***
8755 * DESCRIPTION:
8756 * Preallocates memory (does *not* set the actual count of items !)
8758 * PARAMETER(S):
8759 * [I] infoPtr : valid pointer to the listview structure
8760 * [I] nItems : item count (projected number of items to allocate)
8761 * [I] dwFlags : update flags
8763 * RETURN:
8764 * SUCCESS : TRUE
8765 * FAILURE : FALSE
8767 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
8769 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
8771 if (infoPtr->dwStyle & LVS_OWNERDATA)
8773 INT nOldCount = infoPtr->nItemCount;
8775 if (nItems < nOldCount)
8777 RANGE range = { nItems, nOldCount };
8778 ranges_del(infoPtr->selectionRanges, range);
8779 if (infoPtr->nFocusedItem >= nItems)
8781 LISTVIEW_SetItemFocus(infoPtr, -1);
8782 SetRectEmpty(&infoPtr->rcFocus);
8786 infoPtr->nItemCount = nItems;
8787 LISTVIEW_UpdateScroll(infoPtr);
8789 /* the flags are valid only in ownerdata report and list modes */
8790 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
8792 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
8793 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
8795 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
8796 LISTVIEW_InvalidateList(infoPtr);
8797 else
8799 INT nFrom, nTo;
8800 POINT Origin;
8801 RECT rcErase;
8803 LISTVIEW_GetOrigin(infoPtr, &Origin);
8804 nFrom = min(nOldCount, nItems);
8805 nTo = max(nOldCount, nItems);
8807 if (infoPtr->uView == LV_VIEW_DETAILS)
8809 rcErase.left = 0;
8810 rcErase.top = nFrom * infoPtr->nItemHeight;
8811 rcErase.right = infoPtr->nItemWidth;
8812 rcErase.bottom = nTo * infoPtr->nItemHeight;
8813 OffsetRect(&rcErase, Origin.x, Origin.y);
8814 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8815 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8817 else /* LV_VIEW_LIST */
8819 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
8821 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
8822 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
8823 rcErase.right = rcErase.left + infoPtr->nItemWidth;
8824 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8825 OffsetRect(&rcErase, Origin.x, Origin.y);
8826 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8827 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8829 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
8830 rcErase.top = 0;
8831 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
8832 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8833 OffsetRect(&rcErase, Origin.x, Origin.y);
8834 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8835 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8839 else
8841 /* According to MSDN for non-LVS_OWNERDATA this is just
8842 * a performance issue. The control allocates its internal
8843 * data structures for the number of items specified. It
8844 * cuts down on the number of memory allocations. Therefore
8845 * we will just issue a WARN here
8847 WARN("for non-ownerdata performance option not implemented.\n");
8850 return TRUE;
8853 /***
8854 * DESCRIPTION:
8855 * Sets the position of an item.
8857 * PARAMETER(S):
8858 * [I] infoPtr : valid pointer to the listview structure
8859 * [I] nItem : item index
8860 * [I] pt : coordinate
8862 * RETURN:
8863 * SUCCESS : TRUE
8864 * FAILURE : FALSE
8866 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *pt)
8868 POINT Origin, Pt;
8870 TRACE("(nItem=%d, pt=%s)\n", nItem, wine_dbgstr_point(pt));
8872 if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount ||
8873 !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
8875 Pt = *pt;
8876 LISTVIEW_GetOrigin(infoPtr, &Origin);
8878 /* This point value seems to be an undocumented feature.
8879 * The best guess is that it means either at the origin,
8880 * or at true beginning of the list. I will assume the origin. */
8881 if ((Pt.x == -1) && (Pt.y == -1))
8882 Pt = Origin;
8884 if (infoPtr->uView == LV_VIEW_ICON)
8886 Pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
8887 Pt.y -= ICON_TOP_PADDING;
8889 Pt.x -= Origin.x;
8890 Pt.y -= Origin.y;
8892 infoPtr->bAutoarrange = FALSE;
8894 return LISTVIEW_MoveIconTo(infoPtr, nItem, &Pt, FALSE);
8897 /***
8898 * DESCRIPTION:
8899 * Sets the state of one or many items.
8901 * PARAMETER(S):
8902 * [I] infoPtr : valid pointer to the listview structure
8903 * [I] nItem : item index
8904 * [I] item : item or subitem info
8906 * RETURN:
8907 * SUCCESS : TRUE
8908 * FAILURE : FALSE
8910 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *item)
8912 BOOL ret = TRUE;
8913 LVITEMW lvItem;
8915 if (!item) return FALSE;
8917 lvItem.iItem = nItem;
8918 lvItem.iSubItem = 0;
8919 lvItem.mask = LVIF_STATE;
8920 lvItem.state = item->state;
8921 lvItem.stateMask = item->stateMask;
8922 TRACE("item=%s\n", debuglvitem_t(&lvItem, TRUE));
8924 if (nItem == -1)
8926 UINT oldstate = 0;
8927 BOOL notify;
8929 /* special case optimization for recurring attempt to deselect all */
8930 if (lvItem.state == 0 && lvItem.stateMask == LVIS_SELECTED && !LISTVIEW_GetSelectedCount(infoPtr))
8931 return TRUE;
8933 /* select all isn't allowed in LVS_SINGLESEL */
8934 if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
8935 return FALSE;
8937 /* focus all isn't allowed */
8938 if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
8940 notify = infoPtr->bDoChangeNotify;
8941 if (infoPtr->dwStyle & LVS_OWNERDATA)
8943 infoPtr->bDoChangeNotify = FALSE;
8944 if (!(lvItem.state & LVIS_SELECTED) && LISTVIEW_GetSelectedCount(infoPtr))
8945 oldstate |= LVIS_SELECTED;
8946 if (infoPtr->nFocusedItem != -1) oldstate |= LVIS_FOCUSED;
8949 /* apply to all items */
8950 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8951 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) ret = FALSE;
8953 if (infoPtr->dwStyle & LVS_OWNERDATA)
8955 NMLISTVIEW nmlv;
8957 infoPtr->bDoChangeNotify = notify;
8959 nmlv.iItem = -1;
8960 nmlv.iSubItem = 0;
8961 nmlv.uNewState = lvItem.state & lvItem.stateMask;
8962 nmlv.uOldState = oldstate & lvItem.stateMask;
8963 nmlv.uChanged = LVIF_STATE;
8964 nmlv.ptAction.x = nmlv.ptAction.y = 0;
8965 nmlv.lParam = 0;
8967 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
8970 else
8971 ret = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
8973 return ret;
8976 /***
8977 * DESCRIPTION:
8978 * Sets the text of an item or subitem.
8980 * PARAMETER(S):
8981 * [I] hwnd : window handle
8982 * [I] nItem : item index
8983 * [I] lpLVItem : item or subitem info
8984 * [I] isW : TRUE if input is Unicode
8986 * RETURN:
8987 * SUCCESS : TRUE
8988 * FAILURE : FALSE
8990 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
8992 LVITEMW lvItem;
8994 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8995 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
8997 lvItem.iItem = nItem;
8998 lvItem.iSubItem = lpLVItem->iSubItem;
8999 lvItem.mask = LVIF_TEXT;
9000 lvItem.pszText = lpLVItem->pszText;
9001 lvItem.cchTextMax = lpLVItem->cchTextMax;
9003 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
9005 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
9008 /***
9009 * DESCRIPTION:
9010 * Set item index that marks the start of a multiple selection.
9012 * PARAMETER(S):
9013 * [I] infoPtr : valid pointer to the listview structure
9014 * [I] nIndex : index
9016 * RETURN:
9017 * Index number or -1 if there is no selection mark.
9019 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
9021 INT nOldIndex = infoPtr->nSelectionMark;
9023 TRACE("(nIndex=%d)\n", nIndex);
9025 infoPtr->nSelectionMark = nIndex;
9027 return nOldIndex;
9030 /***
9031 * DESCRIPTION:
9032 * Sets the text background color.
9034 * PARAMETER(S):
9035 * [I] infoPtr : valid pointer to the listview structure
9036 * [I] color : text background color
9038 * RETURN:
9039 * SUCCESS : TRUE
9040 * FAILURE : FALSE
9042 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF color)
9044 TRACE("(color=%x)\n", color);
9046 infoPtr->clrTextBk = color;
9047 return TRUE;
9050 /***
9051 * DESCRIPTION:
9052 * Sets the text foreground color.
9054 * PARAMETER(S):
9055 * [I] infoPtr : valid pointer to the listview structure
9056 * [I] color : text color
9058 * RETURN:
9059 * SUCCESS : TRUE
9060 * FAILURE : FALSE
9062 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF color)
9064 TRACE("(color=%x)\n", color);
9066 infoPtr->clrText = color;
9067 return TRUE;
9070 /***
9071 * DESCRIPTION:
9072 * Sets new ToolTip window to ListView control.
9074 * PARAMETER(S):
9075 * [I] infoPtr : valid pointer to the listview structure
9076 * [I] hwndNewToolTip : handle to new ToolTip
9078 * RETURN:
9079 * old tool tip
9081 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
9083 HWND hwndOldToolTip = infoPtr->hwndToolTip;
9084 infoPtr->hwndToolTip = hwndNewToolTip;
9085 return hwndOldToolTip;
9089 * DESCRIPTION:
9090 * sets the Unicode character format flag for the control
9091 * PARAMETER(S):
9092 * [I] infoPtr :valid pointer to the listview structure
9093 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
9095 * RETURN:
9096 * Old Unicode Format
9098 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL unicode)
9100 SHORT rc = infoPtr->notifyFormat;
9101 infoPtr->notifyFormat = (unicode) ? NFR_UNICODE : NFR_ANSI;
9102 return rc == NFR_UNICODE;
9106 * DESCRIPTION:
9107 * sets the control view mode
9108 * PARAMETER(S):
9109 * [I] infoPtr :valid pointer to the listview structure
9110 * [I] nView :new view mode value
9112 * RETURN:
9113 * SUCCESS: 1
9114 * FAILURE: -1
9116 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
9118 HIMAGELIST himl;
9120 if (infoPtr->uView == nView) return 1;
9122 if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
9123 if (nView == LV_VIEW_TILE)
9125 FIXME("View LV_VIEW_TILE unimplemented\n");
9126 return -1;
9129 infoPtr->uView = nView;
9131 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9132 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9134 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9135 SetRectEmpty(&infoPtr->rcFocus);
9137 himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9138 set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
9140 switch (nView)
9142 case LV_VIEW_ICON:
9143 case LV_VIEW_SMALLICON:
9144 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9145 break;
9146 case LV_VIEW_DETAILS:
9148 HDLAYOUT hl;
9149 WINDOWPOS wp;
9151 LISTVIEW_CreateHeader( infoPtr );
9153 hl.prc = &infoPtr->rcList;
9154 hl.pwpos = &wp;
9155 SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
9156 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9157 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9158 break;
9160 case LV_VIEW_LIST:
9161 break;
9164 LISTVIEW_UpdateItemSize(infoPtr);
9165 LISTVIEW_UpdateSize(infoPtr);
9166 LISTVIEW_UpdateScroll(infoPtr);
9167 LISTVIEW_InvalidateList(infoPtr);
9169 TRACE("nView=%d\n", nView);
9171 return 1;
9174 /* LISTVIEW_SetWorkAreas */
9176 /***
9177 * DESCRIPTION:
9178 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
9180 * PARAMETER(S):
9181 * [I] first : pointer to first ITEM_INFO to compare
9182 * [I] second : pointer to second ITEM_INFO to compare
9183 * [I] lParam : HWND of control
9185 * RETURN:
9186 * if first comes before second : negative
9187 * if first comes after second : positive
9188 * if first and second are equivalent : zero
9190 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
9192 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
9193 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
9194 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
9196 /* Forward the call to the client defined callback */
9197 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
9200 /***
9201 * DESCRIPTION:
9202 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
9204 * PARAMETER(S):
9205 * [I] first : pointer to first ITEM_INFO to compare
9206 * [I] second : pointer to second ITEM_INFO to compare
9207 * [I] lParam : HWND of control
9209 * RETURN:
9210 * if first comes before second : negative
9211 * if first comes after second : positive
9212 * if first and second are equivalent : zero
9214 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
9216 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
9217 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
9218 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
9220 /* Forward the call to the client defined callback */
9221 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
9224 /***
9225 * DESCRIPTION:
9226 * Sorts the listview items.
9228 * PARAMETER(S):
9229 * [I] infoPtr : valid pointer to the listview structure
9230 * [I] pfnCompare : application-defined value
9231 * [I] lParamSort : pointer to comparison callback
9232 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
9234 * RETURN:
9235 * SUCCESS : TRUE
9236 * FAILURE : FALSE
9238 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
9239 LPARAM lParamSort, BOOL IsEx)
9241 HDPA hdpaSubItems;
9242 ITEM_INFO *lpItem;
9243 LPVOID selectionMarkItem = NULL;
9244 LPVOID focusedItem = NULL;
9245 int i;
9247 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
9249 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
9251 if (!pfnCompare) return FALSE;
9252 if (!infoPtr->hdpaItems) return FALSE;
9254 /* if there are 0 or 1 items, there is no need to sort */
9255 if (infoPtr->nItemCount < 2) return TRUE;
9257 /* clear selection */
9258 ranges_clear(infoPtr->selectionRanges);
9260 /* save selection mark and focused item */
9261 if (infoPtr->nSelectionMark >= 0)
9262 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
9263 if (infoPtr->nFocusedItem >= 0)
9264 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
9266 infoPtr->pfnCompare = pfnCompare;
9267 infoPtr->lParamSort = lParamSort;
9268 if (IsEx)
9269 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
9270 else
9271 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
9273 /* restore selection ranges */
9274 for (i=0; i < infoPtr->nItemCount; i++)
9276 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
9277 lpItem = DPA_GetPtr(hdpaSubItems, 0);
9279 if (lpItem->state & LVIS_SELECTED)
9280 ranges_additem(infoPtr->selectionRanges, i);
9282 /* restore selection mark and focused item */
9283 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
9284 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
9286 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
9288 /* refresh the display */
9289 LISTVIEW_InvalidateList(infoPtr);
9290 return TRUE;
9293 /***
9294 * DESCRIPTION:
9295 * Update theme handle after a theme change.
9297 * PARAMETER(S):
9298 * [I] infoPtr : valid pointer to the listview structure
9300 * RETURN:
9301 * SUCCESS : 0
9302 * FAILURE : something else
9304 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
9306 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
9307 CloseThemeData(theme);
9308 OpenThemeData(infoPtr->hwndSelf, themeClass);
9309 return 0;
9312 /***
9313 * DESCRIPTION:
9314 * Updates an items or rearranges the listview control.
9316 * PARAMETER(S):
9317 * [I] infoPtr : valid pointer to the listview structure
9318 * [I] nItem : item index
9320 * RETURN:
9321 * SUCCESS : TRUE
9322 * FAILURE : FALSE
9324 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
9326 TRACE("(nItem=%d)\n", nItem);
9328 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
9330 /* rearrange with default alignment style */
9331 if (is_autoarrange(infoPtr))
9332 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9333 else
9334 LISTVIEW_InvalidateItem(infoPtr, nItem);
9336 return TRUE;
9339 /***
9340 * DESCRIPTION:
9341 * Draw the track line at the place defined in the infoPtr structure.
9342 * The line is drawn with a XOR pen so drawing the line for the second time
9343 * in the same place erases the line.
9345 * PARAMETER(S):
9346 * [I] infoPtr : valid pointer to the listview structure
9348 * RETURN:
9349 * SUCCESS : TRUE
9350 * FAILURE : FALSE
9352 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
9354 HDC hdc;
9356 if (infoPtr->xTrackLine == -1)
9357 return FALSE;
9359 if (!(hdc = GetDC(infoPtr->hwndSelf)))
9360 return FALSE;
9361 PatBlt( hdc, infoPtr->xTrackLine, infoPtr->rcList.top,
9362 1, infoPtr->rcList.bottom - infoPtr->rcList.top, DSTINVERT );
9363 ReleaseDC(infoPtr->hwndSelf, hdc);
9364 return TRUE;
9367 /***
9368 * DESCRIPTION:
9369 * Called when an edit control should be displayed. This function is called after
9370 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
9372 * PARAMETER(S):
9373 * [I] hwnd : Handle to the listview
9374 * [I] uMsg : WM_TIMER (ignored)
9375 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
9376 * [I] dwTimer : The elapsed time (ignored)
9378 * RETURN:
9379 * None.
9381 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
9383 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
9384 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9386 KillTimer(hwnd, idEvent);
9387 editItem->fEnabled = FALSE;
9388 /* check if the item is still selected */
9389 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
9390 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
9393 /***
9394 * DESCRIPTION:
9395 * Creates the listview control - the WM_NCCREATE phase.
9397 * PARAMETER(S):
9398 * [I] hwnd : window handle
9399 * [I] lpcs : the create parameters
9401 * RETURN:
9402 * Success: TRUE
9403 * Failure: FALSE
9405 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
9407 LISTVIEW_INFO *infoPtr;
9408 LOGFONTW logFont;
9410 TRACE("(lpcs=%p)\n", lpcs);
9412 /* initialize info pointer */
9413 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
9414 if (!infoPtr) return FALSE;
9416 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
9418 infoPtr->hwndSelf = hwnd;
9419 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
9420 map_style_view(infoPtr);
9421 /* determine the type of structures to use */
9422 infoPtr->hwndNotify = lpcs->hwndParent;
9423 /* infoPtr->notifyFormat will be filled in WM_CREATE */
9425 /* initialize color information */
9426 infoPtr->clrBk = CLR_NONE;
9427 infoPtr->clrText = CLR_DEFAULT;
9428 infoPtr->clrTextBk = CLR_DEFAULT;
9429 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
9431 /* set default values */
9432 infoPtr->nFocusedItem = -1;
9433 infoPtr->nSelectionMark = -1;
9434 infoPtr->nHotItem = -1;
9435 infoPtr->bRedraw = TRUE;
9436 infoPtr->bNoItemMetrics = TRUE;
9437 infoPtr->bDoChangeNotify = TRUE;
9438 infoPtr->autoSpacing = TRUE;
9439 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON);
9440 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
9441 infoPtr->nEditLabelItem = -1;
9442 infoPtr->nLButtonDownItem = -1;
9443 infoPtr->dwHoverTime = HOVER_DEFAULT; /* default system hover time */
9444 infoPtr->cWheelRemainder = 0;
9445 infoPtr->nMeasureItemHeight = 0;
9446 infoPtr->xTrackLine = -1; /* no track line */
9447 infoPtr->itemEdit.fEnabled = FALSE;
9448 infoPtr->iVersion = COMCTL32_VERSION;
9449 infoPtr->colRectsDirty = FALSE;
9451 /* get default font (icon title) */
9452 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
9453 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
9454 infoPtr->hFont = infoPtr->hDefaultFont;
9455 LISTVIEW_SaveTextMetrics(infoPtr);
9457 /* allocate memory for the data structure */
9458 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
9459 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
9460 if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail;
9461 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
9462 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
9463 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
9464 return TRUE;
9466 fail:
9467 DestroyWindow(infoPtr->hwndHeader);
9468 ranges_destroy(infoPtr->selectionRanges);
9469 DPA_Destroy(infoPtr->hdpaItems);
9470 DPA_Destroy(infoPtr->hdpaItemIds);
9471 DPA_Destroy(infoPtr->hdpaPosX);
9472 DPA_Destroy(infoPtr->hdpaPosY);
9473 DPA_Destroy(infoPtr->hdpaColumns);
9474 Free(infoPtr);
9475 return FALSE;
9478 /***
9479 * DESCRIPTION:
9480 * Creates the listview control - the WM_CREATE phase. Most of the data is
9481 * already set up in LISTVIEW_NCCreate
9483 * PARAMETER(S):
9484 * [I] hwnd : window handle
9485 * [I] lpcs : the create parameters
9487 * RETURN:
9488 * Success: 0
9489 * Failure: -1
9491 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
9493 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9495 TRACE("(lpcs=%p, style=0x%08x)\n", lpcs, lpcs->style);
9497 infoPtr->dwStyle = lpcs->style;
9498 map_style_view(infoPtr);
9500 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
9501 (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9502 /* on error defaulting to ANSI notifications */
9503 if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
9504 TRACE("notify format=%d\n", infoPtr->notifyFormat);
9506 if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
9508 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
9510 else
9511 infoPtr->hwndHeader = 0;
9513 /* init item size to avoid division by 0 */
9514 LISTVIEW_UpdateItemSize (infoPtr);
9515 LISTVIEW_UpdateSize (infoPtr);
9517 if (infoPtr->uView == LV_VIEW_DETAILS)
9519 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
9521 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9523 LISTVIEW_UpdateScroll(infoPtr);
9524 /* send WM_MEASUREITEM notification */
9525 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr);
9528 OpenThemeData(hwnd, themeClass);
9530 /* initialize the icon sizes */
9531 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
9532 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
9533 return 0;
9536 /***
9537 * DESCRIPTION:
9538 * Destroys the listview control.
9540 * PARAMETER(S):
9541 * [I] infoPtr : valid pointer to the listview structure
9543 * RETURN:
9544 * Success: 0
9545 * Failure: -1
9547 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
9549 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
9550 CloseThemeData(theme);
9552 /* delete all items */
9553 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9555 return 0;
9558 /***
9559 * DESCRIPTION:
9560 * Enables the listview control.
9562 * PARAMETER(S):
9563 * [I] infoPtr : valid pointer to the listview structure
9564 * [I] bEnable : specifies whether to enable or disable the window
9566 * RETURN:
9567 * SUCCESS : TRUE
9568 * FAILURE : FALSE
9570 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr)
9572 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
9573 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9574 return TRUE;
9577 /***
9578 * DESCRIPTION:
9579 * Erases the background of the listview control.
9581 * PARAMETER(S):
9582 * [I] infoPtr : valid pointer to the listview structure
9583 * [I] hdc : device context handle
9585 * RETURN:
9586 * SUCCESS : TRUE
9587 * FAILURE : FALSE
9589 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
9591 RECT rc;
9593 TRACE("(hdc=%p)\n", hdc);
9595 if (!GetClipBox(hdc, &rc)) return FALSE;
9597 if (infoPtr->clrBk == CLR_NONE)
9599 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
9600 return SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT,
9601 (WPARAM)hdc, PRF_ERASEBKGND);
9602 else
9603 return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
9606 /* for double buffered controls we need to do this during refresh */
9607 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
9609 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
9613 /***
9614 * DESCRIPTION:
9615 * Helper function for LISTVIEW_[HV]Scroll *only*.
9616 * Performs vertical/horizontal scrolling by a give amount.
9618 * PARAMETER(S):
9619 * [I] infoPtr : valid pointer to the listview structure
9620 * [I] dx : amount of horizontal scroll
9621 * [I] dy : amount of vertical scroll
9623 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
9625 /* now we can scroll the list */
9626 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
9627 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
9628 /* if we have focus, adjust rect */
9629 OffsetRect(&infoPtr->rcFocus, dx, dy);
9630 UpdateWindow(infoPtr->hwndSelf);
9633 /***
9634 * DESCRIPTION:
9635 * Performs vertical scrolling.
9637 * PARAMETER(S):
9638 * [I] infoPtr : valid pointer to the listview structure
9639 * [I] nScrollCode : scroll code
9640 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9641 * [I] hScrollWnd : scrollbar control window handle
9643 * RETURN:
9644 * Zero
9646 * NOTES:
9647 * SB_LINEUP/SB_LINEDOWN:
9648 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
9649 * for LVS_REPORT is 1 line
9650 * for LVS_LIST cannot occur
9653 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9654 INT nScrollDiff)
9656 INT nOldScrollPos, nNewScrollPos;
9657 SCROLLINFO scrollInfo;
9658 BOOL is_an_icon;
9660 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9661 debugscrollcode(nScrollCode), nScrollDiff);
9663 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9665 scrollInfo.cbSize = sizeof(SCROLLINFO);
9666 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9668 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
9670 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
9672 nOldScrollPos = scrollInfo.nPos;
9673 switch (nScrollCode)
9675 case SB_INTERNAL:
9676 break;
9678 case SB_LINEUP:
9679 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
9680 break;
9682 case SB_LINEDOWN:
9683 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
9684 break;
9686 case SB_PAGEUP:
9687 nScrollDiff = -scrollInfo.nPage;
9688 break;
9690 case SB_PAGEDOWN:
9691 nScrollDiff = scrollInfo.nPage;
9692 break;
9694 case SB_THUMBPOSITION:
9695 case SB_THUMBTRACK:
9696 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9697 break;
9699 default:
9700 nScrollDiff = 0;
9703 /* quit right away if pos isn't changing */
9704 if (nScrollDiff == 0) return 0;
9706 /* calculate new position, and handle overflows */
9707 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9708 if (nScrollDiff > 0) {
9709 if (nNewScrollPos < nOldScrollPos ||
9710 nNewScrollPos > scrollInfo.nMax)
9711 nNewScrollPos = scrollInfo.nMax;
9712 } else {
9713 if (nNewScrollPos > nOldScrollPos ||
9714 nNewScrollPos < scrollInfo.nMin)
9715 nNewScrollPos = scrollInfo.nMin;
9718 /* set the new position, and reread in case it changed */
9719 scrollInfo.fMask = SIF_POS;
9720 scrollInfo.nPos = nNewScrollPos;
9721 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
9723 /* carry on only if it really changed */
9724 if (nNewScrollPos == nOldScrollPos) return 0;
9726 /* now adjust to client coordinates */
9727 nScrollDiff = nOldScrollPos - nNewScrollPos;
9728 if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
9730 /* and scroll the window */
9731 scroll_list(infoPtr, 0, nScrollDiff);
9733 return 0;
9736 /***
9737 * DESCRIPTION:
9738 * Performs horizontal scrolling.
9740 * PARAMETER(S):
9741 * [I] infoPtr : valid pointer to the listview structure
9742 * [I] nScrollCode : scroll code
9743 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9744 * [I] hScrollWnd : scrollbar control window handle
9746 * RETURN:
9747 * Zero
9749 * NOTES:
9750 * SB_LINELEFT/SB_LINERIGHT:
9751 * for LVS_ICON, LVS_SMALLICON 1 pixel
9752 * for LVS_REPORT is 1 pixel
9753 * for LVS_LIST is 1 column --> which is a 1 because the
9754 * scroll is based on columns not pixels
9757 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9758 INT nScrollDiff)
9760 INT nOldScrollPos, nNewScrollPos;
9761 SCROLLINFO scrollInfo;
9762 BOOL is_an_icon;
9764 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9765 debugscrollcode(nScrollCode), nScrollDiff);
9767 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9769 scrollInfo.cbSize = sizeof(SCROLLINFO);
9770 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9772 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
9774 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
9776 nOldScrollPos = scrollInfo.nPos;
9778 switch (nScrollCode)
9780 case SB_INTERNAL:
9781 break;
9783 case SB_LINELEFT:
9784 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
9785 break;
9787 case SB_LINERIGHT:
9788 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
9789 break;
9791 case SB_PAGELEFT:
9792 nScrollDiff = -scrollInfo.nPage;
9793 break;
9795 case SB_PAGERIGHT:
9796 nScrollDiff = scrollInfo.nPage;
9797 break;
9799 case SB_THUMBPOSITION:
9800 case SB_THUMBTRACK:
9801 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9802 break;
9804 default:
9805 nScrollDiff = 0;
9808 /* quit right away if pos isn't changing */
9809 if (nScrollDiff == 0) return 0;
9811 /* calculate new position, and handle overflows */
9812 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9813 if (nScrollDiff > 0) {
9814 if (nNewScrollPos < nOldScrollPos ||
9815 nNewScrollPos > scrollInfo.nMax)
9816 nNewScrollPos = scrollInfo.nMax;
9817 } else {
9818 if (nNewScrollPos > nOldScrollPos ||
9819 nNewScrollPos < scrollInfo.nMin)
9820 nNewScrollPos = scrollInfo.nMin;
9823 /* set the new position, and reread in case it changed */
9824 scrollInfo.fMask = SIF_POS;
9825 scrollInfo.nPos = nNewScrollPos;
9826 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
9828 /* carry on only if it really changed */
9829 if (nNewScrollPos == nOldScrollPos) return 0;
9831 if (infoPtr->hwndHeader) LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
9833 /* now adjust to client coordinates */
9834 nScrollDiff = nOldScrollPos - nNewScrollPos;
9835 if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
9837 /* and scroll the window */
9838 scroll_list(infoPtr, nScrollDiff, 0);
9840 return 0;
9843 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
9845 UINT pulScrollLines = 3;
9847 TRACE("(wheelDelta=%d)\n", wheelDelta);
9849 switch(infoPtr->uView)
9851 case LV_VIEW_ICON:
9852 case LV_VIEW_SMALLICON:
9854 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9855 * should be fixed in the future.
9857 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (wheelDelta > 0) ?
9858 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE);
9859 break;
9861 case LV_VIEW_DETAILS:
9862 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
9864 /* if scrolling changes direction, ignore left overs */
9865 if ((wheelDelta < 0 && infoPtr->cWheelRemainder < 0) ||
9866 (wheelDelta > 0 && infoPtr->cWheelRemainder > 0))
9867 infoPtr->cWheelRemainder += wheelDelta;
9868 else
9869 infoPtr->cWheelRemainder = wheelDelta;
9870 if (infoPtr->cWheelRemainder && pulScrollLines)
9872 int cLineScroll;
9873 pulScrollLines = min((UINT)LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
9874 cLineScroll = pulScrollLines * (float)infoPtr->cWheelRemainder / WHEEL_DELTA;
9875 infoPtr->cWheelRemainder -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines;
9876 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, -cLineScroll);
9878 break;
9880 case LV_VIEW_LIST:
9881 LISTVIEW_HScroll(infoPtr, (wheelDelta > 0) ? SB_LINELEFT : SB_LINERIGHT, 0);
9882 break;
9884 return 0;
9887 /***
9888 * DESCRIPTION:
9889 * ???
9891 * PARAMETER(S):
9892 * [I] infoPtr : valid pointer to the listview structure
9893 * [I] nVirtualKey : virtual key
9894 * [I] lKeyData : key data
9896 * RETURN:
9897 * Zero
9899 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
9901 HWND hwndSelf = infoPtr->hwndSelf;
9902 INT nItem = -1;
9903 NMLVKEYDOWN nmKeyDown;
9905 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
9907 /* send LVN_KEYDOWN notification */
9908 nmKeyDown.wVKey = nVirtualKey;
9909 nmKeyDown.flags = 0;
9910 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
9911 if (!IsWindow(hwndSelf))
9912 return 0;
9914 switch (nVirtualKey)
9916 case VK_SPACE:
9917 nItem = infoPtr->nFocusedItem;
9918 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
9919 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
9920 break;
9922 case VK_RETURN:
9923 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
9925 if (!notify(infoPtr, NM_RETURN)) return 0;
9926 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
9928 break;
9930 case VK_HOME:
9931 if (infoPtr->nItemCount > 0)
9932 nItem = 0;
9933 break;
9935 case VK_END:
9936 if (infoPtr->nItemCount > 0)
9937 nItem = infoPtr->nItemCount - 1;
9938 break;
9940 case VK_LEFT:
9941 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
9942 break;
9944 case VK_UP:
9945 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
9946 break;
9948 case VK_RIGHT:
9949 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
9950 break;
9952 case VK_DOWN:
9953 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
9954 break;
9956 case VK_PRIOR:
9957 if (infoPtr->uView == LV_VIEW_DETAILS)
9959 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9960 if (infoPtr->nFocusedItem == topidx)
9961 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
9962 else
9963 nItem = topidx;
9965 else
9966 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
9967 * LISTVIEW_GetCountPerRow(infoPtr);
9968 if(nItem < 0) nItem = 0;
9969 break;
9971 case VK_NEXT:
9972 if (infoPtr->uView == LV_VIEW_DETAILS)
9974 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9975 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
9976 if (infoPtr->nFocusedItem == topidx + cnt - 1)
9977 nItem = infoPtr->nFocusedItem + cnt - 1;
9978 else
9979 nItem = topidx + cnt - 1;
9981 else
9982 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
9983 * LISTVIEW_GetCountPerRow(infoPtr);
9984 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
9985 break;
9988 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
9989 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
9991 return 0;
9994 /***
9995 * DESCRIPTION:
9996 * Kills the focus.
9998 * PARAMETER(S):
9999 * [I] infoPtr : valid pointer to the listview structure
10001 * RETURN:
10002 * Zero
10004 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
10006 TRACE("()\n");
10008 /* drop any left over scroll amount */
10009 infoPtr->cWheelRemainder = 0;
10011 /* if we did not have the focus, there's nothing more to do */
10012 if (!infoPtr->bFocus) return 0;
10014 /* send NM_KILLFOCUS notification */
10015 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
10017 /* if we have a focus rectangle, get rid of it */
10018 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
10020 /* if have a marquee selection, stop it */
10021 if (infoPtr->bMarqueeSelect)
10023 /* Remove the marquee rectangle and release our mouse capture */
10024 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect);
10025 ReleaseCapture();
10027 SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0);
10029 infoPtr->bMarqueeSelect = FALSE;
10030 infoPtr->bScrolling = FALSE;
10031 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
10034 /* set window focus flag */
10035 infoPtr->bFocus = FALSE;
10037 /* invalidate the selected items before resetting focus flag */
10038 LISTVIEW_InvalidateSelectedItems(infoPtr);
10040 return 0;
10043 /***
10044 * DESCRIPTION:
10045 * Processes double click messages (left mouse button).
10047 * PARAMETER(S):
10048 * [I] infoPtr : valid pointer to the listview structure
10049 * [I] wKey : key flag
10050 * [I] x,y : mouse coordinate
10052 * RETURN:
10053 * Zero
10055 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10057 LVHITTESTINFO htInfo;
10059 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y);
10061 /* Cancel the item edition if any */
10062 if (infoPtr->itemEdit.fEnabled)
10064 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
10065 infoPtr->itemEdit.fEnabled = FALSE;
10068 /* send NM_RELEASEDCAPTURE notification */
10069 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10071 htInfo.pt.x = x;
10072 htInfo.pt.y = y;
10074 /* send NM_DBLCLK notification */
10075 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
10076 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
10078 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
10079 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
10081 return 0;
10084 static LRESULT LISTVIEW_TrackMouse(const LISTVIEW_INFO *infoPtr, POINT pt)
10086 MSG msg;
10087 RECT r;
10089 r.top = r.bottom = pt.y;
10090 r.left = r.right = pt.x;
10092 InflateRect(&r, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
10094 SetCapture(infoPtr->hwndSelf);
10096 while (1)
10098 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
10100 if (msg.message == WM_MOUSEMOVE)
10102 pt.x = (short)LOWORD(msg.lParam);
10103 pt.y = (short)HIWORD(msg.lParam);
10104 if (PtInRect(&r, pt))
10105 continue;
10106 else
10108 ReleaseCapture();
10109 return 1;
10112 else if (msg.message >= WM_LBUTTONDOWN &&
10113 msg.message <= WM_RBUTTONDBLCLK)
10115 break;
10118 DispatchMessageW(&msg);
10121 if (GetCapture() != infoPtr->hwndSelf)
10122 return 0;
10125 ReleaseCapture();
10126 return 0;
10130 /***
10131 * DESCRIPTION:
10132 * Processes mouse down messages (left mouse button).
10134 * PARAMETERS:
10135 * infoPtr [I ] valid pointer to the listview structure
10136 * wKey [I ] key flag
10137 * x,y [I ] mouse coordinate
10139 * RETURN:
10140 * Zero
10142 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10144 LVHITTESTINFO lvHitTestInfo;
10145 static BOOL bGroupSelect = TRUE;
10146 POINT pt = { x, y };
10147 INT nItem;
10149 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y);
10151 /* send NM_RELEASEDCAPTURE notification */
10152 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10154 /* set left button down flag and record the click position */
10155 infoPtr->bLButtonDown = TRUE;
10156 infoPtr->ptClickPos = pt;
10157 infoPtr->bDragging = FALSE;
10158 infoPtr->bMarqueeSelect = FALSE;
10159 infoPtr->bScrolling = FALSE;
10161 lvHitTestInfo.pt.x = x;
10162 lvHitTestInfo.pt.y = y;
10164 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
10165 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
10166 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
10168 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
10170 toggle_checkbox_state(infoPtr, nItem);
10171 return 0;
10174 if (infoPtr->dwStyle & LVS_SINGLESEL)
10176 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10177 infoPtr->nEditLabelItem = nItem;
10178 else
10179 LISTVIEW_SetSelection(infoPtr, nItem);
10181 else
10183 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
10185 if (bGroupSelect)
10187 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
10188 LISTVIEW_SetItemFocus(infoPtr, nItem);
10189 infoPtr->nSelectionMark = nItem;
10191 else
10193 LVITEMW item;
10195 item.state = LVIS_SELECTED | LVIS_FOCUSED;
10196 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
10198 LISTVIEW_SetItemState(infoPtr,nItem,&item);
10199 infoPtr->nSelectionMark = nItem;
10202 else if (wKey & MK_CONTROL)
10204 LVITEMW item;
10206 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
10208 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
10209 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
10210 LISTVIEW_SetItemState(infoPtr, nItem, &item);
10211 infoPtr->nSelectionMark = nItem;
10213 else if (wKey & MK_SHIFT)
10215 LISTVIEW_SetGroupSelection(infoPtr, nItem);
10217 else
10219 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10221 infoPtr->nEditLabelItem = nItem;
10222 infoPtr->nLButtonDownItem = nItem;
10224 LISTVIEW_SetItemFocus(infoPtr, nItem);
10226 else
10227 /* set selection (clears other pre-existing selections) */
10228 LISTVIEW_SetSelection(infoPtr, nItem);
10232 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
10233 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
10235 else
10237 if (!infoPtr->bFocus)
10238 SetFocus(infoPtr->hwndSelf);
10240 /* remove all selections */
10241 if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
10242 LISTVIEW_DeselectAll(infoPtr);
10243 ReleaseCapture();
10246 return 0;
10249 /***
10250 * DESCRIPTION:
10251 * Processes mouse up messages (left mouse button).
10253 * PARAMETERS:
10254 * infoPtr [I ] valid pointer to the listview structure
10255 * wKey [I ] key flag
10256 * x,y [I ] mouse coordinate
10258 * RETURN:
10259 * Zero
10261 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10263 LVHITTESTINFO lvHitTestInfo;
10265 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y);
10267 if (!infoPtr->bLButtonDown) return 0;
10269 lvHitTestInfo.pt.x = x;
10270 lvHitTestInfo.pt.y = y;
10272 /* send NM_CLICK notification */
10273 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10274 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
10276 /* set left button flag */
10277 infoPtr->bLButtonDown = FALSE;
10279 /* set a single selection, reset others */
10280 if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
10281 LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
10282 infoPtr->nLButtonDownItem = -1;
10284 if (infoPtr->bDragging || infoPtr->bMarqueeSelect)
10286 /* Remove the marquee rectangle and release our mouse capture */
10287 if (infoPtr->bMarqueeSelect)
10289 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
10290 ReleaseCapture();
10293 SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0);
10294 SetRect(&infoPtr->marqueeDrawRect, 0, 0, 0, 0);
10296 infoPtr->bDragging = FALSE;
10297 infoPtr->bMarqueeSelect = FALSE;
10298 infoPtr->bScrolling = FALSE;
10300 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
10301 return 0;
10304 /* if we clicked on a selected item, edit the label */
10305 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
10307 /* we want to make sure the user doesn't want to do a double click. So we will
10308 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
10310 infoPtr->itemEdit.fEnabled = TRUE;
10311 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
10312 SetTimer(infoPtr->hwndSelf,
10313 (UINT_PTR)&infoPtr->itemEdit,
10314 GetDoubleClickTime(),
10315 LISTVIEW_DelayedEditItem);
10318 if (!infoPtr->bFocus)
10319 SetFocus(infoPtr->hwndSelf);
10321 return 0;
10324 /***
10325 * DESCRIPTION:
10326 * Destroys the listview control (called after WM_DESTROY).
10328 * PARAMETER(S):
10329 * [I] infoPtr : valid pointer to the listview structure
10331 * RETURN:
10332 * Zero
10334 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
10336 INT i;
10338 TRACE("()\n");
10340 /* destroy data structure */
10341 DPA_Destroy(infoPtr->hdpaItems);
10342 DPA_Destroy(infoPtr->hdpaItemIds);
10343 DPA_Destroy(infoPtr->hdpaPosX);
10344 DPA_Destroy(infoPtr->hdpaPosY);
10345 /* columns */
10346 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++)
10347 Free(DPA_GetPtr(infoPtr->hdpaColumns, i));
10348 DPA_Destroy(infoPtr->hdpaColumns);
10349 ranges_destroy(infoPtr->selectionRanges);
10351 /* destroy image lists */
10352 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
10354 ImageList_Destroy(infoPtr->himlNormal);
10355 ImageList_Destroy(infoPtr->himlSmall);
10356 ImageList_Destroy(infoPtr->himlState);
10359 /* destroy font, bkgnd brush */
10360 infoPtr->hFont = 0;
10361 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
10362 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
10364 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
10366 /* free listview info pointer*/
10367 Free(infoPtr);
10369 return 0;
10372 /***
10373 * DESCRIPTION:
10374 * Handles notifications.
10376 * PARAMETER(S):
10377 * [I] infoPtr : valid pointer to the listview structure
10378 * [I] lpnmhdr : notification information
10380 * RETURN:
10381 * Zero
10383 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, NMHDR *lpnmhdr)
10385 NMHEADERW *lpnmh;
10387 TRACE("(lpnmhdr=%p)\n", lpnmhdr);
10389 if (!lpnmhdr || lpnmhdr->hwndFrom != infoPtr->hwndHeader) return 0;
10391 /* remember: HDN_LAST < HDN_FIRST */
10392 if (lpnmhdr->code > HDN_FIRST || lpnmhdr->code < HDN_LAST) return 0;
10393 lpnmh = (NMHEADERW *)lpnmhdr;
10395 if (lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
10397 switch (lpnmhdr->code)
10399 case HDN_TRACKW:
10400 case HDN_TRACKA:
10402 COLUMN_INFO *lpColumnInfo;
10403 POINT ptOrigin;
10404 INT x;
10406 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
10407 break;
10409 /* remove the old line (if any) */
10410 LISTVIEW_DrawTrackLine(infoPtr);
10412 /* compute & draw the new line */
10413 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
10414 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
10415 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
10416 infoPtr->xTrackLine = x + ptOrigin.x;
10417 LISTVIEW_DrawTrackLine(infoPtr);
10418 return notify_forward_header(infoPtr, lpnmh);
10421 case HDN_ENDTRACKA:
10422 case HDN_ENDTRACKW:
10423 /* remove the track line (if any) */
10424 LISTVIEW_DrawTrackLine(infoPtr);
10425 infoPtr->xTrackLine = -1;
10426 return notify_forward_header(infoPtr, lpnmh);
10428 case HDN_BEGINDRAG:
10429 if ((infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0) return 1;
10430 return notify_forward_header(infoPtr, lpnmh);
10432 case HDN_ENDDRAG:
10433 infoPtr->colRectsDirty = TRUE;
10434 LISTVIEW_InvalidateList(infoPtr);
10435 return notify_forward_header(infoPtr, lpnmh);
10437 case HDN_ITEMCHANGEDW:
10438 case HDN_ITEMCHANGEDA:
10440 COLUMN_INFO *lpColumnInfo;
10441 HDITEMW hdi;
10442 INT dx, cxy;
10444 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
10446 hdi.mask = HDI_WIDTH;
10447 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
10448 cxy = hdi.cxy;
10450 else
10451 cxy = lpnmh->pitem->cxy;
10453 /* determine how much we change since the last know position */
10454 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
10455 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
10456 if (dx != 0)
10458 lpColumnInfo->rcHeader.right += dx;
10460 hdi.mask = HDI_ORDER;
10461 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi);
10463 /* not the rightmost one */
10464 if (hdi.iOrder + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
10466 INT nIndex = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
10467 hdi.iOrder + 1, 0);
10468 LISTVIEW_ScrollColumns(infoPtr, nIndex, dx);
10470 else
10472 /* only needs to update the scrolls */
10473 infoPtr->nItemWidth += dx;
10474 LISTVIEW_UpdateScroll(infoPtr);
10476 LISTVIEW_UpdateItemSize(infoPtr);
10477 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
10479 POINT ptOrigin;
10480 RECT rcCol = lpColumnInfo->rcHeader;
10482 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
10483 OffsetRect(&rcCol, ptOrigin.x, 0);
10485 rcCol.top = infoPtr->rcList.top;
10486 rcCol.bottom = infoPtr->rcList.bottom;
10488 /* resizing left-aligned columns leaves most of the left side untouched */
10489 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
10491 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
10492 if (dx > 0)
10493 nMaxDirty += dx;
10494 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
10497 /* when shrinking the last column clear the now unused field */
10498 if (hdi.iOrder == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
10500 RECT right;
10502 rcCol.right -= dx;
10504 /* deal with right from rightmost column area */
10505 right.left = rcCol.right;
10506 right.top = rcCol.top;
10507 right.bottom = rcCol.bottom;
10508 right.right = infoPtr->rcList.right;
10510 LISTVIEW_InvalidateRect(infoPtr, &right);
10513 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
10516 break;
10519 case HDN_ITEMCLICKW:
10520 case HDN_ITEMCLICKA:
10522 /* Handle sorting by Header Column */
10523 NMLISTVIEW nmlv;
10525 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
10526 nmlv.iItem = -1;
10527 nmlv.iSubItem = lpnmh->iItem;
10528 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
10529 return notify_forward_header(infoPtr, lpnmh);
10532 case HDN_DIVIDERDBLCLICKW:
10533 case HDN_DIVIDERDBLCLICKA:
10534 /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS
10535 we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
10536 split needed for that */
10537 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
10538 return notify_forward_header(infoPtr, lpnmh);
10540 return 0;
10543 /***
10544 * DESCRIPTION:
10545 * Paint non-client area of control.
10547 * PARAMETER(S):
10548 * [I] infoPtr : valid pointer to the listview structureof the sender
10549 * [I] region : update region
10551 * RETURN:
10552 * TRUE - frame was painted
10553 * FALSE - call default window proc
10555 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
10557 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
10558 HDC dc;
10559 RECT r;
10560 HRGN cliprgn;
10561 int cxEdge = GetSystemMetrics (SM_CXEDGE),
10562 cyEdge = GetSystemMetrics (SM_CYEDGE);
10564 if (!theme)
10565 return DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)region, 0);
10567 GetWindowRect(infoPtr->hwndSelf, &r);
10569 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
10570 r.right - cxEdge, r.bottom - cyEdge);
10571 if (region != (HRGN)1)
10572 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
10573 OffsetRect(&r, -r.left, -r.top);
10575 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
10576 OffsetRect(&r, -r.left, -r.top);
10578 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
10579 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
10580 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
10581 ReleaseDC(infoPtr->hwndSelf, dc);
10583 /* Call default proc to get the scrollbars etc. painted */
10584 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
10586 return FALSE;
10589 /***
10590 * DESCRIPTION:
10591 * Determines the type of structure to use.
10593 * PARAMETER(S):
10594 * [I] infoPtr : valid pointer to the listview structureof the sender
10595 * [I] hwndFrom : listview window handle
10596 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10598 * RETURN:
10599 * Zero
10601 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
10603 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
10605 if (nCommand == NF_REQUERY)
10606 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
10608 return infoPtr->notifyFormat;
10611 /***
10612 * DESCRIPTION:
10613 * Paints/Repaints the listview control. Internal use.
10615 * PARAMETER(S):
10616 * [I] infoPtr : valid pointer to the listview structure
10617 * [I] hdc : device context handle
10619 * RETURN:
10620 * Zero
10622 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
10624 TRACE("(hdc=%p)\n", hdc);
10626 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
10628 infoPtr->bNoItemMetrics = FALSE;
10629 LISTVIEW_UpdateItemSize(infoPtr);
10630 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
10631 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10632 LISTVIEW_UpdateScroll(infoPtr);
10635 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
10637 if (hdc)
10638 LISTVIEW_Refresh(infoPtr, hdc, NULL);
10639 else
10641 PAINTSTRUCT ps;
10643 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
10644 if (!hdc) return 1;
10645 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
10646 EndPaint(infoPtr->hwndSelf, &ps);
10649 return 0;
10652 /***
10653 * DESCRIPTION:
10654 * Paints/Repaints the listview control, WM_PAINT handler.
10656 * PARAMETER(S):
10657 * [I] infoPtr : valid pointer to the listview structure
10658 * [I] hdc : device context handle
10660 * RETURN:
10661 * Zero
10663 static inline LRESULT LISTVIEW_WMPaint(LISTVIEW_INFO *infoPtr, HDC hdc)
10665 TRACE("(hdc=%p)\n", hdc);
10667 if (!is_redrawing(infoPtr))
10668 return DefWindowProcW (infoPtr->hwndSelf, WM_PAINT, (WPARAM)hdc, 0);
10670 return LISTVIEW_Paint(infoPtr, hdc);
10673 /***
10674 * DESCRIPTION:
10675 * Paints/Repaints the listview control.
10677 * PARAMETER(S):
10678 * [I] infoPtr : valid pointer to the listview structure
10679 * [I] hdc : device context handle
10680 * [I] options : drawing options
10682 * RETURN:
10683 * Zero
10685 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
10687 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
10689 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
10690 return 0;
10692 if (options & PRF_ERASEBKGND)
10693 LISTVIEW_EraseBkgnd(infoPtr, hdc);
10695 if (options & PRF_CLIENT)
10696 LISTVIEW_Paint(infoPtr, hdc);
10698 return 0;
10702 /***
10703 * DESCRIPTION:
10704 * Processes double click messages (right mouse button).
10706 * PARAMETER(S):
10707 * [I] infoPtr : valid pointer to the listview structure
10708 * [I] wKey : key flag
10709 * [I] x,y : mouse coordinate
10711 * RETURN:
10712 * Zero
10714 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10716 LVHITTESTINFO lvHitTestInfo;
10718 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y);
10720 /* send NM_RELEASEDCAPTURE notification */
10721 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10723 /* send NM_RDBLCLK notification */
10724 lvHitTestInfo.pt.x = x;
10725 lvHitTestInfo.pt.y = y;
10726 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10727 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
10729 return 0;
10732 /***
10733 * DESCRIPTION:
10734 * Processes WM_RBUTTONDOWN message and corresponding drag operation.
10736 * PARAMETER(S):
10737 * [I] infoPtr : valid pointer to the listview structure
10738 * [I] wKey : key flag
10739 * [I] x, y : mouse coordinate
10741 * RETURN:
10742 * Zero
10744 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10746 LVHITTESTINFO ht;
10747 INT item;
10749 TRACE("(key=%hu, x=%d, y=%d)\n", wKey, x, y);
10751 /* send NM_RELEASEDCAPTURE notification */
10752 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10754 /* determine the index of the selected item */
10755 ht.pt.x = x;
10756 ht.pt.y = y;
10757 item = LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
10759 /* make sure the listview control window has the focus */
10760 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
10762 if ((item >= 0) && (item < infoPtr->nItemCount))
10764 LISTVIEW_SetItemFocus(infoPtr, item);
10765 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
10766 !LISTVIEW_GetItemState(infoPtr, item, LVIS_SELECTED))
10767 LISTVIEW_SetSelection(infoPtr, item);
10769 else
10770 LISTVIEW_DeselectAll(infoPtr);
10772 if (LISTVIEW_TrackMouse(infoPtr, ht.pt))
10774 if (ht.iItem != -1)
10776 NMLISTVIEW nmlv;
10778 memset(&nmlv, 0, sizeof(nmlv));
10779 nmlv.iItem = ht.iItem;
10780 nmlv.ptAction = ht.pt;
10782 notify_listview(infoPtr, LVN_BEGINRDRAG, &nmlv);
10785 else
10787 SetFocus(infoPtr->hwndSelf);
10789 ht.pt.x = x;
10790 ht.pt.y = y;
10791 LISTVIEW_HitTest(infoPtr, &ht, TRUE, FALSE);
10793 if (notify_click(infoPtr, NM_RCLICK, &ht))
10795 /* Send a WM_CONTEXTMENU message in response to the WM_RBUTTONUP */
10796 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
10797 (WPARAM)infoPtr->hwndSelf, (LPARAM)GetMessagePos());
10801 return 0;
10804 /***
10805 * DESCRIPTION:
10806 * Sets the cursor.
10808 * PARAMETER(S):
10809 * [I] infoPtr : valid pointer to the listview structure
10810 * [I] hwnd : window handle of window containing the cursor
10811 * [I] nHittest : hit-test code
10812 * [I] wMouseMsg : ideintifier of the mouse message
10814 * RETURN:
10815 * TRUE if cursor is set
10816 * FALSE otherwise
10818 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10820 LVHITTESTINFO lvHitTestInfo;
10822 if (!LISTVIEW_IsHotTracking(infoPtr)) goto forward;
10824 if (!infoPtr->hHotCursor) goto forward;
10826 GetCursorPos(&lvHitTestInfo.pt);
10827 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) goto forward;
10829 SetCursor(infoPtr->hHotCursor);
10831 return TRUE;
10833 forward:
10835 return DefWindowProcW(infoPtr->hwndSelf, WM_SETCURSOR, wParam, lParam);
10838 /***
10839 * DESCRIPTION:
10840 * Sets the focus.
10842 * PARAMETER(S):
10843 * [I] infoPtr : valid pointer to the listview structure
10844 * [I] hwndLoseFocus : handle of previously focused window
10846 * RETURN:
10847 * Zero
10849 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
10851 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
10853 /* if we have the focus already, there's nothing to do */
10854 if (infoPtr->bFocus) return 0;
10856 /* send NM_SETFOCUS notification */
10857 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
10859 /* set window focus flag */
10860 infoPtr->bFocus = TRUE;
10862 /* put the focus rect back on */
10863 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
10865 /* redraw all visible selected items */
10866 LISTVIEW_InvalidateSelectedItems(infoPtr);
10868 return 0;
10871 /***
10872 * DESCRIPTION:
10873 * Sets the font.
10875 * PARAMETER(S):
10876 * [I] infoPtr : valid pointer to the listview structure
10877 * [I] fRedraw : font handle
10878 * [I] fRedraw : redraw flag
10880 * RETURN:
10881 * Zero
10883 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
10885 HFONT oldFont = infoPtr->hFont;
10886 INT oldHeight = infoPtr->nItemHeight;
10888 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
10890 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
10891 if (infoPtr->hFont == oldFont) return 0;
10893 LISTVIEW_SaveTextMetrics(infoPtr);
10895 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
10897 if (infoPtr->uView == LV_VIEW_DETAILS)
10899 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
10900 LISTVIEW_UpdateSize(infoPtr);
10901 LISTVIEW_UpdateScroll(infoPtr);
10903 else if (infoPtr->nItemHeight != oldHeight)
10904 LISTVIEW_UpdateScroll(infoPtr);
10906 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
10908 return 0;
10911 /***
10912 * DESCRIPTION:
10913 * Message handling for WM_SETREDRAW.
10914 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
10916 * PARAMETER(S):
10917 * [I] infoPtr : valid pointer to the listview structure
10918 * [I] bRedraw: state of redraw flag
10920 * RETURN:
10921 * DefWinProc return value
10923 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
10925 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
10927 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
10928 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
10930 infoPtr->bRedraw = bRedraw;
10932 if(!bRedraw) return 0;
10934 if (is_autoarrange(infoPtr))
10935 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10936 LISTVIEW_UpdateScroll(infoPtr);
10938 /* despite what the WM_SETREDRAW docs says, apps expect us
10939 * to invalidate the listview here... stupid! */
10940 LISTVIEW_InvalidateList(infoPtr);
10942 return 0;
10945 /***
10946 * DESCRIPTION:
10947 * Resizes the listview control. This function processes WM_SIZE
10948 * messages. At this time, the width and height are not used.
10950 * PARAMETER(S):
10951 * [I] infoPtr : valid pointer to the listview structure
10952 * [I] Width : new width
10953 * [I] Height : new height
10955 * RETURN:
10956 * Zero
10958 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
10960 RECT rcOld = infoPtr->rcList;
10962 TRACE("(width=%d, height=%d)\n", Width, Height);
10964 LISTVIEW_UpdateSize(infoPtr);
10965 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
10967 /* do not bother with display related stuff if we're not redrawing */
10968 if (!is_redrawing(infoPtr)) return 0;
10970 if (is_autoarrange(infoPtr))
10971 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10973 LISTVIEW_UpdateScroll(infoPtr);
10975 /* refresh all only for lists whose height changed significantly */
10976 if ((infoPtr->uView == LV_VIEW_LIST) &&
10977 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
10978 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
10979 LISTVIEW_InvalidateList(infoPtr);
10981 return 0;
10984 /***
10985 * DESCRIPTION:
10986 * Sets the size information.
10988 * PARAMETER(S):
10989 * [I] infoPtr : valid pointer to the listview structure
10991 * RETURN:
10992 * None
10994 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
10996 TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
10998 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
11000 if (infoPtr->uView == LV_VIEW_LIST)
11002 /* Apparently the "LIST" style is supposed to have the same
11003 * number of items in a column even if there is no scroll bar.
11004 * Since if a scroll bar already exists then the bottom is already
11005 * reduced, only reduce if the scroll bar does not currently exist.
11006 * The "2" is there to mimic the native control. I think it may be
11007 * related to either padding or edges. (GLA 7/2002)
11009 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
11010 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
11011 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
11014 /* if control created invisible header isn't created */
11015 if (infoPtr->hwndHeader)
11017 HDLAYOUT hl;
11018 WINDOWPOS wp;
11020 hl.prc = &infoPtr->rcList;
11021 hl.pwpos = &wp;
11022 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
11023 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
11025 if (LISTVIEW_IsHeaderEnabled(infoPtr))
11026 wp.flags |= SWP_SHOWWINDOW;
11027 else
11029 wp.flags |= SWP_HIDEWINDOW;
11030 wp.cy = 0;
11033 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
11034 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
11036 infoPtr->rcList.top = max(wp.cy, 0);
11038 /* extra padding for grid */
11039 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
11040 infoPtr->rcList.top += 2;
11042 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
11045 /***
11046 * DESCRIPTION:
11047 * Processes WM_STYLECHANGED messages.
11049 * PARAMETER(S):
11050 * [I] infoPtr : valid pointer to the listview structure
11051 * [I] wStyleType : window style type (normal or extended)
11052 * [I] lpss : window style information
11054 * RETURN:
11055 * Zero
11057 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
11058 const STYLESTRUCT *lpss)
11060 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
11061 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
11062 UINT style;
11064 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11065 wStyleType, lpss->styleOld, lpss->styleNew);
11067 if (wStyleType != GWL_STYLE) return 0;
11069 infoPtr->dwStyle = lpss->styleNew;
11070 map_style_view(infoPtr);
11072 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
11073 ((lpss->styleNew & WS_HSCROLL) == 0))
11074 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
11076 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
11077 ((lpss->styleNew & WS_VSCROLL) == 0))
11078 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
11080 if (uNewView != uOldView)
11082 HIMAGELIST himl;
11084 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
11085 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
11087 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
11088 SetRectEmpty(&infoPtr->rcFocus);
11090 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
11091 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
11093 if (uNewView == LVS_REPORT)
11095 HDLAYOUT hl;
11096 WINDOWPOS wp;
11098 LISTVIEW_CreateHeader( infoPtr );
11100 hl.prc = &infoPtr->rcList;
11101 hl.pwpos = &wp;
11102 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
11103 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
11104 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
11105 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
11108 LISTVIEW_UpdateItemSize(infoPtr);
11111 if (uNewView == LVS_REPORT || infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
11113 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
11115 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
11117 /* Turn off the header control */
11118 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
11119 TRACE("Hide header control, was 0x%08x\n", style);
11120 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
11121 } else {
11122 /* Turn on the header control */
11123 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
11125 TRACE("Show header control, was 0x%08x\n", style);
11126 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
11132 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
11133 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
11134 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
11136 /* update the size of the client area */
11137 LISTVIEW_UpdateSize(infoPtr);
11139 /* add scrollbars if needed */
11140 LISTVIEW_UpdateScroll(infoPtr);
11142 /* invalidate client area + erase background */
11143 LISTVIEW_InvalidateList(infoPtr);
11145 return 0;
11148 /***
11149 * DESCRIPTION:
11150 * Processes WM_STYLECHANGING messages.
11152 * PARAMETER(S):
11153 * [I] wStyleType : window style type (normal or extended)
11154 * [I0] lpss : window style information
11156 * RETURN:
11157 * Zero
11159 static INT LISTVIEW_StyleChanging(WPARAM wStyleType,
11160 STYLESTRUCT *lpss)
11162 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11163 wStyleType, lpss->styleOld, lpss->styleNew);
11165 /* don't forward LVS_OWNERDATA only if not already set to */
11166 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
11168 if (lpss->styleOld & LVS_OWNERDATA)
11169 lpss->styleNew |= LVS_OWNERDATA;
11170 else
11171 lpss->styleNew &= ~LVS_OWNERDATA;
11174 return 0;
11177 /***
11178 * DESCRIPTION:
11179 * Processes WM_SHOWWINDOW messages.
11181 * PARAMETER(S):
11182 * [I] infoPtr : valid pointer to the listview structure
11183 * [I] bShown : window is being shown (FALSE when hidden)
11184 * [I] iStatus : window show status
11186 * RETURN:
11187 * Zero
11189 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, WPARAM bShown, LPARAM iStatus)
11191 /* header delayed creation */
11192 if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
11194 LISTVIEW_CreateHeader(infoPtr);
11196 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
11197 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
11200 return DefWindowProcW(infoPtr->hwndSelf, WM_SHOWWINDOW, bShown, iStatus);
11203 /***
11204 * DESCRIPTION:
11205 * Processes CCM_GETVERSION messages.
11207 * PARAMETER(S):
11208 * [I] infoPtr : valid pointer to the listview structure
11210 * RETURN:
11211 * Current version
11213 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
11215 return infoPtr->iVersion;
11218 /***
11219 * DESCRIPTION:
11220 * Processes CCM_SETVERSION messages.
11222 * PARAMETER(S):
11223 * [I] infoPtr : valid pointer to the listview structure
11224 * [I] iVersion : version to be set
11226 * RETURN:
11227 * -1 when requested version is greater than DLL version;
11228 * previous version otherwise
11230 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
11232 INT iOldVersion = infoPtr->iVersion;
11234 if (iVersion > COMCTL32_VERSION)
11235 return -1;
11237 infoPtr->iVersion = iVersion;
11239 TRACE("new version %d\n", iVersion);
11241 return iOldVersion;
11244 /***
11245 * DESCRIPTION:
11246 * Window procedure of the listview control.
11249 static LRESULT WINAPI
11250 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11252 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
11254 TRACE("(hwnd=%p uMsg=%x wParam=%lx lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
11256 if (!infoPtr && (uMsg != WM_NCCREATE))
11257 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11259 switch (uMsg)
11261 case LVM_APPROXIMATEVIEWRECT:
11262 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
11263 LOWORD(lParam), HIWORD(lParam));
11264 case LVM_ARRANGE:
11265 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
11267 case LVM_CANCELEDITLABEL:
11268 return LISTVIEW_CancelEditLabel(infoPtr);
11270 case LVM_CREATEDRAGIMAGE:
11271 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
11273 case LVM_DELETEALLITEMS:
11274 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
11276 case LVM_DELETECOLUMN:
11277 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
11279 case LVM_DELETEITEM:
11280 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
11282 case LVM_EDITLABELA:
11283 case LVM_EDITLABELW:
11284 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam,
11285 uMsg == LVM_EDITLABELW);
11286 /* case LVM_ENABLEGROUPVIEW: */
11288 case LVM_ENSUREVISIBLE:
11289 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
11291 case LVM_FINDITEMW:
11292 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
11294 case LVM_FINDITEMA:
11295 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
11297 case LVM_GETBKCOLOR:
11298 return infoPtr->clrBk;
11300 /* case LVM_GETBKIMAGE: */
11302 case LVM_GETCALLBACKMASK:
11303 return infoPtr->uCallbackMask;
11305 case LVM_GETCOLUMNA:
11306 case LVM_GETCOLUMNW:
11307 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11308 uMsg == LVM_GETCOLUMNW);
11310 case LVM_GETCOLUMNORDERARRAY:
11311 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
11313 case LVM_GETCOLUMNWIDTH:
11314 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
11316 case LVM_GETCOUNTPERPAGE:
11317 return LISTVIEW_GetCountPerPage(infoPtr);
11319 case LVM_GETEDITCONTROL:
11320 return (LRESULT)infoPtr->hwndEdit;
11322 case LVM_GETEXTENDEDLISTVIEWSTYLE:
11323 return infoPtr->dwLvExStyle;
11325 /* case LVM_GETGROUPINFO: */
11327 /* case LVM_GETGROUPMETRICS: */
11329 case LVM_GETHEADER:
11330 return (LRESULT)infoPtr->hwndHeader;
11332 case LVM_GETHOTCURSOR:
11333 return (LRESULT)infoPtr->hHotCursor;
11335 case LVM_GETHOTITEM:
11336 return infoPtr->nHotItem;
11338 case LVM_GETHOVERTIME:
11339 return infoPtr->dwHoverTime;
11341 case LVM_GETIMAGELIST:
11342 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
11344 /* case LVM_GETINSERTMARK: */
11346 /* case LVM_GETINSERTMARKCOLOR: */
11348 /* case LVM_GETINSERTMARKRECT: */
11350 case LVM_GETISEARCHSTRINGA:
11351 case LVM_GETISEARCHSTRINGW:
11352 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
11353 return FALSE;
11355 case LVM_GETITEMA:
11356 case LVM_GETITEMW:
11357 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_GETITEMW);
11359 case LVM_GETITEMCOUNT:
11360 return infoPtr->nItemCount;
11362 case LVM_GETITEMPOSITION:
11363 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
11365 case LVM_GETITEMRECT:
11366 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
11368 case LVM_GETITEMSPACING:
11369 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
11371 case LVM_GETITEMSTATE:
11372 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
11374 case LVM_GETITEMTEXTA:
11375 case LVM_GETITEMTEXTW:
11376 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
11377 uMsg == LVM_GETITEMTEXTW);
11379 case LVM_GETNEXTITEM:
11380 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
11382 case LVM_GETNUMBEROFWORKAREAS:
11383 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
11384 return 1;
11386 case LVM_GETORIGIN:
11387 if (!lParam) return FALSE;
11388 if (infoPtr->uView == LV_VIEW_DETAILS ||
11389 infoPtr->uView == LV_VIEW_LIST) return FALSE;
11390 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
11391 return TRUE;
11393 /* case LVM_GETOUTLINECOLOR: */
11395 /* case LVM_GETSELECTEDCOLUMN: */
11397 case LVM_GETSELECTEDCOUNT:
11398 return LISTVIEW_GetSelectedCount(infoPtr);
11400 case LVM_GETSELECTIONMARK:
11401 return infoPtr->nSelectionMark;
11403 case LVM_GETSTRINGWIDTHA:
11404 case LVM_GETSTRINGWIDTHW:
11405 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam,
11406 uMsg == LVM_GETSTRINGWIDTHW);
11408 case LVM_GETSUBITEMRECT:
11409 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
11411 case LVM_GETTEXTBKCOLOR:
11412 return infoPtr->clrTextBk;
11414 case LVM_GETTEXTCOLOR:
11415 return infoPtr->clrText;
11417 /* case LVM_GETTILEINFO: */
11419 /* case LVM_GETTILEVIEWINFO: */
11421 case LVM_GETTOOLTIPS:
11422 if( !infoPtr->hwndToolTip )
11423 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
11424 return (LRESULT)infoPtr->hwndToolTip;
11426 case LVM_GETTOPINDEX:
11427 return LISTVIEW_GetTopIndex(infoPtr);
11429 case LVM_GETUNICODEFORMAT:
11430 return (infoPtr->notifyFormat == NFR_UNICODE);
11432 case LVM_GETVIEW:
11433 return infoPtr->uView;
11435 case LVM_GETVIEWRECT:
11436 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
11438 case LVM_GETWORKAREAS:
11439 FIXME("LVM_GETWORKAREAS: unimplemented\n");
11440 return FALSE;
11442 /* case LVM_HASGROUP: */
11444 case LVM_HITTEST:
11445 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
11447 case LVM_INSERTCOLUMNA:
11448 case LVM_INSERTCOLUMNW:
11449 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11450 uMsg == LVM_INSERTCOLUMNW);
11452 /* case LVM_INSERTGROUP: */
11454 /* case LVM_INSERTGROUPSORTED: */
11456 case LVM_INSERTITEMA:
11457 case LVM_INSERTITEMW:
11458 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_INSERTITEMW);
11460 /* case LVM_INSERTMARKHITTEST: */
11462 /* case LVM_ISGROUPVIEWENABLED: */
11464 case LVM_ISITEMVISIBLE:
11465 return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam);
11467 case LVM_MAPIDTOINDEX:
11468 return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam);
11470 case LVM_MAPINDEXTOID:
11471 return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam);
11473 /* case LVM_MOVEGROUP: */
11475 /* case LVM_MOVEITEMTOGROUP: */
11477 case LVM_REDRAWITEMS:
11478 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
11480 /* case LVM_REMOVEALLGROUPS: */
11482 /* case LVM_REMOVEGROUP: */
11484 case LVM_SCROLL:
11485 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
11487 case LVM_SETBKCOLOR:
11488 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
11490 /* case LVM_SETBKIMAGE: */
11492 case LVM_SETCALLBACKMASK:
11493 infoPtr->uCallbackMask = (UINT)wParam;
11494 return TRUE;
11496 case LVM_SETCOLUMNA:
11497 case LVM_SETCOLUMNW:
11498 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11499 uMsg == LVM_SETCOLUMNW);
11501 case LVM_SETCOLUMNORDERARRAY:
11502 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
11504 case LVM_SETCOLUMNWIDTH:
11505 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
11507 case LVM_SETEXTENDEDLISTVIEWSTYLE:
11508 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
11510 /* case LVM_SETGROUPINFO: */
11512 /* case LVM_SETGROUPMETRICS: */
11514 case LVM_SETHOTCURSOR:
11515 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
11517 case LVM_SETHOTITEM:
11518 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
11520 case LVM_SETHOVERTIME:
11521 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)lParam);
11523 case LVM_SETICONSPACING:
11524 if(lParam == -1)
11525 return LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
11526 return LISTVIEW_SetIconSpacing(infoPtr, LOWORD(lParam), HIWORD(lParam));
11528 case LVM_SETIMAGELIST:
11529 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
11531 /* case LVM_SETINFOTIP: */
11533 /* case LVM_SETINSERTMARK: */
11535 /* case LVM_SETINSERTMARKCOLOR: */
11537 case LVM_SETITEMA:
11538 case LVM_SETITEMW:
11540 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
11541 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
11544 case LVM_SETITEMCOUNT:
11545 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
11547 case LVM_SETITEMPOSITION:
11549 POINT pt;
11550 pt.x = (short)LOWORD(lParam);
11551 pt.y = (short)HIWORD(lParam);
11552 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, &pt);
11555 case LVM_SETITEMPOSITION32:
11556 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam);
11558 case LVM_SETITEMSTATE:
11559 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
11561 case LVM_SETITEMTEXTA:
11562 case LVM_SETITEMTEXTW:
11563 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
11564 uMsg == LVM_SETITEMTEXTW);
11566 /* case LVM_SETOUTLINECOLOR: */
11568 /* case LVM_SETSELECTEDCOLUMN: */
11570 case LVM_SETSELECTIONMARK:
11571 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
11573 case LVM_SETTEXTBKCOLOR:
11574 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
11576 case LVM_SETTEXTCOLOR:
11577 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
11579 /* case LVM_SETTILEINFO: */
11581 /* case LVM_SETTILEVIEWINFO: */
11583 /* case LVM_SETTILEWIDTH: */
11585 case LVM_SETTOOLTIPS:
11586 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
11588 case LVM_SETUNICODEFORMAT:
11589 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
11591 case LVM_SETVIEW:
11592 return LISTVIEW_SetView(infoPtr, wParam);
11594 /* case LVM_SETWORKAREAS: */
11596 /* case LVM_SORTGROUPS: */
11598 case LVM_SORTITEMS:
11599 case LVM_SORTITEMSEX:
11600 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, wParam,
11601 uMsg == LVM_SORTITEMSEX);
11602 case LVM_SUBITEMHITTEST:
11603 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
11605 case LVM_UPDATE:
11606 return LISTVIEW_Update(infoPtr, (INT)wParam);
11608 case CCM_GETVERSION:
11609 return LISTVIEW_GetVersion(infoPtr);
11611 case CCM_SETVERSION:
11612 return LISTVIEW_SetVersion(infoPtr, wParam);
11614 case WM_CHAR:
11615 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
11617 case WM_COMMAND:
11618 return LISTVIEW_Command(infoPtr, wParam, lParam);
11620 case WM_NCCREATE:
11621 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
11623 case WM_CREATE:
11624 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
11626 case WM_DESTROY:
11627 return LISTVIEW_Destroy(infoPtr);
11629 case WM_ENABLE:
11630 return LISTVIEW_Enable(infoPtr);
11632 case WM_ERASEBKGND:
11633 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
11635 case WM_GETDLGCODE:
11636 return DLGC_WANTCHARS | DLGC_WANTARROWS;
11638 case WM_GETFONT:
11639 return (LRESULT)infoPtr->hFont;
11641 case WM_HSCROLL:
11642 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0);
11644 case WM_KEYDOWN:
11645 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
11647 case WM_KILLFOCUS:
11648 return LISTVIEW_KillFocus(infoPtr);
11650 case WM_LBUTTONDBLCLK:
11651 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11653 case WM_LBUTTONDOWN:
11654 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11656 case WM_LBUTTONUP:
11657 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11659 case WM_MOUSEMOVE:
11660 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11662 case WM_MOUSEHOVER:
11663 return LISTVIEW_MouseHover(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11665 case WM_NCDESTROY:
11666 return LISTVIEW_NCDestroy(infoPtr);
11668 case WM_NCPAINT:
11669 return LISTVIEW_NCPaint(infoPtr, (HRGN)wParam);
11671 case WM_NOTIFY:
11672 return LISTVIEW_Notify(infoPtr, (LPNMHDR)lParam);
11674 case WM_NOTIFYFORMAT:
11675 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
11677 case WM_PRINTCLIENT:
11678 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
11680 case WM_PAINT:
11681 return LISTVIEW_WMPaint(infoPtr, (HDC)wParam);
11683 case WM_RBUTTONDBLCLK:
11684 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11686 case WM_RBUTTONDOWN:
11687 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11689 case WM_SETCURSOR:
11690 return LISTVIEW_SetCursor(infoPtr, wParam, lParam);
11692 case WM_SETFOCUS:
11693 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
11695 case WM_SETFONT:
11696 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
11698 case WM_SETREDRAW:
11699 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
11701 case WM_SHOWWINDOW:
11702 return LISTVIEW_ShowWindow(infoPtr, wParam, lParam);
11704 case WM_STYLECHANGED:
11705 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11707 case WM_STYLECHANGING:
11708 return LISTVIEW_StyleChanging(wParam, (LPSTYLESTRUCT)lParam);
11710 case WM_SYSCOLORCHANGE:
11711 COMCTL32_RefreshSysColors();
11712 return 0;
11714 /* case WM_TIMER: */
11715 case WM_THEMECHANGED:
11716 return LISTVIEW_ThemeChanged(infoPtr);
11718 case WM_VSCROLL:
11719 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0);
11721 case WM_MOUSEWHEEL:
11722 if (wParam & (MK_SHIFT | MK_CONTROL))
11723 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11724 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
11726 case WM_WINDOWPOSCHANGED:
11727 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
11729 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
11730 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
11732 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
11734 if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
11736 LISTVIEW_Size(infoPtr, ((WINDOWPOS *)lParam)->cx, ((WINDOWPOS *)lParam)->cy);
11738 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11740 /* case WM_WININICHANGE: */
11742 default:
11743 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
11744 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
11746 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11751 /***
11752 * DESCRIPTION:
11753 * Registers the window class.
11755 * PARAMETER(S):
11756 * None
11758 * RETURN:
11759 * None
11761 void LISTVIEW_Register(void)
11763 WNDCLASSW wndClass;
11765 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
11766 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
11767 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
11768 wndClass.cbClsExtra = 0;
11769 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
11770 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
11771 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
11772 wndClass.lpszClassName = WC_LISTVIEWW;
11773 RegisterClassW(&wndClass);
11776 /***
11777 * DESCRIPTION:
11778 * Unregisters the window class.
11780 * PARAMETER(S):
11781 * None
11783 * RETURN:
11784 * None
11786 void LISTVIEW_Unregister(void)
11788 UnregisterClassW(WC_LISTVIEWW, NULL);
11791 /***
11792 * DESCRIPTION:
11793 * Handle any WM_COMMAND messages
11795 * PARAMETER(S):
11796 * [I] infoPtr : valid pointer to the listview structure
11797 * [I] wParam : the first message parameter
11798 * [I] lParam : the second message parameter
11800 * RETURN:
11801 * Zero.
11803 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
11806 TRACE("(%p %x %x %lx)\n", infoPtr, HIWORD(wParam), LOWORD(wParam), lParam);
11808 if (!infoPtr->hwndEdit) return 0;
11810 switch (HIWORD(wParam))
11812 case EN_UPDATE:
11815 * Adjust the edit window size
11817 WCHAR buffer[1024];
11818 HDC hdc = GetDC(infoPtr->hwndEdit);
11819 HFONT hFont, hOldFont = 0;
11820 RECT rect;
11821 SIZE sz;
11823 if (!infoPtr->hwndEdit || !hdc) return 0;
11824 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
11825 GetWindowRect(infoPtr->hwndEdit, &rect);
11827 /* Select font to get the right dimension of the string */
11828 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
11829 if (hFont)
11831 hOldFont = SelectObject(hdc, hFont);
11834 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
11836 TEXTMETRICW textMetric;
11838 /* Add Extra spacing for the next character */
11839 GetTextMetricsW(hdc, &textMetric);
11840 sz.cx += (textMetric.tmMaxCharWidth * 2);
11842 SetWindowPos(infoPtr->hwndEdit, NULL, 0, 0, sz.cx,
11843 rect.bottom - rect.top, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOZORDER);
11845 if (hFont)
11846 SelectObject(hdc, hOldFont);
11848 ReleaseDC(infoPtr->hwndEdit, hdc);
11850 break;
11852 case EN_KILLFOCUS:
11854 LISTVIEW_CancelEditLabel(infoPtr);
11855 break;
11858 default:
11859 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
11862 return 0;