push 573c111636dcd80fa61914ac833187ced6019cb7
[wine/hacks.git] / dlls / comctl32 / listview.c
blob8ef516a3ec50201429d64759d663fafd20e17a10
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 ? 0 : -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 LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
706 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
707 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
710 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
712 LRESULT result;
714 TRACE("(code=%d)\n", code);
716 pnmh->hwndFrom = infoPtr->hwndSelf;
717 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
718 pnmh->code = code;
719 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
721 TRACE(" <= %ld\n", result);
723 return result;
726 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
728 NMHDR nmh;
729 HWND hwnd = infoPtr->hwndSelf;
730 notify_hdr(infoPtr, code, &nmh);
731 return IsWindow(hwnd);
734 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
736 NMITEMACTIVATE nmia;
737 LVITEMW item;
739 if (htInfo) {
740 nmia.uNewState = 0;
741 nmia.uOldState = 0;
742 nmia.uChanged = 0;
743 nmia.uKeyFlags = 0;
745 item.mask = LVIF_PARAM|LVIF_STATE;
746 item.iItem = htInfo->iItem;
747 item.iSubItem = 0;
748 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
749 nmia.lParam = item.lParam;
750 nmia.uOldState = item.state;
751 nmia.uNewState = item.state | LVIS_ACTIVATING;
752 nmia.uChanged = LVIF_STATE;
755 nmia.iItem = htInfo->iItem;
756 nmia.iSubItem = htInfo->iSubItem;
757 nmia.ptAction = htInfo->pt;
759 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
760 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
761 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
763 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
766 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
768 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
769 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
772 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
774 NMITEMACTIVATE nmia;
775 LVITEMW item;
776 HWND hwnd = infoPtr->hwndSelf;
778 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
779 ZeroMemory(&nmia, sizeof(nmia));
780 nmia.iItem = lvht->iItem;
781 nmia.iSubItem = lvht->iSubItem;
782 nmia.ptAction = lvht->pt;
783 item.mask = LVIF_PARAM;
784 item.iItem = lvht->iItem;
785 item.iSubItem = 0;
786 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
787 notify_hdr(infoPtr, code, (LPNMHDR)&nmia);
788 return IsWindow(hwnd);
791 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
793 NMLISTVIEW nmlv;
794 LVITEMW item;
795 HWND hwnd = infoPtr->hwndSelf;
797 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
798 nmlv.iItem = nItem;
799 item.mask = LVIF_PARAM;
800 item.iItem = nItem;
801 item.iSubItem = 0;
802 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
803 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
804 return IsWindow(hwnd);
807 static int get_ansi_notification(UINT unicodeNotificationCode)
809 switch (unicodeNotificationCode)
811 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
812 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
813 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
814 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
815 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
816 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
818 ERR("unknown notification %x\n", unicodeNotificationCode);
819 assert(FALSE);
820 return 0;
824 Send notification. depends on dispinfoW having same
825 structure as dispinfoA.
826 infoPtr : listview struct
827 notificationCode : *Unicode* notification code
828 pdi : dispinfo structure (can be unicode or ansi)
829 isW : TRUE if dispinfo is Unicode
831 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
833 BOOL bResult = FALSE;
834 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
835 INT cchTempBufMax = 0, savCchTextMax = 0;
836 UINT realNotifCode;
837 LPWSTR pszTempBuf = NULL, savPszText = NULL;
839 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
841 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
842 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
845 if (convertToAnsi || convertToUnicode)
847 if (notificationCode != LVN_GETDISPINFOW)
849 cchTempBufMax = convertToUnicode ?
850 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
851 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
853 else
855 cchTempBufMax = pdi->item.cchTextMax;
856 *pdi->item.pszText = 0; /* make sure we don't process garbage */
859 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
860 if (!pszTempBuf) return FALSE;
862 if (convertToUnicode)
863 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
864 pszTempBuf, cchTempBufMax);
865 else
866 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
867 cchTempBufMax, NULL, NULL);
869 savCchTextMax = pdi->item.cchTextMax;
870 savPszText = pdi->item.pszText;
871 pdi->item.pszText = pszTempBuf;
872 pdi->item.cchTextMax = cchTempBufMax;
875 if (infoPtr->notifyFormat == NFR_ANSI)
876 realNotifCode = get_ansi_notification(notificationCode);
877 else
878 realNotifCode = notificationCode;
879 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
880 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
882 if (convertToUnicode || convertToAnsi)
884 if (convertToUnicode) /* note : pointer can be changed by app ! */
885 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
886 savCchTextMax, NULL, NULL);
887 else
888 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
889 savPszText, savCchTextMax);
890 pdi->item.pszText = savPszText; /* restores our buffer */
891 pdi->item.cchTextMax = savCchTextMax;
892 Free (pszTempBuf);
894 return bResult;
897 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
898 const RECT *rcBounds, const LVITEMW *lplvItem)
900 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
901 lpnmlvcd->nmcd.hdc = hdc;
902 lpnmlvcd->nmcd.rc = *rcBounds;
903 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
904 lpnmlvcd->clrText = infoPtr->clrText;
905 if (!lplvItem) return;
906 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
907 lpnmlvcd->iSubItem = lplvItem->iSubItem;
908 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
909 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
910 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
911 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
914 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
916 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
917 DWORD result;
919 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
920 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
921 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
922 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
923 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
924 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
925 return result;
928 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
930 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
931 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
932 if (lpnmlvcd->clrText == CLR_DEFAULT)
933 lpnmlvcd->clrText = comctl32_color.clrWindowText;
935 /* apparently, for selected items, we have to override the returned values */
936 if (!SubItem)
938 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
940 if (infoPtr->bFocus)
942 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
943 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
945 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
947 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
948 lpnmlvcd->clrText = comctl32_color.clrBtnText;
953 /* Set the text attributes */
954 if (lpnmlvcd->clrTextBk != CLR_NONE)
956 SetBkMode(hdc, OPAQUE);
957 SetBkColor(hdc,lpnmlvcd->clrTextBk);
959 else
960 SetBkMode(hdc, TRANSPARENT);
961 SetTextColor(hdc, lpnmlvcd->clrText);
964 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
966 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
969 /******** Item iterator functions **********************************/
971 static RANGES ranges_create(int count);
972 static void ranges_destroy(RANGES ranges);
973 static BOOL ranges_add(RANGES ranges, RANGE range);
974 static BOOL ranges_del(RANGES ranges, RANGE range);
975 static void ranges_dump(RANGES ranges);
977 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
979 RANGE range = { nItem, nItem + 1 };
981 return ranges_add(ranges, range);
984 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
986 RANGE range = { nItem, nItem + 1 };
988 return ranges_del(ranges, range);
991 /***
992 * ITERATOR DOCUMENTATION
994 * The iterator functions allow for easy, and convenient iteration
995 * over items of interest in the list. Typically, you create a
996 * iterator, use it, and destroy it, as such:
997 * ITERATOR i;
999 * iterator_xxxitems(&i, ...);
1000 * while (iterator_{prev,next}(&i)
1002 * //code which uses i.nItem
1004 * iterator_destroy(&i);
1006 * where xxx is either: framed, or visible.
1007 * Note that it is important that the code destroys the iterator
1008 * after it's done with it, as the creation of the iterator may
1009 * allocate memory, which thus needs to be freed.
1011 * You can iterate both forwards, and backwards through the list,
1012 * by using iterator_next or iterator_prev respectively.
1014 * Lower numbered items are draw on top of higher number items in
1015 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1016 * items may overlap). So, to test items, you should use
1017 * iterator_next
1018 * which lists the items top to bottom (in Z-order).
1019 * For drawing items, you should use
1020 * iterator_prev
1021 * which lists the items bottom to top (in Z-order).
1022 * If you keep iterating over the items after the end-of-items
1023 * marker (-1) is returned, the iterator will start from the
1024 * beginning. Typically, you don't need to test for -1,
1025 * because iterator_{next,prev} will return TRUE if more items
1026 * are to be iterated over, or FALSE otherwise.
1028 * Note: the iterator is defined to be bidirectional. That is,
1029 * any number of prev followed by any number of next, or
1030 * five versa, should leave the iterator at the same item:
1031 * prev * n, next * n = next * n, prev * n
1033 * The iterator has a notion of an out-of-order, special item,
1034 * which sits at the start of the list. This is used in
1035 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1036 * which needs to be first, as it may overlap other items.
1038 * The code is a bit messy because we have:
1039 * - a special item to deal with
1040 * - simple range, or composite range
1041 * - empty range.
1042 * If you find bugs, or want to add features, please make sure you
1043 * always check/modify *both* iterator_prev, and iterator_next.
1046 /****
1047 * This function iterates through the items in increasing order,
1048 * but prefixed by the special item, then -1. That is:
1049 * special, 1, 2, 3, ..., n, -1.
1050 * Each item is listed only once.
1052 static inline BOOL iterator_next(ITERATOR* i)
1054 if (i->nItem == -1)
1056 i->nItem = i->nSpecial;
1057 if (i->nItem != -1) return TRUE;
1059 if (i->nItem == i->nSpecial)
1061 if (i->ranges) i->index = 0;
1062 goto pickarange;
1065 i->nItem++;
1066 testitem:
1067 if (i->nItem == i->nSpecial) i->nItem++;
1068 if (i->nItem < i->range.upper) return TRUE;
1070 pickarange:
1071 if (i->ranges)
1073 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1074 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1075 else goto end;
1077 else if (i->nItem >= i->range.upper) goto end;
1079 i->nItem = i->range.lower;
1080 if (i->nItem >= 0) goto testitem;
1081 end:
1082 i->nItem = -1;
1083 return FALSE;
1086 /****
1087 * This function iterates through the items in decreasing order,
1088 * followed by the special item, then -1. That is:
1089 * n, n-1, ..., 3, 2, 1, special, -1.
1090 * Each item is listed only once.
1092 static inline BOOL iterator_prev(ITERATOR* i)
1094 BOOL start = FALSE;
1096 if (i->nItem == -1)
1098 start = TRUE;
1099 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1100 goto pickarange;
1102 if (i->nItem == i->nSpecial)
1104 i->nItem = -1;
1105 return FALSE;
1108 testitem:
1109 i->nItem--;
1110 if (i->nItem == i->nSpecial) i->nItem--;
1111 if (i->nItem >= i->range.lower) return TRUE;
1113 pickarange:
1114 if (i->ranges)
1116 if (i->index > 0)
1117 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1118 else goto end;
1120 else if (!start && i->nItem < i->range.lower) goto end;
1122 i->nItem = i->range.upper;
1123 if (i->nItem > 0) goto testitem;
1124 end:
1125 return (i->nItem = i->nSpecial) != -1;
1128 static RANGE iterator_range(const ITERATOR *i)
1130 RANGE range;
1132 if (!i->ranges) return i->range;
1134 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1136 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1137 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1139 else range.lower = range.upper = 0;
1141 return range;
1144 /***
1145 * Releases resources associated with this ierator.
1147 static inline void iterator_destroy(const ITERATOR *i)
1149 ranges_destroy(i->ranges);
1152 /***
1153 * Create an empty iterator.
1155 static inline BOOL iterator_empty(ITERATOR* i)
1157 ZeroMemory(i, sizeof(*i));
1158 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1159 return TRUE;
1162 /***
1163 * Create an iterator over a range.
1165 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1167 iterator_empty(i);
1168 i->range = range;
1169 return TRUE;
1172 /***
1173 * Create an iterator over a bunch of ranges.
1174 * Please note that the iterator will take ownership of the ranges,
1175 * and will free them upon destruction.
1177 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1179 iterator_empty(i);
1180 i->ranges = ranges;
1181 return TRUE;
1184 /***
1185 * Creates an iterator over the items which intersect lprc.
1187 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1189 RECT frame = *lprc, rcItem, rcTemp;
1190 POINT Origin;
1192 /* in case we fail, we want to return an empty iterator */
1193 if (!iterator_empty(i)) return FALSE;
1195 LISTVIEW_GetOrigin(infoPtr, &Origin);
1197 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1198 OffsetRect(&frame, -Origin.x, -Origin.y);
1200 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1202 INT nItem;
1204 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1206 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1207 if (IntersectRect(&rcTemp, &rcItem, lprc))
1208 i->nSpecial = infoPtr->nFocusedItem;
1210 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1211 /* to do better here, we need to have PosX, and PosY sorted */
1212 TRACE("building icon ranges:\n");
1213 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1215 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1216 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1217 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1218 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1219 if (IntersectRect(&rcTemp, &rcItem, &frame))
1220 ranges_additem(i->ranges, nItem);
1222 return TRUE;
1224 else if (infoPtr->uView == LV_VIEW_DETAILS)
1226 RANGE range;
1228 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1229 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1231 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1232 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1233 if (range.upper <= range.lower) return TRUE;
1234 if (!iterator_rangeitems(i, range)) return FALSE;
1235 TRACE(" report=%s\n", debugrange(&i->range));
1237 else
1239 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1240 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1241 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1242 INT nFirstCol;
1243 INT nLastCol;
1244 INT lower;
1245 RANGE item_range;
1246 INT nCol;
1248 if (infoPtr->nItemWidth)
1250 nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1251 nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1253 else
1255 nFirstCol = max(frame.left, 0);
1256 nLastCol = min(frame.right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1259 lower = nFirstCol * nPerCol + nFirstRow;
1261 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1262 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1264 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1266 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1267 TRACE("building list ranges:\n");
1268 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1270 item_range.lower = nCol * nPerCol + nFirstRow;
1271 if(item_range.lower >= infoPtr->nItemCount) break;
1272 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1273 TRACE(" list=%s\n", debugrange(&item_range));
1274 ranges_add(i->ranges, item_range);
1278 return TRUE;
1281 /***
1282 * Creates an iterator over the items which intersect the visible region of hdc.
1284 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1286 POINT Origin, Position;
1287 RECT rcItem, rcClip;
1288 INT rgntype;
1290 rgntype = GetClipBox(hdc, &rcClip);
1291 if (rgntype == NULLREGION) return iterator_empty(i);
1292 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1293 if (rgntype == SIMPLEREGION) return TRUE;
1295 /* first deal with the special item */
1296 if (i->nSpecial != -1)
1298 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1299 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1302 /* if we can't deal with the region, we'll just go with the simple range */
1303 LISTVIEW_GetOrigin(infoPtr, &Origin);
1304 TRACE("building visible range:\n");
1305 if (!i->ranges && i->range.lower < i->range.upper)
1307 if (!(i->ranges = ranges_create(50))) return TRUE;
1308 if (!ranges_add(i->ranges, i->range))
1310 ranges_destroy(i->ranges);
1311 i->ranges = 0;
1312 return TRUE;
1316 /* now delete the invisible items from the list */
1317 while(iterator_next(i))
1319 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1320 rcItem.left = Position.x + Origin.x;
1321 rcItem.top = Position.y + Origin.y;
1322 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1323 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1324 if (!RectVisible(hdc, &rcItem))
1325 ranges_delitem(i->ranges, i->nItem);
1327 /* the iterator should restart on the next iterator_next */
1328 TRACE("done\n");
1330 return TRUE;
1333 /******** Misc helper functions ************************************/
1335 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1336 WPARAM wParam, LPARAM lParam, BOOL isW)
1338 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1339 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1342 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1344 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1345 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1348 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1350 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1351 if(state == 1 || state == 2)
1353 LVITEMW lvitem;
1354 state ^= 3;
1355 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1356 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1357 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1361 /* this should be called after window style got updated,
1362 it used to reset view state to match current window style */
1363 static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1365 switch (infoPtr->dwStyle & LVS_TYPEMASK)
1367 case LVS_ICON:
1368 infoPtr->uView = LV_VIEW_ICON;
1369 break;
1370 case LVS_REPORT:
1371 infoPtr->uView = LV_VIEW_DETAILS;
1372 break;
1373 case LVS_SMALLICON:
1374 infoPtr->uView = LV_VIEW_SMALLICON;
1375 break;
1376 case LVS_LIST:
1377 infoPtr->uView = LV_VIEW_LIST;
1381 /******** Internal API functions ************************************/
1383 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1385 static COLUMN_INFO mainItem;
1387 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1388 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1389 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1392 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1394 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1395 HINSTANCE hInst;
1397 if (infoPtr->hwndHeader) return 0;
1399 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1401 /* setup creation flags */
1402 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1403 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1405 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1407 /* create header */
1408 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1409 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1410 if (!infoPtr->hwndHeader) return -1;
1412 /* set header unicode format */
1413 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1415 /* set header font */
1416 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1418 LISTVIEW_UpdateSize(infoPtr);
1420 return 0;
1423 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1425 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1428 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1430 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1433 /* used to handle collapse main item column case */
1434 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1436 return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1437 DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1440 /* Listview invalidation functions: use _only_ these functions to invalidate */
1442 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1444 return infoPtr->bRedraw;
1447 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1449 if(!is_redrawing(infoPtr)) return;
1450 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1451 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1454 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1456 RECT rcBox;
1458 if(!is_redrawing(infoPtr)) return;
1459 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1460 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1463 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1465 POINT Origin, Position;
1466 RECT rcBox;
1468 if(!is_redrawing(infoPtr)) return;
1469 assert (infoPtr->uView == LV_VIEW_DETAILS);
1470 LISTVIEW_GetOrigin(infoPtr, &Origin);
1471 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1472 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1473 rcBox.top = 0;
1474 rcBox.bottom = infoPtr->nItemHeight;
1475 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1476 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1479 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1481 LISTVIEW_InvalidateRect(infoPtr, NULL);
1484 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1486 RECT rcCol;
1488 if(!is_redrawing(infoPtr)) return;
1489 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1490 rcCol.top = infoPtr->rcList.top;
1491 rcCol.bottom = infoPtr->rcList.bottom;
1492 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1495 /***
1496 * DESCRIPTION:
1497 * Retrieves the number of items that can fit vertically in the client area.
1499 * PARAMETER(S):
1500 * [I] infoPtr : valid pointer to the listview structure
1502 * RETURN:
1503 * Number of items per row.
1505 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1507 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1509 return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1512 /***
1513 * DESCRIPTION:
1514 * Retrieves the number of items that can fit horizontally in the client
1515 * area.
1517 * PARAMETER(S):
1518 * [I] infoPtr : valid pointer to the listview structure
1520 * RETURN:
1521 * Number of items per column.
1523 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1525 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1527 return max(nListHeight / infoPtr->nItemHeight, 1);
1531 /*************************************************************************
1532 * LISTVIEW_ProcessLetterKeys
1534 * Processes keyboard messages generated by pressing the letter keys
1535 * on the keyboard.
1536 * What this does is perform a case insensitive search from the
1537 * current position with the following quirks:
1538 * - If two chars or more are pressed in quick succession we search
1539 * for the corresponding string (e.g. 'abc').
1540 * - If there is a delay we wipe away the current search string and
1541 * restart with just that char.
1542 * - If the user keeps pressing the same character, whether slowly or
1543 * fast, so that the search string is entirely composed of this
1544 * character ('aaaaa' for instance), then we search for first item
1545 * that starting with that character.
1546 * - If the user types the above character in quick succession, then
1547 * we must also search for the corresponding string ('aaaaa'), and
1548 * go to that string if there is a match.
1550 * PARAMETERS
1551 * [I] hwnd : handle to the window
1552 * [I] charCode : the character code, the actual character
1553 * [I] keyData : key data
1555 * RETURNS
1557 * Zero.
1559 * BUGS
1561 * - The current implementation has a list of characters it will
1562 * accept and it ignores everything else. In particular it will
1563 * ignore accentuated characters which seems to match what
1564 * Windows does. But I'm not sure it makes sense to follow
1565 * Windows there.
1566 * - We don't sound a beep when the search fails.
1568 * SEE ALSO
1570 * TREEVIEW_ProcessLetterKeys
1572 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1574 INT nItem;
1575 INT endidx,idx;
1576 LVITEMW item;
1577 WCHAR buffer[MAX_PATH];
1578 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1580 /* simple parameter checking */
1581 if (!charCode || !keyData) return 0;
1583 /* only allow the valid WM_CHARs through */
1584 if (!isalnumW(charCode) &&
1585 charCode != '.' && charCode != '`' && charCode != '!' &&
1586 charCode != '@' && charCode != '#' && charCode != '$' &&
1587 charCode != '%' && charCode != '^' && charCode != '&' &&
1588 charCode != '*' && charCode != '(' && charCode != ')' &&
1589 charCode != '-' && charCode != '_' && charCode != '+' &&
1590 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1591 charCode != '}' && charCode != '[' && charCode != '{' &&
1592 charCode != '/' && charCode != '?' && charCode != '>' &&
1593 charCode != '<' && charCode != ',' && charCode != '~')
1594 return 0;
1596 /* if there's one item or less, there is no where to go */
1597 if (infoPtr->nItemCount <= 1) return 0;
1599 /* update the search parameters */
1600 infoPtr->lastKeyPressTimestamp = GetTickCount();
1601 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1602 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1603 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1604 if (infoPtr->charCode != charCode)
1605 infoPtr->charCode = charCode = 0;
1606 } else {
1607 infoPtr->charCode=charCode;
1608 infoPtr->szSearchParam[0]=charCode;
1609 infoPtr->nSearchParamLength=1;
1610 /* Redundant with the 1 char string */
1611 charCode=0;
1614 /* and search from the current position */
1615 nItem=-1;
1616 if (infoPtr->nFocusedItem >= 0) {
1617 endidx=infoPtr->nFocusedItem;
1618 idx=endidx;
1619 /* if looking for single character match,
1620 * then we must always move forward
1622 if (infoPtr->nSearchParamLength == 1)
1623 idx++;
1624 } else {
1625 endidx=infoPtr->nItemCount;
1626 idx=0;
1629 /* Let application handle this for virtual listview */
1630 if (infoPtr->dwStyle & LVS_OWNERDATA)
1632 NMLVFINDITEMW nmlv;
1633 LVFINDINFOW lvfi;
1635 ZeroMemory(&lvfi, sizeof(lvfi));
1636 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1637 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1638 lvfi.psz = infoPtr->szSearchParam;
1639 nmlv.iStart = idx;
1640 nmlv.lvfi = lvfi;
1642 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1644 if (nItem != -1)
1645 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1647 return 0;
1650 do {
1651 if (idx == infoPtr->nItemCount) {
1652 if (endidx == infoPtr->nItemCount || endidx == 0)
1653 break;
1654 idx=0;
1657 /* get item */
1658 item.mask = LVIF_TEXT;
1659 item.iItem = idx;
1660 item.iSubItem = 0;
1661 item.pszText = buffer;
1662 item.cchTextMax = MAX_PATH;
1663 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1665 /* check for a match */
1666 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1667 nItem=idx;
1668 break;
1669 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1670 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1671 /* This would work but we must keep looking for a longer match */
1672 nItem=idx;
1674 idx++;
1675 } while (idx != endidx);
1677 if (nItem != -1)
1678 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1680 return 0;
1683 /*************************************************************************
1684 * LISTVIEW_UpdateHeaderSize [Internal]
1686 * Function to resize the header control
1688 * PARAMS
1689 * [I] hwnd : handle to a window
1690 * [I] nNewScrollPos : scroll pos to set
1692 * RETURNS
1693 * None.
1695 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1697 RECT winRect;
1698 POINT point[2];
1700 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1702 if (!infoPtr->hwndHeader) return;
1704 GetWindowRect(infoPtr->hwndHeader, &winRect);
1705 point[0].x = winRect.left;
1706 point[0].y = winRect.top;
1707 point[1].x = winRect.right;
1708 point[1].y = winRect.bottom;
1710 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1711 point[0].x = -nNewScrollPos;
1712 point[1].x += nNewScrollPos;
1714 SetWindowPos(infoPtr->hwndHeader,0,
1715 point[0].x,point[0].y,point[1].x,point[1].y,
1716 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1717 SWP_NOZORDER | SWP_NOACTIVATE);
1720 /***
1721 * DESCRIPTION:
1722 * Update the scrollbars. This functions should be called whenever
1723 * the content, size or view changes.
1725 * PARAMETER(S):
1726 * [I] infoPtr : valid pointer to the listview structure
1728 * RETURN:
1729 * None
1731 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1733 SCROLLINFO horzInfo, vertInfo;
1734 INT dx, dy;
1736 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1738 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1739 horzInfo.cbSize = sizeof(SCROLLINFO);
1740 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1742 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1743 if (infoPtr->uView == LV_VIEW_LIST)
1745 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1746 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1748 /* scroll by at least one column per page */
1749 if(horzInfo.nPage < infoPtr->nItemWidth)
1750 horzInfo.nPage = infoPtr->nItemWidth;
1752 if (infoPtr->nItemWidth)
1753 horzInfo.nPage /= infoPtr->nItemWidth;
1755 else if (infoPtr->uView == LV_VIEW_DETAILS)
1757 horzInfo.nMax = infoPtr->nItemWidth;
1759 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1761 RECT rcView;
1763 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1766 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1767 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1768 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1769 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1770 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1772 /* Setting the horizontal scroll can change the listview size
1773 * (and potentially everything else) so we need to recompute
1774 * everything again for the vertical scroll
1777 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1778 vertInfo.cbSize = sizeof(SCROLLINFO);
1779 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1781 if (infoPtr->uView == LV_VIEW_DETAILS)
1783 vertInfo.nMax = infoPtr->nItemCount;
1785 /* scroll by at least one page */
1786 if(vertInfo.nPage < infoPtr->nItemHeight)
1787 vertInfo.nPage = infoPtr->nItemHeight;
1789 if (infoPtr->nItemHeight > 0)
1790 vertInfo.nPage /= infoPtr->nItemHeight;
1792 else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1794 RECT rcView;
1796 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1799 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1800 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1801 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1802 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1803 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1805 /* Change of the range may have changed the scroll pos. If so move the content */
1806 if (dx != 0 || dy != 0)
1808 RECT listRect;
1809 listRect = infoPtr->rcList;
1810 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1811 SW_ERASE | SW_INVALIDATE);
1814 /* Update the Header Control */
1815 if (infoPtr->uView == LV_VIEW_DETAILS)
1817 horzInfo.fMask = SIF_POS;
1818 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1819 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1824 /***
1825 * DESCRIPTION:
1826 * Shows/hides the focus rectangle.
1828 * PARAMETER(S):
1829 * [I] infoPtr : valid pointer to the listview structure
1830 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1832 * RETURN:
1833 * None
1835 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1837 HDC hdc;
1839 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1841 if (infoPtr->nFocusedItem < 0) return;
1843 /* we need some gymnastics in ICON mode to handle large items */
1844 if (infoPtr->uView == LV_VIEW_ICON)
1846 RECT rcBox;
1848 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1849 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1851 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1852 return;
1856 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1858 /* for some reason, owner draw should work only in report mode */
1859 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
1861 DRAWITEMSTRUCT dis;
1862 LVITEMW item;
1864 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1865 HFONT hOldFont = SelectObject(hdc, hFont);
1867 item.iItem = infoPtr->nFocusedItem;
1868 item.iSubItem = 0;
1869 item.mask = LVIF_PARAM;
1870 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1872 ZeroMemory(&dis, sizeof(dis));
1873 dis.CtlType = ODT_LISTVIEW;
1874 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1875 dis.itemID = item.iItem;
1876 dis.itemAction = ODA_FOCUS;
1877 if (fShow) dis.itemState |= ODS_FOCUS;
1878 dis.hwndItem = infoPtr->hwndSelf;
1879 dis.hDC = hdc;
1880 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1881 dis.itemData = item.lParam;
1883 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1885 SelectObject(hdc, hOldFont);
1887 else
1889 LISTVIEW_DrawFocusRect(infoPtr, hdc);
1891 done:
1892 ReleaseDC(infoPtr->hwndSelf, hdc);
1895 /***
1896 * Invalidates all visible selected items.
1898 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1900 ITERATOR i;
1902 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1903 while(iterator_next(&i))
1905 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1906 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1908 iterator_destroy(&i);
1912 /***
1913 * DESCRIPTION: [INTERNAL]
1914 * Computes an item's (left,top) corner, relative to rcView.
1915 * That is, the position has NOT been made relative to the Origin.
1916 * This is deliberate, to avoid computing the Origin over, and
1917 * over again, when this function is called in a loop. Instead,
1918 * one can factor the computation of the Origin before the loop,
1919 * and offset the value returned by this function, on every iteration.
1921 * PARAMETER(S):
1922 * [I] infoPtr : valid pointer to the listview structure
1923 * [I] nItem : item number
1924 * [O] lpptOrig : item top, left corner
1926 * RETURN:
1927 * None.
1929 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1931 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1933 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
1935 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1936 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1938 else if (infoPtr->uView == LV_VIEW_LIST)
1940 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1941 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1942 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1944 else /* LV_VIEW_DETAILS */
1946 lpptPosition->x = REPORT_MARGINX;
1947 /* item is always at zero indexed column */
1948 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
1949 lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
1950 lpptPosition->y = nItem * infoPtr->nItemHeight;
1954 /***
1955 * DESCRIPTION: [INTERNAL]
1956 * Compute the rectangles of an item. This is to localize all
1957 * the computations in one place. If you are not interested in some
1958 * of these values, simply pass in a NULL -- the function is smart
1959 * enough to compute only what's necessary. The function computes
1960 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1961 * one, the BOX rectangle. This rectangle is very cheap to compute,
1962 * and is guaranteed to contain all the other rectangles. Computing
1963 * the ICON rect is also cheap, but all the others are potentially
1964 * expensive. This gives an easy and effective optimization when
1965 * searching (like point inclusion, or rectangle intersection):
1966 * first test against the BOX, and if TRUE, test against the desired
1967 * rectangle.
1968 * If the function does not have all the necessary information
1969 * to computed the requested rectangles, will crash with a
1970 * failed assertion. This is done so we catch all programming
1971 * errors, given that the function is called only from our code.
1973 * We have the following 'special' meanings for a few fields:
1974 * * If LVIS_FOCUSED is set, we assume the item has the focus
1975 * This is important in ICON mode, where it might get a larger
1976 * then usual rectangle
1978 * Please note that subitem support works only in REPORT mode.
1980 * PARAMETER(S):
1981 * [I] infoPtr : valid pointer to the listview structure
1982 * [I] lpLVItem : item to compute the measures for
1983 * [O] lprcBox : ptr to Box rectangle
1984 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1985 * [0] lprcSelectBox : ptr to select box rectangle
1986 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1987 * [O] lprcIcon : ptr to Icon rectangle
1988 * Same as LVM_GETITEMRECT with LVIR_ICON
1989 * [O] lprcStateIcon: ptr to State Icon rectangle
1990 * [O] lprcLabel : ptr to Label rectangle
1991 * Same as LVM_GETITEMRECT with LVIR_LABEL
1993 * RETURN:
1994 * None.
1996 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1997 LPRECT lprcBox, LPRECT lprcSelectBox,
1998 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2000 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2001 RECT Box, SelectBox, Icon, Label;
2002 COLUMN_INFO *lpColumnInfo = NULL;
2003 SIZE labelSize = { 0, 0 };
2005 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2007 /* Be smart and try to figure out the minimum we have to do */
2008 if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2009 if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2011 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2012 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2014 if (lprcSelectBox) doSelectBox = TRUE;
2015 if (lprcLabel) doLabel = TRUE;
2016 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2017 if (doSelectBox)
2019 doIcon = TRUE;
2020 doLabel = TRUE;
2023 /************************************************************/
2024 /* compute the box rectangle (it should be cheap to do) */
2025 /************************************************************/
2026 if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2027 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2029 if (lpLVItem->iSubItem)
2031 Box = lpColumnInfo->rcHeader;
2033 else
2035 Box.left = 0;
2036 Box.right = infoPtr->nItemWidth;
2038 Box.top = 0;
2039 Box.bottom = infoPtr->nItemHeight;
2041 /******************************************************************/
2042 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2043 /******************************************************************/
2044 if (doIcon)
2046 LONG state_width = 0;
2048 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2049 state_width = infoPtr->iconStateSize.cx;
2051 if (infoPtr->uView == LV_VIEW_ICON)
2053 Icon.left = Box.left + state_width;
2054 if (infoPtr->himlNormal)
2055 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2056 Icon.top = Box.top + ICON_TOP_PADDING;
2057 Icon.right = Icon.left;
2058 Icon.bottom = Icon.top;
2059 if (infoPtr->himlNormal)
2061 Icon.right += infoPtr->iconSize.cx;
2062 Icon.bottom += infoPtr->iconSize.cy;
2065 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2067 Icon.left = Box.left + state_width;
2069 if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2071 /* we need the indent in report mode */
2072 assert(lpLVItem->mask & LVIF_INDENT);
2073 Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2076 Icon.top = Box.top;
2077 Icon.right = Icon.left;
2078 if (infoPtr->himlSmall &&
2079 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2080 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2081 Icon.right += infoPtr->iconSize.cx;
2082 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2084 if(lprcIcon) *lprcIcon = Icon;
2085 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2087 /* TODO: is this correct? */
2088 if (lprcStateIcon)
2090 lprcStateIcon->left = Icon.left - state_width;
2091 lprcStateIcon->right = Icon.left;
2092 lprcStateIcon->top = Icon.top;
2093 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2094 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2097 else Icon.right = 0;
2099 /************************************************************/
2100 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2101 /************************************************************/
2102 if (doLabel)
2104 /* calculate how far to the right can the label stretch */
2105 Label.right = Box.right;
2106 if (infoPtr->uView == LV_VIEW_DETAILS)
2108 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2111 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2113 labelSize.cx = infoPtr->nItemWidth;
2114 labelSize.cy = infoPtr->nItemHeight;
2115 goto calc_label;
2118 /* we need the text in non owner draw mode */
2119 assert(lpLVItem->mask & LVIF_TEXT);
2120 if (is_textT(lpLVItem->pszText, TRUE))
2122 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2123 HDC hdc = GetDC(infoPtr->hwndSelf);
2124 HFONT hOldFont = SelectObject(hdc, hFont);
2125 UINT uFormat;
2126 RECT rcText;
2128 /* compute rough rectangle where the label will go */
2129 SetRectEmpty(&rcText);
2130 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2131 rcText.bottom = infoPtr->nItemHeight;
2132 if (infoPtr->uView == LV_VIEW_ICON)
2133 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2135 /* now figure out the flags */
2136 if (infoPtr->uView == LV_VIEW_ICON)
2137 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2138 else
2139 uFormat = LV_SL_DT_FLAGS;
2141 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2143 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2144 labelSize.cy = rcText.bottom - rcText.top;
2146 SelectObject(hdc, hOldFont);
2147 ReleaseDC(infoPtr->hwndSelf, hdc);
2150 calc_label:
2151 if (infoPtr->uView == LV_VIEW_ICON)
2153 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2154 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2155 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2156 Label.right = Label.left + labelSize.cx;
2157 Label.bottom = Label.top + infoPtr->nItemHeight;
2158 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2160 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2161 labelSize.cy /= infoPtr->ntmHeight;
2162 labelSize.cy = max(labelSize.cy, 1);
2163 labelSize.cy *= infoPtr->ntmHeight;
2165 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2167 else if (infoPtr->uView == LV_VIEW_DETAILS)
2169 Label.left = Icon.right;
2170 Label.top = Box.top;
2171 Label.right = lpColumnInfo->rcHeader.right;
2172 Label.bottom = Label.top + infoPtr->nItemHeight;
2174 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2176 Label.left = Icon.right;
2177 Label.top = Box.top;
2178 Label.right = min(Label.left + labelSize.cx, Label.right);
2179 Label.bottom = Label.top + infoPtr->nItemHeight;
2182 if (lprcLabel) *lprcLabel = Label;
2183 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2186 /************************************************************/
2187 /* compute SELECT bounding box */
2188 /************************************************************/
2189 if (doSelectBox)
2191 if (infoPtr->uView == LV_VIEW_DETAILS)
2193 SelectBox.left = Icon.left;
2194 SelectBox.top = Box.top;
2195 SelectBox.bottom = Box.bottom;
2196 SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2198 else
2200 UnionRect(&SelectBox, &Icon, &Label);
2202 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2203 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2206 /* Fix the Box if necessary */
2207 if (lprcBox)
2209 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2210 else *lprcBox = Box;
2212 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2215 /***
2216 * DESCRIPTION: [INTERNAL]
2218 * PARAMETER(S):
2219 * [I] infoPtr : valid pointer to the listview structure
2220 * [I] nItem : item number
2221 * [O] lprcBox : ptr to Box rectangle
2223 * RETURN:
2224 * None.
2226 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2228 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2229 POINT Position, Origin;
2230 LVITEMW lvItem;
2232 LISTVIEW_GetOrigin(infoPtr, &Origin);
2233 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2235 /* Be smart and try to figure out the minimum we have to do */
2236 lvItem.mask = 0;
2237 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2238 lvItem.mask |= LVIF_TEXT;
2239 lvItem.iItem = nItem;
2240 lvItem.iSubItem = 0;
2241 lvItem.pszText = szDispText;
2242 lvItem.cchTextMax = DISP_TEXT_SIZE;
2243 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2244 if (infoPtr->uView == LV_VIEW_ICON)
2246 lvItem.mask |= LVIF_STATE;
2247 lvItem.stateMask = LVIS_FOCUSED;
2248 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2250 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2252 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2256 /***
2257 * DESCRIPTION:
2258 * Returns the current icon position, and advances it along the top.
2259 * The returned position is not offset by Origin.
2261 * PARAMETER(S):
2262 * [I] infoPtr : valid pointer to the listview structure
2263 * [O] lpPos : will get the current icon position
2265 * RETURN:
2266 * None
2268 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2270 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2272 *lpPos = infoPtr->currIconPos;
2274 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2275 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2277 infoPtr->currIconPos.x = 0;
2278 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2282 /***
2283 * DESCRIPTION:
2284 * Returns the current icon position, and advances it down the left edge.
2285 * The returned position is not offset by Origin.
2287 * PARAMETER(S):
2288 * [I] infoPtr : valid pointer to the listview structure
2289 * [O] lpPos : will get the current icon position
2291 * RETURN:
2292 * None
2294 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2296 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2298 *lpPos = infoPtr->currIconPos;
2300 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2301 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2303 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2304 infoPtr->currIconPos.y = 0;
2308 /***
2309 * DESCRIPTION:
2310 * Moves an icon to the specified position.
2311 * It takes care of invalidating the item, etc.
2313 * PARAMETER(S):
2314 * [I] infoPtr : valid pointer to the listview structure
2315 * [I] nItem : the item to move
2316 * [I] lpPos : the new icon position
2317 * [I] isNew : flags the item as being new
2319 * RETURN:
2320 * Success: TRUE
2321 * Failure: FALSE
2323 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2325 POINT old;
2327 if (!isNew)
2329 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2330 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2332 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2333 LISTVIEW_InvalidateItem(infoPtr, nItem);
2336 /* Allocating a POINTER for every item is too resource intensive,
2337 * so we'll keep the (x,y) in different arrays */
2338 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2339 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2341 LISTVIEW_InvalidateItem(infoPtr, nItem);
2343 return TRUE;
2346 /***
2347 * DESCRIPTION:
2348 * Arranges listview items in icon display mode.
2350 * PARAMETER(S):
2351 * [I] infoPtr : valid pointer to the listview structure
2352 * [I] nAlignCode : alignment code
2354 * RETURN:
2355 * SUCCESS : TRUE
2356 * FAILURE : FALSE
2358 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2360 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2361 POINT pos;
2362 INT i;
2364 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2366 TRACE("nAlignCode=%d\n", nAlignCode);
2368 if (nAlignCode == LVA_DEFAULT)
2370 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2371 else nAlignCode = LVA_ALIGNTOP;
2374 switch (nAlignCode)
2376 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2377 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2378 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2379 default: return FALSE;
2382 infoPtr->bAutoarrange = TRUE;
2383 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2384 for (i = 0; i < infoPtr->nItemCount; i++)
2386 next_pos(infoPtr, &pos);
2387 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2390 return TRUE;
2393 /***
2394 * DESCRIPTION:
2395 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2396 * For LVS_REPORT always returns empty rectangle.
2398 * PARAMETER(S):
2399 * [I] infoPtr : valid pointer to the listview structure
2400 * [O] lprcView : bounding rectangle
2402 * RETURN:
2403 * SUCCESS : TRUE
2404 * FAILURE : FALSE
2406 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2408 INT i, x, y;
2410 SetRectEmpty(lprcView);
2412 switch (infoPtr->uView)
2414 case LV_VIEW_ICON:
2415 case LV_VIEW_SMALLICON:
2416 for (i = 0; i < infoPtr->nItemCount; i++)
2418 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2419 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2420 lprcView->right = max(lprcView->right, x);
2421 lprcView->bottom = max(lprcView->bottom, y);
2423 if (infoPtr->nItemCount > 0)
2425 lprcView->right += infoPtr->nItemWidth;
2426 lprcView->bottom += infoPtr->nItemHeight;
2428 break;
2430 case LV_VIEW_LIST:
2431 y = LISTVIEW_GetCountPerColumn(infoPtr);
2432 x = infoPtr->nItemCount / y;
2433 if (infoPtr->nItemCount % y) x++;
2434 lprcView->right = x * infoPtr->nItemWidth;
2435 lprcView->bottom = y * infoPtr->nItemHeight;
2436 break;
2440 /***
2441 * DESCRIPTION:
2442 * Retrieves the bounding rectangle of all the items.
2444 * PARAMETER(S):
2445 * [I] infoPtr : valid pointer to the listview structure
2446 * [O] lprcView : bounding rectangle
2448 * RETURN:
2449 * SUCCESS : TRUE
2450 * FAILURE : FALSE
2452 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2454 POINT ptOrigin;
2456 TRACE("(lprcView=%p)\n", lprcView);
2458 if (!lprcView) return FALSE;
2460 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2462 if (infoPtr->uView != LV_VIEW_DETAILS)
2464 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2465 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2468 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2470 return TRUE;
2473 /***
2474 * DESCRIPTION:
2475 * Retrieves the subitem pointer associated with the subitem index.
2477 * PARAMETER(S):
2478 * [I] hdpaSubItems : DPA handle for a specific item
2479 * [I] nSubItem : index of subitem
2481 * RETURN:
2482 * SUCCESS : subitem pointer
2483 * FAILURE : NULL
2485 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2487 SUBITEM_INFO *lpSubItem;
2488 INT i;
2490 /* we should binary search here if need be */
2491 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2493 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2494 if (lpSubItem->iSubItem == nSubItem)
2495 return lpSubItem;
2498 return NULL;
2502 /***
2503 * DESCRIPTION:
2504 * Calculates the desired item width.
2506 * PARAMETER(S):
2507 * [I] infoPtr : valid pointer to the listview structure
2509 * RETURN:
2510 * The desired item width.
2512 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2514 INT nItemWidth = 0;
2516 TRACE("uView=%d\n", infoPtr->uView);
2518 if (infoPtr->uView == LV_VIEW_ICON)
2519 nItemWidth = infoPtr->iconSpacing.cx;
2520 else if (infoPtr->uView == LV_VIEW_DETAILS)
2522 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2524 RECT rcHeader;
2525 INT index;
2527 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2528 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2530 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2531 nItemWidth = rcHeader.right;
2534 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2536 INT i;
2538 for (i = 0; i < infoPtr->nItemCount; i++)
2539 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2541 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2542 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2544 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2547 return nItemWidth;
2550 /***
2551 * DESCRIPTION:
2552 * Calculates the desired item height.
2554 * PARAMETER(S):
2555 * [I] infoPtr : valid pointer to the listview structure
2557 * RETURN:
2558 * The desired item height.
2560 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2562 INT nItemHeight;
2564 TRACE("uView=%d\n", infoPtr->uView);
2566 if (infoPtr->uView == LV_VIEW_ICON)
2567 nItemHeight = infoPtr->iconSpacing.cy;
2568 else
2570 nItemHeight = infoPtr->ntmHeight;
2571 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2572 nItemHeight++;
2573 if (infoPtr->himlState)
2574 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2575 if (infoPtr->himlSmall)
2576 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2577 if (infoPtr->himlState || infoPtr->himlSmall)
2578 nItemHeight += HEIGHT_PADDING;
2579 if (infoPtr->nMeasureItemHeight > 0)
2580 nItemHeight = infoPtr->nMeasureItemHeight;
2583 return max(nItemHeight, 1);
2586 /***
2587 * DESCRIPTION:
2588 * Updates the width, and height of an item.
2590 * PARAMETER(S):
2591 * [I] infoPtr : valid pointer to the listview structure
2593 * RETURN:
2594 * None.
2596 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2598 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2599 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2603 /***
2604 * DESCRIPTION:
2605 * Retrieves and saves important text metrics info for the current
2606 * Listview font.
2608 * PARAMETER(S):
2609 * [I] infoPtr : valid pointer to the listview structure
2612 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2614 HDC hdc = GetDC(infoPtr->hwndSelf);
2615 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2616 HFONT hOldFont = SelectObject(hdc, hFont);
2617 TEXTMETRICW tm;
2618 SIZE sz;
2620 if (GetTextMetricsW(hdc, &tm))
2622 infoPtr->ntmHeight = tm.tmHeight;
2623 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2626 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2627 infoPtr->nEllipsisWidth = sz.cx;
2629 SelectObject(hdc, hOldFont);
2630 ReleaseDC(infoPtr->hwndSelf, hdc);
2632 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2635 /***
2636 * DESCRIPTION:
2637 * A compare function for ranges
2639 * PARAMETER(S)
2640 * [I] range1 : pointer to range 1;
2641 * [I] range2 : pointer to range 2;
2642 * [I] flags : flags
2644 * RETURNS:
2645 * > 0 : if range 1 > range 2
2646 * < 0 : if range 2 > range 1
2647 * = 0 : if range intersects range 2
2649 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2651 INT cmp;
2653 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2654 cmp = -1;
2655 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2656 cmp = 1;
2657 else
2658 cmp = 0;
2660 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2662 return cmp;
2665 #if DEBUG_RANGES
2666 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2667 #else
2668 #define ranges_check(ranges, desc) do { } while(0)
2669 #endif
2671 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2673 INT i;
2674 RANGE *prev, *curr;
2676 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2677 assert (ranges);
2678 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2679 ranges_dump(ranges);
2680 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2682 prev = DPA_GetPtr(ranges->hdpa, 0);
2683 assert (prev->lower >= 0 && prev->lower < prev->upper);
2684 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2686 curr = DPA_GetPtr(ranges->hdpa, i);
2687 assert (prev->upper <= curr->lower);
2688 assert (curr->lower < curr->upper);
2689 prev = curr;
2692 TRACE("--- Done checking---\n");
2695 static RANGES ranges_create(int count)
2697 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2698 if (!ranges) return NULL;
2699 ranges->hdpa = DPA_Create(count);
2700 if (ranges->hdpa) return ranges;
2701 Free(ranges);
2702 return NULL;
2705 static void ranges_clear(RANGES ranges)
2707 INT i;
2709 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2710 Free(DPA_GetPtr(ranges->hdpa, i));
2711 DPA_DeleteAllPtrs(ranges->hdpa);
2715 static void ranges_destroy(RANGES ranges)
2717 if (!ranges) return;
2718 ranges_clear(ranges);
2719 DPA_Destroy(ranges->hdpa);
2720 Free(ranges);
2723 static RANGES ranges_clone(RANGES ranges)
2725 RANGES clone;
2726 INT i;
2728 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2730 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2732 RANGE *newrng = Alloc(sizeof(RANGE));
2733 if (!newrng) goto fail;
2734 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2735 DPA_SetPtr(clone->hdpa, i, newrng);
2737 return clone;
2739 fail:
2740 TRACE ("clone failed\n");
2741 ranges_destroy(clone);
2742 return NULL;
2745 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2747 INT i;
2749 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2750 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2752 return ranges;
2755 static void ranges_dump(RANGES ranges)
2757 INT i;
2759 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2760 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2763 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2765 RANGE srchrng = { nItem, nItem + 1 };
2767 TRACE("(nItem=%d)\n", nItem);
2768 ranges_check(ranges, "before contain");
2769 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2772 static INT ranges_itemcount(RANGES ranges)
2774 INT i, count = 0;
2776 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2778 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2779 count += sel->upper - sel->lower;
2782 return count;
2785 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2787 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2788 INT index;
2790 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2791 if (index == -1) return TRUE;
2793 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2795 chkrng = DPA_GetPtr(ranges->hdpa, index);
2796 if (chkrng->lower >= nItem)
2797 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2798 if (chkrng->upper > nItem)
2799 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2801 return TRUE;
2804 static BOOL ranges_add(RANGES ranges, RANGE range)
2806 RANGE srchrgn;
2807 INT index;
2809 TRACE("(%s)\n", debugrange(&range));
2810 ranges_check(ranges, "before add");
2812 /* try find overlapping regions first */
2813 srchrgn.lower = range.lower - 1;
2814 srchrgn.upper = range.upper + 1;
2815 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2817 if (index == -1)
2819 RANGE *newrgn;
2821 TRACE("Adding new range\n");
2823 /* create the brand new range to insert */
2824 newrgn = Alloc(sizeof(RANGE));
2825 if(!newrgn) goto fail;
2826 *newrgn = range;
2828 /* figure out where to insert it */
2829 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2830 TRACE("index=%d\n", index);
2831 if (index == -1) index = 0;
2833 /* and get it over with */
2834 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2836 Free(newrgn);
2837 goto fail;
2840 else
2842 RANGE *chkrgn, *mrgrgn;
2843 INT fromindex, mergeindex;
2845 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2846 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2848 chkrgn->lower = min(range.lower, chkrgn->lower);
2849 chkrgn->upper = max(range.upper, chkrgn->upper);
2851 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2853 /* merge now common ranges */
2854 fromindex = 0;
2855 srchrgn.lower = chkrgn->lower - 1;
2856 srchrgn.upper = chkrgn->upper + 1;
2860 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2861 if (mergeindex == -1) break;
2862 if (mergeindex == index)
2864 fromindex = index + 1;
2865 continue;
2868 TRACE("Merge with index %i\n", mergeindex);
2870 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2871 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2872 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2873 Free(mrgrgn);
2874 DPA_DeletePtr(ranges->hdpa, mergeindex);
2875 if (mergeindex < index) index --;
2876 } while(1);
2879 ranges_check(ranges, "after add");
2880 return TRUE;
2882 fail:
2883 ranges_check(ranges, "failed add");
2884 return FALSE;
2887 static BOOL ranges_del(RANGES ranges, RANGE range)
2889 RANGE *chkrgn;
2890 INT index;
2892 TRACE("(%s)\n", debugrange(&range));
2893 ranges_check(ranges, "before del");
2895 /* we don't use DPAS_SORTED here, since we need *
2896 * to find the first overlapping range */
2897 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2898 while(index != -1)
2900 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2902 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2904 /* case 1: Same range */
2905 if ( (chkrgn->upper == range.upper) &&
2906 (chkrgn->lower == range.lower) )
2908 DPA_DeletePtr(ranges->hdpa, index);
2909 break;
2911 /* case 2: engulf */
2912 else if ( (chkrgn->upper <= range.upper) &&
2913 (chkrgn->lower >= range.lower) )
2915 DPA_DeletePtr(ranges->hdpa, index);
2917 /* case 3: overlap upper */
2918 else if ( (chkrgn->upper <= range.upper) &&
2919 (chkrgn->lower < range.lower) )
2921 chkrgn->upper = range.lower;
2923 /* case 4: overlap lower */
2924 else if ( (chkrgn->upper > range.upper) &&
2925 (chkrgn->lower >= range.lower) )
2927 chkrgn->lower = range.upper;
2928 break;
2930 /* case 5: fully internal */
2931 else
2933 RANGE tmprgn = *chkrgn, *newrgn;
2935 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2936 newrgn->lower = chkrgn->lower;
2937 newrgn->upper = range.lower;
2938 chkrgn->lower = range.upper;
2939 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2941 Free(newrgn);
2942 goto fail;
2944 chkrgn = &tmprgn;
2945 break;
2948 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2951 ranges_check(ranges, "after del");
2952 return TRUE;
2954 fail:
2955 ranges_check(ranges, "failed del");
2956 return FALSE;
2959 /***
2960 * DESCRIPTION:
2961 * Removes all selection ranges
2963 * Parameters(s):
2964 * [I] infoPtr : valid pointer to the listview structure
2965 * [I] toSkip : item range to skip removing the selection
2967 * RETURNS:
2968 * SUCCESS : TRUE
2969 * FAILURE : FALSE
2971 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2973 LVITEMW lvItem;
2974 ITERATOR i;
2975 RANGES clone;
2977 TRACE("()\n");
2979 lvItem.state = 0;
2980 lvItem.stateMask = LVIS_SELECTED;
2982 /* need to clone the DPA because callbacks can change it */
2983 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2984 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2985 while(iterator_next(&i))
2986 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2987 /* note that the iterator destructor will free the cloned range */
2988 iterator_destroy(&i);
2990 return TRUE;
2993 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2995 RANGES toSkip;
2997 if (!(toSkip = ranges_create(1))) return FALSE;
2998 if (nItem != -1) ranges_additem(toSkip, nItem);
2999 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3000 ranges_destroy(toSkip);
3001 return TRUE;
3004 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3006 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3009 /***
3010 * DESCRIPTION:
3011 * Retrieves the number of items that are marked as selected.
3013 * PARAMETER(S):
3014 * [I] infoPtr : valid pointer to the listview structure
3016 * RETURN:
3017 * Number of items selected.
3019 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3021 INT nSelectedCount = 0;
3023 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3025 INT i;
3026 for (i = 0; i < infoPtr->nItemCount; i++)
3028 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3029 nSelectedCount++;
3032 else
3033 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3035 TRACE("nSelectedCount=%d\n", nSelectedCount);
3036 return nSelectedCount;
3039 /***
3040 * DESCRIPTION:
3041 * Manages the item focus.
3043 * PARAMETER(S):
3044 * [I] infoPtr : valid pointer to the listview structure
3045 * [I] nItem : item index
3047 * RETURN:
3048 * TRUE : focused item changed
3049 * FALSE : focused item has NOT changed
3051 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3053 INT oldFocus = infoPtr->nFocusedItem;
3054 LVITEMW lvItem;
3056 if (nItem == infoPtr->nFocusedItem) return FALSE;
3058 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3059 lvItem.stateMask = LVIS_FOCUSED;
3060 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3062 return oldFocus != infoPtr->nFocusedItem;
3065 /* Helper function for LISTVIEW_ShiftIndices *only* */
3066 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3068 if (nShiftItem < nItem) return nShiftItem;
3070 if (nShiftItem > nItem) return nShiftItem + direction;
3072 if (direction > 0) return nShiftItem + direction;
3074 return min(nShiftItem, infoPtr->nItemCount - 1);
3078 * DESCRIPTION:
3079 * Updates the various indices after an item has been inserted or deleted.
3081 * PARAMETER(S):
3082 * [I] infoPtr : valid pointer to the listview structure
3083 * [I] nItem : item index
3084 * [I] direction : Direction of shift, +1 or -1.
3086 * RETURN:
3087 * None
3089 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3091 INT nNewFocus;
3092 BOOL bOldChange;
3094 /* temporarily disable change notification while shifting items */
3095 bOldChange = infoPtr->bDoChangeNotify;
3096 infoPtr->bDoChangeNotify = FALSE;
3098 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3100 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3102 assert(abs(direction) == 1);
3104 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3106 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3107 if (nNewFocus != infoPtr->nFocusedItem)
3108 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3110 /* But we are not supposed to modify nHotItem! */
3112 infoPtr->bDoChangeNotify = bOldChange;
3117 * DESCRIPTION:
3118 * Adds a block of selections.
3120 * PARAMETER(S):
3121 * [I] infoPtr : valid pointer to the listview structure
3122 * [I] nItem : item index
3124 * RETURN:
3125 * Whether the window is still valid.
3127 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3129 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3130 INT nLast = max(infoPtr->nSelectionMark, nItem);
3131 HWND hwndSelf = infoPtr->hwndSelf;
3132 NMLVODSTATECHANGE nmlv;
3133 LVITEMW item;
3134 BOOL bOldChange;
3135 INT i;
3137 /* Temporarily disable change notification
3138 * If the control is LVS_OWNERDATA, we need to send
3139 * only one LVN_ODSTATECHANGED notification.
3140 * See MSDN documentation for LVN_ITEMCHANGED.
3142 bOldChange = infoPtr->bDoChangeNotify;
3143 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3145 if (nFirst == -1) nFirst = nItem;
3147 item.state = LVIS_SELECTED;
3148 item.stateMask = LVIS_SELECTED;
3150 for (i = nFirst; i <= nLast; i++)
3151 LISTVIEW_SetItemState(infoPtr,i,&item);
3153 ZeroMemory(&nmlv, sizeof(nmlv));
3154 nmlv.iFrom = nFirst;
3155 nmlv.iTo = nLast;
3156 nmlv.uNewState = 0;
3157 nmlv.uOldState = item.state;
3159 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3160 if (!IsWindow(hwndSelf))
3161 return FALSE;
3162 infoPtr->bDoChangeNotify = bOldChange;
3163 return TRUE;
3167 /***
3168 * DESCRIPTION:
3169 * Sets a single group selection.
3171 * PARAMETER(S):
3172 * [I] infoPtr : valid pointer to the listview structure
3173 * [I] nItem : item index
3175 * RETURN:
3176 * None
3178 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3180 RANGES selection;
3181 LVITEMW item;
3182 ITERATOR i;
3183 BOOL bOldChange;
3185 if (!(selection = ranges_create(100))) return;
3187 item.state = LVIS_SELECTED;
3188 item.stateMask = LVIS_SELECTED;
3190 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3192 if (infoPtr->nSelectionMark == -1)
3194 infoPtr->nSelectionMark = nItem;
3195 ranges_additem(selection, nItem);
3197 else
3199 RANGE sel;
3201 sel.lower = min(infoPtr->nSelectionMark, nItem);
3202 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3203 ranges_add(selection, sel);
3206 else
3208 RECT rcItem, rcSel, rcSelMark;
3209 POINT ptItem;
3211 rcItem.left = LVIR_BOUNDS;
3212 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3213 rcSelMark.left = LVIR_BOUNDS;
3214 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3215 UnionRect(&rcSel, &rcItem, &rcSelMark);
3216 iterator_frameditems(&i, infoPtr, &rcSel);
3217 while(iterator_next(&i))
3219 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3220 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3222 iterator_destroy(&i);
3225 /* disable per item notifications on LVS_OWNERDATA style
3226 FIXME: single LVN_ODSTATECHANGED should be used */
3227 bOldChange = infoPtr->bDoChangeNotify;
3228 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3230 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3233 iterator_rangesitems(&i, selection);
3234 while(iterator_next(&i))
3235 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3236 /* this will also destroy the selection */
3237 iterator_destroy(&i);
3239 infoPtr->bDoChangeNotify = bOldChange;
3241 LISTVIEW_SetItemFocus(infoPtr, nItem);
3244 /***
3245 * DESCRIPTION:
3246 * Sets a single selection.
3248 * PARAMETER(S):
3249 * [I] infoPtr : valid pointer to the listview structure
3250 * [I] nItem : item index
3252 * RETURN:
3253 * None
3255 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3257 LVITEMW lvItem;
3259 TRACE("nItem=%d\n", nItem);
3261 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3263 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3264 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3265 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3267 infoPtr->nSelectionMark = nItem;
3270 /***
3271 * DESCRIPTION:
3272 * Set selection(s) with keyboard.
3274 * PARAMETER(S):
3275 * [I] infoPtr : valid pointer to the listview structure
3276 * [I] nItem : item index
3277 * [I] space : VK_SPACE code sent
3279 * RETURN:
3280 * SUCCESS : TRUE (needs to be repainted)
3281 * FAILURE : FALSE (nothing has changed)
3283 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3285 /* FIXME: pass in the state */
3286 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3287 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3288 BOOL bResult = FALSE;
3290 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3291 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3293 bResult = TRUE;
3295 if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3296 LISTVIEW_SetSelection(infoPtr, nItem);
3297 else
3299 if (wShift)
3300 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3301 else if (wCtrl)
3303 LVITEMW lvItem;
3304 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3305 lvItem.stateMask = LVIS_SELECTED;
3306 if (space)
3308 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3309 if (lvItem.state & LVIS_SELECTED)
3310 infoPtr->nSelectionMark = nItem;
3312 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3315 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3318 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3319 return bResult;
3322 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3324 LVHITTESTINFO lvHitTestInfo;
3326 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3327 lvHitTestInfo.pt.x = pt.x;
3328 lvHitTestInfo.pt.y = pt.y;
3330 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3332 lpLVItem->mask = LVIF_PARAM;
3333 lpLVItem->iItem = lvHitTestInfo.iItem;
3334 lpLVItem->iSubItem = 0;
3336 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3339 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3341 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3342 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3343 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3346 /***
3347 * DESCRIPTION:
3348 * Called when the mouse is being actively tracked and has hovered for a specified
3349 * amount of time
3351 * PARAMETER(S):
3352 * [I] infoPtr : valid pointer to the listview structure
3353 * [I] fwKeys : key indicator
3354 * [I] x,y : mouse position
3356 * RETURN:
3357 * 0 if the message was processed, non-zero if there was an error
3359 * INFO:
3360 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3361 * over the item for a certain period of time.
3364 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3366 if (LISTVIEW_isHotTracking(infoPtr))
3368 LVITEMW item;
3369 POINT pt;
3371 pt.x = x;
3372 pt.y = y;
3374 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3375 LISTVIEW_SetSelection(infoPtr, item.iItem);
3378 return 0;
3381 /***
3382 * DESCRIPTION:
3383 * Called whenever WM_MOUSEMOVE is received.
3385 * PARAMETER(S):
3386 * [I] infoPtr : valid pointer to the listview structure
3387 * [I] fwKeys : key indicator
3388 * [I] x,y : mouse position
3390 * RETURN:
3391 * 0 if the message is processed, non-zero if there was an error
3393 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3395 TRACKMOUSEEVENT trackinfo;
3397 if (!(fwKeys & MK_LBUTTON))
3398 infoPtr->bLButtonDown = FALSE;
3400 if (infoPtr->bLButtonDown)
3402 POINT tmp;
3403 RECT rect;
3404 LVHITTESTINFO lvHitTestInfo;
3405 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3406 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3408 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3409 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3410 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3411 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3413 tmp.x = x;
3414 tmp.y = y;
3416 lvHitTestInfo.pt = tmp;
3417 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3419 /* reset item marker */
3420 if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
3421 infoPtr->nLButtonDownItem = -1;
3423 if (!PtInRect(&rect, tmp))
3425 /* this path covers the following:
3426 1. WM_LBUTTONDOWN over selected item (sets focus on it)
3427 2. change focus with keys
3428 3. move mouse over item from step 1 selects it and moves focus on it */
3429 if (infoPtr->nLButtonDownItem != -1 &&
3430 !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
3432 LVITEMW lvItem;
3434 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3435 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3437 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
3438 infoPtr->nLButtonDownItem = -1;
3441 if (!infoPtr->bDragging)
3443 NMLISTVIEW nmlv;
3445 lvHitTestInfo.pt = infoPtr->ptClickPos;
3446 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3448 ZeroMemory(&nmlv, sizeof(nmlv));
3449 nmlv.iItem = lvHitTestInfo.iItem;
3450 nmlv.ptAction = infoPtr->ptClickPos;
3452 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3453 infoPtr->bDragging = TRUE;
3456 return 0;
3460 /* see if we are supposed to be tracking mouse hovering */
3461 if (LISTVIEW_isHotTracking(infoPtr)) {
3462 /* fill in the trackinfo struct */
3463 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3464 trackinfo.dwFlags = TME_QUERY;
3465 trackinfo.hwndTrack = infoPtr->hwndSelf;
3466 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3468 /* see if we are already tracking this hwnd */
3469 _TrackMouseEvent(&trackinfo);
3471 if(!(trackinfo.dwFlags & TME_HOVER)) {
3472 trackinfo.dwFlags = TME_HOVER;
3474 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3475 _TrackMouseEvent(&trackinfo);
3479 return 0;
3483 /***
3484 * Tests whether the item is assignable to a list with style lStyle
3486 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3488 if ( (lpLVItem->mask & LVIF_TEXT) &&
3489 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3490 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3492 return TRUE;
3496 /***
3497 * DESCRIPTION:
3498 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3500 * PARAMETER(S):
3501 * [I] infoPtr : valid pointer to the listview structure
3502 * [I] lpLVItem : valid pointer to new item attributes
3503 * [I] isNew : the item being set is being inserted
3504 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3505 * [O] bChanged : will be set to TRUE if the item really changed
3507 * RETURN:
3508 * SUCCESS : TRUE
3509 * FAILURE : FALSE
3511 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3513 ITEM_INFO *lpItem;
3514 NMLISTVIEW nmlv;
3515 UINT uChanged = 0;
3516 LVITEMW item;
3517 /* stateMask is ignored for LVM_INSERTITEM */
3518 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
3520 TRACE("()\n");
3522 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3524 if (lpLVItem->mask == 0) return TRUE;
3526 if (infoPtr->dwStyle & LVS_OWNERDATA)
3528 /* a virtual listview only stores selection and focus */
3529 if (lpLVItem->mask & ~LVIF_STATE)
3530 return FALSE;
3531 lpItem = NULL;
3533 else
3535 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3536 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3537 assert (lpItem);
3540 /* we need to get the lParam and state of the item */
3541 item.iItem = lpLVItem->iItem;
3542 item.iSubItem = lpLVItem->iSubItem;
3543 item.mask = LVIF_STATE | LVIF_PARAM;
3544 item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
3546 item.state = 0;
3547 item.lParam = 0;
3548 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3550 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3551 /* determine what fields will change */
3552 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
3553 uChanged |= LVIF_STATE;
3555 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3556 uChanged |= LVIF_IMAGE;
3558 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3559 uChanged |= LVIF_PARAM;
3561 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3562 uChanged |= LVIF_INDENT;
3564 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3565 uChanged |= LVIF_TEXT;
3567 TRACE("uChanged=0x%x\n", uChanged);
3568 if (!uChanged) return TRUE;
3569 *bChanged = TRUE;
3571 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3572 nmlv.iItem = lpLVItem->iItem;
3573 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
3574 nmlv.uOldState = item.state;
3575 nmlv.uChanged = uChanged;
3576 nmlv.lParam = item.lParam;
3578 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3579 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3580 /* are enabled */
3581 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3583 HWND hwndSelf = infoPtr->hwndSelf;
3585 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3586 return FALSE;
3587 if (!IsWindow(hwndSelf))
3588 return FALSE;
3591 /* copy information */
3592 if (lpLVItem->mask & LVIF_TEXT)
3593 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3595 if (lpLVItem->mask & LVIF_IMAGE)
3596 lpItem->hdr.iImage = lpLVItem->iImage;
3598 if (lpLVItem->mask & LVIF_PARAM)
3599 lpItem->lParam = lpLVItem->lParam;
3601 if (lpLVItem->mask & LVIF_INDENT)
3602 lpItem->iIndent = lpLVItem->iIndent;
3604 if (uChanged & LVIF_STATE)
3606 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
3608 lpItem->state &= ~stateMask;
3609 lpItem->state |= (lpLVItem->state & stateMask);
3611 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3613 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3614 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3616 else if (stateMask & LVIS_SELECTED)
3618 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3620 /* if we are asked to change focus, and we manage it, do it */
3621 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3623 if (lpLVItem->state & LVIS_FOCUSED)
3625 if (infoPtr->nFocusedItem != -1)
3627 /* remove current focus */
3628 item.mask = LVIF_STATE;
3629 item.state = 0;
3630 item.stateMask = LVIS_FOCUSED;
3632 /* recurse with redrawing an item */
3633 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
3636 infoPtr->nFocusedItem = lpLVItem->iItem;
3637 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST);
3639 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3641 infoPtr->nFocusedItem = -1;
3646 /* if we're inserting the item, we're done */
3647 if (isNew) return TRUE;
3649 /* send LVN_ITEMCHANGED notification */
3650 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3651 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3653 return TRUE;
3656 /***
3657 * DESCRIPTION:
3658 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3660 * PARAMETER(S):
3661 * [I] infoPtr : valid pointer to the listview structure
3662 * [I] lpLVItem : valid pointer to new subitem attributes
3663 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3664 * [O] bChanged : will be set to TRUE if the item really changed
3666 * RETURN:
3667 * SUCCESS : TRUE
3668 * FAILURE : FALSE
3670 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3672 HDPA hdpaSubItems;
3673 SUBITEM_INFO *lpSubItem;
3675 /* we do not support subitems for virtual listviews */
3676 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3678 /* set subitem only if column is present */
3679 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3681 /* First do some sanity checks */
3682 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3683 particularly useful. We currently do not actually do anything with
3684 the flag on subitems.
3686 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3687 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3689 /* get the subitem structure, and create it if not there */
3690 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3691 assert (hdpaSubItems);
3693 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3694 if (!lpSubItem)
3696 SUBITEM_INFO *tmpSubItem;
3697 INT i;
3699 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3700 if (!lpSubItem) return FALSE;
3701 /* we could binary search here, if need be...*/
3702 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3704 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3705 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3707 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3709 Free(lpSubItem);
3710 return FALSE;
3712 lpSubItem->iSubItem = lpLVItem->iSubItem;
3713 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3714 *bChanged = TRUE;
3717 if (lpLVItem->mask & LVIF_IMAGE)
3718 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3720 lpSubItem->hdr.iImage = lpLVItem->iImage;
3721 *bChanged = TRUE;
3724 if (lpLVItem->mask & LVIF_TEXT)
3725 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3727 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3728 *bChanged = TRUE;
3731 return TRUE;
3734 /***
3735 * DESCRIPTION:
3736 * Sets item attributes.
3738 * PARAMETER(S):
3739 * [I] infoPtr : valid pointer to the listview structure
3740 * [I] lpLVItem : new item attributes
3741 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3743 * RETURN:
3744 * SUCCESS : TRUE
3745 * FAILURE : FALSE
3747 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3749 HWND hwndSelf = infoPtr->hwndSelf;
3750 LPWSTR pszText = NULL;
3751 BOOL bResult, bChanged = FALSE;
3753 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3755 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3756 return FALSE;
3758 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3759 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3761 pszText = lpLVItem->pszText;
3762 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3765 /* actually set the fields */
3766 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3768 if (lpLVItem->iSubItem)
3769 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3770 else
3771 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3772 if (!IsWindow(hwndSelf))
3773 return FALSE;
3775 /* redraw item, if necessary */
3776 if (bChanged && !infoPtr->bIsDrawing)
3778 /* this little optimization eliminates some nasty flicker */
3779 if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3780 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3781 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3782 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3783 else
3784 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3786 /* restore text */
3787 if (pszText)
3789 textfreeT(lpLVItem->pszText, isW);
3790 lpLVItem->pszText = pszText;
3793 return bResult;
3796 /***
3797 * DESCRIPTION:
3798 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3800 * PARAMETER(S):
3801 * [I] infoPtr : valid pointer to the listview structure
3803 * RETURN:
3804 * item index
3806 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3808 INT nItem = 0;
3809 SCROLLINFO scrollInfo;
3811 scrollInfo.cbSize = sizeof(SCROLLINFO);
3812 scrollInfo.fMask = SIF_POS;
3814 if (infoPtr->uView == LV_VIEW_LIST)
3816 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3817 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3819 else if (infoPtr->uView == LV_VIEW_DETAILS)
3821 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3822 nItem = scrollInfo.nPos;
3824 else
3826 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3827 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3830 TRACE("nItem=%d\n", nItem);
3832 return nItem;
3836 /***
3837 * DESCRIPTION:
3838 * Erases the background of the given rectangle
3840 * PARAMETER(S):
3841 * [I] infoPtr : valid pointer to the listview structure
3842 * [I] hdc : device context handle
3843 * [I] lprcBox : clipping rectangle
3845 * RETURN:
3846 * Success: TRUE
3847 * Failure: FALSE
3849 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3851 if (!infoPtr->hBkBrush) return FALSE;
3853 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3855 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3858 /***
3859 * DESCRIPTION:
3860 * Draws an item.
3862 * PARAMETER(S):
3863 * [I] infoPtr : valid pointer to the listview structure
3864 * [I] hdc : device context handle
3865 * [I] nItem : item index
3866 * [I] nSubItem : subitem index
3867 * [I] pos : item position in client coordinates
3868 * [I] cdmode : custom draw mode
3870 * RETURN:
3871 * Success: TRUE
3872 * Failure: FALSE
3874 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3876 UINT uFormat;
3877 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3878 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3879 DWORD cdsubitemmode = CDRF_DODEFAULT;
3880 LPRECT lprcFocus;
3881 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3882 NMLVCUSTOMDRAW nmlvcd;
3883 HIMAGELIST himl;
3884 LVITEMW lvItem;
3885 HFONT hOldFont;
3887 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3889 /* get information needed for drawing the item */
3890 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3891 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3892 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
3893 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3894 lvItem.iItem = nItem;
3895 lvItem.iSubItem = nSubItem;
3896 lvItem.state = 0;
3897 lvItem.lParam = 0;
3898 lvItem.cchTextMax = DISP_TEXT_SIZE;
3899 lvItem.pszText = szDispText;
3900 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3901 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3902 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3903 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3904 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3906 /* now check if we need to update the focus rectangle */
3907 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3909 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3910 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3911 OffsetRect(&rcBox, pos.x, pos.y);
3912 OffsetRect(&rcSelect, pos.x, pos.y);
3913 OffsetRect(&rcIcon, pos.x, pos.y);
3914 OffsetRect(&rcStateIcon, pos.x, pos.y);
3915 OffsetRect(&rcLabel, pos.x, pos.y);
3916 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3917 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3918 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3920 /* fill in the custom draw structure */
3921 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3923 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3924 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3925 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3926 if (cdmode & CDRF_NOTIFYITEMDRAW)
3927 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3928 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3929 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3930 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3931 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3933 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3934 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3936 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3937 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3938 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3939 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3941 /* in full row select, subitems, will just use main item's colors */
3942 if (nSubItem && infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3943 nmlvcd.clrTextBk = CLR_NONE;
3945 /* state icons */
3946 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3948 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3949 if (uStateImage)
3951 TRACE("uStateImage=%d\n", uStateImage);
3952 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3953 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3957 /* small icons */
3958 himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3959 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3961 TRACE("iImage=%d\n", lvItem.iImage);
3962 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3963 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3964 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3967 /* Don't bother painting item being edited */
3968 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3970 /* FIXME: temporary hack */
3971 rcSelect.left = rcLabel.left;
3973 /* draw the selection background, if we're drawing the main item */
3974 if (nSubItem == 0)
3976 /* in icon mode, the label rect is really what we want to draw the
3977 * background for */
3978 if (infoPtr->uView == LV_VIEW_ICON)
3979 rcSelect = rcLabel;
3981 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3982 rcSelect.right = rcBox.right;
3984 if (nmlvcd.clrTextBk != CLR_NONE)
3985 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3986 /* store new focus rectangle */
3987 if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect;
3990 /* figure out the text drawing flags */
3991 uFormat = (infoPtr->uView == LV_VIEW_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3992 if (infoPtr->uView == LV_VIEW_ICON)
3993 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3994 else if (nSubItem)
3996 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3998 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3999 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
4000 default: uFormat |= DT_LEFT;
4003 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
4005 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
4006 else rcLabel.left += LABEL_HOR_PADDING;
4008 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4010 /* for GRIDLINES reduce the bottom so the text formats correctly */
4011 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4012 rcLabel.bottom--;
4014 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
4016 postpaint:
4017 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4018 notify_postpaint(infoPtr, &nmlvcd);
4019 if (cdsubitemmode & CDRF_NEWFONT)
4020 SelectObject(hdc, hOldFont);
4021 return TRUE;
4024 /***
4025 * DESCRIPTION:
4026 * Draws listview items when in owner draw mode.
4028 * PARAMETER(S):
4029 * [I] infoPtr : valid pointer to the listview structure
4030 * [I] hdc : device context handle
4032 * RETURN:
4033 * None
4035 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4037 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4038 DWORD cditemmode = CDRF_DODEFAULT;
4039 NMLVCUSTOMDRAW nmlvcd;
4040 POINT Origin, Position;
4041 DRAWITEMSTRUCT dis;
4042 LVITEMW item;
4044 TRACE("()\n");
4046 ZeroMemory(&dis, sizeof(dis));
4048 /* Get scroll info once before loop */
4049 LISTVIEW_GetOrigin(infoPtr, &Origin);
4051 /* iterate through the invalidated rows */
4052 while(iterator_next(i))
4054 item.iItem = i->nItem;
4055 item.iSubItem = 0;
4056 item.mask = LVIF_PARAM | LVIF_STATE;
4057 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4058 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4060 dis.CtlType = ODT_LISTVIEW;
4061 dis.CtlID = uID;
4062 dis.itemID = item.iItem;
4063 dis.itemAction = ODA_DRAWENTIRE;
4064 dis.itemState = 0;
4065 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4066 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4067 dis.hwndItem = infoPtr->hwndSelf;
4068 dis.hDC = hdc;
4069 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4070 dis.rcItem.left = Position.x + Origin.x;
4071 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4072 dis.rcItem.top = Position.y + Origin.y;
4073 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4074 dis.itemData = item.lParam;
4076 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4079 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4080 * structure for the rest. of the paint cycle
4082 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4083 if (cdmode & CDRF_NOTIFYITEMDRAW)
4084 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4086 if (!(cditemmode & CDRF_SKIPDEFAULT))
4088 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4089 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4092 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4093 notify_postpaint(infoPtr, &nmlvcd);
4097 /***
4098 * DESCRIPTION:
4099 * Draws listview items when in report display mode.
4101 * PARAMETER(S):
4102 * [I] infoPtr : valid pointer to the listview structure
4103 * [I] hdc : device context handle
4104 * [I] cdmode : custom draw mode
4106 * RETURN:
4107 * None
4109 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4111 INT rgntype;
4112 RECT rcClip, rcItem;
4113 POINT Origin, Position;
4114 RANGES colRanges;
4115 INT col, index;
4116 ITERATOR j;
4118 TRACE("()\n");
4120 /* figure out what to draw */
4121 rgntype = GetClipBox(hdc, &rcClip);
4122 if (rgntype == NULLREGION) return;
4124 /* Get scroll info once before loop */
4125 LISTVIEW_GetOrigin(infoPtr, &Origin);
4127 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4129 /* narrow down the columns we need to paint */
4130 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4132 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4134 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4135 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4136 ranges_additem(colRanges, index);
4138 iterator_rangesitems(&j, colRanges);
4140 /* in full row select, we _have_ to draw the main item */
4141 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4142 j.nSpecial = 0;
4144 /* iterate through the invalidated rows */
4145 while(iterator_next(i))
4147 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4148 Position.x += Origin.x;
4149 Position.y += Origin.y;
4151 /* iterate through the invalidated columns */
4152 while(iterator_next(&j))
4154 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4156 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4157 rcItem.top = 0;
4158 rcItem.bottom = infoPtr->nItemHeight;
4159 OffsetRect(&rcItem, Position.x, Position.y);
4160 if (!RectVisible(hdc, &rcItem)) continue;
4163 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4166 iterator_destroy(&j);
4169 /***
4170 * DESCRIPTION:
4171 * Draws the gridlines if necessary when in report display mode.
4173 * PARAMETER(S):
4174 * [I] infoPtr : valid pointer to the listview structure
4175 * [I] hdc : device context handle
4177 * RETURN:
4178 * None
4180 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4182 INT rgntype;
4183 INT y, itemheight;
4184 INT col, index;
4185 HPEN hPen, hOldPen;
4186 RECT rcClip, rcItem = {0};
4187 POINT Origin;
4188 RANGES colRanges;
4189 ITERATOR j;
4190 BOOL rmost = FALSE;
4192 TRACE("()\n");
4194 /* figure out what to draw */
4195 rgntype = GetClipBox(hdc, &rcClip);
4196 if (rgntype == NULLREGION) return;
4198 /* Get scroll info once before loop */
4199 LISTVIEW_GetOrigin(infoPtr, &Origin);
4201 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4203 /* narrow down the columns we need to paint */
4204 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4206 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4208 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4209 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4210 ranges_additem(colRanges, index);
4213 /* is right most vertical line visible? */
4214 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4216 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4217 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4218 rmost = (rcItem.right + Origin.x < rcClip.right);
4221 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4223 hOldPen = SelectObject ( hdc, hPen );
4225 /* draw the vertical lines for the columns */
4226 iterator_rangesitems(&j, colRanges);
4227 while(iterator_next(&j))
4229 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4230 if (rcItem.left == 0) continue; /* skip leftmost column */
4231 rcItem.left += Origin.x;
4232 rcItem.right += Origin.x;
4233 rcItem.top = infoPtr->rcList.top;
4234 rcItem.bottom = infoPtr->rcList.bottom;
4235 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4236 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4237 LineTo (hdc, rcItem.left, rcItem.bottom);
4239 iterator_destroy(&j);
4240 /* draw rightmost grid line if visible */
4241 if (rmost)
4243 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4244 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4245 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4247 rcItem.right += Origin.x;
4249 MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL);
4250 LineTo (hdc, rcItem.right, infoPtr->rcList.bottom);
4253 /* draw the horizontial lines for the rows */
4254 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4255 rcItem.left = infoPtr->rcList.left;
4256 rcItem.right = infoPtr->rcList.right;
4257 rcItem.bottom = rcItem.top = Origin.y - 1;
4258 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4259 LineTo(hdc, rcItem.right, rcItem.top);
4260 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4262 rcItem.bottom = rcItem.top = y;
4263 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4264 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4265 LineTo (hdc, rcItem.right, rcItem.top);
4268 SelectObject( hdc, hOldPen );
4269 DeleteObject( hPen );
4273 /***
4274 * DESCRIPTION:
4275 * Draws listview items when in list display mode.
4277 * PARAMETER(S):
4278 * [I] infoPtr : valid pointer to the listview structure
4279 * [I] hdc : device context handle
4280 * [I] cdmode : custom draw mode
4282 * RETURN:
4283 * None
4285 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4287 POINT Origin, Position;
4289 /* Get scroll info once before loop */
4290 LISTVIEW_GetOrigin(infoPtr, &Origin);
4292 while(iterator_prev(i))
4294 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4295 Position.x += Origin.x;
4296 Position.y += Origin.y;
4298 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4303 /***
4304 * DESCRIPTION:
4305 * Draws listview items.
4307 * PARAMETER(S):
4308 * [I] infoPtr : valid pointer to the listview structure
4309 * [I] hdc : device context handle
4310 * [I] prcErase : rect to be erased before refresh (may be NULL)
4312 * RETURN:
4313 * NoneX
4315 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4317 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4318 NMLVCUSTOMDRAW nmlvcd;
4319 HFONT hOldFont = 0;
4320 DWORD cdmode;
4321 INT oldBkMode = 0;
4322 RECT rcClient;
4323 ITERATOR i;
4324 HDC hdcOrig = hdc;
4325 HBITMAP hbmp = NULL;
4326 RANGE range;
4328 LISTVIEW_DUMP(infoPtr);
4330 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4331 TRACE("double buffering\n");
4333 hdc = CreateCompatibleDC(hdcOrig);
4334 if (!hdc) {
4335 ERR("Failed to create DC for backbuffer\n");
4336 return;
4338 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4339 infoPtr->rcList.bottom);
4340 if (!hbmp) {
4341 ERR("Failed to create bitmap for backbuffer\n");
4342 DeleteDC(hdc);
4343 return;
4346 SelectObject(hdc, hbmp);
4347 SelectObject(hdc, infoPtr->hFont);
4348 } else {
4349 /* Save dc values we're gonna trash while drawing
4350 * FIXME: Should be done in LISTVIEW_DrawItem() */
4351 hOldFont = SelectObject(hdc, infoPtr->hFont);
4352 oldBkMode = GetBkMode(hdc);
4353 oldBkColor = GetBkColor(hdc);
4354 oldTextColor = GetTextColor(hdc);
4357 infoPtr->bIsDrawing = TRUE;
4359 if (prcErase) {
4360 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4361 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4362 /* If no erasing was done (usually because RedrawWindow was called
4363 * with RDW_INVALIDATE only) we need to copy the old contents into
4364 * the backbuffer before continuing. */
4365 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4366 infoPtr->rcList.right - infoPtr->rcList.left,
4367 infoPtr->rcList.bottom - infoPtr->rcList.top,
4368 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4371 /* FIXME: Shouldn't need to do this */
4372 oldClrTextBk = infoPtr->clrTextBk;
4373 oldClrText = infoPtr->clrText;
4375 infoPtr->cditemmode = CDRF_DODEFAULT;
4377 GetClientRect(infoPtr->hwndSelf, &rcClient);
4378 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4379 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4380 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4381 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4383 /* Use these colors to draw the items */
4384 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4385 infoPtr->clrText = nmlvcd.clrText;
4387 /* nothing to draw */
4388 if(infoPtr->nItemCount == 0) goto enddraw;
4390 /* figure out what we need to draw */
4391 iterator_visibleitems(&i, infoPtr, hdc);
4392 range = iterator_range(&i);
4394 /* send cache hint notification */
4395 if (infoPtr->dwStyle & LVS_OWNERDATA)
4397 NMLVCACHEHINT nmlv;
4399 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4400 nmlv.iFrom = range.lower;
4401 nmlv.iTo = range.upper - 1;
4402 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4405 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
4406 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4407 else
4409 if (infoPtr->uView == LV_VIEW_DETAILS)
4410 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4411 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
4412 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4414 /* if we have a focus rect and it's visible, draw it */
4415 if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
4416 (range.upper - 1) >= infoPtr->nFocusedItem)
4417 LISTVIEW_DrawFocusRect(infoPtr, hdc);
4419 iterator_destroy(&i);
4421 enddraw:
4422 /* For LVS_EX_GRIDLINES go and draw lines */
4423 /* This includes the case where there were *no* items */
4424 if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4425 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4427 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4428 notify_postpaint(infoPtr, &nmlvcd);
4430 infoPtr->clrTextBk = oldClrTextBk;
4431 infoPtr->clrText = oldClrText;
4433 if(hbmp) {
4434 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4435 infoPtr->rcList.right - infoPtr->rcList.left,
4436 infoPtr->rcList.bottom - infoPtr->rcList.top,
4437 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4439 DeleteObject(hbmp);
4440 DeleteDC(hdc);
4441 } else {
4442 SelectObject(hdc, hOldFont);
4443 SetBkMode(hdc, oldBkMode);
4444 SetBkColor(hdc, oldBkColor);
4445 SetTextColor(hdc, oldTextColor);
4448 infoPtr->bIsDrawing = FALSE;
4452 /***
4453 * DESCRIPTION:
4454 * Calculates the approximate width and height of a given number of items.
4456 * PARAMETER(S):
4457 * [I] infoPtr : valid pointer to the listview structure
4458 * [I] nItemCount : number of items
4459 * [I] wWidth : width
4460 * [I] wHeight : height
4462 * RETURN:
4463 * Returns a DWORD. The width in the low word and the height in high word.
4465 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4466 WORD wWidth, WORD wHeight)
4468 INT nItemCountPerColumn = 1;
4469 INT nColumnCount = 0;
4470 DWORD dwViewRect = 0;
4472 if (nItemCount == -1)
4473 nItemCount = infoPtr->nItemCount;
4475 if (infoPtr->uView == LV_VIEW_LIST)
4477 if (wHeight == 0xFFFF)
4479 /* use current height */
4480 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4483 if (wHeight < infoPtr->nItemHeight)
4484 wHeight = infoPtr->nItemHeight;
4486 if (nItemCount > 0)
4488 if (infoPtr->nItemHeight > 0)
4490 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4491 if (nItemCountPerColumn == 0)
4492 nItemCountPerColumn = 1;
4494 if (nItemCount % nItemCountPerColumn != 0)
4495 nColumnCount = nItemCount / nItemCountPerColumn;
4496 else
4497 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4501 /* Microsoft padding magic */
4502 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4503 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4505 dwViewRect = MAKELONG(wWidth, wHeight);
4507 else if (infoPtr->uView == LV_VIEW_DETAILS)
4509 RECT rcBox;
4511 if (infoPtr->nItemCount > 0)
4513 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4514 wWidth = rcBox.right - rcBox.left;
4515 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4517 else
4519 /* use current height and width */
4520 if (wHeight == 0xffff)
4521 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4522 if (wWidth == 0xffff)
4523 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4526 dwViewRect = MAKELONG(wWidth, wHeight);
4528 else if (infoPtr->uView == LV_VIEW_SMALLICON)
4529 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
4530 else if (infoPtr->uView == LV_VIEW_ICON)
4531 FIXME("uView == LV_VIEW_ICON: not implemented\n");
4533 return dwViewRect;
4537 /***
4538 * DESCRIPTION:
4539 * Create a drag image list for the specified item.
4541 * PARAMETER(S):
4542 * [I] infoPtr : valid pointer to the listview structure
4543 * [I] iItem : index of item
4544 * [O] lppt : Upper-left corner of the image
4546 * RETURN:
4547 * Returns a handle to the image list if successful, NULL otherwise.
4549 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4551 RECT rcItem;
4552 SIZE size;
4553 POINT pos;
4554 HDC hdc, hdcOrig;
4555 HBITMAP hbmp, hOldbmp;
4556 HIMAGELIST dragList = 0;
4557 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4559 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4560 return 0;
4562 rcItem.left = LVIR_BOUNDS;
4563 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4564 return 0;
4566 lppt->x = rcItem.left;
4567 lppt->y = rcItem.top;
4569 size.cx = rcItem.right - rcItem.left;
4570 size.cy = rcItem.bottom - rcItem.top;
4572 hdcOrig = GetDC(infoPtr->hwndSelf);
4573 hdc = CreateCompatibleDC(hdcOrig);
4574 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4575 hOldbmp = SelectObject(hdc, hbmp);
4577 rcItem.left = rcItem.top = 0;
4578 rcItem.right = size.cx;
4579 rcItem.bottom = size.cy;
4580 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4582 pos.x = pos.y = 0;
4583 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4585 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4586 SelectObject(hdc, hOldbmp);
4587 ImageList_Add(dragList, hbmp, 0);
4589 else
4590 SelectObject(hdc, hOldbmp);
4592 DeleteObject(hbmp);
4593 DeleteDC(hdc);
4594 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4596 TRACE("ret=%p\n", dragList);
4598 return dragList;
4602 /***
4603 * DESCRIPTION:
4604 * Removes all listview items and subitems.
4606 * PARAMETER(S):
4607 * [I] infoPtr : valid pointer to the listview structure
4609 * RETURN:
4610 * SUCCESS : TRUE
4611 * FAILURE : FALSE
4613 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4615 NMLISTVIEW nmlv;
4616 HDPA hdpaSubItems = NULL;
4617 BOOL bSuppress;
4618 ITEMHDR *hdrItem;
4619 INT i, j;
4621 TRACE("()\n");
4623 /* we do it directly, to avoid notifications */
4624 ranges_clear(infoPtr->selectionRanges);
4625 infoPtr->nSelectionMark = -1;
4626 infoPtr->nFocusedItem = -1;
4627 SetRectEmpty(&infoPtr->rcFocus);
4628 /* But we are supposed to leave nHotItem as is! */
4631 /* send LVN_DELETEALLITEMS notification */
4632 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4633 nmlv.iItem = -1;
4634 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4636 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4638 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4640 /* send LVN_DELETEITEM notification, if not suppressed
4641 and if it is not a virtual listview */
4642 if (!bSuppress) notify_deleteitem(infoPtr, i);
4643 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4644 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4646 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4647 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4648 Free(hdrItem);
4650 DPA_Destroy(hdpaSubItems);
4651 DPA_DeletePtr(infoPtr->hdpaItems, i);
4653 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4654 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4655 infoPtr->nItemCount --;
4658 if (!destroy)
4660 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4661 LISTVIEW_UpdateScroll(infoPtr);
4663 LISTVIEW_InvalidateList(infoPtr);
4665 return TRUE;
4668 /***
4669 * DESCRIPTION:
4670 * Scrolls, and updates the columns, when a column is changing width.
4672 * PARAMETER(S):
4673 * [I] infoPtr : valid pointer to the listview structure
4674 * [I] nColumn : column to scroll
4675 * [I] dx : amount of scroll, in pixels
4677 * RETURN:
4678 * None.
4680 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4682 COLUMN_INFO *lpColumnInfo;
4683 RECT rcOld, rcCol;
4684 POINT ptOrigin;
4685 INT nCol;
4687 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4688 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4689 rcCol = lpColumnInfo->rcHeader;
4690 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4691 rcCol.left = rcCol.right;
4693 /* adjust the other columns */
4694 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4696 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4697 lpColumnInfo->rcHeader.left += dx;
4698 lpColumnInfo->rcHeader.right += dx;
4701 /* do not update screen if not in report mode */
4702 if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
4704 /* Need to reset the item width when inserting a new column */
4705 infoPtr->nItemWidth += dx;
4707 LISTVIEW_UpdateScroll(infoPtr);
4708 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4710 /* scroll to cover the deleted column, and invalidate for redraw */
4711 rcOld = infoPtr->rcList;
4712 rcOld.left = ptOrigin.x + rcCol.left + dx;
4713 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4716 /***
4717 * DESCRIPTION:
4718 * Removes a column from the listview control.
4720 * PARAMETER(S):
4721 * [I] infoPtr : valid pointer to the listview structure
4722 * [I] nColumn : column index
4724 * RETURN:
4725 * SUCCESS : TRUE
4726 * FAILURE : FALSE
4728 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4730 RECT rcCol;
4732 TRACE("nColumn=%d\n", nColumn);
4734 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4735 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4737 /* While the MSDN specifically says that column zero should not be deleted,
4738 what actually happens is that the column itself is deleted but no items or subitems
4739 are removed.
4742 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4744 if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
4745 return FALSE;
4747 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4748 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4750 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4752 SUBITEM_INFO *lpSubItem, *lpDelItem;
4753 HDPA hdpaSubItems;
4754 INT nItem, nSubItem, i;
4756 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4758 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4759 nSubItem = 0;
4760 lpDelItem = 0;
4761 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4763 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4764 if (lpSubItem->iSubItem == nColumn)
4766 nSubItem = i;
4767 lpDelItem = lpSubItem;
4769 else if (lpSubItem->iSubItem > nColumn)
4771 lpSubItem->iSubItem--;
4775 /* if we found our subitem, zapp it */
4776 if (nSubItem > 0)
4778 /* free string */
4779 if (is_textW(lpDelItem->hdr.pszText))
4780 Free(lpDelItem->hdr.pszText);
4782 /* free item */
4783 Free(lpDelItem);
4785 /* free dpa memory */
4786 DPA_DeletePtr(hdpaSubItems, nSubItem);
4791 /* update the other column info */
4792 LISTVIEW_UpdateItemSize(infoPtr);
4793 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4794 LISTVIEW_InvalidateList(infoPtr);
4795 else
4796 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4798 return TRUE;
4801 /***
4802 * DESCRIPTION:
4803 * Invalidates the listview after an item's insertion or deletion.
4805 * PARAMETER(S):
4806 * [I] infoPtr : valid pointer to the listview structure
4807 * [I] nItem : item index
4808 * [I] dir : -1 if deleting, 1 if inserting
4810 * RETURN:
4811 * None
4813 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4815 INT nPerCol, nItemCol, nItemRow;
4816 RECT rcScroll;
4817 POINT Origin;
4819 /* if we don't refresh, what's the point of scrolling? */
4820 if (!is_redrawing(infoPtr)) return;
4822 assert (abs(dir) == 1);
4824 /* arrange icons if autoarrange is on */
4825 if (is_autoarrange(infoPtr))
4827 BOOL arrange = TRUE;
4828 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4829 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4830 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4833 /* scrollbars need updating */
4834 LISTVIEW_UpdateScroll(infoPtr);
4836 /* figure out the item's position */
4837 if (infoPtr->uView == LV_VIEW_DETAILS)
4838 nPerCol = infoPtr->nItemCount + 1;
4839 else if (infoPtr->uView == LV_VIEW_LIST)
4840 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4841 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
4842 return;
4844 nItemCol = nItem / nPerCol;
4845 nItemRow = nItem % nPerCol;
4846 LISTVIEW_GetOrigin(infoPtr, &Origin);
4848 /* move the items below up a slot */
4849 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4850 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4851 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4852 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4853 OffsetRect(&rcScroll, Origin.x, Origin.y);
4854 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4855 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4857 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4858 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4859 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4862 /* report has only that column, so we're done */
4863 if (infoPtr->uView == LV_VIEW_DETAILS) return;
4865 /* now for LISTs, we have to deal with the columns to the right */
4866 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4867 rcScroll.top = 0;
4868 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4869 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4870 OffsetRect(&rcScroll, Origin.x, Origin.y);
4871 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4872 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4873 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4876 /***
4877 * DESCRIPTION:
4878 * Removes an item from the listview control.
4880 * PARAMETER(S):
4881 * [I] infoPtr : valid pointer to the listview structure
4882 * [I] nItem : item index
4884 * RETURN:
4885 * SUCCESS : TRUE
4886 * FAILURE : FALSE
4888 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4890 LVITEMW item;
4891 const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
4893 TRACE("(nItem=%d)\n", nItem);
4895 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4897 /* remove selection, and focus */
4898 item.state = 0;
4899 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4900 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4902 /* send LVN_DELETEITEM notification. */
4903 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4905 /* we need to do this here, because we'll be deleting stuff */
4906 if (is_icon)
4907 LISTVIEW_InvalidateItem(infoPtr, nItem);
4909 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4911 HDPA hdpaSubItems;
4912 ITEMHDR *hdrItem;
4913 INT i;
4915 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4916 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4918 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4919 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4920 Free(hdrItem);
4922 DPA_Destroy(hdpaSubItems);
4925 if (is_icon)
4927 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4928 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4931 infoPtr->nItemCount--;
4932 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4934 /* now is the invalidation fun */
4935 if (!is_icon)
4936 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4937 return TRUE;
4941 /***
4942 * DESCRIPTION:
4943 * Callback implementation for editlabel control
4945 * PARAMETER(S):
4946 * [I] infoPtr : valid pointer to the listview structure
4947 * [I] storeText : store edit box text as item text
4948 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4950 * RETURN:
4951 * SUCCESS : TRUE
4952 * FAILURE : FALSE
4954 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
4956 HWND hwndSelf = infoPtr->hwndSelf;
4957 NMLVDISPINFOW dispInfo;
4958 INT editedItem = infoPtr->nEditLabelItem;
4959 BOOL bSame;
4960 WCHAR *pszText = NULL;
4961 BOOL res;
4963 if (storeText)
4965 DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit);
4967 if (len)
4969 if ((pszText = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
4971 if (isW) GetWindowTextW(infoPtr->hwndEdit, pszText, len+1);
4972 else GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len+1);
4977 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4979 infoPtr->nEditLabelItem = -1;
4980 infoPtr->hwndEdit = 0;
4982 ZeroMemory(&dispInfo, sizeof(dispInfo));
4983 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4984 dispInfo.item.iItem = editedItem;
4985 dispInfo.item.iSubItem = 0;
4986 dispInfo.item.stateMask = ~0;
4987 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item))
4989 res = FALSE;
4990 goto cleanup;
4993 if (isW)
4994 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4995 else
4997 LPWSTR tmp = textdupTtoW(pszText, FALSE);
4998 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4999 textfreeT(tmp, FALSE);
5001 if (bSame)
5003 res = TRUE;
5004 goto cleanup;
5007 /* add the text from the edit in */
5008 dispInfo.item.mask |= LVIF_TEXT;
5009 dispInfo.item.pszText = pszText;
5010 dispInfo.item.cchTextMax = textlenT(pszText, isW);
5012 /* Do we need to update the Item Text */
5013 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
5015 res = FALSE;
5016 goto cleanup;
5018 if (!IsWindow(hwndSelf))
5020 res = FALSE;
5021 goto cleanup;
5023 if (!pszText) return TRUE;
5025 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5027 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
5028 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
5029 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
5031 LISTVIEW_InvalidateItem(infoPtr, editedItem);
5032 res = TRUE;
5033 goto cleanup;
5037 ZeroMemory(&dispInfo, sizeof(dispInfo));
5038 dispInfo.item.mask = LVIF_TEXT;
5039 dispInfo.item.iItem = editedItem;
5040 dispInfo.item.iSubItem = 0;
5041 dispInfo.item.pszText = pszText;
5042 dispInfo.item.cchTextMax = textlenT(pszText, isW);
5043 res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
5045 cleanup:
5046 Free(pszText);
5048 return res;
5051 /***
5052 * DESCRIPTION:
5053 * Begin in place editing of specified list view item
5055 * PARAMETER(S):
5056 * [I] infoPtr : valid pointer to the listview structure
5057 * [I] nItem : item index
5058 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5060 * RETURN:
5061 * SUCCESS : TRUE
5062 * FAILURE : FALSE
5064 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5066 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5067 NMLVDISPINFOW dispInfo;
5068 RECT rect;
5069 SIZE sz;
5070 HWND hwndSelf = infoPtr->hwndSelf;
5071 HDC hdc;
5072 HFONT hOldFont = NULL;
5073 TEXTMETRICW textMetric;
5075 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5077 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5079 /* Is the EditBox still there, if so remove it */
5080 if(infoPtr->hwndEdit != 0)
5082 SetFocus(infoPtr->hwndSelf);
5083 infoPtr->hwndEdit = 0;
5086 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5088 infoPtr->nEditLabelItem = nItem;
5090 LISTVIEW_SetSelection(infoPtr, nItem);
5091 LISTVIEW_SetItemFocus(infoPtr, nItem);
5092 LISTVIEW_InvalidateItem(infoPtr, nItem);
5094 rect.left = LVIR_LABEL;
5095 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5097 ZeroMemory(&dispInfo, sizeof(dispInfo));
5098 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5099 dispInfo.item.iItem = nItem;
5100 dispInfo.item.iSubItem = 0;
5101 dispInfo.item.stateMask = ~0;
5102 dispInfo.item.pszText = szDispText;
5103 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5104 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5106 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE, isW);
5107 if (!infoPtr->hwndEdit) return 0;
5109 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5111 if (!IsWindow(hwndSelf))
5112 return 0;
5113 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5114 infoPtr->hwndEdit = 0;
5115 return 0;
5118 /* Now position and display edit box */
5119 hdc = GetDC(infoPtr->hwndSelf);
5121 /* Select the font to get appropriate metric dimensions */
5122 if(infoPtr->hFont != 0)
5123 hOldFont = SelectObject(hdc, infoPtr->hFont);
5125 /* Get String Length in pixels */
5126 GetTextExtentPoint32W(hdc, dispInfo.item.pszText, lstrlenW(dispInfo.item.pszText), &sz);
5128 /* Add Extra spacing for the next character */
5129 GetTextMetricsW(hdc, &textMetric);
5130 sz.cx += (textMetric.tmMaxCharWidth * 2);
5132 if(infoPtr->hFont != 0)
5133 SelectObject(hdc, hOldFont);
5135 ReleaseDC(infoPtr->hwndSelf, hdc);
5137 MoveWindow(infoPtr->hwndEdit, rect.left - 2, rect.top - 1, sz.cx,
5138 rect.bottom - rect.top + 2, FALSE);
5139 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5140 SetFocus(infoPtr->hwndEdit);
5141 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5142 return infoPtr->hwndEdit;
5146 /***
5147 * DESCRIPTION:
5148 * Ensures the specified item is visible, scrolling into view if necessary.
5150 * PARAMETER(S):
5151 * [I] infoPtr : valid pointer to the listview structure
5152 * [I] nItem : item index
5153 * [I] bPartial : partially or entirely visible
5155 * RETURN:
5156 * SUCCESS : TRUE
5157 * FAILURE : FALSE
5159 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5161 INT nScrollPosHeight = 0;
5162 INT nScrollPosWidth = 0;
5163 INT nHorzAdjust = 0;
5164 INT nVertAdjust = 0;
5165 INT nHorzDiff = 0;
5166 INT nVertDiff = 0;
5167 RECT rcItem, rcTemp;
5169 rcItem.left = LVIR_BOUNDS;
5170 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5172 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5174 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5176 /* scroll left/right, but in LV_VIEW_DETAILS mode */
5177 if (infoPtr->uView == LV_VIEW_LIST)
5178 nScrollPosWidth = infoPtr->nItemWidth;
5179 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
5180 nScrollPosWidth = 1;
5182 if (rcItem.left < infoPtr->rcList.left)
5184 nHorzAdjust = -1;
5185 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5187 else
5189 nHorzAdjust = 1;
5190 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5194 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5196 /* scroll up/down, but not in LVS_LIST mode */
5197 if (infoPtr->uView == LV_VIEW_DETAILS)
5198 nScrollPosHeight = infoPtr->nItemHeight;
5199 else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
5200 nScrollPosHeight = 1;
5202 if (rcItem.top < infoPtr->rcList.top)
5204 nVertAdjust = -1;
5205 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5207 else
5209 nVertAdjust = 1;
5210 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5214 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5216 if (nScrollPosWidth)
5218 INT diff = nHorzDiff / nScrollPosWidth;
5219 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5220 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5223 if (nScrollPosHeight)
5225 INT diff = nVertDiff / nScrollPosHeight;
5226 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5227 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5230 return TRUE;
5233 /***
5234 * DESCRIPTION:
5235 * Searches for an item with specific characteristics.
5237 * PARAMETER(S):
5238 * [I] hwnd : window handle
5239 * [I] nStart : base item index
5240 * [I] lpFindInfo : item information to look for
5242 * RETURN:
5243 * SUCCESS : index of item
5244 * FAILURE : -1
5246 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5247 const LVFINDINFOW *lpFindInfo)
5249 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5250 BOOL bWrap = FALSE, bNearest = FALSE;
5251 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5252 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5253 POINT Position, Destination;
5254 LVITEMW lvItem;
5256 /* Search in virtual listviews should be done by application, not by
5257 listview control, so we just send LVN_ODFINDITEMW and return the result */
5258 if (infoPtr->dwStyle & LVS_OWNERDATA)
5260 NMLVFINDITEMW nmlv;
5262 nmlv.iStart = nStart;
5263 nmlv.lvfi = *lpFindInfo;
5264 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5267 if (!lpFindInfo || nItem < 0) return -1;
5269 lvItem.mask = 0;
5270 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5272 lvItem.mask |= LVIF_TEXT;
5273 lvItem.pszText = szDispText;
5274 lvItem.cchTextMax = DISP_TEXT_SIZE;
5277 if (lpFindInfo->flags & LVFI_WRAP)
5278 bWrap = TRUE;
5280 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5281 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
5283 POINT Origin;
5284 RECT rcArea;
5286 LISTVIEW_GetOrigin(infoPtr, &Origin);
5287 Destination.x = lpFindInfo->pt.x - Origin.x;
5288 Destination.y = lpFindInfo->pt.y - Origin.y;
5289 switch(lpFindInfo->vkDirection)
5291 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5292 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5293 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5294 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5295 case VK_HOME: Destination.x = Destination.y = 0; break;
5296 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5297 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5298 case VK_END:
5299 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5300 Destination.x = rcArea.right;
5301 Destination.y = rcArea.bottom;
5302 break;
5303 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5305 bNearest = TRUE;
5307 else Destination.x = Destination.y = 0;
5309 /* if LVFI_PARAM is specified, all other flags are ignored */
5310 if (lpFindInfo->flags & LVFI_PARAM)
5312 lvItem.mask |= LVIF_PARAM;
5313 bNearest = FALSE;
5314 lvItem.mask &= ~LVIF_TEXT;
5317 again:
5318 for (; nItem < nLast; nItem++)
5320 lvItem.iItem = nItem;
5321 lvItem.iSubItem = 0;
5322 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5324 if (lvItem.mask & LVIF_PARAM)
5326 if (lpFindInfo->lParam == lvItem.lParam)
5327 return nItem;
5328 else
5329 continue;
5332 if (lvItem.mask & LVIF_TEXT)
5334 if (lpFindInfo->flags & LVFI_PARTIAL)
5336 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5338 else
5340 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5344 if (!bNearest) return nItem;
5346 /* This is very inefficient. To do a good job here,
5347 * we need a sorted array of (x,y) item positions */
5348 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5350 /* compute the distance^2 to the destination */
5351 xdist = Destination.x - Position.x;
5352 ydist = Destination.y - Position.y;
5353 dist = xdist * xdist + ydist * ydist;
5355 /* remember the distance, and item if it's closer */
5356 if (dist < mindist)
5358 mindist = dist;
5359 nNearestItem = nItem;
5363 if (bWrap)
5365 nItem = 0;
5366 nLast = min(nStart + 1, infoPtr->nItemCount);
5367 bWrap = FALSE;
5368 goto again;
5371 return nNearestItem;
5374 /***
5375 * DESCRIPTION:
5376 * Searches for an item with specific characteristics.
5378 * PARAMETER(S):
5379 * [I] hwnd : window handle
5380 * [I] nStart : base item index
5381 * [I] lpFindInfo : item information to look for
5383 * RETURN:
5384 * SUCCESS : index of item
5385 * FAILURE : -1
5387 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5388 const LVFINDINFOA *lpFindInfo)
5390 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5391 LVFINDINFOW fiw;
5392 INT res;
5393 LPWSTR strW = NULL;
5395 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5396 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5397 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5398 textfreeT(strW, FALSE);
5399 return res;
5402 /***
5403 * DESCRIPTION:
5404 * Retrieves the background image of the listview control.
5406 * PARAMETER(S):
5407 * [I] infoPtr : valid pointer to the listview structure
5408 * [O] lpBkImage : background image attributes
5410 * RETURN:
5411 * SUCCESS : TRUE
5412 * FAILURE : FALSE
5414 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5415 /* { */
5416 /* FIXME (listview, "empty stub!\n"); */
5417 /* return FALSE; */
5418 /* } */
5420 /***
5421 * DESCRIPTION:
5422 * Retrieves column attributes.
5424 * PARAMETER(S):
5425 * [I] infoPtr : valid pointer to the listview structure
5426 * [I] nColumn : column index
5427 * [IO] lpColumn : column information
5428 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5429 * otherwise it is in fact a LPLVCOLUMNA
5431 * RETURN:
5432 * SUCCESS : TRUE
5433 * FAILURE : FALSE
5435 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5437 COLUMN_INFO *lpColumnInfo;
5438 HDITEMW hdi;
5440 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5441 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5443 /* initialize memory */
5444 ZeroMemory(&hdi, sizeof(hdi));
5446 if (lpColumn->mask & LVCF_TEXT)
5448 hdi.mask |= HDI_TEXT;
5449 hdi.pszText = lpColumn->pszText;
5450 hdi.cchTextMax = lpColumn->cchTextMax;
5453 if (lpColumn->mask & LVCF_IMAGE)
5454 hdi.mask |= HDI_IMAGE;
5456 if (lpColumn->mask & LVCF_ORDER)
5457 hdi.mask |= HDI_ORDER;
5459 if (lpColumn->mask & LVCF_SUBITEM)
5460 hdi.mask |= HDI_LPARAM;
5462 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5464 if (lpColumn->mask & LVCF_FMT)
5465 lpColumn->fmt = lpColumnInfo->fmt;
5467 if (lpColumn->mask & LVCF_WIDTH)
5468 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5470 if (lpColumn->mask & LVCF_IMAGE)
5471 lpColumn->iImage = hdi.iImage;
5473 if (lpColumn->mask & LVCF_ORDER)
5474 lpColumn->iOrder = hdi.iOrder;
5476 if (lpColumn->mask & LVCF_SUBITEM)
5477 lpColumn->iSubItem = hdi.lParam;
5479 return TRUE;
5483 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5485 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
5487 if (!lpiArray)
5488 return FALSE;
5490 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
5493 /***
5494 * DESCRIPTION:
5495 * Retrieves the column width.
5497 * PARAMETER(S):
5498 * [I] infoPtr : valid pointer to the listview structure
5499 * [I] int : column index
5501 * RETURN:
5502 * SUCCESS : column width
5503 * FAILURE : zero
5505 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5507 INT nColumnWidth = 0;
5508 HDITEMW hdItem;
5510 TRACE("nColumn=%d\n", nColumn);
5512 /* we have a 'column' in LIST and REPORT mode only */
5513 switch(infoPtr->uView)
5515 case LV_VIEW_LIST:
5516 nColumnWidth = infoPtr->nItemWidth;
5517 break;
5518 case LV_VIEW_DETAILS:
5519 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5520 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5521 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5523 * TODO: should we do the same in LVM_GETCOLUMN?
5525 hdItem.mask = HDI_WIDTH;
5526 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5528 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5529 return 0;
5531 nColumnWidth = hdItem.cxy;
5532 break;
5535 TRACE("nColumnWidth=%d\n", nColumnWidth);
5536 return nColumnWidth;
5539 /***
5540 * DESCRIPTION:
5541 * In list or report display mode, retrieves the number of items that can fit
5542 * vertically in the visible area. In icon or small icon display mode,
5543 * retrieves the total number of visible items.
5545 * PARAMETER(S):
5546 * [I] infoPtr : valid pointer to the listview structure
5548 * RETURN:
5549 * Number of fully visible items.
5551 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5553 switch (infoPtr->uView)
5555 case LV_VIEW_ICON:
5556 case LV_VIEW_SMALLICON:
5557 return infoPtr->nItemCount;
5558 case LV_VIEW_DETAILS:
5559 return LISTVIEW_GetCountPerColumn(infoPtr);
5560 case LV_VIEW_LIST:
5561 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5563 assert(FALSE);
5564 return 0;
5567 /***
5568 * DESCRIPTION:
5569 * Retrieves an image list handle.
5571 * PARAMETER(S):
5572 * [I] infoPtr : valid pointer to the listview structure
5573 * [I] nImageList : image list identifier
5575 * RETURN:
5576 * SUCCESS : image list handle
5577 * FAILURE : NULL
5579 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5581 switch (nImageList)
5583 case LVSIL_NORMAL: return infoPtr->himlNormal;
5584 case LVSIL_SMALL: return infoPtr->himlSmall;
5585 case LVSIL_STATE: return infoPtr->himlState;
5587 return NULL;
5590 /* LISTVIEW_GetISearchString */
5592 /***
5593 * DESCRIPTION:
5594 * Retrieves item attributes.
5596 * PARAMETER(S):
5597 * [I] hwnd : window handle
5598 * [IO] lpLVItem : item info
5599 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5600 * if FALSE, then lpLVItem is a LPLVITEMA.
5602 * NOTE:
5603 * This is the internal 'GetItem' interface -- it tries to
5604 * be smart and avoid text copies, if possible, by modifying
5605 * lpLVItem->pszText to point to the text string. Please note
5606 * that this is not always possible (e.g. OWNERDATA), so on
5607 * entry you *must* supply valid values for pszText, and cchTextMax.
5608 * The only difference to the documented interface is that upon
5609 * return, you should use *only* the lpLVItem->pszText, rather than
5610 * the buffer pointer you provided on input. Most code already does
5611 * that, so it's not a problem.
5612 * For the two cases when the text must be copied (that is,
5613 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5615 * RETURN:
5616 * SUCCESS : TRUE
5617 * FAILURE : FALSE
5619 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5621 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5622 NMLVDISPINFOW dispInfo;
5623 ITEM_INFO *lpItem;
5624 ITEMHDR* pItemHdr;
5625 HDPA hdpaSubItems;
5626 INT isubitem;
5628 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5630 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5631 return FALSE;
5633 if (lpLVItem->mask == 0) return TRUE;
5635 /* make a local copy */
5636 isubitem = lpLVItem->iSubItem;
5638 /* a quick optimization if all we're asked is the focus state
5639 * these queries are worth optimising since they are common,
5640 * and can be answered in constant time, without the heavy accesses */
5641 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5642 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5644 lpLVItem->state = 0;
5645 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5646 lpLVItem->state |= LVIS_FOCUSED;
5647 return TRUE;
5650 ZeroMemory(&dispInfo, sizeof(dispInfo));
5652 /* if the app stores all the data, handle it separately */
5653 if (infoPtr->dwStyle & LVS_OWNERDATA)
5655 dispInfo.item.state = 0;
5657 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5658 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
5659 ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
5661 UINT mask = lpLVItem->mask;
5663 /* NOTE: copy only fields which we _know_ are initialized, some apps
5664 * depend on the uninitialized fields being 0 */
5665 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5666 dispInfo.item.iItem = lpLVItem->iItem;
5667 dispInfo.item.iSubItem = isubitem;
5668 if (lpLVItem->mask & LVIF_TEXT)
5670 if (lpLVItem->mask & LVIF_NORECOMPUTE)
5671 /* reset mask */
5672 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
5673 else
5675 dispInfo.item.pszText = lpLVItem->pszText;
5676 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5679 if (lpLVItem->mask & LVIF_STATE)
5680 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5681 /* could be zeroed on LVIF_NORECOMPUTE case */
5682 if (dispInfo.item.mask != 0)
5684 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5685 dispInfo.item.stateMask = lpLVItem->stateMask;
5686 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5688 /* full size structure expected - _WIN32IE >= 0x560 */
5689 *lpLVItem = dispInfo.item;
5691 else if (lpLVItem->mask & LVIF_INDENT)
5693 /* indent member expected - _WIN32IE >= 0x300 */
5694 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5696 else
5698 /* minimal structure expected */
5699 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5701 lpLVItem->mask = mask;
5702 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5706 /* make sure lParam is zeroed out */
5707 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5709 /* callback marked pointer required here */
5710 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
5711 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
5713 /* we store only a little state, so if we're not asked, we're done */
5714 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5716 /* if focus is handled by us, report it */
5717 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5719 lpLVItem->state &= ~LVIS_FOCUSED;
5720 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5721 lpLVItem->state |= LVIS_FOCUSED;
5724 /* and do the same for selection, if we handle it */
5725 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5727 lpLVItem->state &= ~LVIS_SELECTED;
5728 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5729 lpLVItem->state |= LVIS_SELECTED;
5732 return TRUE;
5735 /* find the item and subitem structures before we proceed */
5736 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5737 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5738 assert (lpItem);
5740 if (isubitem)
5742 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5743 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5744 if (!lpSubItem)
5746 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5747 isubitem = 0;
5750 else
5751 pItemHdr = &lpItem->hdr;
5753 /* Do we need to query the state from the app? */
5754 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5756 dispInfo.item.mask |= LVIF_STATE;
5757 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5760 /* Do we need to enquire about the image? */
5761 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5762 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5764 dispInfo.item.mask |= LVIF_IMAGE;
5765 dispInfo.item.iImage = I_IMAGECALLBACK;
5768 /* Only items support indentation */
5769 if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
5770 (isubitem == 0))
5772 dispInfo.item.mask |= LVIF_INDENT;
5773 dispInfo.item.iIndent = I_INDENTCALLBACK;
5776 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5777 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
5778 !is_textW(pItemHdr->pszText))
5780 dispInfo.item.mask |= LVIF_TEXT;
5781 dispInfo.item.pszText = lpLVItem->pszText;
5782 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5783 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5784 *dispInfo.item.pszText = '\0';
5787 /* If we don't have all the requested info, query the application */
5788 if (dispInfo.item.mask != 0)
5790 dispInfo.item.iItem = lpLVItem->iItem;
5791 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5792 dispInfo.item.lParam = lpItem->lParam;
5793 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5794 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5797 /* we should not store values for subitems */
5798 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5800 /* Now, handle the iImage field */
5801 if (dispInfo.item.mask & LVIF_IMAGE)
5803 lpLVItem->iImage = dispInfo.item.iImage;
5804 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5805 pItemHdr->iImage = dispInfo.item.iImage;
5807 else if (lpLVItem->mask & LVIF_IMAGE)
5809 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5810 lpLVItem->iImage = pItemHdr->iImage;
5811 else
5812 lpLVItem->iImage = 0;
5815 /* The pszText field */
5816 if (dispInfo.item.mask & LVIF_TEXT)
5818 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5819 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5821 lpLVItem->pszText = dispInfo.item.pszText;
5823 else if (lpLVItem->mask & LVIF_TEXT)
5825 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
5826 if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
5827 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5830 /* Next is the lParam field */
5831 if (dispInfo.item.mask & LVIF_PARAM)
5833 lpLVItem->lParam = dispInfo.item.lParam;
5834 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5835 lpItem->lParam = dispInfo.item.lParam;
5837 else if (lpLVItem->mask & LVIF_PARAM)
5838 lpLVItem->lParam = lpItem->lParam;
5840 /* if this is a subitem, we're done */
5841 if (isubitem) return TRUE;
5843 /* ... the state field (this one is different due to uCallbackmask) */
5844 if (lpLVItem->mask & LVIF_STATE)
5846 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5847 if (dispInfo.item.mask & LVIF_STATE)
5849 lpLVItem->state &= ~dispInfo.item.stateMask;
5850 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5852 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5854 lpLVItem->state &= ~LVIS_FOCUSED;
5855 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5856 lpLVItem->state |= LVIS_FOCUSED;
5858 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5860 lpLVItem->state &= ~LVIS_SELECTED;
5861 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5862 lpLVItem->state |= LVIS_SELECTED;
5866 /* and last, but not least, the indent field */
5867 if (dispInfo.item.mask & LVIF_INDENT)
5869 lpLVItem->iIndent = dispInfo.item.iIndent;
5870 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
5871 lpItem->iIndent = dispInfo.item.iIndent;
5873 else if (lpLVItem->mask & LVIF_INDENT)
5875 lpLVItem->iIndent = lpItem->iIndent;
5878 return TRUE;
5881 /***
5882 * DESCRIPTION:
5883 * Retrieves item attributes.
5885 * PARAMETER(S):
5886 * [I] hwnd : window handle
5887 * [IO] lpLVItem : item info
5888 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5889 * if FALSE, then lpLVItem is a LPLVITEMA.
5891 * NOTE:
5892 * This is the external 'GetItem' interface -- it properly copies
5893 * the text in the provided buffer.
5895 * RETURN:
5896 * SUCCESS : TRUE
5897 * FAILURE : FALSE
5899 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5901 LPWSTR pszText;
5902 BOOL bResult;
5904 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5905 return FALSE;
5907 pszText = lpLVItem->pszText;
5908 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5909 if (bResult && lpLVItem->pszText != pszText)
5911 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
5912 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5913 else
5914 pszText = LPSTR_TEXTCALLBACKW;
5916 lpLVItem->pszText = pszText;
5918 return bResult;
5922 /***
5923 * DESCRIPTION:
5924 * Retrieves the position (upper-left) of the listview control item.
5925 * Note that for LVS_ICON style, the upper-left is that of the icon
5926 * and not the bounding box.
5928 * PARAMETER(S):
5929 * [I] infoPtr : valid pointer to the listview structure
5930 * [I] nItem : item index
5931 * [O] lpptPosition : coordinate information
5933 * RETURN:
5934 * SUCCESS : TRUE
5935 * FAILURE : FALSE
5937 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5939 POINT Origin;
5941 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5943 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5945 LISTVIEW_GetOrigin(infoPtr, &Origin);
5946 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5948 if (infoPtr->uView == LV_VIEW_ICON)
5950 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5951 lpptPosition->y += ICON_TOP_PADDING;
5953 lpptPosition->x += Origin.x;
5954 lpptPosition->y += Origin.y;
5956 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5957 return TRUE;
5961 /***
5962 * DESCRIPTION:
5963 * Retrieves the bounding rectangle for a listview control item.
5965 * PARAMETER(S):
5966 * [I] infoPtr : valid pointer to the listview structure
5967 * [I] nItem : item index
5968 * [IO] lprc : bounding rectangle coordinates
5969 * lprc->left specifies the portion of the item for which the bounding
5970 * rectangle will be retrieved.
5972 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5973 * including the icon and label.
5975 * * For LVS_ICON
5976 * * Experiment shows that native control returns:
5977 * * width = min (48, length of text line)
5978 * * .left = position.x - (width - iconsize.cx)/2
5979 * * .right = .left + width
5980 * * height = #lines of text * ntmHeight + icon height + 8
5981 * * .top = position.y - 2
5982 * * .bottom = .top + height
5983 * * separation between items .y = itemSpacing.cy - height
5984 * * .x = itemSpacing.cx - width
5985 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5987 * * For LVS_ICON
5988 * * Experiment shows that native control returns:
5989 * * width = iconSize.cx + 16
5990 * * .left = position.x - (width - iconsize.cx)/2
5991 * * .right = .left + width
5992 * * height = iconSize.cy + 4
5993 * * .top = position.y - 2
5994 * * .bottom = .top + height
5995 * * separation between items .y = itemSpacing.cy - height
5996 * * .x = itemSpacing.cx - width
5997 * LVIR_LABEL Returns the bounding rectangle of the item text.
5999 * * For LVS_ICON
6000 * * Experiment shows that native control returns:
6001 * * width = text length
6002 * * .left = position.x - width/2
6003 * * .right = .left + width
6004 * * height = ntmH * linecount + 2
6005 * * .top = position.y + iconSize.cy + 6
6006 * * .bottom = .top + height
6007 * * separation between items .y = itemSpacing.cy - height
6008 * * .x = itemSpacing.cx - width
6009 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
6010 * rectangles, but excludes columns in report view.
6012 * RETURN:
6013 * SUCCESS : TRUE
6014 * FAILURE : FALSE
6016 * NOTES
6017 * Note that the bounding rectangle of the label in the LVS_ICON view depends
6018 * upon whether the window has the focus currently and on whether the item
6019 * is the one with the focus. Ensure that the control's record of which
6020 * item has the focus agrees with the items' records.
6022 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6024 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6025 BOOL doLabel = TRUE, oversizedBox = FALSE;
6026 POINT Position, Origin;
6027 LVITEMW lvItem;
6029 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
6031 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6033 LISTVIEW_GetOrigin(infoPtr, &Origin);
6034 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6036 /* Be smart and try to figure out the minimum we have to do */
6037 if (lprc->left == LVIR_ICON) doLabel = FALSE;
6038 if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
6039 if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
6040 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
6041 oversizedBox = TRUE;
6043 /* get what we need from the item before hand, so we make
6044 * only one request. This can speed up things, if data
6045 * is stored on the app side */
6046 lvItem.mask = 0;
6047 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6048 if (doLabel) lvItem.mask |= LVIF_TEXT;
6049 lvItem.iItem = nItem;
6050 lvItem.iSubItem = 0;
6051 lvItem.pszText = szDispText;
6052 lvItem.cchTextMax = DISP_TEXT_SIZE;
6053 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6054 /* we got the state already up, simulate it here, to avoid a reget */
6055 if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
6057 lvItem.mask |= LVIF_STATE;
6058 lvItem.stateMask = LVIS_FOCUSED;
6059 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
6062 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
6063 lprc->left = LVIR_BOUNDS;
6064 switch(lprc->left)
6066 case LVIR_ICON:
6067 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6068 break;
6070 case LVIR_LABEL:
6071 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
6072 break;
6074 case LVIR_BOUNDS:
6075 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6076 break;
6078 case LVIR_SELECTBOUNDS:
6079 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
6080 break;
6082 default:
6083 WARN("Unknown value: %d\n", lprc->left);
6084 return FALSE;
6087 if (infoPtr->uView == LV_VIEW_DETAILS)
6088 OffsetRect(lprc, Origin.x, Position.y + Origin.y);
6089 else
6090 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
6092 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
6094 return TRUE;
6097 /***
6098 * DESCRIPTION:
6099 * Retrieves the spacing between listview control items.
6101 * PARAMETER(S):
6102 * [I] infoPtr : valid pointer to the listview structure
6103 * [IO] lprc : rectangle to receive the output
6104 * on input, lprc->top = nSubItem
6105 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6107 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6108 * not only those of the first column.
6109 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6111 * RETURN:
6112 * TRUE: success
6113 * FALSE: failure
6115 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6117 POINT Position;
6118 LVITEMW lvItem;
6119 INT nColumn;
6121 if (!lprc) return FALSE;
6123 nColumn = lprc->top;
6125 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6126 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6127 if (lprc->top == 0)
6128 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6130 if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
6132 /* special case for header items */
6133 if (nItem == -1)
6135 if (lprc->left != LVIR_BOUNDS)
6137 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6138 return FALSE;
6141 if (infoPtr->hwndHeader)
6142 return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
6143 else
6145 memset(lprc, 0, sizeof(RECT));
6146 return TRUE;
6150 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6152 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6154 lvItem.mask = 0;
6155 lvItem.iItem = nItem;
6156 lvItem.iSubItem = nColumn;
6158 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6159 switch(lprc->left)
6161 case LVIR_ICON:
6162 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6163 break;
6165 case LVIR_LABEL:
6166 case LVIR_BOUNDS:
6167 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6168 break;
6170 default:
6171 ERR("Unknown bounds=%d\n", lprc->left);
6172 return FALSE;
6175 OffsetRect(lprc, 0, Position.y);
6176 return TRUE;
6180 /***
6181 * DESCRIPTION:
6182 * Retrieves the width of a label.
6184 * PARAMETER(S):
6185 * [I] infoPtr : valid pointer to the listview structure
6187 * RETURN:
6188 * SUCCESS : string width (in pixels)
6189 * FAILURE : zero
6191 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6193 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6194 LVITEMW lvItem;
6196 TRACE("(nItem=%d)\n", nItem);
6198 lvItem.mask = LVIF_TEXT;
6199 lvItem.iItem = nItem;
6200 lvItem.iSubItem = 0;
6201 lvItem.pszText = szDispText;
6202 lvItem.cchTextMax = DISP_TEXT_SIZE;
6203 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6205 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6208 /***
6209 * DESCRIPTION:
6210 * Retrieves the spacing between listview control items.
6212 * PARAMETER(S):
6213 * [I] infoPtr : valid pointer to the listview structure
6214 * [I] bSmall : flag for small or large icon
6216 * RETURN:
6217 * Horizontal + vertical spacing
6219 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6221 LONG lResult;
6223 if (!bSmall)
6225 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6227 else
6229 if (infoPtr->uView == LV_VIEW_ICON)
6230 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6231 else
6232 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6234 return lResult;
6237 /***
6238 * DESCRIPTION:
6239 * Retrieves the state of a listview control item.
6241 * PARAMETER(S):
6242 * [I] infoPtr : valid pointer to the listview structure
6243 * [I] nItem : item index
6244 * [I] uMask : state mask
6246 * RETURN:
6247 * State specified by the mask.
6249 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6251 LVITEMW lvItem;
6253 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6255 lvItem.iItem = nItem;
6256 lvItem.iSubItem = 0;
6257 lvItem.mask = LVIF_STATE;
6258 lvItem.stateMask = uMask;
6259 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6261 return lvItem.state & uMask;
6264 /***
6265 * DESCRIPTION:
6266 * Retrieves the text of a listview control item or subitem.
6268 * PARAMETER(S):
6269 * [I] hwnd : window handle
6270 * [I] nItem : item index
6271 * [IO] lpLVItem : item information
6272 * [I] isW : TRUE if lpLVItem is Unicode
6274 * RETURN:
6275 * SUCCESS : string length
6276 * FAILURE : 0
6278 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6280 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6282 lpLVItem->mask = LVIF_TEXT;
6283 lpLVItem->iItem = nItem;
6284 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6286 return textlenT(lpLVItem->pszText, isW);
6289 /***
6290 * DESCRIPTION:
6291 * Searches for an item based on properties + relationships.
6293 * PARAMETER(S):
6294 * [I] infoPtr : valid pointer to the listview structure
6295 * [I] nItem : item index
6296 * [I] uFlags : relationship flag
6298 * RETURN:
6299 * SUCCESS : item index
6300 * FAILURE : -1
6302 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6304 UINT uMask = 0;
6305 LVFINDINFOW lvFindInfo;
6306 INT nCountPerColumn;
6307 INT nCountPerRow;
6308 INT i;
6310 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6311 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6313 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6315 if (uFlags & LVNI_CUT)
6316 uMask |= LVIS_CUT;
6318 if (uFlags & LVNI_DROPHILITED)
6319 uMask |= LVIS_DROPHILITED;
6321 if (uFlags & LVNI_FOCUSED)
6322 uMask |= LVIS_FOCUSED;
6324 if (uFlags & LVNI_SELECTED)
6325 uMask |= LVIS_SELECTED;
6327 /* if we're asked for the focused item, that's only one,
6328 * so it's worth optimizing */
6329 if (uFlags & LVNI_FOCUSED)
6331 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6332 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6335 if (uFlags & LVNI_ABOVE)
6337 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6339 while (nItem >= 0)
6341 nItem--;
6342 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6343 return nItem;
6346 else
6348 /* Special case for autoarrange - move 'til the top of a list */
6349 if (is_autoarrange(infoPtr))
6351 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6352 while (nItem - nCountPerRow >= 0)
6354 nItem -= nCountPerRow;
6355 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6356 return nItem;
6358 return -1;
6360 lvFindInfo.flags = LVFI_NEARESTXY;
6361 lvFindInfo.vkDirection = VK_UP;
6362 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6363 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6365 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6366 return nItem;
6370 else if (uFlags & LVNI_BELOW)
6372 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6374 while (nItem < infoPtr->nItemCount)
6376 nItem++;
6377 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6378 return nItem;
6381 else
6383 /* Special case for autoarrange - move 'til the bottom of a list */
6384 if (is_autoarrange(infoPtr))
6386 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6387 while (nItem + nCountPerRow < infoPtr->nItemCount )
6389 nItem += nCountPerRow;
6390 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6391 return nItem;
6393 return -1;
6395 lvFindInfo.flags = LVFI_NEARESTXY;
6396 lvFindInfo.vkDirection = VK_DOWN;
6397 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6398 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6400 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6401 return nItem;
6405 else if (uFlags & LVNI_TOLEFT)
6407 if (infoPtr->uView == LV_VIEW_LIST)
6409 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6410 while (nItem - nCountPerColumn >= 0)
6412 nItem -= nCountPerColumn;
6413 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6414 return nItem;
6417 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6419 /* Special case for autoarrange - move 'til the beginning of a row */
6420 if (is_autoarrange(infoPtr))
6422 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6423 while (nItem % nCountPerRow > 0)
6425 nItem --;
6426 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6427 return nItem;
6429 return -1;
6431 lvFindInfo.flags = LVFI_NEARESTXY;
6432 lvFindInfo.vkDirection = VK_LEFT;
6433 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6434 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6436 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6437 return nItem;
6441 else if (uFlags & LVNI_TORIGHT)
6443 if (infoPtr->uView == LV_VIEW_LIST)
6445 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6446 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6448 nItem += nCountPerColumn;
6449 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6450 return nItem;
6453 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6455 /* Special case for autoarrange - move 'til the end of a row */
6456 if (is_autoarrange(infoPtr))
6458 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6459 while (nItem % nCountPerRow < nCountPerRow - 1 )
6461 nItem ++;
6462 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6463 return nItem;
6465 return -1;
6467 lvFindInfo.flags = LVFI_NEARESTXY;
6468 lvFindInfo.vkDirection = VK_RIGHT;
6469 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6470 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6472 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6473 return nItem;
6477 else
6479 nItem++;
6481 /* search by index */
6482 for (i = nItem; i < infoPtr->nItemCount; i++)
6484 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6485 return i;
6489 return -1;
6492 /* LISTVIEW_GetNumberOfWorkAreas */
6494 /***
6495 * DESCRIPTION:
6496 * Retrieves the origin coordinates when in icon or small icon display mode.
6498 * PARAMETER(S):
6499 * [I] infoPtr : valid pointer to the listview structure
6500 * [O] lpptOrigin : coordinate information
6502 * RETURN:
6503 * None.
6505 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6507 INT nHorzPos = 0, nVertPos = 0;
6508 SCROLLINFO scrollInfo;
6510 scrollInfo.cbSize = sizeof(SCROLLINFO);
6511 scrollInfo.fMask = SIF_POS;
6513 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6514 nHorzPos = scrollInfo.nPos;
6515 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6516 nVertPos = scrollInfo.nPos;
6518 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6520 lpptOrigin->x = infoPtr->rcList.left;
6521 lpptOrigin->y = infoPtr->rcList.top;
6522 if (infoPtr->uView == LV_VIEW_LIST)
6523 nHorzPos *= infoPtr->nItemWidth;
6524 else if (infoPtr->uView == LV_VIEW_DETAILS)
6525 nVertPos *= infoPtr->nItemHeight;
6527 lpptOrigin->x -= nHorzPos;
6528 lpptOrigin->y -= nVertPos;
6530 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6533 /***
6534 * DESCRIPTION:
6535 * Retrieves the width of a string.
6537 * PARAMETER(S):
6538 * [I] hwnd : window handle
6539 * [I] lpszText : text string to process
6540 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6542 * RETURN:
6543 * SUCCESS : string width (in pixels)
6544 * FAILURE : zero
6546 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6548 SIZE stringSize;
6550 stringSize.cx = 0;
6551 if (is_textT(lpszText, isW))
6553 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6554 HDC hdc = GetDC(infoPtr->hwndSelf);
6555 HFONT hOldFont = SelectObject(hdc, hFont);
6557 if (isW)
6558 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6559 else
6560 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6561 SelectObject(hdc, hOldFont);
6562 ReleaseDC(infoPtr->hwndSelf, hdc);
6564 return stringSize.cx;
6567 /***
6568 * DESCRIPTION:
6569 * Determines which listview item is located at the specified position.
6571 * PARAMETER(S):
6572 * [I] infoPtr : valid pointer to the listview structure
6573 * [IO] lpht : hit test information
6574 * [I] subitem : fill out iSubItem.
6575 * [I] select : return the index only if the hit selects the item
6577 * NOTE:
6578 * (mm 20001022): We must not allow iSubItem to be touched, for
6579 * an app might pass only a structure with space up to iItem!
6580 * (MS Office 97 does that for instance in the file open dialog)
6582 * RETURN:
6583 * SUCCESS : item index
6584 * FAILURE : -1
6586 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6588 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6589 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6590 POINT Origin, Position, opt;
6591 LVITEMW lvItem;
6592 ITERATOR i;
6593 INT iItem;
6595 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6597 lpht->flags = 0;
6598 lpht->iItem = -1;
6599 if (subitem) lpht->iSubItem = 0;
6601 if (infoPtr->rcList.left > lpht->pt.x)
6602 lpht->flags |= LVHT_TOLEFT;
6603 else if (infoPtr->rcList.right < lpht->pt.x)
6604 lpht->flags |= LVHT_TORIGHT;
6606 if (infoPtr->rcList.top > lpht->pt.y)
6607 lpht->flags |= LVHT_ABOVE;
6608 else if (infoPtr->rcList.bottom < lpht->pt.y)
6609 lpht->flags |= LVHT_BELOW;
6611 TRACE("lpht->flags=0x%x\n", lpht->flags);
6612 if (lpht->flags) return -1;
6614 lpht->flags |= LVHT_NOWHERE;
6616 LISTVIEW_GetOrigin(infoPtr, &Origin);
6618 /* first deal with the large items */
6619 rcSearch.left = lpht->pt.x;
6620 rcSearch.top = lpht->pt.y;
6621 rcSearch.right = rcSearch.left + 1;
6622 rcSearch.bottom = rcSearch.top + 1;
6624 iterator_frameditems(&i, infoPtr, &rcSearch);
6625 iterator_next(&i); /* go to first item in the sequence */
6626 iItem = i.nItem;
6627 iterator_destroy(&i);
6629 TRACE("lpht->iItem=%d\n", iItem);
6630 if (iItem == -1) return -1;
6632 if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
6634 RECT bounds, *pRect;
6635 INT j;
6637 /* for top/bottom only */
6638 bounds.left = LVIR_BOUNDS;
6639 LISTVIEW_GetItemRect(infoPtr, iItem, &bounds);
6641 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6643 pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
6644 bounds.left = pRect->left;
6645 bounds.right = pRect->right;
6647 if (PtInRect(&bounds, lpht->pt))
6649 lpht->iSubItem = j;
6650 break;
6653 TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
6656 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6657 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6658 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6659 if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6660 lvItem.iItem = iItem;
6661 lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
6662 lvItem.pszText = szDispText;
6663 lvItem.cchTextMax = DISP_TEXT_SIZE;
6664 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6665 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6667 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6668 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6669 opt.x = lpht->pt.x - Position.x - Origin.x;
6670 opt.y = lpht->pt.y - Position.y - Origin.y;
6672 if (infoPtr->uView == LV_VIEW_DETAILS)
6673 rcBounds = rcBox;
6674 else
6676 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6677 UnionRect(&rcBounds, &rcBounds, &rcState);
6679 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6680 if (!PtInRect(&rcBounds, opt)) return -1;
6682 if (PtInRect(&rcIcon, opt))
6683 lpht->flags |= LVHT_ONITEMICON;
6684 else if (PtInRect(&rcLabel, opt))
6685 lpht->flags |= LVHT_ONITEMLABEL;
6686 else if (infoPtr->himlState && PtInRect(&rcState, opt))
6687 lpht->flags |= LVHT_ONITEMSTATEICON;
6688 /* special case for LVS_EX_FULLROWSELECT */
6689 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
6690 !(lpht->flags & LVHT_ONITEM))
6692 lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
6694 if (lpht->flags & LVHT_ONITEM)
6695 lpht->flags &= ~LVHT_NOWHERE;
6696 TRACE("lpht->flags=0x%x\n", lpht->flags);
6698 if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
6699 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6700 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6702 if (infoPtr->uView == LV_VIEW_DETAILS)
6704 /* get main item bounds */
6705 lvItem.iSubItem = 0;
6706 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6707 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6708 UnionRect(&rcBounds, &rcBounds, &rcState);
6710 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6712 return lpht->iItem = iItem;
6715 /***
6716 * DESCRIPTION:
6717 * Inserts a new item in the listview control.
6719 * PARAMETER(S):
6720 * [I] infoPtr : valid pointer to the listview structure
6721 * [I] lpLVItem : item information
6722 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6724 * RETURN:
6725 * SUCCESS : new item index
6726 * FAILURE : -1
6728 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6730 INT nItem;
6731 HDPA hdpaSubItems;
6732 NMLISTVIEW nmlv;
6733 ITEM_INFO *lpItem;
6734 BOOL is_sorted, has_changed;
6735 LVITEMW item;
6736 HWND hwndSelf = infoPtr->hwndSelf;
6738 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6740 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6742 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6743 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6745 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6747 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6749 /* insert item in listview control data structure */
6750 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6751 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6753 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6754 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6756 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6758 /* calculate new item index */
6759 if (is_sorted)
6761 HDPA hItem;
6762 ITEM_INFO *item_s;
6763 INT i = 0, cmpv;
6765 while (i < infoPtr->nItemCount)
6767 hItem = DPA_GetPtr( infoPtr->hdpaItems, i);
6768 item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
6770 cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, TRUE);
6771 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
6773 if (cmpv >= 0) break;
6774 i++;
6776 nItem = i;
6778 else
6779 nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
6781 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6782 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6783 if (nItem == -1) goto fail;
6784 infoPtr->nItemCount++;
6786 /* shift indices first so they don't get tangled */
6787 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6789 /* set the item attributes */
6790 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6792 /* full size structure expected - _WIN32IE >= 0x560 */
6793 item = *lpLVItem;
6795 else if (lpLVItem->mask & LVIF_INDENT)
6797 /* indent member expected - _WIN32IE >= 0x300 */
6798 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6800 else
6802 /* minimal structure expected */
6803 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6805 item.iItem = nItem;
6806 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6808 item.mask |= LVIF_STATE;
6809 item.stateMask |= LVIS_STATEIMAGEMASK;
6810 item.state &= ~LVIS_STATEIMAGEMASK;
6811 item.state |= INDEXTOSTATEIMAGEMASK(1);
6813 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6815 /* make room for the position, if we are in the right mode */
6816 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6818 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6819 goto undo;
6820 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6822 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6823 goto undo;
6827 /* send LVN_INSERTITEM notification */
6828 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6829 nmlv.iItem = nItem;
6830 nmlv.lParam = lpItem->lParam;
6831 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6832 if (!IsWindow(hwndSelf))
6833 return -1;
6835 /* align items (set position of each item) */
6836 if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
6838 POINT pt;
6840 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6841 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6842 else
6843 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6845 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6848 /* now is the invalidation fun */
6849 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6850 return nItem;
6852 undo:
6853 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6854 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6855 infoPtr->nItemCount--;
6856 fail:
6857 DPA_DeletePtr(hdpaSubItems, 0);
6858 DPA_Destroy (hdpaSubItems);
6859 Free (lpItem);
6860 return -1;
6863 /***
6864 * DESCRIPTION:
6865 * Redraws a range of items.
6867 * PARAMETER(S):
6868 * [I] infoPtr : valid pointer to the listview structure
6869 * [I] nFirst : first item
6870 * [I] nLast : last item
6872 * RETURN:
6873 * SUCCESS : TRUE
6874 * FAILURE : FALSE
6876 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6878 INT i;
6880 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6881 max(nFirst, nLast) >= infoPtr->nItemCount)
6882 return FALSE;
6884 for (i = nFirst; i <= nLast; i++)
6885 LISTVIEW_InvalidateItem(infoPtr, i);
6887 return TRUE;
6890 /***
6891 * DESCRIPTION:
6892 * Scroll the content of a listview.
6894 * PARAMETER(S):
6895 * [I] infoPtr : valid pointer to the listview structure
6896 * [I] dx : horizontal scroll amount in pixels
6897 * [I] dy : vertical scroll amount in pixels
6899 * RETURN:
6900 * SUCCESS : TRUE
6901 * FAILURE : FALSE
6903 * COMMENTS:
6904 * If the control is in report view (LV_VIEW_DETAILS) the control can
6905 * be scrolled only in line increments. "dy" will be rounded to the
6906 * nearest number of pixels that are a whole line. Ex: if line height
6907 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6908 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6910 * For: (per experimentation with native control and CSpy ListView)
6911 * LV_VIEW_ICON dy=1 = 1 pixel (vertical only)
6912 * dx ignored
6913 * LV_VIEW_SMALLICON dy=1 = 1 pixel (vertical only)
6914 * dx ignored
6915 * LV_VIEW_LIST dx=1 = 1 column (horizontal only)
6916 * but will only scroll 1 column per message
6917 * no matter what the value.
6918 * dy must be 0 or FALSE returned.
6919 * LV_VIEW_DETAILS dx=1 = 1 pixel
6920 * dy= see above
6923 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6925 switch(infoPtr->uView) {
6926 case LV_VIEW_DETAILS:
6927 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6928 dy /= infoPtr->nItemHeight;
6929 break;
6930 case LV_VIEW_LIST:
6931 if (dy != 0) return FALSE;
6932 break;
6933 default: /* icon */
6934 dx = 0;
6935 break;
6938 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6939 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6941 return TRUE;
6944 /***
6945 * DESCRIPTION:
6946 * Sets the background color.
6948 * PARAMETER(S):
6949 * [I] infoPtr : valid pointer to the listview structure
6950 * [I] clrBk : background color
6952 * RETURN:
6953 * SUCCESS : TRUE
6954 * FAILURE : FALSE
6956 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6958 TRACE("(clrBk=%x)\n", clrBk);
6960 if(infoPtr->clrBk != clrBk) {
6961 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6962 infoPtr->clrBk = clrBk;
6963 if (clrBk == CLR_NONE)
6964 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6965 else
6966 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6967 LISTVIEW_InvalidateList(infoPtr);
6970 return TRUE;
6973 /* LISTVIEW_SetBkImage */
6975 /*** Helper for {Insert,Set}ColumnT *only* */
6976 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6977 const LVCOLUMNW *lpColumn, BOOL isW)
6979 if (lpColumn->mask & LVCF_FMT)
6981 /* format member is valid */
6982 lphdi->mask |= HDI_FORMAT;
6984 /* set text alignment (leftmost column must be left-aligned) */
6985 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6986 lphdi->fmt |= HDF_LEFT;
6987 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6988 lphdi->fmt |= HDF_RIGHT;
6989 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6990 lphdi->fmt |= HDF_CENTER;
6992 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6993 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6995 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6997 lphdi->fmt |= HDF_IMAGE;
6998 lphdi->iImage = I_IMAGECALLBACK;
7002 if (lpColumn->mask & LVCF_WIDTH)
7004 lphdi->mask |= HDI_WIDTH;
7005 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
7007 /* make it fill the remainder of the controls width */
7008 RECT rcHeader;
7009 INT item_index;
7011 for(item_index = 0; item_index < (nColumn - 1); item_index++)
7013 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
7014 lphdi->cxy += rcHeader.right - rcHeader.left;
7017 /* retrieve the layout of the header */
7018 GetClientRect(infoPtr->hwndSelf, &rcHeader);
7019 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
7021 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
7023 else
7024 lphdi->cxy = lpColumn->cx;
7027 if (lpColumn->mask & LVCF_TEXT)
7029 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
7030 lphdi->fmt |= HDF_STRING;
7031 lphdi->pszText = lpColumn->pszText;
7032 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
7035 if (lpColumn->mask & LVCF_IMAGE)
7037 lphdi->mask |= HDI_IMAGE;
7038 lphdi->iImage = lpColumn->iImage;
7041 if (lpColumn->mask & LVCF_ORDER)
7043 lphdi->mask |= HDI_ORDER;
7044 lphdi->iOrder = lpColumn->iOrder;
7049 /***
7050 * DESCRIPTION:
7051 * Inserts a new column.
7053 * PARAMETER(S):
7054 * [I] infoPtr : valid pointer to the listview structure
7055 * [I] nColumn : column index
7056 * [I] lpColumn : column information
7057 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
7059 * RETURN:
7060 * SUCCESS : new column index
7061 * FAILURE : -1
7063 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
7064 const LVCOLUMNW *lpColumn, BOOL isW)
7066 COLUMN_INFO *lpColumnInfo;
7067 INT nNewColumn;
7068 HDITEMW hdi;
7070 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7072 if (!lpColumn || nColumn < 0) return -1;
7073 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
7075 ZeroMemory(&hdi, sizeof(HDITEMW));
7076 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7079 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
7080 * (can be seen in SPY) otherwise column never gets added.
7082 if (!(lpColumn->mask & LVCF_WIDTH)) {
7083 hdi.mask |= HDI_WIDTH;
7084 hdi.cxy = 10;
7088 * when the iSubItem is available Windows copies it to the header lParam. It seems
7089 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7091 if (lpColumn->mask & LVCF_SUBITEM)
7093 hdi.mask |= HDI_LPARAM;
7094 hdi.lParam = lpColumn->iSubItem;
7097 /* create header if not present */
7098 LISTVIEW_CreateHeader(infoPtr);
7099 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
7100 (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
7102 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7105 /* insert item in header control */
7106 nNewColumn = SendMessageW(infoPtr->hwndHeader,
7107 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7108 (WPARAM)nColumn, (LPARAM)&hdi);
7109 if (nNewColumn == -1) return -1;
7110 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7112 /* create our own column info */
7113 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7114 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7116 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7117 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
7118 goto fail;
7120 /* now we have to actually adjust the data */
7121 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7123 SUBITEM_INFO *lpSubItem;
7124 HDPA hdpaSubItems;
7125 INT nItem, i;
7127 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7129 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7130 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7132 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7133 if (lpSubItem->iSubItem >= nNewColumn)
7134 lpSubItem->iSubItem++;
7139 /* make space for the new column */
7140 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7141 LISTVIEW_UpdateItemSize(infoPtr);
7143 return nNewColumn;
7145 fail:
7146 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7147 if (lpColumnInfo)
7149 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7150 Free(lpColumnInfo);
7152 return -1;
7155 /***
7156 * DESCRIPTION:
7157 * Sets the attributes of a header item.
7159 * PARAMETER(S):
7160 * [I] infoPtr : valid pointer to the listview structure
7161 * [I] nColumn : column index
7162 * [I] lpColumn : column attributes
7163 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7165 * RETURN:
7166 * SUCCESS : TRUE
7167 * FAILURE : FALSE
7169 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7170 const LVCOLUMNW *lpColumn, BOOL isW)
7172 HDITEMW hdi, hdiget;
7173 BOOL bResult;
7175 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7177 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7179 ZeroMemory(&hdi, sizeof(HDITEMW));
7180 if (lpColumn->mask & LVCF_FMT)
7182 hdi.mask |= HDI_FORMAT;
7183 hdiget.mask = HDI_FORMAT;
7184 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7185 hdi.fmt = hdiget.fmt & HDF_STRING;
7187 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7189 /* set header item attributes */
7190 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7191 if (!bResult) return FALSE;
7193 if (lpColumn->mask & LVCF_FMT)
7195 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7196 int oldFmt = lpColumnInfo->fmt;
7198 lpColumnInfo->fmt = lpColumn->fmt;
7199 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7201 if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7205 return TRUE;
7208 /***
7209 * DESCRIPTION:
7210 * Sets the column order array
7212 * PARAMETERS:
7213 * [I] infoPtr : valid pointer to the listview structure
7214 * [I] iCount : number of elements in column order array
7215 * [I] lpiArray : pointer to column order array
7217 * RETURN:
7218 * SUCCESS : TRUE
7219 * FAILURE : FALSE
7221 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7223 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7225 if (!lpiArray)
7226 return FALSE;
7228 return TRUE;
7232 /***
7233 * DESCRIPTION:
7234 * Sets the width of a column
7236 * PARAMETERS:
7237 * [I] infoPtr : valid pointer to the listview structure
7238 * [I] nColumn : column index
7239 * [I] cx : column width
7241 * RETURN:
7242 * SUCCESS : TRUE
7243 * FAILURE : FALSE
7245 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7247 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7248 INT max_cx = 0;
7249 HDITEMW hdi;
7251 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7253 /* set column width only if in report or list mode */
7254 if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
7256 /* take care of invalid cx values */
7257 if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE;
7258 else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE;
7260 /* resize all columns if in LV_VIEW_LIST mode */
7261 if(infoPtr->uView == LV_VIEW_LIST)
7263 infoPtr->nItemWidth = cx;
7264 LISTVIEW_InvalidateList(infoPtr);
7265 return TRUE;
7268 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7270 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7272 INT nLabelWidth;
7273 LVITEMW lvItem;
7275 lvItem.mask = LVIF_TEXT;
7276 lvItem.iItem = 0;
7277 lvItem.iSubItem = nColumn;
7278 lvItem.pszText = szDispText;
7279 lvItem.cchTextMax = DISP_TEXT_SIZE;
7280 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7282 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7283 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7284 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7286 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7287 max_cx += infoPtr->iconSize.cx;
7288 max_cx += TRAILING_LABEL_PADDING;
7291 /* autosize based on listview items width */
7292 if(cx == LVSCW_AUTOSIZE)
7293 cx = max_cx;
7294 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7296 /* if iCol is the last column make it fill the remainder of the controls width */
7297 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7299 RECT rcHeader;
7300 POINT Origin;
7302 LISTVIEW_GetOrigin(infoPtr, &Origin);
7303 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7305 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7307 else
7309 /* Despite what the MS docs say, if this is not the last
7310 column, then MS resizes the column to the width of the
7311 largest text string in the column, including headers
7312 and items. This is different from LVSCW_AUTOSIZE in that
7313 LVSCW_AUTOSIZE ignores the header string length. */
7314 cx = 0;
7316 /* retrieve header text */
7317 hdi.mask = HDI_TEXT;
7318 hdi.cchTextMax = DISP_TEXT_SIZE;
7319 hdi.pszText = szDispText;
7320 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7322 HDC hdc = GetDC(infoPtr->hwndSelf);
7323 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7324 SIZE size;
7326 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7327 cx = size.cx + TRAILING_HEADER_PADDING;
7328 /* FIXME: Take into account the header image, if one is present */
7329 SelectObject(hdc, old_font);
7330 ReleaseDC(infoPtr->hwndSelf, hdc);
7332 cx = max (cx, max_cx);
7336 if (cx < 0) return FALSE;
7338 /* call header to update the column change */
7339 hdi.mask = HDI_WIDTH;
7340 hdi.cxy = cx;
7341 TRACE("hdi.cxy=%d\n", hdi.cxy);
7342 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7345 /***
7346 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7349 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7351 HDC hdc_wnd, hdc;
7352 HBITMAP hbm_im, hbm_mask, hbm_orig;
7353 RECT rc;
7354 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7355 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7356 HIMAGELIST himl;
7358 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7359 ILC_COLOR | ILC_MASK, 2, 2);
7360 hdc_wnd = GetDC(infoPtr->hwndSelf);
7361 hdc = CreateCompatibleDC(hdc_wnd);
7362 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7363 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7364 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7366 rc.left = rc.top = 0;
7367 rc.right = GetSystemMetrics(SM_CXSMICON);
7368 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7370 hbm_orig = SelectObject(hdc, hbm_mask);
7371 FillRect(hdc, &rc, hbr_white);
7372 InflateRect(&rc, -2, -2);
7373 FillRect(hdc, &rc, hbr_black);
7375 SelectObject(hdc, hbm_im);
7376 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7377 SelectObject(hdc, hbm_orig);
7378 ImageList_Add(himl, hbm_im, hbm_mask);
7380 SelectObject(hdc, hbm_im);
7381 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7382 SelectObject(hdc, hbm_orig);
7383 ImageList_Add(himl, hbm_im, hbm_mask);
7385 DeleteObject(hbm_mask);
7386 DeleteObject(hbm_im);
7387 DeleteDC(hdc);
7389 return himl;
7392 /***
7393 * DESCRIPTION:
7394 * Sets the extended listview style.
7396 * PARAMETERS:
7397 * [I] infoPtr : valid pointer to the listview structure
7398 * [I] dwMask : mask
7399 * [I] dwStyle : style
7401 * RETURN:
7402 * SUCCESS : previous style
7403 * FAILURE : 0
7405 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7407 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7409 /* set new style */
7410 if (dwMask)
7411 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7412 else
7413 infoPtr->dwLvExStyle = dwExStyle;
7415 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7417 HIMAGELIST himl = 0;
7418 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7420 LVITEMW item;
7421 item.mask = LVIF_STATE;
7422 item.stateMask = LVIS_STATEIMAGEMASK;
7423 item.state = INDEXTOSTATEIMAGEMASK(1);
7424 LISTVIEW_SetItemState(infoPtr, -1, &item);
7426 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7428 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7431 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7433 DWORD dwStyle;
7435 /* if not already created */
7436 LISTVIEW_CreateHeader(infoPtr);
7438 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7439 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7440 dwStyle |= HDS_DRAGDROP;
7441 else
7442 dwStyle &= ~HDS_DRAGDROP;
7443 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7446 /* GRIDLINES adds decoration at top so changes sizes */
7447 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7449 LISTVIEW_UpdateSize(infoPtr);
7453 LISTVIEW_InvalidateList(infoPtr);
7454 return dwOldExStyle;
7457 /***
7458 * DESCRIPTION:
7459 * Sets the new hot cursor used during hot tracking and hover selection.
7461 * PARAMETER(S):
7462 * [I] infoPtr : valid pointer to the listview structure
7463 * [I] hCursor : the new hot cursor handle
7465 * RETURN:
7466 * Returns the previous hot cursor
7468 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7470 HCURSOR oldCursor = infoPtr->hHotCursor;
7472 infoPtr->hHotCursor = hCursor;
7474 return oldCursor;
7478 /***
7479 * DESCRIPTION:
7480 * Sets the hot item index.
7482 * PARAMETERS:
7483 * [I] infoPtr : valid pointer to the listview structure
7484 * [I] iIndex : index
7486 * RETURN:
7487 * SUCCESS : previous hot item index
7488 * FAILURE : -1 (no hot item)
7490 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7492 INT iOldIndex = infoPtr->nHotItem;
7494 infoPtr->nHotItem = iIndex;
7496 return iOldIndex;
7500 /***
7501 * DESCRIPTION:
7502 * Sets the amount of time the cursor must hover over an item before it is selected.
7504 * PARAMETER(S):
7505 * [I] infoPtr : valid pointer to the listview structure
7506 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7508 * RETURN:
7509 * Returns the previous hover time
7511 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7513 DWORD oldHoverTime = infoPtr->dwHoverTime;
7515 infoPtr->dwHoverTime = dwHoverTime;
7517 return oldHoverTime;
7520 /***
7521 * DESCRIPTION:
7522 * Sets spacing for icons of LVS_ICON style.
7524 * PARAMETER(S):
7525 * [I] infoPtr : valid pointer to the listview structure
7526 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7527 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7529 * RETURN:
7530 * MAKELONG(oldcx, oldcy)
7532 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7534 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7536 TRACE("requested=(%d,%d)\n", cx, cy);
7538 /* this is supported only for LVS_ICON style */
7539 if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
7541 /* set to defaults, if instructed to */
7542 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7543 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7545 /* if 0 then compute width
7546 * FIXME: Should scan each item and determine max width of
7547 * icon or label, then make that the width */
7548 if (cx == 0)
7549 cx = infoPtr->iconSpacing.cx;
7551 /* if 0 then compute height */
7552 if (cy == 0)
7553 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7554 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7557 infoPtr->iconSpacing.cx = cx;
7558 infoPtr->iconSpacing.cy = cy;
7560 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7561 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7562 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7563 infoPtr->ntmHeight);
7565 /* these depend on the iconSpacing */
7566 LISTVIEW_UpdateItemSize(infoPtr);
7568 return oldspacing;
7571 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7573 INT cx, cy;
7575 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7577 size->cx = cx;
7578 size->cy = cy;
7580 else
7582 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7583 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7587 /***
7588 * DESCRIPTION:
7589 * Sets image lists.
7591 * PARAMETER(S):
7592 * [I] infoPtr : valid pointer to the listview structure
7593 * [I] nType : image list type
7594 * [I] himl : image list handle
7596 * RETURN:
7597 * SUCCESS : old image list
7598 * FAILURE : NULL
7600 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7602 INT oldHeight = infoPtr->nItemHeight;
7603 HIMAGELIST himlOld = 0;
7605 TRACE("(nType=%d, himl=%p\n", nType, himl);
7607 switch (nType)
7609 case LVSIL_NORMAL:
7610 himlOld = infoPtr->himlNormal;
7611 infoPtr->himlNormal = himl;
7612 if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7613 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7614 break;
7616 case LVSIL_SMALL:
7617 himlOld = infoPtr->himlSmall;
7618 infoPtr->himlSmall = himl;
7619 if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7620 break;
7622 case LVSIL_STATE:
7623 himlOld = infoPtr->himlState;
7624 infoPtr->himlState = himl;
7625 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7626 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7627 break;
7629 default:
7630 ERR("Unknown icon type=%d\n", nType);
7631 return NULL;
7634 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7635 if (infoPtr->nItemHeight != oldHeight)
7636 LISTVIEW_UpdateScroll(infoPtr);
7638 return himlOld;
7641 /***
7642 * DESCRIPTION:
7643 * Preallocates memory (does *not* set the actual count of items !)
7645 * PARAMETER(S):
7646 * [I] infoPtr : valid pointer to the listview structure
7647 * [I] nItems : item count (projected number of items to allocate)
7648 * [I] dwFlags : update flags
7650 * RETURN:
7651 * SUCCESS : TRUE
7652 * FAILURE : FALSE
7654 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7656 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7658 if (infoPtr->dwStyle & LVS_OWNERDATA)
7660 INT nOldCount = infoPtr->nItemCount;
7662 if (nItems < nOldCount)
7664 RANGE range = { nItems, nOldCount };
7665 ranges_del(infoPtr->selectionRanges, range);
7666 if (infoPtr->nFocusedItem >= nItems)
7668 LISTVIEW_SetItemFocus(infoPtr, -1);
7669 SetRectEmpty(&infoPtr->rcFocus);
7673 infoPtr->nItemCount = nItems;
7674 LISTVIEW_UpdateScroll(infoPtr);
7676 /* the flags are valid only in ownerdata report and list modes */
7677 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
7679 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7680 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7682 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7683 LISTVIEW_InvalidateList(infoPtr);
7684 else
7686 INT nFrom, nTo;
7687 POINT Origin;
7688 RECT rcErase;
7690 LISTVIEW_GetOrigin(infoPtr, &Origin);
7691 nFrom = min(nOldCount, nItems);
7692 nTo = max(nOldCount, nItems);
7694 if (infoPtr->uView == LV_VIEW_DETAILS)
7696 rcErase.left = 0;
7697 rcErase.top = nFrom * infoPtr->nItemHeight;
7698 rcErase.right = infoPtr->nItemWidth;
7699 rcErase.bottom = nTo * infoPtr->nItemHeight;
7700 OffsetRect(&rcErase, Origin.x, Origin.y);
7701 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7702 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7704 else /* LV_VIEW_LIST */
7706 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7708 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7709 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7710 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7711 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7712 OffsetRect(&rcErase, Origin.x, Origin.y);
7713 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7714 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7716 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7717 rcErase.top = 0;
7718 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7719 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7720 OffsetRect(&rcErase, Origin.x, Origin.y);
7721 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7722 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7726 else
7728 /* According to MSDN for non-LVS_OWNERDATA this is just
7729 * a performance issue. The control allocates its internal
7730 * data structures for the number of items specified. It
7731 * cuts down on the number of memory allocations. Therefore
7732 * we will just issue a WARN here
7734 WARN("for non-ownerdata performance option not implemented.\n");
7737 return TRUE;
7740 /***
7741 * DESCRIPTION:
7742 * Sets the position of an item.
7744 * PARAMETER(S):
7745 * [I] infoPtr : valid pointer to the listview structure
7746 * [I] nItem : item index
7747 * [I] pt : coordinate
7749 * RETURN:
7750 * SUCCESS : TRUE
7751 * FAILURE : FALSE
7753 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7755 POINT Origin;
7757 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7759 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7760 !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
7762 LISTVIEW_GetOrigin(infoPtr, &Origin);
7764 /* This point value seems to be an undocumented feature.
7765 * The best guess is that it means either at the origin,
7766 * or at true beginning of the list. I will assume the origin. */
7767 if ((pt.x == -1) && (pt.y == -1))
7768 pt = Origin;
7770 if (infoPtr->uView == LV_VIEW_ICON)
7772 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7773 pt.y -= ICON_TOP_PADDING;
7775 pt.x -= Origin.x;
7776 pt.y -= Origin.y;
7778 infoPtr->bAutoarrange = FALSE;
7780 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7783 /***
7784 * DESCRIPTION:
7785 * Sets the state of one or many items.
7787 * PARAMETER(S):
7788 * [I] infoPtr : valid pointer to the listview structure
7789 * [I] nItem : item index
7790 * [I] lpLVItem : item or subitem info
7792 * RETURN:
7793 * SUCCESS : TRUE
7794 * FAILURE : FALSE
7796 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7798 BOOL bResult = TRUE;
7799 LVITEMW lvItem;
7801 lvItem.iItem = nItem;
7802 lvItem.iSubItem = 0;
7803 lvItem.mask = LVIF_STATE;
7804 lvItem.state = lpLVItem->state;
7805 lvItem.stateMask = lpLVItem->stateMask;
7806 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7808 if (nItem == -1)
7810 /* select all isn't allowed in LVS_SINGLESEL */
7811 if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
7812 return FALSE;
7814 /* apply to all items */
7815 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7816 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7818 else
7819 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7822 * Update selection mark
7824 * Investigation on windows 2k showed that selection mark was updated
7825 * whenever a new selection was made, but if the selected item was
7826 * unselected it was not updated.
7828 * we are probably still not 100% accurate, but this at least sets the
7829 * proper selection mark when it is needed
7832 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7833 (infoPtr->nSelectionMark == -1))
7835 int i;
7836 for (i = 0; i < infoPtr->nItemCount; i++)
7838 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7840 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7842 infoPtr->nSelectionMark = i;
7843 break;
7846 else if (ranges_contain(infoPtr->selectionRanges, i))
7848 infoPtr->nSelectionMark = i;
7849 break;
7854 return bResult;
7857 /***
7858 * DESCRIPTION:
7859 * Sets the text of an item or subitem.
7861 * PARAMETER(S):
7862 * [I] hwnd : window handle
7863 * [I] nItem : item index
7864 * [I] lpLVItem : item or subitem info
7865 * [I] isW : TRUE if input is Unicode
7867 * RETURN:
7868 * SUCCESS : TRUE
7869 * FAILURE : FALSE
7871 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7873 LVITEMW lvItem;
7875 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7877 lvItem.iItem = nItem;
7878 lvItem.iSubItem = lpLVItem->iSubItem;
7879 lvItem.mask = LVIF_TEXT;
7880 lvItem.pszText = lpLVItem->pszText;
7881 lvItem.cchTextMax = lpLVItem->cchTextMax;
7883 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7885 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7888 /***
7889 * DESCRIPTION:
7890 * Set item index that marks the start of a multiple selection.
7892 * PARAMETER(S):
7893 * [I] infoPtr : valid pointer to the listview structure
7894 * [I] nIndex : index
7896 * RETURN:
7897 * Index number or -1 if there is no selection mark.
7899 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7901 INT nOldIndex = infoPtr->nSelectionMark;
7903 TRACE("(nIndex=%d)\n", nIndex);
7905 infoPtr->nSelectionMark = nIndex;
7907 return nOldIndex;
7910 /***
7911 * DESCRIPTION:
7912 * Sets the text background color.
7914 * PARAMETER(S):
7915 * [I] infoPtr : valid pointer to the listview structure
7916 * [I] clrTextBk : text background color
7918 * RETURN:
7919 * SUCCESS : TRUE
7920 * FAILURE : FALSE
7922 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7924 TRACE("(clrTextBk=%x)\n", clrTextBk);
7926 if (infoPtr->clrTextBk != clrTextBk)
7928 infoPtr->clrTextBk = clrTextBk;
7929 LISTVIEW_InvalidateList(infoPtr);
7932 return TRUE;
7935 /***
7936 * DESCRIPTION:
7937 * Sets the text foreground color.
7939 * PARAMETER(S):
7940 * [I] infoPtr : valid pointer to the listview structure
7941 * [I] clrText : text color
7943 * RETURN:
7944 * SUCCESS : TRUE
7945 * FAILURE : FALSE
7947 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7949 TRACE("(clrText=%x)\n", clrText);
7951 if (infoPtr->clrText != clrText)
7953 infoPtr->clrText = clrText;
7954 LISTVIEW_InvalidateList(infoPtr);
7957 return TRUE;
7960 /***
7961 * DESCRIPTION:
7962 * Sets new ToolTip window to ListView control.
7964 * PARAMETER(S):
7965 * [I] infoPtr : valid pointer to the listview structure
7966 * [I] hwndNewToolTip : handle to new ToolTip
7968 * RETURN:
7969 * old tool tip
7971 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7973 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7974 infoPtr->hwndToolTip = hwndNewToolTip;
7975 return hwndOldToolTip;
7979 * DESCRIPTION:
7980 * sets the Unicode character format flag for the control
7981 * PARAMETER(S):
7982 * [I] infoPtr :valid pointer to the listview structure
7983 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7985 * RETURN:
7986 * Old Unicode Format
7988 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7990 SHORT rc = infoPtr->notifyFormat;
7991 infoPtr->notifyFormat = (fUnicode) ? NFR_UNICODE : NFR_ANSI;
7992 return rc == NFR_UNICODE;
7996 * DESCRIPTION:
7997 * sets the control view mode
7998 * PARAMETER(S):
7999 * [I] infoPtr :valid pointer to the listview structure
8000 * [I] nView :new view mode value
8002 * RETURN:
8003 * SUCCESS: 1
8004 * FAILURE: -1
8006 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
8008 SIZE oldIconSize = infoPtr->iconSize;
8009 HIMAGELIST himl;
8011 if (infoPtr->uView == nView) return 1;
8013 if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
8014 if (nView == LV_VIEW_TILE)
8016 FIXME("View LV_VIEW_TILE unimplemented\n");
8017 return -1;
8020 infoPtr->uView = nView;
8022 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8023 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8025 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8026 SetRectEmpty(&infoPtr->rcFocus);
8028 himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8029 set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
8031 switch (nView)
8033 case LV_VIEW_ICON:
8034 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8036 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
8037 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8038 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8040 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8041 break;
8042 case LV_VIEW_SMALLICON:
8043 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8044 break;
8045 case LV_VIEW_DETAILS:
8047 HDLAYOUT hl;
8048 WINDOWPOS wp;
8050 LISTVIEW_CreateHeader( infoPtr );
8052 hl.prc = &infoPtr->rcList;
8053 hl.pwpos = &wp;
8054 SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
8055 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8056 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
8057 break;
8059 case LV_VIEW_LIST:
8060 break;
8063 LISTVIEW_UpdateItemSize(infoPtr);
8064 LISTVIEW_UpdateSize(infoPtr);
8065 LISTVIEW_UpdateScroll(infoPtr);
8066 LISTVIEW_InvalidateList(infoPtr);
8068 TRACE("nView=%d\n", nView);
8070 return 1;
8073 /* LISTVIEW_SetWorkAreas */
8075 /***
8076 * DESCRIPTION:
8077 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
8079 * PARAMETER(S):
8080 * [I] first : pointer to first ITEM_INFO to compare
8081 * [I] second : pointer to second ITEM_INFO to compare
8082 * [I] lParam : HWND of control
8084 * RETURN:
8085 * if first comes before second : negative
8086 * if first comes after second : positive
8087 * if first and second are equivalent : zero
8089 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8091 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8092 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
8093 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
8095 /* Forward the call to the client defined callback */
8096 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8099 /***
8100 * DESCRIPTION:
8101 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
8103 * PARAMETER(S):
8104 * [I] first : pointer to first ITEM_INFO to compare
8105 * [I] second : pointer to second ITEM_INFO to compare
8106 * [I] lParam : HWND of control
8108 * RETURN:
8109 * if first comes before second : negative
8110 * if first comes after second : positive
8111 * if first and second are equivalent : zero
8113 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
8115 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8116 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
8117 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
8119 /* Forward the call to the client defined callback */
8120 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
8123 /***
8124 * DESCRIPTION:
8125 * Sorts the listview items.
8127 * PARAMETER(S):
8128 * [I] infoPtr : valid pointer to the listview structure
8129 * [I] pfnCompare : application-defined value
8130 * [I] lParamSort : pointer to comparison callback
8131 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
8133 * RETURN:
8134 * SUCCESS : TRUE
8135 * FAILURE : FALSE
8137 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
8138 LPARAM lParamSort, BOOL IsEx)
8140 HDPA hdpaSubItems;
8141 ITEM_INFO *lpItem;
8142 LPVOID selectionMarkItem = NULL;
8143 LPVOID focusedItem = NULL;
8144 int i;
8146 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
8148 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
8150 if (!pfnCompare) return FALSE;
8151 if (!infoPtr->hdpaItems) return FALSE;
8153 /* if there are 0 or 1 items, there is no need to sort */
8154 if (infoPtr->nItemCount < 2) return TRUE;
8156 /* clear selection */
8157 ranges_clear(infoPtr->selectionRanges);
8159 /* save selection mark and focused item */
8160 if (infoPtr->nSelectionMark >= 0)
8161 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
8162 if (infoPtr->nFocusedItem >= 0)
8163 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
8165 infoPtr->pfnCompare = pfnCompare;
8166 infoPtr->lParamSort = lParamSort;
8167 if (IsEx)
8168 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
8169 else
8170 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
8172 /* restore selection ranges */
8173 for (i=0; i < infoPtr->nItemCount; i++)
8175 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
8176 lpItem = DPA_GetPtr(hdpaSubItems, 0);
8178 if (lpItem->state & LVIS_SELECTED)
8179 ranges_additem(infoPtr->selectionRanges, i);
8181 /* restore selection mark and focused item */
8182 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8183 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8185 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8187 /* refresh the display */
8188 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON)
8189 LISTVIEW_InvalidateList(infoPtr);
8191 return TRUE;
8194 /***
8195 * DESCRIPTION:
8196 * Update theme handle after a theme change.
8198 * PARAMETER(S):
8199 * [I] infoPtr : valid pointer to the listview structure
8201 * RETURN:
8202 * SUCCESS : 0
8203 * FAILURE : something else
8205 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8207 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8208 CloseThemeData(theme);
8209 OpenThemeData(infoPtr->hwndSelf, themeClass);
8210 return 0;
8213 /***
8214 * DESCRIPTION:
8215 * Updates an items or rearranges the listview control.
8217 * PARAMETER(S):
8218 * [I] infoPtr : valid pointer to the listview structure
8219 * [I] nItem : item index
8221 * RETURN:
8222 * SUCCESS : TRUE
8223 * FAILURE : FALSE
8225 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8227 TRACE("(nItem=%d)\n", nItem);
8229 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8231 /* rearrange with default alignment style */
8232 if (is_autoarrange(infoPtr))
8233 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8234 else
8235 LISTVIEW_InvalidateItem(infoPtr, nItem);
8237 return TRUE;
8240 /***
8241 * DESCRIPTION:
8242 * Draw the track line at the place defined in the infoPtr structure.
8243 * The line is drawn with a XOR pen so drawing the line for the second time
8244 * in the same place erases the line.
8246 * PARAMETER(S):
8247 * [I] infoPtr : valid pointer to the listview structure
8249 * RETURN:
8250 * SUCCESS : TRUE
8251 * FAILURE : FALSE
8253 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8255 HPEN hOldPen;
8256 HDC hdc;
8257 INT oldROP;
8259 if (infoPtr->xTrackLine == -1)
8260 return FALSE;
8262 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8263 return FALSE;
8264 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8265 oldROP = SetROP2(hdc, R2_XORPEN);
8266 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8267 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8268 SetROP2(hdc, oldROP);
8269 SelectObject(hdc, hOldPen);
8270 ReleaseDC(infoPtr->hwndSelf, hdc);
8271 return TRUE;
8274 /***
8275 * DESCRIPTION:
8276 * Called when an edit control should be displayed. This function is called after
8277 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8279 * PARAMETER(S):
8280 * [I] hwnd : Handle to the listview
8281 * [I] uMsg : WM_TIMER (ignored)
8282 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8283 * [I] dwTimer : The elapsed time (ignored)
8285 * RETURN:
8286 * None.
8288 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8290 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8291 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8293 KillTimer(hwnd, idEvent);
8294 editItem->fEnabled = FALSE;
8295 /* check if the item is still selected */
8296 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8297 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8300 /***
8301 * DESCRIPTION:
8302 * Creates the listview control - the WM_NCCREATE phase.
8304 * PARAMETER(S):
8305 * [I] hwnd : window handle
8306 * [I] lpcs : the create parameters
8308 * RETURN:
8309 * Success: TRUE
8310 * Failure: FALSE
8312 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8314 LISTVIEW_INFO *infoPtr;
8315 LOGFONTW logFont;
8317 TRACE("(lpcs=%p)\n", lpcs);
8319 /* initialize info pointer */
8320 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8321 if (!infoPtr) return FALSE;
8323 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8325 infoPtr->hwndSelf = hwnd;
8326 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8327 map_style_view(infoPtr);
8328 /* determine the type of structures to use */
8329 infoPtr->hwndNotify = lpcs->hwndParent;
8330 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8332 /* initialize color information */
8333 infoPtr->clrBk = CLR_NONE;
8334 infoPtr->clrText = CLR_DEFAULT;
8335 infoPtr->clrTextBk = CLR_DEFAULT;
8336 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8338 /* set default values */
8339 infoPtr->nFocusedItem = -1;
8340 infoPtr->nSelectionMark = -1;
8341 infoPtr->nHotItem = -1;
8342 infoPtr->bRedraw = TRUE;
8343 infoPtr->bNoItemMetrics = TRUE;
8344 infoPtr->bDoChangeNotify = TRUE;
8345 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8346 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8347 infoPtr->nEditLabelItem = -1;
8348 infoPtr->nLButtonDownItem = -1;
8349 infoPtr->dwHoverTime = -1; /* default system hover time */
8350 infoPtr->nMeasureItemHeight = 0;
8351 infoPtr->xTrackLine = -1; /* no track line */
8352 infoPtr->itemEdit.fEnabled = FALSE;
8353 infoPtr->iVersion = COMCTL32_VERSION;
8355 /* get default font (icon title) */
8356 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8357 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8358 infoPtr->hFont = infoPtr->hDefaultFont;
8359 LISTVIEW_SaveTextMetrics(infoPtr);
8361 /* allocate memory for the data structure */
8362 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8363 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8364 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8365 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8366 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8367 return TRUE;
8369 fail:
8370 DestroyWindow(infoPtr->hwndHeader);
8371 ranges_destroy(infoPtr->selectionRanges);
8372 DPA_Destroy(infoPtr->hdpaItems);
8373 DPA_Destroy(infoPtr->hdpaPosX);
8374 DPA_Destroy(infoPtr->hdpaPosY);
8375 DPA_Destroy(infoPtr->hdpaColumns);
8376 Free(infoPtr);
8377 return FALSE;
8380 /***
8381 * DESCRIPTION:
8382 * Creates the listview control - the WM_CREATE phase. Most of the data is
8383 * already set up in LISTVIEW_NCCreate
8385 * PARAMETER(S):
8386 * [I] hwnd : window handle
8387 * [I] lpcs : the create parameters
8389 * RETURN:
8390 * Success: 0
8391 * Failure: -1
8393 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8395 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8397 TRACE("(lpcs=%p)\n", lpcs);
8399 infoPtr->dwStyle = lpcs->style;
8400 map_style_view(infoPtr);
8402 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8403 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8404 /* on error defaulting to ANSI notifications */
8405 if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
8407 if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
8409 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
8411 else
8412 infoPtr->hwndHeader = 0;
8414 /* init item size to avoid division by 0 */
8415 LISTVIEW_UpdateItemSize (infoPtr);
8417 if (infoPtr->uView == LV_VIEW_DETAILS)
8419 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8421 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8423 LISTVIEW_UpdateScroll(infoPtr);
8426 OpenThemeData(hwnd, themeClass);
8428 /* initialize the icon sizes */
8429 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
8430 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8431 return 0;
8434 /***
8435 * DESCRIPTION:
8436 * Destroys the listview control.
8438 * PARAMETER(S):
8439 * [I] infoPtr : valid pointer to the listview structure
8441 * RETURN:
8442 * Success: 0
8443 * Failure: -1
8445 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8447 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8448 CloseThemeData(theme);
8449 return 0;
8452 /***
8453 * DESCRIPTION:
8454 * Enables the listview control.
8456 * PARAMETER(S):
8457 * [I] infoPtr : valid pointer to the listview structure
8458 * [I] bEnable : specifies whether to enable or disable the window
8460 * RETURN:
8461 * SUCCESS : TRUE
8462 * FAILURE : FALSE
8464 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8466 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8467 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8468 return TRUE;
8471 /***
8472 * DESCRIPTION:
8473 * Erases the background of the listview control.
8475 * PARAMETER(S):
8476 * [I] infoPtr : valid pointer to the listview structure
8477 * [I] hdc : device context handle
8479 * RETURN:
8480 * SUCCESS : TRUE
8481 * FAILURE : FALSE
8483 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8485 RECT rc;
8487 TRACE("(hdc=%p)\n", hdc);
8489 if (!GetClipBox(hdc, &rc)) return FALSE;
8491 if (infoPtr->clrBk == CLR_NONE)
8492 return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
8494 /* for double buffered controls we need to do this during refresh */
8495 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8497 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8501 /***
8502 * DESCRIPTION:
8503 * Helper function for LISTVIEW_[HV]Scroll *only*.
8504 * Performs vertical/horizontal scrolling by a give amount.
8506 * PARAMETER(S):
8507 * [I] infoPtr : valid pointer to the listview structure
8508 * [I] dx : amount of horizontal scroll
8509 * [I] dy : amount of vertical scroll
8511 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8513 /* now we can scroll the list */
8514 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8515 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8516 /* if we have focus, adjust rect */
8517 OffsetRect(&infoPtr->rcFocus, dx, dy);
8518 UpdateWindow(infoPtr->hwndSelf);
8521 /***
8522 * DESCRIPTION:
8523 * Performs vertical scrolling.
8525 * PARAMETER(S):
8526 * [I] infoPtr : valid pointer to the listview structure
8527 * [I] nScrollCode : scroll code
8528 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8529 * [I] hScrollWnd : scrollbar control window handle
8531 * RETURN:
8532 * Zero
8534 * NOTES:
8535 * SB_LINEUP/SB_LINEDOWN:
8536 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8537 * for LVS_REPORT is 1 line
8538 * for LVS_LIST cannot occur
8541 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8542 INT nScrollDiff, HWND hScrollWnd)
8544 INT nOldScrollPos, nNewScrollPos;
8545 SCROLLINFO scrollInfo;
8546 BOOL is_an_icon;
8548 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8549 debugscrollcode(nScrollCode), nScrollDiff);
8551 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8553 scrollInfo.cbSize = sizeof(SCROLLINFO);
8554 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8556 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
8558 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8560 nOldScrollPos = scrollInfo.nPos;
8561 switch (nScrollCode)
8563 case SB_INTERNAL:
8564 break;
8566 case SB_LINEUP:
8567 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8568 break;
8570 case SB_LINEDOWN:
8571 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8572 break;
8574 case SB_PAGEUP:
8575 nScrollDiff = -scrollInfo.nPage;
8576 break;
8578 case SB_PAGEDOWN:
8579 nScrollDiff = scrollInfo.nPage;
8580 break;
8582 case SB_THUMBPOSITION:
8583 case SB_THUMBTRACK:
8584 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8585 break;
8587 default:
8588 nScrollDiff = 0;
8591 /* quit right away if pos isn't changing */
8592 if (nScrollDiff == 0) return 0;
8594 /* calculate new position, and handle overflows */
8595 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8596 if (nScrollDiff > 0) {
8597 if (nNewScrollPos < nOldScrollPos ||
8598 nNewScrollPos > scrollInfo.nMax)
8599 nNewScrollPos = scrollInfo.nMax;
8600 } else {
8601 if (nNewScrollPos > nOldScrollPos ||
8602 nNewScrollPos < scrollInfo.nMin)
8603 nNewScrollPos = scrollInfo.nMin;
8606 /* set the new position, and reread in case it changed */
8607 scrollInfo.fMask = SIF_POS;
8608 scrollInfo.nPos = nNewScrollPos;
8609 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8611 /* carry on only if it really changed */
8612 if (nNewScrollPos == nOldScrollPos) return 0;
8614 /* now adjust to client coordinates */
8615 nScrollDiff = nOldScrollPos - nNewScrollPos;
8616 if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
8618 /* and scroll the window */
8619 scroll_list(infoPtr, 0, nScrollDiff);
8621 return 0;
8624 /***
8625 * DESCRIPTION:
8626 * Performs horizontal scrolling.
8628 * PARAMETER(S):
8629 * [I] infoPtr : valid pointer to the listview structure
8630 * [I] nScrollCode : scroll code
8631 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8632 * [I] hScrollWnd : scrollbar control window handle
8634 * RETURN:
8635 * Zero
8637 * NOTES:
8638 * SB_LINELEFT/SB_LINERIGHT:
8639 * for LVS_ICON, LVS_SMALLICON 1 pixel
8640 * for LVS_REPORT is 1 pixel
8641 * for LVS_LIST is 1 column --> which is a 1 because the
8642 * scroll is based on columns not pixels
8645 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8646 INT nScrollDiff, HWND hScrollWnd)
8648 INT nOldScrollPos, nNewScrollPos;
8649 SCROLLINFO scrollInfo;
8651 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8652 debugscrollcode(nScrollCode), nScrollDiff);
8654 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8656 scrollInfo.cbSize = sizeof(SCROLLINFO);
8657 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8659 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8661 nOldScrollPos = scrollInfo.nPos;
8663 switch (nScrollCode)
8665 case SB_INTERNAL:
8666 break;
8668 case SB_LINELEFT:
8669 nScrollDiff = -1;
8670 break;
8672 case SB_LINERIGHT:
8673 nScrollDiff = 1;
8674 break;
8676 case SB_PAGELEFT:
8677 nScrollDiff = -scrollInfo.nPage;
8678 break;
8680 case SB_PAGERIGHT:
8681 nScrollDiff = scrollInfo.nPage;
8682 break;
8684 case SB_THUMBPOSITION:
8685 case SB_THUMBTRACK:
8686 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8687 break;
8689 default:
8690 nScrollDiff = 0;
8693 /* quit right away if pos isn't changing */
8694 if (nScrollDiff == 0) return 0;
8696 /* calculate new position, and handle overflows */
8697 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8698 if (nScrollDiff > 0) {
8699 if (nNewScrollPos < nOldScrollPos ||
8700 nNewScrollPos > scrollInfo.nMax)
8701 nNewScrollPos = scrollInfo.nMax;
8702 } else {
8703 if (nNewScrollPos > nOldScrollPos ||
8704 nNewScrollPos < scrollInfo.nMin)
8705 nNewScrollPos = scrollInfo.nMin;
8708 /* set the new position, and reread in case it changed */
8709 scrollInfo.fMask = SIF_POS;
8710 scrollInfo.nPos = nNewScrollPos;
8711 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8713 /* carry on only if it really changed */
8714 if (nNewScrollPos == nOldScrollPos) return 0;
8716 if (infoPtr->uView == LV_VIEW_DETAILS)
8717 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8719 /* now adjust to client coordinates */
8720 nScrollDiff = nOldScrollPos - nNewScrollPos;
8721 if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
8723 /* and scroll the window */
8724 scroll_list(infoPtr, nScrollDiff, 0);
8726 return 0;
8729 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8731 INT gcWheelDelta = 0;
8732 INT pulScrollLines = 3;
8733 SCROLLINFO scrollInfo;
8735 TRACE("(wheelDelta=%d)\n", wheelDelta);
8737 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8738 gcWheelDelta -= wheelDelta;
8740 scrollInfo.cbSize = sizeof(SCROLLINFO);
8741 scrollInfo.fMask = SIF_POS;
8743 switch(infoPtr->uView)
8745 case LV_VIEW_ICON:
8746 case LV_VIEW_SMALLICON:
8748 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8749 * should be fixed in the future.
8751 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8752 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8753 break;
8755 case LV_VIEW_DETAILS:
8756 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8758 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8759 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8760 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8762 break;
8764 case LV_VIEW_LIST:
8765 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8766 break;
8768 return 0;
8771 /***
8772 * DESCRIPTION:
8773 * ???
8775 * PARAMETER(S):
8776 * [I] infoPtr : valid pointer to the listview structure
8777 * [I] nVirtualKey : virtual key
8778 * [I] lKeyData : key data
8780 * RETURN:
8781 * Zero
8783 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8785 HWND hwndSelf = infoPtr->hwndSelf;
8786 INT nItem = -1;
8787 NMLVKEYDOWN nmKeyDown;
8789 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8791 /* send LVN_KEYDOWN notification */
8792 nmKeyDown.wVKey = nVirtualKey;
8793 nmKeyDown.flags = 0;
8794 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8795 if (!IsWindow(hwndSelf))
8796 return 0;
8798 switch (nVirtualKey)
8800 case VK_SPACE:
8801 nItem = infoPtr->nFocusedItem;
8802 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8803 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8804 break;
8806 case VK_RETURN:
8807 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8809 if (!notify(infoPtr, NM_RETURN)) return 0;
8810 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8812 break;
8814 case VK_HOME:
8815 if (infoPtr->nItemCount > 0)
8816 nItem = 0;
8817 break;
8819 case VK_END:
8820 if (infoPtr->nItemCount > 0)
8821 nItem = infoPtr->nItemCount - 1;
8822 break;
8824 case VK_LEFT:
8825 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
8826 break;
8828 case VK_UP:
8829 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
8830 break;
8832 case VK_RIGHT:
8833 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
8834 break;
8836 case VK_DOWN:
8837 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
8838 break;
8840 case VK_PRIOR:
8841 if (infoPtr->uView == LV_VIEW_DETAILS)
8843 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8844 if (infoPtr->nFocusedItem == topidx)
8845 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8846 else
8847 nItem = topidx;
8849 else
8850 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8851 * LISTVIEW_GetCountPerRow(infoPtr);
8852 if(nItem < 0) nItem = 0;
8853 break;
8855 case VK_NEXT:
8856 if (infoPtr->uView == LV_VIEW_DETAILS)
8858 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8859 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8860 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8861 nItem = infoPtr->nFocusedItem + cnt - 1;
8862 else
8863 nItem = topidx + cnt - 1;
8865 else
8866 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8867 * LISTVIEW_GetCountPerRow(infoPtr);
8868 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8869 break;
8872 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8873 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8875 return 0;
8878 /***
8879 * DESCRIPTION:
8880 * Kills the focus.
8882 * PARAMETER(S):
8883 * [I] infoPtr : valid pointer to the listview structure
8885 * RETURN:
8886 * Zero
8888 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8890 TRACE("()\n");
8892 /* if we did not have the focus, there's nothing to do */
8893 if (!infoPtr->bFocus) return 0;
8895 /* send NM_KILLFOCUS notification */
8896 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8898 /* if we have a focus rectangle, get rid of it */
8899 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8901 /* set window focus flag */
8902 infoPtr->bFocus = FALSE;
8904 /* invalidate the selected items before resetting focus flag */
8905 LISTVIEW_InvalidateSelectedItems(infoPtr);
8907 return 0;
8910 /***
8911 * DESCRIPTION:
8912 * Processes double click messages (left mouse button).
8914 * PARAMETER(S):
8915 * [I] infoPtr : valid pointer to the listview structure
8916 * [I] wKey : key flag
8917 * [I] x,y : mouse coordinate
8919 * RETURN:
8920 * Zero
8922 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8924 LVHITTESTINFO htInfo;
8926 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8928 /* Cancel the item edition if any */
8929 if (infoPtr->itemEdit.fEnabled)
8931 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8932 infoPtr->itemEdit.fEnabled = FALSE;
8935 /* send NM_RELEASEDCAPTURE notification */
8936 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8938 htInfo.pt.x = x;
8939 htInfo.pt.y = y;
8941 /* send NM_DBLCLK notification */
8942 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8943 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8945 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8946 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8948 return 0;
8951 /***
8952 * DESCRIPTION:
8953 * Processes mouse down messages (left mouse button).
8955 * PARAMETERS:
8956 * infoPtr [I ] valid pointer to the listview structure
8957 * wKey [I ] key flag
8958 * x,y [I ] mouse coordinate
8960 * RETURN:
8961 * Zero
8963 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8965 LVHITTESTINFO lvHitTestInfo;
8966 static BOOL bGroupSelect = TRUE;
8967 POINT pt = { x, y };
8968 INT nItem;
8970 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8972 /* send NM_RELEASEDCAPTURE notification */
8973 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8975 /* set left button down flag and record the click position */
8976 infoPtr->bLButtonDown = TRUE;
8977 infoPtr->ptClickPos = pt;
8978 infoPtr->bDragging = FALSE;
8980 lvHitTestInfo.pt.x = x;
8981 lvHitTestInfo.pt.y = y;
8983 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8984 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8985 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8987 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8989 toggle_checkbox_state(infoPtr, nItem);
8990 return 0;
8993 if (infoPtr->dwStyle & LVS_SINGLESEL)
8995 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8996 infoPtr->nEditLabelItem = nItem;
8997 else
8998 LISTVIEW_SetSelection(infoPtr, nItem);
9000 else
9002 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
9004 if (bGroupSelect)
9006 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
9007 LISTVIEW_SetItemFocus(infoPtr, nItem);
9008 infoPtr->nSelectionMark = nItem;
9010 else
9012 LVITEMW item;
9014 item.state = LVIS_SELECTED | LVIS_FOCUSED;
9015 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9017 LISTVIEW_SetItemState(infoPtr,nItem,&item);
9018 infoPtr->nSelectionMark = nItem;
9021 else if (wKey & MK_CONTROL)
9023 LVITEMW item;
9025 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
9027 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
9028 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9029 LISTVIEW_SetItemState(infoPtr, nItem, &item);
9030 infoPtr->nSelectionMark = nItem;
9032 else if (wKey & MK_SHIFT)
9034 LISTVIEW_SetGroupSelection(infoPtr, nItem);
9036 else
9038 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9040 infoPtr->nEditLabelItem = nItem;
9041 infoPtr->nLButtonDownItem = nItem;
9043 LISTVIEW_SetItemFocus(infoPtr, nItem);
9045 else
9046 /* set selection (clears other pre-existing selections) */
9047 LISTVIEW_SetSelection(infoPtr, nItem);
9051 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
9052 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
9054 else
9056 /* remove all selections */
9057 if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
9058 LISTVIEW_DeselectAll(infoPtr);
9059 ReleaseCapture();
9062 return 0;
9065 /***
9066 * DESCRIPTION:
9067 * Processes mouse up messages (left mouse button).
9069 * PARAMETERS:
9070 * infoPtr [I ] valid pointer to the listview structure
9071 * wKey [I ] key flag
9072 * x,y [I ] mouse coordinate
9074 * RETURN:
9075 * Zero
9077 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9079 LVHITTESTINFO lvHitTestInfo;
9081 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9083 if (!infoPtr->bLButtonDown) return 0;
9085 lvHitTestInfo.pt.x = x;
9086 lvHitTestInfo.pt.y = y;
9088 /* send NM_CLICK notification */
9089 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9090 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
9092 /* set left button flag */
9093 infoPtr->bLButtonDown = FALSE;
9095 /* set a single selection, reset others */
9096 if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
9097 LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
9098 infoPtr->nLButtonDownItem = -1;
9100 if (infoPtr->bDragging)
9102 infoPtr->bDragging = FALSE;
9103 return 0;
9106 /* if we clicked on a selected item, edit the label */
9107 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
9109 /* we want to make sure the user doesn't want to do a double click. So we will
9110 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
9112 infoPtr->itemEdit.fEnabled = TRUE;
9113 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
9114 SetTimer(infoPtr->hwndSelf,
9115 (UINT_PTR)&infoPtr->itemEdit,
9116 GetDoubleClickTime(),
9117 LISTVIEW_DelayedEditItem);
9120 if (!infoPtr->bFocus)
9121 SetFocus(infoPtr->hwndSelf);
9123 return 0;
9126 /***
9127 * DESCRIPTION:
9128 * Destroys the listview control (called after WM_DESTROY).
9130 * PARAMETER(S):
9131 * [I] infoPtr : valid pointer to the listview structure
9133 * RETURN:
9134 * Zero
9136 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
9138 TRACE("()\n");
9140 /* delete all items */
9141 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9143 /* destroy data structure */
9144 DPA_Destroy(infoPtr->hdpaItems);
9145 DPA_Destroy(infoPtr->hdpaPosX);
9146 DPA_Destroy(infoPtr->hdpaPosY);
9147 DPA_Destroy(infoPtr->hdpaColumns);
9148 ranges_destroy(infoPtr->selectionRanges);
9150 /* destroy image lists */
9151 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
9153 if (infoPtr->himlNormal)
9154 ImageList_Destroy(infoPtr->himlNormal);
9155 if (infoPtr->himlSmall)
9156 ImageList_Destroy(infoPtr->himlSmall);
9157 if (infoPtr->himlState)
9158 ImageList_Destroy(infoPtr->himlState);
9161 /* destroy font, bkgnd brush */
9162 infoPtr->hFont = 0;
9163 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
9164 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
9166 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
9168 /* free listview info pointer*/
9169 Free(infoPtr);
9171 return 0;
9174 /***
9175 * DESCRIPTION:
9176 * Handles notifications from header.
9178 * PARAMETER(S):
9179 * [I] infoPtr : valid pointer to the listview structure
9180 * [I] nCtrlId : control identifier
9181 * [I] lpnmh : notification information
9183 * RETURN:
9184 * Zero
9186 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
9188 HWND hwndSelf = infoPtr->hwndSelf;
9190 TRACE("(lpnmh=%p)\n", lpnmh);
9192 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9194 switch (lpnmh->hdr.code)
9196 case HDN_TRACKW:
9197 case HDN_TRACKA:
9199 COLUMN_INFO *lpColumnInfo;
9200 POINT ptOrigin;
9201 INT x;
9203 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9204 break;
9206 /* remove the old line (if any) */
9207 LISTVIEW_DrawTrackLine(infoPtr);
9209 /* compute & draw the new line */
9210 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9211 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9212 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9213 infoPtr->xTrackLine = x + ptOrigin.x;
9214 LISTVIEW_DrawTrackLine(infoPtr);
9215 break;
9218 case HDN_ENDTRACKA:
9219 case HDN_ENDTRACKW:
9220 /* remove the track line (if any) */
9221 LISTVIEW_DrawTrackLine(infoPtr);
9222 infoPtr->xTrackLine = -1;
9223 break;
9225 case HDN_ENDDRAG:
9226 FIXME("Changing column order not implemented\n");
9227 return TRUE;
9229 case HDN_ITEMCHANGINGW:
9230 case HDN_ITEMCHANGINGA:
9231 return notify_forward_header(infoPtr, lpnmh);
9233 case HDN_ITEMCHANGEDW:
9234 case HDN_ITEMCHANGEDA:
9236 COLUMN_INFO *lpColumnInfo;
9237 INT dx, cxy;
9239 notify_forward_header(infoPtr, lpnmh);
9240 if (!IsWindow(hwndSelf))
9241 break;
9243 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9245 HDITEMW hdi;
9247 hdi.mask = HDI_WIDTH;
9248 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
9249 cxy = hdi.cxy;
9251 else
9252 cxy = lpnmh->pitem->cxy;
9254 /* determine how much we change since the last know position */
9255 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9256 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9257 if (dx != 0)
9259 lpColumnInfo->rcHeader.right += dx;
9260 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9261 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9262 else
9264 /* only needs to update the scrolls */
9265 infoPtr->nItemWidth += dx;
9266 LISTVIEW_UpdateScroll(infoPtr);
9268 LISTVIEW_UpdateItemSize(infoPtr);
9269 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
9271 POINT ptOrigin;
9272 RECT rcCol = lpColumnInfo->rcHeader;
9274 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9275 OffsetRect(&rcCol, ptOrigin.x, 0);
9277 rcCol.top = infoPtr->rcList.top;
9278 rcCol.bottom = infoPtr->rcList.bottom;
9280 /* resizing left-aligned columns leaves most of the left side untouched */
9281 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9283 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9284 if (dx > 0)
9285 nMaxDirty += dx;
9286 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9289 /* when shrinking the last column clear the now unused field */
9290 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9292 RECT right;
9294 rcCol.right -= dx;
9296 /* deal with right from rightmost column area */
9297 right.left = rcCol.right;
9298 right.top = rcCol.top;
9299 right.bottom = rcCol.bottom;
9300 right.right = infoPtr->rcList.right;
9302 LISTVIEW_InvalidateRect(infoPtr, &right);
9305 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9309 break;
9311 case HDN_ITEMCLICKW:
9312 case HDN_ITEMCLICKA:
9314 /* Handle sorting by Header Column */
9315 NMLISTVIEW nmlv;
9317 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9318 nmlv.iItem = -1;
9319 nmlv.iSubItem = lpnmh->iItem;
9320 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9321 notify_forward_header(infoPtr, lpnmh);
9323 break;
9325 case HDN_DIVIDERDBLCLICKW:
9326 case HDN_DIVIDERDBLCLICKA:
9327 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9328 break;
9331 return 0;
9334 /***
9335 * DESCRIPTION:
9336 * Paint non-client area of control.
9338 * PARAMETER(S):
9339 * [I] infoPtr : valid pointer to the listview structureof the sender
9340 * [I] region : update region
9342 * RETURN:
9343 * TRUE - frame was painted
9344 * FALSE - call default window proc
9346 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9348 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9349 HDC dc;
9350 RECT r;
9351 HRGN cliprgn;
9352 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9353 cyEdge = GetSystemMetrics (SM_CYEDGE);
9355 if (!theme) return FALSE;
9357 GetWindowRect(infoPtr->hwndSelf, &r);
9359 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9360 r.right - cxEdge, r.bottom - cyEdge);
9361 if (region != (HRGN)1)
9362 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9363 OffsetRect(&r, -r.left, -r.top);
9365 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9366 OffsetRect(&r, -r.left, -r.top);
9368 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9369 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9370 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9371 ReleaseDC(infoPtr->hwndSelf, dc);
9373 /* Call default proc to get the scrollbars etc. painted */
9374 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9376 return TRUE;
9379 /***
9380 * DESCRIPTION:
9381 * Determines the type of structure to use.
9383 * PARAMETER(S):
9384 * [I] infoPtr : valid pointer to the listview structureof the sender
9385 * [I] hwndFrom : listview window handle
9386 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9388 * RETURN:
9389 * Zero
9391 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9393 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9395 if (nCommand == NF_REQUERY)
9396 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9398 return infoPtr->notifyFormat;
9401 /***
9402 * DESCRIPTION:
9403 * Paints/Repaints the listview control.
9405 * PARAMETER(S):
9406 * [I] infoPtr : valid pointer to the listview structure
9407 * [I] hdc : device context handle
9409 * RETURN:
9410 * Zero
9412 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9414 TRACE("(hdc=%p)\n", hdc);
9416 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9418 infoPtr->bNoItemMetrics = FALSE;
9419 LISTVIEW_UpdateItemSize(infoPtr);
9420 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
9421 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9422 LISTVIEW_UpdateScroll(infoPtr);
9425 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
9427 if (hdc)
9428 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9429 else
9431 PAINTSTRUCT ps;
9433 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9434 if (!hdc) return 1;
9435 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9436 EndPaint(infoPtr->hwndSelf, &ps);
9439 return 0;
9443 /***
9444 * DESCRIPTION:
9445 * Paints/Repaints the listview control.
9447 * PARAMETER(S):
9448 * [I] infoPtr : valid pointer to the listview structure
9449 * [I] hdc : device context handle
9450 * [I] options : drawing options
9452 * RETURN:
9453 * Zero
9455 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9457 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9459 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9460 return 0;
9462 if (options & PRF_ERASEBKGND)
9463 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9465 if (options & PRF_CLIENT)
9466 LISTVIEW_Paint(infoPtr, hdc);
9468 return 0;
9472 /***
9473 * DESCRIPTION:
9474 * Processes double click messages (right mouse button).
9476 * PARAMETER(S):
9477 * [I] infoPtr : valid pointer to the listview structure
9478 * [I] wKey : key flag
9479 * [I] x,y : mouse coordinate
9481 * RETURN:
9482 * Zero
9484 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9486 LVHITTESTINFO lvHitTestInfo;
9488 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9490 /* send NM_RELEASEDCAPTURE notification */
9491 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9493 /* send NM_RDBLCLK notification */
9494 lvHitTestInfo.pt.x = x;
9495 lvHitTestInfo.pt.y = y;
9496 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9497 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9499 return 0;
9502 /***
9503 * DESCRIPTION:
9504 * Processes mouse down messages (right mouse button).
9506 * PARAMETER(S):
9507 * [I] infoPtr : valid pointer to the listview structure
9508 * [I] wKey : key flag
9509 * [I] x,y : mouse coordinate
9511 * RETURN:
9512 * Zero
9514 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9516 LVHITTESTINFO lvHitTestInfo;
9517 INT nItem;
9519 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9521 /* send NM_RELEASEDCAPTURE notification */
9522 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9524 /* make sure the listview control window has the focus */
9525 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9527 /* set right button down flag */
9528 infoPtr->bRButtonDown = TRUE;
9530 /* determine the index of the selected item */
9531 lvHitTestInfo.pt.x = x;
9532 lvHitTestInfo.pt.y = y;
9533 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9535 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9537 LISTVIEW_SetItemFocus(infoPtr, nItem);
9538 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9539 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9540 LISTVIEW_SetSelection(infoPtr, nItem);
9542 else
9544 LISTVIEW_DeselectAll(infoPtr);
9547 return 0;
9550 /***
9551 * DESCRIPTION:
9552 * Processes mouse up messages (right mouse button).
9554 * PARAMETER(S):
9555 * [I] infoPtr : valid pointer to the listview structure
9556 * [I] wKey : key flag
9557 * [I] x,y : mouse coordinate
9559 * RETURN:
9560 * Zero
9562 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9564 LVHITTESTINFO lvHitTestInfo;
9565 POINT pt;
9567 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9569 if (!infoPtr->bRButtonDown) return 0;
9571 /* set button flag */
9572 infoPtr->bRButtonDown = FALSE;
9574 /* Send NM_RCLICK notification */
9575 lvHitTestInfo.pt.x = x;
9576 lvHitTestInfo.pt.y = y;
9577 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9578 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9580 /* Change to screen coordinate for WM_CONTEXTMENU */
9581 pt = lvHitTestInfo.pt;
9582 ClientToScreen(infoPtr->hwndSelf, &pt);
9584 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9585 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9586 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9588 return 0;
9592 /***
9593 * DESCRIPTION:
9594 * Sets the cursor.
9596 * PARAMETER(S):
9597 * [I] infoPtr : valid pointer to the listview structure
9598 * [I] hwnd : window handle of window containing the cursor
9599 * [I] nHittest : hit-test code
9600 * [I] wMouseMsg : ideintifier of the mouse message
9602 * RETURN:
9603 * TRUE if cursor is set
9604 * FALSE otherwise
9606 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9608 LVHITTESTINFO lvHitTestInfo;
9610 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9612 if(!infoPtr->hHotCursor) return FALSE;
9614 GetCursorPos(&lvHitTestInfo.pt);
9615 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9617 SetCursor(infoPtr->hHotCursor);
9619 return TRUE;
9622 /***
9623 * DESCRIPTION:
9624 * Sets the focus.
9626 * PARAMETER(S):
9627 * [I] infoPtr : valid pointer to the listview structure
9628 * [I] hwndLoseFocus : handle of previously focused window
9630 * RETURN:
9631 * Zero
9633 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9635 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9637 /* if we have the focus already, there's nothing to do */
9638 if (infoPtr->bFocus) return 0;
9640 /* send NM_SETFOCUS notification */
9641 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9643 /* set window focus flag */
9644 infoPtr->bFocus = TRUE;
9646 /* put the focus rect back on */
9647 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9649 /* redraw all visible selected items */
9650 LISTVIEW_InvalidateSelectedItems(infoPtr);
9652 return 0;
9655 /***
9656 * DESCRIPTION:
9657 * Sets the font.
9659 * PARAMETER(S):
9660 * [I] infoPtr : valid pointer to the listview structure
9661 * [I] fRedraw : font handle
9662 * [I] fRedraw : redraw flag
9664 * RETURN:
9665 * Zero
9667 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9669 HFONT oldFont = infoPtr->hFont;
9671 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9673 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9674 if (infoPtr->hFont == oldFont) return 0;
9676 LISTVIEW_SaveTextMetrics(infoPtr);
9678 if (infoPtr->uView == LV_VIEW_DETAILS)
9680 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9681 LISTVIEW_UpdateSize(infoPtr);
9682 LISTVIEW_UpdateScroll(infoPtr);
9685 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9687 return 0;
9690 /***
9691 * DESCRIPTION:
9692 * Message handling for WM_SETREDRAW.
9693 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9695 * PARAMETER(S):
9696 * [I] infoPtr : valid pointer to the listview structure
9697 * [I] bRedraw: state of redraw flag
9699 * RETURN:
9700 * DefWinProc return value
9702 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9704 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9706 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9707 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9709 infoPtr->bRedraw = bRedraw;
9711 if(!bRedraw) return 0;
9713 if (is_autoarrange(infoPtr))
9714 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9715 LISTVIEW_UpdateScroll(infoPtr);
9717 /* despite what the WM_SETREDRAW docs says, apps expect us
9718 * to invalidate the listview here... stupid! */
9719 LISTVIEW_InvalidateList(infoPtr);
9721 return 0;
9724 /***
9725 * DESCRIPTION:
9726 * Resizes the listview control. This function processes WM_SIZE
9727 * messages. At this time, the width and height are not used.
9729 * PARAMETER(S):
9730 * [I] infoPtr : valid pointer to the listview structure
9731 * [I] Width : new width
9732 * [I] Height : new height
9734 * RETURN:
9735 * Zero
9737 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9739 RECT rcOld = infoPtr->rcList;
9741 TRACE("(width=%d, height=%d)\n", Width, Height);
9743 LISTVIEW_UpdateSize(infoPtr);
9744 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9746 /* do not bother with display related stuff if we're not redrawing */
9747 if (!is_redrawing(infoPtr)) return 0;
9749 if (is_autoarrange(infoPtr))
9750 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9752 LISTVIEW_UpdateScroll(infoPtr);
9754 /* refresh all only for lists whose height changed significantly */
9755 if ((infoPtr->uView == LV_VIEW_LIST) &&
9756 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9757 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9758 LISTVIEW_InvalidateList(infoPtr);
9760 return 0;
9763 /***
9764 * DESCRIPTION:
9765 * Sets the size information.
9767 * PARAMETER(S):
9768 * [I] infoPtr : valid pointer to the listview structure
9770 * RETURN:
9771 * None
9773 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9775 TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
9777 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9779 if (infoPtr->uView == LV_VIEW_LIST)
9781 /* Apparently the "LIST" style is supposed to have the same
9782 * number of items in a column even if there is no scroll bar.
9783 * Since if a scroll bar already exists then the bottom is already
9784 * reduced, only reduce if the scroll bar does not currently exist.
9785 * The "2" is there to mimic the native control. I think it may be
9786 * related to either padding or edges. (GLA 7/2002)
9788 if (!(infoPtr->dwStyle & WS_HSCROLL))
9789 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9790 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9792 else if (infoPtr->uView == LV_VIEW_DETAILS)
9794 HDLAYOUT hl;
9795 WINDOWPOS wp;
9797 hl.prc = &infoPtr->rcList;
9798 hl.pwpos = &wp;
9799 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9800 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9801 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9802 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9803 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9804 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9806 infoPtr->rcList.top = max(wp.cy, 0);
9807 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9810 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9813 /***
9814 * DESCRIPTION:
9815 * Processes WM_STYLECHANGED messages.
9817 * PARAMETER(S):
9818 * [I] infoPtr : valid pointer to the listview structure
9819 * [I] wStyleType : window style type (normal or extended)
9820 * [I] lpss : window style information
9822 * RETURN:
9823 * Zero
9825 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9826 const STYLESTRUCT *lpss)
9828 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9829 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9830 UINT style;
9832 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9833 wStyleType, lpss->styleOld, lpss->styleNew);
9835 if (wStyleType != GWL_STYLE) return 0;
9837 infoPtr->dwStyle = lpss->styleNew;
9838 map_style_view(infoPtr);
9840 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9841 ((lpss->styleNew & WS_HSCROLL) == 0))
9842 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9844 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9845 ((lpss->styleNew & WS_VSCROLL) == 0))
9846 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9848 if (uNewView != uOldView)
9850 SIZE oldIconSize = infoPtr->iconSize;
9851 HIMAGELIST himl;
9853 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9854 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9856 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9857 SetRectEmpty(&infoPtr->rcFocus);
9859 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9860 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9862 if (uNewView == LVS_ICON)
9864 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9866 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9867 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9868 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9871 else if (uNewView == LVS_REPORT)
9873 HDLAYOUT hl;
9874 WINDOWPOS wp;
9876 LISTVIEW_CreateHeader( infoPtr );
9878 hl.prc = &infoPtr->rcList;
9879 hl.pwpos = &wp;
9880 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9881 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9882 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9883 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9886 LISTVIEW_UpdateItemSize(infoPtr);
9889 if (uNewView == LVS_REPORT)
9891 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9893 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9895 /* Turn off the header control */
9896 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9897 TRACE("Hide header control, was 0x%08x\n", style);
9898 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9899 } else {
9900 /* Turn on the header control */
9901 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9903 TRACE("Show header control, was 0x%08x\n", style);
9904 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9910 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9911 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9912 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9914 /* update the size of the client area */
9915 LISTVIEW_UpdateSize(infoPtr);
9917 /* add scrollbars if needed */
9918 LISTVIEW_UpdateScroll(infoPtr);
9920 /* invalidate client area + erase background */
9921 LISTVIEW_InvalidateList(infoPtr);
9923 return 0;
9926 /***
9927 * DESCRIPTION:
9928 * Processes WM_STYLECHANGING messages.
9930 * PARAMETER(S):
9931 * [I] infoPtr : valid pointer to the listview structure
9932 * [I] wStyleType : window style type (normal or extended)
9933 * [I0] lpss : window style information
9935 * RETURN:
9936 * Zero
9938 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9939 STYLESTRUCT *lpss)
9941 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9942 wStyleType, lpss->styleOld, lpss->styleNew);
9944 /* don't forward LVS_OWNERDATA only if not already set to */
9945 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
9947 if (lpss->styleOld & LVS_OWNERDATA)
9948 lpss->styleNew |= LVS_OWNERDATA;
9949 else
9950 lpss->styleNew &= ~LVS_OWNERDATA;
9953 return 0;
9956 /***
9957 * DESCRIPTION:
9958 * Processes WM_SHOWWINDOW messages.
9960 * PARAMETER(S):
9961 * [I] infoPtr : valid pointer to the listview structure
9962 * [I] bShown : window is being shown (FALSE when hidden)
9963 * [I] iStatus : window show status
9965 * RETURN:
9966 * Zero
9968 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
9970 /* header delayed creation */
9971 if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
9973 LISTVIEW_CreateHeader(infoPtr);
9975 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
9976 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9979 return 0;
9982 /***
9983 * DESCRIPTION:
9984 * Processes CCM_GETVERSION messages.
9986 * PARAMETER(S):
9987 * [I] infoPtr : valid pointer to the listview structure
9989 * RETURN:
9990 * Current version
9992 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
9994 return infoPtr->iVersion;
9997 /***
9998 * DESCRIPTION:
9999 * Processes CCM_SETVERSION messages.
10001 * PARAMETER(S):
10002 * [I] infoPtr : valid pointer to the listview structure
10003 * [I] iVersion : version to be set
10005 * RETURN:
10006 * -1 when requested version is greater than DLL version;
10007 * previous version otherwise
10009 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
10011 INT iOldVersion = infoPtr->iVersion;
10013 if (iVersion > COMCTL32_VERSION)
10014 return -1;
10016 infoPtr->iVersion = iVersion;
10018 TRACE("new version %d\n", iVersion);
10020 return iOldVersion;
10023 /***
10024 * DESCRIPTION:
10025 * Window procedure of the listview control.
10028 static LRESULT WINAPI
10029 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10031 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
10033 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
10035 if (!infoPtr && (uMsg != WM_NCCREATE))
10036 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10038 switch (uMsg)
10040 case LVM_APPROXIMATEVIEWRECT:
10041 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
10042 LOWORD(lParam), HIWORD(lParam));
10043 case LVM_ARRANGE:
10044 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
10046 /* case LVM_CANCELEDITLABEL: */
10048 case LVM_CREATEDRAGIMAGE:
10049 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
10051 case LVM_DELETEALLITEMS:
10052 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
10054 case LVM_DELETECOLUMN:
10055 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
10057 case LVM_DELETEITEM:
10058 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
10060 case LVM_EDITLABELW:
10061 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
10063 case LVM_EDITLABELA:
10064 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
10066 /* case LVM_ENABLEGROUPVIEW: */
10068 case LVM_ENSUREVISIBLE:
10069 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
10071 case LVM_FINDITEMW:
10072 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
10074 case LVM_FINDITEMA:
10075 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
10077 case LVM_GETBKCOLOR:
10078 return infoPtr->clrBk;
10080 /* case LVM_GETBKIMAGE: */
10082 case LVM_GETCALLBACKMASK:
10083 return infoPtr->uCallbackMask;
10085 case LVM_GETCOLUMNA:
10086 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10088 case LVM_GETCOLUMNW:
10089 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10091 case LVM_GETCOLUMNORDERARRAY:
10092 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10094 case LVM_GETCOLUMNWIDTH:
10095 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
10097 case LVM_GETCOUNTPERPAGE:
10098 return LISTVIEW_GetCountPerPage(infoPtr);
10100 case LVM_GETEDITCONTROL:
10101 return (LRESULT)infoPtr->hwndEdit;
10103 case LVM_GETEXTENDEDLISTVIEWSTYLE:
10104 return infoPtr->dwLvExStyle;
10106 /* case LVM_GETGROUPINFO: */
10108 /* case LVM_GETGROUPMETRICS: */
10110 case LVM_GETHEADER:
10111 return (LRESULT)infoPtr->hwndHeader;
10113 case LVM_GETHOTCURSOR:
10114 return (LRESULT)infoPtr->hHotCursor;
10116 case LVM_GETHOTITEM:
10117 return infoPtr->nHotItem;
10119 case LVM_GETHOVERTIME:
10120 return infoPtr->dwHoverTime;
10122 case LVM_GETIMAGELIST:
10123 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
10125 /* case LVM_GETINSERTMARK: */
10127 /* case LVM_GETINSERTMARKCOLOR: */
10129 /* case LVM_GETINSERTMARKRECT: */
10131 case LVM_GETISEARCHSTRINGA:
10132 case LVM_GETISEARCHSTRINGW:
10133 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
10134 return FALSE;
10136 case LVM_GETITEMA:
10137 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
10139 case LVM_GETITEMW:
10140 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
10142 case LVM_GETITEMCOUNT:
10143 return infoPtr->nItemCount;
10145 case LVM_GETITEMPOSITION:
10146 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
10148 case LVM_GETITEMRECT:
10149 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
10151 case LVM_GETITEMSPACING:
10152 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
10154 case LVM_GETITEMSTATE:
10155 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
10157 case LVM_GETITEMTEXTA:
10158 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10160 case LVM_GETITEMTEXTW:
10161 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10163 case LVM_GETNEXTITEM:
10164 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
10166 case LVM_GETNUMBEROFWORKAREAS:
10167 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
10168 return 1;
10170 case LVM_GETORIGIN:
10171 if (!lParam) return FALSE;
10172 if (infoPtr->uView == LV_VIEW_DETAILS ||
10173 infoPtr->uView == LV_VIEW_LIST) return FALSE;
10174 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
10175 return TRUE;
10177 /* case LVM_GETOUTLINECOLOR: */
10179 /* case LVM_GETSELECTEDCOLUMN: */
10181 case LVM_GETSELECTEDCOUNT:
10182 return LISTVIEW_GetSelectedCount(infoPtr);
10184 case LVM_GETSELECTIONMARK:
10185 return infoPtr->nSelectionMark;
10187 case LVM_GETSTRINGWIDTHA:
10188 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
10190 case LVM_GETSTRINGWIDTHW:
10191 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
10193 case LVM_GETSUBITEMRECT:
10194 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10196 case LVM_GETTEXTBKCOLOR:
10197 return infoPtr->clrTextBk;
10199 case LVM_GETTEXTCOLOR:
10200 return infoPtr->clrText;
10202 /* case LVM_GETTILEINFO: */
10204 /* case LVM_GETTILEVIEWINFO: */
10206 case LVM_GETTOOLTIPS:
10207 if( !infoPtr->hwndToolTip )
10208 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10209 return (LRESULT)infoPtr->hwndToolTip;
10211 case LVM_GETTOPINDEX:
10212 return LISTVIEW_GetTopIndex(infoPtr);
10214 case LVM_GETUNICODEFORMAT:
10215 return (infoPtr->notifyFormat == NFR_UNICODE);
10217 case LVM_GETVIEW:
10218 return infoPtr->uView;
10220 case LVM_GETVIEWRECT:
10221 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10223 case LVM_GETWORKAREAS:
10224 FIXME("LVM_GETWORKAREAS: unimplemented\n");
10225 return FALSE;
10227 /* case LVM_HASGROUP: */
10229 case LVM_HITTEST:
10230 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
10232 case LVM_INSERTCOLUMNA:
10233 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10235 case LVM_INSERTCOLUMNW:
10236 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10238 /* case LVM_INSERTGROUP: */
10240 /* case LVM_INSERTGROUPSORTED: */
10242 case LVM_INSERTITEMA:
10243 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10245 case LVM_INSERTITEMW:
10246 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10248 /* case LVM_INSERTMARKHITTEST: */
10250 /* case LVM_ISGROUPVIEWENABLED: */
10252 /* case LVM_MAPIDTOINDEX: */
10254 /* case LVM_MAPINDEXTOID: */
10256 /* case LVM_MOVEGROUP: */
10258 /* case LVM_MOVEITEMTOGROUP: */
10260 case LVM_REDRAWITEMS:
10261 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10263 /* case LVM_REMOVEALLGROUPS: */
10265 /* case LVM_REMOVEGROUP: */
10267 case LVM_SCROLL:
10268 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10270 case LVM_SETBKCOLOR:
10271 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10273 /* case LVM_SETBKIMAGE: */
10275 case LVM_SETCALLBACKMASK:
10276 infoPtr->uCallbackMask = (UINT)wParam;
10277 return TRUE;
10279 case LVM_SETCOLUMNA:
10280 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10282 case LVM_SETCOLUMNW:
10283 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10285 case LVM_SETCOLUMNORDERARRAY:
10286 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10288 case LVM_SETCOLUMNWIDTH:
10289 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10291 case LVM_SETEXTENDEDLISTVIEWSTYLE:
10292 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10294 /* case LVM_SETGROUPINFO: */
10296 /* case LVM_SETGROUPMETRICS: */
10298 case LVM_SETHOTCURSOR:
10299 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10301 case LVM_SETHOTITEM:
10302 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10304 case LVM_SETHOVERTIME:
10305 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10307 case LVM_SETICONSPACING:
10308 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10310 case LVM_SETIMAGELIST:
10311 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10313 /* case LVM_SETINFOTIP: */
10315 /* case LVM_SETINSERTMARK: */
10317 /* case LVM_SETINSERTMARKCOLOR: */
10319 case LVM_SETITEMA:
10320 case LVM_SETITEMW:
10322 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10323 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10326 case LVM_SETITEMCOUNT:
10327 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10329 case LVM_SETITEMPOSITION:
10331 POINT pt;
10332 pt.x = (short)LOWORD(lParam);
10333 pt.y = (short)HIWORD(lParam);
10334 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10337 case LVM_SETITEMPOSITION32:
10338 if (lParam == 0) return FALSE;
10339 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10341 case LVM_SETITEMSTATE:
10342 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10344 case LVM_SETITEMTEXTA:
10345 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10347 case LVM_SETITEMTEXTW:
10348 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10350 /* case LVM_SETOUTLINECOLOR: */
10352 /* case LVM_SETSELECTEDCOLUMN: */
10354 case LVM_SETSELECTIONMARK:
10355 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10357 case LVM_SETTEXTBKCOLOR:
10358 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10360 case LVM_SETTEXTCOLOR:
10361 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10363 /* case LVM_SETTILEINFO: */
10365 /* case LVM_SETTILEVIEWINFO: */
10367 /* case LVM_SETTILEWIDTH: */
10369 case LVM_SETTOOLTIPS:
10370 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10372 case LVM_SETUNICODEFORMAT:
10373 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10375 case LVM_SETVIEW:
10376 return LISTVIEW_SetView(infoPtr, wParam);
10378 /* case LVM_SETWORKAREAS: */
10380 /* case LVM_SORTGROUPS: */
10382 case LVM_SORTITEMS:
10383 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10385 case LVM_SORTITEMSEX:
10386 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10388 case LVM_SUBITEMHITTEST:
10389 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10391 case LVM_UPDATE:
10392 return LISTVIEW_Update(infoPtr, (INT)wParam);
10394 case CCM_GETVERSION:
10395 return LISTVIEW_GetVersion(infoPtr);
10397 case CCM_SETVERSION:
10398 return LISTVIEW_SetVersion(infoPtr, wParam);
10400 case WM_CHAR:
10401 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10403 case WM_COMMAND:
10404 return LISTVIEW_Command(infoPtr, wParam, lParam);
10406 case WM_NCCREATE:
10407 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10409 case WM_CREATE:
10410 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10412 case WM_DESTROY:
10413 return LISTVIEW_Destroy(infoPtr);
10415 case WM_ENABLE:
10416 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10418 case WM_ERASEBKGND:
10419 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10421 case WM_GETDLGCODE:
10422 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10424 case WM_GETFONT:
10425 return (LRESULT)infoPtr->hFont;
10427 case WM_HSCROLL:
10428 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10430 case WM_KEYDOWN:
10431 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10433 case WM_KILLFOCUS:
10434 return LISTVIEW_KillFocus(infoPtr);
10436 case WM_LBUTTONDBLCLK:
10437 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10439 case WM_LBUTTONDOWN:
10440 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10442 case WM_LBUTTONUP:
10443 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10445 case WM_MOUSEMOVE:
10446 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10448 case WM_MOUSEHOVER:
10449 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10451 case WM_NCDESTROY:
10452 return LISTVIEW_NCDestroy(infoPtr);
10454 case WM_NCPAINT:
10455 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10456 return 0;
10457 goto fwd_msg;
10459 case WM_NOTIFY:
10460 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10461 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10462 else return 0;
10464 case WM_NOTIFYFORMAT:
10465 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10467 case WM_PRINTCLIENT:
10468 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10470 case WM_PAINT:
10471 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10473 case WM_RBUTTONDBLCLK:
10474 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10476 case WM_RBUTTONDOWN:
10477 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10479 case WM_RBUTTONUP:
10480 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10482 case WM_SETCURSOR:
10483 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10484 return TRUE;
10485 goto fwd_msg;
10487 case WM_SETFOCUS:
10488 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10490 case WM_SETFONT:
10491 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10493 case WM_SETREDRAW:
10494 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10496 case WM_SHOWWINDOW:
10497 LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10498 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10500 case WM_SIZE:
10501 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10503 case WM_STYLECHANGED:
10504 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10506 case WM_STYLECHANGING:
10507 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10509 case WM_SYSCOLORCHANGE:
10510 COMCTL32_RefreshSysColors();
10511 return 0;
10513 /* case WM_TIMER: */
10514 case WM_THEMECHANGED:
10515 return LISTVIEW_ThemeChanged(infoPtr);
10517 case WM_VSCROLL:
10518 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10520 case WM_MOUSEWHEEL:
10521 if (wParam & (MK_SHIFT | MK_CONTROL))
10522 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10523 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10525 case WM_WINDOWPOSCHANGED:
10526 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10528 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10529 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10531 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
10533 MEASUREITEMSTRUCT mis;
10534 mis.CtlType = ODT_LISTVIEW;
10535 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10536 mis.itemID = -1;
10537 mis.itemWidth = 0;
10538 mis.itemData = 0;
10539 mis.itemHeight= infoPtr->nItemHeight;
10540 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10541 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10542 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10545 LISTVIEW_UpdateSize(infoPtr);
10546 LISTVIEW_UpdateScroll(infoPtr);
10548 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10550 /* case WM_WININICHANGE: */
10552 default:
10553 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10554 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10556 fwd_msg:
10557 /* call default window procedure */
10558 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10563 /***
10564 * DESCRIPTION:
10565 * Registers the window class.
10567 * PARAMETER(S):
10568 * None
10570 * RETURN:
10571 * None
10573 void LISTVIEW_Register(void)
10575 WNDCLASSW wndClass;
10577 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10578 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10579 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10580 wndClass.cbClsExtra = 0;
10581 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10582 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10583 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10584 wndClass.lpszClassName = WC_LISTVIEWW;
10585 RegisterClassW(&wndClass);
10588 /***
10589 * DESCRIPTION:
10590 * Unregisters the window class.
10592 * PARAMETER(S):
10593 * None
10595 * RETURN:
10596 * None
10598 void LISTVIEW_Unregister(void)
10600 UnregisterClassW(WC_LISTVIEWW, NULL);
10603 /***
10604 * DESCRIPTION:
10605 * Handle any WM_COMMAND messages
10607 * PARAMETER(S):
10608 * [I] infoPtr : valid pointer to the listview structure
10609 * [I] wParam : the first message parameter
10610 * [I] lParam : the second message parameter
10612 * RETURN:
10613 * Zero.
10615 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10617 switch (HIWORD(wParam))
10619 case EN_UPDATE:
10622 * Adjust the edit window size
10624 WCHAR buffer[1024];
10625 HDC hdc = GetDC(infoPtr->hwndEdit);
10626 HFONT hFont, hOldFont = 0;
10627 RECT rect;
10628 SIZE sz;
10630 if (!infoPtr->hwndEdit || !hdc) return 0;
10631 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10632 GetWindowRect(infoPtr->hwndEdit, &rect);
10634 /* Select font to get the right dimension of the string */
10635 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10636 if(hFont != 0)
10638 hOldFont = SelectObject(hdc, hFont);
10641 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10643 TEXTMETRICW textMetric;
10645 /* Add Extra spacing for the next character */
10646 GetTextMetricsW(hdc, &textMetric);
10647 sz.cx += (textMetric.tmMaxCharWidth * 2);
10649 SetWindowPos (
10650 infoPtr->hwndEdit,
10651 HWND_TOP,
10654 sz.cx,
10655 rect.bottom - rect.top,
10656 SWP_DRAWFRAME|SWP_NOMOVE);
10658 if(hFont != 0)
10659 SelectObject(hdc, hOldFont);
10661 ReleaseDC(infoPtr->hwndEdit, hdc);
10663 break;
10665 case EN_KILLFOCUS:
10667 /* handle value will be lost after LISTVIEW_EndEditLabelT */
10668 HWND edit = infoPtr->hwndEdit;
10670 LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit));
10671 SendMessageW(edit, WM_CLOSE, 0, 0);
10674 default:
10675 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10678 return 0;
10682 /***
10683 * DESCRIPTION:
10684 * Subclassed edit control windproc function
10686 * PARAMETER(S):
10687 * [I] hwnd : the edit window handle
10688 * [I] uMsg : the message that is to be processed
10689 * [I] wParam : first message parameter
10690 * [I] lParam : second message parameter
10691 * [I] isW : TRUE if input is Unicode
10693 * RETURN:
10694 * Zero.
10696 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10698 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10699 BOOL save = TRUE;
10701 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10702 hwnd, uMsg, wParam, lParam, isW);
10704 switch (uMsg)
10706 case WM_GETDLGCODE:
10707 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10709 case WM_DESTROY:
10711 WNDPROC editProc = infoPtr->EditWndProc;
10712 infoPtr->EditWndProc = 0;
10713 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10714 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10717 case WM_KEYDOWN:
10718 if (VK_ESCAPE == (INT)wParam)
10720 save = FALSE;
10721 break;
10723 else if (VK_RETURN == (INT)wParam)
10724 break;
10726 default:
10727 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10730 /* kill the edit */
10731 if (infoPtr->hwndEdit)
10732 LISTVIEW_EndEditLabelT(infoPtr, save, isW);
10734 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10735 return 0;
10738 /***
10739 * DESCRIPTION:
10740 * Subclassed edit control Unicode windproc function
10742 * PARAMETER(S):
10743 * [I] hwnd : the edit window handle
10744 * [I] uMsg : the message that is to be processed
10745 * [I] wParam : first message parameter
10746 * [I] lParam : second message parameter
10748 * RETURN:
10750 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10752 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10755 /***
10756 * DESCRIPTION:
10757 * Subclassed edit control ANSI windproc function
10759 * PARAMETER(S):
10760 * [I] hwnd : the edit window handle
10761 * [I] uMsg : the message that is to be processed
10762 * [I] wParam : first message parameter
10763 * [I] lParam : second message parameter
10765 * RETURN:
10767 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10769 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10772 /***
10773 * DESCRIPTION:
10774 * Creates a subclassed edit control
10776 * PARAMETER(S):
10777 * [I] infoPtr : valid pointer to the listview structure
10778 * [I] text : initial text for the edit
10779 * [I] style : the window style
10780 * [I] isW : TRUE if input is Unicode
10782 * RETURN:
10784 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style, BOOL isW)
10786 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10787 HWND hedit;
10788 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10790 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10792 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10794 /* Window will be resized and positioned after LVN_BEGINLABELEDIT */
10795 if (isW)
10796 hedit = CreateWindowW(editName, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
10797 else
10798 hedit = CreateWindowA("Edit", (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
10800 if (!hedit) return 0;
10802 infoPtr->EditWndProc = (WNDPROC)
10803 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10804 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10806 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10808 return hedit;