push 955563f995be8b0942dbb757ceb5b3a4c8ccafbf
[wine/hacks.git] / dlls / comctl32 / listview.c
blobebf49c622eb4f66729f181f1248589222ffcaeb7
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 Nikolay Sivov
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 * NOTES
27 * This code was audited for completeness against the documented features
28 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
30 * Unless otherwise noted, we believe this code to be complete, as per
31 * the specification mentioned above.
32 * If you discover missing features, or bugs, please note them below.
34 * TODO:
36 * Default Message Processing
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
40 * or small icon and the LVS_AUTOARRANGE style is specified.
41 * -- WM_TIMER
42 * -- WM_WININICHANGE
44 * Features
45 * -- Hot item handling, mouse hovering
46 * -- Workareas support
47 * -- Tilemode support
48 * -- Groups support
50 * Bugs
51 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
52 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
53 * -- LVA_SNAPTOGRID not implemented
54 * -- LISTVIEW_ApproximateViewRect partially implemented
55 * -- LISTVIEW_[GS]etColumnOrderArray stubs
56 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
57 * -- LISTVIEW_SetIconSpacing is incomplete
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_CUT
78 * -- LVIS_DROPHILITED
79 * -- LVIS_OVERLAYMASK
81 * Styles
82 * -- LVS_NOLABELWRAP
83 * -- LVS_NOSCROLL (see Q137520)
84 * -- LVS_ALIGNTOP
86 * Extended Styles
87 * -- LVS_EX_BORDERSELECT
88 * -- LVS_EX_FLATSB
89 * -- LVS_EX_HEADERDRAGDROP
90 * -- LVS_EX_INFOTIP
91 * -- LVS_EX_LABELTIP
92 * -- LVS_EX_MULTIWORKAREAS
93 * -- LVS_EX_REGIONAL
94 * -- LVS_EX_SIMPLESELECT
95 * -- LVS_EX_TWOCLICKACTIVATE
96 * -- LVS_EX_UNDERLINECOLD
97 * -- LVS_EX_UNDERLINEHOT
99 * Notifications:
100 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
101 * -- LVN_GETINFOTIP
102 * -- LVN_HOTTRACK
103 * -- LVN_MARQUEEBEGIN
104 * -- LVN_SETDISPINFO
105 * -- NM_HOVER
106 * -- LVN_BEGINRDRAG
108 * Messages:
109 * -- LVM_CANCELEDITLABEL
110 * -- LVM_ENABLEGROUPVIEW
111 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
112 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
113 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
114 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
115 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
116 * -- LVM_GETINSERTMARKRECT
117 * -- LVM_GETNUMBEROFWORKAREAS
118 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
119 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
120 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
121 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
122 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
123 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
124 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
125 * -- LVM_INSERTGROUPSORTED
126 * -- LVM_INSERTMARKHITTEST
127 * -- LVM_ISGROUPVIEWENABLED
128 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
129 * -- LVM_MOVEGROUP
130 * -- LVM_MOVEITEMTOGROUP
131 * -- LVM_SETINFOTIP
132 * -- LVM_SETTILEWIDTH
133 * -- LVM_SORTGROUPS
135 * Macros:
136 * -- ListView_GetHoverTime, ListView_SetHoverTime
137 * -- ListView_GetISearchString
138 * -- ListView_GetNumberOfWorkAreas
139 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
141 * Functions:
142 * -- LVGroupComparE
144 * Known differences in message stream from native control (not known if
145 * these differences cause problems):
146 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
147 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
148 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
149 * processing for "USEDOUBLECLICKTIME".
152 #include "config.h"
153 #include "wine/port.h"
155 #include <assert.h>
156 #include <ctype.h>
157 #include <string.h>
158 #include <stdlib.h>
159 #include <stdarg.h>
160 #include <stdio.h>
162 #include "windef.h"
163 #include "winbase.h"
164 #include "winnt.h"
165 #include "wingdi.h"
166 #include "winuser.h"
167 #include "winnls.h"
168 #include "commctrl.h"
169 #include "comctl32.h"
170 #include "uxtheme.h"
172 #include "wine/debug.h"
173 #include "wine/unicode.h"
175 WINE_DEFAULT_DEBUG_CHANNEL(listview);
177 /* make sure you set this to 0 for production use! */
178 #define DEBUG_RANGES 1
180 typedef struct tagCOLUMN_INFO
182 RECT rcHeader; /* tracks the header's rectangle */
183 int fmt; /* same as LVCOLUMN.fmt */
184 } COLUMN_INFO;
186 typedef struct tagITEMHDR
188 LPWSTR pszText;
189 INT iImage;
190 } ITEMHDR, *LPITEMHDR;
192 typedef struct tagSUBITEM_INFO
194 ITEMHDR hdr;
195 INT iSubItem;
196 } SUBITEM_INFO;
198 typedef struct tagITEM_INFO
200 ITEMHDR hdr;
201 UINT state;
202 LPARAM lParam;
203 INT iIndent;
204 } ITEM_INFO;
206 typedef struct tagRANGE
208 INT lower;
209 INT upper;
210 } RANGE;
212 typedef struct tagRANGES
214 HDPA hdpa;
215 } *RANGES;
217 typedef struct tagITERATOR
219 INT nItem;
220 INT nSpecial;
221 RANGE range;
222 RANGES ranges;
223 INT index;
224 } ITERATOR;
226 typedef struct tagDELAYED_ITEM_EDIT
228 BOOL fEnabled;
229 INT iItem;
230 } DELAYED_ITEM_EDIT;
232 typedef struct tagLISTVIEW_INFO
234 HWND hwndSelf;
235 HBRUSH hBkBrush;
236 COLORREF clrBk;
237 COLORREF clrText;
238 COLORREF clrTextBk;
239 HIMAGELIST himlNormal;
240 HIMAGELIST himlSmall;
241 HIMAGELIST himlState;
242 BOOL bLButtonDown;
243 BOOL bRButtonDown;
244 BOOL bDragging;
245 POINT ptClickPos; /* point where the user clicked */
246 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
247 INT nItemHeight;
248 INT nItemWidth;
249 RANGES selectionRanges;
250 INT nSelectionMark;
251 INT nHotItem;
252 SHORT notifyFormat;
253 HWND hwndNotify;
254 RECT rcList; /* This rectangle is really the window
255 * client rectangle possibly reduced by the
256 * horizontal scroll bar and/or header - see
257 * LISTVIEW_UpdateSize. This rectangle offset
258 * by the LISTVIEW_GetOrigin value is in
259 * client coordinates */
260 SIZE iconSize;
261 SIZE iconSpacing;
262 SIZE iconStateSize;
263 UINT uCallbackMask;
264 HWND hwndHeader;
265 HCURSOR hHotCursor;
266 HFONT hDefaultFont;
267 HFONT hFont;
268 INT ntmHeight; /* Some cached metrics of the font used */
269 INT ntmMaxCharWidth; /* by the listview to draw items */
270 INT nEllipsisWidth;
271 BOOL bRedraw; /* Turns on/off repaints & invalidations */
272 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
273 BOOL bFocus;
274 BOOL bDoChangeNotify; /* send change notification messages? */
275 INT nFocusedItem;
276 RECT rcFocus;
277 DWORD dwStyle; /* the cached window GWL_STYLE */
278 DWORD dwLvExStyle; /* extended listview style */
279 DWORD uView; /* current view available through LVM_[G,S]ETVIEW */
280 INT nItemCount; /* the number of items in the list */
281 HDPA hdpaItems; /* array ITEM_INFO pointers */
282 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
283 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
284 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
285 POINT currIconPos; /* this is the position next icon will be placed */
286 PFNLVCOMPARE pfnCompare;
287 LPARAM lParamSort;
288 HWND hwndEdit;
289 WNDPROC EditWndProc;
290 INT nEditLabelItem;
291 INT nLButtonDownItem; /* tracks item to reset multiselection on WM_LBUTTONUP */
292 DWORD dwHoverTime;
293 HWND hwndToolTip;
295 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
297 DWORD lastKeyPressTimestamp;
298 WPARAM charCode;
299 INT nSearchParamLength;
300 WCHAR szSearchParam[ MAX_PATH ];
301 BOOL bIsDrawing;
302 INT nMeasureItemHeight;
303 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
304 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
306 DWORD iVersion; /* CCM_[G,S]ETVERSION */
307 } LISTVIEW_INFO;
310 * constants
312 /* How many we debug buffer to allocate */
313 #define DEBUG_BUFFERS 20
314 /* The size of a single debug bbuffer */
315 #define DEBUG_BUFFER_SIZE 256
317 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
318 #define SB_INTERNAL -1
320 /* maximum size of a label */
321 #define DISP_TEXT_SIZE 512
323 /* padding for items in list and small icon display modes */
324 #define WIDTH_PADDING 12
326 /* padding for items in list, report and small icon display modes */
327 #define HEIGHT_PADDING 1
329 /* offset of items in report display mode */
330 #define REPORT_MARGINX 2
332 /* padding for icon in large icon display mode
333 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
334 * that HITTEST will see.
335 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
336 * ICON_TOP_PADDING - sum of the two above.
337 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
338 * LABEL_HOR_PADDING - between text and sides of box
339 * LABEL_VERT_PADDING - between bottom of text and end of box
341 * ICON_LR_PADDING - additional width above icon size.
342 * ICON_LR_HALF - half of the above value
344 #define ICON_TOP_PADDING_NOTHITABLE 2
345 #define ICON_TOP_PADDING_HITABLE 2
346 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
347 #define ICON_BOTTOM_PADDING 4
348 #define LABEL_HOR_PADDING 5
349 #define LABEL_VERT_PADDING 7
350 #define ICON_LR_PADDING 16
351 #define ICON_LR_HALF (ICON_LR_PADDING/2)
353 /* default label width for items in list and small icon display modes */
354 #define DEFAULT_LABEL_WIDTH 40
356 /* default column width for items in list display mode */
357 #define DEFAULT_COLUMN_WIDTH 128
359 /* Size of "line" scroll for V & H scrolls */
360 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
362 /* Padding between image and label */
363 #define IMAGE_PADDING 2
365 /* Padding behind the label */
366 #define TRAILING_LABEL_PADDING 12
367 #define TRAILING_HEADER_PADDING 11
369 /* Border for the icon caption */
370 #define CAPTION_BORDER 2
372 /* Standard DrawText flags */
373 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
374 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
375 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
377 /* Image index from state */
378 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
380 /* The time in milliseconds to reset the search in the list */
381 #define KEY_DELAY 450
383 /* Dump the LISTVIEW_INFO structure to the debug channel */
384 #define LISTVIEW_DUMP(iP) do { \
385 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
386 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
387 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
388 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
389 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
390 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
391 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
392 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
393 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
394 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
395 } while(0)
397 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
400 * forward declarations
402 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
403 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
404 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
405 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
406 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
407 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
408 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
409 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
410 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
411 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
412 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
413 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
414 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
415 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
416 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
417 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
418 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
419 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, BOOL);
420 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
421 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
423 /******** Text handling functions *************************************/
425 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
426 * text string. The string may be ANSI or Unicode, in which case
427 * the boolean isW tells us the type of the string.
429 * The name of the function tell what type of strings it expects:
430 * W: Unicode, T: ANSI/Unicode - function of isW
433 static inline BOOL is_textW(LPCWSTR text)
435 return text != NULL && text != LPSTR_TEXTCALLBACKW;
438 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
440 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
441 return is_textW(text);
444 static inline int textlenT(LPCWSTR text, BOOL isW)
446 return !is_textT(text, isW) ? 0 :
447 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
450 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
452 if (isDestW)
453 if (isSrcW) lstrcpynW(dest, src, max);
454 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
455 else
456 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
457 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
460 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
462 LPWSTR wstr = (LPWSTR)text;
464 if (!isW && is_textT(text, isW))
466 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
467 wstr = Alloc(len * sizeof(WCHAR));
468 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
470 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
471 return wstr;
474 static inline void textfreeT(LPWSTR wstr, BOOL isW)
476 if (!isW && is_textT(wstr, isW)) Free (wstr);
480 * dest is a pointer to a Unicode string
481 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
483 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
485 BOOL bResult = TRUE;
487 if (src == LPSTR_TEXTCALLBACKW)
489 if (is_textW(*dest)) Free(*dest);
490 *dest = LPSTR_TEXTCALLBACKW;
492 else
494 LPWSTR pszText = textdupTtoW(src, isW);
495 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
496 bResult = Str_SetPtrW(dest, pszText);
497 textfreeT(pszText, isW);
499 return bResult;
503 * compares a Unicode to a Unicode/ANSI text string
505 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
507 if (!aw) return bt ? -1 : 0;
508 if (!bt) return aw ? 1 : 0;
509 if (aw == LPSTR_TEXTCALLBACKW)
510 return bt == LPSTR_TEXTCALLBACKW ? 1 : -1;
511 if (bt != LPSTR_TEXTCALLBACKW)
513 LPWSTR bw = textdupTtoW(bt, isW);
514 int r = bw ? lstrcmpW(aw, bw) : 1;
515 textfreeT(bw, isW);
516 return r;
519 return 1;
522 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
524 int res;
526 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
527 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
528 return res ? res - sizeof(WCHAR) : res;
531 /******** Debugging functions *****************************************/
533 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
535 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
536 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
539 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
541 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
542 n = min(textlenT(text, isW), n);
543 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
546 static char* debug_getbuf(void)
548 static int index = 0;
549 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
550 return buffers[index++ % DEBUG_BUFFERS];
553 static inline const char* debugrange(const RANGE *lprng)
555 if (!lprng) return "(null)";
556 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
559 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
561 char* buf = debug_getbuf(), *text = buf;
562 int len, size = DEBUG_BUFFER_SIZE;
564 if (pScrollInfo == NULL) return "(null)";
565 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
566 if (len == -1) goto end; buf += len; size -= len;
567 if (pScrollInfo->fMask & SIF_RANGE)
568 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
569 else len = 0;
570 if (len == -1) goto end; buf += len; size -= len;
571 if (pScrollInfo->fMask & SIF_PAGE)
572 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
573 else len = 0;
574 if (len == -1) goto end; buf += len; size -= len;
575 if (pScrollInfo->fMask & SIF_POS)
576 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
577 else len = 0;
578 if (len == -1) goto end; buf += len; size -= len;
579 if (pScrollInfo->fMask & SIF_TRACKPOS)
580 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
581 else len = 0;
582 if (len == -1) goto end; buf += len; size -= len;
583 goto undo;
584 end:
585 buf = text + strlen(text);
586 undo:
587 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
588 return text;
591 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
593 if (!plvnm) return "(null)";
594 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
595 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
596 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
597 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
600 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
602 char* buf = debug_getbuf(), *text = buf;
603 int len, size = DEBUG_BUFFER_SIZE;
605 if (lpLVItem == NULL) return "(null)";
606 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
607 if (len == -1) goto end; buf += len; size -= len;
608 if (lpLVItem->mask & LVIF_STATE)
609 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
610 else len = 0;
611 if (len == -1) goto end; buf += len; size -= len;
612 if (lpLVItem->mask & LVIF_TEXT)
613 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
614 else len = 0;
615 if (len == -1) goto end; buf += len; size -= len;
616 if (lpLVItem->mask & LVIF_IMAGE)
617 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
618 else len = 0;
619 if (len == -1) goto end; buf += len; size -= len;
620 if (lpLVItem->mask & LVIF_PARAM)
621 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
622 else len = 0;
623 if (len == -1) goto end; buf += len; size -= len;
624 if (lpLVItem->mask & LVIF_INDENT)
625 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
626 else len = 0;
627 if (len == -1) goto end; buf += len; size -= len;
628 goto undo;
629 end:
630 buf = text + strlen(text);
631 undo:
632 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
633 return text;
636 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
638 char* buf = debug_getbuf(), *text = buf;
639 int len, size = DEBUG_BUFFER_SIZE;
641 if (lpColumn == NULL) return "(null)";
642 len = snprintf(buf, size, "{");
643 if (len == -1) goto end; buf += len; size -= len;
644 if (lpColumn->mask & LVCF_SUBITEM)
645 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
646 else len = 0;
647 if (len == -1) goto end; buf += len; size -= len;
648 if (lpColumn->mask & LVCF_FMT)
649 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
650 else len = 0;
651 if (len == -1) goto end; buf += len; size -= len;
652 if (lpColumn->mask & LVCF_WIDTH)
653 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
654 else len = 0;
655 if (len == -1) goto end; buf += len; size -= len;
656 if (lpColumn->mask & LVCF_TEXT)
657 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
658 else len = 0;
659 if (len == -1) goto end; buf += len; size -= len;
660 if (lpColumn->mask & LVCF_IMAGE)
661 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
662 else len = 0;
663 if (len == -1) goto end; buf += len; size -= len;
664 if (lpColumn->mask & LVCF_ORDER)
665 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
666 else len = 0;
667 if (len == -1) goto end; buf += len; size -= len;
668 goto undo;
669 end:
670 buf = text + strlen(text);
671 undo:
672 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
673 return text;
676 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
678 if (!lpht) return "(null)";
680 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
681 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
684 /* Return the corresponding text for a given scroll value */
685 static inline LPCSTR debugscrollcode(int nScrollCode)
687 switch(nScrollCode)
689 case SB_LINELEFT: return "SB_LINELEFT";
690 case SB_LINERIGHT: return "SB_LINERIGHT";
691 case SB_PAGELEFT: return "SB_PAGELEFT";
692 case SB_PAGERIGHT: return "SB_PAGERIGHT";
693 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
694 case SB_THUMBTRACK: return "SB_THUMBTRACK";
695 case SB_ENDSCROLL: return "SB_ENDSCROLL";
696 case SB_INTERNAL: return "SB_INTERNAL";
697 default: return "unknown";
702 /******** Notification functions ************************************/
704 static int get_ansi_notification(UINT unicodeNotificationCode)
706 switch (unicodeNotificationCode)
708 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
709 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
710 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
711 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
712 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
713 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
714 /* header forwards */
715 case HDN_TRACKW: return HDN_TRACKA;
716 case HDN_ENDTRACKW: return HDN_ENDTRACKA;
717 case HDN_BEGINDRAG: return HDN_BEGINDRAG;
718 case HDN_ENDDRAG: return HDN_ENDDRAG;
719 case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
720 case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
721 case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
722 case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
724 ERR("unknown notification %x\n", unicodeNotificationCode);
725 assert(FALSE);
726 return 0;
729 /* forwards header notifications to listview parent */
730 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
732 NMHEADERA nmhA;
733 HDITEMA hditema;
734 HD_TEXTFILTERA textfilter;
735 LPSTR text = NULL, filter = NULL;
736 LRESULT ret;
738 /* on unicode format exit earlier */
739 if (infoPtr->notifyFormat == NFR_UNICODE)
740 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
741 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
743 /* header always supplies unicode notifications,
744 all we have to do is to convert strings to ANSI */
745 nmhA = *(NMHEADERA*)lpnmh;
746 if (lpnmh->pitem)
748 hditema = *(HDITEMA*)lpnmh->pitem;
749 nmhA.pitem = &hditema;
750 /* convert item text */
751 if (lpnmh->pitem->mask & HDI_TEXT)
753 Str_SetPtrWtoA(&hditema.pszText, lpnmh->pitem->pszText);
754 text = hditema.pszText;
756 /* convert filter text */
757 if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
758 lpnmh->pitem->pvFilter)
760 hditema.pvFilter = &textfilter;
761 textfilter = *(HD_TEXTFILTERA*)(lpnmh->pitem->pvFilter);
762 Str_SetPtrWtoA(&textfilter.pszText, ((HD_TEXTFILTERW*)lpnmh->pitem->pvFilter)->pszText);
763 filter = textfilter.pszText;
766 nmhA.hdr.code = get_ansi_notification(lpnmh->hdr.code);
768 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
769 (WPARAM)nmhA.hdr.idFrom, (LPARAM)&nmhA);
771 /* cleanup */
772 Free(text);
773 Free(filter);
775 return ret;
778 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
780 LRESULT result;
782 TRACE("(code=%d)\n", code);
784 pnmh->hwndFrom = infoPtr->hwndSelf;
785 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
786 pnmh->code = code;
787 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
789 TRACE(" <= %ld\n", result);
791 return result;
794 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
796 NMHDR nmh;
797 HWND hwnd = infoPtr->hwndSelf;
798 notify_hdr(infoPtr, code, &nmh);
799 return IsWindow(hwnd);
802 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
804 NMITEMACTIVATE nmia;
805 LVITEMW item;
807 if (htInfo) {
808 nmia.uNewState = 0;
809 nmia.uOldState = 0;
810 nmia.uChanged = 0;
811 nmia.uKeyFlags = 0;
813 item.mask = LVIF_PARAM|LVIF_STATE;
814 item.iItem = htInfo->iItem;
815 item.iSubItem = 0;
816 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
817 nmia.lParam = item.lParam;
818 nmia.uOldState = item.state;
819 nmia.uNewState = item.state | LVIS_ACTIVATING;
820 nmia.uChanged = LVIF_STATE;
823 nmia.iItem = htInfo->iItem;
824 nmia.iSubItem = htInfo->iSubItem;
825 nmia.ptAction = htInfo->pt;
827 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
828 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
829 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
831 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
834 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
836 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
837 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
840 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
842 NMITEMACTIVATE nmia;
843 LVITEMW item;
844 HWND hwnd = infoPtr->hwndSelf;
846 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
847 ZeroMemory(&nmia, sizeof(nmia));
848 nmia.iItem = lvht->iItem;
849 nmia.iSubItem = lvht->iSubItem;
850 nmia.ptAction = lvht->pt;
851 item.mask = LVIF_PARAM;
852 item.iItem = lvht->iItem;
853 item.iSubItem = 0;
854 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
855 notify_hdr(infoPtr, code, (LPNMHDR)&nmia);
856 return IsWindow(hwnd);
859 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
861 NMLISTVIEW nmlv;
862 LVITEMW item;
863 HWND hwnd = infoPtr->hwndSelf;
865 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
866 nmlv.iItem = nItem;
867 item.mask = LVIF_PARAM;
868 item.iItem = nItem;
869 item.iSubItem = 0;
870 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
871 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
872 return IsWindow(hwnd);
876 Send notification. depends on dispinfoW having same
877 structure as dispinfoA.
878 infoPtr : listview struct
879 notificationCode : *Unicode* notification code
880 pdi : dispinfo structure (can be unicode or ansi)
881 isW : TRUE if dispinfo is Unicode
883 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
885 BOOL bResult = FALSE;
886 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
887 INT cchTempBufMax = 0, savCchTextMax = 0;
888 UINT realNotifCode;
889 LPWSTR pszTempBuf = NULL, savPszText = NULL;
891 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
893 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
894 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
897 if (convertToAnsi || convertToUnicode)
899 if (notificationCode != LVN_GETDISPINFOW)
901 cchTempBufMax = convertToUnicode ?
902 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
903 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
905 else
907 cchTempBufMax = pdi->item.cchTextMax;
908 *pdi->item.pszText = 0; /* make sure we don't process garbage */
911 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
912 if (!pszTempBuf) return FALSE;
914 if (convertToUnicode)
915 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
916 pszTempBuf, cchTempBufMax);
917 else
918 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
919 cchTempBufMax, NULL, NULL);
921 savCchTextMax = pdi->item.cchTextMax;
922 savPszText = pdi->item.pszText;
923 pdi->item.pszText = pszTempBuf;
924 pdi->item.cchTextMax = cchTempBufMax;
927 if (infoPtr->notifyFormat == NFR_ANSI)
928 realNotifCode = get_ansi_notification(notificationCode);
929 else
930 realNotifCode = notificationCode;
931 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
932 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
934 if (convertToUnicode || convertToAnsi)
936 if (convertToUnicode) /* note : pointer can be changed by app ! */
937 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
938 savCchTextMax, NULL, NULL);
939 else
940 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
941 savPszText, savCchTextMax);
942 pdi->item.pszText = savPszText; /* restores our buffer */
943 pdi->item.cchTextMax = savCchTextMax;
944 Free (pszTempBuf);
946 return bResult;
949 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
950 const RECT *rcBounds, const LVITEMW *lplvItem)
952 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
953 lpnmlvcd->nmcd.hdc = hdc;
954 lpnmlvcd->nmcd.rc = *rcBounds;
955 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
956 lpnmlvcd->clrText = infoPtr->clrText;
957 if (!lplvItem) return;
958 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
959 lpnmlvcd->iSubItem = lplvItem->iSubItem;
960 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
961 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
962 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
963 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
966 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
968 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
969 DWORD result;
971 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
972 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
973 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
974 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
975 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
976 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
977 return result;
980 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
982 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
983 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
984 if (lpnmlvcd->clrText == CLR_DEFAULT)
985 lpnmlvcd->clrText = comctl32_color.clrWindowText;
987 /* apparently, for selected items, we have to override the returned values */
988 if (!SubItem)
990 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
992 if (infoPtr->bFocus)
994 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
995 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
997 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
999 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
1000 lpnmlvcd->clrText = comctl32_color.clrBtnText;
1005 /* Set the text attributes */
1006 if (lpnmlvcd->clrTextBk != CLR_NONE)
1008 SetBkMode(hdc, OPAQUE);
1009 SetBkColor(hdc,lpnmlvcd->clrTextBk);
1011 else
1012 SetBkMode(hdc, TRANSPARENT);
1013 SetTextColor(hdc, lpnmlvcd->clrText);
1016 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1018 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1021 /******** Item iterator functions **********************************/
1023 static RANGES ranges_create(int count);
1024 static void ranges_destroy(RANGES ranges);
1025 static BOOL ranges_add(RANGES ranges, RANGE range);
1026 static BOOL ranges_del(RANGES ranges, RANGE range);
1027 static void ranges_dump(RANGES ranges);
1029 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1031 RANGE range = { nItem, nItem + 1 };
1033 return ranges_add(ranges, range);
1036 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1038 RANGE range = { nItem, nItem + 1 };
1040 return ranges_del(ranges, range);
1043 /***
1044 * ITERATOR DOCUMENTATION
1046 * The iterator functions allow for easy, and convenient iteration
1047 * over items of interest in the list. Typically, you create a
1048 * iterator, use it, and destroy it, as such:
1049 * ITERATOR i;
1051 * iterator_xxxitems(&i, ...);
1052 * while (iterator_{prev,next}(&i)
1054 * //code which uses i.nItem
1056 * iterator_destroy(&i);
1058 * where xxx is either: framed, or visible.
1059 * Note that it is important that the code destroys the iterator
1060 * after it's done with it, as the creation of the iterator may
1061 * allocate memory, which thus needs to be freed.
1063 * You can iterate both forwards, and backwards through the list,
1064 * by using iterator_next or iterator_prev respectively.
1066 * Lower numbered items are draw on top of higher number items in
1067 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1068 * items may overlap). So, to test items, you should use
1069 * iterator_next
1070 * which lists the items top to bottom (in Z-order).
1071 * For drawing items, you should use
1072 * iterator_prev
1073 * which lists the items bottom to top (in Z-order).
1074 * If you keep iterating over the items after the end-of-items
1075 * marker (-1) is returned, the iterator will start from the
1076 * beginning. Typically, you don't need to test for -1,
1077 * because iterator_{next,prev} will return TRUE if more items
1078 * are to be iterated over, or FALSE otherwise.
1080 * Note: the iterator is defined to be bidirectional. That is,
1081 * any number of prev followed by any number of next, or
1082 * five versa, should leave the iterator at the same item:
1083 * prev * n, next * n = next * n, prev * n
1085 * The iterator has a notion of an out-of-order, special item,
1086 * which sits at the start of the list. This is used in
1087 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1088 * which needs to be first, as it may overlap other items.
1090 * The code is a bit messy because we have:
1091 * - a special item to deal with
1092 * - simple range, or composite range
1093 * - empty range.
1094 * If you find bugs, or want to add features, please make sure you
1095 * always check/modify *both* iterator_prev, and iterator_next.
1098 /****
1099 * This function iterates through the items in increasing order,
1100 * but prefixed by the special item, then -1. That is:
1101 * special, 1, 2, 3, ..., n, -1.
1102 * Each item is listed only once.
1104 static inline BOOL iterator_next(ITERATOR* i)
1106 if (i->nItem == -1)
1108 i->nItem = i->nSpecial;
1109 if (i->nItem != -1) return TRUE;
1111 if (i->nItem == i->nSpecial)
1113 if (i->ranges) i->index = 0;
1114 goto pickarange;
1117 i->nItem++;
1118 testitem:
1119 if (i->nItem == i->nSpecial) i->nItem++;
1120 if (i->nItem < i->range.upper) return TRUE;
1122 pickarange:
1123 if (i->ranges)
1125 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1126 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1127 else goto end;
1129 else if (i->nItem >= i->range.upper) goto end;
1131 i->nItem = i->range.lower;
1132 if (i->nItem >= 0) goto testitem;
1133 end:
1134 i->nItem = -1;
1135 return FALSE;
1138 /****
1139 * This function iterates through the items in decreasing order,
1140 * followed by the special item, then -1. That is:
1141 * n, n-1, ..., 3, 2, 1, special, -1.
1142 * Each item is listed only once.
1144 static inline BOOL iterator_prev(ITERATOR* i)
1146 BOOL start = FALSE;
1148 if (i->nItem == -1)
1150 start = TRUE;
1151 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1152 goto pickarange;
1154 if (i->nItem == i->nSpecial)
1156 i->nItem = -1;
1157 return FALSE;
1160 testitem:
1161 i->nItem--;
1162 if (i->nItem == i->nSpecial) i->nItem--;
1163 if (i->nItem >= i->range.lower) return TRUE;
1165 pickarange:
1166 if (i->ranges)
1168 if (i->index > 0)
1169 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1170 else goto end;
1172 else if (!start && i->nItem < i->range.lower) goto end;
1174 i->nItem = i->range.upper;
1175 if (i->nItem > 0) goto testitem;
1176 end:
1177 return (i->nItem = i->nSpecial) != -1;
1180 static RANGE iterator_range(const ITERATOR *i)
1182 RANGE range;
1184 if (!i->ranges) return i->range;
1186 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1188 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1189 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1191 else range.lower = range.upper = 0;
1193 return range;
1196 /***
1197 * Releases resources associated with this ierator.
1199 static inline void iterator_destroy(const ITERATOR *i)
1201 ranges_destroy(i->ranges);
1204 /***
1205 * Create an empty iterator.
1207 static inline BOOL iterator_empty(ITERATOR* i)
1209 ZeroMemory(i, sizeof(*i));
1210 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1211 return TRUE;
1214 /***
1215 * Create an iterator over a range.
1217 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1219 iterator_empty(i);
1220 i->range = range;
1221 return TRUE;
1224 /***
1225 * Create an iterator over a bunch of ranges.
1226 * Please note that the iterator will take ownership of the ranges,
1227 * and will free them upon destruction.
1229 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1231 iterator_empty(i);
1232 i->ranges = ranges;
1233 return TRUE;
1236 /***
1237 * Creates an iterator over the items which intersect lprc.
1239 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1241 RECT frame = *lprc, rcItem, rcTemp;
1242 POINT Origin;
1244 /* in case we fail, we want to return an empty iterator */
1245 if (!iterator_empty(i)) return FALSE;
1247 LISTVIEW_GetOrigin(infoPtr, &Origin);
1249 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1250 OffsetRect(&frame, -Origin.x, -Origin.y);
1252 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1254 INT nItem;
1256 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1258 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1259 if (IntersectRect(&rcTemp, &rcItem, lprc))
1260 i->nSpecial = infoPtr->nFocusedItem;
1262 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1263 /* to do better here, we need to have PosX, and PosY sorted */
1264 TRACE("building icon ranges:\n");
1265 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1267 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1268 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1269 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1270 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1271 if (IntersectRect(&rcTemp, &rcItem, &frame))
1272 ranges_additem(i->ranges, nItem);
1274 return TRUE;
1276 else if (infoPtr->uView == LV_VIEW_DETAILS)
1278 RANGE range;
1280 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1281 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1283 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1284 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1285 if (range.upper <= range.lower) return TRUE;
1286 if (!iterator_rangeitems(i, range)) return FALSE;
1287 TRACE(" report=%s\n", debugrange(&i->range));
1289 else
1291 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1292 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1293 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1294 INT nFirstCol;
1295 INT nLastCol;
1296 INT lower;
1297 RANGE item_range;
1298 INT nCol;
1300 if (infoPtr->nItemWidth)
1302 nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1303 nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1305 else
1307 nFirstCol = max(frame.left, 0);
1308 nLastCol = min(frame.right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1311 lower = nFirstCol * nPerCol + nFirstRow;
1313 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1314 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1316 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1318 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1319 TRACE("building list ranges:\n");
1320 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1322 item_range.lower = nCol * nPerCol + nFirstRow;
1323 if(item_range.lower >= infoPtr->nItemCount) break;
1324 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1325 TRACE(" list=%s\n", debugrange(&item_range));
1326 ranges_add(i->ranges, item_range);
1330 return TRUE;
1333 /***
1334 * Creates an iterator over the items which intersect the visible region of hdc.
1336 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1338 POINT Origin, Position;
1339 RECT rcItem, rcClip;
1340 INT rgntype;
1342 rgntype = GetClipBox(hdc, &rcClip);
1343 if (rgntype == NULLREGION) return iterator_empty(i);
1344 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1345 if (rgntype == SIMPLEREGION) return TRUE;
1347 /* first deal with the special item */
1348 if (i->nSpecial != -1)
1350 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1351 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1354 /* if we can't deal with the region, we'll just go with the simple range */
1355 LISTVIEW_GetOrigin(infoPtr, &Origin);
1356 TRACE("building visible range:\n");
1357 if (!i->ranges && i->range.lower < i->range.upper)
1359 if (!(i->ranges = ranges_create(50))) return TRUE;
1360 if (!ranges_add(i->ranges, i->range))
1362 ranges_destroy(i->ranges);
1363 i->ranges = 0;
1364 return TRUE;
1368 /* now delete the invisible items from the list */
1369 while(iterator_next(i))
1371 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1372 rcItem.left = Position.x + Origin.x;
1373 rcItem.top = Position.y + Origin.y;
1374 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1375 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1376 if (!RectVisible(hdc, &rcItem))
1377 ranges_delitem(i->ranges, i->nItem);
1379 /* the iterator should restart on the next iterator_next */
1380 TRACE("done\n");
1382 return TRUE;
1385 /******** Misc helper functions ************************************/
1387 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1388 WPARAM wParam, LPARAM lParam, BOOL isW)
1390 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1391 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1394 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1396 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1397 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1400 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1402 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1403 if(state == 1 || state == 2)
1405 LVITEMW lvitem;
1406 state ^= 3;
1407 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1408 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1409 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1413 /* this should be called after window style got updated,
1414 it used to reset view state to match current window style */
1415 static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1417 switch (infoPtr->dwStyle & LVS_TYPEMASK)
1419 case LVS_ICON:
1420 infoPtr->uView = LV_VIEW_ICON;
1421 break;
1422 case LVS_REPORT:
1423 infoPtr->uView = LV_VIEW_DETAILS;
1424 break;
1425 case LVS_SMALLICON:
1426 infoPtr->uView = LV_VIEW_SMALLICON;
1427 break;
1428 case LVS_LIST:
1429 infoPtr->uView = LV_VIEW_LIST;
1433 /******** Internal API functions ************************************/
1435 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1437 static COLUMN_INFO mainItem;
1439 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1440 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1441 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1444 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1446 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1447 HINSTANCE hInst;
1449 if (infoPtr->hwndHeader) return 0;
1451 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1453 /* setup creation flags */
1454 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1455 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1457 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1459 /* create header */
1460 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1461 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1462 if (!infoPtr->hwndHeader) return -1;
1464 /* set header unicode format */
1465 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1467 /* set header font */
1468 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1470 LISTVIEW_UpdateSize(infoPtr);
1472 return 0;
1475 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1477 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1480 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1482 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1485 /* used to handle collapse main item column case */
1486 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1488 return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1489 DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1492 /* Listview invalidation functions: use _only_ these functions to invalidate */
1494 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1496 return infoPtr->bRedraw;
1499 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1501 if(!is_redrawing(infoPtr)) return;
1502 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1503 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1506 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1508 RECT rcBox;
1510 if(!is_redrawing(infoPtr)) return;
1511 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1512 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1515 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1517 POINT Origin, Position;
1518 RECT rcBox;
1520 if(!is_redrawing(infoPtr)) return;
1521 assert (infoPtr->uView == LV_VIEW_DETAILS);
1522 LISTVIEW_GetOrigin(infoPtr, &Origin);
1523 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1524 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1525 rcBox.top = 0;
1526 rcBox.bottom = infoPtr->nItemHeight;
1527 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1528 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1531 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1533 LISTVIEW_InvalidateRect(infoPtr, NULL);
1536 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1538 RECT rcCol;
1540 if(!is_redrawing(infoPtr)) return;
1541 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1542 rcCol.top = infoPtr->rcList.top;
1543 rcCol.bottom = infoPtr->rcList.bottom;
1544 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1547 /***
1548 * DESCRIPTION:
1549 * Retrieves the number of items that can fit vertically in the client area.
1551 * PARAMETER(S):
1552 * [I] infoPtr : valid pointer to the listview structure
1554 * RETURN:
1555 * Number of items per row.
1557 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1559 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1561 return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1564 /***
1565 * DESCRIPTION:
1566 * Retrieves the number of items that can fit horizontally in the client
1567 * area.
1569 * PARAMETER(S):
1570 * [I] infoPtr : valid pointer to the listview structure
1572 * RETURN:
1573 * Number of items per column.
1575 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1577 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1579 return max(nListHeight / infoPtr->nItemHeight, 1);
1583 /*************************************************************************
1584 * LISTVIEW_ProcessLetterKeys
1586 * Processes keyboard messages generated by pressing the letter keys
1587 * on the keyboard.
1588 * What this does is perform a case insensitive search from the
1589 * current position with the following quirks:
1590 * - If two chars or more are pressed in quick succession we search
1591 * for the corresponding string (e.g. 'abc').
1592 * - If there is a delay we wipe away the current search string and
1593 * restart with just that char.
1594 * - If the user keeps pressing the same character, whether slowly or
1595 * fast, so that the search string is entirely composed of this
1596 * character ('aaaaa' for instance), then we search for first item
1597 * that starting with that character.
1598 * - If the user types the above character in quick succession, then
1599 * we must also search for the corresponding string ('aaaaa'), and
1600 * go to that string if there is a match.
1602 * PARAMETERS
1603 * [I] hwnd : handle to the window
1604 * [I] charCode : the character code, the actual character
1605 * [I] keyData : key data
1607 * RETURNS
1609 * Zero.
1611 * BUGS
1613 * - The current implementation has a list of characters it will
1614 * accept and it ignores everything else. In particular it will
1615 * ignore accentuated characters which seems to match what
1616 * Windows does. But I'm not sure it makes sense to follow
1617 * Windows there.
1618 * - We don't sound a beep when the search fails.
1620 * SEE ALSO
1622 * TREEVIEW_ProcessLetterKeys
1624 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1626 INT nItem;
1627 INT endidx,idx;
1628 LVITEMW item;
1629 WCHAR buffer[MAX_PATH];
1630 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1632 /* simple parameter checking */
1633 if (!charCode || !keyData) return 0;
1635 /* only allow the valid WM_CHARs through */
1636 if (!isalnumW(charCode) &&
1637 charCode != '.' && charCode != '`' && charCode != '!' &&
1638 charCode != '@' && charCode != '#' && charCode != '$' &&
1639 charCode != '%' && charCode != '^' && charCode != '&' &&
1640 charCode != '*' && charCode != '(' && charCode != ')' &&
1641 charCode != '-' && charCode != '_' && charCode != '+' &&
1642 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1643 charCode != '}' && charCode != '[' && charCode != '{' &&
1644 charCode != '/' && charCode != '?' && charCode != '>' &&
1645 charCode != '<' && charCode != ',' && charCode != '~')
1646 return 0;
1648 /* if there's one item or less, there is no where to go */
1649 if (infoPtr->nItemCount <= 1) return 0;
1651 /* update the search parameters */
1652 infoPtr->lastKeyPressTimestamp = GetTickCount();
1653 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1654 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1655 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1656 if (infoPtr->charCode != charCode)
1657 infoPtr->charCode = charCode = 0;
1658 } else {
1659 infoPtr->charCode=charCode;
1660 infoPtr->szSearchParam[0]=charCode;
1661 infoPtr->nSearchParamLength=1;
1662 /* Redundant with the 1 char string */
1663 charCode=0;
1666 /* and search from the current position */
1667 nItem=-1;
1668 if (infoPtr->nFocusedItem >= 0) {
1669 endidx=infoPtr->nFocusedItem;
1670 idx=endidx;
1671 /* if looking for single character match,
1672 * then we must always move forward
1674 if (infoPtr->nSearchParamLength == 1)
1675 idx++;
1676 } else {
1677 endidx=infoPtr->nItemCount;
1678 idx=0;
1681 /* Let application handle this for virtual listview */
1682 if (infoPtr->dwStyle & LVS_OWNERDATA)
1684 NMLVFINDITEMW nmlv;
1685 LVFINDINFOW lvfi;
1687 ZeroMemory(&lvfi, sizeof(lvfi));
1688 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1689 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1690 lvfi.psz = infoPtr->szSearchParam;
1691 nmlv.iStart = idx;
1692 nmlv.lvfi = lvfi;
1694 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1696 if (nItem != -1)
1697 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1699 return 0;
1702 do {
1703 if (idx == infoPtr->nItemCount) {
1704 if (endidx == infoPtr->nItemCount || endidx == 0)
1705 break;
1706 idx=0;
1709 /* get item */
1710 item.mask = LVIF_TEXT;
1711 item.iItem = idx;
1712 item.iSubItem = 0;
1713 item.pszText = buffer;
1714 item.cchTextMax = MAX_PATH;
1715 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1717 /* check for a match */
1718 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1719 nItem=idx;
1720 break;
1721 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1722 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1723 /* This would work but we must keep looking for a longer match */
1724 nItem=idx;
1726 idx++;
1727 } while (idx != endidx);
1729 if (nItem != -1)
1730 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1732 return 0;
1735 /*************************************************************************
1736 * LISTVIEW_UpdateHeaderSize [Internal]
1738 * Function to resize the header control
1740 * PARAMS
1741 * [I] hwnd : handle to a window
1742 * [I] nNewScrollPos : scroll pos to set
1744 * RETURNS
1745 * None.
1747 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1749 RECT winRect;
1750 POINT point[2];
1752 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1754 if (!infoPtr->hwndHeader) return;
1756 GetWindowRect(infoPtr->hwndHeader, &winRect);
1757 point[0].x = winRect.left;
1758 point[0].y = winRect.top;
1759 point[1].x = winRect.right;
1760 point[1].y = winRect.bottom;
1762 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1763 point[0].x = -nNewScrollPos;
1764 point[1].x += nNewScrollPos;
1766 SetWindowPos(infoPtr->hwndHeader,0,
1767 point[0].x,point[0].y,point[1].x,point[1].y,
1768 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1769 SWP_NOZORDER | SWP_NOACTIVATE);
1772 /***
1773 * DESCRIPTION:
1774 * Update the scrollbars. This functions should be called whenever
1775 * the content, size or view changes.
1777 * PARAMETER(S):
1778 * [I] infoPtr : valid pointer to the listview structure
1780 * RETURN:
1781 * None
1783 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1785 SCROLLINFO horzInfo, vertInfo;
1786 INT dx, dy;
1788 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1790 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1791 horzInfo.cbSize = sizeof(SCROLLINFO);
1792 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1794 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1795 if (infoPtr->uView == LV_VIEW_LIST)
1797 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1798 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1800 /* scroll by at least one column per page */
1801 if(horzInfo.nPage < infoPtr->nItemWidth)
1802 horzInfo.nPage = infoPtr->nItemWidth;
1804 if (infoPtr->nItemWidth)
1805 horzInfo.nPage /= infoPtr->nItemWidth;
1807 else if (infoPtr->uView == LV_VIEW_DETAILS)
1809 horzInfo.nMax = infoPtr->nItemWidth;
1811 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1813 RECT rcView;
1815 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1818 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1819 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1820 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1821 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1822 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1824 /* Setting the horizontal scroll can change the listview size
1825 * (and potentially everything else) so we need to recompute
1826 * everything again for the vertical scroll
1829 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1830 vertInfo.cbSize = sizeof(SCROLLINFO);
1831 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1833 if (infoPtr->uView == LV_VIEW_DETAILS)
1835 vertInfo.nMax = infoPtr->nItemCount;
1837 /* scroll by at least one page */
1838 if(vertInfo.nPage < infoPtr->nItemHeight)
1839 vertInfo.nPage = infoPtr->nItemHeight;
1841 if (infoPtr->nItemHeight > 0)
1842 vertInfo.nPage /= infoPtr->nItemHeight;
1844 else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1846 RECT rcView;
1848 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1851 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1852 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1853 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1854 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1855 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1857 /* Change of the range may have changed the scroll pos. If so move the content */
1858 if (dx != 0 || dy != 0)
1860 RECT listRect;
1861 listRect = infoPtr->rcList;
1862 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1863 SW_ERASE | SW_INVALIDATE);
1866 /* Update the Header Control */
1867 if (infoPtr->uView == LV_VIEW_DETAILS)
1869 horzInfo.fMask = SIF_POS;
1870 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1871 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1876 /***
1877 * DESCRIPTION:
1878 * Shows/hides the focus rectangle.
1880 * PARAMETER(S):
1881 * [I] infoPtr : valid pointer to the listview structure
1882 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1884 * RETURN:
1885 * None
1887 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1889 HDC hdc;
1891 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1893 if (infoPtr->nFocusedItem < 0) return;
1895 /* we need some gymnastics in ICON mode to handle large items */
1896 if (infoPtr->uView == LV_VIEW_ICON)
1898 RECT rcBox;
1900 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1901 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1903 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1904 return;
1908 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1910 /* for some reason, owner draw should work only in report mode */
1911 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
1913 DRAWITEMSTRUCT dis;
1914 LVITEMW item;
1916 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1917 HFONT hOldFont = SelectObject(hdc, hFont);
1919 item.iItem = infoPtr->nFocusedItem;
1920 item.iSubItem = 0;
1921 item.mask = LVIF_PARAM;
1922 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1924 ZeroMemory(&dis, sizeof(dis));
1925 dis.CtlType = ODT_LISTVIEW;
1926 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1927 dis.itemID = item.iItem;
1928 dis.itemAction = ODA_FOCUS;
1929 if (fShow) dis.itemState |= ODS_FOCUS;
1930 dis.hwndItem = infoPtr->hwndSelf;
1931 dis.hDC = hdc;
1932 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1933 dis.itemData = item.lParam;
1935 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1937 SelectObject(hdc, hOldFont);
1939 else
1941 LISTVIEW_DrawFocusRect(infoPtr, hdc);
1943 done:
1944 ReleaseDC(infoPtr->hwndSelf, hdc);
1947 /***
1948 * Invalidates all visible selected items.
1950 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1952 ITERATOR i;
1954 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1955 while(iterator_next(&i))
1957 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1958 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1960 iterator_destroy(&i);
1964 /***
1965 * DESCRIPTION: [INTERNAL]
1966 * Computes an item's (left,top) corner, relative to rcView.
1967 * That is, the position has NOT been made relative to the Origin.
1968 * This is deliberate, to avoid computing the Origin over, and
1969 * over again, when this function is called in a loop. Instead,
1970 * one can factor the computation of the Origin before the loop,
1971 * and offset the value returned by this function, on every iteration.
1973 * PARAMETER(S):
1974 * [I] infoPtr : valid pointer to the listview structure
1975 * [I] nItem : item number
1976 * [O] lpptOrig : item top, left corner
1978 * RETURN:
1979 * None.
1981 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1983 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1985 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
1987 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1988 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1990 else if (infoPtr->uView == LV_VIEW_LIST)
1992 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1993 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1994 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1996 else /* LV_VIEW_DETAILS */
1998 lpptPosition->x = REPORT_MARGINX;
1999 /* item is always at zero indexed column */
2000 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2001 lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
2002 lpptPosition->y = nItem * infoPtr->nItemHeight;
2006 /***
2007 * DESCRIPTION: [INTERNAL]
2008 * Compute the rectangles of an item. This is to localize all
2009 * the computations in one place. If you are not interested in some
2010 * of these values, simply pass in a NULL -- the function is smart
2011 * enough to compute only what's necessary. The function computes
2012 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2013 * one, the BOX rectangle. This rectangle is very cheap to compute,
2014 * and is guaranteed to contain all the other rectangles. Computing
2015 * the ICON rect is also cheap, but all the others are potentially
2016 * expensive. This gives an easy and effective optimization when
2017 * searching (like point inclusion, or rectangle intersection):
2018 * first test against the BOX, and if TRUE, test against the desired
2019 * rectangle.
2020 * If the function does not have all the necessary information
2021 * to computed the requested rectangles, will crash with a
2022 * failed assertion. This is done so we catch all programming
2023 * errors, given that the function is called only from our code.
2025 * We have the following 'special' meanings for a few fields:
2026 * * If LVIS_FOCUSED is set, we assume the item has the focus
2027 * This is important in ICON mode, where it might get a larger
2028 * then usual rectangle
2030 * Please note that subitem support works only in REPORT mode.
2032 * PARAMETER(S):
2033 * [I] infoPtr : valid pointer to the listview structure
2034 * [I] lpLVItem : item to compute the measures for
2035 * [O] lprcBox : ptr to Box rectangle
2036 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2037 * [0] lprcSelectBox : ptr to select box rectangle
2038 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2039 * [O] lprcIcon : ptr to Icon rectangle
2040 * Same as LVM_GETITEMRECT with LVIR_ICON
2041 * [O] lprcStateIcon: ptr to State Icon rectangle
2042 * [O] lprcLabel : ptr to Label rectangle
2043 * Same as LVM_GETITEMRECT with LVIR_LABEL
2045 * RETURN:
2046 * None.
2048 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
2049 LPRECT lprcBox, LPRECT lprcSelectBox,
2050 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2052 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2053 RECT Box, SelectBox, Icon, Label;
2054 COLUMN_INFO *lpColumnInfo = NULL;
2055 SIZE labelSize = { 0, 0 };
2057 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2059 /* Be smart and try to figure out the minimum we have to do */
2060 if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2061 if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2063 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2064 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2066 if (lprcSelectBox) doSelectBox = TRUE;
2067 if (lprcLabel) doLabel = TRUE;
2068 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2069 if (doSelectBox)
2071 doIcon = TRUE;
2072 doLabel = TRUE;
2075 /************************************************************/
2076 /* compute the box rectangle (it should be cheap to do) */
2077 /************************************************************/
2078 if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2079 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2081 if (lpLVItem->iSubItem)
2083 Box = lpColumnInfo->rcHeader;
2085 else
2087 Box.left = 0;
2088 Box.right = infoPtr->nItemWidth;
2090 Box.top = 0;
2091 Box.bottom = infoPtr->nItemHeight;
2093 /******************************************************************/
2094 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2095 /******************************************************************/
2096 if (doIcon)
2098 LONG state_width = 0;
2100 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2101 state_width = infoPtr->iconStateSize.cx;
2103 if (infoPtr->uView == LV_VIEW_ICON)
2105 Icon.left = Box.left + state_width;
2106 if (infoPtr->himlNormal)
2107 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2108 Icon.top = Box.top + ICON_TOP_PADDING;
2109 Icon.right = Icon.left;
2110 Icon.bottom = Icon.top;
2111 if (infoPtr->himlNormal)
2113 Icon.right += infoPtr->iconSize.cx;
2114 Icon.bottom += infoPtr->iconSize.cy;
2117 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2119 Icon.left = Box.left + state_width;
2121 if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2123 /* we need the indent in report mode */
2124 assert(lpLVItem->mask & LVIF_INDENT);
2125 Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2128 Icon.top = Box.top;
2129 Icon.right = Icon.left;
2130 if (infoPtr->himlSmall &&
2131 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2132 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2133 Icon.right += infoPtr->iconSize.cx;
2134 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2136 if(lprcIcon) *lprcIcon = Icon;
2137 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2139 /* TODO: is this correct? */
2140 if (lprcStateIcon)
2142 lprcStateIcon->left = Icon.left - state_width;
2143 lprcStateIcon->right = Icon.left;
2144 lprcStateIcon->top = Icon.top;
2145 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2146 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2149 else Icon.right = 0;
2151 /************************************************************/
2152 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2153 /************************************************************/
2154 if (doLabel)
2156 /* calculate how far to the right can the label stretch */
2157 Label.right = Box.right;
2158 if (infoPtr->uView == LV_VIEW_DETAILS)
2160 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2163 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2165 labelSize.cx = infoPtr->nItemWidth;
2166 labelSize.cy = infoPtr->nItemHeight;
2167 goto calc_label;
2170 /* we need the text in non owner draw mode */
2171 assert(lpLVItem->mask & LVIF_TEXT);
2172 if (is_textT(lpLVItem->pszText, TRUE))
2174 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2175 HDC hdc = GetDC(infoPtr->hwndSelf);
2176 HFONT hOldFont = SelectObject(hdc, hFont);
2177 UINT uFormat;
2178 RECT rcText;
2180 /* compute rough rectangle where the label will go */
2181 SetRectEmpty(&rcText);
2182 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2183 rcText.bottom = infoPtr->nItemHeight;
2184 if (infoPtr->uView == LV_VIEW_ICON)
2185 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2187 /* now figure out the flags */
2188 if (infoPtr->uView == LV_VIEW_ICON)
2189 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2190 else
2191 uFormat = LV_SL_DT_FLAGS;
2193 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2195 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2196 labelSize.cy = rcText.bottom - rcText.top;
2198 SelectObject(hdc, hOldFont);
2199 ReleaseDC(infoPtr->hwndSelf, hdc);
2202 calc_label:
2203 if (infoPtr->uView == LV_VIEW_ICON)
2205 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2206 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2207 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2208 Label.right = Label.left + labelSize.cx;
2209 Label.bottom = Label.top + infoPtr->nItemHeight;
2210 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2212 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2213 labelSize.cy /= infoPtr->ntmHeight;
2214 labelSize.cy = max(labelSize.cy, 1);
2215 labelSize.cy *= infoPtr->ntmHeight;
2217 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2219 else if (infoPtr->uView == LV_VIEW_DETAILS)
2221 Label.left = Icon.right;
2222 Label.top = Box.top;
2223 Label.right = lpColumnInfo->rcHeader.right;
2224 Label.bottom = Label.top + infoPtr->nItemHeight;
2226 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2228 Label.left = Icon.right;
2229 Label.top = Box.top;
2230 Label.right = min(Label.left + labelSize.cx, Label.right);
2231 Label.bottom = Label.top + infoPtr->nItemHeight;
2234 if (lprcLabel) *lprcLabel = Label;
2235 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2238 /************************************************************/
2239 /* compute SELECT bounding box */
2240 /************************************************************/
2241 if (doSelectBox)
2243 if (infoPtr->uView == LV_VIEW_DETAILS)
2245 SelectBox.left = Icon.left;
2246 SelectBox.top = Box.top;
2247 SelectBox.bottom = Box.bottom;
2248 SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2250 else
2252 UnionRect(&SelectBox, &Icon, &Label);
2254 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2255 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2258 /* Fix the Box if necessary */
2259 if (lprcBox)
2261 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2262 else *lprcBox = Box;
2264 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2267 /***
2268 * DESCRIPTION: [INTERNAL]
2270 * PARAMETER(S):
2271 * [I] infoPtr : valid pointer to the listview structure
2272 * [I] nItem : item number
2273 * [O] lprcBox : ptr to Box rectangle
2275 * RETURN:
2276 * None.
2278 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2280 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2281 POINT Position, Origin;
2282 LVITEMW lvItem;
2284 LISTVIEW_GetOrigin(infoPtr, &Origin);
2285 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2287 /* Be smart and try to figure out the minimum we have to do */
2288 lvItem.mask = 0;
2289 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2290 lvItem.mask |= LVIF_TEXT;
2291 lvItem.iItem = nItem;
2292 lvItem.iSubItem = 0;
2293 lvItem.pszText = szDispText;
2294 lvItem.cchTextMax = DISP_TEXT_SIZE;
2295 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2296 if (infoPtr->uView == LV_VIEW_ICON)
2298 lvItem.mask |= LVIF_STATE;
2299 lvItem.stateMask = LVIS_FOCUSED;
2300 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2302 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2304 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
2305 SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))
2307 OffsetRect(lprcBox, Origin.x, Position.y + Origin.y);
2309 else
2310 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2314 /***
2315 * DESCRIPTION:
2316 * Returns the current icon position, and advances it along the top.
2317 * The returned position is not offset by Origin.
2319 * PARAMETER(S):
2320 * [I] infoPtr : valid pointer to the listview structure
2321 * [O] lpPos : will get the current icon position
2323 * RETURN:
2324 * None
2326 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2328 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2330 *lpPos = infoPtr->currIconPos;
2332 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2333 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2335 infoPtr->currIconPos.x = 0;
2336 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2340 /***
2341 * DESCRIPTION:
2342 * Returns the current icon position, and advances it down the left edge.
2343 * The returned position is not offset by Origin.
2345 * PARAMETER(S):
2346 * [I] infoPtr : valid pointer to the listview structure
2347 * [O] lpPos : will get the current icon position
2349 * RETURN:
2350 * None
2352 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2354 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2356 *lpPos = infoPtr->currIconPos;
2358 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2359 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2361 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2362 infoPtr->currIconPos.y = 0;
2366 /***
2367 * DESCRIPTION:
2368 * Moves an icon to the specified position.
2369 * It takes care of invalidating the item, etc.
2371 * PARAMETER(S):
2372 * [I] infoPtr : valid pointer to the listview structure
2373 * [I] nItem : the item to move
2374 * [I] lpPos : the new icon position
2375 * [I] isNew : flags the item as being new
2377 * RETURN:
2378 * Success: TRUE
2379 * Failure: FALSE
2381 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2383 POINT old;
2385 if (!isNew)
2387 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2388 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2390 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2391 LISTVIEW_InvalidateItem(infoPtr, nItem);
2394 /* Allocating a POINTER for every item is too resource intensive,
2395 * so we'll keep the (x,y) in different arrays */
2396 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2397 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2399 LISTVIEW_InvalidateItem(infoPtr, nItem);
2401 return TRUE;
2404 /***
2405 * DESCRIPTION:
2406 * Arranges listview items in icon display mode.
2408 * PARAMETER(S):
2409 * [I] infoPtr : valid pointer to the listview structure
2410 * [I] nAlignCode : alignment code
2412 * RETURN:
2413 * SUCCESS : TRUE
2414 * FAILURE : FALSE
2416 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2418 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2419 POINT pos;
2420 INT i;
2422 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2424 TRACE("nAlignCode=%d\n", nAlignCode);
2426 if (nAlignCode == LVA_DEFAULT)
2428 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2429 else nAlignCode = LVA_ALIGNTOP;
2432 switch (nAlignCode)
2434 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2435 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2436 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2437 default: return FALSE;
2440 infoPtr->bAutoarrange = TRUE;
2441 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2442 for (i = 0; i < infoPtr->nItemCount; i++)
2444 next_pos(infoPtr, &pos);
2445 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2448 return TRUE;
2451 /***
2452 * DESCRIPTION:
2453 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2454 * For LVS_REPORT always returns empty rectangle.
2456 * PARAMETER(S):
2457 * [I] infoPtr : valid pointer to the listview structure
2458 * [O] lprcView : bounding rectangle
2460 * RETURN:
2461 * SUCCESS : TRUE
2462 * FAILURE : FALSE
2464 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2466 INT i, x, y;
2468 SetRectEmpty(lprcView);
2470 switch (infoPtr->uView)
2472 case LV_VIEW_ICON:
2473 case LV_VIEW_SMALLICON:
2474 for (i = 0; i < infoPtr->nItemCount; i++)
2476 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2477 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2478 lprcView->right = max(lprcView->right, x);
2479 lprcView->bottom = max(lprcView->bottom, y);
2481 if (infoPtr->nItemCount > 0)
2483 lprcView->right += infoPtr->nItemWidth;
2484 lprcView->bottom += infoPtr->nItemHeight;
2486 break;
2488 case LV_VIEW_LIST:
2489 y = LISTVIEW_GetCountPerColumn(infoPtr);
2490 x = infoPtr->nItemCount / y;
2491 if (infoPtr->nItemCount % y) x++;
2492 lprcView->right = x * infoPtr->nItemWidth;
2493 lprcView->bottom = y * infoPtr->nItemHeight;
2494 break;
2498 /***
2499 * DESCRIPTION:
2500 * Retrieves the bounding rectangle of all the items.
2502 * PARAMETER(S):
2503 * [I] infoPtr : valid pointer to the listview structure
2504 * [O] lprcView : bounding rectangle
2506 * RETURN:
2507 * SUCCESS : TRUE
2508 * FAILURE : FALSE
2510 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2512 POINT ptOrigin;
2514 TRACE("(lprcView=%p)\n", lprcView);
2516 if (!lprcView) return FALSE;
2518 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2520 if (infoPtr->uView != LV_VIEW_DETAILS)
2522 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2523 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2526 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2528 return TRUE;
2531 /***
2532 * DESCRIPTION:
2533 * Retrieves the subitem pointer associated with the subitem index.
2535 * PARAMETER(S):
2536 * [I] hdpaSubItems : DPA handle for a specific item
2537 * [I] nSubItem : index of subitem
2539 * RETURN:
2540 * SUCCESS : subitem pointer
2541 * FAILURE : NULL
2543 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2545 SUBITEM_INFO *lpSubItem;
2546 INT i;
2548 /* we should binary search here if need be */
2549 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2551 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2552 if (lpSubItem->iSubItem == nSubItem)
2553 return lpSubItem;
2556 return NULL;
2560 /***
2561 * DESCRIPTION:
2562 * Calculates the desired item width.
2564 * PARAMETER(S):
2565 * [I] infoPtr : valid pointer to the listview structure
2567 * RETURN:
2568 * The desired item width.
2570 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2572 INT nItemWidth = 0;
2574 TRACE("uView=%d\n", infoPtr->uView);
2576 if (infoPtr->uView == LV_VIEW_ICON)
2577 nItemWidth = infoPtr->iconSpacing.cx;
2578 else if (infoPtr->uView == LV_VIEW_DETAILS)
2580 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2582 RECT rcHeader;
2583 INT index;
2585 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2586 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2588 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2589 nItemWidth = rcHeader.right;
2592 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2594 INT i;
2596 for (i = 0; i < infoPtr->nItemCount; i++)
2597 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2599 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2600 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2602 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2605 return nItemWidth;
2608 /***
2609 * DESCRIPTION:
2610 * Calculates the desired item height.
2612 * PARAMETER(S):
2613 * [I] infoPtr : valid pointer to the listview structure
2615 * RETURN:
2616 * The desired item height.
2618 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2620 INT nItemHeight;
2622 TRACE("uView=%d\n", infoPtr->uView);
2624 if (infoPtr->uView == LV_VIEW_ICON)
2625 nItemHeight = infoPtr->iconSpacing.cy;
2626 else
2628 nItemHeight = infoPtr->ntmHeight;
2629 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2630 nItemHeight++;
2631 if (infoPtr->himlState)
2632 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2633 if (infoPtr->himlSmall)
2634 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2635 if (infoPtr->himlState || infoPtr->himlSmall)
2636 nItemHeight += HEIGHT_PADDING;
2637 if (infoPtr->nMeasureItemHeight > 0)
2638 nItemHeight = infoPtr->nMeasureItemHeight;
2641 return max(nItemHeight, 1);
2644 /***
2645 * DESCRIPTION:
2646 * Updates the width, and height of an item.
2648 * PARAMETER(S):
2649 * [I] infoPtr : valid pointer to the listview structure
2651 * RETURN:
2652 * None.
2654 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2656 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2657 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2661 /***
2662 * DESCRIPTION:
2663 * Retrieves and saves important text metrics info for the current
2664 * Listview font.
2666 * PARAMETER(S):
2667 * [I] infoPtr : valid pointer to the listview structure
2670 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2672 HDC hdc = GetDC(infoPtr->hwndSelf);
2673 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2674 HFONT hOldFont = SelectObject(hdc, hFont);
2675 TEXTMETRICW tm;
2676 SIZE sz;
2678 if (GetTextMetricsW(hdc, &tm))
2680 infoPtr->ntmHeight = tm.tmHeight;
2681 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2684 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2685 infoPtr->nEllipsisWidth = sz.cx;
2687 SelectObject(hdc, hOldFont);
2688 ReleaseDC(infoPtr->hwndSelf, hdc);
2690 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2693 /***
2694 * DESCRIPTION:
2695 * A compare function for ranges
2697 * PARAMETER(S)
2698 * [I] range1 : pointer to range 1;
2699 * [I] range2 : pointer to range 2;
2700 * [I] flags : flags
2702 * RETURNS:
2703 * > 0 : if range 1 > range 2
2704 * < 0 : if range 2 > range 1
2705 * = 0 : if range intersects range 2
2707 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2709 INT cmp;
2711 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2712 cmp = -1;
2713 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2714 cmp = 1;
2715 else
2716 cmp = 0;
2718 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2720 return cmp;
2723 #if DEBUG_RANGES
2724 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2725 #else
2726 #define ranges_check(ranges, desc) do { } while(0)
2727 #endif
2729 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2731 INT i;
2732 RANGE *prev, *curr;
2734 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2735 assert (ranges);
2736 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2737 ranges_dump(ranges);
2738 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2740 prev = DPA_GetPtr(ranges->hdpa, 0);
2741 assert (prev->lower >= 0 && prev->lower < prev->upper);
2742 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2744 curr = DPA_GetPtr(ranges->hdpa, i);
2745 assert (prev->upper <= curr->lower);
2746 assert (curr->lower < curr->upper);
2747 prev = curr;
2750 TRACE("--- Done checking---\n");
2753 static RANGES ranges_create(int count)
2755 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2756 if (!ranges) return NULL;
2757 ranges->hdpa = DPA_Create(count);
2758 if (ranges->hdpa) return ranges;
2759 Free(ranges);
2760 return NULL;
2763 static void ranges_clear(RANGES ranges)
2765 INT i;
2767 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2768 Free(DPA_GetPtr(ranges->hdpa, i));
2769 DPA_DeleteAllPtrs(ranges->hdpa);
2773 static void ranges_destroy(RANGES ranges)
2775 if (!ranges) return;
2776 ranges_clear(ranges);
2777 DPA_Destroy(ranges->hdpa);
2778 Free(ranges);
2781 static RANGES ranges_clone(RANGES ranges)
2783 RANGES clone;
2784 INT i;
2786 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2788 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2790 RANGE *newrng = Alloc(sizeof(RANGE));
2791 if (!newrng) goto fail;
2792 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2793 DPA_SetPtr(clone->hdpa, i, newrng);
2795 return clone;
2797 fail:
2798 TRACE ("clone failed\n");
2799 ranges_destroy(clone);
2800 return NULL;
2803 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2805 INT i;
2807 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2808 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2810 return ranges;
2813 static void ranges_dump(RANGES ranges)
2815 INT i;
2817 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2818 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2821 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2823 RANGE srchrng = { nItem, nItem + 1 };
2825 TRACE("(nItem=%d)\n", nItem);
2826 ranges_check(ranges, "before contain");
2827 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2830 static INT ranges_itemcount(RANGES ranges)
2832 INT i, count = 0;
2834 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2836 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2837 count += sel->upper - sel->lower;
2840 return count;
2843 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2845 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2846 INT index;
2848 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2849 if (index == -1) return TRUE;
2851 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2853 chkrng = DPA_GetPtr(ranges->hdpa, index);
2854 if (chkrng->lower >= nItem)
2855 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2856 if (chkrng->upper > nItem)
2857 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2859 return TRUE;
2862 static BOOL ranges_add(RANGES ranges, RANGE range)
2864 RANGE srchrgn;
2865 INT index;
2867 TRACE("(%s)\n", debugrange(&range));
2868 ranges_check(ranges, "before add");
2870 /* try find overlapping regions first */
2871 srchrgn.lower = range.lower - 1;
2872 srchrgn.upper = range.upper + 1;
2873 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2875 if (index == -1)
2877 RANGE *newrgn;
2879 TRACE("Adding new range\n");
2881 /* create the brand new range to insert */
2882 newrgn = Alloc(sizeof(RANGE));
2883 if(!newrgn) goto fail;
2884 *newrgn = range;
2886 /* figure out where to insert it */
2887 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2888 TRACE("index=%d\n", index);
2889 if (index == -1) index = 0;
2891 /* and get it over with */
2892 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2894 Free(newrgn);
2895 goto fail;
2898 else
2900 RANGE *chkrgn, *mrgrgn;
2901 INT fromindex, mergeindex;
2903 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2904 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2906 chkrgn->lower = min(range.lower, chkrgn->lower);
2907 chkrgn->upper = max(range.upper, chkrgn->upper);
2909 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2911 /* merge now common ranges */
2912 fromindex = 0;
2913 srchrgn.lower = chkrgn->lower - 1;
2914 srchrgn.upper = chkrgn->upper + 1;
2918 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2919 if (mergeindex == -1) break;
2920 if (mergeindex == index)
2922 fromindex = index + 1;
2923 continue;
2926 TRACE("Merge with index %i\n", mergeindex);
2928 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2929 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2930 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2931 Free(mrgrgn);
2932 DPA_DeletePtr(ranges->hdpa, mergeindex);
2933 if (mergeindex < index) index --;
2934 } while(1);
2937 ranges_check(ranges, "after add");
2938 return TRUE;
2940 fail:
2941 ranges_check(ranges, "failed add");
2942 return FALSE;
2945 static BOOL ranges_del(RANGES ranges, RANGE range)
2947 RANGE *chkrgn;
2948 INT index;
2950 TRACE("(%s)\n", debugrange(&range));
2951 ranges_check(ranges, "before del");
2953 /* we don't use DPAS_SORTED here, since we need *
2954 * to find the first overlapping range */
2955 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2956 while(index != -1)
2958 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2960 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2962 /* case 1: Same range */
2963 if ( (chkrgn->upper == range.upper) &&
2964 (chkrgn->lower == range.lower) )
2966 DPA_DeletePtr(ranges->hdpa, index);
2967 break;
2969 /* case 2: engulf */
2970 else if ( (chkrgn->upper <= range.upper) &&
2971 (chkrgn->lower >= range.lower) )
2973 DPA_DeletePtr(ranges->hdpa, index);
2975 /* case 3: overlap upper */
2976 else if ( (chkrgn->upper <= range.upper) &&
2977 (chkrgn->lower < range.lower) )
2979 chkrgn->upper = range.lower;
2981 /* case 4: overlap lower */
2982 else if ( (chkrgn->upper > range.upper) &&
2983 (chkrgn->lower >= range.lower) )
2985 chkrgn->lower = range.upper;
2986 break;
2988 /* case 5: fully internal */
2989 else
2991 RANGE tmprgn = *chkrgn, *newrgn;
2993 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2994 newrgn->lower = chkrgn->lower;
2995 newrgn->upper = range.lower;
2996 chkrgn->lower = range.upper;
2997 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2999 Free(newrgn);
3000 goto fail;
3002 chkrgn = &tmprgn;
3003 break;
3006 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
3009 ranges_check(ranges, "after del");
3010 return TRUE;
3012 fail:
3013 ranges_check(ranges, "failed del");
3014 return FALSE;
3017 /***
3018 * DESCRIPTION:
3019 * Removes all selection ranges
3021 * Parameters(s):
3022 * [I] infoPtr : valid pointer to the listview structure
3023 * [I] toSkip : item range to skip removing the selection
3025 * RETURNS:
3026 * SUCCESS : TRUE
3027 * FAILURE : FALSE
3029 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
3031 LVITEMW lvItem;
3032 ITERATOR i;
3033 RANGES clone;
3035 TRACE("()\n");
3037 lvItem.state = 0;
3038 lvItem.stateMask = LVIS_SELECTED;
3040 /* need to clone the DPA because callbacks can change it */
3041 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
3042 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
3043 while(iterator_next(&i))
3044 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
3045 /* note that the iterator destructor will free the cloned range */
3046 iterator_destroy(&i);
3048 return TRUE;
3051 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
3053 RANGES toSkip;
3055 if (!(toSkip = ranges_create(1))) return FALSE;
3056 if (nItem != -1) ranges_additem(toSkip, nItem);
3057 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3058 ranges_destroy(toSkip);
3059 return TRUE;
3062 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3064 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3067 /***
3068 * DESCRIPTION:
3069 * Retrieves the number of items that are marked as selected.
3071 * PARAMETER(S):
3072 * [I] infoPtr : valid pointer to the listview structure
3074 * RETURN:
3075 * Number of items selected.
3077 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3079 INT nSelectedCount = 0;
3081 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3083 INT i;
3084 for (i = 0; i < infoPtr->nItemCount; i++)
3086 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3087 nSelectedCount++;
3090 else
3091 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3093 TRACE("nSelectedCount=%d\n", nSelectedCount);
3094 return nSelectedCount;
3097 /***
3098 * DESCRIPTION:
3099 * Manages the item focus.
3101 * PARAMETER(S):
3102 * [I] infoPtr : valid pointer to the listview structure
3103 * [I] nItem : item index
3105 * RETURN:
3106 * TRUE : focused item changed
3107 * FALSE : focused item has NOT changed
3109 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3111 INT oldFocus = infoPtr->nFocusedItem;
3112 LVITEMW lvItem;
3114 if (nItem == infoPtr->nFocusedItem) return FALSE;
3116 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3117 lvItem.stateMask = LVIS_FOCUSED;
3118 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3120 return oldFocus != infoPtr->nFocusedItem;
3123 /* Helper function for LISTVIEW_ShiftIndices *only* */
3124 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3126 if (nShiftItem < nItem) return nShiftItem;
3128 if (nShiftItem > nItem) return nShiftItem + direction;
3130 if (direction > 0) return nShiftItem + direction;
3132 return min(nShiftItem, infoPtr->nItemCount - 1);
3136 * DESCRIPTION:
3137 * Updates the various indices after an item has been inserted or deleted.
3139 * PARAMETER(S):
3140 * [I] infoPtr : valid pointer to the listview structure
3141 * [I] nItem : item index
3142 * [I] direction : Direction of shift, +1 or -1.
3144 * RETURN:
3145 * None
3147 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3149 INT nNewFocus;
3150 BOOL bOldChange;
3152 /* temporarily disable change notification while shifting items */
3153 bOldChange = infoPtr->bDoChangeNotify;
3154 infoPtr->bDoChangeNotify = FALSE;
3156 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3158 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3160 assert(abs(direction) == 1);
3162 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3164 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3165 if (nNewFocus != infoPtr->nFocusedItem)
3166 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3168 /* But we are not supposed to modify nHotItem! */
3170 infoPtr->bDoChangeNotify = bOldChange;
3175 * DESCRIPTION:
3176 * Adds a block of selections.
3178 * PARAMETER(S):
3179 * [I] infoPtr : valid pointer to the listview structure
3180 * [I] nItem : item index
3182 * RETURN:
3183 * Whether the window is still valid.
3185 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3187 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3188 INT nLast = max(infoPtr->nSelectionMark, nItem);
3189 HWND hwndSelf = infoPtr->hwndSelf;
3190 NMLVODSTATECHANGE nmlv;
3191 LVITEMW item;
3192 BOOL bOldChange;
3193 INT i;
3195 /* Temporarily disable change notification
3196 * If the control is LVS_OWNERDATA, we need to send
3197 * only one LVN_ODSTATECHANGED notification.
3198 * See MSDN documentation for LVN_ITEMCHANGED.
3200 bOldChange = infoPtr->bDoChangeNotify;
3201 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3203 if (nFirst == -1) nFirst = nItem;
3205 item.state = LVIS_SELECTED;
3206 item.stateMask = LVIS_SELECTED;
3208 for (i = nFirst; i <= nLast; i++)
3209 LISTVIEW_SetItemState(infoPtr,i,&item);
3211 ZeroMemory(&nmlv, sizeof(nmlv));
3212 nmlv.iFrom = nFirst;
3213 nmlv.iTo = nLast;
3214 nmlv.uNewState = 0;
3215 nmlv.uOldState = item.state;
3217 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3218 if (!IsWindow(hwndSelf))
3219 return FALSE;
3220 infoPtr->bDoChangeNotify = bOldChange;
3221 return TRUE;
3225 /***
3226 * DESCRIPTION:
3227 * Sets a single group selection.
3229 * PARAMETER(S):
3230 * [I] infoPtr : valid pointer to the listview structure
3231 * [I] nItem : item index
3233 * RETURN:
3234 * None
3236 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3238 RANGES selection;
3239 LVITEMW item;
3240 ITERATOR i;
3241 BOOL bOldChange;
3243 if (!(selection = ranges_create(100))) return;
3245 item.state = LVIS_SELECTED;
3246 item.stateMask = LVIS_SELECTED;
3248 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3250 if (infoPtr->nSelectionMark == -1)
3252 infoPtr->nSelectionMark = nItem;
3253 ranges_additem(selection, nItem);
3255 else
3257 RANGE sel;
3259 sel.lower = min(infoPtr->nSelectionMark, nItem);
3260 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3261 ranges_add(selection, sel);
3264 else
3266 RECT rcItem, rcSel, rcSelMark;
3267 POINT ptItem;
3269 rcItem.left = LVIR_BOUNDS;
3270 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3271 rcSelMark.left = LVIR_BOUNDS;
3272 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3273 UnionRect(&rcSel, &rcItem, &rcSelMark);
3274 iterator_frameditems(&i, infoPtr, &rcSel);
3275 while(iterator_next(&i))
3277 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3278 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3280 iterator_destroy(&i);
3283 /* disable per item notifications on LVS_OWNERDATA style
3284 FIXME: single LVN_ODSTATECHANGED should be used */
3285 bOldChange = infoPtr->bDoChangeNotify;
3286 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3288 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3291 iterator_rangesitems(&i, selection);
3292 while(iterator_next(&i))
3293 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3294 /* this will also destroy the selection */
3295 iterator_destroy(&i);
3297 infoPtr->bDoChangeNotify = bOldChange;
3299 LISTVIEW_SetItemFocus(infoPtr, nItem);
3302 /***
3303 * DESCRIPTION:
3304 * Sets a single selection.
3306 * PARAMETER(S):
3307 * [I] infoPtr : valid pointer to the listview structure
3308 * [I] nItem : item index
3310 * RETURN:
3311 * None
3313 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3315 LVITEMW lvItem;
3317 TRACE("nItem=%d\n", nItem);
3319 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3321 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3322 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3323 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3325 infoPtr->nSelectionMark = nItem;
3328 /***
3329 * DESCRIPTION:
3330 * Set selection(s) with keyboard.
3332 * PARAMETER(S):
3333 * [I] infoPtr : valid pointer to the listview structure
3334 * [I] nItem : item index
3335 * [I] space : VK_SPACE code sent
3337 * RETURN:
3338 * SUCCESS : TRUE (needs to be repainted)
3339 * FAILURE : FALSE (nothing has changed)
3341 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3343 /* FIXME: pass in the state */
3344 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3345 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3346 BOOL bResult = FALSE;
3348 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3349 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3351 bResult = TRUE;
3353 if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3354 LISTVIEW_SetSelection(infoPtr, nItem);
3355 else
3357 if (wShift)
3358 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3359 else if (wCtrl)
3361 LVITEMW lvItem;
3362 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3363 lvItem.stateMask = LVIS_SELECTED;
3364 if (space)
3366 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3367 if (lvItem.state & LVIS_SELECTED)
3368 infoPtr->nSelectionMark = nItem;
3370 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3373 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3376 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3377 return bResult;
3380 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3382 LVHITTESTINFO lvHitTestInfo;
3384 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3385 lvHitTestInfo.pt.x = pt.x;
3386 lvHitTestInfo.pt.y = pt.y;
3388 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3390 lpLVItem->mask = LVIF_PARAM;
3391 lpLVItem->iItem = lvHitTestInfo.iItem;
3392 lpLVItem->iSubItem = 0;
3394 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3397 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3399 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3400 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3401 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3404 /***
3405 * DESCRIPTION:
3406 * Called when the mouse is being actively tracked and has hovered for a specified
3407 * amount of time
3409 * PARAMETER(S):
3410 * [I] infoPtr : valid pointer to the listview structure
3411 * [I] fwKeys : key indicator
3412 * [I] x,y : mouse position
3414 * RETURN:
3415 * 0 if the message was processed, non-zero if there was an error
3417 * INFO:
3418 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3419 * over the item for a certain period of time.
3422 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3424 if (LISTVIEW_isHotTracking(infoPtr))
3426 LVITEMW item;
3427 POINT pt;
3429 pt.x = x;
3430 pt.y = y;
3432 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3433 LISTVIEW_SetSelection(infoPtr, item.iItem);
3436 return 0;
3439 /***
3440 * DESCRIPTION:
3441 * Called whenever WM_MOUSEMOVE is received.
3443 * PARAMETER(S):
3444 * [I] infoPtr : valid pointer to the listview structure
3445 * [I] fwKeys : key indicator
3446 * [I] x,y : mouse position
3448 * RETURN:
3449 * 0 if the message is processed, non-zero if there was an error
3451 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3453 TRACKMOUSEEVENT trackinfo;
3455 if (!(fwKeys & MK_LBUTTON))
3456 infoPtr->bLButtonDown = FALSE;
3458 if (infoPtr->bLButtonDown)
3460 POINT tmp;
3461 RECT rect;
3462 LVHITTESTINFO lvHitTestInfo;
3463 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3464 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3466 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3467 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3468 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3469 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3471 tmp.x = x;
3472 tmp.y = y;
3474 lvHitTestInfo.pt = tmp;
3475 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3477 /* reset item marker */
3478 if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
3479 infoPtr->nLButtonDownItem = -1;
3481 if (!PtInRect(&rect, tmp))
3483 /* this path covers the following:
3484 1. WM_LBUTTONDOWN over selected item (sets focus on it)
3485 2. change focus with keys
3486 3. move mouse over item from step 1 selects it and moves focus on it */
3487 if (infoPtr->nLButtonDownItem != -1 &&
3488 !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
3490 LVITEMW lvItem;
3492 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3493 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3495 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
3496 infoPtr->nLButtonDownItem = -1;
3499 if (!infoPtr->bDragging)
3501 NMLISTVIEW nmlv;
3503 lvHitTestInfo.pt = infoPtr->ptClickPos;
3504 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3506 ZeroMemory(&nmlv, sizeof(nmlv));
3507 nmlv.iItem = lvHitTestInfo.iItem;
3508 nmlv.ptAction = infoPtr->ptClickPos;
3510 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3511 infoPtr->bDragging = TRUE;
3514 return 0;
3518 /* see if we are supposed to be tracking mouse hovering */
3519 if (LISTVIEW_isHotTracking(infoPtr)) {
3520 /* fill in the trackinfo struct */
3521 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3522 trackinfo.dwFlags = TME_QUERY;
3523 trackinfo.hwndTrack = infoPtr->hwndSelf;
3524 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3526 /* see if we are already tracking this hwnd */
3527 _TrackMouseEvent(&trackinfo);
3529 if(!(trackinfo.dwFlags & TME_HOVER)) {
3530 trackinfo.dwFlags = TME_HOVER;
3532 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3533 _TrackMouseEvent(&trackinfo);
3537 return 0;
3541 /***
3542 * Tests whether the item is assignable to a list with style lStyle
3544 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3546 if ( (lpLVItem->mask & LVIF_TEXT) &&
3547 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3548 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3550 return TRUE;
3554 /***
3555 * DESCRIPTION:
3556 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3558 * PARAMETER(S):
3559 * [I] infoPtr : valid pointer to the listview structure
3560 * [I] lpLVItem : valid pointer to new item attributes
3561 * [I] isNew : the item being set is being inserted
3562 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3563 * [O] bChanged : will be set to TRUE if the item really changed
3565 * RETURN:
3566 * SUCCESS : TRUE
3567 * FAILURE : FALSE
3569 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3571 ITEM_INFO *lpItem;
3572 NMLISTVIEW nmlv;
3573 UINT uChanged = 0;
3574 LVITEMW item;
3575 /* stateMask is ignored for LVM_INSERTITEM */
3576 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
3578 TRACE("()\n");
3580 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3582 if (lpLVItem->mask == 0) return TRUE;
3584 if (infoPtr->dwStyle & LVS_OWNERDATA)
3586 /* a virtual listview only stores selection and focus */
3587 if (lpLVItem->mask & ~LVIF_STATE)
3588 return FALSE;
3589 lpItem = NULL;
3591 else
3593 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3594 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3595 assert (lpItem);
3598 /* we need to get the lParam and state of the item */
3599 item.iItem = lpLVItem->iItem;
3600 item.iSubItem = lpLVItem->iSubItem;
3601 item.mask = LVIF_STATE | LVIF_PARAM;
3602 item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
3604 item.state = 0;
3605 item.lParam = 0;
3606 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3608 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3609 /* determine what fields will change */
3610 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
3611 uChanged |= LVIF_STATE;
3613 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3614 uChanged |= LVIF_IMAGE;
3616 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3617 uChanged |= LVIF_PARAM;
3619 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3620 uChanged |= LVIF_INDENT;
3622 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3623 uChanged |= LVIF_TEXT;
3625 TRACE("uChanged=0x%x\n", uChanged);
3626 if (!uChanged) return TRUE;
3627 *bChanged = TRUE;
3629 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3630 nmlv.iItem = lpLVItem->iItem;
3631 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
3632 nmlv.uOldState = item.state;
3633 nmlv.uChanged = uChanged;
3634 nmlv.lParam = item.lParam;
3636 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3637 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3638 /* are enabled */
3639 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3641 HWND hwndSelf = infoPtr->hwndSelf;
3643 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3644 return FALSE;
3645 if (!IsWindow(hwndSelf))
3646 return FALSE;
3649 /* copy information */
3650 if (lpLVItem->mask & LVIF_TEXT)
3651 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3653 if (lpLVItem->mask & LVIF_IMAGE)
3654 lpItem->hdr.iImage = lpLVItem->iImage;
3656 if (lpLVItem->mask & LVIF_PARAM)
3657 lpItem->lParam = lpLVItem->lParam;
3659 if (lpLVItem->mask & LVIF_INDENT)
3660 lpItem->iIndent = lpLVItem->iIndent;
3662 if (uChanged & LVIF_STATE)
3664 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
3666 lpItem->state &= ~stateMask;
3667 lpItem->state |= (lpLVItem->state & stateMask);
3669 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3671 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3672 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3674 else if (stateMask & LVIS_SELECTED)
3676 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3678 /* if we are asked to change focus, and we manage it, do it */
3679 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3681 if (lpLVItem->state & LVIS_FOCUSED)
3683 if (infoPtr->nFocusedItem != -1)
3685 /* remove current focus */
3686 item.mask = LVIF_STATE;
3687 item.state = 0;
3688 item.stateMask = LVIS_FOCUSED;
3690 /* recurse with redrawing an item */
3691 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
3694 infoPtr->nFocusedItem = lpLVItem->iItem;
3695 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST);
3697 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3699 infoPtr->nFocusedItem = -1;
3704 /* if we're inserting the item, we're done */
3705 if (isNew) return TRUE;
3707 /* send LVN_ITEMCHANGED notification */
3708 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3709 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3711 return TRUE;
3714 /***
3715 * DESCRIPTION:
3716 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3718 * PARAMETER(S):
3719 * [I] infoPtr : valid pointer to the listview structure
3720 * [I] lpLVItem : valid pointer to new subitem attributes
3721 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3722 * [O] bChanged : will be set to TRUE if the item really changed
3724 * RETURN:
3725 * SUCCESS : TRUE
3726 * FAILURE : FALSE
3728 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3730 HDPA hdpaSubItems;
3731 SUBITEM_INFO *lpSubItem;
3733 /* we do not support subitems for virtual listviews */
3734 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3736 /* set subitem only if column is present */
3737 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3739 /* First do some sanity checks */
3740 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3741 particularly useful. We currently do not actually do anything with
3742 the flag on subitems.
3744 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3745 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3747 /* get the subitem structure, and create it if not there */
3748 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3749 assert (hdpaSubItems);
3751 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3752 if (!lpSubItem)
3754 SUBITEM_INFO *tmpSubItem;
3755 INT i;
3757 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3758 if (!lpSubItem) return FALSE;
3759 /* we could binary search here, if need be...*/
3760 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3762 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3763 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3765 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3767 Free(lpSubItem);
3768 return FALSE;
3770 lpSubItem->iSubItem = lpLVItem->iSubItem;
3771 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3772 *bChanged = TRUE;
3775 if (lpLVItem->mask & LVIF_IMAGE)
3776 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3778 lpSubItem->hdr.iImage = lpLVItem->iImage;
3779 *bChanged = TRUE;
3782 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW))
3784 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3785 *bChanged = TRUE;
3788 return TRUE;
3791 /***
3792 * DESCRIPTION:
3793 * Sets item attributes.
3795 * PARAMETER(S):
3796 * [I] infoPtr : valid pointer to the listview structure
3797 * [I] lpLVItem : new item attributes
3798 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3800 * RETURN:
3801 * SUCCESS : TRUE
3802 * FAILURE : FALSE
3804 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3806 HWND hwndSelf = infoPtr->hwndSelf;
3807 LPWSTR pszText = NULL;
3808 BOOL bResult, bChanged = FALSE;
3810 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3812 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3813 return FALSE;
3815 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3816 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3818 pszText = lpLVItem->pszText;
3819 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3822 /* actually set the fields */
3823 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3825 if (lpLVItem->iSubItem)
3826 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3827 else
3828 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3829 if (!IsWindow(hwndSelf))
3830 return FALSE;
3832 /* redraw item, if necessary */
3833 if (bChanged && !infoPtr->bIsDrawing)
3835 /* this little optimization eliminates some nasty flicker */
3836 if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3837 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3838 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3839 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3840 else
3841 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3843 /* restore text */
3844 if (pszText)
3846 textfreeT(lpLVItem->pszText, isW);
3847 lpLVItem->pszText = pszText;
3850 return bResult;
3853 /***
3854 * DESCRIPTION:
3855 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3857 * PARAMETER(S):
3858 * [I] infoPtr : valid pointer to the listview structure
3860 * RETURN:
3861 * item index
3863 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3865 INT nItem = 0;
3866 SCROLLINFO scrollInfo;
3868 scrollInfo.cbSize = sizeof(SCROLLINFO);
3869 scrollInfo.fMask = SIF_POS;
3871 if (infoPtr->uView == LV_VIEW_LIST)
3873 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3874 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3876 else if (infoPtr->uView == LV_VIEW_DETAILS)
3878 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3879 nItem = scrollInfo.nPos;
3881 else
3883 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3884 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3887 TRACE("nItem=%d\n", nItem);
3889 return nItem;
3893 /***
3894 * DESCRIPTION:
3895 * Erases the background of the given rectangle
3897 * PARAMETER(S):
3898 * [I] infoPtr : valid pointer to the listview structure
3899 * [I] hdc : device context handle
3900 * [I] lprcBox : clipping rectangle
3902 * RETURN:
3903 * Success: TRUE
3904 * Failure: FALSE
3906 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3908 if (!infoPtr->hBkBrush) return FALSE;
3910 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3912 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3915 /***
3916 * DESCRIPTION:
3917 * Draws an item.
3919 * PARAMETER(S):
3920 * [I] infoPtr : valid pointer to the listview structure
3921 * [I] hdc : device context handle
3922 * [I] nItem : item index
3923 * [I] nSubItem : subitem index
3924 * [I] pos : item position in client coordinates
3925 * [I] cdmode : custom draw mode
3927 * RETURN:
3928 * Success: TRUE
3929 * Failure: FALSE
3931 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3933 UINT uFormat;
3934 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3935 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3936 DWORD cdsubitemmode = CDRF_DODEFAULT;
3937 LPRECT lprcFocus;
3938 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3939 NMLVCUSTOMDRAW nmlvcd;
3940 HIMAGELIST himl;
3941 LVITEMW lvItem;
3942 HFONT hOldFont;
3944 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3946 /* get information needed for drawing the item */
3947 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3948 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3949 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
3950 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3951 lvItem.iItem = nItem;
3952 lvItem.iSubItem = nSubItem;
3953 lvItem.state = 0;
3954 lvItem.lParam = 0;
3955 lvItem.cchTextMax = DISP_TEXT_SIZE;
3956 lvItem.pszText = szDispText;
3957 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3958 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3959 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3960 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3961 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3963 /* now check if we need to update the focus rectangle */
3964 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3966 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3967 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3968 OffsetRect(&rcBox, pos.x, pos.y);
3969 OffsetRect(&rcSelect, pos.x, pos.y);
3970 OffsetRect(&rcIcon, pos.x, pos.y);
3971 OffsetRect(&rcStateIcon, pos.x, pos.y);
3972 OffsetRect(&rcLabel, pos.x, pos.y);
3973 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3974 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3975 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3977 /* fill in the custom draw structure */
3978 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3980 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3981 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3982 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3983 if (cdmode & CDRF_NOTIFYITEMDRAW)
3984 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3985 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3986 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3987 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3988 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3990 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3991 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3993 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3994 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3995 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3996 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3998 /* in full row select, subitems, will just use main item's colors */
3999 if (nSubItem && infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4000 nmlvcd.clrTextBk = CLR_NONE;
4002 /* state icons */
4003 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
4005 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
4006 if (uStateImage)
4008 TRACE("uStateImage=%d\n", uStateImage);
4009 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
4010 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
4014 /* small icons */
4015 himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
4016 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
4018 TRACE("iImage=%d\n", lvItem.iImage);
4019 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
4020 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
4021 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
4024 /* Don't bother painting item being edited */
4025 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
4027 /* FIXME: temporary hack */
4028 rcSelect.left = rcLabel.left;
4030 /* draw the selection background, if we're drawing the main item */
4031 if (nSubItem == 0)
4033 /* in icon mode, the label rect is really what we want to draw the
4034 * background for */
4035 if (infoPtr->uView == LV_VIEW_ICON)
4036 rcSelect = rcLabel;
4038 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4039 rcSelect.right = rcBox.right;
4041 if (nmlvcd.clrTextBk != CLR_NONE)
4042 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
4043 /* store new focus rectangle */
4044 if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect;
4047 /* figure out the text drawing flags */
4048 uFormat = (infoPtr->uView == LV_VIEW_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
4049 if (infoPtr->uView == LV_VIEW_ICON)
4050 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
4051 else if (nSubItem)
4053 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
4055 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
4056 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
4057 default: uFormat |= DT_LEFT;
4060 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
4062 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
4063 else rcLabel.left += LABEL_HOR_PADDING;
4065 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4067 /* for GRIDLINES reduce the bottom so the text formats correctly */
4068 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4069 rcLabel.bottom--;
4071 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
4073 postpaint:
4074 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4075 notify_postpaint(infoPtr, &nmlvcd);
4076 if (cdsubitemmode & CDRF_NEWFONT)
4077 SelectObject(hdc, hOldFont);
4078 return TRUE;
4081 /***
4082 * DESCRIPTION:
4083 * Draws listview items when in owner draw mode.
4085 * PARAMETER(S):
4086 * [I] infoPtr : valid pointer to the listview structure
4087 * [I] hdc : device context handle
4089 * RETURN:
4090 * None
4092 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4094 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4095 DWORD cditemmode = CDRF_DODEFAULT;
4096 NMLVCUSTOMDRAW nmlvcd;
4097 POINT Origin, Position;
4098 DRAWITEMSTRUCT dis;
4099 LVITEMW item;
4101 TRACE("()\n");
4103 ZeroMemory(&dis, sizeof(dis));
4105 /* Get scroll info once before loop */
4106 LISTVIEW_GetOrigin(infoPtr, &Origin);
4108 /* iterate through the invalidated rows */
4109 while(iterator_next(i))
4111 item.iItem = i->nItem;
4112 item.iSubItem = 0;
4113 item.mask = LVIF_PARAM | LVIF_STATE;
4114 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4115 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4117 dis.CtlType = ODT_LISTVIEW;
4118 dis.CtlID = uID;
4119 dis.itemID = item.iItem;
4120 dis.itemAction = ODA_DRAWENTIRE;
4121 dis.itemState = 0;
4122 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4123 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4124 dis.hwndItem = infoPtr->hwndSelf;
4125 dis.hDC = hdc;
4126 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4127 dis.rcItem.left = Position.x + Origin.x;
4128 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4129 dis.rcItem.top = Position.y + Origin.y;
4130 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4131 dis.itemData = item.lParam;
4133 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4136 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4137 * structure for the rest. of the paint cycle
4139 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4140 if (cdmode & CDRF_NOTIFYITEMDRAW)
4141 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4143 if (!(cditemmode & CDRF_SKIPDEFAULT))
4145 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4146 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4149 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4150 notify_postpaint(infoPtr, &nmlvcd);
4154 /***
4155 * DESCRIPTION:
4156 * Draws listview items when in report display mode.
4158 * PARAMETER(S):
4159 * [I] infoPtr : valid pointer to the listview structure
4160 * [I] hdc : device context handle
4161 * [I] cdmode : custom draw mode
4163 * RETURN:
4164 * None
4166 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4168 INT rgntype;
4169 RECT rcClip, rcItem;
4170 POINT Origin, Position;
4171 RANGES colRanges;
4172 INT col, index;
4173 ITERATOR j;
4175 TRACE("()\n");
4177 /* figure out what to draw */
4178 rgntype = GetClipBox(hdc, &rcClip);
4179 if (rgntype == NULLREGION) return;
4181 /* Get scroll info once before loop */
4182 LISTVIEW_GetOrigin(infoPtr, &Origin);
4184 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4186 /* narrow down the columns we need to paint */
4187 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4189 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4191 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4192 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4193 ranges_additem(colRanges, index);
4195 iterator_rangesitems(&j, colRanges);
4197 /* in full row select, we _have_ to draw the main item */
4198 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4199 j.nSpecial = 0;
4201 /* iterate through the invalidated rows */
4202 while(iterator_next(i))
4204 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4205 Position.y += Origin.y;
4207 /* iterate through the invalidated columns */
4208 while(iterator_next(&j))
4210 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4211 Position.x = (j.nItem == 0) ? rcItem.left + Origin.x : Origin.x;
4213 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4215 rcItem.top = 0;
4216 rcItem.bottom = infoPtr->nItemHeight;
4217 OffsetRect(&rcItem, Position.x, Position.y);
4218 if (!RectVisible(hdc, &rcItem)) continue;
4221 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4224 iterator_destroy(&j);
4227 /***
4228 * DESCRIPTION:
4229 * Draws the gridlines if necessary when in report display mode.
4231 * PARAMETER(S):
4232 * [I] infoPtr : valid pointer to the listview structure
4233 * [I] hdc : device context handle
4235 * RETURN:
4236 * None
4238 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4240 INT rgntype;
4241 INT y, itemheight;
4242 INT col, index;
4243 HPEN hPen, hOldPen;
4244 RECT rcClip, rcItem = {0};
4245 POINT Origin;
4246 RANGES colRanges;
4247 ITERATOR j;
4248 BOOL rmost = FALSE;
4250 TRACE("()\n");
4252 /* figure out what to draw */
4253 rgntype = GetClipBox(hdc, &rcClip);
4254 if (rgntype == NULLREGION) return;
4256 /* Get scroll info once before loop */
4257 LISTVIEW_GetOrigin(infoPtr, &Origin);
4259 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4261 /* narrow down the columns we need to paint */
4262 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4264 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4266 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4267 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4268 ranges_additem(colRanges, index);
4271 /* is right most vertical line visible? */
4272 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4274 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4275 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4276 rmost = (rcItem.right + Origin.x < rcClip.right);
4279 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4281 hOldPen = SelectObject ( hdc, hPen );
4283 /* draw the vertical lines for the columns */
4284 iterator_rangesitems(&j, colRanges);
4285 while(iterator_next(&j))
4287 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4288 if (rcItem.left == 0) continue; /* skip leftmost column */
4289 rcItem.left += Origin.x;
4290 rcItem.right += Origin.x;
4291 rcItem.top = infoPtr->rcList.top;
4292 rcItem.bottom = infoPtr->rcList.bottom;
4293 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4294 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4295 LineTo (hdc, rcItem.left, rcItem.bottom);
4297 iterator_destroy(&j);
4298 /* draw rightmost grid line if visible */
4299 if (rmost)
4301 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4302 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4303 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4305 rcItem.right += Origin.x;
4307 MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL);
4308 LineTo (hdc, rcItem.right, infoPtr->rcList.bottom);
4311 /* draw the horizontial lines for the rows */
4312 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4313 rcItem.left = infoPtr->rcList.left;
4314 rcItem.right = infoPtr->rcList.right;
4315 rcItem.bottom = rcItem.top = Origin.y - 1;
4316 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4317 LineTo(hdc, rcItem.right, rcItem.top);
4318 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4320 rcItem.bottom = rcItem.top = y;
4321 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4322 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4323 LineTo (hdc, rcItem.right, rcItem.top);
4326 SelectObject( hdc, hOldPen );
4327 DeleteObject( hPen );
4331 /***
4332 * DESCRIPTION:
4333 * Draws listview items when in list display mode.
4335 * PARAMETER(S):
4336 * [I] infoPtr : valid pointer to the listview structure
4337 * [I] hdc : device context handle
4338 * [I] cdmode : custom draw mode
4340 * RETURN:
4341 * None
4343 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4345 POINT Origin, Position;
4347 /* Get scroll info once before loop */
4348 LISTVIEW_GetOrigin(infoPtr, &Origin);
4350 while(iterator_prev(i))
4352 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4353 Position.x += Origin.x;
4354 Position.y += Origin.y;
4356 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4361 /***
4362 * DESCRIPTION:
4363 * Draws listview items.
4365 * PARAMETER(S):
4366 * [I] infoPtr : valid pointer to the listview structure
4367 * [I] hdc : device context handle
4368 * [I] prcErase : rect to be erased before refresh (may be NULL)
4370 * RETURN:
4371 * NoneX
4373 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4375 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4376 NMLVCUSTOMDRAW nmlvcd;
4377 HFONT hOldFont = 0;
4378 DWORD cdmode;
4379 INT oldBkMode = 0;
4380 RECT rcClient;
4381 ITERATOR i;
4382 HDC hdcOrig = hdc;
4383 HBITMAP hbmp = NULL;
4384 RANGE range;
4386 LISTVIEW_DUMP(infoPtr);
4388 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4389 TRACE("double buffering\n");
4391 hdc = CreateCompatibleDC(hdcOrig);
4392 if (!hdc) {
4393 ERR("Failed to create DC for backbuffer\n");
4394 return;
4396 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4397 infoPtr->rcList.bottom);
4398 if (!hbmp) {
4399 ERR("Failed to create bitmap for backbuffer\n");
4400 DeleteDC(hdc);
4401 return;
4404 SelectObject(hdc, hbmp);
4405 SelectObject(hdc, infoPtr->hFont);
4406 } else {
4407 /* Save dc values we're gonna trash while drawing
4408 * FIXME: Should be done in LISTVIEW_DrawItem() */
4409 hOldFont = SelectObject(hdc, infoPtr->hFont);
4410 oldBkMode = GetBkMode(hdc);
4411 oldBkColor = GetBkColor(hdc);
4412 oldTextColor = GetTextColor(hdc);
4415 infoPtr->bIsDrawing = TRUE;
4417 if (prcErase) {
4418 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4419 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4420 /* If no erasing was done (usually because RedrawWindow was called
4421 * with RDW_INVALIDATE only) we need to copy the old contents into
4422 * the backbuffer before continuing. */
4423 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4424 infoPtr->rcList.right - infoPtr->rcList.left,
4425 infoPtr->rcList.bottom - infoPtr->rcList.top,
4426 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4429 /* FIXME: Shouldn't need to do this */
4430 oldClrTextBk = infoPtr->clrTextBk;
4431 oldClrText = infoPtr->clrText;
4433 infoPtr->cditemmode = CDRF_DODEFAULT;
4435 GetClientRect(infoPtr->hwndSelf, &rcClient);
4436 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4437 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4438 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4439 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4441 /* Use these colors to draw the items */
4442 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4443 infoPtr->clrText = nmlvcd.clrText;
4445 /* nothing to draw */
4446 if(infoPtr->nItemCount == 0) goto enddraw;
4448 /* figure out what we need to draw */
4449 iterator_visibleitems(&i, infoPtr, hdc);
4450 range = iterator_range(&i);
4452 /* send cache hint notification */
4453 if (infoPtr->dwStyle & LVS_OWNERDATA)
4455 NMLVCACHEHINT nmlv;
4457 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4458 nmlv.iFrom = range.lower;
4459 nmlv.iTo = range.upper - 1;
4460 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4463 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
4464 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4465 else
4467 if (infoPtr->uView == LV_VIEW_DETAILS)
4468 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4469 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
4470 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4472 /* if we have a focus rect and it's visible, draw it */
4473 if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
4474 (range.upper - 1) >= infoPtr->nFocusedItem)
4475 LISTVIEW_DrawFocusRect(infoPtr, hdc);
4477 iterator_destroy(&i);
4479 enddraw:
4480 /* For LVS_EX_GRIDLINES go and draw lines */
4481 /* This includes the case where there were *no* items */
4482 if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4483 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4485 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4486 notify_postpaint(infoPtr, &nmlvcd);
4488 infoPtr->clrTextBk = oldClrTextBk;
4489 infoPtr->clrText = oldClrText;
4491 if(hbmp) {
4492 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4493 infoPtr->rcList.right - infoPtr->rcList.left,
4494 infoPtr->rcList.bottom - infoPtr->rcList.top,
4495 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4497 DeleteObject(hbmp);
4498 DeleteDC(hdc);
4499 } else {
4500 SelectObject(hdc, hOldFont);
4501 SetBkMode(hdc, oldBkMode);
4502 SetBkColor(hdc, oldBkColor);
4503 SetTextColor(hdc, oldTextColor);
4506 infoPtr->bIsDrawing = FALSE;
4510 /***
4511 * DESCRIPTION:
4512 * Calculates the approximate width and height of a given number of items.
4514 * PARAMETER(S):
4515 * [I] infoPtr : valid pointer to the listview structure
4516 * [I] nItemCount : number of items
4517 * [I] wWidth : width
4518 * [I] wHeight : height
4520 * RETURN:
4521 * Returns a DWORD. The width in the low word and the height in high word.
4523 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4524 WORD wWidth, WORD wHeight)
4526 INT nItemCountPerColumn = 1;
4527 INT nColumnCount = 0;
4528 DWORD dwViewRect = 0;
4530 if (nItemCount == -1)
4531 nItemCount = infoPtr->nItemCount;
4533 if (infoPtr->uView == LV_VIEW_LIST)
4535 if (wHeight == 0xFFFF)
4537 /* use current height */
4538 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4541 if (wHeight < infoPtr->nItemHeight)
4542 wHeight = infoPtr->nItemHeight;
4544 if (nItemCount > 0)
4546 if (infoPtr->nItemHeight > 0)
4548 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4549 if (nItemCountPerColumn == 0)
4550 nItemCountPerColumn = 1;
4552 if (nItemCount % nItemCountPerColumn != 0)
4553 nColumnCount = nItemCount / nItemCountPerColumn;
4554 else
4555 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4559 /* Microsoft padding magic */
4560 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4561 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4563 dwViewRect = MAKELONG(wWidth, wHeight);
4565 else if (infoPtr->uView == LV_VIEW_DETAILS)
4567 RECT rcBox;
4569 if (infoPtr->nItemCount > 0)
4571 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4572 wWidth = rcBox.right - rcBox.left;
4573 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4575 else
4577 /* use current height and width */
4578 if (wHeight == 0xffff)
4579 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4580 if (wWidth == 0xffff)
4581 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4584 dwViewRect = MAKELONG(wWidth, wHeight);
4586 else if (infoPtr->uView == LV_VIEW_SMALLICON)
4587 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
4588 else if (infoPtr->uView == LV_VIEW_ICON)
4589 FIXME("uView == LV_VIEW_ICON: not implemented\n");
4591 return dwViewRect;
4595 /***
4596 * DESCRIPTION:
4597 * Create a drag image list for the specified item.
4599 * PARAMETER(S):
4600 * [I] infoPtr : valid pointer to the listview structure
4601 * [I] iItem : index of item
4602 * [O] lppt : Upper-left corner of the image
4604 * RETURN:
4605 * Returns a handle to the image list if successful, NULL otherwise.
4607 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4609 RECT rcItem;
4610 SIZE size;
4611 POINT pos;
4612 HDC hdc, hdcOrig;
4613 HBITMAP hbmp, hOldbmp;
4614 HIMAGELIST dragList = 0;
4615 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4617 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4618 return 0;
4620 rcItem.left = LVIR_BOUNDS;
4621 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4622 return 0;
4624 lppt->x = rcItem.left;
4625 lppt->y = rcItem.top;
4627 size.cx = rcItem.right - rcItem.left;
4628 size.cy = rcItem.bottom - rcItem.top;
4630 hdcOrig = GetDC(infoPtr->hwndSelf);
4631 hdc = CreateCompatibleDC(hdcOrig);
4632 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4633 hOldbmp = SelectObject(hdc, hbmp);
4635 rcItem.left = rcItem.top = 0;
4636 rcItem.right = size.cx;
4637 rcItem.bottom = size.cy;
4638 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4640 pos.x = pos.y = 0;
4641 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4643 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4644 SelectObject(hdc, hOldbmp);
4645 ImageList_Add(dragList, hbmp, 0);
4647 else
4648 SelectObject(hdc, hOldbmp);
4650 DeleteObject(hbmp);
4651 DeleteDC(hdc);
4652 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4654 TRACE("ret=%p\n", dragList);
4656 return dragList;
4660 /***
4661 * DESCRIPTION:
4662 * Removes all listview items and subitems.
4664 * PARAMETER(S):
4665 * [I] infoPtr : valid pointer to the listview structure
4667 * RETURN:
4668 * SUCCESS : TRUE
4669 * FAILURE : FALSE
4671 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4673 NMLISTVIEW nmlv;
4674 HDPA hdpaSubItems = NULL;
4675 BOOL bSuppress;
4676 ITEMHDR *hdrItem;
4677 INT i, j;
4679 TRACE("()\n");
4681 /* we do it directly, to avoid notifications */
4682 ranges_clear(infoPtr->selectionRanges);
4683 infoPtr->nSelectionMark = -1;
4684 infoPtr->nFocusedItem = -1;
4685 SetRectEmpty(&infoPtr->rcFocus);
4686 /* But we are supposed to leave nHotItem as is! */
4689 /* send LVN_DELETEALLITEMS notification */
4690 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4691 nmlv.iItem = -1;
4692 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4694 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4696 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4698 /* send LVN_DELETEITEM notification, if not suppressed
4699 and if it is not a virtual listview */
4700 if (!bSuppress) notify_deleteitem(infoPtr, i);
4701 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4702 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4704 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4705 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4706 Free(hdrItem);
4708 DPA_Destroy(hdpaSubItems);
4709 DPA_DeletePtr(infoPtr->hdpaItems, i);
4711 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4712 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4713 infoPtr->nItemCount --;
4716 if (!destroy)
4718 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4719 LISTVIEW_UpdateScroll(infoPtr);
4721 LISTVIEW_InvalidateList(infoPtr);
4723 return TRUE;
4726 /***
4727 * DESCRIPTION:
4728 * Scrolls, and updates the columns, when a column is changing width.
4730 * PARAMETER(S):
4731 * [I] infoPtr : valid pointer to the listview structure
4732 * [I] nColumn : column to scroll
4733 * [I] dx : amount of scroll, in pixels
4735 * RETURN:
4736 * None.
4738 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4740 COLUMN_INFO *lpColumnInfo;
4741 RECT rcOld, rcCol;
4742 POINT ptOrigin;
4743 INT nCol;
4745 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4746 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4747 rcCol = lpColumnInfo->rcHeader;
4748 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4749 rcCol.left = rcCol.right;
4751 /* adjust the other columns */
4752 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4754 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4755 lpColumnInfo->rcHeader.left += dx;
4756 lpColumnInfo->rcHeader.right += dx;
4759 /* do not update screen if not in report mode */
4760 if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
4762 /* Need to reset the item width when inserting a new column */
4763 infoPtr->nItemWidth += dx;
4765 LISTVIEW_UpdateScroll(infoPtr);
4766 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4768 /* scroll to cover the deleted column, and invalidate for redraw */
4769 rcOld = infoPtr->rcList;
4770 rcOld.left = ptOrigin.x + rcCol.left + dx;
4771 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4774 /***
4775 * DESCRIPTION:
4776 * Removes a column from the listview control.
4778 * PARAMETER(S):
4779 * [I] infoPtr : valid pointer to the listview structure
4780 * [I] nColumn : column index
4782 * RETURN:
4783 * SUCCESS : TRUE
4784 * FAILURE : FALSE
4786 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4788 RECT rcCol;
4790 TRACE("nColumn=%d\n", nColumn);
4792 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4793 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4795 /* While the MSDN specifically says that column zero should not be deleted,
4796 what actually happens is that the column itself is deleted but no items or subitems
4797 are removed.
4800 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4802 if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
4803 return FALSE;
4805 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4806 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4808 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4810 SUBITEM_INFO *lpSubItem, *lpDelItem;
4811 HDPA hdpaSubItems;
4812 INT nItem, nSubItem, i;
4814 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4816 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4817 nSubItem = 0;
4818 lpDelItem = 0;
4819 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4821 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4822 if (lpSubItem->iSubItem == nColumn)
4824 nSubItem = i;
4825 lpDelItem = lpSubItem;
4827 else if (lpSubItem->iSubItem > nColumn)
4829 lpSubItem->iSubItem--;
4833 /* if we found our subitem, zapp it */
4834 if (nSubItem > 0)
4836 /* free string */
4837 if (is_textW(lpDelItem->hdr.pszText))
4838 Free(lpDelItem->hdr.pszText);
4840 /* free item */
4841 Free(lpDelItem);
4843 /* free dpa memory */
4844 DPA_DeletePtr(hdpaSubItems, nSubItem);
4849 /* update the other column info */
4850 LISTVIEW_UpdateItemSize(infoPtr);
4851 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4852 LISTVIEW_InvalidateList(infoPtr);
4853 else
4854 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4856 return TRUE;
4859 /***
4860 * DESCRIPTION:
4861 * Invalidates the listview after an item's insertion or deletion.
4863 * PARAMETER(S):
4864 * [I] infoPtr : valid pointer to the listview structure
4865 * [I] nItem : item index
4866 * [I] dir : -1 if deleting, 1 if inserting
4868 * RETURN:
4869 * None
4871 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4873 INT nPerCol, nItemCol, nItemRow;
4874 RECT rcScroll;
4875 POINT Origin;
4877 /* if we don't refresh, what's the point of scrolling? */
4878 if (!is_redrawing(infoPtr)) return;
4880 assert (abs(dir) == 1);
4882 /* arrange icons if autoarrange is on */
4883 if (is_autoarrange(infoPtr))
4885 BOOL arrange = TRUE;
4886 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4887 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4888 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4891 /* scrollbars need updating */
4892 LISTVIEW_UpdateScroll(infoPtr);
4894 /* figure out the item's position */
4895 if (infoPtr->uView == LV_VIEW_DETAILS)
4896 nPerCol = infoPtr->nItemCount + 1;
4897 else if (infoPtr->uView == LV_VIEW_LIST)
4898 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4899 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
4900 return;
4902 nItemCol = nItem / nPerCol;
4903 nItemRow = nItem % nPerCol;
4904 LISTVIEW_GetOrigin(infoPtr, &Origin);
4906 /* move the items below up a slot */
4907 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4908 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4909 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4910 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4911 OffsetRect(&rcScroll, Origin.x, Origin.y);
4912 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4913 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4915 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4916 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4917 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4920 /* report has only that column, so we're done */
4921 if (infoPtr->uView == LV_VIEW_DETAILS) return;
4923 /* now for LISTs, we have to deal with the columns to the right */
4924 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4925 rcScroll.top = 0;
4926 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4927 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4928 OffsetRect(&rcScroll, Origin.x, Origin.y);
4929 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4930 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4931 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4934 /***
4935 * DESCRIPTION:
4936 * Removes an item from the listview control.
4938 * PARAMETER(S):
4939 * [I] infoPtr : valid pointer to the listview structure
4940 * [I] nItem : item index
4942 * RETURN:
4943 * SUCCESS : TRUE
4944 * FAILURE : FALSE
4946 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4948 LVITEMW item;
4949 const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
4951 TRACE("(nItem=%d)\n", nItem);
4953 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4955 /* remove selection, and focus */
4956 item.state = 0;
4957 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4958 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4960 /* send LVN_DELETEITEM notification. */
4961 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4963 /* we need to do this here, because we'll be deleting stuff */
4964 if (is_icon)
4965 LISTVIEW_InvalidateItem(infoPtr, nItem);
4967 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4969 HDPA hdpaSubItems;
4970 ITEMHDR *hdrItem;
4971 INT i;
4973 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4974 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4976 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4977 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4978 Free(hdrItem);
4980 DPA_Destroy(hdpaSubItems);
4983 if (is_icon)
4985 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4986 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4989 infoPtr->nItemCount--;
4990 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4992 /* now is the invalidation fun */
4993 if (!is_icon)
4994 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4995 return TRUE;
4999 /***
5000 * DESCRIPTION:
5001 * Callback implementation for editlabel control
5003 * PARAMETER(S):
5004 * [I] infoPtr : valid pointer to the listview structure
5005 * [I] storeText : store edit box text as item text
5006 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5008 * RETURN:
5009 * SUCCESS : TRUE
5010 * FAILURE : FALSE
5012 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
5014 HWND hwndSelf = infoPtr->hwndSelf;
5015 NMLVDISPINFOW dispInfo;
5016 INT editedItem = infoPtr->nEditLabelItem;
5017 BOOL bSame;
5018 WCHAR *pszText = NULL;
5019 BOOL res;
5021 if (storeText)
5023 DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit);
5025 if (len)
5027 if ((pszText = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
5029 if (isW) GetWindowTextW(infoPtr->hwndEdit, pszText, len+1);
5030 else GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len+1);
5035 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
5037 infoPtr->nEditLabelItem = -1;
5038 infoPtr->hwndEdit = 0;
5040 ZeroMemory(&dispInfo, sizeof(dispInfo));
5041 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5042 dispInfo.item.iItem = editedItem;
5043 dispInfo.item.iSubItem = 0;
5044 dispInfo.item.stateMask = ~0;
5045 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item))
5047 res = FALSE;
5048 goto cleanup;
5051 if (isW)
5052 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
5053 else
5055 LPWSTR tmp = textdupTtoW(pszText, FALSE);
5056 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
5057 textfreeT(tmp, FALSE);
5059 if (bSame)
5061 res = TRUE;
5062 goto cleanup;
5065 /* add the text from the edit in */
5066 dispInfo.item.mask |= LVIF_TEXT;
5067 dispInfo.item.pszText = pszText;
5068 dispInfo.item.cchTextMax = textlenT(pszText, isW);
5070 /* Do we need to update the Item Text */
5071 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
5073 res = FALSE;
5074 goto cleanup;
5076 if (!IsWindow(hwndSelf))
5078 res = FALSE;
5079 goto cleanup;
5081 if (!pszText) return TRUE;
5083 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5085 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
5086 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
5087 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
5089 LISTVIEW_InvalidateItem(infoPtr, editedItem);
5090 res = TRUE;
5091 goto cleanup;
5095 ZeroMemory(&dispInfo, sizeof(dispInfo));
5096 dispInfo.item.mask = LVIF_TEXT;
5097 dispInfo.item.iItem = editedItem;
5098 dispInfo.item.iSubItem = 0;
5099 dispInfo.item.pszText = pszText;
5100 dispInfo.item.cchTextMax = textlenT(pszText, isW);
5101 res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
5103 cleanup:
5104 Free(pszText);
5106 return res;
5109 /***
5110 * DESCRIPTION:
5111 * Begin in place editing of specified list view item
5113 * PARAMETER(S):
5114 * [I] infoPtr : valid pointer to the listview structure
5115 * [I] nItem : item index
5116 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5118 * RETURN:
5119 * SUCCESS : TRUE
5120 * FAILURE : FALSE
5122 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5124 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5125 NMLVDISPINFOW dispInfo;
5126 RECT rect;
5127 SIZE sz;
5128 HWND hwndSelf = infoPtr->hwndSelf;
5129 HDC hdc;
5130 HFONT hOldFont = NULL;
5131 TEXTMETRICW textMetric;
5133 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5135 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5137 /* Is the EditBox still there, if so remove it */
5138 if(infoPtr->hwndEdit != 0)
5140 SetFocus(infoPtr->hwndSelf);
5141 infoPtr->hwndEdit = 0;
5144 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5146 infoPtr->nEditLabelItem = nItem;
5148 LISTVIEW_SetSelection(infoPtr, nItem);
5149 LISTVIEW_SetItemFocus(infoPtr, nItem);
5150 LISTVIEW_InvalidateItem(infoPtr, nItem);
5152 rect.left = LVIR_LABEL;
5153 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5155 ZeroMemory(&dispInfo, sizeof(dispInfo));
5156 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5157 dispInfo.item.iItem = nItem;
5158 dispInfo.item.iSubItem = 0;
5159 dispInfo.item.stateMask = ~0;
5160 dispInfo.item.pszText = szDispText;
5161 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5162 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5164 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE, isW);
5165 if (!infoPtr->hwndEdit) return 0;
5167 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5169 if (!IsWindow(hwndSelf))
5170 return 0;
5171 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5172 infoPtr->hwndEdit = 0;
5173 return 0;
5176 /* Now position and display edit box */
5177 hdc = GetDC(infoPtr->hwndSelf);
5179 /* Select the font to get appropriate metric dimensions */
5180 if(infoPtr->hFont != 0)
5181 hOldFont = SelectObject(hdc, infoPtr->hFont);
5183 /* Get String Length in pixels */
5184 GetTextExtentPoint32W(hdc, dispInfo.item.pszText, lstrlenW(dispInfo.item.pszText), &sz);
5186 /* Add Extra spacing for the next character */
5187 GetTextMetricsW(hdc, &textMetric);
5188 sz.cx += (textMetric.tmMaxCharWidth * 2);
5190 if(infoPtr->hFont != 0)
5191 SelectObject(hdc, hOldFont);
5193 ReleaseDC(infoPtr->hwndSelf, hdc);
5195 MoveWindow(infoPtr->hwndEdit, rect.left - 2, rect.top - 1, sz.cx,
5196 rect.bottom - rect.top + 2, FALSE);
5197 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5198 SetFocus(infoPtr->hwndEdit);
5199 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5200 return infoPtr->hwndEdit;
5204 /***
5205 * DESCRIPTION:
5206 * Ensures the specified item is visible, scrolling into view if necessary.
5208 * PARAMETER(S):
5209 * [I] infoPtr : valid pointer to the listview structure
5210 * [I] nItem : item index
5211 * [I] bPartial : partially or entirely visible
5213 * RETURN:
5214 * SUCCESS : TRUE
5215 * FAILURE : FALSE
5217 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5219 INT nScrollPosHeight = 0;
5220 INT nScrollPosWidth = 0;
5221 INT nHorzAdjust = 0;
5222 INT nVertAdjust = 0;
5223 INT nHorzDiff = 0;
5224 INT nVertDiff = 0;
5225 RECT rcItem, rcTemp;
5227 rcItem.left = LVIR_BOUNDS;
5228 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5230 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5232 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5234 /* scroll left/right, but in LV_VIEW_DETAILS mode */
5235 if (infoPtr->uView == LV_VIEW_LIST)
5236 nScrollPosWidth = infoPtr->nItemWidth;
5237 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
5238 nScrollPosWidth = 1;
5240 if (rcItem.left < infoPtr->rcList.left)
5242 nHorzAdjust = -1;
5243 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5245 else
5247 nHorzAdjust = 1;
5248 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5252 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5254 /* scroll up/down, but not in LVS_LIST mode */
5255 if (infoPtr->uView == LV_VIEW_DETAILS)
5256 nScrollPosHeight = infoPtr->nItemHeight;
5257 else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
5258 nScrollPosHeight = 1;
5260 if (rcItem.top < infoPtr->rcList.top)
5262 nVertAdjust = -1;
5263 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5265 else
5267 nVertAdjust = 1;
5268 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5272 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5274 if (nScrollPosWidth)
5276 INT diff = nHorzDiff / nScrollPosWidth;
5277 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5278 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5281 if (nScrollPosHeight)
5283 INT diff = nVertDiff / nScrollPosHeight;
5284 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5285 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5288 return TRUE;
5291 /***
5292 * DESCRIPTION:
5293 * Searches for an item with specific characteristics.
5295 * PARAMETER(S):
5296 * [I] hwnd : window handle
5297 * [I] nStart : base item index
5298 * [I] lpFindInfo : item information to look for
5300 * RETURN:
5301 * SUCCESS : index of item
5302 * FAILURE : -1
5304 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5305 const LVFINDINFOW *lpFindInfo)
5307 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5308 BOOL bWrap = FALSE, bNearest = FALSE;
5309 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5310 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5311 POINT Position, Destination;
5312 LVITEMW lvItem;
5314 /* Search in virtual listviews should be done by application, not by
5315 listview control, so we just send LVN_ODFINDITEMW and return the result */
5316 if (infoPtr->dwStyle & LVS_OWNERDATA)
5318 NMLVFINDITEMW nmlv;
5320 nmlv.iStart = nStart;
5321 nmlv.lvfi = *lpFindInfo;
5322 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5325 if (!lpFindInfo || nItem < 0) return -1;
5327 lvItem.mask = 0;
5328 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5330 lvItem.mask |= LVIF_TEXT;
5331 lvItem.pszText = szDispText;
5332 lvItem.cchTextMax = DISP_TEXT_SIZE;
5335 if (lpFindInfo->flags & LVFI_WRAP)
5336 bWrap = TRUE;
5338 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5339 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
5341 POINT Origin;
5342 RECT rcArea;
5344 LISTVIEW_GetOrigin(infoPtr, &Origin);
5345 Destination.x = lpFindInfo->pt.x - Origin.x;
5346 Destination.y = lpFindInfo->pt.y - Origin.y;
5347 switch(lpFindInfo->vkDirection)
5349 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5350 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5351 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5352 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5353 case VK_HOME: Destination.x = Destination.y = 0; break;
5354 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5355 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5356 case VK_END:
5357 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5358 Destination.x = rcArea.right;
5359 Destination.y = rcArea.bottom;
5360 break;
5361 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5363 bNearest = TRUE;
5365 else Destination.x = Destination.y = 0;
5367 /* if LVFI_PARAM is specified, all other flags are ignored */
5368 if (lpFindInfo->flags & LVFI_PARAM)
5370 lvItem.mask |= LVIF_PARAM;
5371 bNearest = FALSE;
5372 lvItem.mask &= ~LVIF_TEXT;
5375 again:
5376 for (; nItem < nLast; nItem++)
5378 lvItem.iItem = nItem;
5379 lvItem.iSubItem = 0;
5380 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5382 if (lvItem.mask & LVIF_PARAM)
5384 if (lpFindInfo->lParam == lvItem.lParam)
5385 return nItem;
5386 else
5387 continue;
5390 if (lvItem.mask & LVIF_TEXT)
5392 if (lpFindInfo->flags & LVFI_PARTIAL)
5394 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5396 else
5398 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5402 if (!bNearest) return nItem;
5404 /* This is very inefficient. To do a good job here,
5405 * we need a sorted array of (x,y) item positions */
5406 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5408 /* compute the distance^2 to the destination */
5409 xdist = Destination.x - Position.x;
5410 ydist = Destination.y - Position.y;
5411 dist = xdist * xdist + ydist * ydist;
5413 /* remember the distance, and item if it's closer */
5414 if (dist < mindist)
5416 mindist = dist;
5417 nNearestItem = nItem;
5421 if (bWrap)
5423 nItem = 0;
5424 nLast = min(nStart + 1, infoPtr->nItemCount);
5425 bWrap = FALSE;
5426 goto again;
5429 return nNearestItem;
5432 /***
5433 * DESCRIPTION:
5434 * Searches for an item with specific characteristics.
5436 * PARAMETER(S):
5437 * [I] hwnd : window handle
5438 * [I] nStart : base item index
5439 * [I] lpFindInfo : item information to look for
5441 * RETURN:
5442 * SUCCESS : index of item
5443 * FAILURE : -1
5445 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5446 const LVFINDINFOA *lpFindInfo)
5448 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5449 LVFINDINFOW fiw;
5450 INT res;
5451 LPWSTR strW = NULL;
5453 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5454 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5455 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5456 textfreeT(strW, FALSE);
5457 return res;
5460 /***
5461 * DESCRIPTION:
5462 * Retrieves the background image of the listview control.
5464 * PARAMETER(S):
5465 * [I] infoPtr : valid pointer to the listview structure
5466 * [O] lpBkImage : background image attributes
5468 * RETURN:
5469 * SUCCESS : TRUE
5470 * FAILURE : FALSE
5472 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5473 /* { */
5474 /* FIXME (listview, "empty stub!\n"); */
5475 /* return FALSE; */
5476 /* } */
5478 /***
5479 * DESCRIPTION:
5480 * Retrieves column attributes.
5482 * PARAMETER(S):
5483 * [I] infoPtr : valid pointer to the listview structure
5484 * [I] nColumn : column index
5485 * [IO] lpColumn : column information
5486 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5487 * otherwise it is in fact a LPLVCOLUMNA
5489 * RETURN:
5490 * SUCCESS : TRUE
5491 * FAILURE : FALSE
5493 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5495 COLUMN_INFO *lpColumnInfo;
5496 HDITEMW hdi;
5498 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5499 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5501 /* initialize memory */
5502 ZeroMemory(&hdi, sizeof(hdi));
5504 if (lpColumn->mask & LVCF_TEXT)
5506 hdi.mask |= HDI_TEXT;
5507 hdi.pszText = lpColumn->pszText;
5508 hdi.cchTextMax = lpColumn->cchTextMax;
5511 if (lpColumn->mask & LVCF_IMAGE)
5512 hdi.mask |= HDI_IMAGE;
5514 if (lpColumn->mask & LVCF_ORDER)
5515 hdi.mask |= HDI_ORDER;
5517 if (lpColumn->mask & LVCF_SUBITEM)
5518 hdi.mask |= HDI_LPARAM;
5520 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5522 if (lpColumn->mask & LVCF_FMT)
5523 lpColumn->fmt = lpColumnInfo->fmt;
5525 if (lpColumn->mask & LVCF_WIDTH)
5526 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5528 if (lpColumn->mask & LVCF_IMAGE)
5529 lpColumn->iImage = hdi.iImage;
5531 if (lpColumn->mask & LVCF_ORDER)
5532 lpColumn->iOrder = hdi.iOrder;
5534 if (lpColumn->mask & LVCF_SUBITEM)
5535 lpColumn->iSubItem = hdi.lParam;
5537 return TRUE;
5541 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5543 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
5545 if (!lpiArray)
5546 return FALSE;
5548 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
5551 /***
5552 * DESCRIPTION:
5553 * Retrieves the column width.
5555 * PARAMETER(S):
5556 * [I] infoPtr : valid pointer to the listview structure
5557 * [I] int : column index
5559 * RETURN:
5560 * SUCCESS : column width
5561 * FAILURE : zero
5563 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5565 INT nColumnWidth = 0;
5566 HDITEMW hdItem;
5568 TRACE("nColumn=%d\n", nColumn);
5570 /* we have a 'column' in LIST and REPORT mode only */
5571 switch(infoPtr->uView)
5573 case LV_VIEW_LIST:
5574 nColumnWidth = infoPtr->nItemWidth;
5575 break;
5576 case LV_VIEW_DETAILS:
5577 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5578 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5579 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5581 * TODO: should we do the same in LVM_GETCOLUMN?
5583 hdItem.mask = HDI_WIDTH;
5584 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5586 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5587 return 0;
5589 nColumnWidth = hdItem.cxy;
5590 break;
5593 TRACE("nColumnWidth=%d\n", nColumnWidth);
5594 return nColumnWidth;
5597 /***
5598 * DESCRIPTION:
5599 * In list or report display mode, retrieves the number of items that can fit
5600 * vertically in the visible area. In icon or small icon display mode,
5601 * retrieves the total number of visible items.
5603 * PARAMETER(S):
5604 * [I] infoPtr : valid pointer to the listview structure
5606 * RETURN:
5607 * Number of fully visible items.
5609 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5611 switch (infoPtr->uView)
5613 case LV_VIEW_ICON:
5614 case LV_VIEW_SMALLICON:
5615 return infoPtr->nItemCount;
5616 case LV_VIEW_DETAILS:
5617 return LISTVIEW_GetCountPerColumn(infoPtr);
5618 case LV_VIEW_LIST:
5619 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5621 assert(FALSE);
5622 return 0;
5625 /***
5626 * DESCRIPTION:
5627 * Retrieves an image list handle.
5629 * PARAMETER(S):
5630 * [I] infoPtr : valid pointer to the listview structure
5631 * [I] nImageList : image list identifier
5633 * RETURN:
5634 * SUCCESS : image list handle
5635 * FAILURE : NULL
5637 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5639 switch (nImageList)
5641 case LVSIL_NORMAL: return infoPtr->himlNormal;
5642 case LVSIL_SMALL: return infoPtr->himlSmall;
5643 case LVSIL_STATE: return infoPtr->himlState;
5645 return NULL;
5648 /* LISTVIEW_GetISearchString */
5650 /***
5651 * DESCRIPTION:
5652 * Retrieves item attributes.
5654 * PARAMETER(S):
5655 * [I] hwnd : window handle
5656 * [IO] lpLVItem : item info
5657 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5658 * if FALSE, then lpLVItem is a LPLVITEMA.
5660 * NOTE:
5661 * This is the internal 'GetItem' interface -- it tries to
5662 * be smart and avoid text copies, if possible, by modifying
5663 * lpLVItem->pszText to point to the text string. Please note
5664 * that this is not always possible (e.g. OWNERDATA), so on
5665 * entry you *must* supply valid values for pszText, and cchTextMax.
5666 * The only difference to the documented interface is that upon
5667 * return, you should use *only* the lpLVItem->pszText, rather than
5668 * the buffer pointer you provided on input. Most code already does
5669 * that, so it's not a problem.
5670 * For the two cases when the text must be copied (that is,
5671 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5673 * RETURN:
5674 * SUCCESS : TRUE
5675 * FAILURE : FALSE
5677 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5679 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5680 NMLVDISPINFOW dispInfo;
5681 ITEM_INFO *lpItem;
5682 ITEMHDR* pItemHdr;
5683 HDPA hdpaSubItems;
5684 INT isubitem;
5686 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5688 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5689 return FALSE;
5691 if (lpLVItem->mask == 0) return TRUE;
5693 /* make a local copy */
5694 isubitem = lpLVItem->iSubItem;
5696 /* a quick optimization if all we're asked is the focus state
5697 * these queries are worth optimising since they are common,
5698 * and can be answered in constant time, without the heavy accesses */
5699 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5700 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5702 lpLVItem->state = 0;
5703 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5704 lpLVItem->state |= LVIS_FOCUSED;
5705 return TRUE;
5708 ZeroMemory(&dispInfo, sizeof(dispInfo));
5710 /* if the app stores all the data, handle it separately */
5711 if (infoPtr->dwStyle & LVS_OWNERDATA)
5713 dispInfo.item.state = 0;
5715 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5716 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
5717 ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
5719 UINT mask = lpLVItem->mask;
5721 /* NOTE: copy only fields which we _know_ are initialized, some apps
5722 * depend on the uninitialized fields being 0 */
5723 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5724 dispInfo.item.iItem = lpLVItem->iItem;
5725 dispInfo.item.iSubItem = isubitem;
5726 if (lpLVItem->mask & LVIF_TEXT)
5728 if (lpLVItem->mask & LVIF_NORECOMPUTE)
5729 /* reset mask */
5730 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
5731 else
5733 dispInfo.item.pszText = lpLVItem->pszText;
5734 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5737 if (lpLVItem->mask & LVIF_STATE)
5738 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5739 /* could be zeroed on LVIF_NORECOMPUTE case */
5740 if (dispInfo.item.mask != 0)
5742 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5743 dispInfo.item.stateMask = lpLVItem->stateMask;
5744 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5746 /* full size structure expected - _WIN32IE >= 0x560 */
5747 *lpLVItem = dispInfo.item;
5749 else if (lpLVItem->mask & LVIF_INDENT)
5751 /* indent member expected - _WIN32IE >= 0x300 */
5752 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5754 else
5756 /* minimal structure expected */
5757 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5759 lpLVItem->mask = mask;
5760 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5764 /* make sure lParam is zeroed out */
5765 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5767 /* callback marked pointer required here */
5768 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
5769 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
5771 /* we store only a little state, so if we're not asked, we're done */
5772 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5774 /* if focus is handled by us, report it */
5775 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5777 lpLVItem->state &= ~LVIS_FOCUSED;
5778 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5779 lpLVItem->state |= LVIS_FOCUSED;
5782 /* and do the same for selection, if we handle it */
5783 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5785 lpLVItem->state &= ~LVIS_SELECTED;
5786 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5787 lpLVItem->state |= LVIS_SELECTED;
5790 return TRUE;
5793 /* find the item and subitem structures before we proceed */
5794 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5795 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5796 assert (lpItem);
5798 if (isubitem)
5800 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5801 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5802 if (!lpSubItem)
5804 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5805 isubitem = 0;
5808 else
5809 pItemHdr = &lpItem->hdr;
5811 /* Do we need to query the state from the app? */
5812 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5814 dispInfo.item.mask |= LVIF_STATE;
5815 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5818 /* Do we need to enquire about the image? */
5819 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5820 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5822 dispInfo.item.mask |= LVIF_IMAGE;
5823 dispInfo.item.iImage = I_IMAGECALLBACK;
5826 /* Only items support indentation */
5827 if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
5828 (isubitem == 0))
5830 dispInfo.item.mask |= LVIF_INDENT;
5831 dispInfo.item.iIndent = I_INDENTCALLBACK;
5834 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5835 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
5836 !is_textW(pItemHdr->pszText))
5838 dispInfo.item.mask |= LVIF_TEXT;
5839 dispInfo.item.pszText = lpLVItem->pszText;
5840 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5841 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5842 *dispInfo.item.pszText = '\0';
5845 /* If we don't have all the requested info, query the application */
5846 if (dispInfo.item.mask != 0)
5848 dispInfo.item.iItem = lpLVItem->iItem;
5849 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5850 dispInfo.item.lParam = lpItem->lParam;
5851 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5852 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5855 /* we should not store values for subitems */
5856 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5858 /* Now, handle the iImage field */
5859 if (dispInfo.item.mask & LVIF_IMAGE)
5861 lpLVItem->iImage = dispInfo.item.iImage;
5862 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5863 pItemHdr->iImage = dispInfo.item.iImage;
5865 else if (lpLVItem->mask & LVIF_IMAGE)
5867 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5868 lpLVItem->iImage = pItemHdr->iImage;
5869 else
5870 lpLVItem->iImage = 0;
5873 /* The pszText field */
5874 if (dispInfo.item.mask & LVIF_TEXT)
5876 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5877 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5879 lpLVItem->pszText = dispInfo.item.pszText;
5881 else if (lpLVItem->mask & LVIF_TEXT)
5883 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
5884 if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
5885 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5888 /* Next is the lParam field */
5889 if (dispInfo.item.mask & LVIF_PARAM)
5891 lpLVItem->lParam = dispInfo.item.lParam;
5892 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5893 lpItem->lParam = dispInfo.item.lParam;
5895 else if (lpLVItem->mask & LVIF_PARAM)
5896 lpLVItem->lParam = lpItem->lParam;
5898 /* if this is a subitem, we're done */
5899 if (isubitem) return TRUE;
5901 /* ... the state field (this one is different due to uCallbackmask) */
5902 if (lpLVItem->mask & LVIF_STATE)
5904 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5905 if (dispInfo.item.mask & LVIF_STATE)
5907 lpLVItem->state &= ~dispInfo.item.stateMask;
5908 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5910 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5912 lpLVItem->state &= ~LVIS_FOCUSED;
5913 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5914 lpLVItem->state |= LVIS_FOCUSED;
5916 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5918 lpLVItem->state &= ~LVIS_SELECTED;
5919 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5920 lpLVItem->state |= LVIS_SELECTED;
5924 /* and last, but not least, the indent field */
5925 if (dispInfo.item.mask & LVIF_INDENT)
5927 lpLVItem->iIndent = dispInfo.item.iIndent;
5928 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
5929 lpItem->iIndent = dispInfo.item.iIndent;
5931 else if (lpLVItem->mask & LVIF_INDENT)
5933 lpLVItem->iIndent = lpItem->iIndent;
5936 return TRUE;
5939 /***
5940 * DESCRIPTION:
5941 * Retrieves item attributes.
5943 * PARAMETER(S):
5944 * [I] hwnd : window handle
5945 * [IO] lpLVItem : item info
5946 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5947 * if FALSE, then lpLVItem is a LPLVITEMA.
5949 * NOTE:
5950 * This is the external 'GetItem' interface -- it properly copies
5951 * the text in the provided buffer.
5953 * RETURN:
5954 * SUCCESS : TRUE
5955 * FAILURE : FALSE
5957 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5959 LPWSTR pszText;
5960 BOOL bResult;
5962 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5963 return FALSE;
5965 pszText = lpLVItem->pszText;
5966 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5967 if (bResult && lpLVItem->pszText != pszText)
5969 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
5970 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5971 else
5972 pszText = LPSTR_TEXTCALLBACKW;
5974 lpLVItem->pszText = pszText;
5976 return bResult;
5980 /***
5981 * DESCRIPTION:
5982 * Retrieves the position (upper-left) of the listview control item.
5983 * Note that for LVS_ICON style, the upper-left is that of the icon
5984 * and not the bounding box.
5986 * PARAMETER(S):
5987 * [I] infoPtr : valid pointer to the listview structure
5988 * [I] nItem : item index
5989 * [O] lpptPosition : coordinate information
5991 * RETURN:
5992 * SUCCESS : TRUE
5993 * FAILURE : FALSE
5995 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5997 POINT Origin;
5999 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
6001 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6003 LISTVIEW_GetOrigin(infoPtr, &Origin);
6004 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
6006 if (infoPtr->uView == LV_VIEW_ICON)
6008 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6009 lpptPosition->y += ICON_TOP_PADDING;
6011 lpptPosition->x += Origin.x;
6012 lpptPosition->y += Origin.y;
6014 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
6015 return TRUE;
6019 /***
6020 * DESCRIPTION:
6021 * Retrieves the bounding rectangle for a listview control item.
6023 * PARAMETER(S):
6024 * [I] infoPtr : valid pointer to the listview structure
6025 * [I] nItem : item index
6026 * [IO] lprc : bounding rectangle coordinates
6027 * lprc->left specifies the portion of the item for which the bounding
6028 * rectangle will be retrieved.
6030 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
6031 * including the icon and label.
6033 * * For LVS_ICON
6034 * * Experiment shows that native control returns:
6035 * * width = min (48, length of text line)
6036 * * .left = position.x - (width - iconsize.cx)/2
6037 * * .right = .left + width
6038 * * height = #lines of text * ntmHeight + icon height + 8
6039 * * .top = position.y - 2
6040 * * .bottom = .top + height
6041 * * separation between items .y = itemSpacing.cy - height
6042 * * .x = itemSpacing.cx - width
6043 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
6045 * * For LVS_ICON
6046 * * Experiment shows that native control returns:
6047 * * width = iconSize.cx + 16
6048 * * .left = position.x - (width - iconsize.cx)/2
6049 * * .right = .left + width
6050 * * height = iconSize.cy + 4
6051 * * .top = position.y - 2
6052 * * .bottom = .top + height
6053 * * separation between items .y = itemSpacing.cy - height
6054 * * .x = itemSpacing.cx - width
6055 * LVIR_LABEL Returns the bounding rectangle of the item text.
6057 * * For LVS_ICON
6058 * * Experiment shows that native control returns:
6059 * * width = text length
6060 * * .left = position.x - width/2
6061 * * .right = .left + width
6062 * * height = ntmH * linecount + 2
6063 * * .top = position.y + iconSize.cy + 6
6064 * * .bottom = .top + height
6065 * * separation between items .y = itemSpacing.cy - height
6066 * * .x = itemSpacing.cx - width
6067 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
6068 * rectangles, but excludes columns in report view.
6070 * RETURN:
6071 * SUCCESS : TRUE
6072 * FAILURE : FALSE
6074 * NOTES
6075 * Note that the bounding rectangle of the label in the LVS_ICON view depends
6076 * upon whether the window has the focus currently and on whether the item
6077 * is the one with the focus. Ensure that the control's record of which
6078 * item has the focus agrees with the items' records.
6080 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6082 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6083 BOOL doLabel = TRUE, oversizedBox = FALSE;
6084 POINT Position, Origin;
6085 LVITEMW lvItem;
6087 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
6089 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6091 LISTVIEW_GetOrigin(infoPtr, &Origin);
6092 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6094 /* Be smart and try to figure out the minimum we have to do */
6095 if (lprc->left == LVIR_ICON) doLabel = FALSE;
6096 if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
6097 if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
6098 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
6099 oversizedBox = TRUE;
6101 /* get what we need from the item before hand, so we make
6102 * only one request. This can speed up things, if data
6103 * is stored on the app side */
6104 lvItem.mask = 0;
6105 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6106 if (doLabel) lvItem.mask |= LVIF_TEXT;
6107 lvItem.iItem = nItem;
6108 lvItem.iSubItem = 0;
6109 lvItem.pszText = szDispText;
6110 lvItem.cchTextMax = DISP_TEXT_SIZE;
6111 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6112 /* we got the state already up, simulate it here, to avoid a reget */
6113 if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
6115 lvItem.mask |= LVIF_STATE;
6116 lvItem.stateMask = LVIS_FOCUSED;
6117 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
6120 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
6121 lprc->left = LVIR_BOUNDS;
6122 switch(lprc->left)
6124 case LVIR_ICON:
6125 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6126 break;
6128 case LVIR_LABEL:
6129 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
6130 break;
6132 case LVIR_BOUNDS:
6133 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6134 break;
6136 case LVIR_SELECTBOUNDS:
6137 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
6138 break;
6140 default:
6141 WARN("Unknown value: %d\n", lprc->left);
6142 return FALSE;
6145 if (infoPtr->uView == LV_VIEW_DETAILS)
6146 OffsetRect(lprc, Origin.x, Position.y + Origin.y);
6147 else
6148 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
6150 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
6152 return TRUE;
6155 /***
6156 * DESCRIPTION:
6157 * Retrieves the spacing between listview control items.
6159 * PARAMETER(S):
6160 * [I] infoPtr : valid pointer to the listview structure
6161 * [IO] lprc : rectangle to receive the output
6162 * on input, lprc->top = nSubItem
6163 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6165 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6166 * not only those of the first column.
6167 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6169 * RETURN:
6170 * TRUE: success
6171 * FALSE: failure
6173 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6175 POINT Position;
6176 LVITEMW lvItem;
6177 INT nColumn;
6179 if (!lprc) return FALSE;
6181 nColumn = lprc->top;
6183 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6184 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6185 if (lprc->top == 0)
6186 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6188 if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
6190 /* special case for header items */
6191 if (nItem == -1)
6193 if (lprc->left != LVIR_BOUNDS)
6195 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6196 return FALSE;
6199 if (infoPtr->hwndHeader)
6200 return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
6201 else
6203 memset(lprc, 0, sizeof(RECT));
6204 return TRUE;
6208 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6210 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6212 lvItem.mask = 0;
6213 lvItem.iItem = nItem;
6214 lvItem.iSubItem = nColumn;
6216 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6217 switch(lprc->left)
6219 case LVIR_ICON:
6220 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6221 break;
6223 case LVIR_LABEL:
6224 case LVIR_BOUNDS:
6225 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6226 break;
6228 default:
6229 ERR("Unknown bounds=%d\n", lprc->left);
6230 return FALSE;
6233 OffsetRect(lprc, 0, Position.y);
6234 return TRUE;
6238 /***
6239 * DESCRIPTION:
6240 * Retrieves the width of a label.
6242 * PARAMETER(S):
6243 * [I] infoPtr : valid pointer to the listview structure
6245 * RETURN:
6246 * SUCCESS : string width (in pixels)
6247 * FAILURE : zero
6249 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6251 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6252 LVITEMW lvItem;
6254 TRACE("(nItem=%d)\n", nItem);
6256 lvItem.mask = LVIF_TEXT;
6257 lvItem.iItem = nItem;
6258 lvItem.iSubItem = 0;
6259 lvItem.pszText = szDispText;
6260 lvItem.cchTextMax = DISP_TEXT_SIZE;
6261 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6263 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6266 /***
6267 * DESCRIPTION:
6268 * Retrieves the spacing between listview control items.
6270 * PARAMETER(S):
6271 * [I] infoPtr : valid pointer to the listview structure
6272 * [I] bSmall : flag for small or large icon
6274 * RETURN:
6275 * Horizontal + vertical spacing
6277 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6279 LONG lResult;
6281 if (!bSmall)
6283 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6285 else
6287 if (infoPtr->uView == LV_VIEW_ICON)
6288 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6289 else
6290 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6292 return lResult;
6295 /***
6296 * DESCRIPTION:
6297 * Retrieves the state of a listview control item.
6299 * PARAMETER(S):
6300 * [I] infoPtr : valid pointer to the listview structure
6301 * [I] nItem : item index
6302 * [I] uMask : state mask
6304 * RETURN:
6305 * State specified by the mask.
6307 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6309 LVITEMW lvItem;
6311 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6313 lvItem.iItem = nItem;
6314 lvItem.iSubItem = 0;
6315 lvItem.mask = LVIF_STATE;
6316 lvItem.stateMask = uMask;
6317 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6319 return lvItem.state & uMask;
6322 /***
6323 * DESCRIPTION:
6324 * Retrieves the text of a listview control item or subitem.
6326 * PARAMETER(S):
6327 * [I] hwnd : window handle
6328 * [I] nItem : item index
6329 * [IO] lpLVItem : item information
6330 * [I] isW : TRUE if lpLVItem is Unicode
6332 * RETURN:
6333 * SUCCESS : string length
6334 * FAILURE : 0
6336 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6338 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6340 lpLVItem->mask = LVIF_TEXT;
6341 lpLVItem->iItem = nItem;
6342 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6344 return textlenT(lpLVItem->pszText, isW);
6347 /***
6348 * DESCRIPTION:
6349 * Searches for an item based on properties + relationships.
6351 * PARAMETER(S):
6352 * [I] infoPtr : valid pointer to the listview structure
6353 * [I] nItem : item index
6354 * [I] uFlags : relationship flag
6356 * RETURN:
6357 * SUCCESS : item index
6358 * FAILURE : -1
6360 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6362 UINT uMask = 0;
6363 LVFINDINFOW lvFindInfo;
6364 INT nCountPerColumn;
6365 INT nCountPerRow;
6366 INT i;
6368 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6369 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6371 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6373 if (uFlags & LVNI_CUT)
6374 uMask |= LVIS_CUT;
6376 if (uFlags & LVNI_DROPHILITED)
6377 uMask |= LVIS_DROPHILITED;
6379 if (uFlags & LVNI_FOCUSED)
6380 uMask |= LVIS_FOCUSED;
6382 if (uFlags & LVNI_SELECTED)
6383 uMask |= LVIS_SELECTED;
6385 /* if we're asked for the focused item, that's only one,
6386 * so it's worth optimizing */
6387 if (uFlags & LVNI_FOCUSED)
6389 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6390 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6393 if (uFlags & LVNI_ABOVE)
6395 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6397 while (nItem >= 0)
6399 nItem--;
6400 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6401 return nItem;
6404 else
6406 /* Special case for autoarrange - move 'til the top of a list */
6407 if (is_autoarrange(infoPtr))
6409 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6410 while (nItem - nCountPerRow >= 0)
6412 nItem -= nCountPerRow;
6413 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6414 return nItem;
6416 return -1;
6418 lvFindInfo.flags = LVFI_NEARESTXY;
6419 lvFindInfo.vkDirection = VK_UP;
6420 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6421 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6423 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6424 return nItem;
6428 else if (uFlags & LVNI_BELOW)
6430 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6432 while (nItem < infoPtr->nItemCount)
6434 nItem++;
6435 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6436 return nItem;
6439 else
6441 /* Special case for autoarrange - move 'til the bottom of a list */
6442 if (is_autoarrange(infoPtr))
6444 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6445 while (nItem + nCountPerRow < infoPtr->nItemCount )
6447 nItem += nCountPerRow;
6448 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6449 return nItem;
6451 return -1;
6453 lvFindInfo.flags = LVFI_NEARESTXY;
6454 lvFindInfo.vkDirection = VK_DOWN;
6455 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6456 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6458 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6459 return nItem;
6463 else if (uFlags & LVNI_TOLEFT)
6465 if (infoPtr->uView == LV_VIEW_LIST)
6467 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6468 while (nItem - nCountPerColumn >= 0)
6470 nItem -= nCountPerColumn;
6471 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6472 return nItem;
6475 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6477 /* Special case for autoarrange - move 'til the beginning of a row */
6478 if (is_autoarrange(infoPtr))
6480 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6481 while (nItem % nCountPerRow > 0)
6483 nItem --;
6484 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6485 return nItem;
6487 return -1;
6489 lvFindInfo.flags = LVFI_NEARESTXY;
6490 lvFindInfo.vkDirection = VK_LEFT;
6491 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6492 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6494 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6495 return nItem;
6499 else if (uFlags & LVNI_TORIGHT)
6501 if (infoPtr->uView == LV_VIEW_LIST)
6503 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6504 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6506 nItem += nCountPerColumn;
6507 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6508 return nItem;
6511 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6513 /* Special case for autoarrange - move 'til the end of a row */
6514 if (is_autoarrange(infoPtr))
6516 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6517 while (nItem % nCountPerRow < nCountPerRow - 1 )
6519 nItem ++;
6520 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6521 return nItem;
6523 return -1;
6525 lvFindInfo.flags = LVFI_NEARESTXY;
6526 lvFindInfo.vkDirection = VK_RIGHT;
6527 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6528 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6530 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6531 return nItem;
6535 else
6537 nItem++;
6539 /* search by index */
6540 for (i = nItem; i < infoPtr->nItemCount; i++)
6542 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6543 return i;
6547 return -1;
6550 /* LISTVIEW_GetNumberOfWorkAreas */
6552 /***
6553 * DESCRIPTION:
6554 * Retrieves the origin coordinates when in icon or small icon display mode.
6556 * PARAMETER(S):
6557 * [I] infoPtr : valid pointer to the listview structure
6558 * [O] lpptOrigin : coordinate information
6560 * RETURN:
6561 * None.
6563 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6565 INT nHorzPos = 0, nVertPos = 0;
6566 SCROLLINFO scrollInfo;
6568 scrollInfo.cbSize = sizeof(SCROLLINFO);
6569 scrollInfo.fMask = SIF_POS;
6571 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6572 nHorzPos = scrollInfo.nPos;
6573 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6574 nVertPos = scrollInfo.nPos;
6576 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6578 lpptOrigin->x = infoPtr->rcList.left;
6579 lpptOrigin->y = infoPtr->rcList.top;
6580 if (infoPtr->uView == LV_VIEW_LIST)
6581 nHorzPos *= infoPtr->nItemWidth;
6582 else if (infoPtr->uView == LV_VIEW_DETAILS)
6583 nVertPos *= infoPtr->nItemHeight;
6585 lpptOrigin->x -= nHorzPos;
6586 lpptOrigin->y -= nVertPos;
6588 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6591 /***
6592 * DESCRIPTION:
6593 * Retrieves the width of a string.
6595 * PARAMETER(S):
6596 * [I] hwnd : window handle
6597 * [I] lpszText : text string to process
6598 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6600 * RETURN:
6601 * SUCCESS : string width (in pixels)
6602 * FAILURE : zero
6604 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6606 SIZE stringSize;
6608 stringSize.cx = 0;
6609 if (is_textT(lpszText, isW))
6611 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6612 HDC hdc = GetDC(infoPtr->hwndSelf);
6613 HFONT hOldFont = SelectObject(hdc, hFont);
6615 if (isW)
6616 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6617 else
6618 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6619 SelectObject(hdc, hOldFont);
6620 ReleaseDC(infoPtr->hwndSelf, hdc);
6622 return stringSize.cx;
6625 /***
6626 * DESCRIPTION:
6627 * Determines which listview item is located at the specified position.
6629 * PARAMETER(S):
6630 * [I] infoPtr : valid pointer to the listview structure
6631 * [IO] lpht : hit test information
6632 * [I] subitem : fill out iSubItem.
6633 * [I] select : return the index only if the hit selects the item
6635 * NOTE:
6636 * (mm 20001022): We must not allow iSubItem to be touched, for
6637 * an app might pass only a structure with space up to iItem!
6638 * (MS Office 97 does that for instance in the file open dialog)
6640 * RETURN:
6641 * SUCCESS : item index
6642 * FAILURE : -1
6644 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6646 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6647 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6648 POINT Origin, Position, opt;
6649 LVITEMW lvItem;
6650 ITERATOR i;
6651 INT iItem;
6653 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6655 lpht->flags = 0;
6656 lpht->iItem = -1;
6657 if (subitem) lpht->iSubItem = 0;
6659 if (infoPtr->rcList.left > lpht->pt.x)
6660 lpht->flags |= LVHT_TOLEFT;
6661 else if (infoPtr->rcList.right < lpht->pt.x)
6662 lpht->flags |= LVHT_TORIGHT;
6664 if (infoPtr->rcList.top > lpht->pt.y)
6665 lpht->flags |= LVHT_ABOVE;
6666 else if (infoPtr->rcList.bottom < lpht->pt.y)
6667 lpht->flags |= LVHT_BELOW;
6669 TRACE("lpht->flags=0x%x\n", lpht->flags);
6670 if (lpht->flags) return -1;
6672 lpht->flags |= LVHT_NOWHERE;
6674 LISTVIEW_GetOrigin(infoPtr, &Origin);
6676 /* first deal with the large items */
6677 rcSearch.left = lpht->pt.x;
6678 rcSearch.top = lpht->pt.y;
6679 rcSearch.right = rcSearch.left + 1;
6680 rcSearch.bottom = rcSearch.top + 1;
6682 iterator_frameditems(&i, infoPtr, &rcSearch);
6683 iterator_next(&i); /* go to first item in the sequence */
6684 iItem = i.nItem;
6685 iterator_destroy(&i);
6687 TRACE("lpht->iItem=%d\n", iItem);
6688 if (iItem == -1) return -1;
6690 if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
6692 RECT bounds, *pRect;
6693 INT j;
6695 /* for top/bottom only */
6696 bounds.left = LVIR_BOUNDS;
6697 LISTVIEW_GetItemRect(infoPtr, iItem, &bounds);
6698 opt.x = lpht->pt.x - Origin.x;
6699 opt.y = lpht->pt.y;
6701 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6703 pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
6704 bounds.left = pRect->left;
6705 bounds.right = pRect->right;
6707 if (PtInRect(&bounds, opt))
6709 lpht->iSubItem = j;
6710 break;
6713 TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
6716 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6717 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6718 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6719 if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6720 lvItem.iItem = iItem;
6721 lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
6722 lvItem.pszText = szDispText;
6723 lvItem.cchTextMax = DISP_TEXT_SIZE;
6724 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6725 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6727 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6728 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6729 opt.x = lpht->pt.x - Position.x - Origin.x;
6730 opt.y = lpht->pt.y - Position.y - Origin.y;
6732 if (infoPtr->uView == LV_VIEW_DETAILS)
6734 rcBounds = rcBox;
6735 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
6736 opt.x = lpht->pt.x - Origin.x;
6738 else
6740 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6741 UnionRect(&rcBounds, &rcBounds, &rcState);
6743 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6744 if (!PtInRect(&rcBounds, opt)) return -1;
6746 if (PtInRect(&rcIcon, opt))
6747 lpht->flags |= LVHT_ONITEMICON;
6748 else if (PtInRect(&rcLabel, opt))
6749 lpht->flags |= LVHT_ONITEMLABEL;
6750 else if (infoPtr->himlState && PtInRect(&rcState, opt))
6751 lpht->flags |= LVHT_ONITEMSTATEICON;
6752 /* special case for LVS_EX_FULLROWSELECT */
6753 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
6754 !(lpht->flags & LVHT_ONITEM))
6756 lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
6758 if (lpht->flags & LVHT_ONITEM)
6759 lpht->flags &= ~LVHT_NOWHERE;
6760 TRACE("lpht->flags=0x%x\n", lpht->flags);
6762 if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
6763 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6764 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6766 if (infoPtr->uView == LV_VIEW_DETAILS)
6768 /* get main item bounds */
6769 lvItem.iSubItem = 0;
6770 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6771 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6772 UnionRect(&rcBounds, &rcBounds, &rcState);
6774 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6776 return lpht->iItem = iItem;
6779 /***
6780 * DESCRIPTION:
6781 * Inserts a new item in the listview control.
6783 * PARAMETER(S):
6784 * [I] infoPtr : valid pointer to the listview structure
6785 * [I] lpLVItem : item information
6786 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6788 * RETURN:
6789 * SUCCESS : new item index
6790 * FAILURE : -1
6792 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6794 INT nItem;
6795 HDPA hdpaSubItems;
6796 NMLISTVIEW nmlv;
6797 ITEM_INFO *lpItem;
6798 BOOL is_sorted, has_changed;
6799 LVITEMW item;
6800 HWND hwndSelf = infoPtr->hwndSelf;
6802 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6804 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6806 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6807 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6809 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6811 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6813 /* insert item in listview control data structure */
6814 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6815 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6817 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6818 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6820 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6822 /* calculate new item index */
6823 if (is_sorted)
6825 HDPA hItem;
6826 ITEM_INFO *item_s;
6827 INT i = 0, cmpv;
6829 while (i < infoPtr->nItemCount)
6831 hItem = DPA_GetPtr( infoPtr->hdpaItems, i);
6832 item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
6834 cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, TRUE);
6835 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
6837 if (cmpv >= 0) break;
6838 i++;
6840 nItem = i;
6842 else
6843 nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
6845 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6846 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6847 if (nItem == -1) goto fail;
6848 infoPtr->nItemCount++;
6850 /* shift indices first so they don't get tangled */
6851 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6853 /* set the item attributes */
6854 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6856 /* full size structure expected - _WIN32IE >= 0x560 */
6857 item = *lpLVItem;
6859 else if (lpLVItem->mask & LVIF_INDENT)
6861 /* indent member expected - _WIN32IE >= 0x300 */
6862 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6864 else
6866 /* minimal structure expected */
6867 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6869 item.iItem = nItem;
6870 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6872 item.mask |= LVIF_STATE;
6873 item.stateMask |= LVIS_STATEIMAGEMASK;
6874 item.state &= ~LVIS_STATEIMAGEMASK;
6875 item.state |= INDEXTOSTATEIMAGEMASK(1);
6877 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6879 /* make room for the position, if we are in the right mode */
6880 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6882 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6883 goto undo;
6884 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6886 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6887 goto undo;
6891 /* send LVN_INSERTITEM notification */
6892 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6893 nmlv.iItem = nItem;
6894 nmlv.lParam = lpItem->lParam;
6895 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6896 if (!IsWindow(hwndSelf))
6897 return -1;
6899 /* align items (set position of each item) */
6900 if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
6902 POINT pt;
6904 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6905 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6906 else
6907 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6909 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6912 /* now is the invalidation fun */
6913 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6914 return nItem;
6916 undo:
6917 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6918 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6919 infoPtr->nItemCount--;
6920 fail:
6921 DPA_DeletePtr(hdpaSubItems, 0);
6922 DPA_Destroy (hdpaSubItems);
6923 Free (lpItem);
6924 return -1;
6927 /***
6928 * DESCRIPTION:
6929 * Redraws a range of items.
6931 * PARAMETER(S):
6932 * [I] infoPtr : valid pointer to the listview structure
6933 * [I] nFirst : first item
6934 * [I] nLast : last item
6936 * RETURN:
6937 * SUCCESS : TRUE
6938 * FAILURE : FALSE
6940 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6942 INT i;
6944 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6945 max(nFirst, nLast) >= infoPtr->nItemCount)
6946 return FALSE;
6948 for (i = nFirst; i <= nLast; i++)
6949 LISTVIEW_InvalidateItem(infoPtr, i);
6951 return TRUE;
6954 /***
6955 * DESCRIPTION:
6956 * Scroll the content of a listview.
6958 * PARAMETER(S):
6959 * [I] infoPtr : valid pointer to the listview structure
6960 * [I] dx : horizontal scroll amount in pixels
6961 * [I] dy : vertical scroll amount in pixels
6963 * RETURN:
6964 * SUCCESS : TRUE
6965 * FAILURE : FALSE
6967 * COMMENTS:
6968 * If the control is in report view (LV_VIEW_DETAILS) the control can
6969 * be scrolled only in line increments. "dy" will be rounded to the
6970 * nearest number of pixels that are a whole line. Ex: if line height
6971 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6972 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6974 * For: (per experimentation with native control and CSpy ListView)
6975 * LV_VIEW_ICON dy=1 = 1 pixel (vertical only)
6976 * dx ignored
6977 * LV_VIEW_SMALLICON dy=1 = 1 pixel (vertical only)
6978 * dx ignored
6979 * LV_VIEW_LIST dx=1 = 1 column (horizontal only)
6980 * but will only scroll 1 column per message
6981 * no matter what the value.
6982 * dy must be 0 or FALSE returned.
6983 * LV_VIEW_DETAILS dx=1 = 1 pixel
6984 * dy= see above
6987 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6989 switch(infoPtr->uView) {
6990 case LV_VIEW_DETAILS:
6991 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6992 dy /= infoPtr->nItemHeight;
6993 break;
6994 case LV_VIEW_LIST:
6995 if (dy != 0) return FALSE;
6996 break;
6997 default: /* icon */
6998 dx = 0;
6999 break;
7002 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
7003 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
7005 return TRUE;
7008 /***
7009 * DESCRIPTION:
7010 * Sets the background color.
7012 * PARAMETER(S):
7013 * [I] infoPtr : valid pointer to the listview structure
7014 * [I] clrBk : background color
7016 * RETURN:
7017 * SUCCESS : TRUE
7018 * FAILURE : FALSE
7020 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
7022 TRACE("(clrBk=%x)\n", clrBk);
7024 if(infoPtr->clrBk != clrBk) {
7025 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7026 infoPtr->clrBk = clrBk;
7027 if (clrBk == CLR_NONE)
7028 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
7029 else
7030 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
7031 LISTVIEW_InvalidateList(infoPtr);
7034 return TRUE;
7037 /* LISTVIEW_SetBkImage */
7039 /*** Helper for {Insert,Set}ColumnT *only* */
7040 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
7041 const LVCOLUMNW *lpColumn, BOOL isW)
7043 if (lpColumn->mask & LVCF_FMT)
7045 /* format member is valid */
7046 lphdi->mask |= HDI_FORMAT;
7048 /* set text alignment (leftmost column must be left-aligned) */
7049 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7050 lphdi->fmt |= HDF_LEFT;
7051 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
7052 lphdi->fmt |= HDF_RIGHT;
7053 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
7054 lphdi->fmt |= HDF_CENTER;
7056 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7057 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
7059 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7061 lphdi->fmt |= HDF_IMAGE;
7062 lphdi->iImage = I_IMAGECALLBACK;
7066 if (lpColumn->mask & LVCF_WIDTH)
7068 lphdi->mask |= HDI_WIDTH;
7069 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
7071 /* make it fill the remainder of the controls width */
7072 RECT rcHeader;
7073 INT item_index;
7075 for(item_index = 0; item_index < (nColumn - 1); item_index++)
7077 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
7078 lphdi->cxy += rcHeader.right - rcHeader.left;
7081 /* retrieve the layout of the header */
7082 GetClientRect(infoPtr->hwndSelf, &rcHeader);
7083 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
7085 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
7087 else
7088 lphdi->cxy = lpColumn->cx;
7091 if (lpColumn->mask & LVCF_TEXT)
7093 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
7094 lphdi->fmt |= HDF_STRING;
7095 lphdi->pszText = lpColumn->pszText;
7096 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
7099 if (lpColumn->mask & LVCF_IMAGE)
7101 lphdi->mask |= HDI_IMAGE;
7102 lphdi->iImage = lpColumn->iImage;
7105 if (lpColumn->mask & LVCF_ORDER)
7107 lphdi->mask |= HDI_ORDER;
7108 lphdi->iOrder = lpColumn->iOrder;
7113 /***
7114 * DESCRIPTION:
7115 * Inserts a new column.
7117 * PARAMETER(S):
7118 * [I] infoPtr : valid pointer to the listview structure
7119 * [I] nColumn : column index
7120 * [I] lpColumn : column information
7121 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
7123 * RETURN:
7124 * SUCCESS : new column index
7125 * FAILURE : -1
7127 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
7128 const LVCOLUMNW *lpColumn, BOOL isW)
7130 COLUMN_INFO *lpColumnInfo;
7131 INT nNewColumn;
7132 HDITEMW hdi;
7134 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7136 if (!lpColumn || nColumn < 0) return -1;
7137 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
7139 ZeroMemory(&hdi, sizeof(HDITEMW));
7140 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7143 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
7144 * (can be seen in SPY) otherwise column never gets added.
7146 if (!(lpColumn->mask & LVCF_WIDTH)) {
7147 hdi.mask |= HDI_WIDTH;
7148 hdi.cxy = 10;
7152 * when the iSubItem is available Windows copies it to the header lParam. It seems
7153 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7155 if (lpColumn->mask & LVCF_SUBITEM)
7157 hdi.mask |= HDI_LPARAM;
7158 hdi.lParam = lpColumn->iSubItem;
7161 /* create header if not present */
7162 LISTVIEW_CreateHeader(infoPtr);
7163 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
7164 (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
7166 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7169 /* insert item in header control */
7170 nNewColumn = SendMessageW(infoPtr->hwndHeader,
7171 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7172 (WPARAM)nColumn, (LPARAM)&hdi);
7173 if (nNewColumn == -1) return -1;
7174 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7176 /* create our own column info */
7177 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7178 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7180 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7181 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
7182 goto fail;
7184 /* now we have to actually adjust the data */
7185 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7187 SUBITEM_INFO *lpSubItem;
7188 HDPA hdpaSubItems;
7189 INT nItem, i;
7191 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7193 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7194 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7196 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7197 if (lpSubItem->iSubItem >= nNewColumn)
7198 lpSubItem->iSubItem++;
7203 /* make space for the new column */
7204 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7205 LISTVIEW_UpdateItemSize(infoPtr);
7207 return nNewColumn;
7209 fail:
7210 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7211 if (lpColumnInfo)
7213 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7214 Free(lpColumnInfo);
7216 return -1;
7219 /***
7220 * DESCRIPTION:
7221 * Sets the attributes of a header item.
7223 * PARAMETER(S):
7224 * [I] infoPtr : valid pointer to the listview structure
7225 * [I] nColumn : column index
7226 * [I] lpColumn : column attributes
7227 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7229 * RETURN:
7230 * SUCCESS : TRUE
7231 * FAILURE : FALSE
7233 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7234 const LVCOLUMNW *lpColumn, BOOL isW)
7236 HDITEMW hdi, hdiget;
7237 BOOL bResult;
7239 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7241 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7243 ZeroMemory(&hdi, sizeof(HDITEMW));
7244 if (lpColumn->mask & LVCF_FMT)
7246 hdi.mask |= HDI_FORMAT;
7247 hdiget.mask = HDI_FORMAT;
7248 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
7249 hdi.fmt = hdiget.fmt & HDF_STRING;
7251 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7253 /* set header item attributes */
7254 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7255 if (!bResult) return FALSE;
7257 if (lpColumn->mask & LVCF_FMT)
7259 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7260 int oldFmt = lpColumnInfo->fmt;
7262 lpColumnInfo->fmt = lpColumn->fmt;
7263 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7265 if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7269 return TRUE;
7272 /***
7273 * DESCRIPTION:
7274 * Sets the column order array
7276 * PARAMETERS:
7277 * [I] infoPtr : valid pointer to the listview structure
7278 * [I] iCount : number of elements in column order array
7279 * [I] lpiArray : pointer to column order array
7281 * RETURN:
7282 * SUCCESS : TRUE
7283 * FAILURE : FALSE
7285 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7287 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7289 if (!lpiArray)
7290 return FALSE;
7292 return TRUE;
7296 /***
7297 * DESCRIPTION:
7298 * Sets the width of a column
7300 * PARAMETERS:
7301 * [I] infoPtr : valid pointer to the listview structure
7302 * [I] nColumn : column index
7303 * [I] cx : column width
7305 * RETURN:
7306 * SUCCESS : TRUE
7307 * FAILURE : FALSE
7309 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7311 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7312 INT max_cx = 0;
7313 HDITEMW hdi;
7315 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7317 /* set column width only if in report or list mode */
7318 if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
7320 /* take care of invalid cx values */
7321 if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE;
7322 else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE;
7324 /* resize all columns if in LV_VIEW_LIST mode */
7325 if(infoPtr->uView == LV_VIEW_LIST)
7327 infoPtr->nItemWidth = cx;
7328 LISTVIEW_InvalidateList(infoPtr);
7329 return TRUE;
7332 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7334 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7336 INT nLabelWidth;
7337 LVITEMW lvItem;
7339 lvItem.mask = LVIF_TEXT;
7340 lvItem.iItem = 0;
7341 lvItem.iSubItem = nColumn;
7342 lvItem.pszText = szDispText;
7343 lvItem.cchTextMax = DISP_TEXT_SIZE;
7344 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7346 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7347 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7348 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7350 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7351 max_cx += infoPtr->iconSize.cx;
7352 max_cx += TRAILING_LABEL_PADDING;
7355 /* autosize based on listview items width */
7356 if(cx == LVSCW_AUTOSIZE)
7357 cx = max_cx;
7358 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7360 /* if iCol is the last column make it fill the remainder of the controls width */
7361 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7363 RECT rcHeader;
7364 POINT Origin;
7366 LISTVIEW_GetOrigin(infoPtr, &Origin);
7367 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7369 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7371 else
7373 /* Despite what the MS docs say, if this is not the last
7374 column, then MS resizes the column to the width of the
7375 largest text string in the column, including headers
7376 and items. This is different from LVSCW_AUTOSIZE in that
7377 LVSCW_AUTOSIZE ignores the header string length. */
7378 cx = 0;
7380 /* retrieve header text */
7381 hdi.mask = HDI_TEXT;
7382 hdi.cchTextMax = DISP_TEXT_SIZE;
7383 hdi.pszText = szDispText;
7384 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
7386 HDC hdc = GetDC(infoPtr->hwndSelf);
7387 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7388 SIZE size;
7390 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7391 cx = size.cx + TRAILING_HEADER_PADDING;
7392 /* FIXME: Take into account the header image, if one is present */
7393 SelectObject(hdc, old_font);
7394 ReleaseDC(infoPtr->hwndSelf, hdc);
7396 cx = max (cx, max_cx);
7400 if (cx < 0) return FALSE;
7402 /* call header to update the column change */
7403 hdi.mask = HDI_WIDTH;
7404 hdi.cxy = cx;
7405 TRACE("hdi.cxy=%d\n", hdi.cxy);
7406 return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
7409 /***
7410 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7413 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7415 HDC hdc_wnd, hdc;
7416 HBITMAP hbm_im, hbm_mask, hbm_orig;
7417 RECT rc;
7418 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7419 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7420 HIMAGELIST himl;
7422 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7423 ILC_COLOR | ILC_MASK, 2, 2);
7424 hdc_wnd = GetDC(infoPtr->hwndSelf);
7425 hdc = CreateCompatibleDC(hdc_wnd);
7426 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7427 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7428 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7430 rc.left = rc.top = 0;
7431 rc.right = GetSystemMetrics(SM_CXSMICON);
7432 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7434 hbm_orig = SelectObject(hdc, hbm_mask);
7435 FillRect(hdc, &rc, hbr_white);
7436 InflateRect(&rc, -2, -2);
7437 FillRect(hdc, &rc, hbr_black);
7439 SelectObject(hdc, hbm_im);
7440 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7441 SelectObject(hdc, hbm_orig);
7442 ImageList_Add(himl, hbm_im, hbm_mask);
7444 SelectObject(hdc, hbm_im);
7445 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7446 SelectObject(hdc, hbm_orig);
7447 ImageList_Add(himl, hbm_im, hbm_mask);
7449 DeleteObject(hbm_mask);
7450 DeleteObject(hbm_im);
7451 DeleteDC(hdc);
7453 return himl;
7456 /***
7457 * DESCRIPTION:
7458 * Sets the extended listview style.
7460 * PARAMETERS:
7461 * [I] infoPtr : valid pointer to the listview structure
7462 * [I] dwMask : mask
7463 * [I] dwStyle : style
7465 * RETURN:
7466 * SUCCESS : previous style
7467 * FAILURE : 0
7469 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7471 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7473 /* set new style */
7474 if (dwMask)
7475 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7476 else
7477 infoPtr->dwLvExStyle = dwExStyle;
7479 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7481 HIMAGELIST himl = 0;
7482 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7484 LVITEMW item;
7485 item.mask = LVIF_STATE;
7486 item.stateMask = LVIS_STATEIMAGEMASK;
7487 item.state = INDEXTOSTATEIMAGEMASK(1);
7488 LISTVIEW_SetItemState(infoPtr, -1, &item);
7490 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7492 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7495 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7497 DWORD dwStyle;
7499 /* if not already created */
7500 LISTVIEW_CreateHeader(infoPtr);
7502 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7503 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7504 dwStyle |= HDS_DRAGDROP;
7505 else
7506 dwStyle &= ~HDS_DRAGDROP;
7507 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7510 /* GRIDLINES adds decoration at top so changes sizes */
7511 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7513 LISTVIEW_UpdateSize(infoPtr);
7517 LISTVIEW_InvalidateList(infoPtr);
7518 return dwOldExStyle;
7521 /***
7522 * DESCRIPTION:
7523 * Sets the new hot cursor used during hot tracking and hover selection.
7525 * PARAMETER(S):
7526 * [I] infoPtr : valid pointer to the listview structure
7527 * [I] hCursor : the new hot cursor handle
7529 * RETURN:
7530 * Returns the previous hot cursor
7532 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7534 HCURSOR oldCursor = infoPtr->hHotCursor;
7536 infoPtr->hHotCursor = hCursor;
7538 return oldCursor;
7542 /***
7543 * DESCRIPTION:
7544 * Sets the hot item index.
7546 * PARAMETERS:
7547 * [I] infoPtr : valid pointer to the listview structure
7548 * [I] iIndex : index
7550 * RETURN:
7551 * SUCCESS : previous hot item index
7552 * FAILURE : -1 (no hot item)
7554 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7556 INT iOldIndex = infoPtr->nHotItem;
7558 infoPtr->nHotItem = iIndex;
7560 return iOldIndex;
7564 /***
7565 * DESCRIPTION:
7566 * Sets the amount of time the cursor must hover over an item before it is selected.
7568 * PARAMETER(S):
7569 * [I] infoPtr : valid pointer to the listview structure
7570 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7572 * RETURN:
7573 * Returns the previous hover time
7575 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7577 DWORD oldHoverTime = infoPtr->dwHoverTime;
7579 infoPtr->dwHoverTime = dwHoverTime;
7581 return oldHoverTime;
7584 /***
7585 * DESCRIPTION:
7586 * Sets spacing for icons of LVS_ICON style.
7588 * PARAMETER(S):
7589 * [I] infoPtr : valid pointer to the listview structure
7590 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7591 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7593 * RETURN:
7594 * MAKELONG(oldcx, oldcy)
7596 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7598 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7600 TRACE("requested=(%d,%d)\n", cx, cy);
7602 /* this is supported only for LVS_ICON style */
7603 if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
7605 /* set to defaults, if instructed to */
7606 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7607 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7609 /* if 0 then compute width
7610 * FIXME: Should scan each item and determine max width of
7611 * icon or label, then make that the width */
7612 if (cx == 0)
7613 cx = infoPtr->iconSpacing.cx;
7615 /* if 0 then compute height */
7616 if (cy == 0)
7617 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7618 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7621 infoPtr->iconSpacing.cx = cx;
7622 infoPtr->iconSpacing.cy = cy;
7624 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7625 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7626 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7627 infoPtr->ntmHeight);
7629 /* these depend on the iconSpacing */
7630 LISTVIEW_UpdateItemSize(infoPtr);
7632 return oldspacing;
7635 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7637 INT cx, cy;
7639 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7641 size->cx = cx;
7642 size->cy = cy;
7644 else
7646 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7647 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7651 /***
7652 * DESCRIPTION:
7653 * Sets image lists.
7655 * PARAMETER(S):
7656 * [I] infoPtr : valid pointer to the listview structure
7657 * [I] nType : image list type
7658 * [I] himl : image list handle
7660 * RETURN:
7661 * SUCCESS : old image list
7662 * FAILURE : NULL
7664 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7666 INT oldHeight = infoPtr->nItemHeight;
7667 HIMAGELIST himlOld = 0;
7669 TRACE("(nType=%d, himl=%p\n", nType, himl);
7671 switch (nType)
7673 case LVSIL_NORMAL:
7674 himlOld = infoPtr->himlNormal;
7675 infoPtr->himlNormal = himl;
7676 if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7677 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7678 break;
7680 case LVSIL_SMALL:
7681 himlOld = infoPtr->himlSmall;
7682 infoPtr->himlSmall = himl;
7683 if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7684 break;
7686 case LVSIL_STATE:
7687 himlOld = infoPtr->himlState;
7688 infoPtr->himlState = himl;
7689 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7690 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7691 break;
7693 default:
7694 ERR("Unknown icon type=%d\n", nType);
7695 return NULL;
7698 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7699 if (infoPtr->nItemHeight != oldHeight)
7700 LISTVIEW_UpdateScroll(infoPtr);
7702 return himlOld;
7705 /***
7706 * DESCRIPTION:
7707 * Preallocates memory (does *not* set the actual count of items !)
7709 * PARAMETER(S):
7710 * [I] infoPtr : valid pointer to the listview structure
7711 * [I] nItems : item count (projected number of items to allocate)
7712 * [I] dwFlags : update flags
7714 * RETURN:
7715 * SUCCESS : TRUE
7716 * FAILURE : FALSE
7718 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7720 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7722 if (infoPtr->dwStyle & LVS_OWNERDATA)
7724 INT nOldCount = infoPtr->nItemCount;
7726 if (nItems < nOldCount)
7728 RANGE range = { nItems, nOldCount };
7729 ranges_del(infoPtr->selectionRanges, range);
7730 if (infoPtr->nFocusedItem >= nItems)
7732 LISTVIEW_SetItemFocus(infoPtr, -1);
7733 SetRectEmpty(&infoPtr->rcFocus);
7737 infoPtr->nItemCount = nItems;
7738 LISTVIEW_UpdateScroll(infoPtr);
7740 /* the flags are valid only in ownerdata report and list modes */
7741 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
7743 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7744 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7746 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7747 LISTVIEW_InvalidateList(infoPtr);
7748 else
7750 INT nFrom, nTo;
7751 POINT Origin;
7752 RECT rcErase;
7754 LISTVIEW_GetOrigin(infoPtr, &Origin);
7755 nFrom = min(nOldCount, nItems);
7756 nTo = max(nOldCount, nItems);
7758 if (infoPtr->uView == LV_VIEW_DETAILS)
7760 rcErase.left = 0;
7761 rcErase.top = nFrom * infoPtr->nItemHeight;
7762 rcErase.right = infoPtr->nItemWidth;
7763 rcErase.bottom = nTo * infoPtr->nItemHeight;
7764 OffsetRect(&rcErase, Origin.x, Origin.y);
7765 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7766 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7768 else /* LV_VIEW_LIST */
7770 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7772 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7773 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7774 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7775 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7776 OffsetRect(&rcErase, Origin.x, Origin.y);
7777 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7778 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7780 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7781 rcErase.top = 0;
7782 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7783 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7784 OffsetRect(&rcErase, Origin.x, Origin.y);
7785 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7786 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7790 else
7792 /* According to MSDN for non-LVS_OWNERDATA this is just
7793 * a performance issue. The control allocates its internal
7794 * data structures for the number of items specified. It
7795 * cuts down on the number of memory allocations. Therefore
7796 * we will just issue a WARN here
7798 WARN("for non-ownerdata performance option not implemented.\n");
7801 return TRUE;
7804 /***
7805 * DESCRIPTION:
7806 * Sets the position of an item.
7808 * PARAMETER(S):
7809 * [I] infoPtr : valid pointer to the listview structure
7810 * [I] nItem : item index
7811 * [I] pt : coordinate
7813 * RETURN:
7814 * SUCCESS : TRUE
7815 * FAILURE : FALSE
7817 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7819 POINT Origin;
7821 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7823 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7824 !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
7826 LISTVIEW_GetOrigin(infoPtr, &Origin);
7828 /* This point value seems to be an undocumented feature.
7829 * The best guess is that it means either at the origin,
7830 * or at true beginning of the list. I will assume the origin. */
7831 if ((pt.x == -1) && (pt.y == -1))
7832 pt = Origin;
7834 if (infoPtr->uView == LV_VIEW_ICON)
7836 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7837 pt.y -= ICON_TOP_PADDING;
7839 pt.x -= Origin.x;
7840 pt.y -= Origin.y;
7842 infoPtr->bAutoarrange = FALSE;
7844 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7847 /***
7848 * DESCRIPTION:
7849 * Sets the state of one or many items.
7851 * PARAMETER(S):
7852 * [I] infoPtr : valid pointer to the listview structure
7853 * [I] nItem : item index
7854 * [I] lpLVItem : item or subitem info
7856 * RETURN:
7857 * SUCCESS : TRUE
7858 * FAILURE : FALSE
7860 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7862 BOOL bResult = TRUE;
7863 LVITEMW lvItem;
7865 lvItem.iItem = nItem;
7866 lvItem.iSubItem = 0;
7867 lvItem.mask = LVIF_STATE;
7868 lvItem.state = lpLVItem->state;
7869 lvItem.stateMask = lpLVItem->stateMask;
7870 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7872 if (nItem == -1)
7874 /* select all isn't allowed in LVS_SINGLESEL */
7875 if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
7876 return FALSE;
7878 /* apply to all items */
7879 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7880 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7882 else
7883 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7886 * Update selection mark
7888 * Investigation on windows 2k showed that selection mark was updated
7889 * whenever a new selection was made, but if the selected item was
7890 * unselected it was not updated.
7892 * we are probably still not 100% accurate, but this at least sets the
7893 * proper selection mark when it is needed
7896 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7897 (infoPtr->nSelectionMark == -1))
7899 int i;
7900 for (i = 0; i < infoPtr->nItemCount; i++)
7902 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7904 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7906 infoPtr->nSelectionMark = i;
7907 break;
7910 else if (ranges_contain(infoPtr->selectionRanges, i))
7912 infoPtr->nSelectionMark = i;
7913 break;
7918 return bResult;
7921 /***
7922 * DESCRIPTION:
7923 * Sets the text of an item or subitem.
7925 * PARAMETER(S):
7926 * [I] hwnd : window handle
7927 * [I] nItem : item index
7928 * [I] lpLVItem : item or subitem info
7929 * [I] isW : TRUE if input is Unicode
7931 * RETURN:
7932 * SUCCESS : TRUE
7933 * FAILURE : FALSE
7935 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7937 LVITEMW lvItem;
7939 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7941 lvItem.iItem = nItem;
7942 lvItem.iSubItem = lpLVItem->iSubItem;
7943 lvItem.mask = LVIF_TEXT;
7944 lvItem.pszText = lpLVItem->pszText;
7945 lvItem.cchTextMax = lpLVItem->cchTextMax;
7947 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7949 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7952 /***
7953 * DESCRIPTION:
7954 * Set item index that marks the start of a multiple selection.
7956 * PARAMETER(S):
7957 * [I] infoPtr : valid pointer to the listview structure
7958 * [I] nIndex : index
7960 * RETURN:
7961 * Index number or -1 if there is no selection mark.
7963 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7965 INT nOldIndex = infoPtr->nSelectionMark;
7967 TRACE("(nIndex=%d)\n", nIndex);
7969 infoPtr->nSelectionMark = nIndex;
7971 return nOldIndex;
7974 /***
7975 * DESCRIPTION:
7976 * Sets the text background color.
7978 * PARAMETER(S):
7979 * [I] infoPtr : valid pointer to the listview structure
7980 * [I] clrTextBk : text background color
7982 * RETURN:
7983 * SUCCESS : TRUE
7984 * FAILURE : FALSE
7986 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7988 TRACE("(clrTextBk=%x)\n", clrTextBk);
7990 if (infoPtr->clrTextBk != clrTextBk)
7992 infoPtr->clrTextBk = clrTextBk;
7993 LISTVIEW_InvalidateList(infoPtr);
7996 return TRUE;
7999 /***
8000 * DESCRIPTION:
8001 * Sets the text foreground color.
8003 * PARAMETER(S):
8004 * [I] infoPtr : valid pointer to the listview structure
8005 * [I] clrText : text color
8007 * RETURN:
8008 * SUCCESS : TRUE
8009 * FAILURE : FALSE
8011 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
8013 TRACE("(clrText=%x)\n", clrText);
8015 if (infoPtr->clrText != clrText)
8017 infoPtr->clrText = clrText;
8018 LISTVIEW_InvalidateList(infoPtr);
8021 return TRUE;
8024 /***
8025 * DESCRIPTION:
8026 * Sets new ToolTip window to ListView control.
8028 * PARAMETER(S):
8029 * [I] infoPtr : valid pointer to the listview structure
8030 * [I] hwndNewToolTip : handle to new ToolTip
8032 * RETURN:
8033 * old tool tip
8035 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
8037 HWND hwndOldToolTip = infoPtr->hwndToolTip;
8038 infoPtr->hwndToolTip = hwndNewToolTip;
8039 return hwndOldToolTip;
8043 * DESCRIPTION:
8044 * sets the Unicode character format flag for the control
8045 * PARAMETER(S):
8046 * [I] infoPtr :valid pointer to the listview structure
8047 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
8049 * RETURN:
8050 * Old Unicode Format
8052 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
8054 SHORT rc = infoPtr->notifyFormat;
8055 infoPtr->notifyFormat = (fUnicode) ? NFR_UNICODE : NFR_ANSI;
8056 return rc == NFR_UNICODE;
8060 * DESCRIPTION:
8061 * sets the control view mode
8062 * PARAMETER(S):
8063 * [I] infoPtr :valid pointer to the listview structure
8064 * [I] nView :new view mode value
8066 * RETURN:
8067 * SUCCESS: 1
8068 * FAILURE: -1
8070 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
8072 SIZE oldIconSize = infoPtr->iconSize;
8073 HIMAGELIST himl;
8075 if (infoPtr->uView == nView) return 1;
8077 if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
8078 if (nView == LV_VIEW_TILE)
8080 FIXME("View LV_VIEW_TILE unimplemented\n");
8081 return -1;
8084 infoPtr->uView = nView;
8086 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8087 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8089 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8090 SetRectEmpty(&infoPtr->rcFocus);
8092 himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8093 set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
8095 switch (nView)
8097 case LV_VIEW_ICON:
8098 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8100 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
8101 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8102 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8104 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8105 break;
8106 case LV_VIEW_SMALLICON:
8107 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8108 break;
8109 case LV_VIEW_DETAILS:
8111 HDLAYOUT hl;
8112 WINDOWPOS wp;
8114 LISTVIEW_CreateHeader( infoPtr );
8116 hl.prc = &infoPtr->rcList;
8117 hl.pwpos = &wp;
8118 SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
8119 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8120 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
8121 break;
8123 case LV_VIEW_LIST:
8124 break;
8127 LISTVIEW_UpdateItemSize(infoPtr);
8128 LISTVIEW_UpdateSize(infoPtr);
8129 LISTVIEW_UpdateScroll(infoPtr);
8130 LISTVIEW_InvalidateList(infoPtr);
8132 TRACE("nView=%d\n", nView);
8134 return 1;
8137 /* LISTVIEW_SetWorkAreas */
8139 /***
8140 * DESCRIPTION:
8141 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
8143 * PARAMETER(S):
8144 * [I] first : pointer to first ITEM_INFO to compare
8145 * [I] second : pointer to second ITEM_INFO to compare
8146 * [I] lParam : HWND of control
8148 * RETURN:
8149 * if first comes before second : negative
8150 * if first comes after second : positive
8151 * if first and second are equivalent : zero
8153 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8155 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8156 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
8157 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
8159 /* Forward the call to the client defined callback */
8160 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8163 /***
8164 * DESCRIPTION:
8165 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
8167 * PARAMETER(S):
8168 * [I] first : pointer to first ITEM_INFO to compare
8169 * [I] second : pointer to second ITEM_INFO to compare
8170 * [I] lParam : HWND of control
8172 * RETURN:
8173 * if first comes before second : negative
8174 * if first comes after second : positive
8175 * if first and second are equivalent : zero
8177 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
8179 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8180 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
8181 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
8183 /* Forward the call to the client defined callback */
8184 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
8187 /***
8188 * DESCRIPTION:
8189 * Sorts the listview items.
8191 * PARAMETER(S):
8192 * [I] infoPtr : valid pointer to the listview structure
8193 * [I] pfnCompare : application-defined value
8194 * [I] lParamSort : pointer to comparison callback
8195 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
8197 * RETURN:
8198 * SUCCESS : TRUE
8199 * FAILURE : FALSE
8201 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
8202 LPARAM lParamSort, BOOL IsEx)
8204 HDPA hdpaSubItems;
8205 ITEM_INFO *lpItem;
8206 LPVOID selectionMarkItem = NULL;
8207 LPVOID focusedItem = NULL;
8208 int i;
8210 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
8212 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
8214 if (!pfnCompare) return FALSE;
8215 if (!infoPtr->hdpaItems) return FALSE;
8217 /* if there are 0 or 1 items, there is no need to sort */
8218 if (infoPtr->nItemCount < 2) return TRUE;
8220 /* clear selection */
8221 ranges_clear(infoPtr->selectionRanges);
8223 /* save selection mark and focused item */
8224 if (infoPtr->nSelectionMark >= 0)
8225 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
8226 if (infoPtr->nFocusedItem >= 0)
8227 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
8229 infoPtr->pfnCompare = pfnCompare;
8230 infoPtr->lParamSort = lParamSort;
8231 if (IsEx)
8232 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
8233 else
8234 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
8236 /* restore selection ranges */
8237 for (i=0; i < infoPtr->nItemCount; i++)
8239 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
8240 lpItem = DPA_GetPtr(hdpaSubItems, 0);
8242 if (lpItem->state & LVIS_SELECTED)
8243 ranges_additem(infoPtr->selectionRanges, i);
8245 /* restore selection mark and focused item */
8246 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8247 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8249 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8251 /* refresh the display */
8252 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON)
8253 LISTVIEW_InvalidateList(infoPtr);
8255 return TRUE;
8258 /***
8259 * DESCRIPTION:
8260 * Update theme handle after a theme change.
8262 * PARAMETER(S):
8263 * [I] infoPtr : valid pointer to the listview structure
8265 * RETURN:
8266 * SUCCESS : 0
8267 * FAILURE : something else
8269 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8271 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8272 CloseThemeData(theme);
8273 OpenThemeData(infoPtr->hwndSelf, themeClass);
8274 return 0;
8277 /***
8278 * DESCRIPTION:
8279 * Updates an items or rearranges the listview control.
8281 * PARAMETER(S):
8282 * [I] infoPtr : valid pointer to the listview structure
8283 * [I] nItem : item index
8285 * RETURN:
8286 * SUCCESS : TRUE
8287 * FAILURE : FALSE
8289 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8291 TRACE("(nItem=%d)\n", nItem);
8293 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8295 /* rearrange with default alignment style */
8296 if (is_autoarrange(infoPtr))
8297 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8298 else
8299 LISTVIEW_InvalidateItem(infoPtr, nItem);
8301 return TRUE;
8304 /***
8305 * DESCRIPTION:
8306 * Draw the track line at the place defined in the infoPtr structure.
8307 * The line is drawn with a XOR pen so drawing the line for the second time
8308 * in the same place erases the line.
8310 * PARAMETER(S):
8311 * [I] infoPtr : valid pointer to the listview structure
8313 * RETURN:
8314 * SUCCESS : TRUE
8315 * FAILURE : FALSE
8317 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8319 HPEN hOldPen;
8320 HDC hdc;
8321 INT oldROP;
8323 if (infoPtr->xTrackLine == -1)
8324 return FALSE;
8326 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8327 return FALSE;
8328 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8329 oldROP = SetROP2(hdc, R2_XORPEN);
8330 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8331 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8332 SetROP2(hdc, oldROP);
8333 SelectObject(hdc, hOldPen);
8334 ReleaseDC(infoPtr->hwndSelf, hdc);
8335 return TRUE;
8338 /***
8339 * DESCRIPTION:
8340 * Called when an edit control should be displayed. This function is called after
8341 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8343 * PARAMETER(S):
8344 * [I] hwnd : Handle to the listview
8345 * [I] uMsg : WM_TIMER (ignored)
8346 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8347 * [I] dwTimer : The elapsed time (ignored)
8349 * RETURN:
8350 * None.
8352 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8354 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8355 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8357 KillTimer(hwnd, idEvent);
8358 editItem->fEnabled = FALSE;
8359 /* check if the item is still selected */
8360 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8361 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8364 /***
8365 * DESCRIPTION:
8366 * Creates the listview control - the WM_NCCREATE phase.
8368 * PARAMETER(S):
8369 * [I] hwnd : window handle
8370 * [I] lpcs : the create parameters
8372 * RETURN:
8373 * Success: TRUE
8374 * Failure: FALSE
8376 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8378 LISTVIEW_INFO *infoPtr;
8379 LOGFONTW logFont;
8381 TRACE("(lpcs=%p)\n", lpcs);
8383 /* initialize info pointer */
8384 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8385 if (!infoPtr) return FALSE;
8387 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8389 infoPtr->hwndSelf = hwnd;
8390 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8391 map_style_view(infoPtr);
8392 /* determine the type of structures to use */
8393 infoPtr->hwndNotify = lpcs->hwndParent;
8394 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8396 /* initialize color information */
8397 infoPtr->clrBk = CLR_NONE;
8398 infoPtr->clrText = CLR_DEFAULT;
8399 infoPtr->clrTextBk = CLR_DEFAULT;
8400 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8402 /* set default values */
8403 infoPtr->nFocusedItem = -1;
8404 infoPtr->nSelectionMark = -1;
8405 infoPtr->nHotItem = -1;
8406 infoPtr->bRedraw = TRUE;
8407 infoPtr->bNoItemMetrics = TRUE;
8408 infoPtr->bDoChangeNotify = TRUE;
8409 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8410 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8411 infoPtr->nEditLabelItem = -1;
8412 infoPtr->nLButtonDownItem = -1;
8413 infoPtr->dwHoverTime = -1; /* default system hover time */
8414 infoPtr->nMeasureItemHeight = 0;
8415 infoPtr->xTrackLine = -1; /* no track line */
8416 infoPtr->itemEdit.fEnabled = FALSE;
8417 infoPtr->iVersion = COMCTL32_VERSION;
8419 /* get default font (icon title) */
8420 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8421 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8422 infoPtr->hFont = infoPtr->hDefaultFont;
8423 LISTVIEW_SaveTextMetrics(infoPtr);
8425 /* allocate memory for the data structure */
8426 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8427 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8428 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8429 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8430 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8431 return TRUE;
8433 fail:
8434 DestroyWindow(infoPtr->hwndHeader);
8435 ranges_destroy(infoPtr->selectionRanges);
8436 DPA_Destroy(infoPtr->hdpaItems);
8437 DPA_Destroy(infoPtr->hdpaPosX);
8438 DPA_Destroy(infoPtr->hdpaPosY);
8439 DPA_Destroy(infoPtr->hdpaColumns);
8440 Free(infoPtr);
8441 return FALSE;
8444 /***
8445 * DESCRIPTION:
8446 * Creates the listview control - the WM_CREATE phase. Most of the data is
8447 * already set up in LISTVIEW_NCCreate
8449 * PARAMETER(S):
8450 * [I] hwnd : window handle
8451 * [I] lpcs : the create parameters
8453 * RETURN:
8454 * Success: 0
8455 * Failure: -1
8457 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8459 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8461 TRACE("(lpcs=%p)\n", lpcs);
8463 infoPtr->dwStyle = lpcs->style;
8464 map_style_view(infoPtr);
8466 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8467 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8468 /* on error defaulting to ANSI notifications */
8469 if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
8471 if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
8473 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
8475 else
8476 infoPtr->hwndHeader = 0;
8478 /* init item size to avoid division by 0 */
8479 LISTVIEW_UpdateItemSize (infoPtr);
8481 if (infoPtr->uView == LV_VIEW_DETAILS)
8483 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8485 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8487 LISTVIEW_UpdateScroll(infoPtr);
8490 OpenThemeData(hwnd, themeClass);
8492 /* initialize the icon sizes */
8493 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
8494 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8495 return 0;
8498 /***
8499 * DESCRIPTION:
8500 * Destroys the listview control.
8502 * PARAMETER(S):
8503 * [I] infoPtr : valid pointer to the listview structure
8505 * RETURN:
8506 * Success: 0
8507 * Failure: -1
8509 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8511 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8512 CloseThemeData(theme);
8513 return 0;
8516 /***
8517 * DESCRIPTION:
8518 * Enables the listview control.
8520 * PARAMETER(S):
8521 * [I] infoPtr : valid pointer to the listview structure
8522 * [I] bEnable : specifies whether to enable or disable the window
8524 * RETURN:
8525 * SUCCESS : TRUE
8526 * FAILURE : FALSE
8528 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8530 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8531 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8532 return TRUE;
8535 /***
8536 * DESCRIPTION:
8537 * Erases the background of the listview control.
8539 * PARAMETER(S):
8540 * [I] infoPtr : valid pointer to the listview structure
8541 * [I] hdc : device context handle
8543 * RETURN:
8544 * SUCCESS : TRUE
8545 * FAILURE : FALSE
8547 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8549 RECT rc;
8551 TRACE("(hdc=%p)\n", hdc);
8553 if (!GetClipBox(hdc, &rc)) return FALSE;
8555 if (infoPtr->clrBk == CLR_NONE)
8556 return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
8558 /* for double buffered controls we need to do this during refresh */
8559 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8561 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8565 /***
8566 * DESCRIPTION:
8567 * Helper function for LISTVIEW_[HV]Scroll *only*.
8568 * Performs vertical/horizontal scrolling by a give amount.
8570 * PARAMETER(S):
8571 * [I] infoPtr : valid pointer to the listview structure
8572 * [I] dx : amount of horizontal scroll
8573 * [I] dy : amount of vertical scroll
8575 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8577 /* now we can scroll the list */
8578 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8579 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8580 /* if we have focus, adjust rect */
8581 OffsetRect(&infoPtr->rcFocus, dx, dy);
8582 UpdateWindow(infoPtr->hwndSelf);
8585 /***
8586 * DESCRIPTION:
8587 * Performs vertical scrolling.
8589 * PARAMETER(S):
8590 * [I] infoPtr : valid pointer to the listview structure
8591 * [I] nScrollCode : scroll code
8592 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8593 * [I] hScrollWnd : scrollbar control window handle
8595 * RETURN:
8596 * Zero
8598 * NOTES:
8599 * SB_LINEUP/SB_LINEDOWN:
8600 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8601 * for LVS_REPORT is 1 line
8602 * for LVS_LIST cannot occur
8605 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8606 INT nScrollDiff, HWND hScrollWnd)
8608 INT nOldScrollPos, nNewScrollPos;
8609 SCROLLINFO scrollInfo;
8610 BOOL is_an_icon;
8612 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8613 debugscrollcode(nScrollCode), nScrollDiff);
8615 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8617 scrollInfo.cbSize = sizeof(SCROLLINFO);
8618 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8620 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
8622 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8624 nOldScrollPos = scrollInfo.nPos;
8625 switch (nScrollCode)
8627 case SB_INTERNAL:
8628 break;
8630 case SB_LINEUP:
8631 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8632 break;
8634 case SB_LINEDOWN:
8635 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8636 break;
8638 case SB_PAGEUP:
8639 nScrollDiff = -scrollInfo.nPage;
8640 break;
8642 case SB_PAGEDOWN:
8643 nScrollDiff = scrollInfo.nPage;
8644 break;
8646 case SB_THUMBPOSITION:
8647 case SB_THUMBTRACK:
8648 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8649 break;
8651 default:
8652 nScrollDiff = 0;
8655 /* quit right away if pos isn't changing */
8656 if (nScrollDiff == 0) return 0;
8658 /* calculate new position, and handle overflows */
8659 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8660 if (nScrollDiff > 0) {
8661 if (nNewScrollPos < nOldScrollPos ||
8662 nNewScrollPos > scrollInfo.nMax)
8663 nNewScrollPos = scrollInfo.nMax;
8664 } else {
8665 if (nNewScrollPos > nOldScrollPos ||
8666 nNewScrollPos < scrollInfo.nMin)
8667 nNewScrollPos = scrollInfo.nMin;
8670 /* set the new position, and reread in case it changed */
8671 scrollInfo.fMask = SIF_POS;
8672 scrollInfo.nPos = nNewScrollPos;
8673 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8675 /* carry on only if it really changed */
8676 if (nNewScrollPos == nOldScrollPos) return 0;
8678 /* now adjust to client coordinates */
8679 nScrollDiff = nOldScrollPos - nNewScrollPos;
8680 if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
8682 /* and scroll the window */
8683 scroll_list(infoPtr, 0, nScrollDiff);
8685 return 0;
8688 /***
8689 * DESCRIPTION:
8690 * Performs horizontal scrolling.
8692 * PARAMETER(S):
8693 * [I] infoPtr : valid pointer to the listview structure
8694 * [I] nScrollCode : scroll code
8695 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8696 * [I] hScrollWnd : scrollbar control window handle
8698 * RETURN:
8699 * Zero
8701 * NOTES:
8702 * SB_LINELEFT/SB_LINERIGHT:
8703 * for LVS_ICON, LVS_SMALLICON 1 pixel
8704 * for LVS_REPORT is 1 pixel
8705 * for LVS_LIST is 1 column --> which is a 1 because the
8706 * scroll is based on columns not pixels
8709 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8710 INT nScrollDiff, HWND hScrollWnd)
8712 INT nOldScrollPos, nNewScrollPos;
8713 SCROLLINFO scrollInfo;
8715 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8716 debugscrollcode(nScrollCode), nScrollDiff);
8718 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8720 scrollInfo.cbSize = sizeof(SCROLLINFO);
8721 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8723 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8725 nOldScrollPos = scrollInfo.nPos;
8727 switch (nScrollCode)
8729 case SB_INTERNAL:
8730 break;
8732 case SB_LINELEFT:
8733 nScrollDiff = -1;
8734 break;
8736 case SB_LINERIGHT:
8737 nScrollDiff = 1;
8738 break;
8740 case SB_PAGELEFT:
8741 nScrollDiff = -scrollInfo.nPage;
8742 break;
8744 case SB_PAGERIGHT:
8745 nScrollDiff = scrollInfo.nPage;
8746 break;
8748 case SB_THUMBPOSITION:
8749 case SB_THUMBTRACK:
8750 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8751 break;
8753 default:
8754 nScrollDiff = 0;
8757 /* quit right away if pos isn't changing */
8758 if (nScrollDiff == 0) return 0;
8760 /* calculate new position, and handle overflows */
8761 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8762 if (nScrollDiff > 0) {
8763 if (nNewScrollPos < nOldScrollPos ||
8764 nNewScrollPos > scrollInfo.nMax)
8765 nNewScrollPos = scrollInfo.nMax;
8766 } else {
8767 if (nNewScrollPos > nOldScrollPos ||
8768 nNewScrollPos < scrollInfo.nMin)
8769 nNewScrollPos = scrollInfo.nMin;
8772 /* set the new position, and reread in case it changed */
8773 scrollInfo.fMask = SIF_POS;
8774 scrollInfo.nPos = nNewScrollPos;
8775 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8777 /* carry on only if it really changed */
8778 if (nNewScrollPos == nOldScrollPos) return 0;
8780 if (infoPtr->uView == LV_VIEW_DETAILS)
8781 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8783 /* now adjust to client coordinates */
8784 nScrollDiff = nOldScrollPos - nNewScrollPos;
8785 if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
8787 /* and scroll the window */
8788 scroll_list(infoPtr, nScrollDiff, 0);
8790 return 0;
8793 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8795 INT gcWheelDelta = 0;
8796 INT pulScrollLines = 3;
8797 SCROLLINFO scrollInfo;
8799 TRACE("(wheelDelta=%d)\n", wheelDelta);
8801 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8802 gcWheelDelta -= wheelDelta;
8804 scrollInfo.cbSize = sizeof(SCROLLINFO);
8805 scrollInfo.fMask = SIF_POS;
8807 switch(infoPtr->uView)
8809 case LV_VIEW_ICON:
8810 case LV_VIEW_SMALLICON:
8812 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8813 * should be fixed in the future.
8815 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8816 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8817 break;
8819 case LV_VIEW_DETAILS:
8820 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8822 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8823 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8824 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8826 break;
8828 case LV_VIEW_LIST:
8829 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8830 break;
8832 return 0;
8835 /***
8836 * DESCRIPTION:
8837 * ???
8839 * PARAMETER(S):
8840 * [I] infoPtr : valid pointer to the listview structure
8841 * [I] nVirtualKey : virtual key
8842 * [I] lKeyData : key data
8844 * RETURN:
8845 * Zero
8847 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8849 HWND hwndSelf = infoPtr->hwndSelf;
8850 INT nItem = -1;
8851 NMLVKEYDOWN nmKeyDown;
8853 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8855 /* send LVN_KEYDOWN notification */
8856 nmKeyDown.wVKey = nVirtualKey;
8857 nmKeyDown.flags = 0;
8858 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8859 if (!IsWindow(hwndSelf))
8860 return 0;
8862 switch (nVirtualKey)
8864 case VK_SPACE:
8865 nItem = infoPtr->nFocusedItem;
8866 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8867 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8868 break;
8870 case VK_RETURN:
8871 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8873 if (!notify(infoPtr, NM_RETURN)) return 0;
8874 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8876 break;
8878 case VK_HOME:
8879 if (infoPtr->nItemCount > 0)
8880 nItem = 0;
8881 break;
8883 case VK_END:
8884 if (infoPtr->nItemCount > 0)
8885 nItem = infoPtr->nItemCount - 1;
8886 break;
8888 case VK_LEFT:
8889 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
8890 break;
8892 case VK_UP:
8893 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
8894 break;
8896 case VK_RIGHT:
8897 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
8898 break;
8900 case VK_DOWN:
8901 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
8902 break;
8904 case VK_PRIOR:
8905 if (infoPtr->uView == LV_VIEW_DETAILS)
8907 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8908 if (infoPtr->nFocusedItem == topidx)
8909 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8910 else
8911 nItem = topidx;
8913 else
8914 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8915 * LISTVIEW_GetCountPerRow(infoPtr);
8916 if(nItem < 0) nItem = 0;
8917 break;
8919 case VK_NEXT:
8920 if (infoPtr->uView == LV_VIEW_DETAILS)
8922 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8923 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8924 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8925 nItem = infoPtr->nFocusedItem + cnt - 1;
8926 else
8927 nItem = topidx + cnt - 1;
8929 else
8930 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8931 * LISTVIEW_GetCountPerRow(infoPtr);
8932 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8933 break;
8936 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8937 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8939 return 0;
8942 /***
8943 * DESCRIPTION:
8944 * Kills the focus.
8946 * PARAMETER(S):
8947 * [I] infoPtr : valid pointer to the listview structure
8949 * RETURN:
8950 * Zero
8952 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8954 TRACE("()\n");
8956 /* if we did not have the focus, there's nothing to do */
8957 if (!infoPtr->bFocus) return 0;
8959 /* send NM_KILLFOCUS notification */
8960 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8962 /* if we have a focus rectangle, get rid of it */
8963 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8965 /* set window focus flag */
8966 infoPtr->bFocus = FALSE;
8968 /* invalidate the selected items before resetting focus flag */
8969 LISTVIEW_InvalidateSelectedItems(infoPtr);
8971 return 0;
8974 /***
8975 * DESCRIPTION:
8976 * Processes double click messages (left mouse button).
8978 * PARAMETER(S):
8979 * [I] infoPtr : valid pointer to the listview structure
8980 * [I] wKey : key flag
8981 * [I] x,y : mouse coordinate
8983 * RETURN:
8984 * Zero
8986 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8988 LVHITTESTINFO htInfo;
8990 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8992 /* Cancel the item edition if any */
8993 if (infoPtr->itemEdit.fEnabled)
8995 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8996 infoPtr->itemEdit.fEnabled = FALSE;
8999 /* send NM_RELEASEDCAPTURE notification */
9000 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9002 htInfo.pt.x = x;
9003 htInfo.pt.y = y;
9005 /* send NM_DBLCLK notification */
9006 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
9007 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
9009 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
9010 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
9012 return 0;
9015 /***
9016 * DESCRIPTION:
9017 * Processes mouse down messages (left mouse button).
9019 * PARAMETERS:
9020 * infoPtr [I ] valid pointer to the listview structure
9021 * wKey [I ] key flag
9022 * x,y [I ] mouse coordinate
9024 * RETURN:
9025 * Zero
9027 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9029 LVHITTESTINFO lvHitTestInfo;
9030 static BOOL bGroupSelect = TRUE;
9031 POINT pt = { x, y };
9032 INT nItem;
9034 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9036 /* send NM_RELEASEDCAPTURE notification */
9037 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9039 /* set left button down flag and record the click position */
9040 infoPtr->bLButtonDown = TRUE;
9041 infoPtr->ptClickPos = pt;
9042 infoPtr->bDragging = FALSE;
9044 lvHitTestInfo.pt.x = x;
9045 lvHitTestInfo.pt.y = y;
9047 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9048 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
9049 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9051 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
9053 toggle_checkbox_state(infoPtr, nItem);
9054 return 0;
9057 if (infoPtr->dwStyle & LVS_SINGLESEL)
9059 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9060 infoPtr->nEditLabelItem = nItem;
9061 else
9062 LISTVIEW_SetSelection(infoPtr, nItem);
9064 else
9066 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
9068 if (bGroupSelect)
9070 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
9071 LISTVIEW_SetItemFocus(infoPtr, nItem);
9072 infoPtr->nSelectionMark = nItem;
9074 else
9076 LVITEMW item;
9078 item.state = LVIS_SELECTED | LVIS_FOCUSED;
9079 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9081 LISTVIEW_SetItemState(infoPtr,nItem,&item);
9082 infoPtr->nSelectionMark = nItem;
9085 else if (wKey & MK_CONTROL)
9087 LVITEMW item;
9089 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
9091 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
9092 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9093 LISTVIEW_SetItemState(infoPtr, nItem, &item);
9094 infoPtr->nSelectionMark = nItem;
9096 else if (wKey & MK_SHIFT)
9098 LISTVIEW_SetGroupSelection(infoPtr, nItem);
9100 else
9102 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9104 infoPtr->nEditLabelItem = nItem;
9105 infoPtr->nLButtonDownItem = nItem;
9107 LISTVIEW_SetItemFocus(infoPtr, nItem);
9109 else
9110 /* set selection (clears other pre-existing selections) */
9111 LISTVIEW_SetSelection(infoPtr, nItem);
9115 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
9116 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
9118 else
9120 /* remove all selections */
9121 if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
9122 LISTVIEW_DeselectAll(infoPtr);
9123 ReleaseCapture();
9126 return 0;
9129 /***
9130 * DESCRIPTION:
9131 * Processes mouse up messages (left mouse button).
9133 * PARAMETERS:
9134 * infoPtr [I ] valid pointer to the listview structure
9135 * wKey [I ] key flag
9136 * x,y [I ] mouse coordinate
9138 * RETURN:
9139 * Zero
9141 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9143 LVHITTESTINFO lvHitTestInfo;
9145 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9147 if (!infoPtr->bLButtonDown) return 0;
9149 lvHitTestInfo.pt.x = x;
9150 lvHitTestInfo.pt.y = y;
9152 /* send NM_CLICK notification */
9153 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9154 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
9156 /* set left button flag */
9157 infoPtr->bLButtonDown = FALSE;
9159 /* set a single selection, reset others */
9160 if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
9161 LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
9162 infoPtr->nLButtonDownItem = -1;
9164 if (infoPtr->bDragging)
9166 infoPtr->bDragging = FALSE;
9167 return 0;
9170 /* if we clicked on a selected item, edit the label */
9171 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
9173 /* we want to make sure the user doesn't want to do a double click. So we will
9174 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
9176 infoPtr->itemEdit.fEnabled = TRUE;
9177 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
9178 SetTimer(infoPtr->hwndSelf,
9179 (UINT_PTR)&infoPtr->itemEdit,
9180 GetDoubleClickTime(),
9181 LISTVIEW_DelayedEditItem);
9184 if (!infoPtr->bFocus)
9185 SetFocus(infoPtr->hwndSelf);
9187 return 0;
9190 /***
9191 * DESCRIPTION:
9192 * Destroys the listview control (called after WM_DESTROY).
9194 * PARAMETER(S):
9195 * [I] infoPtr : valid pointer to the listview structure
9197 * RETURN:
9198 * Zero
9200 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
9202 TRACE("()\n");
9204 /* delete all items */
9205 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9207 /* destroy data structure */
9208 DPA_Destroy(infoPtr->hdpaItems);
9209 DPA_Destroy(infoPtr->hdpaPosX);
9210 DPA_Destroy(infoPtr->hdpaPosY);
9211 DPA_Destroy(infoPtr->hdpaColumns);
9212 ranges_destroy(infoPtr->selectionRanges);
9214 /* destroy image lists */
9215 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
9217 if (infoPtr->himlNormal)
9218 ImageList_Destroy(infoPtr->himlNormal);
9219 if (infoPtr->himlSmall)
9220 ImageList_Destroy(infoPtr->himlSmall);
9221 if (infoPtr->himlState)
9222 ImageList_Destroy(infoPtr->himlState);
9225 /* destroy font, bkgnd brush */
9226 infoPtr->hFont = 0;
9227 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
9228 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
9230 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
9232 /* free listview info pointer*/
9233 Free(infoPtr);
9235 return 0;
9238 /***
9239 * DESCRIPTION:
9240 * Handles notifications from header.
9242 * PARAMETER(S):
9243 * [I] infoPtr : valid pointer to the listview structure
9244 * [I] nCtrlId : control identifier
9245 * [I] lpnmh : notification information
9247 * RETURN:
9248 * Zero
9250 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
9252 HWND hwndSelf = infoPtr->hwndSelf;
9254 TRACE("(lpnmh=%p)\n", lpnmh);
9256 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9258 switch (lpnmh->hdr.code)
9260 case HDN_TRACKW:
9261 case HDN_TRACKA:
9263 COLUMN_INFO *lpColumnInfo;
9264 POINT ptOrigin;
9265 INT x;
9267 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9268 break;
9270 /* remove the old line (if any) */
9271 LISTVIEW_DrawTrackLine(infoPtr);
9273 /* compute & draw the new line */
9274 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9275 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9276 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9277 infoPtr->xTrackLine = x + ptOrigin.x;
9278 LISTVIEW_DrawTrackLine(infoPtr);
9279 break;
9282 case HDN_ENDTRACKA:
9283 case HDN_ENDTRACKW:
9284 /* remove the track line (if any) */
9285 LISTVIEW_DrawTrackLine(infoPtr);
9286 infoPtr->xTrackLine = -1;
9287 break;
9289 case HDN_BEGINDRAG:
9290 notify_forward_header(infoPtr, lpnmh);
9291 return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0;
9293 case HDN_ENDDRAG:
9294 FIXME("Changing column order not implemented\n");
9295 notify_forward_header(infoPtr, lpnmh);
9296 return TRUE;
9298 case HDN_ITEMCHANGINGW:
9299 case HDN_ITEMCHANGINGA:
9300 return notify_forward_header(infoPtr, lpnmh);
9302 case HDN_ITEMCHANGEDW:
9303 case HDN_ITEMCHANGEDA:
9305 COLUMN_INFO *lpColumnInfo;
9306 INT dx, cxy;
9308 notify_forward_header(infoPtr, lpnmh);
9309 if (!IsWindow(hwndSelf))
9310 break;
9312 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9314 HDITEMW hdi;
9316 hdi.mask = HDI_WIDTH;
9317 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
9318 cxy = hdi.cxy;
9320 else
9321 cxy = lpnmh->pitem->cxy;
9323 /* determine how much we change since the last know position */
9324 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9325 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9326 if (dx != 0)
9328 lpColumnInfo->rcHeader.right += dx;
9329 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9330 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9331 else
9333 /* only needs to update the scrolls */
9334 infoPtr->nItemWidth += dx;
9335 LISTVIEW_UpdateScroll(infoPtr);
9337 LISTVIEW_UpdateItemSize(infoPtr);
9338 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
9340 POINT ptOrigin;
9341 RECT rcCol = lpColumnInfo->rcHeader;
9343 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9344 OffsetRect(&rcCol, ptOrigin.x, 0);
9346 rcCol.top = infoPtr->rcList.top;
9347 rcCol.bottom = infoPtr->rcList.bottom;
9349 /* resizing left-aligned columns leaves most of the left side untouched */
9350 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9352 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9353 if (dx > 0)
9354 nMaxDirty += dx;
9355 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9358 /* when shrinking the last column clear the now unused field */
9359 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9361 RECT right;
9363 rcCol.right -= dx;
9365 /* deal with right from rightmost column area */
9366 right.left = rcCol.right;
9367 right.top = rcCol.top;
9368 right.bottom = rcCol.bottom;
9369 right.right = infoPtr->rcList.right;
9371 LISTVIEW_InvalidateRect(infoPtr, &right);
9374 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9378 break;
9380 case HDN_ITEMCLICKW:
9381 case HDN_ITEMCLICKA:
9383 /* Handle sorting by Header Column */
9384 NMLISTVIEW nmlv;
9386 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9387 nmlv.iItem = -1;
9388 nmlv.iSubItem = lpnmh->iItem;
9389 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9390 notify_forward_header(infoPtr, lpnmh);
9392 break;
9394 case HDN_DIVIDERDBLCLICKW:
9395 case HDN_DIVIDERDBLCLICKA:
9396 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9397 notify_forward_header(infoPtr, lpnmh);
9398 break;
9401 return 0;
9404 /***
9405 * DESCRIPTION:
9406 * Paint non-client area of control.
9408 * PARAMETER(S):
9409 * [I] infoPtr : valid pointer to the listview structureof the sender
9410 * [I] region : update region
9412 * RETURN:
9413 * TRUE - frame was painted
9414 * FALSE - call default window proc
9416 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9418 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9419 HDC dc;
9420 RECT r;
9421 HRGN cliprgn;
9422 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9423 cyEdge = GetSystemMetrics (SM_CYEDGE);
9425 if (!theme) return FALSE;
9427 GetWindowRect(infoPtr->hwndSelf, &r);
9429 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9430 r.right - cxEdge, r.bottom - cyEdge);
9431 if (region != (HRGN)1)
9432 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9433 OffsetRect(&r, -r.left, -r.top);
9435 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9436 OffsetRect(&r, -r.left, -r.top);
9438 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9439 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9440 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9441 ReleaseDC(infoPtr->hwndSelf, dc);
9443 /* Call default proc to get the scrollbars etc. painted */
9444 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9446 return TRUE;
9449 /***
9450 * DESCRIPTION:
9451 * Determines the type of structure to use.
9453 * PARAMETER(S):
9454 * [I] infoPtr : valid pointer to the listview structureof the sender
9455 * [I] hwndFrom : listview window handle
9456 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9458 * RETURN:
9459 * Zero
9461 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9463 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9465 if (nCommand == NF_REQUERY)
9466 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9468 return infoPtr->notifyFormat;
9471 /***
9472 * DESCRIPTION:
9473 * Paints/Repaints the listview control.
9475 * PARAMETER(S):
9476 * [I] infoPtr : valid pointer to the listview structure
9477 * [I] hdc : device context handle
9479 * RETURN:
9480 * Zero
9482 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9484 TRACE("(hdc=%p)\n", hdc);
9486 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9488 infoPtr->bNoItemMetrics = FALSE;
9489 LISTVIEW_UpdateItemSize(infoPtr);
9490 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
9491 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9492 LISTVIEW_UpdateScroll(infoPtr);
9495 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
9497 if (hdc)
9498 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9499 else
9501 PAINTSTRUCT ps;
9503 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9504 if (!hdc) return 1;
9505 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9506 EndPaint(infoPtr->hwndSelf, &ps);
9509 return 0;
9513 /***
9514 * DESCRIPTION:
9515 * Paints/Repaints the listview control.
9517 * PARAMETER(S):
9518 * [I] infoPtr : valid pointer to the listview structure
9519 * [I] hdc : device context handle
9520 * [I] options : drawing options
9522 * RETURN:
9523 * Zero
9525 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9527 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9529 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9530 return 0;
9532 if (options & PRF_ERASEBKGND)
9533 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9535 if (options & PRF_CLIENT)
9536 LISTVIEW_Paint(infoPtr, hdc);
9538 return 0;
9542 /***
9543 * DESCRIPTION:
9544 * Processes double click messages (right mouse button).
9546 * PARAMETER(S):
9547 * [I] infoPtr : valid pointer to the listview structure
9548 * [I] wKey : key flag
9549 * [I] x,y : mouse coordinate
9551 * RETURN:
9552 * Zero
9554 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9556 LVHITTESTINFO lvHitTestInfo;
9558 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9560 /* send NM_RELEASEDCAPTURE notification */
9561 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9563 /* send NM_RDBLCLK notification */
9564 lvHitTestInfo.pt.x = x;
9565 lvHitTestInfo.pt.y = y;
9566 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9567 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9569 return 0;
9572 /***
9573 * DESCRIPTION:
9574 * Processes mouse down messages (right mouse button).
9576 * PARAMETER(S):
9577 * [I] infoPtr : valid pointer to the listview structure
9578 * [I] wKey : key flag
9579 * [I] x,y : mouse coordinate
9581 * RETURN:
9582 * Zero
9584 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9586 LVHITTESTINFO lvHitTestInfo;
9587 INT nItem;
9589 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9591 /* send NM_RELEASEDCAPTURE notification */
9592 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9594 /* make sure the listview control window has the focus */
9595 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9597 /* set right button down flag */
9598 infoPtr->bRButtonDown = TRUE;
9600 /* determine the index of the selected item */
9601 lvHitTestInfo.pt.x = x;
9602 lvHitTestInfo.pt.y = y;
9603 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9605 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9607 LISTVIEW_SetItemFocus(infoPtr, nItem);
9608 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9609 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9610 LISTVIEW_SetSelection(infoPtr, nItem);
9612 else
9614 LISTVIEW_DeselectAll(infoPtr);
9617 return 0;
9620 /***
9621 * DESCRIPTION:
9622 * Processes mouse up messages (right mouse button).
9624 * PARAMETER(S):
9625 * [I] infoPtr : valid pointer to the listview structure
9626 * [I] wKey : key flag
9627 * [I] x,y : mouse coordinate
9629 * RETURN:
9630 * Zero
9632 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9634 LVHITTESTINFO lvHitTestInfo;
9635 POINT pt;
9637 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9639 if (!infoPtr->bRButtonDown) return 0;
9641 /* set button flag */
9642 infoPtr->bRButtonDown = FALSE;
9644 /* Send NM_RCLICK notification */
9645 lvHitTestInfo.pt.x = x;
9646 lvHitTestInfo.pt.y = y;
9647 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9648 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9650 /* Change to screen coordinate for WM_CONTEXTMENU */
9651 pt = lvHitTestInfo.pt;
9652 ClientToScreen(infoPtr->hwndSelf, &pt);
9654 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9655 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9656 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9658 return 0;
9662 /***
9663 * DESCRIPTION:
9664 * Sets the cursor.
9666 * PARAMETER(S):
9667 * [I] infoPtr : valid pointer to the listview structure
9668 * [I] hwnd : window handle of window containing the cursor
9669 * [I] nHittest : hit-test code
9670 * [I] wMouseMsg : ideintifier of the mouse message
9672 * RETURN:
9673 * TRUE if cursor is set
9674 * FALSE otherwise
9676 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9678 LVHITTESTINFO lvHitTestInfo;
9680 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9682 if(!infoPtr->hHotCursor) return FALSE;
9684 GetCursorPos(&lvHitTestInfo.pt);
9685 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9687 SetCursor(infoPtr->hHotCursor);
9689 return TRUE;
9692 /***
9693 * DESCRIPTION:
9694 * Sets the focus.
9696 * PARAMETER(S):
9697 * [I] infoPtr : valid pointer to the listview structure
9698 * [I] hwndLoseFocus : handle of previously focused window
9700 * RETURN:
9701 * Zero
9703 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9705 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9707 /* if we have the focus already, there's nothing to do */
9708 if (infoPtr->bFocus) return 0;
9710 /* send NM_SETFOCUS notification */
9711 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9713 /* set window focus flag */
9714 infoPtr->bFocus = TRUE;
9716 /* put the focus rect back on */
9717 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9719 /* redraw all visible selected items */
9720 LISTVIEW_InvalidateSelectedItems(infoPtr);
9722 return 0;
9725 /***
9726 * DESCRIPTION:
9727 * Sets the font.
9729 * PARAMETER(S):
9730 * [I] infoPtr : valid pointer to the listview structure
9731 * [I] fRedraw : font handle
9732 * [I] fRedraw : redraw flag
9734 * RETURN:
9735 * Zero
9737 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9739 HFONT oldFont = infoPtr->hFont;
9741 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9743 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9744 if (infoPtr->hFont == oldFont) return 0;
9746 LISTVIEW_SaveTextMetrics(infoPtr);
9748 if (infoPtr->uView == LV_VIEW_DETAILS)
9750 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9751 LISTVIEW_UpdateSize(infoPtr);
9752 LISTVIEW_UpdateScroll(infoPtr);
9755 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9757 return 0;
9760 /***
9761 * DESCRIPTION:
9762 * Message handling for WM_SETREDRAW.
9763 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9765 * PARAMETER(S):
9766 * [I] infoPtr : valid pointer to the listview structure
9767 * [I] bRedraw: state of redraw flag
9769 * RETURN:
9770 * DefWinProc return value
9772 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9774 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9776 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9777 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9779 infoPtr->bRedraw = bRedraw;
9781 if(!bRedraw) return 0;
9783 if (is_autoarrange(infoPtr))
9784 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9785 LISTVIEW_UpdateScroll(infoPtr);
9787 /* despite what the WM_SETREDRAW docs says, apps expect us
9788 * to invalidate the listview here... stupid! */
9789 LISTVIEW_InvalidateList(infoPtr);
9791 return 0;
9794 /***
9795 * DESCRIPTION:
9796 * Resizes the listview control. This function processes WM_SIZE
9797 * messages. At this time, the width and height are not used.
9799 * PARAMETER(S):
9800 * [I] infoPtr : valid pointer to the listview structure
9801 * [I] Width : new width
9802 * [I] Height : new height
9804 * RETURN:
9805 * Zero
9807 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9809 RECT rcOld = infoPtr->rcList;
9811 TRACE("(width=%d, height=%d)\n", Width, Height);
9813 LISTVIEW_UpdateSize(infoPtr);
9814 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9816 /* do not bother with display related stuff if we're not redrawing */
9817 if (!is_redrawing(infoPtr)) return 0;
9819 if (is_autoarrange(infoPtr))
9820 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9822 LISTVIEW_UpdateScroll(infoPtr);
9824 /* refresh all only for lists whose height changed significantly */
9825 if ((infoPtr->uView == LV_VIEW_LIST) &&
9826 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9827 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9828 LISTVIEW_InvalidateList(infoPtr);
9830 return 0;
9833 /***
9834 * DESCRIPTION:
9835 * Sets the size information.
9837 * PARAMETER(S):
9838 * [I] infoPtr : valid pointer to the listview structure
9840 * RETURN:
9841 * None
9843 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9845 TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
9847 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9849 if (infoPtr->uView == LV_VIEW_LIST)
9851 /* Apparently the "LIST" style is supposed to have the same
9852 * number of items in a column even if there is no scroll bar.
9853 * Since if a scroll bar already exists then the bottom is already
9854 * reduced, only reduce if the scroll bar does not currently exist.
9855 * The "2" is there to mimic the native control. I think it may be
9856 * related to either padding or edges. (GLA 7/2002)
9858 if (!(infoPtr->dwStyle & WS_HSCROLL))
9859 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9860 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9862 else if (infoPtr->uView == LV_VIEW_DETAILS)
9864 HDLAYOUT hl;
9865 WINDOWPOS wp;
9867 hl.prc = &infoPtr->rcList;
9868 hl.pwpos = &wp;
9869 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9870 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9871 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9872 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9873 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9874 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9876 infoPtr->rcList.top = max(wp.cy, 0);
9877 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9880 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9883 /***
9884 * DESCRIPTION:
9885 * Processes WM_STYLECHANGED messages.
9887 * PARAMETER(S):
9888 * [I] infoPtr : valid pointer to the listview structure
9889 * [I] wStyleType : window style type (normal or extended)
9890 * [I] lpss : window style information
9892 * RETURN:
9893 * Zero
9895 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9896 const STYLESTRUCT *lpss)
9898 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9899 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9900 UINT style;
9902 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9903 wStyleType, lpss->styleOld, lpss->styleNew);
9905 if (wStyleType != GWL_STYLE) return 0;
9907 infoPtr->dwStyle = lpss->styleNew;
9908 map_style_view(infoPtr);
9910 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9911 ((lpss->styleNew & WS_HSCROLL) == 0))
9912 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9914 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9915 ((lpss->styleNew & WS_VSCROLL) == 0))
9916 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9918 if (uNewView != uOldView)
9920 SIZE oldIconSize = infoPtr->iconSize;
9921 HIMAGELIST himl;
9923 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9924 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9926 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9927 SetRectEmpty(&infoPtr->rcFocus);
9929 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9930 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9932 if (uNewView == LVS_ICON)
9934 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9936 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9937 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9938 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9941 else if (uNewView == LVS_REPORT)
9943 HDLAYOUT hl;
9944 WINDOWPOS wp;
9946 LISTVIEW_CreateHeader( infoPtr );
9948 hl.prc = &infoPtr->rcList;
9949 hl.pwpos = &wp;
9950 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9951 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9952 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9953 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9956 LISTVIEW_UpdateItemSize(infoPtr);
9959 if (uNewView == LVS_REPORT)
9961 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9963 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9965 /* Turn off the header control */
9966 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9967 TRACE("Hide header control, was 0x%08x\n", style);
9968 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9969 } else {
9970 /* Turn on the header control */
9971 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9973 TRACE("Show header control, was 0x%08x\n", style);
9974 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9980 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9981 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9982 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9984 /* update the size of the client area */
9985 LISTVIEW_UpdateSize(infoPtr);
9987 /* add scrollbars if needed */
9988 LISTVIEW_UpdateScroll(infoPtr);
9990 /* invalidate client area + erase background */
9991 LISTVIEW_InvalidateList(infoPtr);
9993 return 0;
9996 /***
9997 * DESCRIPTION:
9998 * Processes WM_STYLECHANGING messages.
10000 * PARAMETER(S):
10001 * [I] infoPtr : valid pointer to the listview structure
10002 * [I] wStyleType : window style type (normal or extended)
10003 * [I0] lpss : window style information
10005 * RETURN:
10006 * Zero
10008 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10009 STYLESTRUCT *lpss)
10011 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10012 wStyleType, lpss->styleOld, lpss->styleNew);
10014 /* don't forward LVS_OWNERDATA only if not already set to */
10015 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
10017 if (lpss->styleOld & LVS_OWNERDATA)
10018 lpss->styleNew |= LVS_OWNERDATA;
10019 else
10020 lpss->styleNew &= ~LVS_OWNERDATA;
10023 return 0;
10026 /***
10027 * DESCRIPTION:
10028 * Processes WM_SHOWWINDOW messages.
10030 * PARAMETER(S):
10031 * [I] infoPtr : valid pointer to the listview structure
10032 * [I] bShown : window is being shown (FALSE when hidden)
10033 * [I] iStatus : window show status
10035 * RETURN:
10036 * Zero
10038 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
10040 /* header delayed creation */
10041 if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
10043 LISTVIEW_CreateHeader(infoPtr);
10045 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
10046 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
10049 return 0;
10052 /***
10053 * DESCRIPTION:
10054 * Processes CCM_GETVERSION messages.
10056 * PARAMETER(S):
10057 * [I] infoPtr : valid pointer to the listview structure
10059 * RETURN:
10060 * Current version
10062 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
10064 return infoPtr->iVersion;
10067 /***
10068 * DESCRIPTION:
10069 * Processes CCM_SETVERSION messages.
10071 * PARAMETER(S):
10072 * [I] infoPtr : valid pointer to the listview structure
10073 * [I] iVersion : version to be set
10075 * RETURN:
10076 * -1 when requested version is greater than DLL version;
10077 * previous version otherwise
10079 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
10081 INT iOldVersion = infoPtr->iVersion;
10083 if (iVersion > COMCTL32_VERSION)
10084 return -1;
10086 infoPtr->iVersion = iVersion;
10088 TRACE("new version %d\n", iVersion);
10090 return iOldVersion;
10093 /***
10094 * DESCRIPTION:
10095 * Window procedure of the listview control.
10098 static LRESULT WINAPI
10099 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10101 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
10103 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
10105 if (!infoPtr && (uMsg != WM_NCCREATE))
10106 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10108 switch (uMsg)
10110 case LVM_APPROXIMATEVIEWRECT:
10111 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
10112 LOWORD(lParam), HIWORD(lParam));
10113 case LVM_ARRANGE:
10114 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
10116 /* case LVM_CANCELEDITLABEL: */
10118 case LVM_CREATEDRAGIMAGE:
10119 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
10121 case LVM_DELETEALLITEMS:
10122 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
10124 case LVM_DELETECOLUMN:
10125 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
10127 case LVM_DELETEITEM:
10128 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
10130 case LVM_EDITLABELW:
10131 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
10133 case LVM_EDITLABELA:
10134 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
10136 /* case LVM_ENABLEGROUPVIEW: */
10138 case LVM_ENSUREVISIBLE:
10139 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
10141 case LVM_FINDITEMW:
10142 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
10144 case LVM_FINDITEMA:
10145 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
10147 case LVM_GETBKCOLOR:
10148 return infoPtr->clrBk;
10150 /* case LVM_GETBKIMAGE: */
10152 case LVM_GETCALLBACKMASK:
10153 return infoPtr->uCallbackMask;
10155 case LVM_GETCOLUMNA:
10156 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10158 case LVM_GETCOLUMNW:
10159 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10161 case LVM_GETCOLUMNORDERARRAY:
10162 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10164 case LVM_GETCOLUMNWIDTH:
10165 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
10167 case LVM_GETCOUNTPERPAGE:
10168 return LISTVIEW_GetCountPerPage(infoPtr);
10170 case LVM_GETEDITCONTROL:
10171 return (LRESULT)infoPtr->hwndEdit;
10173 case LVM_GETEXTENDEDLISTVIEWSTYLE:
10174 return infoPtr->dwLvExStyle;
10176 /* case LVM_GETGROUPINFO: */
10178 /* case LVM_GETGROUPMETRICS: */
10180 case LVM_GETHEADER:
10181 return (LRESULT)infoPtr->hwndHeader;
10183 case LVM_GETHOTCURSOR:
10184 return (LRESULT)infoPtr->hHotCursor;
10186 case LVM_GETHOTITEM:
10187 return infoPtr->nHotItem;
10189 case LVM_GETHOVERTIME:
10190 return infoPtr->dwHoverTime;
10192 case LVM_GETIMAGELIST:
10193 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
10195 /* case LVM_GETINSERTMARK: */
10197 /* case LVM_GETINSERTMARKCOLOR: */
10199 /* case LVM_GETINSERTMARKRECT: */
10201 case LVM_GETISEARCHSTRINGA:
10202 case LVM_GETISEARCHSTRINGW:
10203 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
10204 return FALSE;
10206 case LVM_GETITEMA:
10207 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
10209 case LVM_GETITEMW:
10210 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
10212 case LVM_GETITEMCOUNT:
10213 return infoPtr->nItemCount;
10215 case LVM_GETITEMPOSITION:
10216 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
10218 case LVM_GETITEMRECT:
10219 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
10221 case LVM_GETITEMSPACING:
10222 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
10224 case LVM_GETITEMSTATE:
10225 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
10227 case LVM_GETITEMTEXTA:
10228 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10230 case LVM_GETITEMTEXTW:
10231 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10233 case LVM_GETNEXTITEM:
10234 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
10236 case LVM_GETNUMBEROFWORKAREAS:
10237 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
10238 return 1;
10240 case LVM_GETORIGIN:
10241 if (!lParam) return FALSE;
10242 if (infoPtr->uView == LV_VIEW_DETAILS ||
10243 infoPtr->uView == LV_VIEW_LIST) return FALSE;
10244 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
10245 return TRUE;
10247 /* case LVM_GETOUTLINECOLOR: */
10249 /* case LVM_GETSELECTEDCOLUMN: */
10251 case LVM_GETSELECTEDCOUNT:
10252 return LISTVIEW_GetSelectedCount(infoPtr);
10254 case LVM_GETSELECTIONMARK:
10255 return infoPtr->nSelectionMark;
10257 case LVM_GETSTRINGWIDTHA:
10258 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
10260 case LVM_GETSTRINGWIDTHW:
10261 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
10263 case LVM_GETSUBITEMRECT:
10264 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10266 case LVM_GETTEXTBKCOLOR:
10267 return infoPtr->clrTextBk;
10269 case LVM_GETTEXTCOLOR:
10270 return infoPtr->clrText;
10272 /* case LVM_GETTILEINFO: */
10274 /* case LVM_GETTILEVIEWINFO: */
10276 case LVM_GETTOOLTIPS:
10277 if( !infoPtr->hwndToolTip )
10278 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10279 return (LRESULT)infoPtr->hwndToolTip;
10281 case LVM_GETTOPINDEX:
10282 return LISTVIEW_GetTopIndex(infoPtr);
10284 case LVM_GETUNICODEFORMAT:
10285 return (infoPtr->notifyFormat == NFR_UNICODE);
10287 case LVM_GETVIEW:
10288 return infoPtr->uView;
10290 case LVM_GETVIEWRECT:
10291 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10293 case LVM_GETWORKAREAS:
10294 FIXME("LVM_GETWORKAREAS: unimplemented\n");
10295 return FALSE;
10297 /* case LVM_HASGROUP: */
10299 case LVM_HITTEST:
10300 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
10302 case LVM_INSERTCOLUMNA:
10303 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10305 case LVM_INSERTCOLUMNW:
10306 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10308 /* case LVM_INSERTGROUP: */
10310 /* case LVM_INSERTGROUPSORTED: */
10312 case LVM_INSERTITEMA:
10313 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10315 case LVM_INSERTITEMW:
10316 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10318 /* case LVM_INSERTMARKHITTEST: */
10320 /* case LVM_ISGROUPVIEWENABLED: */
10322 /* case LVM_MAPIDTOINDEX: */
10324 /* case LVM_MAPINDEXTOID: */
10326 /* case LVM_MOVEGROUP: */
10328 /* case LVM_MOVEITEMTOGROUP: */
10330 case LVM_REDRAWITEMS:
10331 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10333 /* case LVM_REMOVEALLGROUPS: */
10335 /* case LVM_REMOVEGROUP: */
10337 case LVM_SCROLL:
10338 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10340 case LVM_SETBKCOLOR:
10341 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10343 /* case LVM_SETBKIMAGE: */
10345 case LVM_SETCALLBACKMASK:
10346 infoPtr->uCallbackMask = (UINT)wParam;
10347 return TRUE;
10349 case LVM_SETCOLUMNA:
10350 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10352 case LVM_SETCOLUMNW:
10353 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10355 case LVM_SETCOLUMNORDERARRAY:
10356 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10358 case LVM_SETCOLUMNWIDTH:
10359 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10361 case LVM_SETEXTENDEDLISTVIEWSTYLE:
10362 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10364 /* case LVM_SETGROUPINFO: */
10366 /* case LVM_SETGROUPMETRICS: */
10368 case LVM_SETHOTCURSOR:
10369 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10371 case LVM_SETHOTITEM:
10372 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10374 case LVM_SETHOVERTIME:
10375 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10377 case LVM_SETICONSPACING:
10378 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10380 case LVM_SETIMAGELIST:
10381 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10383 /* case LVM_SETINFOTIP: */
10385 /* case LVM_SETINSERTMARK: */
10387 /* case LVM_SETINSERTMARKCOLOR: */
10389 case LVM_SETITEMA:
10390 case LVM_SETITEMW:
10392 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10393 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10396 case LVM_SETITEMCOUNT:
10397 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10399 case LVM_SETITEMPOSITION:
10401 POINT pt;
10402 pt.x = (short)LOWORD(lParam);
10403 pt.y = (short)HIWORD(lParam);
10404 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10407 case LVM_SETITEMPOSITION32:
10408 if (lParam == 0) return FALSE;
10409 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10411 case LVM_SETITEMSTATE:
10412 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10414 case LVM_SETITEMTEXTA:
10415 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10417 case LVM_SETITEMTEXTW:
10418 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10420 /* case LVM_SETOUTLINECOLOR: */
10422 /* case LVM_SETSELECTEDCOLUMN: */
10424 case LVM_SETSELECTIONMARK:
10425 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10427 case LVM_SETTEXTBKCOLOR:
10428 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10430 case LVM_SETTEXTCOLOR:
10431 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10433 /* case LVM_SETTILEINFO: */
10435 /* case LVM_SETTILEVIEWINFO: */
10437 /* case LVM_SETTILEWIDTH: */
10439 case LVM_SETTOOLTIPS:
10440 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10442 case LVM_SETUNICODEFORMAT:
10443 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10445 case LVM_SETVIEW:
10446 return LISTVIEW_SetView(infoPtr, wParam);
10448 /* case LVM_SETWORKAREAS: */
10450 /* case LVM_SORTGROUPS: */
10452 case LVM_SORTITEMS:
10453 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10455 case LVM_SORTITEMSEX:
10456 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10458 case LVM_SUBITEMHITTEST:
10459 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10461 case LVM_UPDATE:
10462 return LISTVIEW_Update(infoPtr, (INT)wParam);
10464 case CCM_GETVERSION:
10465 return LISTVIEW_GetVersion(infoPtr);
10467 case CCM_SETVERSION:
10468 return LISTVIEW_SetVersion(infoPtr, wParam);
10470 case WM_CHAR:
10471 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10473 case WM_COMMAND:
10474 return LISTVIEW_Command(infoPtr, wParam, lParam);
10476 case WM_NCCREATE:
10477 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10479 case WM_CREATE:
10480 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10482 case WM_DESTROY:
10483 return LISTVIEW_Destroy(infoPtr);
10485 case WM_ENABLE:
10486 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10488 case WM_ERASEBKGND:
10489 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10491 case WM_GETDLGCODE:
10492 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10494 case WM_GETFONT:
10495 return (LRESULT)infoPtr->hFont;
10497 case WM_HSCROLL:
10498 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10500 case WM_KEYDOWN:
10501 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10503 case WM_KILLFOCUS:
10504 return LISTVIEW_KillFocus(infoPtr);
10506 case WM_LBUTTONDBLCLK:
10507 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10509 case WM_LBUTTONDOWN:
10510 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10512 case WM_LBUTTONUP:
10513 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10515 case WM_MOUSEMOVE:
10516 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10518 case WM_MOUSEHOVER:
10519 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10521 case WM_NCDESTROY:
10522 return LISTVIEW_NCDestroy(infoPtr);
10524 case WM_NCPAINT:
10525 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10526 return 0;
10527 goto fwd_msg;
10529 case WM_NOTIFY:
10530 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10531 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10532 else return 0;
10534 case WM_NOTIFYFORMAT:
10535 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10537 case WM_PRINTCLIENT:
10538 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10540 case WM_PAINT:
10541 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10543 case WM_RBUTTONDBLCLK:
10544 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10546 case WM_RBUTTONDOWN:
10547 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10549 case WM_RBUTTONUP:
10550 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10552 case WM_SETCURSOR:
10553 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10554 return TRUE;
10555 goto fwd_msg;
10557 case WM_SETFOCUS:
10558 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10560 case WM_SETFONT:
10561 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10563 case WM_SETREDRAW:
10564 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10566 case WM_SHOWWINDOW:
10567 LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10568 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10570 case WM_SIZE:
10571 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10573 case WM_STYLECHANGED:
10574 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10576 case WM_STYLECHANGING:
10577 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10579 case WM_SYSCOLORCHANGE:
10580 COMCTL32_RefreshSysColors();
10581 return 0;
10583 /* case WM_TIMER: */
10584 case WM_THEMECHANGED:
10585 return LISTVIEW_ThemeChanged(infoPtr);
10587 case WM_VSCROLL:
10588 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10590 case WM_MOUSEWHEEL:
10591 if (wParam & (MK_SHIFT | MK_CONTROL))
10592 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10593 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10595 case WM_WINDOWPOSCHANGED:
10596 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10598 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10599 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10601 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
10603 MEASUREITEMSTRUCT mis;
10604 mis.CtlType = ODT_LISTVIEW;
10605 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10606 mis.itemID = -1;
10607 mis.itemWidth = 0;
10608 mis.itemData = 0;
10609 mis.itemHeight= infoPtr->nItemHeight;
10610 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10611 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10612 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10615 LISTVIEW_UpdateSize(infoPtr);
10616 LISTVIEW_UpdateScroll(infoPtr);
10618 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10620 /* case WM_WININICHANGE: */
10622 default:
10623 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10624 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10626 fwd_msg:
10627 /* call default window procedure */
10628 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10633 /***
10634 * DESCRIPTION:
10635 * Registers the window class.
10637 * PARAMETER(S):
10638 * None
10640 * RETURN:
10641 * None
10643 void LISTVIEW_Register(void)
10645 WNDCLASSW wndClass;
10647 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10648 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10649 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10650 wndClass.cbClsExtra = 0;
10651 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10652 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10653 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10654 wndClass.lpszClassName = WC_LISTVIEWW;
10655 RegisterClassW(&wndClass);
10658 /***
10659 * DESCRIPTION:
10660 * Unregisters the window class.
10662 * PARAMETER(S):
10663 * None
10665 * RETURN:
10666 * None
10668 void LISTVIEW_Unregister(void)
10670 UnregisterClassW(WC_LISTVIEWW, NULL);
10673 /***
10674 * DESCRIPTION:
10675 * Handle any WM_COMMAND messages
10677 * PARAMETER(S):
10678 * [I] infoPtr : valid pointer to the listview structure
10679 * [I] wParam : the first message parameter
10680 * [I] lParam : the second message parameter
10682 * RETURN:
10683 * Zero.
10685 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10687 switch (HIWORD(wParam))
10689 case EN_UPDATE:
10692 * Adjust the edit window size
10694 WCHAR buffer[1024];
10695 HDC hdc = GetDC(infoPtr->hwndEdit);
10696 HFONT hFont, hOldFont = 0;
10697 RECT rect;
10698 SIZE sz;
10700 if (!infoPtr->hwndEdit || !hdc) return 0;
10701 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10702 GetWindowRect(infoPtr->hwndEdit, &rect);
10704 /* Select font to get the right dimension of the string */
10705 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10706 if(hFont != 0)
10708 hOldFont = SelectObject(hdc, hFont);
10711 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10713 TEXTMETRICW textMetric;
10715 /* Add Extra spacing for the next character */
10716 GetTextMetricsW(hdc, &textMetric);
10717 sz.cx += (textMetric.tmMaxCharWidth * 2);
10719 SetWindowPos (
10720 infoPtr->hwndEdit,
10721 HWND_TOP,
10724 sz.cx,
10725 rect.bottom - rect.top,
10726 SWP_DRAWFRAME|SWP_NOMOVE);
10728 if(hFont != 0)
10729 SelectObject(hdc, hOldFont);
10731 ReleaseDC(infoPtr->hwndEdit, hdc);
10733 break;
10735 case EN_KILLFOCUS:
10737 /* handle value will be lost after LISTVIEW_EndEditLabelT */
10738 HWND edit = infoPtr->hwndEdit;
10740 LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit));
10741 SendMessageW(edit, WM_CLOSE, 0, 0);
10744 default:
10745 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10748 return 0;
10752 /***
10753 * DESCRIPTION:
10754 * Subclassed edit control windproc function
10756 * PARAMETER(S):
10757 * [I] hwnd : the edit window handle
10758 * [I] uMsg : the message that is to be processed
10759 * [I] wParam : first message parameter
10760 * [I] lParam : second message parameter
10761 * [I] isW : TRUE if input is Unicode
10763 * RETURN:
10764 * Zero.
10766 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10768 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10769 BOOL save = TRUE;
10771 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10772 hwnd, uMsg, wParam, lParam, isW);
10774 switch (uMsg)
10776 case WM_GETDLGCODE:
10777 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10779 case WM_DESTROY:
10781 WNDPROC editProc = infoPtr->EditWndProc;
10782 infoPtr->EditWndProc = 0;
10783 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10784 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10787 case WM_KEYDOWN:
10788 if (VK_ESCAPE == (INT)wParam)
10790 save = FALSE;
10791 break;
10793 else if (VK_RETURN == (INT)wParam)
10794 break;
10796 default:
10797 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10800 /* kill the edit */
10801 if (infoPtr->hwndEdit)
10802 LISTVIEW_EndEditLabelT(infoPtr, save, isW);
10804 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10805 return 0;
10808 /***
10809 * DESCRIPTION:
10810 * Subclassed edit control Unicode windproc function
10812 * PARAMETER(S):
10813 * [I] hwnd : the edit window handle
10814 * [I] uMsg : the message that is to be processed
10815 * [I] wParam : first message parameter
10816 * [I] lParam : second message parameter
10818 * RETURN:
10820 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10822 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10825 /***
10826 * DESCRIPTION:
10827 * Subclassed edit control ANSI windproc function
10829 * PARAMETER(S):
10830 * [I] hwnd : the edit window handle
10831 * [I] uMsg : the message that is to be processed
10832 * [I] wParam : first message parameter
10833 * [I] lParam : second message parameter
10835 * RETURN:
10837 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10839 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10842 /***
10843 * DESCRIPTION:
10844 * Creates a subclassed edit control
10846 * PARAMETER(S):
10847 * [I] infoPtr : valid pointer to the listview structure
10848 * [I] text : initial text for the edit
10849 * [I] style : the window style
10850 * [I] isW : TRUE if input is Unicode
10852 * RETURN:
10854 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style, BOOL isW)
10856 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10857 HWND hedit;
10858 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10860 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10862 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10864 /* Window will be resized and positioned after LVN_BEGINLABELEDIT */
10865 if (isW)
10866 hedit = CreateWindowW(editName, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
10867 else
10868 hedit = CreateWindowA("Edit", (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
10870 if (!hedit) return 0;
10872 infoPtr->EditWndProc = (WNDPROC)
10873 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10874 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10876 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10878 return hedit;