comctl32: Fix some gcc 4.1 warnings caused by windowsx.h macros.
[wine/gsoc_dplay.git] / dlls / comctl32 / listview.c
blob9210e8d2dbea3f404644240686455196b948d087
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
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * NOTES
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
33 * TODO:
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
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_ERASEBKGND: forward this message to the parent window if the bkgnd
40 * color is CLR_NONE.
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
43 * -- WM_TIMER
44 * -- WM_WININICHANGE
46 * Features
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
49 * -- Tilemode support
50 * -- Groups support
52 * Bugs
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
63 * Speedups
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
73 * Flags
74 * -- LVIF_COLUMNS
75 * -- LVIF_GROUPID
76 * -- LVIF_NORECOMPUTE
78 * States
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
80 * -- LVIS_CUT
81 * -- LVIS_DROPHILITED
82 * -- LVIS_OVERLAYMASK
84 * Styles
85 * -- LVS_NOLABELWRAP
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
88 * -- LVS_ALIGNTOP
89 * -- LVS_TYPESTYLEMASK
91 * Extended Styles
92 * -- LVS_EX_BORDERSELECT
93 * -- LVS_EX_FLATSB
94 * -- LVS_EX_GRIDLINES
95 * -- LVS_EX_HEADERDRAGDROP
96 * -- LVS_EX_INFOTIP
97 * -- LVS_EX_LABELTIP
98 * -- LVS_EX_MULTIWORKAREAS
99 * -- LVS_EX_ONECLICKACTIVATE
100 * -- LVS_EX_REGIONAL
101 * -- LVS_EX_SIMPLESELECT
102 * -- LVS_EX_TRACKSELECT
103 * -- LVS_EX_TWOCLICKACTIVATE
104 * -- LVS_EX_UNDERLINECOLD
105 * -- LVS_EX_UNDERLINEHOT
107 * Notifications:
108 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
109 * -- LVN_GETINFOTIP
110 * -- LVN_HOTTRACK
111 * -- LVN_MARQUEEBEGIN
112 * -- LVN_ODFINDITEM
113 * -- LVN_SETDISPINFO
114 * -- NM_HOVER
115 * -- LVN_BEGINRDRAG
117 * Messages:
118 * -- LVM_CANCELEDITLABEL
119 * -- LVM_ENABLEGROUPVIEW
120 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
121 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
122 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
123 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
124 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
125 * -- LVM_GETINSERTMARKRECT
126 * -- LVM_GETNUMBEROFWORKAREAS
127 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
128 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
129 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
130 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
131 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
132 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
133 * -- LVM_GETVIEW, LVM_SETVIEW
134 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
135 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
136 * -- LVM_INSERTGROUPSORTED
137 * -- LVM_INSERTMARKHITTEST
138 * -- LVM_ISGROUPVIEWENABLED
139 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
140 * -- LVM_MOVEGROUP
141 * -- LVM_MOVEITEMTOGROUP
142 * -- LVM_SETINFOTIP
143 * -- LVM_SETTILEWIDTH
144 * -- LVM_SORTGROUPS
145 * -- LVM_SORTITEMSEX
147 * Macros:
148 * -- ListView_GetCheckSate, ListView_SetCheckState
149 * -- ListView_GetHoverTime, ListView_SetHoverTime
150 * -- ListView_GetISearchString
151 * -- ListView_GetNumberOfWorkAreas
152 * -- ListView_GetOrigin
153 * -- ListView_GetTextBkColor
154 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
155 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
156 * -- ListView_SortItemsEx
158 * Functions:
159 * -- LVGroupComparE
161 * Known differences in message stream from native control (not known if
162 * these differences cause problems):
163 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
164 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
165 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
166 * processing for "USEDOUBLECLICKTIME".
169 #include "config.h"
170 #include "wine/port.h"
172 #include <assert.h>
173 #include <ctype.h>
174 #include <string.h>
175 #include <stdlib.h>
176 #include <stdarg.h>
177 #include <stdio.h>
179 #include "windef.h"
180 #include "winbase.h"
181 #include "winnt.h"
182 #include "wingdi.h"
183 #include "winuser.h"
184 #include "winnls.h"
185 #include "commctrl.h"
186 #include "comctl32.h"
187 #include "uxtheme.h"
189 #include "wine/debug.h"
190 #include "wine/unicode.h"
192 WINE_DEFAULT_DEBUG_CHANNEL(listview);
194 /* make sure you set this to 0 for production use! */
195 #define DEBUG_RANGES 1
197 typedef struct tagCOLUMN_INFO
199 RECT rcHeader; /* tracks the header's rectangle */
200 int fmt; /* same as LVCOLUMN.fmt */
201 } COLUMN_INFO;
203 typedef struct tagITEMHDR
205 LPWSTR pszText;
206 INT iImage;
207 } ITEMHDR, *LPITEMHDR;
209 typedef struct tagSUBITEM_INFO
211 ITEMHDR hdr;
212 INT iSubItem;
213 } SUBITEM_INFO;
215 typedef struct tagITEM_INFO
217 ITEMHDR hdr;
218 UINT state;
219 LPARAM lParam;
220 INT iIndent;
221 } ITEM_INFO;
223 typedef struct tagRANGE
225 INT lower;
226 INT upper;
227 } RANGE;
229 typedef struct tagRANGES
231 HDPA hdpa;
232 } *RANGES;
234 typedef struct tagITERATOR
236 INT nItem;
237 INT nSpecial;
238 RANGE range;
239 RANGES ranges;
240 INT index;
241 } ITERATOR;
243 typedef struct tagLISTVIEW_INFO
245 HWND hwndSelf;
246 HBRUSH hBkBrush;
247 COLORREF clrBk;
248 COLORREF clrText;
249 COLORREF clrTextBk;
250 COLORREF clrTextBkDefault;
251 HIMAGELIST himlNormal;
252 HIMAGELIST himlSmall;
253 HIMAGELIST himlState;
254 BOOL bLButtonDown;
255 BOOL bRButtonDown;
256 POINT ptClickPos; /* point where the user clicked */
257 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
258 INT nItemHeight;
259 INT nItemWidth;
260 RANGES selectionRanges;
261 INT nSelectionMark;
262 INT nHotItem;
263 SHORT notifyFormat;
264 HWND hwndNotify;
265 RECT rcList; /* This rectangle is really the window
266 * client rectangle possibly reduced by the
267 * horizontal scroll bar and/or header - see
268 * LISTVIEW_UpdateSize. This rectangle offset
269 * by the LISTVIEW_GetOrigin value is in
270 * client coordinates */
271 SIZE iconSize;
272 SIZE iconSpacing;
273 SIZE iconStateSize;
274 UINT uCallbackMask;
275 HWND hwndHeader;
276 HCURSOR hHotCursor;
277 HFONT hDefaultFont;
278 HFONT hFont;
279 INT ntmHeight; /* Some cached metrics of the font used */
280 INT ntmMaxCharWidth; /* by the listview to draw items */
281 INT nEllipsisWidth;
282 BOOL bRedraw; /* Turns on/off repaints & invalidations */
283 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
284 BOOL bFocus;
285 BOOL bDoChangeNotify; /* send change notification messages? */
286 INT nFocusedItem;
287 RECT rcFocus;
288 DWORD dwStyle; /* the cached window GWL_STYLE */
289 DWORD dwLvExStyle; /* extended listview style */
290 INT nItemCount; /* the number of items in the list */
291 HDPA hdpaItems; /* array ITEM_INFO pointers */
292 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
293 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
294 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
295 POINT currIconPos; /* this is the position next icon will be placed */
296 PFNLVCOMPARE pfnCompare;
297 LPARAM lParamSort;
298 HWND hwndEdit;
299 WNDPROC EditWndProc;
300 INT nEditLabelItem;
301 DWORD dwHoverTime;
302 HWND hwndToolTip;
304 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
306 DWORD lastKeyPressTimestamp;
307 WPARAM charCode;
308 INT nSearchParamLength;
309 WCHAR szSearchParam[ MAX_PATH ];
310 BOOL bIsDrawing;
311 INT nMeasureItemHeight;
312 } LISTVIEW_INFO;
315 * constants
317 /* How many we debug buffer to allocate */
318 #define DEBUG_BUFFERS 20
319 /* The size of a single debug bbuffer */
320 #define DEBUG_BUFFER_SIZE 256
322 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
323 #define SB_INTERNAL -1
325 /* maximum size of a label */
326 #define DISP_TEXT_SIZE 512
328 /* padding for items in list and small icon display modes */
329 #define WIDTH_PADDING 12
331 /* padding for items in list, report and small icon display modes */
332 #define HEIGHT_PADDING 1
334 /* offset of items in report display mode */
335 #define REPORT_MARGINX 2
337 /* padding for icon in large icon display mode
338 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
339 * that HITTEST will see.
340 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
341 * ICON_TOP_PADDING - sum of the two above.
342 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
343 * LABEL_HOR_PADDING - between text and sides of box
344 * LABEL_VERT_PADDING - between bottom of text and end of box
346 * ICON_LR_PADDING - additional width above icon size.
347 * ICON_LR_HALF - half of the above value
349 #define ICON_TOP_PADDING_NOTHITABLE 2
350 #define ICON_TOP_PADDING_HITABLE 2
351 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
352 #define ICON_BOTTOM_PADDING 4
353 #define LABEL_HOR_PADDING 5
354 #define LABEL_VERT_PADDING 7
355 #define ICON_LR_PADDING 16
356 #define ICON_LR_HALF (ICON_LR_PADDING/2)
358 /* default label width for items in list and small icon display modes */
359 #define DEFAULT_LABEL_WIDTH 40
361 /* default column width for items in list display mode */
362 #define DEFAULT_COLUMN_WIDTH 128
364 /* Size of "line" scroll for V & H scrolls */
365 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
367 /* Padding betwen image and label */
368 #define IMAGE_PADDING 2
370 /* Padding behind the label */
371 #define TRAILING_LABEL_PADDING 12
372 #define TRAILING_HEADER_PADDING 11
374 /* Border for the icon caption */
375 #define CAPTION_BORDER 2
377 /* Standard DrawText flags */
378 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
379 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
380 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
382 /* Image index from state */
383 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
385 /* The time in milliseconds to reset the search in the list */
386 #define KEY_DELAY 450
388 /* Dump the LISTVIEW_INFO structure to the debug channel */
389 #define LISTVIEW_DUMP(iP) do { \
390 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
391 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
392 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
393 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
394 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
395 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
396 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
397 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
398 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
399 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
400 } while(0)
402 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
405 * forward declarations
407 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
408 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
409 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
410 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
411 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
412 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
413 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
414 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
415 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
416 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
417 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
418 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
419 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
420 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
421 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
422 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
423 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
424 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
425 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
426 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
427 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
428 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
429 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
430 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
431 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
432 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
433 static INT LISTVIEW_HitTest(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
435 /******** Text handling functions *************************************/
437 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
438 * text string. The string may be ANSI or Unicode, in which case
439 * the boolean isW tells us the type of the string.
441 * The name of the function tell what type of strings it expects:
442 * W: Unicode, T: ANSI/Unicode - function of isW
445 static inline BOOL is_textW(LPCWSTR text)
447 return text != NULL && text != LPSTR_TEXTCALLBACKW;
450 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
452 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
453 return is_textW(text);
456 static inline int textlenT(LPCWSTR text, BOOL isW)
458 return !is_textT(text, isW) ? 0 :
459 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
462 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
464 if (isDestW)
465 if (isSrcW) lstrcpynW(dest, src, max);
466 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
467 else
468 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
469 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
472 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
474 LPWSTR wstr = (LPWSTR)text;
476 if (!isW && is_textT(text, isW))
478 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
479 wstr = Alloc(len * sizeof(WCHAR));
480 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
482 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
483 return wstr;
486 static inline void textfreeT(LPWSTR wstr, BOOL isW)
488 if (!isW && is_textT(wstr, isW)) Free (wstr);
492 * dest is a pointer to a Unicode string
493 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
495 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
497 BOOL bResult = TRUE;
499 if (src == LPSTR_TEXTCALLBACKW)
501 if (is_textW(*dest)) Free(*dest);
502 *dest = LPSTR_TEXTCALLBACKW;
504 else
506 LPWSTR pszText = textdupTtoW(src, isW);
507 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
508 bResult = Str_SetPtrW(dest, pszText);
509 textfreeT(pszText, isW);
511 return bResult;
515 * compares a Unicode to a Unicode/ANSI text string
517 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
519 if (!aw) return bt ? -1 : 0;
520 if (!bt) return aw ? 1 : 0;
521 if (aw == LPSTR_TEXTCALLBACKW)
522 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
523 if (bt != LPSTR_TEXTCALLBACKW)
525 LPWSTR bw = textdupTtoW(bt, isW);
526 int r = bw ? lstrcmpW(aw, bw) : 1;
527 textfreeT(bw, isW);
528 return r;
531 return 1;
534 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
536 int res;
538 n = min(min(n, strlenW(s1)), strlenW(s2));
539 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
540 return res ? res - sizeof(WCHAR) : res;
543 /******** Debugging functions *****************************************/
545 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
547 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
548 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
551 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
553 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
554 n = min(textlenT(text, isW), n);
555 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
558 static char* debug_getbuf(void)
560 static int index = 0;
561 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
562 return buffers[index++ % DEBUG_BUFFERS];
565 static inline const char* debugrange(const RANGE *lprng)
567 if (!lprng) return "(null)";
568 return wine_dbg_sprintf("[%d, %d)", lprng->lower, lprng->upper);
571 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
573 char* buf = debug_getbuf(), *text = buf;
574 int len, size = DEBUG_BUFFER_SIZE;
576 if (pScrollInfo == NULL) return "(null)";
577 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
578 if (len == -1) goto end; buf += len; size -= len;
579 if (pScrollInfo->fMask & SIF_RANGE)
580 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
581 else len = 0;
582 if (len == -1) goto end; buf += len; size -= len;
583 if (pScrollInfo->fMask & SIF_PAGE)
584 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
585 else len = 0;
586 if (len == -1) goto end; buf += len; size -= len;
587 if (pScrollInfo->fMask & SIF_POS)
588 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
589 else len = 0;
590 if (len == -1) goto end; buf += len; size -= len;
591 if (pScrollInfo->fMask & SIF_TRACKPOS)
592 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
593 else len = 0;
594 if (len == -1) goto end; buf += len; size -= len;
595 goto undo;
596 end:
597 buf = text + strlen(text);
598 undo:
599 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
600 return text;
603 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
605 if (!plvnm) return "(null)";
606 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
607 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
608 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
609 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
612 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
614 char* buf = debug_getbuf(), *text = buf;
615 int len, size = DEBUG_BUFFER_SIZE;
617 if (lpLVItem == NULL) return "(null)";
618 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
619 if (len == -1) goto end; buf += len; size -= len;
620 if (lpLVItem->mask & LVIF_STATE)
621 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
622 else len = 0;
623 if (len == -1) goto end; buf += len; size -= len;
624 if (lpLVItem->mask & LVIF_TEXT)
625 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
626 else len = 0;
627 if (len == -1) goto end; buf += len; size -= len;
628 if (lpLVItem->mask & LVIF_IMAGE)
629 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
630 else len = 0;
631 if (len == -1) goto end; buf += len; size -= len;
632 if (lpLVItem->mask & LVIF_PARAM)
633 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
634 else len = 0;
635 if (len == -1) goto end; buf += len; size -= len;
636 if (lpLVItem->mask & LVIF_INDENT)
637 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
638 else len = 0;
639 if (len == -1) goto end; buf += len; size -= len;
640 goto undo;
641 end:
642 buf = text + strlen(text);
643 undo:
644 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
645 return text;
648 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
650 char* buf = debug_getbuf(), *text = buf;
651 int len, size = DEBUG_BUFFER_SIZE;
653 if (lpColumn == NULL) return "(null)";
654 len = snprintf(buf, size, "{");
655 if (len == -1) goto end; buf += len; size -= len;
656 if (lpColumn->mask & LVCF_SUBITEM)
657 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
658 else len = 0;
659 if (len == -1) goto end; buf += len; size -= len;
660 if (lpColumn->mask & LVCF_FMT)
661 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
662 else len = 0;
663 if (len == -1) goto end; buf += len; size -= len;
664 if (lpColumn->mask & LVCF_WIDTH)
665 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
666 else len = 0;
667 if (len == -1) goto end; buf += len; size -= len;
668 if (lpColumn->mask & LVCF_TEXT)
669 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
670 else len = 0;
671 if (len == -1) goto end; buf += len; size -= len;
672 if (lpColumn->mask & LVCF_IMAGE)
673 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
674 else len = 0;
675 if (len == -1) goto end; buf += len; size -= len;
676 if (lpColumn->mask & LVCF_ORDER)
677 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
678 else len = 0;
679 if (len == -1) goto end; buf += len; size -= len;
680 goto undo;
681 end:
682 buf = text + strlen(text);
683 undo:
684 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
685 return text;
688 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
690 if (!lpht) return "(null)";
692 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
693 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
696 /* Return the corresponding text for a given scroll value */
697 static inline LPCSTR debugscrollcode(int nScrollCode)
699 switch(nScrollCode)
701 case SB_LINELEFT: return "SB_LINELEFT";
702 case SB_LINERIGHT: return "SB_LINERIGHT";
703 case SB_PAGELEFT: return "SB_PAGELEFT";
704 case SB_PAGERIGHT: return "SB_PAGERIGHT";
705 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
706 case SB_THUMBTRACK: return "SB_THUMBTRACK";
707 case SB_ENDSCROLL: return "SB_ENDSCROLL";
708 case SB_INTERNAL: return "SB_INTERNAL";
709 default: return "unknown";
714 /******** Notification functions i************************************/
716 static LRESULT notify_forward_header(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
718 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
719 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
722 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
724 LRESULT result;
726 TRACE("(code=%d)\n", code);
728 pnmh->hwndFrom = infoPtr->hwndSelf;
729 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
730 pnmh->code = code;
731 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
732 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
734 TRACE(" <= %ld\n", result);
736 return result;
739 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
741 NMHDR nmh;
742 HWND hwnd = infoPtr->hwndSelf;
743 notify_hdr(infoPtr, code, &nmh);
744 return IsWindow(hwnd);
747 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
749 NMITEMACTIVATE nmia;
750 LVITEMW item;
752 if (htInfo) {
753 nmia.uNewState = 0;
754 nmia.uOldState = 0;
755 nmia.uChanged = 0;
756 nmia.uKeyFlags = 0;
758 item.mask = LVIF_PARAM|LVIF_STATE;
759 item.iItem = htInfo->iItem;
760 item.iSubItem = 0;
761 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
762 nmia.lParam = item.lParam;
763 nmia.uOldState = item.state;
764 nmia.uNewState = item.state | LVIS_ACTIVATING;
765 nmia.uChanged = LVIF_STATE;
768 nmia.iItem = htInfo->iItem;
769 nmia.iSubItem = htInfo->iSubItem;
770 nmia.ptAction = htInfo->pt;
772 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
773 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
774 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
776 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
779 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
781 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
782 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
785 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
787 NMLISTVIEW nmlv;
788 LVITEMW item;
789 HWND hwnd = infoPtr->hwndSelf;
791 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
792 ZeroMemory(&nmlv, sizeof(nmlv));
793 nmlv.iItem = lvht->iItem;
794 nmlv.iSubItem = lvht->iSubItem;
795 nmlv.ptAction = lvht->pt;
796 item.mask = LVIF_PARAM;
797 item.iItem = lvht->iItem;
798 item.iSubItem = 0;
799 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
800 notify_listview(infoPtr, code, &nmlv);
801 return IsWindow(hwnd);
804 static BOOL notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
806 NMLISTVIEW nmlv;
807 LVITEMW item;
808 HWND hwnd = infoPtr->hwndSelf;
810 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
811 nmlv.iItem = nItem;
812 item.mask = LVIF_PARAM;
813 item.iItem = nItem;
814 item.iSubItem = 0;
815 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
816 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
817 return IsWindow(hwnd);
820 static int get_ansi_notification(INT unicodeNotificationCode)
822 switch (unicodeNotificationCode)
824 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
825 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
826 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
827 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
828 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
829 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
831 ERR("unknown notification %x\n", unicodeNotificationCode);
832 assert(FALSE);
833 return 0;
837 Send notification. depends on dispinfoW having same
838 structure as dispinfoA.
839 infoPtr : listview struct
840 notificationCode : *Unicode* notification code
841 pdi : dispinfo structure (can be unicode or ansi)
842 isW : TRUE if dispinfo is Unicode
844 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
846 BOOL bResult = FALSE;
847 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
848 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
849 LPWSTR pszTempBuf = NULL, savPszText = NULL;
851 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
853 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
854 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
857 if (convertToAnsi || convertToUnicode)
859 if (notificationCode != LVN_GETDISPINFOW)
861 cchTempBufMax = convertToUnicode ?
862 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
863 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
865 else
867 cchTempBufMax = pdi->item.cchTextMax;
868 *pdi->item.pszText = 0; /* make sure we don't process garbage */
871 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
872 if (!pszTempBuf) return FALSE;
874 if (convertToUnicode)
875 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
876 pszTempBuf, cchTempBufMax);
877 else
878 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
879 cchTempBufMax, NULL, NULL);
881 savCchTextMax = pdi->item.cchTextMax;
882 savPszText = pdi->item.pszText;
883 pdi->item.pszText = pszTempBuf;
884 pdi->item.cchTextMax = cchTempBufMax;
887 if (infoPtr->notifyFormat == NFR_ANSI)
888 realNotifCode = get_ansi_notification(notificationCode);
889 else
890 realNotifCode = notificationCode;
891 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
892 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
894 if (convertToUnicode || convertToAnsi)
896 if (convertToUnicode) /* note : pointer can be changed by app ! */
897 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
898 savCchTextMax, NULL, NULL);
899 else
900 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
901 savPszText, savCchTextMax);
902 pdi->item.pszText = savPszText; /* restores our buffer */
903 pdi->item.cchTextMax = savCchTextMax;
904 Free (pszTempBuf);
906 return bResult;
909 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
910 const RECT *rcBounds, const LVITEMW *lplvItem)
912 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
913 lpnmlvcd->nmcd.hdc = hdc;
914 lpnmlvcd->nmcd.rc = *rcBounds;
915 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
916 lpnmlvcd->clrText = infoPtr->clrText;
917 if (!lplvItem) return;
918 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
919 lpnmlvcd->iSubItem = lplvItem->iSubItem;
920 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
921 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
922 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
923 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
926 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
928 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
929 DWORD result;
931 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
932 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
933 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
934 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
935 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
936 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
937 return result;
940 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
942 /* apprently, for selected items, we have to override the returned values */
943 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
945 if (infoPtr->bFocus)
947 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
948 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
950 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
952 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
953 lpnmlvcd->clrText = comctl32_color.clrBtnText;
957 /* Set the text attributes */
958 if (lpnmlvcd->clrTextBk != CLR_NONE)
960 SetBkMode(hdc, OPAQUE);
961 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
962 SetBkColor(hdc, infoPtr->clrTextBkDefault);
963 else
964 SetBkColor(hdc,lpnmlvcd->clrTextBk);
966 else
967 SetBkMode(hdc, TRANSPARENT);
968 SetTextColor(hdc, lpnmlvcd->clrText);
971 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
973 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
976 /******** Item iterator functions **********************************/
978 static RANGES ranges_create(int count);
979 static void ranges_destroy(RANGES ranges);
980 static BOOL ranges_add(RANGES ranges, RANGE range);
981 static BOOL ranges_del(RANGES ranges, RANGE range);
982 static void ranges_dump(RANGES ranges);
984 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
986 RANGE range = { nItem, nItem + 1 };
988 return ranges_add(ranges, range);
991 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
993 RANGE range = { nItem, nItem + 1 };
995 return ranges_del(ranges, range);
998 /***
999 * ITERATOR DOCUMENTATION
1001 * The iterator functions allow for easy, and convenient iteration
1002 * over items of iterest in the list. Typically, you create a
1003 * iterator, use it, and destroy it, as such:
1004 * ITERATOR i;
1006 * iterator_xxxitems(&i, ...);
1007 * while (iterator_{prev,next}(&i)
1009 * //code which uses i.nItem
1011 * iterator_destroy(&i);
1013 * where xxx is either: framed, or visible.
1014 * Note that it is important that the code destroys the iterator
1015 * after it's done with it, as the creation of the iterator may
1016 * allocate memory, which thus needs to be freed.
1018 * You can iterate both forwards, and backwards through the list,
1019 * by using iterator_next or iterator_prev respectively.
1021 * Lower numbered items are draw on top of higher number items in
1022 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1023 * items may overlap). So, to test items, you should use
1024 * iterator_next
1025 * which lists the items top to bottom (in Z-order).
1026 * For drawing items, you should use
1027 * iterator_prev
1028 * which lists the items bottom to top (in Z-order).
1029 * If you keep iterating over the items after the end-of-items
1030 * marker (-1) is returned, the iterator will start from the
1031 * beginning. Typically, you don't need to test for -1,
1032 * because iterator_{next,prev} will return TRUE if more items
1033 * are to be iterated over, or FALSE otherwise.
1035 * Note: the iterator is defined to be bidirectional. That is,
1036 * any number of prev followed by any number of next, or
1037 * five versa, should leave the iterator at the same item:
1038 * prev * n, next * n = next * n, prev * n
1040 * The iterator has a notion of an out-of-order, special item,
1041 * which sits at the start of the list. This is used in
1042 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1043 * which needs to be first, as it may overlap other items.
1045 * The code is a bit messy because we have:
1046 * - a special item to deal with
1047 * - simple range, or composite range
1048 * - empty range.
1049 * If you find bugs, or want to add features, please make sure you
1050 * always check/modify *both* iterator_prev, and iterator_next.
1053 /****
1054 * This function iterates through the items in increasing order,
1055 * but prefixed by the special item, then -1. That is:
1056 * special, 1, 2, 3, ..., n, -1.
1057 * Each item is listed only once.
1059 static inline BOOL iterator_next(ITERATOR* i)
1061 if (i->nItem == -1)
1063 i->nItem = i->nSpecial;
1064 if (i->nItem != -1) return TRUE;
1066 if (i->nItem == i->nSpecial)
1068 if (i->ranges) i->index = 0;
1069 goto pickarange;
1072 i->nItem++;
1073 testitem:
1074 if (i->nItem == i->nSpecial) i->nItem++;
1075 if (i->nItem < i->range.upper) return TRUE;
1077 pickarange:
1078 if (i->ranges)
1080 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1081 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1082 else goto end;
1084 else if (i->nItem >= i->range.upper) goto end;
1086 i->nItem = i->range.lower;
1087 if (i->nItem >= 0) goto testitem;
1088 end:
1089 i->nItem = -1;
1090 return FALSE;
1093 /****
1094 * This function iterates through the items in decreasing order,
1095 * followed by the special item, then -1. That is:
1096 * n, n-1, ..., 3, 2, 1, special, -1.
1097 * Each item is listed only once.
1099 static inline BOOL iterator_prev(ITERATOR* i)
1101 BOOL start = FALSE;
1103 if (i->nItem == -1)
1105 start = TRUE;
1106 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1107 goto pickarange;
1109 if (i->nItem == i->nSpecial)
1111 i->nItem = -1;
1112 return FALSE;
1115 testitem:
1116 i->nItem--;
1117 if (i->nItem == i->nSpecial) i->nItem--;
1118 if (i->nItem >= i->range.lower) return TRUE;
1120 pickarange:
1121 if (i->ranges)
1123 if (i->index > 0)
1124 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1125 else goto end;
1127 else if (!start && i->nItem < i->range.lower) goto end;
1129 i->nItem = i->range.upper;
1130 if (i->nItem > 0) goto testitem;
1131 end:
1132 return (i->nItem = i->nSpecial) != -1;
1135 static RANGE iterator_range(ITERATOR* i)
1137 RANGE range;
1139 if (!i->ranges) return i->range;
1141 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1143 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1144 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1146 else range.lower = range.upper = 0;
1148 return range;
1151 /***
1152 * Releases resources associated with this ierator.
1154 static inline void iterator_destroy(ITERATOR* i)
1156 ranges_destroy(i->ranges);
1159 /***
1160 * Create an empty iterator.
1162 static inline BOOL iterator_empty(ITERATOR* i)
1164 ZeroMemory(i, sizeof(*i));
1165 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1166 return TRUE;
1169 /***
1170 * Create an iterator over a range.
1172 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1174 iterator_empty(i);
1175 i->range = range;
1176 return TRUE;
1179 /***
1180 * Create an iterator over a bunch of ranges.
1181 * Please note that the iterator will take ownership of the ranges,
1182 * and will free them upon destruction.
1184 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1186 iterator_empty(i);
1187 i->ranges = ranges;
1188 return TRUE;
1191 /***
1192 * Creates an iterator over the items which intersect lprc.
1194 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1196 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1197 RECT frame = *lprc, rcItem, rcTemp;
1198 POINT Origin;
1200 /* in case we fail, we want to return an empty iterator */
1201 if (!iterator_empty(i)) return FALSE;
1203 LISTVIEW_GetOrigin(infoPtr, &Origin);
1205 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1206 OffsetRect(&frame, -Origin.x, -Origin.y);
1208 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1210 INT nItem;
1212 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1214 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1215 if (IntersectRect(&rcTemp, &rcItem, lprc))
1216 i->nSpecial = infoPtr->nFocusedItem;
1218 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1219 /* to do better here, we need to have PosX, and PosY sorted */
1220 TRACE("building icon ranges:\n");
1221 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1223 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1224 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1225 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1226 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1227 if (IntersectRect(&rcTemp, &rcItem, &frame))
1228 ranges_additem(i->ranges, nItem);
1230 return TRUE;
1232 else if (uView == LVS_REPORT)
1234 RANGE range;
1236 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1237 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1239 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1240 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1241 if (range.upper <= range.lower) return TRUE;
1242 if (!iterator_rangeitems(i, range)) return FALSE;
1243 TRACE(" report=%s\n", debugrange(&i->range));
1245 else
1247 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1248 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1249 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1250 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1251 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1252 INT lower = nFirstCol * nPerCol + nFirstRow;
1253 RANGE item_range;
1254 INT nCol;
1256 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1257 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1259 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1261 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1262 TRACE("building list ranges:\n");
1263 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1265 item_range.lower = nCol * nPerCol + nFirstRow;
1266 if(item_range.lower >= infoPtr->nItemCount) break;
1267 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1268 TRACE(" list=%s\n", debugrange(&item_range));
1269 ranges_add(i->ranges, item_range);
1273 return TRUE;
1276 /***
1277 * Creates an iterator over the items which intersect the visible region of hdc.
1279 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1281 POINT Origin, Position;
1282 RECT rcItem, rcClip;
1283 INT rgntype;
1285 rgntype = GetClipBox(hdc, &rcClip);
1286 if (rgntype == NULLREGION) return iterator_empty(i);
1287 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1288 if (rgntype == SIMPLEREGION) return TRUE;
1290 /* first deal with the special item */
1291 if (i->nSpecial != -1)
1293 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1294 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1297 /* if we can't deal with the region, we'll just go with the simple range */
1298 LISTVIEW_GetOrigin(infoPtr, &Origin);
1299 TRACE("building visible range:\n");
1300 if (!i->ranges && i->range.lower < i->range.upper)
1302 if (!(i->ranges = ranges_create(50))) return TRUE;
1303 if (!ranges_add(i->ranges, i->range))
1305 ranges_destroy(i->ranges);
1306 i->ranges = 0;
1307 return TRUE;
1311 /* now delete the invisible items from the list */
1312 while(iterator_next(i))
1314 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1315 rcItem.left = Position.x + Origin.x;
1316 rcItem.top = Position.y + Origin.y;
1317 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1318 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1319 if (!RectVisible(hdc, &rcItem))
1320 ranges_delitem(i->ranges, i->nItem);
1322 /* the iterator should restart on the next iterator_next */
1323 TRACE("done\n");
1325 return TRUE;
1328 /******** Misc helper functions ************************************/
1330 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1331 WPARAM wParam, LPARAM lParam, BOOL isW)
1333 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1334 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1337 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1339 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1341 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1342 (uView == LVS_ICON || uView == LVS_SMALLICON);
1345 /******** Internal API functions ************************************/
1347 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1349 static COLUMN_INFO mainItem;
1351 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1352 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1353 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1356 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1358 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1361 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1363 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1366 /* Listview invalidation functions: use _only_ these functions to invalidate */
1368 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1370 return infoPtr->bRedraw;
1373 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1375 if(!is_redrawing(infoPtr)) return;
1376 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1377 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1380 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1382 RECT rcBox;
1384 if(!is_redrawing(infoPtr)) return;
1385 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1386 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1389 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1391 POINT Origin, Position;
1392 RECT rcBox;
1394 if(!is_redrawing(infoPtr)) return;
1395 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1396 LISTVIEW_GetOrigin(infoPtr, &Origin);
1397 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1398 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1399 rcBox.top = 0;
1400 rcBox.bottom = infoPtr->nItemHeight;
1401 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1402 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1405 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1407 LISTVIEW_InvalidateRect(infoPtr, NULL);
1410 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1412 RECT rcCol;
1414 if(!is_redrawing(infoPtr)) return;
1415 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1416 rcCol.top = infoPtr->rcList.top;
1417 rcCol.bottom = infoPtr->rcList.bottom;
1418 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1421 /***
1422 * DESCRIPTION:
1423 * Retrieves the number of items that can fit vertically in the client area.
1425 * PARAMETER(S):
1426 * [I] infoPtr : valid pointer to the listview structure
1428 * RETURN:
1429 * Number of items per row.
1431 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1433 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1435 return max(nListWidth/infoPtr->nItemWidth, 1);
1438 /***
1439 * DESCRIPTION:
1440 * Retrieves the number of items that can fit horizontally in the client
1441 * area.
1443 * PARAMETER(S):
1444 * [I] infoPtr : valid pointer to the listview structure
1446 * RETURN:
1447 * Number of items per column.
1449 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1451 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1453 return max(nListHeight / infoPtr->nItemHeight, 1);
1457 /*************************************************************************
1458 * LISTVIEW_ProcessLetterKeys
1460 * Processes keyboard messages generated by pressing the letter keys
1461 * on the keyboard.
1462 * What this does is perform a case insensitive search from the
1463 * current position with the following quirks:
1464 * - If two chars or more are pressed in quick succession we search
1465 * for the corresponding string (e.g. 'abc').
1466 * - If there is a delay we wipe away the current search string and
1467 * restart with just that char.
1468 * - If the user keeps pressing the same character, whether slowly or
1469 * fast, so that the search string is entirely composed of this
1470 * character ('aaaaa' for instance), then we search for first item
1471 * that starting with that character.
1472 * - If the user types the above character in quick succession, then
1473 * we must also search for the corresponding string ('aaaaa'), and
1474 * go to that string if there is a match.
1476 * PARAMETERS
1477 * [I] hwnd : handle to the window
1478 * [I] charCode : the character code, the actual character
1479 * [I] keyData : key data
1481 * RETURNS
1483 * Zero.
1485 * BUGS
1487 * - The current implementation has a list of characters it will
1488 * accept and it ignores averything else. In particular it will
1489 * ignore accentuated characters which seems to match what
1490 * Windows does. But I'm not sure it makes sense to follow
1491 * Windows there.
1492 * - We don't sound a beep when the search fails.
1494 * SEE ALSO
1496 * TREEVIEW_ProcessLetterKeys
1498 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1500 INT nItem;
1501 INT endidx,idx;
1502 LVITEMW item;
1503 WCHAR buffer[MAX_PATH];
1504 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1506 /* simple parameter checking */
1507 if (!charCode || !keyData) return 0;
1509 /* only allow the valid WM_CHARs through */
1510 if (!isalnum(charCode) &&
1511 charCode != '.' && charCode != '`' && charCode != '!' &&
1512 charCode != '@' && charCode != '#' && charCode != '$' &&
1513 charCode != '%' && charCode != '^' && charCode != '&' &&
1514 charCode != '*' && charCode != '(' && charCode != ')' &&
1515 charCode != '-' && charCode != '_' && charCode != '+' &&
1516 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1517 charCode != '}' && charCode != '[' && charCode != '{' &&
1518 charCode != '/' && charCode != '?' && charCode != '>' &&
1519 charCode != '<' && charCode != ',' && charCode != '~')
1520 return 0;
1522 /* if there's one item or less, there is no where to go */
1523 if (infoPtr->nItemCount <= 1) return 0;
1525 /* update the search parameters */
1526 infoPtr->lastKeyPressTimestamp = GetTickCount();
1527 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1528 if (infoPtr->nSearchParamLength < MAX_PATH)
1529 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1530 if (infoPtr->charCode != charCode)
1531 infoPtr->charCode = charCode = 0;
1532 } else {
1533 infoPtr->charCode=charCode;
1534 infoPtr->szSearchParam[0]=charCode;
1535 infoPtr->nSearchParamLength=1;
1536 /* Redundant with the 1 char string */
1537 charCode=0;
1540 /* and search from the current position */
1541 nItem=-1;
1542 if (infoPtr->nFocusedItem >= 0) {
1543 endidx=infoPtr->nFocusedItem;
1544 idx=endidx;
1545 /* if looking for single character match,
1546 * then we must always move forward
1548 if (infoPtr->nSearchParamLength == 1)
1549 idx++;
1550 } else {
1551 endidx=infoPtr->nItemCount;
1552 idx=0;
1554 do {
1555 if (idx == infoPtr->nItemCount) {
1556 if (endidx == infoPtr->nItemCount || endidx == 0)
1557 break;
1558 idx=0;
1561 /* get item */
1562 item.mask = LVIF_TEXT;
1563 item.iItem = idx;
1564 item.iSubItem = 0;
1565 item.pszText = buffer;
1566 item.cchTextMax = MAX_PATH;
1567 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1569 /* check for a match */
1570 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1571 nItem=idx;
1572 break;
1573 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1574 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1575 /* This would work but we must keep looking for a longer match */
1576 nItem=idx;
1578 idx++;
1579 } while (idx != endidx);
1581 if (nItem != -1)
1582 LISTVIEW_KeySelection(infoPtr, nItem);
1584 return 0;
1587 /*************************************************************************
1588 * LISTVIEW_UpdateHeaderSize [Internal]
1590 * Function to resize the header control
1592 * PARAMS
1593 * [I] hwnd : handle to a window
1594 * [I] nNewScrollPos : scroll pos to set
1596 * RETURNS
1597 * None.
1599 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1601 RECT winRect;
1602 POINT point[2];
1604 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1606 GetWindowRect(infoPtr->hwndHeader, &winRect);
1607 point[0].x = winRect.left;
1608 point[0].y = winRect.top;
1609 point[1].x = winRect.right;
1610 point[1].y = winRect.bottom;
1612 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1613 point[0].x = -nNewScrollPos;
1614 point[1].x += nNewScrollPos;
1616 SetWindowPos(infoPtr->hwndHeader,0,
1617 point[0].x,point[0].y,point[1].x,point[1].y,
1618 SWP_NOZORDER | SWP_NOACTIVATE);
1621 /***
1622 * DESCRIPTION:
1623 * Update the scrollbars. This functions should be called whenever
1624 * the content, size or view changes.
1626 * PARAMETER(S):
1627 * [I] infoPtr : valid pointer to the listview structure
1629 * RETURN:
1630 * None
1632 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1634 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1635 SCROLLINFO horzInfo, vertInfo;
1637 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1639 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1640 horzInfo.cbSize = sizeof(SCROLLINFO);
1641 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1643 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1644 if (uView == LVS_LIST)
1646 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1647 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1649 /* scroll by at least one column per page */
1650 if(horzInfo.nPage < infoPtr->nItemWidth)
1651 horzInfo.nPage = infoPtr->nItemWidth;
1653 horzInfo.nPage /= infoPtr->nItemWidth;
1655 else if (uView == LVS_REPORT)
1657 horzInfo.nMax = infoPtr->nItemWidth;
1659 else /* LVS_ICON, or LVS_SMALLICON */
1661 RECT rcView;
1663 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1666 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1667 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1668 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1669 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1671 /* Setting the horizontal scroll can change the listview size
1672 * (and potentially everything else) so we need to recompute
1673 * everything again for the vertical scroll
1676 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1677 vertInfo.cbSize = sizeof(SCROLLINFO);
1678 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1680 if (uView == LVS_REPORT)
1682 vertInfo.nMax = infoPtr->nItemCount;
1684 /* scroll by at least one page */
1685 if(vertInfo.nPage < infoPtr->nItemHeight)
1686 vertInfo.nPage = infoPtr->nItemHeight;
1688 vertInfo.nPage /= infoPtr->nItemHeight;
1690 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1692 RECT rcView;
1694 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1697 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1698 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1699 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1700 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1702 /* Update the Header Control */
1703 if (uView == LVS_REPORT)
1705 horzInfo.fMask = SIF_POS;
1706 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1707 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1712 /***
1713 * DESCRIPTION:
1714 * Shows/hides the focus rectangle.
1716 * PARAMETER(S):
1717 * [I] infoPtr : valid pointer to the listview structure
1718 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1720 * RETURN:
1721 * None
1723 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1725 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1726 HDC hdc;
1728 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1730 if (infoPtr->nFocusedItem < 0) return;
1732 /* we need some gymnastics in ICON mode to handle large items */
1733 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1735 RECT rcBox;
1737 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1738 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1740 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1741 return;
1745 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1747 /* for some reason, owner draw should work only in report mode */
1748 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1750 DRAWITEMSTRUCT dis;
1751 LVITEMW item;
1753 item.iItem = infoPtr->nFocusedItem;
1754 item.iSubItem = 0;
1755 item.mask = LVIF_PARAM;
1756 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1758 ZeroMemory(&dis, sizeof(dis));
1759 dis.CtlType = ODT_LISTVIEW;
1760 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1761 dis.itemID = item.iItem;
1762 dis.itemAction = ODA_FOCUS;
1763 if (fShow) dis.itemState |= ODS_FOCUS;
1764 dis.hwndItem = infoPtr->hwndSelf;
1765 dis.hDC = hdc;
1766 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1767 dis.itemData = item.lParam;
1769 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1771 else
1773 DrawFocusRect(hdc, &infoPtr->rcFocus);
1775 done:
1776 ReleaseDC(infoPtr->hwndSelf, hdc);
1779 /***
1780 * Invalidates all visible selected items.
1782 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1784 ITERATOR i;
1786 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1787 while(iterator_next(&i))
1789 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1790 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1792 iterator_destroy(&i);
1796 /***
1797 * DESCRIPTION: [INTERNAL]
1798 * Computes an item's (left,top) corner, relative to rcView.
1799 * That is, the position has NOT been made relative to the Origin.
1800 * This is deliberate, to avoid computing the Origin over, and
1801 * over again, when this function is call in a loop. Instead,
1802 * one ca factor the computation of the Origin before the loop,
1803 * and offset the value retured by this function, on every iteration.
1805 * PARAMETER(S):
1806 * [I] infoPtr : valid pointer to the listview structure
1807 * [I] nItem : item number
1808 * [O] lpptOrig : item top, left corner
1810 * RETURN:
1811 * None.
1813 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1815 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1817 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1819 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1821 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1822 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1824 else if (uView == LVS_LIST)
1826 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1827 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1828 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1830 else /* LVS_REPORT */
1832 lpptPosition->x = 0;
1833 lpptPosition->y = nItem * infoPtr->nItemHeight;
1837 /***
1838 * DESCRIPTION: [INTERNAL]
1839 * Compute the rectangles of an item. This is to localize all
1840 * the computations in one place. If you are not interested in some
1841 * of these values, simply pass in a NULL -- the fucntion is smart
1842 * enough to compute only what's necessary. The function computes
1843 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1844 * one, the BOX rectangle. This rectangle is very cheap to compute,
1845 * and is guaranteed to contain all the other rectangles. Computing
1846 * the ICON rect is also cheap, but all the others are potentaily
1847 * expensive. This gives an easy and effective optimization when
1848 * searching (like point inclusion, or rectangle intersection):
1849 * first test against the BOX, and if TRUE, test agains the desired
1850 * rectangle.
1851 * If the function does not have all the necessary information
1852 * to computed the requested rectangles, will crash with a
1853 * failed assertion. This is done so we catch all programming
1854 * errors, given that the function is called only from our code.
1856 * We have the following 'special' meanings for a few fields:
1857 * * If LVIS_FOCUSED is set, we assume the item has the focus
1858 * This is important in ICON mode, where it might get a larger
1859 * then usual rectange
1861 * Please note that subitem support works only in REPORT mode.
1863 * PARAMETER(S):
1864 * [I] infoPtr : valid pointer to the listview structure
1865 * [I] lpLVItem : item to compute the measures for
1866 * [O] lprcBox : ptr to Box rectangle
1867 * The internal LVIR_BOX rectangle
1868 * [0] lprcState : ptr to State icon rectangle
1869 * The internal LVIR_STATE rectangle
1870 * [O] lprcIcon : ptr to Icon rectangle
1871 * Same as LVM_GETITEMRECT with LVIR_ICON
1872 * [O] lprcLabel : ptr to Label rectangle
1873 * Same as LVM_GETITEMRECT with LVIR_LABEL
1875 * RETURN:
1876 * None.
1878 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1879 LPRECT lprcBox, LPRECT lprcState,
1880 LPRECT lprcIcon, LPRECT lprcLabel)
1882 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1883 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1884 RECT Box, State, Icon, Label;
1885 COLUMN_INFO *lpColumnInfo = NULL;
1887 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1889 /* Be smart and try to figure out the minimum we have to do */
1890 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1891 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1893 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1894 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1896 if (lprcLabel) doLabel = TRUE;
1897 if (doLabel || lprcIcon) doIcon = TRUE;
1898 if (doIcon || lprcState) doState = TRUE;
1900 /************************************************************/
1901 /* compute the box rectangle (it should be cheap to do) */
1902 /************************************************************/
1903 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1904 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1906 if (lpLVItem->iSubItem)
1908 Box = lpColumnInfo->rcHeader;
1910 else
1912 Box.left = 0;
1913 Box.right = infoPtr->nItemWidth;
1915 Box.top = 0;
1916 Box.bottom = infoPtr->nItemHeight;
1918 /************************************************************/
1919 /* compute STATEICON bounding box */
1920 /************************************************************/
1921 if (doState)
1923 if (uView == LVS_ICON)
1925 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1926 if (infoPtr->himlNormal)
1927 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1928 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1930 else
1932 /* we need the ident in report mode, if we don't have it, we fail */
1933 State.left = Box.left;
1934 if (uView == LVS_REPORT)
1936 if (lpLVItem->iSubItem == 0)
1938 State.left += REPORT_MARGINX;
1939 assert(lpLVItem->mask & LVIF_INDENT);
1940 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1943 State.top = Box.top;
1945 State.right = State.left;
1946 State.bottom = State.top;
1947 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1949 State.right += infoPtr->iconStateSize.cx;
1950 State.bottom += infoPtr->iconStateSize.cy;
1952 if (lprcState) *lprcState = State;
1953 TRACE(" - state=%s\n", wine_dbgstr_rect(&State));
1955 else State.right = 0;
1957 /************************************************************/
1958 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1959 /************************************************************/
1960 if (doIcon)
1962 if (uView == LVS_ICON)
1964 Icon.left = Box.left;
1965 if (infoPtr->himlNormal)
1966 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1967 Icon.top = Box.top + ICON_TOP_PADDING;
1968 Icon.right = Icon.left;
1969 Icon.bottom = Icon.top;
1970 if (infoPtr->himlNormal)
1972 Icon.right += infoPtr->iconSize.cx;
1973 Icon.bottom += infoPtr->iconSize.cy;
1976 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1978 Icon.left = State.right;
1979 Icon.top = Box.top;
1980 Icon.right = Icon.left;
1981 if (infoPtr->himlSmall &&
1982 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1983 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1984 Icon.right += infoPtr->iconSize.cx;
1985 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1987 if(lprcIcon) *lprcIcon = Icon;
1988 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
1990 else Icon.right = 0;
1992 /************************************************************/
1993 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1994 /************************************************************/
1995 if (doLabel)
1997 SIZE labelSize = { 0, 0 };
1999 /* calculate how far to the right can the label strech */
2000 Label.right = Box.right;
2001 if (uView == LVS_REPORT)
2003 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2006 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2008 labelSize.cx = infoPtr->nItemWidth;
2009 labelSize.cy = infoPtr->nItemHeight;
2010 goto calc_label;
2013 /* we need the text in non owner draw mode */
2014 assert(lpLVItem->mask & LVIF_TEXT);
2015 if (is_textT(lpLVItem->pszText, TRUE))
2017 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2018 HDC hdc = GetDC(infoPtr->hwndSelf);
2019 HFONT hOldFont = SelectObject(hdc, hFont);
2020 UINT uFormat;
2021 RECT rcText;
2023 /* compute rough rectangle where the label will go */
2024 SetRectEmpty(&rcText);
2025 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2026 rcText.bottom = infoPtr->nItemHeight;
2027 if (uView == LVS_ICON)
2028 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2030 /* now figure out the flags */
2031 if (uView == LVS_ICON)
2032 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2033 else
2034 uFormat = LV_SL_DT_FLAGS;
2036 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2038 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2039 labelSize.cy = rcText.bottom - rcText.top;
2041 SelectObject(hdc, hOldFont);
2042 ReleaseDC(infoPtr->hwndSelf, hdc);
2045 calc_label:
2046 if (uView == LVS_ICON)
2048 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2049 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2050 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2051 Label.right = Label.left + labelSize.cx;
2052 Label.bottom = Label.top + infoPtr->nItemHeight;
2053 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2055 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2056 labelSize.cy /= infoPtr->ntmHeight;
2057 labelSize.cy = max(labelSize.cy, 1);
2058 labelSize.cy *= infoPtr->ntmHeight;
2060 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2062 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2064 Label.left = Icon.right;
2065 Label.top = Box.top;
2066 Label.right = min(Label.left + labelSize.cx, Label.right);
2067 Label.bottom = Label.top + infoPtr->nItemHeight;
2070 if (lprcLabel) *lprcLabel = Label;
2071 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2074 /* Fix the Box if necessary */
2075 if (lprcBox)
2077 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2078 else *lprcBox = Box;
2080 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2083 /***
2084 * DESCRIPTION: [INTERNAL]
2086 * PARAMETER(S):
2087 * [I] infoPtr : valid pointer to the listview structure
2088 * [I] nItem : item number
2089 * [O] lprcBox : ptr to Box rectangle
2091 * RETURN:
2092 * None.
2094 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2096 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2097 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2098 POINT Position, Origin;
2099 LVITEMW lvItem;
2101 LISTVIEW_GetOrigin(infoPtr, &Origin);
2102 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2104 /* Be smart and try to figure out the minimum we have to do */
2105 lvItem.mask = 0;
2106 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2107 lvItem.mask |= LVIF_TEXT;
2108 lvItem.iItem = nItem;
2109 lvItem.iSubItem = 0;
2110 lvItem.pszText = szDispText;
2111 lvItem.cchTextMax = DISP_TEXT_SIZE;
2112 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2113 if (uView == LVS_ICON)
2115 lvItem.mask |= LVIF_STATE;
2116 lvItem.stateMask = LVIS_FOCUSED;
2117 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2119 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2121 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2125 /***
2126 * DESCRIPTION:
2127 * Returns the current icon position, and advances it along the top.
2128 * The returned position is not offset by Origin.
2130 * PARAMETER(S):
2131 * [I] infoPtr : valid pointer to the listview structure
2132 * [O] lpPos : will get the current icon position
2134 * RETURN:
2135 * None
2137 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2139 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2141 *lpPos = infoPtr->currIconPos;
2143 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2144 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2146 infoPtr->currIconPos.x = 0;
2147 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2151 /***
2152 * DESCRIPTION:
2153 * Returns the current icon position, and advances it down the left edge.
2154 * The returned position is not offset by Origin.
2156 * PARAMETER(S):
2157 * [I] infoPtr : valid pointer to the listview structure
2158 * [O] lpPos : will get the current icon position
2160 * RETURN:
2161 * None
2163 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2165 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2167 *lpPos = infoPtr->currIconPos;
2169 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2170 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2172 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2173 infoPtr->currIconPos.y = 0;
2177 /***
2178 * DESCRIPTION:
2179 * Moves an icon to the specified position.
2180 * It takes care of invalidating the item, etc.
2182 * PARAMETER(S):
2183 * [I] infoPtr : valid pointer to the listview structure
2184 * [I] nItem : the item to move
2185 * [I] lpPos : the new icon position
2186 * [I] isNew : flags the item as being new
2188 * RETURN:
2189 * Success: TRUE
2190 * Failure: FALSE
2192 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2194 POINT old;
2196 if (!isNew)
2198 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2199 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2201 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2202 LISTVIEW_InvalidateItem(infoPtr, nItem);
2205 /* Allocating a POINTER for every item is too resource intensive,
2206 * so we'll keep the (x,y) in different arrays */
2207 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2208 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2210 LISTVIEW_InvalidateItem(infoPtr, nItem);
2212 return TRUE;
2215 /***
2216 * DESCRIPTION:
2217 * Arranges listview items in icon display mode.
2219 * PARAMETER(S):
2220 * [I] infoPtr : valid pointer to the listview structure
2221 * [I] nAlignCode : alignment code
2223 * RETURN:
2224 * SUCCESS : TRUE
2225 * FAILURE : FALSE
2227 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2229 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2230 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2231 POINT pos;
2232 INT i;
2234 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2236 TRACE("nAlignCode=%d\n", nAlignCode);
2238 if (nAlignCode == LVA_DEFAULT)
2240 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2241 else nAlignCode = LVA_ALIGNTOP;
2244 switch (nAlignCode)
2246 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2247 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2248 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2249 default: return FALSE;
2252 infoPtr->bAutoarrange = TRUE;
2253 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2254 for (i = 0; i < infoPtr->nItemCount; i++)
2256 next_pos(infoPtr, &pos);
2257 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2260 return TRUE;
2263 /***
2264 * DESCRIPTION:
2265 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2267 * PARAMETER(S):
2268 * [I] infoPtr : valid pointer to the listview structure
2269 * [O] lprcView : bounding rectangle
2271 * RETURN:
2272 * SUCCESS : TRUE
2273 * FAILURE : FALSE
2275 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2277 INT i, x, y;
2279 SetRectEmpty(lprcView);
2281 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2283 case LVS_ICON:
2284 case LVS_SMALLICON:
2285 for (i = 0; i < infoPtr->nItemCount; i++)
2287 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2288 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2289 lprcView->right = max(lprcView->right, x);
2290 lprcView->bottom = max(lprcView->bottom, y);
2292 if (infoPtr->nItemCount > 0)
2294 lprcView->right += infoPtr->nItemWidth;
2295 lprcView->bottom += infoPtr->nItemHeight;
2297 break;
2299 case LVS_LIST:
2300 y = LISTVIEW_GetCountPerColumn(infoPtr);
2301 x = infoPtr->nItemCount / y;
2302 if (infoPtr->nItemCount % y) x++;
2303 lprcView->right = x * infoPtr->nItemWidth;
2304 lprcView->bottom = y * infoPtr->nItemHeight;
2305 break;
2307 case LVS_REPORT:
2308 lprcView->right = infoPtr->nItemWidth;
2309 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2310 break;
2314 /***
2315 * DESCRIPTION:
2316 * Retrieves the bounding rectangle of all the items.
2318 * PARAMETER(S):
2319 * [I] infoPtr : valid pointer to the listview structure
2320 * [O] lprcView : bounding rectangle
2322 * RETURN:
2323 * SUCCESS : TRUE
2324 * FAILURE : FALSE
2326 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2328 POINT ptOrigin;
2330 TRACE("(lprcView=%p)\n", lprcView);
2332 if (!lprcView) return FALSE;
2334 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2335 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2336 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2338 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2340 return TRUE;
2343 /***
2344 * DESCRIPTION:
2345 * Retrieves the subitem pointer associated with the subitem index.
2347 * PARAMETER(S):
2348 * [I] hdpaSubItems : DPA handle for a specific item
2349 * [I] nSubItem : index of subitem
2351 * RETURN:
2352 * SUCCESS : subitem pointer
2353 * FAILURE : NULL
2355 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2357 SUBITEM_INFO *lpSubItem;
2358 INT i;
2360 /* we should binary search here if need be */
2361 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2363 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2364 if (lpSubItem->iSubItem == nSubItem)
2365 return lpSubItem;
2368 return NULL;
2372 /***
2373 * DESCRIPTION:
2374 * Caclulates the desired item width.
2376 * PARAMETER(S):
2377 * [I] infoPtr : valid pointer to the listview structure
2379 * RETURN:
2380 * The desired item width.
2382 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2384 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2385 INT nItemWidth = 0;
2387 TRACE("uView=%d\n", uView);
2389 if (uView == LVS_ICON)
2390 nItemWidth = infoPtr->iconSpacing.cx;
2391 else if (uView == LVS_REPORT)
2393 RECT rcHeader;
2395 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2397 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2398 nItemWidth = rcHeader.right;
2401 else /* LVS_SMALLICON, or LVS_LIST */
2403 INT i;
2405 for (i = 0; i < infoPtr->nItemCount; i++)
2406 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2408 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2409 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2411 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2414 return max(nItemWidth, 1);
2417 /***
2418 * DESCRIPTION:
2419 * Caclulates the desired item height.
2421 * PARAMETER(S):
2422 * [I] infoPtr : valid pointer to the listview structure
2424 * RETURN:
2425 * The desired item height.
2427 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2429 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2430 INT nItemHeight;
2432 TRACE("uView=%d\n", uView);
2434 if (uView == LVS_ICON)
2435 nItemHeight = infoPtr->iconSpacing.cy;
2436 else
2438 nItemHeight = infoPtr->ntmHeight;
2439 if (infoPtr->himlState)
2440 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2441 if (infoPtr->himlSmall)
2442 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2443 if (infoPtr->himlState || infoPtr->himlSmall)
2444 nItemHeight += HEIGHT_PADDING;
2445 if (infoPtr->nMeasureItemHeight > 0)
2446 nItemHeight = infoPtr->nMeasureItemHeight;
2449 return max(nItemHeight, 1);
2452 /***
2453 * DESCRIPTION:
2454 * Updates the width, and height of an item.
2456 * PARAMETER(S):
2457 * [I] infoPtr : valid pointer to the listview structure
2459 * RETURN:
2460 * None.
2462 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2464 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2465 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2469 /***
2470 * DESCRIPTION:
2471 * Retrieves and saves important text metrics info for the current
2472 * Listview font.
2474 * PARAMETER(S):
2475 * [I] infoPtr : valid pointer to the listview structure
2478 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2480 HDC hdc = GetDC(infoPtr->hwndSelf);
2481 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2482 HFONT hOldFont = SelectObject(hdc, hFont);
2483 TEXTMETRICW tm;
2484 SIZE sz;
2486 if (GetTextMetricsW(hdc, &tm))
2488 infoPtr->ntmHeight = tm.tmHeight;
2489 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2492 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2493 infoPtr->nEllipsisWidth = sz.cx;
2495 SelectObject(hdc, hOldFont);
2496 ReleaseDC(infoPtr->hwndSelf, hdc);
2498 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2501 /***
2502 * DESCRIPTION:
2503 * A compare function for ranges
2505 * PARAMETER(S)
2506 * [I] range1 : pointer to range 1;
2507 * [I] range2 : pointer to range 2;
2508 * [I] flags : flags
2510 * RETURNS:
2511 * > 0 : if range 1 > range 2
2512 * < 0 : if range 2 > range 1
2513 * = 0 : if range intersects range 2
2515 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2517 INT cmp;
2519 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2520 cmp = -1;
2521 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2522 cmp = 1;
2523 else
2524 cmp = 0;
2526 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2528 return cmp;
2531 #if DEBUG_RANGES
2532 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2533 #else
2534 #define ranges_check(ranges, desc) do { } while(0)
2535 #endif
2537 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2539 INT i;
2540 RANGE *prev, *curr;
2542 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2543 assert (ranges);
2544 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2545 ranges_dump(ranges);
2546 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2547 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2548 assert (prev->lower >= 0 && prev->lower < prev->upper);
2549 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2551 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2552 assert (prev->upper <= curr->lower);
2553 assert (curr->lower < curr->upper);
2554 prev = curr;
2556 TRACE("--- Done checking---\n");
2559 static RANGES ranges_create(int count)
2561 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2562 if (!ranges) return NULL;
2563 ranges->hdpa = DPA_Create(count);
2564 if (ranges->hdpa) return ranges;
2565 Free(ranges);
2566 return NULL;
2569 static void ranges_clear(RANGES ranges)
2571 INT i;
2573 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2574 Free(DPA_GetPtr(ranges->hdpa, i));
2575 DPA_DeleteAllPtrs(ranges->hdpa);
2579 static void ranges_destroy(RANGES ranges)
2581 if (!ranges) return;
2582 ranges_clear(ranges);
2583 DPA_Destroy(ranges->hdpa);
2584 Free(ranges);
2587 static RANGES ranges_clone(RANGES ranges)
2589 RANGES clone;
2590 INT i;
2592 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2594 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2596 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2597 if (!newrng) goto fail;
2598 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2599 DPA_SetPtr(clone->hdpa, i, newrng);
2601 return clone;
2603 fail:
2604 TRACE ("clone failed\n");
2605 ranges_destroy(clone);
2606 return NULL;
2609 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2611 INT i;
2613 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2614 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2616 return ranges;
2619 static void ranges_dump(RANGES ranges)
2621 INT i;
2623 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2624 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2627 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2629 RANGE srchrng = { nItem, nItem + 1 };
2631 TRACE("(nItem=%d)\n", nItem);
2632 ranges_check(ranges, "before contain");
2633 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2636 static INT ranges_itemcount(RANGES ranges)
2638 INT i, count = 0;
2640 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2642 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2643 count += sel->upper - sel->lower;
2646 return count;
2649 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2651 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2652 INT index;
2654 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2655 if (index == -1) return TRUE;
2657 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2659 chkrng = DPA_GetPtr(ranges->hdpa, index);
2660 if (chkrng->lower >= nItem)
2661 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2662 if (chkrng->upper > nItem)
2663 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2665 return TRUE;
2668 static BOOL ranges_add(RANGES ranges, RANGE range)
2670 RANGE srchrgn;
2671 INT index;
2673 TRACE("(%s)\n", debugrange(&range));
2674 ranges_check(ranges, "before add");
2676 /* try find overlapping regions first */
2677 srchrgn.lower = range.lower - 1;
2678 srchrgn.upper = range.upper + 1;
2679 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2681 if (index == -1)
2683 RANGE *newrgn;
2685 TRACE("Adding new range\n");
2687 /* create the brand new range to insert */
2688 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2689 if(!newrgn) goto fail;
2690 *newrgn = range;
2692 /* figure out where to insert it */
2693 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2694 TRACE("index=%d\n", index);
2695 if (index == -1) index = 0;
2697 /* and get it over with */
2698 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2700 Free(newrgn);
2701 goto fail;
2704 else
2706 RANGE *chkrgn, *mrgrgn;
2707 INT fromindex, mergeindex;
2709 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2710 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2712 chkrgn->lower = min(range.lower, chkrgn->lower);
2713 chkrgn->upper = max(range.upper, chkrgn->upper);
2715 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2717 /* merge now common anges */
2718 fromindex = 0;
2719 srchrgn.lower = chkrgn->lower - 1;
2720 srchrgn.upper = chkrgn->upper + 1;
2724 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2725 if (mergeindex == -1) break;
2726 if (mergeindex == index)
2728 fromindex = index + 1;
2729 continue;
2732 TRACE("Merge with index %i\n", mergeindex);
2734 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2735 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2736 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2737 Free(mrgrgn);
2738 DPA_DeletePtr(ranges->hdpa, mergeindex);
2739 if (mergeindex < index) index --;
2740 } while(1);
2743 ranges_check(ranges, "after add");
2744 return TRUE;
2746 fail:
2747 ranges_check(ranges, "failed add");
2748 return FALSE;
2751 static BOOL ranges_del(RANGES ranges, RANGE range)
2753 RANGE *chkrgn;
2754 INT index;
2756 TRACE("(%s)\n", debugrange(&range));
2757 ranges_check(ranges, "before del");
2759 /* we don't use DPAS_SORTED here, since we need *
2760 * to find the first overlapping range */
2761 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2762 while(index != -1)
2764 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2766 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2768 /* case 1: Same range */
2769 if ( (chkrgn->upper == range.upper) &&
2770 (chkrgn->lower == range.lower) )
2772 DPA_DeletePtr(ranges->hdpa, index);
2773 break;
2775 /* case 2: engulf */
2776 else if ( (chkrgn->upper <= range.upper) &&
2777 (chkrgn->lower >= range.lower) )
2779 DPA_DeletePtr(ranges->hdpa, index);
2781 /* case 3: overlap upper */
2782 else if ( (chkrgn->upper <= range.upper) &&
2783 (chkrgn->lower < range.lower) )
2785 chkrgn->upper = range.lower;
2787 /* case 4: overlap lower */
2788 else if ( (chkrgn->upper > range.upper) &&
2789 (chkrgn->lower >= range.lower) )
2791 chkrgn->lower = range.upper;
2792 break;
2794 /* case 5: fully internal */
2795 else
2797 RANGE tmprgn = *chkrgn, *newrgn;
2799 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2800 newrgn->lower = chkrgn->lower;
2801 newrgn->upper = range.lower;
2802 chkrgn->lower = range.upper;
2803 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2805 Free(newrgn);
2806 goto fail;
2808 chkrgn = &tmprgn;
2809 break;
2812 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2815 ranges_check(ranges, "after del");
2816 return TRUE;
2818 fail:
2819 ranges_check(ranges, "failed del");
2820 return FALSE;
2823 /***
2824 * DESCRIPTION:
2825 * Removes all selection ranges
2827 * Parameters(s):
2828 * [I] infoPtr : valid pointer to the listview structure
2829 * [I] toSkip : item range to skip removing the selection
2831 * RETURNS:
2832 * SUCCESS : TRUE
2833 * FAILURE : TRUE
2835 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2837 LVITEMW lvItem;
2838 ITERATOR i;
2839 RANGES clone;
2841 TRACE("()\n");
2843 lvItem.state = 0;
2844 lvItem.stateMask = LVIS_SELECTED;
2846 /* need to clone the DPA because callbacks can change it */
2847 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2848 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2849 while(iterator_next(&i))
2850 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2851 /* note that the iterator destructor will free the cloned range */
2852 iterator_destroy(&i);
2854 return TRUE;
2857 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2859 RANGES toSkip;
2861 if (!(toSkip = ranges_create(1))) return FALSE;
2862 if (nItem != -1) ranges_additem(toSkip, nItem);
2863 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2864 ranges_destroy(toSkip);
2865 return TRUE;
2868 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2870 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2873 /***
2874 * DESCRIPTION:
2875 * Retrieves the number of items that are marked as selected.
2877 * PARAMETER(S):
2878 * [I] infoPtr : valid pointer to the listview structure
2880 * RETURN:
2881 * Number of items selected.
2883 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2885 INT nSelectedCount = 0;
2887 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2889 INT i;
2890 for (i = 0; i < infoPtr->nItemCount; i++)
2892 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2893 nSelectedCount++;
2896 else
2897 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2899 TRACE("nSelectedCount=%d\n", nSelectedCount);
2900 return nSelectedCount;
2903 /***
2904 * DESCRIPTION:
2905 * Manages the item focus.
2907 * PARAMETER(S):
2908 * [I] infoPtr : valid pointer to the listview structure
2909 * [I] nItem : item index
2911 * RETURN:
2912 * TRUE : focused item changed
2913 * FALSE : focused item has NOT changed
2915 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2917 INT oldFocus = infoPtr->nFocusedItem;
2918 LVITEMW lvItem;
2920 if (nItem == infoPtr->nFocusedItem) return FALSE;
2922 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2923 lvItem.stateMask = LVIS_FOCUSED;
2924 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2926 return oldFocus != infoPtr->nFocusedItem;
2929 /* Helper function for LISTVIEW_ShiftIndices *only* */
2930 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2932 if (nShiftItem < nItem) return nShiftItem;
2934 if (nShiftItem > nItem) return nShiftItem + direction;
2936 if (direction > 0) return nShiftItem + direction;
2938 return min(nShiftItem, infoPtr->nItemCount - 1);
2942 * DESCRIPTION:
2943 * Updates the various indices after an item has been inserted or deleted.
2945 * PARAMETER(S):
2946 * [I] infoPtr : valid pointer to the listview structure
2947 * [I] nItem : item index
2948 * [I] direction : Direction of shift, +1 or -1.
2950 * RETURN:
2951 * None
2953 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2955 INT nNewFocus;
2956 BOOL bOldChange;
2958 /* temporarily disable change notification while shifting items */
2959 bOldChange = infoPtr->bDoChangeNotify;
2960 infoPtr->bDoChangeNotify = FALSE;
2962 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2964 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2966 assert(abs(direction) == 1);
2968 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2970 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2971 if (nNewFocus != infoPtr->nFocusedItem)
2972 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2974 /* But we are not supposed to modify nHotItem! */
2976 infoPtr->bDoChangeNotify = bOldChange;
2981 * DESCRIPTION:
2982 * Adds a block of selections.
2984 * PARAMETER(S):
2985 * [I] infoPtr : valid pointer to the listview structure
2986 * [I] nItem : item index
2988 * RETURN:
2989 * Whether the window is still valid.
2991 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2993 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2994 INT nLast = max(infoPtr->nSelectionMark, nItem);
2995 HWND hwndSelf = infoPtr->hwndSelf;
2996 NMLVODSTATECHANGE nmlv;
2997 LVITEMW item;
2998 BOOL bOldChange;
2999 INT i;
3001 /* Temporarily disable change notification
3002 * If the control is LVS_OWNERDATA, we need to send
3003 * only one LVN_ODSTATECHANGED notification.
3004 * See MSDN documentation for LVN_ITEMCHANGED.
3006 bOldChange = infoPtr->bDoChangeNotify;
3007 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3009 if (nFirst == -1) nFirst = nItem;
3011 item.state = LVIS_SELECTED;
3012 item.stateMask = LVIS_SELECTED;
3014 for (i = nFirst; i <= nLast; i++)
3015 LISTVIEW_SetItemState(infoPtr,i,&item);
3017 ZeroMemory(&nmlv, sizeof(nmlv));
3018 nmlv.iFrom = nFirst;
3019 nmlv.iTo = nLast;
3020 nmlv.uNewState = 0;
3021 nmlv.uOldState = item.state;
3023 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3024 if (!IsWindow(hwndSelf))
3025 return FALSE;
3026 infoPtr->bDoChangeNotify = bOldChange;
3027 return TRUE;
3031 /***
3032 * DESCRIPTION:
3033 * Sets a single group selection.
3035 * PARAMETER(S):
3036 * [I] infoPtr : valid pointer to the listview structure
3037 * [I] nItem : item index
3039 * RETURN:
3040 * None
3042 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3044 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3045 RANGES selection;
3046 LVITEMW item;
3047 ITERATOR i;
3049 if (!(selection = ranges_create(100))) return;
3051 item.state = LVIS_SELECTED;
3052 item.stateMask = LVIS_SELECTED;
3054 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3056 if (infoPtr->nSelectionMark == -1)
3058 infoPtr->nSelectionMark = nItem;
3059 ranges_additem(selection, nItem);
3061 else
3063 RANGE sel;
3065 sel.lower = min(infoPtr->nSelectionMark, nItem);
3066 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3067 ranges_add(selection, sel);
3070 else
3072 RECT rcItem, rcSel, rcSelMark;
3073 POINT ptItem;
3075 rcItem.left = LVIR_BOUNDS;
3076 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3077 rcSelMark.left = LVIR_BOUNDS;
3078 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3079 UnionRect(&rcSel, &rcItem, &rcSelMark);
3080 iterator_frameditems(&i, infoPtr, &rcSel);
3081 while(iterator_next(&i))
3083 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3084 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3086 iterator_destroy(&i);
3089 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3090 iterator_rangesitems(&i, selection);
3091 while(iterator_next(&i))
3092 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3093 /* this will also destroy the selection */
3094 iterator_destroy(&i);
3096 LISTVIEW_SetItemFocus(infoPtr, nItem);
3099 /***
3100 * DESCRIPTION:
3101 * Sets a single selection.
3103 * PARAMETER(S):
3104 * [I] infoPtr : valid pointer to the listview structure
3105 * [I] nItem : item index
3107 * RETURN:
3108 * None
3110 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3112 LVITEMW lvItem;
3114 TRACE("nItem=%d\n", nItem);
3116 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3118 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3119 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3120 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3122 infoPtr->nSelectionMark = nItem;
3125 /***
3126 * DESCRIPTION:
3127 * Set selection(s) with keyboard.
3129 * PARAMETER(S):
3130 * [I] infoPtr : valid pointer to the listview structure
3131 * [I] nItem : item index
3133 * RETURN:
3134 * SUCCESS : TRUE (needs to be repainted)
3135 * FAILURE : FALSE (nothing has changed)
3137 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3139 /* FIXME: pass in the state */
3140 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3141 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3142 BOOL bResult = FALSE;
3144 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3145 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3147 if (infoPtr->dwStyle & LVS_SINGLESEL)
3149 bResult = TRUE;
3150 LISTVIEW_SetSelection(infoPtr, nItem);
3152 else
3154 if (wShift)
3156 bResult = TRUE;
3157 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3159 else if (wCtrl)
3161 LVITEMW lvItem;
3162 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3163 lvItem.stateMask = LVIS_SELECTED;
3164 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3166 if (lvItem.state & LVIS_SELECTED)
3167 infoPtr->nSelectionMark = nItem;
3169 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3171 else
3173 bResult = TRUE;
3174 LISTVIEW_SetSelection(infoPtr, nItem);
3177 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3180 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3181 return bResult;
3184 static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3186 LVHITTESTINFO lvHitTestInfo;
3188 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3189 lvHitTestInfo.pt.x = pt.x;
3190 lvHitTestInfo.pt.y = pt.y;
3192 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3194 lpLVItem->mask = LVIF_PARAM;
3195 lpLVItem->iItem = lvHitTestInfo.iItem;
3196 lpLVItem->iSubItem = 0;
3198 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3201 /***
3202 * DESCRIPTION:
3203 * Called when the mouse is being actively tracked and has hovered for a specified
3204 * amount of time
3206 * PARAMETER(S):
3207 * [I] infoPtr : valid pointer to the listview structure
3208 * [I] fwKeys : key indicator
3209 * [I] x,y : mouse position
3211 * RETURN:
3212 * 0 if the message was processed, non-zero if there was an error
3214 * INFO:
3215 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3216 * over the item for a certain period of time.
3219 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3221 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3223 LVITEMW item;
3224 POINT pt;
3226 pt.x = x;
3227 pt.y = y;
3229 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3230 LISTVIEW_SetSelection(infoPtr, item.iItem);
3233 return 0;
3236 /***
3237 * DESCRIPTION:
3238 * Called whenever WM_MOUSEMOVE is received.
3240 * PARAMETER(S):
3241 * [I] infoPtr : valid pointer to the listview structure
3242 * [I] fwKeys : key indicator
3243 * [I] x,y : mouse position
3245 * RETURN:
3246 * 0 if the message is processed, non-zero if there was an error
3248 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3250 TRACKMOUSEEVENT trackinfo;
3252 if (!(fwKeys & MK_LBUTTON))
3253 infoPtr->bLButtonDown = FALSE;
3255 if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3257 LVHITTESTINFO lvHitTestInfo;
3258 NMLISTVIEW nmlv;
3260 lvHitTestInfo.pt = infoPtr->ptClickPos;
3261 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3263 ZeroMemory(&nmlv, sizeof(nmlv));
3264 nmlv.iItem = lvHitTestInfo.iItem;
3265 nmlv.ptAction = infoPtr->ptClickPos;
3267 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3269 return 0;
3271 else
3272 infoPtr->bLButtonDown = FALSE;
3274 /* see if we are supposed to be tracking mouse hovering */
3275 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3276 /* fill in the trackinfo struct */
3277 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3278 trackinfo.dwFlags = TME_QUERY;
3279 trackinfo.hwndTrack = infoPtr->hwndSelf;
3280 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3282 /* see if we are already tracking this hwnd */
3283 _TrackMouseEvent(&trackinfo);
3285 if(!(trackinfo.dwFlags & TME_HOVER)) {
3286 trackinfo.dwFlags = TME_HOVER;
3288 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3289 _TrackMouseEvent(&trackinfo);
3293 return 0;
3297 /***
3298 * Tests wheather the item is assignable to a list with style lStyle
3300 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3302 if ( (lpLVItem->mask & LVIF_TEXT) &&
3303 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3304 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3306 return TRUE;
3310 /***
3311 * DESCRIPTION:
3312 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3314 * PARAMETER(S):
3315 * [I] infoPtr : valid pointer to the listview structure
3316 * [I] lpLVItem : valid pointer to new item atttributes
3317 * [I] isNew : the item being set is being inserted
3318 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3319 * [O] bChanged : will be set to TRUE if the item really changed
3321 * RETURN:
3322 * SUCCESS : TRUE
3323 * FAILURE : FALSE
3325 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3327 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3328 ITEM_INFO *lpItem;
3329 NMLISTVIEW nmlv;
3330 UINT uChanged = 0;
3331 LVITEMW item;
3333 TRACE("()\n");
3335 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3337 if (lpLVItem->mask == 0) return TRUE;
3339 if (infoPtr->dwStyle & LVS_OWNERDATA)
3341 /* a virtual listview we stores only selection and focus */
3342 if (lpLVItem->mask & ~LVIF_STATE)
3343 return FALSE;
3344 lpItem = NULL;
3346 else
3348 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3349 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3350 assert (lpItem);
3353 /* we need to get the lParam and state of the item */
3354 item.iItem = lpLVItem->iItem;
3355 item.iSubItem = lpLVItem->iSubItem;
3356 item.mask = LVIF_STATE | LVIF_PARAM;
3357 item.stateMask = ~0;
3358 item.state = 0;
3359 item.lParam = 0;
3360 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3362 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3363 /* determine what fields will change */
3364 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3365 uChanged |= LVIF_STATE;
3367 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3368 uChanged |= LVIF_IMAGE;
3370 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3371 uChanged |= LVIF_PARAM;
3373 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3374 uChanged |= LVIF_INDENT;
3376 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3377 uChanged |= LVIF_TEXT;
3379 TRACE("uChanged=0x%x\n", uChanged);
3380 if (!uChanged) return TRUE;
3381 *bChanged = TRUE;
3383 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3384 nmlv.iItem = lpLVItem->iItem;
3385 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3386 nmlv.uOldState = item.state;
3387 nmlv.uChanged = uChanged;
3388 nmlv.lParam = item.lParam;
3390 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3391 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3392 /* are enabled */
3393 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3395 HWND hwndSelf = infoPtr->hwndSelf;
3397 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3398 return FALSE;
3399 if (!IsWindow(hwndSelf))
3400 return FALSE;
3403 /* copy information */
3404 if (lpLVItem->mask & LVIF_TEXT)
3405 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3407 if (lpLVItem->mask & LVIF_IMAGE)
3408 lpItem->hdr.iImage = lpLVItem->iImage;
3410 if (lpLVItem->mask & LVIF_PARAM)
3411 lpItem->lParam = lpLVItem->lParam;
3413 if (lpLVItem->mask & LVIF_INDENT)
3414 lpItem->iIndent = lpLVItem->iIndent;
3416 if (uChanged & LVIF_STATE)
3418 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3420 lpItem->state &= ~lpLVItem->stateMask;
3421 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3423 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3425 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3426 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3428 else if (lpLVItem->stateMask & LVIS_SELECTED)
3429 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3431 /* if we are asked to change focus, and we manage it, do it */
3432 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3434 if (lpLVItem->state & LVIS_FOCUSED)
3436 LISTVIEW_SetItemFocus(infoPtr, -1);
3437 infoPtr->nFocusedItem = lpLVItem->iItem;
3438 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3440 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3441 infoPtr->nFocusedItem = -1;
3445 /* if we're inserting the item, we're done */
3446 if (isNew) return TRUE;
3448 /* send LVN_ITEMCHANGED notification */
3449 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3450 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3452 return TRUE;
3455 /***
3456 * DESCRIPTION:
3457 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3459 * PARAMETER(S):
3460 * [I] infoPtr : valid pointer to the listview structure
3461 * [I] lpLVItem : valid pointer to new subitem atttributes
3462 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3463 * [O] bChanged : will be set to TRUE if the item really changed
3465 * RETURN:
3466 * SUCCESS : TRUE
3467 * FAILURE : FALSE
3469 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3471 HDPA hdpaSubItems;
3472 SUBITEM_INFO *lpSubItem;
3474 /* we do not support subitems for virtual listviews */
3475 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3477 /* set subitem only if column is present */
3478 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3480 /* First do some sanity checks */
3481 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3482 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3484 /* get the subitem structure, and create it if not there */
3485 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3486 assert (hdpaSubItems);
3488 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3489 if (!lpSubItem)
3491 SUBITEM_INFO *tmpSubItem;
3492 INT i;
3494 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3495 if (!lpSubItem) return FALSE;
3496 /* we could binary search here, if need be...*/
3497 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3499 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3500 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3502 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3504 Free(lpSubItem);
3505 return FALSE;
3507 lpSubItem->iSubItem = lpLVItem->iSubItem;
3508 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3509 *bChanged = TRUE;
3512 if (lpLVItem->mask & LVIF_IMAGE)
3513 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3515 lpSubItem->hdr.iImage = lpLVItem->iImage;
3516 *bChanged = TRUE;
3519 if (lpLVItem->mask & LVIF_TEXT)
3520 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3522 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3523 *bChanged = TRUE;
3526 return TRUE;
3529 /***
3530 * DESCRIPTION:
3531 * Sets item attributes.
3533 * PARAMETER(S):
3534 * [I] infoPtr : valid pointer to the listview structure
3535 * [I] lpLVItem : new item atttributes
3536 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3538 * RETURN:
3539 * SUCCESS : TRUE
3540 * FAILURE : FALSE
3542 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3544 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3545 HWND hwndSelf = infoPtr->hwndSelf;
3546 LPWSTR pszText = NULL;
3547 BOOL bResult, bChanged = FALSE;
3549 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3551 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3552 return FALSE;
3554 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3555 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3557 pszText = lpLVItem->pszText;
3558 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3561 /* actually set the fields */
3562 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3564 if (lpLVItem->iSubItem)
3565 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3566 else
3567 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3568 if (!IsWindow(hwndSelf))
3569 return FALSE;
3571 /* redraw item, if necessary */
3572 if (bChanged && !infoPtr->bIsDrawing)
3574 /* this little optimization eliminates some nasty flicker */
3575 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3576 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3577 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3578 else
3579 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3581 /* restore text */
3582 if (pszText)
3584 textfreeT(lpLVItem->pszText, isW);
3585 ((LVITEMW *)lpLVItem)->pszText = pszText;
3588 return bResult;
3591 /***
3592 * DESCRIPTION:
3593 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3595 * PARAMETER(S):
3596 * [I] infoPtr : valid pointer to the listview structure
3598 * RETURN:
3599 * item index
3601 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3603 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3604 INT nItem = 0;
3605 SCROLLINFO scrollInfo;
3607 scrollInfo.cbSize = sizeof(SCROLLINFO);
3608 scrollInfo.fMask = SIF_POS;
3610 if (uView == LVS_LIST)
3612 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3613 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3615 else if (uView == LVS_REPORT)
3617 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3618 nItem = scrollInfo.nPos;
3620 else
3622 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3623 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3626 TRACE("nItem=%d\n", nItem);
3628 return nItem;
3632 /***
3633 * DESCRIPTION:
3634 * Erases the background of the given rectangle
3636 * PARAMETER(S):
3637 * [I] infoPtr : valid pointer to the listview structure
3638 * [I] hdc : device context handle
3639 * [I] lprcBox : clipping rectangle
3641 * RETURN:
3642 * Success: TRUE
3643 * Failure: FALSE
3645 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3647 if (!infoPtr->hBkBrush) return FALSE;
3649 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3651 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3654 /***
3655 * DESCRIPTION:
3656 * Draws an item.
3658 * PARAMETER(S):
3659 * [I] infoPtr : valid pointer to the listview structure
3660 * [I] hdc : device context handle
3661 * [I] nItem : item index
3662 * [I] nSubItem : subitem index
3663 * [I] pos : item position in client coordinates
3664 * [I] cdmode : custom draw mode
3666 * RETURN:
3667 * Success: TRUE
3668 * Failure: FALSE
3670 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3672 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3673 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3674 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3675 DWORD cdsubitemmode = CDRF_DODEFAULT;
3676 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3677 NMLVCUSTOMDRAW nmlvcd;
3678 HIMAGELIST himl;
3679 LVITEMW lvItem;
3680 HFONT hOldFont;
3682 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3684 /* get information needed for drawing the item */
3685 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3686 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3687 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3688 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3689 lvItem.iItem = nItem;
3690 lvItem.iSubItem = nSubItem;
3691 lvItem.state = 0;
3692 lvItem.lParam = 0;
3693 lvItem.cchTextMax = DISP_TEXT_SIZE;
3694 lvItem.pszText = szDispText;
3695 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3696 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3697 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3698 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3699 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3701 /* now check if we need to update the focus rectangle */
3702 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3704 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3705 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3706 OffsetRect(&rcBox, pos.x, pos.y);
3707 OffsetRect(&rcState, pos.x, pos.y);
3708 OffsetRect(&rcIcon, pos.x, pos.y);
3709 OffsetRect(&rcLabel, pos.x, pos.y);
3710 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3711 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcState),
3712 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3714 /* fill in the custom draw structure */
3715 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3717 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3718 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3719 if (cdmode & CDRF_NOTIFYITEMDRAW)
3720 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3721 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3722 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3723 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3724 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3726 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3727 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3729 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3730 prepaint_setup(infoPtr, hdc, &nmlvcd);
3732 /* in full row select, subitems, will just use main item's colors */
3733 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3734 nmlvcd.clrTextBk = CLR_NONE;
3736 /* state icons */
3737 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3739 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3740 if (uStateImage)
3742 TRACE("uStateImage=%d\n", uStateImage);
3743 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3747 /* small icons */
3748 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3749 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3751 TRACE("iImage=%d\n", lvItem.iImage);
3752 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3753 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3756 /* Don't bother painting item being edited */
3757 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3759 /* draw the selection background, if we're drawing the main item */
3760 if (nSubItem == 0)
3762 rcSelect = rcLabel;
3763 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3764 rcSelect.right = rcBox.right;
3766 if (nmlvcd.clrTextBk != CLR_NONE)
3767 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3768 if(lprcFocus) *lprcFocus = rcSelect;
3771 /* figure out the text drawing flags */
3772 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3773 if (uView == LVS_ICON)
3774 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3775 else if (nSubItem)
3777 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3779 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3780 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3781 default: uFormat |= DT_LEFT;
3784 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3786 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3787 else rcLabel.left += LABEL_HOR_PADDING;
3789 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3790 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3792 postpaint:
3793 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3794 notify_postpaint(infoPtr, &nmlvcd);
3795 if (cdsubitemmode & CDRF_NEWFONT)
3796 SelectObject(hdc, hOldFont);
3797 return TRUE;
3800 /***
3801 * DESCRIPTION:
3802 * Draws listview items when in owner draw mode.
3804 * PARAMETER(S):
3805 * [I] infoPtr : valid pointer to the listview structure
3806 * [I] hdc : device context handle
3808 * RETURN:
3809 * None
3811 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3813 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3814 DWORD cditemmode = CDRF_DODEFAULT;
3815 NMLVCUSTOMDRAW nmlvcd;
3816 POINT Origin, Position;
3817 DRAWITEMSTRUCT dis;
3818 LVITEMW item;
3820 TRACE("()\n");
3822 ZeroMemory(&dis, sizeof(dis));
3824 /* Get scroll info once before loop */
3825 LISTVIEW_GetOrigin(infoPtr, &Origin);
3827 /* iterate through the invalidated rows */
3828 while(iterator_next(i))
3830 item.iItem = i->nItem;
3831 item.iSubItem = 0;
3832 item.mask = LVIF_PARAM | LVIF_STATE;
3833 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3834 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3836 dis.CtlType = ODT_LISTVIEW;
3837 dis.CtlID = uID;
3838 dis.itemID = item.iItem;
3839 dis.itemAction = ODA_DRAWENTIRE;
3840 dis.itemState = 0;
3841 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3842 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3843 dis.hwndItem = infoPtr->hwndSelf;
3844 dis.hDC = hdc;
3845 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3846 dis.rcItem.left = Position.x + Origin.x;
3847 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3848 dis.rcItem.top = Position.y + Origin.y;
3849 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3850 dis.itemData = item.lParam;
3852 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3855 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3856 * structure for the rest. of the paint cycle
3858 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3859 if (cdmode & CDRF_NOTIFYITEMDRAW)
3860 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3862 if (!(cditemmode & CDRF_SKIPDEFAULT))
3864 prepaint_setup (infoPtr, hdc, &nmlvcd);
3865 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3868 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3869 notify_postpaint(infoPtr, &nmlvcd);
3873 /***
3874 * DESCRIPTION:
3875 * Draws listview items when in report display mode.
3877 * PARAMETER(S):
3878 * [I] infoPtr : valid pointer to the listview structure
3879 * [I] hdc : device context handle
3880 * [I] cdmode : custom draw mode
3882 * RETURN:
3883 * None
3885 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3887 INT rgntype;
3888 RECT rcClip, rcItem;
3889 POINT Origin, Position;
3890 RANGE colRange;
3891 ITERATOR j;
3893 TRACE("()\n");
3895 /* figure out what to draw */
3896 rgntype = GetClipBox(hdc, &rcClip);
3897 if (rgntype == NULLREGION) return;
3899 /* Get scroll info once before loop */
3900 LISTVIEW_GetOrigin(infoPtr, &Origin);
3902 /* narrow down the columns we need to paint */
3903 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3905 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3906 if (rcItem.right + Origin.x >= rcClip.left) break;
3908 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3910 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3911 if (rcItem.left + Origin.x < rcClip.right) break;
3913 iterator_rangeitems(&j, colRange);
3915 /* in full row select, we _have_ to draw the main item */
3916 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3917 j.nSpecial = 0;
3919 /* iterate through the invalidated rows */
3920 while(iterator_next(i))
3922 /* iterate through the invalidated columns */
3923 while(iterator_next(&j))
3925 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3926 Position.x += Origin.x;
3927 Position.y += Origin.y;
3929 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3931 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3932 rcItem.top = 0;
3933 rcItem.bottom = infoPtr->nItemHeight;
3934 OffsetRect(&rcItem, Position.x, Position.y);
3935 if (!RectVisible(hdc, &rcItem)) continue;
3938 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3941 iterator_destroy(&j);
3944 /***
3945 * DESCRIPTION:
3946 * Draws listview items when in list display mode.
3948 * PARAMETER(S):
3949 * [I] infoPtr : valid pointer to the listview structure
3950 * [I] hdc : device context handle
3951 * [I] cdmode : custom draw mode
3953 * RETURN:
3954 * None
3956 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3958 POINT Origin, Position;
3960 /* Get scroll info once before loop */
3961 LISTVIEW_GetOrigin(infoPtr, &Origin);
3963 while(iterator_prev(i))
3965 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3966 Position.x += Origin.x;
3967 Position.y += Origin.y;
3969 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3974 /***
3975 * DESCRIPTION:
3976 * Draws listview items.
3978 * PARAMETER(S):
3979 * [I] infoPtr : valid pointer to the listview structure
3980 * [I] hdc : device context handle
3982 * RETURN:
3983 * NoneX
3985 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3987 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3988 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3989 NMLVCUSTOMDRAW nmlvcd;
3990 HFONT hOldFont;
3991 DWORD cdmode;
3992 INT oldBkMode;
3993 RECT rcClient;
3994 ITERATOR i;
3996 LISTVIEW_DUMP(infoPtr);
3998 infoPtr->bIsDrawing = TRUE;
4000 /* save dc values we're gonna trash while drawing */
4001 hOldFont = SelectObject(hdc, infoPtr->hFont);
4002 oldBkMode = GetBkMode(hdc);
4003 infoPtr->clrTextBkDefault = GetBkColor(hdc);
4004 oldTextColor = GetTextColor(hdc);
4006 oldClrTextBk = infoPtr->clrTextBk;
4007 oldClrText = infoPtr->clrText;
4009 infoPtr->cditemmode = CDRF_DODEFAULT;
4011 GetClientRect(infoPtr->hwndSelf, &rcClient);
4012 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4013 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4014 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4015 prepaint_setup(infoPtr, hdc, &nmlvcd);
4017 /* Use these colors to draw the items */
4018 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4019 infoPtr->clrText = nmlvcd.clrText;
4021 /* nothing to draw */
4022 if(infoPtr->nItemCount == 0) goto enddraw;
4024 /* figure out what we need to draw */
4025 iterator_visibleitems(&i, infoPtr, hdc);
4027 /* send cache hint notification */
4028 if (infoPtr->dwStyle & LVS_OWNERDATA)
4030 RANGE range = iterator_range(&i);
4031 NMLVCACHEHINT nmlv;
4033 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4034 nmlv.iFrom = range.lower;
4035 nmlv.iTo = range.upper - 1;
4036 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4039 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4040 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4041 else
4043 if (uView == LVS_REPORT)
4044 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4045 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4046 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4048 /* if we have a focus rect, draw it */
4049 if (infoPtr->bFocus)
4050 DrawFocusRect(hdc, &infoPtr->rcFocus);
4052 iterator_destroy(&i);
4054 enddraw:
4055 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4056 notify_postpaint(infoPtr, &nmlvcd);
4058 infoPtr->clrTextBk = oldClrTextBk;
4059 infoPtr->clrText = oldClrText;
4061 SelectObject(hdc, hOldFont);
4062 SetBkMode(hdc, oldBkMode);
4063 SetBkColor(hdc, infoPtr->clrTextBkDefault);
4064 SetTextColor(hdc, oldTextColor);
4065 infoPtr->bIsDrawing = FALSE;
4069 /***
4070 * DESCRIPTION:
4071 * Calculates the approximate width and height of a given number of items.
4073 * PARAMETER(S):
4074 * [I] infoPtr : valid pointer to the listview structure
4075 * [I] nItemCount : number of items
4076 * [I] wWidth : width
4077 * [I] wHeight : height
4079 * RETURN:
4080 * Returns a DWORD. The width in the low word and the height in high word.
4082 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4083 WORD wWidth, WORD wHeight)
4085 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4086 INT nItemCountPerColumn = 1;
4087 INT nColumnCount = 0;
4088 DWORD dwViewRect = 0;
4090 if (nItemCount == -1)
4091 nItemCount = infoPtr->nItemCount;
4093 if (uView == LVS_LIST)
4095 if (wHeight == 0xFFFF)
4097 /* use current height */
4098 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4101 if (wHeight < infoPtr->nItemHeight)
4102 wHeight = infoPtr->nItemHeight;
4104 if (nItemCount > 0)
4106 if (infoPtr->nItemHeight > 0)
4108 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4109 if (nItemCountPerColumn == 0)
4110 nItemCountPerColumn = 1;
4112 if (nItemCount % nItemCountPerColumn != 0)
4113 nColumnCount = nItemCount / nItemCountPerColumn;
4114 else
4115 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4119 /* Microsoft padding magic */
4120 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4121 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4123 dwViewRect = MAKELONG(wWidth, wHeight);
4125 else if (uView == LVS_REPORT)
4127 RECT rcBox;
4129 if (infoPtr->nItemCount > 0)
4131 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4132 wWidth = rcBox.right - rcBox.left;
4133 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4135 else
4137 /* use current height and width */
4138 if (wHeight == 0xffff)
4139 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4140 if (wWidth == 0xffff)
4141 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4144 dwViewRect = MAKELONG(wWidth, wHeight);
4146 else if (uView == LVS_SMALLICON)
4147 FIXME("uView == LVS_SMALLICON: not implemented\n");
4148 else if (uView == LVS_ICON)
4149 FIXME("uView == LVS_ICON: not implemented\n");
4151 return dwViewRect;
4155 /***
4156 * DESCRIPTION:
4157 * Create a drag image list for the specified item.
4159 * PARAMETER(S):
4160 * [I] infoPtr : valid pointer to the listview structure
4161 * [I] iItem : index of item
4162 * [O] lppt : Upperr-left corner of the image
4164 * RETURN:
4165 * Returns a handle to the image list if successful, NULL otherwise.
4167 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4169 RECT rcItem;
4170 SIZE size;
4171 POINT pos;
4172 HDC hdc, hdcOrig;
4173 HBITMAP hbmp, hOldbmp;
4174 HIMAGELIST dragList = 0;
4175 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4177 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4178 return 0;
4180 rcItem.left = LVIR_BOUNDS;
4181 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4182 return 0;
4184 lppt->x = rcItem.left;
4185 lppt->y = rcItem.top;
4187 size.cx = rcItem.right - rcItem.left;
4188 size.cy = rcItem.bottom - rcItem.top;
4190 hdcOrig = GetDC(infoPtr->hwndSelf);
4191 hdc = CreateCompatibleDC(hdcOrig);
4192 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4193 hOldbmp = SelectObject(hdc, hbmp);
4195 rcItem.left = rcItem.top = 0;
4196 rcItem.right = size.cx;
4197 rcItem.bottom = size.cy;
4198 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4200 pos.x = pos.y = 0;
4201 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4203 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4204 SelectObject(hdc, hOldbmp);
4205 ImageList_Add(dragList, hbmp, 0);
4207 else
4208 SelectObject(hdc, hOldbmp);
4210 DeleteObject(hbmp);
4211 DeleteDC(hdc);
4212 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4214 TRACE("ret=%p\n", dragList);
4216 return dragList;
4220 /***
4221 * DESCRIPTION:
4222 * Removes all listview items and subitems.
4224 * PARAMETER(S):
4225 * [I] infoPtr : valid pointer to the listview structure
4227 * RETURN:
4228 * SUCCESS : TRUE
4229 * FAILURE : FALSE
4231 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4233 NMLISTVIEW nmlv;
4234 HDPA hdpaSubItems = NULL;
4235 BOOL bSuppress;
4236 ITEMHDR *hdrItem;
4237 INT i, j;
4239 TRACE("()\n");
4241 /* we do it directly, to avoid notifications */
4242 ranges_clear(infoPtr->selectionRanges);
4243 infoPtr->nSelectionMark = -1;
4244 infoPtr->nFocusedItem = -1;
4245 SetRectEmpty(&infoPtr->rcFocus);
4246 /* But we are supposed to leave nHotItem as is! */
4249 /* send LVN_DELETEALLITEMS notification */
4250 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4251 nmlv.iItem = -1;
4252 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4254 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4256 /* send LVN_DELETEITEM notification, if not suppressed */
4257 if (!bSuppress) notify_deleteitem(infoPtr, i);
4258 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4260 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4261 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4263 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4264 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4265 Free(hdrItem);
4267 DPA_Destroy(hdpaSubItems);
4268 DPA_DeletePtr(infoPtr->hdpaItems, i);
4270 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4271 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4272 infoPtr->nItemCount --;
4275 LISTVIEW_UpdateScroll(infoPtr);
4277 LISTVIEW_InvalidateList(infoPtr);
4279 return TRUE;
4282 /***
4283 * DESCRIPTION:
4284 * Scrolls, and updates the columns, when a column is changing width.
4286 * PARAMETER(S):
4287 * [I] infoPtr : valid pointer to the listview structure
4288 * [I] nColumn : column to scroll
4289 * [I] dx : amount of scroll, in pixels
4291 * RETURN:
4292 * None.
4294 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4296 COLUMN_INFO *lpColumnInfo;
4297 RECT rcOld, rcCol;
4298 POINT ptOrigin;
4299 INT nCol;
4301 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4302 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4303 rcCol = lpColumnInfo->rcHeader;
4304 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4305 rcCol.left = rcCol.right;
4307 /* ajust the other columns */
4308 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4310 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4311 lpColumnInfo->rcHeader.left += dx;
4312 lpColumnInfo->rcHeader.right += dx;
4315 /* do not update screen if not in report mode */
4316 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4318 /* if we have a focus, must first erase the focus rect */
4319 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4321 /* Need to reset the item width when inserting a new column */
4322 infoPtr->nItemWidth += dx;
4324 LISTVIEW_UpdateScroll(infoPtr);
4325 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4327 /* scroll to cover the deleted column, and invalidate for redraw */
4328 rcOld = infoPtr->rcList;
4329 rcOld.left = ptOrigin.x + rcCol.left + dx;
4330 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4332 /* we can restore focus now */
4333 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4336 /***
4337 * DESCRIPTION:
4338 * Removes a column from the listview control.
4340 * PARAMETER(S):
4341 * [I] infoPtr : valid pointer to the listview structure
4342 * [I] nColumn : column index
4344 * RETURN:
4345 * SUCCESS : TRUE
4346 * FAILURE : FALSE
4348 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4350 RECT rcCol;
4352 TRACE("nColumn=%d\n", nColumn);
4354 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4355 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4357 /* While the MSDN specifically says that column zero should not be deleted,
4358 what actually happens is that the column itself is deleted but no items or subitems
4359 are removed.
4362 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4364 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4365 return FALSE;
4367 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4368 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4370 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4372 SUBITEM_INFO *lpSubItem, *lpDelItem;
4373 HDPA hdpaSubItems;
4374 INT nItem, nSubItem, i;
4376 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4378 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4379 nSubItem = 0;
4380 lpDelItem = 0;
4381 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4383 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4384 if (lpSubItem->iSubItem == nColumn)
4386 nSubItem = i;
4387 lpDelItem = lpSubItem;
4389 else if (lpSubItem->iSubItem > nColumn)
4391 lpSubItem->iSubItem--;
4395 /* if we found our subitem, zapp it */
4396 if (nSubItem > 0)
4398 /* free string */
4399 if (is_textW(lpDelItem->hdr.pszText))
4400 Free(lpDelItem->hdr.pszText);
4402 /* free item */
4403 Free(lpDelItem);
4405 /* free dpa memory */
4406 DPA_DeletePtr(hdpaSubItems, nSubItem);
4411 /* update the other column info */
4412 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4413 LISTVIEW_InvalidateList(infoPtr);
4414 else
4415 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4417 return TRUE;
4420 /***
4421 * DESCRIPTION:
4422 * Invalidates the listview after an item's insertion or deletion.
4424 * PARAMETER(S):
4425 * [I] infoPtr : valid pointer to the listview structure
4426 * [I] nItem : item index
4427 * [I] dir : -1 if deleting, 1 if inserting
4429 * RETURN:
4430 * None
4432 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4434 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4435 INT nPerCol, nItemCol, nItemRow;
4436 RECT rcScroll;
4437 POINT Origin;
4439 /* if we don't refresh, what's the point of scrolling? */
4440 if (!is_redrawing(infoPtr)) return;
4442 assert (abs(dir) == 1);
4444 /* arrange icons if autoarrange is on */
4445 if (is_autoarrange(infoPtr))
4447 BOOL arrange = TRUE;
4448 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4449 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4450 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4453 /* scrollbars need updating */
4454 LISTVIEW_UpdateScroll(infoPtr);
4456 /* figure out the item's position */
4457 if (uView == LVS_REPORT)
4458 nPerCol = infoPtr->nItemCount + 1;
4459 else if (uView == LVS_LIST)
4460 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4461 else /* LVS_ICON, or LVS_SMALLICON */
4462 return;
4464 nItemCol = nItem / nPerCol;
4465 nItemRow = nItem % nPerCol;
4466 LISTVIEW_GetOrigin(infoPtr, &Origin);
4468 /* move the items below up a slot */
4469 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4470 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4471 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4472 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4473 OffsetRect(&rcScroll, Origin.x, Origin.y);
4474 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4475 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4477 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4478 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4479 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4482 /* report has only that column, so we're done */
4483 if (uView == LVS_REPORT) return;
4485 /* now for LISTs, we have to deal with the columns to the right */
4486 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4487 rcScroll.top = 0;
4488 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4489 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4490 OffsetRect(&rcScroll, Origin.x, Origin.y);
4491 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4492 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4493 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4496 /***
4497 * DESCRIPTION:
4498 * Removes an item from the listview control.
4500 * PARAMETER(S):
4501 * [I] infoPtr : valid pointer to the listview structure
4502 * [I] nItem : item index
4504 * RETURN:
4505 * SUCCESS : TRUE
4506 * FAILURE : FALSE
4508 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4510 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4511 LVITEMW item;
4513 TRACE("(nItem=%d)\n", nItem);
4515 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4517 /* remove selection, and focus */
4518 item.state = 0;
4519 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4520 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4522 /* send LVN_DELETEITEM notification. */
4523 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4525 /* we need to do this here, because we'll be deleting stuff */
4526 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4527 LISTVIEW_InvalidateItem(infoPtr, nItem);
4529 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4531 HDPA hdpaSubItems;
4532 ITEMHDR *hdrItem;
4533 INT i;
4535 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4536 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4538 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4539 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4540 Free(hdrItem);
4542 DPA_Destroy(hdpaSubItems);
4545 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4547 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4548 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4551 infoPtr->nItemCount--;
4552 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4554 /* now is the invalidation fun */
4555 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4556 return TRUE;
4560 /***
4561 * DESCRIPTION:
4562 * Callback implementation for editlabel control
4564 * PARAMETER(S):
4565 * [I] infoPtr : valid pointer to the listview structure
4566 * [I] pszText : modified text
4567 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4569 * RETURN:
4570 * SUCCESS : TRUE
4571 * FAILURE : FALSE
4573 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4575 HWND hwndSelf = infoPtr->hwndSelf;
4576 NMLVDISPINFOW dispInfo;
4578 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4580 ZeroMemory(&dispInfo, sizeof(dispInfo));
4581 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4582 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4583 dispInfo.item.iSubItem = 0;
4584 dispInfo.item.stateMask = ~0;
4585 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4586 /* add the text from the edit in */
4587 dispInfo.item.mask |= LVIF_TEXT;
4588 dispInfo.item.pszText = pszText;
4589 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4591 /* Do we need to update the Item Text */
4592 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4593 if (!IsWindow(hwndSelf))
4594 return FALSE;
4595 if (!pszText) return TRUE;
4597 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4599 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4600 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4601 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4603 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4604 return TRUE;
4608 ZeroMemory(&dispInfo, sizeof(dispInfo));
4609 dispInfo.item.mask = LVIF_TEXT;
4610 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4611 dispInfo.item.iSubItem = 0;
4612 dispInfo.item.pszText = pszText;
4613 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4614 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4617 /***
4618 * DESCRIPTION:
4619 * Begin in place editing of specified list view item
4621 * PARAMETER(S):
4622 * [I] infoPtr : valid pointer to the listview structure
4623 * [I] nItem : item index
4624 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4626 * RETURN:
4627 * SUCCESS : TRUE
4628 * FAILURE : FALSE
4630 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4632 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4633 NMLVDISPINFOW dispInfo;
4634 RECT rect;
4635 HWND hwndSelf = infoPtr->hwndSelf;
4637 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4639 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4640 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4642 infoPtr->nEditLabelItem = nItem;
4644 /* Is the EditBox still there, if so remove it */
4645 if(infoPtr->hwndEdit != 0)
4647 SetFocus(infoPtr->hwndSelf);
4648 infoPtr->hwndEdit = 0;
4651 LISTVIEW_SetSelection(infoPtr, nItem);
4652 LISTVIEW_SetItemFocus(infoPtr, nItem);
4653 LISTVIEW_InvalidateItem(infoPtr, nItem);
4655 rect.left = LVIR_LABEL;
4656 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4658 ZeroMemory(&dispInfo, sizeof(dispInfo));
4659 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4660 dispInfo.item.iItem = nItem;
4661 dispInfo.item.iSubItem = 0;
4662 dispInfo.item.stateMask = ~0;
4663 dispInfo.item.pszText = szDispText;
4664 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4665 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4667 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4668 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4669 if (!infoPtr->hwndEdit) return 0;
4671 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4673 if (!IsWindow(hwndSelf))
4674 return 0;
4675 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4676 infoPtr->hwndEdit = 0;
4677 return 0;
4680 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4681 SetFocus(infoPtr->hwndEdit);
4682 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4683 return infoPtr->hwndEdit;
4687 /***
4688 * DESCRIPTION:
4689 * Ensures the specified item is visible, scrolling into view if necessary.
4691 * PARAMETER(S):
4692 * [I] infoPtr : valid pointer to the listview structure
4693 * [I] nItem : item index
4694 * [I] bPartial : partially or entirely visible
4696 * RETURN:
4697 * SUCCESS : TRUE
4698 * FAILURE : FALSE
4700 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4702 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4703 INT nScrollPosHeight = 0;
4704 INT nScrollPosWidth = 0;
4705 INT nHorzAdjust = 0;
4706 INT nVertAdjust = 0;
4707 INT nHorzDiff = 0;
4708 INT nVertDiff = 0;
4709 RECT rcItem, rcTemp;
4711 rcItem.left = LVIR_BOUNDS;
4712 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4714 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4716 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4718 /* scroll left/right, but in LVS_REPORT mode */
4719 if (uView == LVS_LIST)
4720 nScrollPosWidth = infoPtr->nItemWidth;
4721 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4722 nScrollPosWidth = 1;
4724 if (rcItem.left < infoPtr->rcList.left)
4726 nHorzAdjust = -1;
4727 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4729 else
4731 nHorzAdjust = 1;
4732 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4736 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4738 /* scroll up/down, but not in LVS_LIST mode */
4739 if (uView == LVS_REPORT)
4740 nScrollPosHeight = infoPtr->nItemHeight;
4741 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4742 nScrollPosHeight = 1;
4744 if (rcItem.top < infoPtr->rcList.top)
4746 nVertAdjust = -1;
4747 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4749 else
4751 nVertAdjust = 1;
4752 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4756 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4758 if (nScrollPosWidth)
4760 INT diff = nHorzDiff / nScrollPosWidth;
4761 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4762 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4765 if (nScrollPosHeight)
4767 INT diff = nVertDiff / nScrollPosHeight;
4768 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4769 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4772 return TRUE;
4775 /***
4776 * DESCRIPTION:
4777 * Searches for an item with specific characteristics.
4779 * PARAMETER(S):
4780 * [I] hwnd : window handle
4781 * [I] nStart : base item index
4782 * [I] lpFindInfo : item information to look for
4784 * RETURN:
4785 * SUCCESS : index of item
4786 * FAILURE : -1
4788 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4789 const LVFINDINFOW *lpFindInfo)
4791 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4792 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4793 BOOL bWrap = FALSE, bNearest = FALSE;
4794 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4795 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4796 POINT Position, Destination;
4797 LVITEMW lvItem;
4799 if (!lpFindInfo || nItem < 0) return -1;
4801 lvItem.mask = 0;
4802 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4804 lvItem.mask |= LVIF_TEXT;
4805 lvItem.pszText = szDispText;
4806 lvItem.cchTextMax = DISP_TEXT_SIZE;
4809 if (lpFindInfo->flags & LVFI_WRAP)
4810 bWrap = TRUE;
4812 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4813 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4815 POINT Origin;
4816 RECT rcArea;
4818 LISTVIEW_GetOrigin(infoPtr, &Origin);
4819 Destination.x = lpFindInfo->pt.x - Origin.x;
4820 Destination.y = lpFindInfo->pt.y - Origin.y;
4821 switch(lpFindInfo->vkDirection)
4823 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4824 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4825 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4826 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4827 case VK_HOME: Destination.x = Destination.y = 0; break;
4828 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4829 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4830 case VK_END:
4831 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4832 Destination.x = rcArea.right;
4833 Destination.y = rcArea.bottom;
4834 break;
4835 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4837 bNearest = TRUE;
4839 else Destination.x = Destination.y = 0;
4841 /* if LVFI_PARAM is specified, all other flags are ignored */
4842 if (lpFindInfo->flags & LVFI_PARAM)
4844 lvItem.mask |= LVIF_PARAM;
4845 bNearest = FALSE;
4846 lvItem.mask &= ~LVIF_TEXT;
4849 again:
4850 for (; nItem < nLast; nItem++)
4852 lvItem.iItem = nItem;
4853 lvItem.iSubItem = 0;
4854 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4856 if (lvItem.mask & LVIF_PARAM)
4858 if (lpFindInfo->lParam == lvItem.lParam)
4859 return nItem;
4860 else
4861 continue;
4864 if (lvItem.mask & LVIF_TEXT)
4866 if (lpFindInfo->flags & LVFI_PARTIAL)
4868 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4870 else
4872 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4876 if (!bNearest) return nItem;
4878 /* This is very inefficient. To do a good job here,
4879 * we need a sorted array of (x,y) item positions */
4880 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4882 /* compute the distance^2 to the destination */
4883 xdist = Destination.x - Position.x;
4884 ydist = Destination.y - Position.y;
4885 dist = xdist * xdist + ydist * ydist;
4887 /* remember the distance, and item if it's closer */
4888 if (dist < mindist)
4890 mindist = dist;
4891 nNearestItem = nItem;
4895 if (bWrap)
4897 nItem = 0;
4898 nLast = min(nStart + 1, infoPtr->nItemCount);
4899 bWrap = FALSE;
4900 goto again;
4903 return nNearestItem;
4906 /***
4907 * DESCRIPTION:
4908 * Searches for an item with specific characteristics.
4910 * PARAMETER(S):
4911 * [I] hwnd : window handle
4912 * [I] nStart : base item index
4913 * [I] lpFindInfo : item information to look for
4915 * RETURN:
4916 * SUCCESS : index of item
4917 * FAILURE : -1
4919 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4920 const LVFINDINFOA *lpFindInfo)
4922 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4923 LVFINDINFOW fiw;
4924 INT res;
4926 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4927 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4928 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4929 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4930 return res;
4933 /***
4934 * DESCRIPTION:
4935 * Retrieves the background image of the listview control.
4937 * PARAMETER(S):
4938 * [I] infoPtr : valid pointer to the listview structure
4939 * [O] lpBkImage : background image attributes
4941 * RETURN:
4942 * SUCCESS : TRUE
4943 * FAILURE : FALSE
4945 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4946 /* { */
4947 /* FIXME (listview, "empty stub!\n"); */
4948 /* return FALSE; */
4949 /* } */
4951 /***
4952 * DESCRIPTION:
4953 * Retrieves column attributes.
4955 * PARAMETER(S):
4956 * [I] infoPtr : valid pointer to the listview structure
4957 * [I] nColumn : column index
4958 * [IO] lpColumn : column information
4959 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4960 * otherwise it is in fact a LPLVCOLUMNA
4962 * RETURN:
4963 * SUCCESS : TRUE
4964 * FAILURE : FALSE
4966 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4968 COLUMN_INFO *lpColumnInfo;
4969 HDITEMW hdi;
4971 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4972 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4974 /* initialize memory */
4975 ZeroMemory(&hdi, sizeof(hdi));
4977 if (lpColumn->mask & LVCF_TEXT)
4979 hdi.mask |= HDI_TEXT;
4980 hdi.pszText = lpColumn->pszText;
4981 hdi.cchTextMax = lpColumn->cchTextMax;
4984 if (lpColumn->mask & LVCF_IMAGE)
4985 hdi.mask |= HDI_IMAGE;
4987 if (lpColumn->mask & LVCF_ORDER)
4988 hdi.mask |= HDI_ORDER;
4990 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4992 if (lpColumn->mask & LVCF_FMT)
4993 lpColumn->fmt = lpColumnInfo->fmt;
4995 if (lpColumn->mask & LVCF_WIDTH)
4996 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4998 if (lpColumn->mask & LVCF_IMAGE)
4999 lpColumn->iImage = hdi.iImage;
5001 if (lpColumn->mask & LVCF_ORDER)
5002 lpColumn->iOrder = hdi.iOrder;
5004 return TRUE;
5008 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5010 INT i;
5012 if (!lpiArray)
5013 return FALSE;
5015 /* FIXME: little hack */
5016 for (i = 0; i < iCount; i++)
5017 lpiArray[i] = i;
5019 return TRUE;
5022 /***
5023 * DESCRIPTION:
5024 * Retrieves the column width.
5026 * PARAMETER(S):
5027 * [I] infoPtr : valid pointer to the listview structure
5028 * [I] int : column index
5030 * RETURN:
5031 * SUCCESS : column width
5032 * FAILURE : zero
5034 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5036 INT nColumnWidth = 0;
5037 RECT rcHeader;
5039 TRACE("nColumn=%d\n", nColumn);
5041 /* we have a 'column' in LIST and REPORT mode only */
5042 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5044 case LVS_LIST:
5045 nColumnWidth = infoPtr->nItemWidth;
5046 break;
5047 case LVS_REPORT:
5048 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5049 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5050 nColumnWidth = rcHeader.right - rcHeader.left;
5051 break;
5054 TRACE("nColumnWidth=%d\n", nColumnWidth);
5055 return nColumnWidth;
5058 /***
5059 * DESCRIPTION:
5060 * In list or report display mode, retrieves the number of items that can fit
5061 * vertically in the visible area. In icon or small icon display mode,
5062 * retrieves the total number of visible items.
5064 * PARAMETER(S):
5065 * [I] infoPtr : valid pointer to the listview structure
5067 * RETURN:
5068 * Number of fully visible items.
5070 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5072 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5074 case LVS_ICON:
5075 case LVS_SMALLICON:
5076 return infoPtr->nItemCount;
5077 case LVS_REPORT:
5078 return LISTVIEW_GetCountPerColumn(infoPtr);
5079 case LVS_LIST:
5080 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5082 assert(FALSE);
5083 return 0;
5086 /***
5087 * DESCRIPTION:
5088 * Retrieves an image list handle.
5090 * PARAMETER(S):
5091 * [I] infoPtr : valid pointer to the listview structure
5092 * [I] nImageList : image list identifier
5094 * RETURN:
5095 * SUCCESS : image list handle
5096 * FAILURE : NULL
5098 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5100 switch (nImageList)
5102 case LVSIL_NORMAL: return infoPtr->himlNormal;
5103 case LVSIL_SMALL: return infoPtr->himlSmall;
5104 case LVSIL_STATE: return infoPtr->himlState;
5106 return NULL;
5109 /* LISTVIEW_GetISearchString */
5111 /***
5112 * DESCRIPTION:
5113 * Retrieves item attributes.
5115 * PARAMETER(S):
5116 * [I] hwnd : window handle
5117 * [IO] lpLVItem : item info
5118 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5119 * if FALSE, the lpLVItem is a LPLVITEMA.
5121 * NOTE:
5122 * This is the internal 'GetItem' interface -- it tries to
5123 * be smart and avoid text copies, if possible, by modifying
5124 * lpLVItem->pszText to point to the text string. Please note
5125 * that this is not always possible (e.g. OWNERDATA), so on
5126 * entry you *must* supply valid values for pszText, and cchTextMax.
5127 * The only difference to the documented interface is that upon
5128 * return, you should use *only* the lpLVItem->pszText, rather than
5129 * the buffer pointer you provided on input. Most code already does
5130 * that, so it's not a problem.
5131 * For the two cases when the text must be copied (that is,
5132 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5134 * RETURN:
5135 * SUCCESS : TRUE
5136 * FAILURE : FALSE
5138 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5140 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5141 NMLVDISPINFOW dispInfo;
5142 ITEM_INFO *lpItem;
5143 ITEMHDR* pItemHdr;
5144 HDPA hdpaSubItems;
5145 INT isubitem;
5147 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5149 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5150 return FALSE;
5152 if (lpLVItem->mask == 0) return TRUE;
5154 /* make a local copy */
5155 isubitem = lpLVItem->iSubItem;
5157 /* a quick optimization if all we're asked is the focus state
5158 * these queries are worth optimising since they are common,
5159 * and can be answered in constant time, without the heavy accesses */
5160 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5161 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5163 lpLVItem->state = 0;
5164 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5165 lpLVItem->state |= LVIS_FOCUSED;
5166 return TRUE;
5169 ZeroMemory(&dispInfo, sizeof(dispInfo));
5171 /* if the app stores all the data, handle it separately */
5172 if (infoPtr->dwStyle & LVS_OWNERDATA)
5174 dispInfo.item.state = 0;
5176 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5177 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5179 /* NOTE: copy only fields which we _know_ are initialized, some apps
5180 * depend on the uninitialized fields being 0 */
5181 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5182 dispInfo.item.iItem = lpLVItem->iItem;
5183 dispInfo.item.iSubItem = isubitem;
5184 if (lpLVItem->mask & LVIF_TEXT)
5186 dispInfo.item.pszText = lpLVItem->pszText;
5187 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5189 if (lpLVItem->mask & LVIF_STATE)
5190 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5191 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5192 dispInfo.item.stateMask = lpLVItem->stateMask;
5193 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5195 /* full size structure expected - _WIN32IE >= 0x560 */
5196 *lpLVItem = dispInfo.item;
5198 else if (lpLVItem->mask & LVIF_INDENT)
5200 /* indent member expected - _WIN32IE >= 0x300 */
5201 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5203 else
5205 /* minimal structure expected */
5206 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5208 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5211 /* make sure lParam is zeroed out */
5212 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5214 /* we store only a little state, so if we're not asked, we're done */
5215 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5217 /* if focus is handled by us, report it */
5218 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5220 lpLVItem->state &= ~LVIS_FOCUSED;
5221 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5222 lpLVItem->state |= LVIS_FOCUSED;
5225 /* and do the same for selection, if we handle it */
5226 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5228 lpLVItem->state &= ~LVIS_SELECTED;
5229 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5230 lpLVItem->state |= LVIS_SELECTED;
5233 return TRUE;
5236 /* find the item and subitem structures before we proceed */
5237 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5238 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5239 assert (lpItem);
5241 if (isubitem)
5243 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5244 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5245 if (!lpSubItem)
5247 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5248 isubitem = 0;
5251 else
5252 pItemHdr = &lpItem->hdr;
5254 /* Do we need to query the state from the app? */
5255 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5257 dispInfo.item.mask |= LVIF_STATE;
5258 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5261 /* Do we need to enquire about the image? */
5262 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5263 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5265 dispInfo.item.mask |= LVIF_IMAGE;
5266 dispInfo.item.iImage = I_IMAGECALLBACK;
5269 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5270 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5272 dispInfo.item.mask |= LVIF_TEXT;
5273 dispInfo.item.pszText = lpLVItem->pszText;
5274 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5275 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5276 *dispInfo.item.pszText = '\0';
5279 /* If we don't have all the requested info, query the application */
5280 if (dispInfo.item.mask != 0)
5282 dispInfo.item.iItem = lpLVItem->iItem;
5283 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5284 dispInfo.item.lParam = lpItem->lParam;
5285 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5286 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5289 /* we should not store values for subitems */
5290 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5292 /* Now, handle the iImage field */
5293 if (dispInfo.item.mask & LVIF_IMAGE)
5295 lpLVItem->iImage = dispInfo.item.iImage;
5296 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5297 pItemHdr->iImage = dispInfo.item.iImage;
5299 else if (lpLVItem->mask & LVIF_IMAGE)
5301 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5302 lpLVItem->iImage = pItemHdr->iImage;
5303 else
5304 lpLVItem->iImage = 0;
5307 /* The pszText field */
5308 if (dispInfo.item.mask & LVIF_TEXT)
5310 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5311 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5313 lpLVItem->pszText = dispInfo.item.pszText;
5315 else if (lpLVItem->mask & LVIF_TEXT)
5317 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5318 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5321 /* if this is a subitem, we're done */
5322 if (isubitem) return TRUE;
5324 /* Next is the lParam field */
5325 if (dispInfo.item.mask & LVIF_PARAM)
5327 lpLVItem->lParam = dispInfo.item.lParam;
5328 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5329 lpItem->lParam = dispInfo.item.lParam;
5331 else if (lpLVItem->mask & LVIF_PARAM)
5332 lpLVItem->lParam = lpItem->lParam;
5334 /* ... the state field (this one is different due to uCallbackmask) */
5335 if (lpLVItem->mask & LVIF_STATE)
5337 lpLVItem->state = lpItem->state;
5338 if (dispInfo.item.mask & LVIF_STATE)
5340 lpLVItem->state &= ~dispInfo.item.stateMask;
5341 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5343 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5345 lpLVItem->state &= ~LVIS_FOCUSED;
5346 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5347 lpLVItem->state |= LVIS_FOCUSED;
5349 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5351 lpLVItem->state &= ~LVIS_SELECTED;
5352 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5353 lpLVItem->state |= LVIS_SELECTED;
5357 /* and last, but not least, the indent field */
5358 if (lpLVItem->mask & LVIF_INDENT)
5359 lpLVItem->iIndent = lpItem->iIndent;
5361 return TRUE;
5364 /***
5365 * DESCRIPTION:
5366 * Retrieves item attributes.
5368 * PARAMETER(S):
5369 * [I] hwnd : window handle
5370 * [IO] lpLVItem : item info
5371 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5372 * if FALSE, the lpLVItem is a LPLVITEMA.
5374 * NOTE:
5375 * This is the external 'GetItem' interface -- it properly copies
5376 * the text in the provided buffer.
5378 * RETURN:
5379 * SUCCESS : TRUE
5380 * FAILURE : FALSE
5382 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5384 LPWSTR pszText;
5385 BOOL bResult;
5387 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5388 return FALSE;
5390 pszText = lpLVItem->pszText;
5391 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5392 if (bResult && lpLVItem->pszText != pszText)
5393 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5394 lpLVItem->pszText = pszText;
5396 return bResult;
5400 /***
5401 * DESCRIPTION:
5402 * Retrieves the position (upper-left) of the listview control item.
5403 * Note that for LVS_ICON style, the upper-left is that of the icon
5404 * and not the bounding box.
5406 * PARAMETER(S):
5407 * [I] infoPtr : valid pointer to the listview structure
5408 * [I] nItem : item index
5409 * [O] lpptPosition : coordinate information
5411 * RETURN:
5412 * SUCCESS : TRUE
5413 * FAILURE : FALSE
5415 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5417 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5418 POINT Origin;
5420 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5422 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5424 LISTVIEW_GetOrigin(infoPtr, &Origin);
5425 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5427 if (uView == LVS_ICON)
5429 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5430 lpptPosition->y += ICON_TOP_PADDING;
5432 lpptPosition->x += Origin.x;
5433 lpptPosition->y += Origin.y;
5435 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5436 return TRUE;
5440 /***
5441 * DESCRIPTION:
5442 * Retrieves the bounding rectangle for a listview control item.
5444 * PARAMETER(S):
5445 * [I] infoPtr : valid pointer to the listview structure
5446 * [I] nItem : item index
5447 * [IO] lprc : bounding rectangle coordinates
5448 * lprc->left specifies the portion of the item for which the bounding
5449 * rectangle will be retrieved.
5451 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5452 * including the icon and label.
5454 * * For LVS_ICON
5455 * * Experiment shows that native control returns:
5456 * * width = min (48, length of text line)
5457 * * .left = position.x - (width - iconsize.cx)/2
5458 * * .right = .left + width
5459 * * height = #lines of text * ntmHeight + icon height + 8
5460 * * .top = position.y - 2
5461 * * .bottom = .top + height
5462 * * separation between items .y = itemSpacing.cy - height
5463 * * .x = itemSpacing.cx - width
5464 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5466 * * For LVS_ICON
5467 * * Experiment shows that native control returns:
5468 * * width = iconSize.cx + 16
5469 * * .left = position.x - (width - iconsize.cx)/2
5470 * * .right = .left + width
5471 * * height = iconSize.cy + 4
5472 * * .top = position.y - 2
5473 * * .bottom = .top + height
5474 * * separation between items .y = itemSpacing.cy - height
5475 * * .x = itemSpacing.cx - width
5476 * LVIR_LABEL Returns the bounding rectangle of the item text.
5478 * * For LVS_ICON
5479 * * Experiment shows that native control returns:
5480 * * width = text length
5481 * * .left = position.x - width/2
5482 * * .right = .left + width
5483 * * height = ntmH * linecount + 2
5484 * * .top = position.y + iconSize.cy + 6
5485 * * .bottom = .top + height
5486 * * separation between items .y = itemSpacing.cy - height
5487 * * .x = itemSpacing.cx - width
5488 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5489 * rectangles, but excludes columns in report view.
5491 * RETURN:
5492 * SUCCESS : TRUE
5493 * FAILURE : FALSE
5495 * NOTES
5496 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5497 * upon whether the window has the focus currently and on whether the item
5498 * is the one with the focus. Ensure that the control's record of which
5499 * item has the focus agrees with the items' records.
5501 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5503 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5504 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5505 BOOL doLabel = TRUE, oversizedBox = FALSE;
5506 POINT Position, Origin;
5507 LVITEMW lvItem;
5508 RECT label_rect;
5510 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5512 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5514 LISTVIEW_GetOrigin(infoPtr, &Origin);
5515 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5517 /* Be smart and try to figure out the minimum we have to do */
5518 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5519 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5520 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5521 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5522 oversizedBox = TRUE;
5524 /* get what we need from the item before hand, so we make
5525 * only one request. This can speed up things, if data
5526 * is stored on the app side */
5527 lvItem.mask = 0;
5528 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5529 if (doLabel) lvItem.mask |= LVIF_TEXT;
5530 lvItem.iItem = nItem;
5531 lvItem.iSubItem = 0;
5532 lvItem.pszText = szDispText;
5533 lvItem.cchTextMax = DISP_TEXT_SIZE;
5534 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5535 /* we got the state already up, simulate it here, to avoid a reget */
5536 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5538 lvItem.mask |= LVIF_STATE;
5539 lvItem.stateMask = LVIS_FOCUSED;
5540 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5543 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5544 lprc->left = LVIR_BOUNDS;
5545 switch(lprc->left)
5547 case LVIR_ICON:
5548 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5549 break;
5551 case LVIR_LABEL:
5552 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5553 break;
5555 case LVIR_BOUNDS:
5556 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5557 break;
5559 case LVIR_SELECTBOUNDS:
5560 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5561 UnionRect(lprc, lprc, &label_rect);
5562 break;
5564 default:
5565 WARN("Unknown value: %ld\n", lprc->left);
5566 return FALSE;
5569 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5571 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5573 return TRUE;
5576 /***
5577 * DESCRIPTION:
5578 * Retrieves the spacing between listview control items.
5580 * PARAMETER(S):
5581 * [I] infoPtr : valid pointer to the listview structure
5582 * [IO] lprc : rectangle to receive the output
5583 * on input, lprc->top = nSubItem
5584 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5586 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5587 * not only those of the first column.
5588 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5590 * RETURN:
5591 * TRUE: success
5592 * FALSE: failure
5594 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5596 POINT Position;
5597 LVITEMW lvItem;
5598 INT nColumn = lprc->top;
5600 if (!lprc) return FALSE;
5602 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5603 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5604 if (lprc->top == 0)
5605 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5607 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5609 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5611 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5613 lvItem.mask = 0;
5614 lvItem.iItem = nItem;
5615 lvItem.iSubItem = nColumn;
5617 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5618 switch(lprc->left)
5620 case LVIR_ICON:
5621 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5622 break;
5624 case LVIR_LABEL:
5625 case LVIR_BOUNDS:
5626 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5627 break;
5629 default:
5630 ERR("Unknown bounds=%ld\n", lprc->left);
5631 return FALSE;
5634 OffsetRect(lprc, Position.x, Position.y);
5635 return TRUE;
5639 /***
5640 * DESCRIPTION:
5641 * Retrieves the width of a label.
5643 * PARAMETER(S):
5644 * [I] infoPtr : valid pointer to the listview structure
5646 * RETURN:
5647 * SUCCESS : string width (in pixels)
5648 * FAILURE : zero
5650 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5652 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5653 LVITEMW lvItem;
5655 TRACE("(nItem=%d)\n", nItem);
5657 lvItem.mask = LVIF_TEXT;
5658 lvItem.iItem = nItem;
5659 lvItem.iSubItem = 0;
5660 lvItem.pszText = szDispText;
5661 lvItem.cchTextMax = DISP_TEXT_SIZE;
5662 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5664 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5667 /***
5668 * DESCRIPTION:
5669 * Retrieves the spacing between listview control items.
5671 * PARAMETER(S):
5672 * [I] infoPtr : valid pointer to the listview structure
5673 * [I] bSmall : flag for small or large icon
5675 * RETURN:
5676 * Horizontal + vertical spacing
5678 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5680 LONG lResult;
5682 if (!bSmall)
5684 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5686 else
5688 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5689 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5690 else
5691 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5693 return lResult;
5696 /***
5697 * DESCRIPTION:
5698 * Retrieves the state of a listview control item.
5700 * PARAMETER(S):
5701 * [I] infoPtr : valid pointer to the listview structure
5702 * [I] nItem : item index
5703 * [I] uMask : state mask
5705 * RETURN:
5706 * State specified by the mask.
5708 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5710 LVITEMW lvItem;
5712 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5714 lvItem.iItem = nItem;
5715 lvItem.iSubItem = 0;
5716 lvItem.mask = LVIF_STATE;
5717 lvItem.stateMask = uMask;
5718 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5720 return lvItem.state & uMask;
5723 /***
5724 * DESCRIPTION:
5725 * Retrieves the text of a listview control item or subitem.
5727 * PARAMETER(S):
5728 * [I] hwnd : window handle
5729 * [I] nItem : item index
5730 * [IO] lpLVItem : item information
5731 * [I] isW : TRUE if lpLVItem is Unicode
5733 * RETURN:
5734 * SUCCESS : string length
5735 * FAILURE : 0
5737 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5739 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5741 lpLVItem->mask = LVIF_TEXT;
5742 lpLVItem->iItem = nItem;
5743 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5745 return textlenT(lpLVItem->pszText, isW);
5748 /***
5749 * DESCRIPTION:
5750 * Searches for an item based on properties + relationships.
5752 * PARAMETER(S):
5753 * [I] infoPtr : valid pointer to the listview structure
5754 * [I] nItem : item index
5755 * [I] uFlags : relationship flag
5757 * RETURN:
5758 * SUCCESS : item index
5759 * FAILURE : -1
5761 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5763 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5764 UINT uMask = 0;
5765 LVFINDINFOW lvFindInfo;
5766 INT nCountPerColumn;
5767 INT nCountPerRow;
5768 INT i;
5770 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5771 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5773 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5775 if (uFlags & LVNI_CUT)
5776 uMask |= LVIS_CUT;
5778 if (uFlags & LVNI_DROPHILITED)
5779 uMask |= LVIS_DROPHILITED;
5781 if (uFlags & LVNI_FOCUSED)
5782 uMask |= LVIS_FOCUSED;
5784 if (uFlags & LVNI_SELECTED)
5785 uMask |= LVIS_SELECTED;
5787 /* if we're asked for the focused item, that's only one,
5788 * so it's worth optimizing */
5789 if (uFlags & LVNI_FOCUSED)
5791 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5792 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5795 if (uFlags & LVNI_ABOVE)
5797 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5799 while (nItem >= 0)
5801 nItem--;
5802 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5803 return nItem;
5806 else
5808 /* Special case for autoarrange - move 'til the top of a list */
5809 if (is_autoarrange(infoPtr))
5811 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5812 while (nItem - nCountPerRow >= 0)
5814 nItem -= nCountPerRow;
5815 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5816 return nItem;
5818 return -1;
5820 lvFindInfo.flags = LVFI_NEARESTXY;
5821 lvFindInfo.vkDirection = VK_UP;
5822 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5823 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5825 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5826 return nItem;
5830 else if (uFlags & LVNI_BELOW)
5832 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5834 while (nItem < infoPtr->nItemCount)
5836 nItem++;
5837 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5838 return nItem;
5841 else
5843 /* Special case for autoarrange - move 'til the bottom of a list */
5844 if (is_autoarrange(infoPtr))
5846 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5847 while (nItem + nCountPerRow < infoPtr->nItemCount )
5849 nItem += nCountPerRow;
5850 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5851 return nItem;
5853 return -1;
5855 lvFindInfo.flags = LVFI_NEARESTXY;
5856 lvFindInfo.vkDirection = VK_DOWN;
5857 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5858 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5860 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5861 return nItem;
5865 else if (uFlags & LVNI_TOLEFT)
5867 if (uView == LVS_LIST)
5869 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5870 while (nItem - nCountPerColumn >= 0)
5872 nItem -= nCountPerColumn;
5873 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5874 return nItem;
5877 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5879 /* Special case for autoarrange - move 'ti the beginning of a row */
5880 if (is_autoarrange(infoPtr))
5882 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5883 while (nItem % nCountPerRow > 0)
5885 nItem --;
5886 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5887 return nItem;
5889 return -1;
5891 lvFindInfo.flags = LVFI_NEARESTXY;
5892 lvFindInfo.vkDirection = VK_LEFT;
5893 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5894 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5896 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5897 return nItem;
5901 else if (uFlags & LVNI_TORIGHT)
5903 if (uView == LVS_LIST)
5905 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5906 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5908 nItem += nCountPerColumn;
5909 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5910 return nItem;
5913 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5915 /* Special case for autoarrange - move 'til the end of a row */
5916 if (is_autoarrange(infoPtr))
5918 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5919 while (nItem % nCountPerRow < nCountPerRow - 1 )
5921 nItem ++;
5922 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5923 return nItem;
5925 return -1;
5927 lvFindInfo.flags = LVFI_NEARESTXY;
5928 lvFindInfo.vkDirection = VK_RIGHT;
5929 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5930 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5932 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5933 return nItem;
5937 else
5939 nItem++;
5941 /* search by index */
5942 for (i = nItem; i < infoPtr->nItemCount; i++)
5944 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5945 return i;
5949 return -1;
5952 /* LISTVIEW_GetNumberOfWorkAreas */
5954 /***
5955 * DESCRIPTION:
5956 * Retrieves the origin coordinates when in icon or small icon display mode.
5958 * PARAMETER(S):
5959 * [I] infoPtr : valid pointer to the listview structure
5960 * [O] lpptOrigin : coordinate information
5962 * RETURN:
5963 * None.
5965 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5967 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5968 INT nHorzPos = 0, nVertPos = 0;
5969 SCROLLINFO scrollInfo;
5971 scrollInfo.cbSize = sizeof(SCROLLINFO);
5972 scrollInfo.fMask = SIF_POS;
5974 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5975 nHorzPos = scrollInfo.nPos;
5976 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5977 nVertPos = scrollInfo.nPos;
5979 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5981 lpptOrigin->x = infoPtr->rcList.left;
5982 lpptOrigin->y = infoPtr->rcList.top;
5983 if (uView == LVS_LIST)
5984 nHorzPos *= infoPtr->nItemWidth;
5985 else if (uView == LVS_REPORT)
5986 nVertPos *= infoPtr->nItemHeight;
5988 lpptOrigin->x -= nHorzPos;
5989 lpptOrigin->y -= nVertPos;
5991 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
5994 /***
5995 * DESCRIPTION:
5996 * Retrieves the width of a string.
5998 * PARAMETER(S):
5999 * [I] hwnd : window handle
6000 * [I] lpszText : text string to process
6001 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6003 * RETURN:
6004 * SUCCESS : string width (in pixels)
6005 * FAILURE : zero
6007 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6009 SIZE stringSize;
6011 stringSize.cx = 0;
6012 if (is_textT(lpszText, isW))
6014 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6015 HDC hdc = GetDC(infoPtr->hwndSelf);
6016 HFONT hOldFont = SelectObject(hdc, hFont);
6018 if (isW)
6019 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6020 else
6021 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6022 SelectObject(hdc, hOldFont);
6023 ReleaseDC(infoPtr->hwndSelf, hdc);
6025 return stringSize.cx;
6028 /***
6029 * DESCRIPTION:
6030 * Determines which listview item is located at the specified position.
6032 * PARAMETER(S):
6033 * [I] infoPtr : valid pointer to the listview structure
6034 * [IO] lpht : hit test information
6035 * [I] subitem : fill out iSubItem.
6036 * [I] select : return the index only if the hit selects the item
6038 * NOTE:
6039 * (mm 20001022): We must not allow iSubItem to be touched, for
6040 * an app might pass only a structure with space up to iItem!
6041 * (MS Office 97 does that for instance in the file open dialog)
6043 * RETURN:
6044 * SUCCESS : item index
6045 * FAILURE : -1
6047 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6049 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6050 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6051 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6052 POINT Origin, Position, opt;
6053 LVITEMW lvItem;
6054 ITERATOR i;
6055 INT iItem;
6057 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6059 lpht->flags = 0;
6060 lpht->iItem = -1;
6061 if (subitem) lpht->iSubItem = 0;
6063 if (infoPtr->rcList.left > lpht->pt.x)
6064 lpht->flags |= LVHT_TOLEFT;
6065 else if (infoPtr->rcList.right < lpht->pt.x)
6066 lpht->flags |= LVHT_TORIGHT;
6068 if (infoPtr->rcList.top > lpht->pt.y)
6069 lpht->flags |= LVHT_ABOVE;
6070 else if (infoPtr->rcList.bottom < lpht->pt.y)
6071 lpht->flags |= LVHT_BELOW;
6073 TRACE("lpht->flags=0x%x\n", lpht->flags);
6074 if (lpht->flags) return -1;
6076 lpht->flags |= LVHT_NOWHERE;
6078 LISTVIEW_GetOrigin(infoPtr, &Origin);
6080 /* first deal with the large items */
6081 rcSearch.left = lpht->pt.x;
6082 rcSearch.top = lpht->pt.y;
6083 rcSearch.right = rcSearch.left + 1;
6084 rcSearch.bottom = rcSearch.top + 1;
6086 iterator_frameditems(&i, infoPtr, &rcSearch);
6087 iterator_next(&i); /* go to first item in the sequence */
6088 iItem = i.nItem;
6089 iterator_destroy(&i);
6091 TRACE("lpht->iItem=%d\n", iItem);
6092 if (iItem == -1) return -1;
6094 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6095 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6096 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6097 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6098 lvItem.iItem = iItem;
6099 lvItem.iSubItem = 0;
6100 lvItem.pszText = szDispText;
6101 lvItem.cchTextMax = DISP_TEXT_SIZE;
6102 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6103 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6105 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6106 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6107 opt.x = lpht->pt.x - Position.x - Origin.x;
6108 opt.y = lpht->pt.y - Position.y - Origin.y;
6110 if (uView == LVS_REPORT)
6111 rcBounds = rcBox;
6112 else
6113 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6114 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6115 if (!PtInRect(&rcBounds, opt)) return -1;
6117 if (PtInRect(&rcIcon, opt))
6118 lpht->flags |= LVHT_ONITEMICON;
6119 else if (PtInRect(&rcLabel, opt))
6120 lpht->flags |= LVHT_ONITEMLABEL;
6121 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6122 lpht->flags |= LVHT_ONITEMSTATEICON;
6123 if (lpht->flags & LVHT_ONITEM)
6124 lpht->flags &= ~LVHT_NOWHERE;
6126 TRACE("lpht->flags=0x%x\n", lpht->flags);
6127 if (uView == LVS_REPORT && subitem)
6129 INT j;
6131 rcBounds.right = rcBounds.left;
6132 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6134 rcBounds.left = rcBounds.right;
6135 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6136 if (PtInRect(&rcBounds, opt))
6138 lpht->iSubItem = j;
6139 break;
6144 if (select && !(uView == LVS_REPORT &&
6145 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6146 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6148 if (uView == LVS_REPORT)
6150 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6151 UnionRect(&rcBounds, &rcBounds, &rcState);
6153 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6155 return lpht->iItem = iItem;
6159 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6160 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6161 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6162 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6163 their own sort proc. when sending LVM_SORTITEMS.
6165 /* Platform SDK:
6166 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6168 LVS_SORTXXX must be specified,
6169 LVS_OWNERDRAW is not set,
6170 <item>.pszText is not LPSTR_TEXTCALLBACK.
6172 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6173 are sorted based on item text..."
6175 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6177 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6178 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6179 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6181 /* if we're sorting descending, negate the return value */
6182 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6185 /***
6186 * DESCRIPTION:
6187 * Inserts a new item in the listview control.
6189 * PARAMETER(S):
6190 * [I] infoPtr : valid pointer to the listview structure
6191 * [I] lpLVItem : item information
6192 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6194 * RETURN:
6195 * SUCCESS : new item index
6196 * FAILURE : -1
6198 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6200 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6201 INT nItem;
6202 HDPA hdpaSubItems;
6203 NMLISTVIEW nmlv;
6204 ITEM_INFO *lpItem;
6205 BOOL is_sorted, has_changed;
6206 LVITEMW item;
6207 HWND hwndSelf = infoPtr->hwndSelf;
6209 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6211 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6213 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6214 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6216 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6218 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6220 /* insert item in listview control data structure */
6221 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6222 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6224 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6225 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6227 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6228 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6229 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6230 if (nItem == -1) goto fail;
6231 infoPtr->nItemCount++;
6233 /* shift indices first so they don't get tangled */
6234 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6236 /* set the item attributes */
6237 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6239 /* full size structure expected - _WIN32IE >= 0x560 */
6240 item = *lpLVItem;
6242 else if (lpLVItem->mask & LVIF_INDENT)
6244 /* indent member expected - _WIN32IE >= 0x300 */
6245 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6247 else
6249 /* minimal structure expected */
6250 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6252 item.iItem = nItem;
6253 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6254 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6256 /* if we're sorted, sort the list, and update the index */
6257 if (is_sorted)
6259 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6260 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6261 assert(nItem != -1);
6264 /* make room for the position, if we are in the right mode */
6265 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6267 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6268 goto undo;
6269 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6271 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6272 goto undo;
6276 /* send LVN_INSERTITEM notification */
6277 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6278 nmlv.iItem = nItem;
6279 nmlv.lParam = lpItem->lParam;
6280 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6281 if (!IsWindow(hwndSelf))
6282 return -1;
6284 /* align items (set position of each item) */
6285 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6287 POINT pt;
6289 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6290 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6291 else
6292 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6294 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6297 /* now is the invalidation fun */
6298 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6299 return nItem;
6301 undo:
6302 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6303 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6304 infoPtr->nItemCount--;
6305 fail:
6306 DPA_DeletePtr(hdpaSubItems, 0);
6307 DPA_Destroy (hdpaSubItems);
6308 Free (lpItem);
6309 return -1;
6312 /***
6313 * DESCRIPTION:
6314 * Redraws a range of items.
6316 * PARAMETER(S):
6317 * [I] infoPtr : valid pointer to the listview structure
6318 * [I] nFirst : first item
6319 * [I] nLast : last item
6321 * RETURN:
6322 * SUCCESS : TRUE
6323 * FAILURE : FALSE
6325 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6327 INT i;
6329 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6330 max(nFirst, nLast) >= infoPtr->nItemCount)
6331 return FALSE;
6333 for (i = nFirst; i <= nLast; i++)
6334 LISTVIEW_InvalidateItem(infoPtr, i);
6336 return TRUE;
6339 /***
6340 * DESCRIPTION:
6341 * Scroll the content of a listview.
6343 * PARAMETER(S):
6344 * [I] infoPtr : valid pointer to the listview structure
6345 * [I] dx : horizontal scroll amount in pixels
6346 * [I] dy : vertical scroll amount in pixels
6348 * RETURN:
6349 * SUCCESS : TRUE
6350 * FAILURE : FALSE
6352 * COMMENTS:
6353 * If the control is in report mode (LVS_REPORT) the control can
6354 * be scrolled only in line increments. "dy" will be rounded to the
6355 * nearest number of pixels that are a whole line. Ex: if line height
6356 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6357 * is passed the the scroll will be 0. (per MSDN 7/2002)
6359 * For: (per experimentaion with native control and CSpy ListView)
6360 * LVS_ICON dy=1 = 1 pixel (vertical only)
6361 * dx ignored
6362 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6363 * dx ignored
6364 * LVS_LIST dx=1 = 1 column (horizontal only)
6365 * but will only scroll 1 column per message
6366 * no matter what the value.
6367 * dy must be 0 or FALSE returned.
6368 * LVS_REPORT dx=1 = 1 pixel
6369 * dy= see above
6372 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6374 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6375 case LVS_REPORT:
6376 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6377 dy /= infoPtr->nItemHeight;
6378 break;
6379 case LVS_LIST:
6380 if (dy != 0) return FALSE;
6381 break;
6382 default: /* icon */
6383 dx = 0;
6384 break;
6387 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6388 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6390 return TRUE;
6393 /***
6394 * DESCRIPTION:
6395 * Sets the background color.
6397 * PARAMETER(S):
6398 * [I] infoPtr : valid pointer to the listview structure
6399 * [I] clrBk : background color
6401 * RETURN:
6402 * SUCCESS : TRUE
6403 * FAILURE : FALSE
6405 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6407 TRACE("(clrBk=%lx)\n", clrBk);
6409 if(infoPtr->clrBk != clrBk) {
6410 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6411 infoPtr->clrBk = clrBk;
6412 if (clrBk == CLR_NONE)
6413 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6414 else
6415 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6416 LISTVIEW_InvalidateList(infoPtr);
6419 return TRUE;
6422 /* LISTVIEW_SetBkImage */
6424 /*** Helper for {Insert,Set}ColumnT *only* */
6425 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6427 if (lpColumn->mask & LVCF_FMT)
6429 /* format member is valid */
6430 lphdi->mask |= HDI_FORMAT;
6432 /* set text alignment (leftmost column must be left-aligned) */
6433 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6434 lphdi->fmt |= HDF_LEFT;
6435 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6436 lphdi->fmt |= HDF_RIGHT;
6437 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6438 lphdi->fmt |= HDF_CENTER;
6440 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6441 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6443 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6445 lphdi->fmt |= HDF_IMAGE;
6446 lphdi->iImage = I_IMAGECALLBACK;
6450 if (lpColumn->mask & LVCF_WIDTH)
6452 lphdi->mask |= HDI_WIDTH;
6453 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6455 /* make it fill the remainder of the controls width */
6456 RECT rcHeader;
6457 INT item_index;
6459 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6461 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6462 lphdi->cxy += rcHeader.right - rcHeader.left;
6465 /* retrieve the layout of the header */
6466 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6467 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6469 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6471 else
6472 lphdi->cxy = lpColumn->cx;
6475 if (lpColumn->mask & LVCF_TEXT)
6477 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6478 lphdi->fmt |= HDF_STRING;
6479 lphdi->pszText = lpColumn->pszText;
6480 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6483 if (lpColumn->mask & LVCF_IMAGE)
6485 lphdi->mask |= HDI_IMAGE;
6486 lphdi->iImage = lpColumn->iImage;
6489 if (lpColumn->mask & LVCF_ORDER)
6491 lphdi->mask |= HDI_ORDER;
6492 lphdi->iOrder = lpColumn->iOrder;
6497 /***
6498 * DESCRIPTION:
6499 * Inserts a new column.
6501 * PARAMETER(S):
6502 * [I] infoPtr : valid pointer to the listview structure
6503 * [I] nColumn : column index
6504 * [I] lpColumn : column information
6505 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6507 * RETURN:
6508 * SUCCESS : new column index
6509 * FAILURE : -1
6511 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6512 const LVCOLUMNW *lpColumn, BOOL isW)
6514 COLUMN_INFO *lpColumnInfo;
6515 INT nNewColumn;
6516 HDITEMW hdi;
6518 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6520 if (!lpColumn || nColumn < 0) return -1;
6521 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6523 ZeroMemory(&hdi, sizeof(HDITEMW));
6524 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6526 /* insert item in header control */
6527 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6528 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6529 (WPARAM)nColumn, (LPARAM)&hdi);
6530 if (nNewColumn == -1) return -1;
6531 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6533 /* create our own column info */
6534 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6535 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6537 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6538 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6540 /* now we have to actually adjust the data */
6541 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6543 SUBITEM_INFO *lpSubItem;
6544 HDPA hdpaSubItems;
6545 INT nItem, i;
6547 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6549 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6550 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6552 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6553 if (lpSubItem->iSubItem >= nNewColumn)
6554 lpSubItem->iSubItem++;
6559 /* make space for the new column */
6560 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6562 return nNewColumn;
6564 fail:
6565 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6566 if (lpColumnInfo)
6568 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6569 Free(lpColumnInfo);
6571 return -1;
6574 /***
6575 * DESCRIPTION:
6576 * Sets the attributes of a header item.
6578 * PARAMETER(S):
6579 * [I] infoPtr : valid pointer to the listview structure
6580 * [I] nColumn : column index
6581 * [I] lpColumn : column attributes
6582 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6584 * RETURN:
6585 * SUCCESS : TRUE
6586 * FAILURE : FALSE
6588 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6589 const LVCOLUMNW *lpColumn, BOOL isW)
6591 HDITEMW hdi, hdiget;
6592 BOOL bResult;
6594 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6596 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6598 ZeroMemory(&hdi, sizeof(HDITEMW));
6599 if (lpColumn->mask & LVCF_FMT)
6601 hdi.mask |= HDI_FORMAT;
6602 hdiget.mask = HDI_FORMAT;
6603 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6604 hdi.fmt = hdiget.fmt & HDF_STRING;
6606 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6608 /* set header item attributes */
6609 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6610 if (!bResult) return FALSE;
6612 if (lpColumn->mask & LVCF_FMT)
6614 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6615 int oldFmt = lpColumnInfo->fmt;
6617 lpColumnInfo->fmt = lpColumn->fmt;
6618 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6620 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6621 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6625 return TRUE;
6628 /***
6629 * DESCRIPTION:
6630 * Sets the column order array
6632 * PARAMETERS:
6633 * [I] infoPtr : valid pointer to the listview structure
6634 * [I] iCount : number of elements in column order array
6635 * [I] lpiArray : pointer to column order array
6637 * RETURN:
6638 * SUCCESS : TRUE
6639 * FAILURE : FALSE
6641 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6643 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6645 if (!lpiArray)
6646 return FALSE;
6648 return TRUE;
6652 /***
6653 * DESCRIPTION:
6654 * Sets the width of a column
6656 * PARAMETERS:
6657 * [I] infoPtr : valid pointer to the listview structure
6658 * [I] nColumn : column index
6659 * [I] cx : column width
6661 * RETURN:
6662 * SUCCESS : TRUE
6663 * FAILURE : FALSE
6665 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6667 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6668 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6669 INT max_cx = 0;
6670 HDITEMW hdi;
6672 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6674 /* set column width only if in report or list mode */
6675 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6677 /* take care of invalid cx values */
6678 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6679 else if (uView == LVS_LIST && cx < 1) return FALSE;
6681 /* resize all columns if in LVS_LIST mode */
6682 if(uView == LVS_LIST)
6684 infoPtr->nItemWidth = cx;
6685 LISTVIEW_InvalidateList(infoPtr);
6686 return TRUE;
6689 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6691 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6693 INT nLabelWidth;
6694 LVITEMW lvItem;
6696 lvItem.mask = LVIF_TEXT;
6697 lvItem.iItem = 0;
6698 lvItem.iSubItem = nColumn;
6699 lvItem.pszText = szDispText;
6700 lvItem.cchTextMax = DISP_TEXT_SIZE;
6701 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6703 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6704 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6705 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6707 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6708 max_cx += infoPtr->iconSize.cx;
6709 max_cx += TRAILING_LABEL_PADDING;
6712 /* autosize based on listview items width */
6713 if(cx == LVSCW_AUTOSIZE)
6714 cx = max_cx;
6715 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6717 /* if iCol is the last column make it fill the remainder of the controls width */
6718 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6720 RECT rcHeader;
6721 POINT Origin;
6723 LISTVIEW_GetOrigin(infoPtr, &Origin);
6724 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6726 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6728 else
6730 /* Despite what the MS docs say, if this is not the last
6731 column, then MS resizes the column to the width of the
6732 largest text string in the column, including headers
6733 and items. This is different from LVSCW_AUTOSIZE in that
6734 LVSCW_AUTOSIZE ignores the header string length. */
6735 cx = 0;
6737 /* retrieve header text */
6738 hdi.mask = HDI_TEXT;
6739 hdi.cchTextMax = DISP_TEXT_SIZE;
6740 hdi.pszText = szDispText;
6741 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6743 HDC hdc = GetDC(infoPtr->hwndSelf);
6744 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6745 SIZE size;
6747 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6748 cx = size.cx + TRAILING_HEADER_PADDING;
6749 /* FIXME: Take into account the header image, if one is present */
6750 SelectObject(hdc, old_font);
6751 ReleaseDC(infoPtr->hwndSelf, hdc);
6753 cx = max (cx, max_cx);
6757 if (cx < 0) return FALSE;
6759 /* call header to update the column change */
6760 hdi.mask = HDI_WIDTH;
6761 hdi.cxy = cx;
6762 TRACE("hdi.cxy=%d\n", hdi.cxy);
6763 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6766 /***
6767 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6770 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6772 HDC hdc_wnd, hdc;
6773 HBITMAP hbm_im, hbm_mask, hbm_orig;
6774 RECT rc;
6775 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6776 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6777 HIMAGELIST himl;
6779 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6780 ILC_COLOR | ILC_MASK, 2, 2);
6781 hdc_wnd = GetDC(infoPtr->hwndSelf);
6782 hdc = CreateCompatibleDC(hdc_wnd);
6783 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6784 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6785 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6787 rc.left = rc.top = 0;
6788 rc.right = GetSystemMetrics(SM_CXSMICON);
6789 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6791 hbm_orig = SelectObject(hdc, hbm_mask);
6792 FillRect(hdc, &rc, hbr_white);
6793 InflateRect(&rc, -3, -3);
6794 FillRect(hdc, &rc, hbr_black);
6796 SelectObject(hdc, hbm_im);
6797 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6798 SelectObject(hdc, hbm_orig);
6799 ImageList_Add(himl, hbm_im, hbm_mask);
6801 SelectObject(hdc, hbm_im);
6802 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6803 SelectObject(hdc, hbm_orig);
6804 ImageList_Add(himl, hbm_im, hbm_mask);
6806 DeleteObject(hbm_mask);
6807 DeleteObject(hbm_im);
6808 DeleteDC(hdc);
6810 return himl;
6813 /***
6814 * DESCRIPTION:
6815 * Sets the extended listview style.
6817 * PARAMETERS:
6818 * [I] infoPtr : valid pointer to the listview structure
6819 * [I] dwMask : mask
6820 * [I] dwStyle : style
6822 * RETURN:
6823 * SUCCESS : previous style
6824 * FAILURE : 0
6826 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6828 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6830 /* set new style */
6831 if (dwMask)
6832 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6833 else
6834 infoPtr->dwLvExStyle = dwStyle;
6836 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6838 HIMAGELIST himl = 0;
6839 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6840 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6841 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6844 return dwOldStyle;
6847 /***
6848 * DESCRIPTION:
6849 * Sets the new hot cursor used during hot tracking and hover selection.
6851 * PARAMETER(S):
6852 * [I] infoPtr : valid pointer to the listview structure
6853 * [I] hCursor : the new hot cursor handle
6855 * RETURN:
6856 * Returns the previous hot cursor
6858 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6860 HCURSOR oldCursor = infoPtr->hHotCursor;
6862 infoPtr->hHotCursor = hCursor;
6864 return oldCursor;
6868 /***
6869 * DESCRIPTION:
6870 * Sets the hot item index.
6872 * PARAMETERS:
6873 * [I] infoPtr : valid pointer to the listview structure
6874 * [I] iIndex : index
6876 * RETURN:
6877 * SUCCESS : previous hot item index
6878 * FAILURE : -1 (no hot item)
6880 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6882 INT iOldIndex = infoPtr->nHotItem;
6884 infoPtr->nHotItem = iIndex;
6886 return iOldIndex;
6890 /***
6891 * DESCRIPTION:
6892 * Sets the amount of time the cursor must hover over an item before it is selected.
6894 * PARAMETER(S):
6895 * [I] infoPtr : valid pointer to the listview structure
6896 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6898 * RETURN:
6899 * Returns the previous hover time
6901 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6903 DWORD oldHoverTime = infoPtr->dwHoverTime;
6905 infoPtr->dwHoverTime = dwHoverTime;
6907 return oldHoverTime;
6910 /***
6911 * DESCRIPTION:
6912 * Sets spacing for icons of LVS_ICON style.
6914 * PARAMETER(S):
6915 * [I] infoPtr : valid pointer to the listview structure
6916 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6917 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6919 * RETURN:
6920 * MAKELONG(oldcx, oldcy)
6922 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6924 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6925 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6927 TRACE("requested=(%d,%d)\n", cx, cy);
6929 /* this is supported only for LVS_ICON style */
6930 if (uView != LVS_ICON) return oldspacing;
6932 /* set to defaults, if instructed to */
6933 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6934 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6936 /* if 0 then compute width
6937 * FIXME: Should scan each item and determine max width of
6938 * icon or label, then make that the width */
6939 if (cx == 0)
6940 cx = infoPtr->iconSpacing.cx;
6942 /* if 0 then compute height */
6943 if (cy == 0)
6944 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6945 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6948 infoPtr->iconSpacing.cx = cx;
6949 infoPtr->iconSpacing.cy = cy;
6951 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6952 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6953 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6954 infoPtr->ntmHeight);
6956 /* these depend on the iconSpacing */
6957 LISTVIEW_UpdateItemSize(infoPtr);
6959 return oldspacing;
6962 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6964 INT cx, cy;
6966 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6968 size->cx = cx;
6969 size->cy = cy;
6971 else
6973 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6974 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6978 /***
6979 * DESCRIPTION:
6980 * Sets image lists.
6982 * PARAMETER(S):
6983 * [I] infoPtr : valid pointer to the listview structure
6984 * [I] nType : image list type
6985 * [I] himl : image list handle
6987 * RETURN:
6988 * SUCCESS : old image list
6989 * FAILURE : NULL
6991 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6993 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6994 INT oldHeight = infoPtr->nItemHeight;
6995 HIMAGELIST himlOld = 0;
6997 TRACE("(nType=%d, himl=%p\n", nType, himl);
6999 switch (nType)
7001 case LVSIL_NORMAL:
7002 himlOld = infoPtr->himlNormal;
7003 infoPtr->himlNormal = himl;
7004 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7005 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7006 break;
7008 case LVSIL_SMALL:
7009 himlOld = infoPtr->himlSmall;
7010 infoPtr->himlSmall = himl;
7011 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7012 break;
7014 case LVSIL_STATE:
7015 himlOld = infoPtr->himlState;
7016 infoPtr->himlState = himl;
7017 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7018 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7019 break;
7021 default:
7022 ERR("Unknown icon type=%d\n", nType);
7023 return NULL;
7026 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7027 if (infoPtr->nItemHeight != oldHeight)
7028 LISTVIEW_UpdateScroll(infoPtr);
7030 return himlOld;
7033 /***
7034 * DESCRIPTION:
7035 * Preallocates memory (does *not* set the actual count of items !)
7037 * PARAMETER(S):
7038 * [I] infoPtr : valid pointer to the listview structure
7039 * [I] nItems : item count (projected number of items to allocate)
7040 * [I] dwFlags : update flags
7042 * RETURN:
7043 * SUCCESS : TRUE
7044 * FAILURE : FALSE
7046 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7048 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7050 if (infoPtr->dwStyle & LVS_OWNERDATA)
7052 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7053 INT nOldCount = infoPtr->nItemCount;
7055 if (nItems < nOldCount)
7057 RANGE range = { nItems, nOldCount };
7058 ranges_del(infoPtr->selectionRanges, range);
7059 if (infoPtr->nFocusedItem >= nItems)
7061 infoPtr->nFocusedItem = -1;
7062 SetRectEmpty(&infoPtr->rcFocus);
7066 infoPtr->nItemCount = nItems;
7067 LISTVIEW_UpdateScroll(infoPtr);
7069 /* the flags are valid only in ownerdata report and list modes */
7070 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7072 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7073 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7075 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7076 LISTVIEW_InvalidateList(infoPtr);
7077 else
7079 INT nFrom, nTo;
7080 POINT Origin;
7081 RECT rcErase;
7083 LISTVIEW_GetOrigin(infoPtr, &Origin);
7084 nFrom = min(nOldCount, nItems);
7085 nTo = max(nOldCount, nItems);
7087 if (uView == LVS_REPORT)
7089 rcErase.left = 0;
7090 rcErase.top = nFrom * infoPtr->nItemHeight;
7091 rcErase.right = infoPtr->nItemWidth;
7092 rcErase.bottom = nTo * infoPtr->nItemHeight;
7093 OffsetRect(&rcErase, Origin.x, Origin.y);
7094 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7095 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7097 else /* LVS_LIST */
7099 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7101 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7102 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7103 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7104 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7105 OffsetRect(&rcErase, Origin.x, Origin.y);
7106 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7107 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7109 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7110 rcErase.top = 0;
7111 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7112 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7113 OffsetRect(&rcErase, Origin.x, Origin.y);
7114 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7115 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7119 else
7121 /* According to MSDN for non-LVS_OWNERDATA this is just
7122 * a performance issue. The control allocates its internal
7123 * data structures for the number of items specified. It
7124 * cuts down on the number of memory allocations. Therefore
7125 * we will just issue a WARN here
7127 WARN("for non-ownerdata performance option not implemented.\n");
7130 return TRUE;
7133 /***
7134 * DESCRIPTION:
7135 * Sets the position of an item.
7137 * PARAMETER(S):
7138 * [I] infoPtr : valid pointer to the listview structure
7139 * [I] nItem : item index
7140 * [I] pt : coordinate
7142 * RETURN:
7143 * SUCCESS : TRUE
7144 * FAILURE : FALSE
7146 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7148 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7149 POINT Origin;
7151 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7153 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7154 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7156 LISTVIEW_GetOrigin(infoPtr, &Origin);
7158 /* This point value seems to be an undocumented feature.
7159 * The best guess is that it means either at the origin,
7160 * or at true beginning of the list. I will assume the origin. */
7161 if ((pt.x == -1) && (pt.y == -1))
7162 pt = Origin;
7164 if (uView == LVS_ICON)
7166 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7167 pt.y -= ICON_TOP_PADDING;
7169 pt.x -= Origin.x;
7170 pt.y -= Origin.y;
7172 infoPtr->bAutoarrange = FALSE;
7174 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7177 /***
7178 * DESCRIPTION:
7179 * Sets the state of one or many items.
7181 * PARAMETER(S):
7182 * [I] infoPtr : valid pointer to the listview structure
7183 * [I] nItem : item index
7184 * [I] lpLVItem : item or subitem info
7186 * RETURN:
7187 * SUCCESS : TRUE
7188 * FAILURE : FALSE
7190 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7192 BOOL bResult = TRUE;
7193 LVITEMW lvItem;
7195 lvItem.iItem = nItem;
7196 lvItem.iSubItem = 0;
7197 lvItem.mask = LVIF_STATE;
7198 lvItem.state = lpLVItem->state;
7199 lvItem.stateMask = lpLVItem->stateMask;
7200 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7202 if (nItem == -1)
7204 /* apply to all items */
7205 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7206 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7208 else
7209 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7212 * Update selection mark
7214 * Investigation on windows 2k showed that selection mark was updated
7215 * whenever a new selection was made, but if the selected item was
7216 * unselected it was not updated.
7218 * we are probably still not 100% accurate, but this at least sets the
7219 * proper selection mark when it is needed
7222 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7223 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7225 int i;
7226 infoPtr->nSelectionMark = -1;
7227 for (i = 0; i < infoPtr->nItemCount; i++)
7229 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7231 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7233 infoPtr->nSelectionMark = i;
7234 break;
7237 else if (ranges_contain(infoPtr->selectionRanges, i))
7239 infoPtr->nSelectionMark = i;
7240 break;
7245 return bResult;
7248 /***
7249 * DESCRIPTION:
7250 * Sets the text of an item or subitem.
7252 * PARAMETER(S):
7253 * [I] hwnd : window handle
7254 * [I] nItem : item index
7255 * [I] lpLVItem : item or subitem info
7256 * [I] isW : TRUE if input is Unicode
7258 * RETURN:
7259 * SUCCESS : TRUE
7260 * FAILURE : FALSE
7262 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7264 LVITEMW lvItem;
7266 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7268 lvItem.iItem = nItem;
7269 lvItem.iSubItem = lpLVItem->iSubItem;
7270 lvItem.mask = LVIF_TEXT;
7271 lvItem.pszText = lpLVItem->pszText;
7272 lvItem.cchTextMax = lpLVItem->cchTextMax;
7274 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7276 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7279 /***
7280 * DESCRIPTION:
7281 * Set item index that marks the start of a multiple selection.
7283 * PARAMETER(S):
7284 * [I] infoPtr : valid pointer to the listview structure
7285 * [I] nIndex : index
7287 * RETURN:
7288 * Index number or -1 if there is no selection mark.
7290 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7292 INT nOldIndex = infoPtr->nSelectionMark;
7294 TRACE("(nIndex=%d)\n", nIndex);
7296 infoPtr->nSelectionMark = nIndex;
7298 return nOldIndex;
7301 /***
7302 * DESCRIPTION:
7303 * Sets the text background color.
7305 * PARAMETER(S):
7306 * [I] infoPtr : valid pointer to the listview structure
7307 * [I] clrTextBk : text background color
7309 * RETURN:
7310 * SUCCESS : TRUE
7311 * FAILURE : FALSE
7313 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7315 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7317 if (infoPtr->clrTextBk != clrTextBk)
7319 infoPtr->clrTextBk = clrTextBk;
7320 LISTVIEW_InvalidateList(infoPtr);
7323 return TRUE;
7326 /***
7327 * DESCRIPTION:
7328 * Sets the text foreground color.
7330 * PARAMETER(S):
7331 * [I] infoPtr : valid pointer to the listview structure
7332 * [I] clrText : text color
7334 * RETURN:
7335 * SUCCESS : TRUE
7336 * FAILURE : FALSE
7338 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7340 TRACE("(clrText=%lx)\n", clrText);
7342 if (infoPtr->clrText != clrText)
7344 infoPtr->clrText = clrText;
7345 LISTVIEW_InvalidateList(infoPtr);
7348 return TRUE;
7351 /***
7352 * DESCRIPTION:
7353 * Determines which listview item is located at the specified position.
7355 * PARAMETER(S):
7356 * [I] infoPtr : valid pointer to the listview structure
7357 * [I] hwndNewToolTip : handle to new ToolTip
7359 * RETURN:
7360 * old tool tip
7362 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7364 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7365 infoPtr->hwndToolTip = hwndNewToolTip;
7366 return hwndOldToolTip;
7369 /* LISTVIEW_SetUnicodeFormat */
7370 /* LISTVIEW_SetWorkAreas */
7372 /***
7373 * DESCRIPTION:
7374 * Callback internally used by LISTVIEW_SortItems()
7376 * PARAMETER(S):
7377 * [I] first : pointer to first ITEM_INFO to compare
7378 * [I] second : pointer to second ITEM_INFO to compare
7379 * [I] lParam : HWND of control
7381 * RETURN:
7382 * if first comes before second : negative
7383 * if first comes after second : positive
7384 * if first and second are equivalent : zero
7386 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7388 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7389 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7390 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7392 /* Forward the call to the client defined callback */
7393 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7396 /***
7397 * DESCRIPTION:
7398 * Sorts the listview items.
7400 * PARAMETER(S):
7401 * [I] infoPtr : valid pointer to the listview structure
7402 * [I] pfnCompare : application-defined value
7403 * [I] lParamSort : pointer to comparision callback
7405 * RETURN:
7406 * SUCCESS : TRUE
7407 * FAILURE : FALSE
7409 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7411 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7412 HDPA hdpaSubItems;
7413 ITEM_INFO *lpItem;
7414 LPVOID selectionMarkItem;
7415 LVITEMW item;
7416 int i;
7418 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7420 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7422 if (!pfnCompare) return FALSE;
7423 if (!infoPtr->hdpaItems) return FALSE;
7425 /* if there are 0 or 1 items, there is no need to sort */
7426 if (infoPtr->nItemCount < 2) return TRUE;
7428 if (infoPtr->nFocusedItem >= 0)
7430 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7431 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7432 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7434 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7435 /* clear the lpItem->state for non-selected ones */
7436 /* remove the selection ranges */
7438 infoPtr->pfnCompare = pfnCompare;
7439 infoPtr->lParamSort = lParamSort;
7440 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7442 /* Adjust selections and indices so that they are the way they should
7443 * be after the sort (otherwise, the list items move around, but
7444 * whatever is at the item's previous original position will be
7445 * selected instead)
7447 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7448 for (i=0; i < infoPtr->nItemCount; i++)
7450 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7451 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7453 if (lpItem->state & LVIS_SELECTED)
7455 item.state = LVIS_SELECTED;
7456 item.stateMask = LVIS_SELECTED;
7457 LISTVIEW_SetItemState(infoPtr, i, &item);
7459 if (lpItem->state & LVIS_FOCUSED)
7461 infoPtr->nFocusedItem = i;
7462 lpItem->state &= ~LVIS_FOCUSED;
7465 if (selectionMarkItem != NULL)
7466 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7467 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7469 /* refresh the display */
7470 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7471 LISTVIEW_InvalidateList(infoPtr);
7473 return TRUE;
7476 /***
7477 * DESCRIPTION:
7478 * Update theme handle after a theme change.
7480 * PARAMETER(S):
7481 * [I] infoPtr : valid pointer to the listview structure
7483 * RETURN:
7484 * SUCCESS : 0
7485 * FAILURE : something else
7487 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7489 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7490 CloseThemeData(theme);
7491 OpenThemeData(infoPtr->hwndSelf, themeClass);
7492 return 0;
7495 /***
7496 * DESCRIPTION:
7497 * Updates an items or rearranges the listview control.
7499 * PARAMETER(S):
7500 * [I] infoPtr : valid pointer to the listview structure
7501 * [I] nItem : item index
7503 * RETURN:
7504 * SUCCESS : TRUE
7505 * FAILURE : FALSE
7507 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7509 TRACE("(nItem=%d)\n", nItem);
7511 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7513 /* rearrange with default alignment style */
7514 if (is_autoarrange(infoPtr))
7515 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7516 else
7517 LISTVIEW_InvalidateItem(infoPtr, nItem);
7519 return TRUE;
7523 /***
7524 * DESCRIPTION:
7525 * Creates the listview control.
7527 * PARAMETER(S):
7528 * [I] hwnd : window handle
7529 * [I] lpcs : the create parameters
7531 * RETURN:
7532 * Success: 0
7533 * Failure: -1
7535 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7537 LISTVIEW_INFO *infoPtr;
7538 UINT uView = lpcs->style & LVS_TYPEMASK;
7539 LOGFONTW logFont;
7541 TRACE("(lpcs=%p)\n", lpcs);
7543 /* initialize info pointer */
7544 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7545 if (!infoPtr) return -1;
7547 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7549 infoPtr->hwndSelf = hwnd;
7550 infoPtr->dwStyle = lpcs->style;
7551 /* determine the type of structures to use */
7552 infoPtr->hwndNotify = lpcs->hwndParent;
7553 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7554 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7556 /* initialize color information */
7557 infoPtr->clrBk = CLR_NONE;
7558 infoPtr->clrText = comctl32_color.clrWindowText;
7559 infoPtr->clrTextBk = CLR_DEFAULT;
7560 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7562 /* set default values */
7563 infoPtr->nFocusedItem = -1;
7564 infoPtr->nSelectionMark = -1;
7565 infoPtr->nHotItem = -1;
7566 infoPtr->bRedraw = TRUE;
7567 infoPtr->bNoItemMetrics = TRUE;
7568 infoPtr->bDoChangeNotify = TRUE;
7569 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7570 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7571 infoPtr->nEditLabelItem = -1;
7572 infoPtr->dwHoverTime = -1; /* default system hover time */
7573 infoPtr->nMeasureItemHeight = 0;
7575 /* get default font (icon title) */
7576 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7577 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7578 infoPtr->hFont = infoPtr->hDefaultFont;
7579 LISTVIEW_SaveTextMetrics(infoPtr);
7581 /* create header */
7582 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7583 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7584 0, 0, 0, 0, hwnd, NULL,
7585 lpcs->hInstance, NULL);
7586 if (!infoPtr->hwndHeader) goto fail;
7588 /* set header unicode format */
7589 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7591 /* set header font */
7592 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7594 /* allocate memory for the data structure */
7595 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7596 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7597 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7598 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7599 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7601 /* initialize the icon sizes */
7602 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7603 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7605 /* init item size to avoid division by 0 */
7606 LISTVIEW_UpdateItemSize (infoPtr);
7608 if (uView == LVS_REPORT)
7610 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7612 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7614 else
7616 /* set HDS_HIDDEN flag to hide the header bar */
7617 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7618 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7622 OpenThemeData(hwnd, themeClass);
7624 return 0;
7626 fail:
7627 DestroyWindow(infoPtr->hwndHeader);
7628 ranges_destroy(infoPtr->selectionRanges);
7629 DPA_Destroy(infoPtr->hdpaItems);
7630 DPA_Destroy(infoPtr->hdpaPosX);
7631 DPA_Destroy(infoPtr->hdpaPosY);
7632 DPA_Destroy(infoPtr->hdpaColumns);
7633 Free(infoPtr);
7634 return -1;
7637 /***
7638 * DESCRIPTION:
7639 * Destroys the listview control.
7641 * PARAMETER(S):
7642 * [I] infoPtr : valid pointer to the listview structure
7644 * RETURN:
7645 * Success: 0
7646 * Failure: -1
7648 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7650 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7651 CloseThemeData(theme);
7652 return 0;
7655 /***
7656 * DESCRIPTION:
7657 * Enables the listview control.
7659 * PARAMETER(S):
7660 * [I] infoPtr : valid pointer to the listview structure
7661 * [I] bEnable : specifies whether to enable or disable the window
7663 * RETURN:
7664 * SUCCESS : TRUE
7665 * FAILURE : FALSE
7667 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7669 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7670 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7671 return TRUE;
7674 /***
7675 * DESCRIPTION:
7676 * Erases the background of the listview control.
7678 * PARAMETER(S):
7679 * [I] infoPtr : valid pointer to the listview structure
7680 * [I] hdc : device context handle
7682 * RETURN:
7683 * SUCCESS : TRUE
7684 * FAILURE : FALSE
7686 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7688 RECT rc;
7690 TRACE("(hdc=%p)\n", hdc);
7692 if (!GetClipBox(hdc, &rc)) return FALSE;
7694 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7698 /***
7699 * DESCRIPTION:
7700 * Helper function for LISTVIEW_[HV]Scroll *only*.
7701 * Performs vertical/horizontal scrolling by a give amount.
7703 * PARAMETER(S):
7704 * [I] infoPtr : valid pointer to the listview structure
7705 * [I] dx : amount of horizontal scroll
7706 * [I] dy : amount of vertical scroll
7708 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7710 /* now we can scroll the list */
7711 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7712 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7713 /* if we have focus, adjust rect */
7714 OffsetRect(&infoPtr->rcFocus, dx, dy);
7715 UpdateWindow(infoPtr->hwndSelf);
7718 /***
7719 * DESCRIPTION:
7720 * Performs vertical scrolling.
7722 * PARAMETER(S):
7723 * [I] infoPtr : valid pointer to the listview structure
7724 * [I] nScrollCode : scroll code
7725 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7726 * [I] hScrollWnd : scrollbar control window handle
7728 * RETURN:
7729 * Zero
7731 * NOTES:
7732 * SB_LINEUP/SB_LINEDOWN:
7733 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7734 * for LVS_REPORT is 1 line
7735 * for LVS_LIST cannot occur
7738 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7739 INT nScrollDiff, HWND hScrollWnd)
7741 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7742 INT nOldScrollPos, nNewScrollPos;
7743 SCROLLINFO scrollInfo;
7744 BOOL is_an_icon;
7746 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7747 debugscrollcode(nScrollCode), nScrollDiff);
7749 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7751 scrollInfo.cbSize = sizeof(SCROLLINFO);
7752 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7754 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7756 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7758 nOldScrollPos = scrollInfo.nPos;
7759 switch (nScrollCode)
7761 case SB_INTERNAL:
7762 break;
7764 case SB_LINEUP:
7765 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7766 break;
7768 case SB_LINEDOWN:
7769 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7770 break;
7772 case SB_PAGEUP:
7773 nScrollDiff = -scrollInfo.nPage;
7774 break;
7776 case SB_PAGEDOWN:
7777 nScrollDiff = scrollInfo.nPage;
7778 break;
7780 case SB_THUMBPOSITION:
7781 case SB_THUMBTRACK:
7782 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7783 break;
7785 default:
7786 nScrollDiff = 0;
7789 /* quit right away if pos isn't changing */
7790 if (nScrollDiff == 0) return 0;
7792 /* calculate new position, and handle overflows */
7793 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7794 if (nScrollDiff > 0) {
7795 if (nNewScrollPos < nOldScrollPos ||
7796 nNewScrollPos > scrollInfo.nMax)
7797 nNewScrollPos = scrollInfo.nMax;
7798 } else {
7799 if (nNewScrollPos > nOldScrollPos ||
7800 nNewScrollPos < scrollInfo.nMin)
7801 nNewScrollPos = scrollInfo.nMin;
7804 /* set the new position, and reread in case it changed */
7805 scrollInfo.fMask = SIF_POS;
7806 scrollInfo.nPos = nNewScrollPos;
7807 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7809 /* carry on only if it really changed */
7810 if (nNewScrollPos == nOldScrollPos) return 0;
7812 /* now adjust to client coordinates */
7813 nScrollDiff = nOldScrollPos - nNewScrollPos;
7814 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7816 /* and scroll the window */
7817 scroll_list(infoPtr, 0, nScrollDiff);
7819 return 0;
7822 /***
7823 * DESCRIPTION:
7824 * Performs horizontal scrolling.
7826 * PARAMETER(S):
7827 * [I] infoPtr : valid pointer to the listview structure
7828 * [I] nScrollCode : scroll code
7829 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7830 * [I] hScrollWnd : scrollbar control window handle
7832 * RETURN:
7833 * Zero
7835 * NOTES:
7836 * SB_LINELEFT/SB_LINERIGHT:
7837 * for LVS_ICON, LVS_SMALLICON 1 pixel
7838 * for LVS_REPORT is 1 pixel
7839 * for LVS_LIST is 1 column --> which is a 1 because the
7840 * scroll is based on columns not pixels
7843 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7844 INT nScrollDiff, HWND hScrollWnd)
7846 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7847 INT nOldScrollPos, nNewScrollPos;
7848 SCROLLINFO scrollInfo;
7850 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7851 debugscrollcode(nScrollCode), nScrollDiff);
7853 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7855 scrollInfo.cbSize = sizeof(SCROLLINFO);
7856 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7858 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7860 nOldScrollPos = scrollInfo.nPos;
7862 switch (nScrollCode)
7864 case SB_INTERNAL:
7865 break;
7867 case SB_LINELEFT:
7868 nScrollDiff = -1;
7869 break;
7871 case SB_LINERIGHT:
7872 nScrollDiff = 1;
7873 break;
7875 case SB_PAGELEFT:
7876 nScrollDiff = -scrollInfo.nPage;
7877 break;
7879 case SB_PAGERIGHT:
7880 nScrollDiff = scrollInfo.nPage;
7881 break;
7883 case SB_THUMBPOSITION:
7884 case SB_THUMBTRACK:
7885 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7886 break;
7888 default:
7889 nScrollDiff = 0;
7892 /* quit right away if pos isn't changing */
7893 if (nScrollDiff == 0) return 0;
7895 /* calculate new position, and handle overflows */
7896 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7897 if (nScrollDiff > 0) {
7898 if (nNewScrollPos < nOldScrollPos ||
7899 nNewScrollPos > scrollInfo.nMax)
7900 nNewScrollPos = scrollInfo.nMax;
7901 } else {
7902 if (nNewScrollPos > nOldScrollPos ||
7903 nNewScrollPos < scrollInfo.nMin)
7904 nNewScrollPos = scrollInfo.nMin;
7907 /* set the new position, and reread in case it changed */
7908 scrollInfo.fMask = SIF_POS;
7909 scrollInfo.nPos = nNewScrollPos;
7910 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7912 /* carry on only if it really changed */
7913 if (nNewScrollPos == nOldScrollPos) return 0;
7915 if(uView == LVS_REPORT)
7916 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7918 /* now adjust to client coordinates */
7919 nScrollDiff = nOldScrollPos - nNewScrollPos;
7920 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7922 /* and scroll the window */
7923 scroll_list(infoPtr, nScrollDiff, 0);
7925 return 0;
7928 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7930 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7931 INT gcWheelDelta = 0;
7932 INT pulScrollLines = 3;
7933 SCROLLINFO scrollInfo;
7935 TRACE("(wheelDelta=%d)\n", wheelDelta);
7937 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7938 gcWheelDelta -= wheelDelta;
7940 scrollInfo.cbSize = sizeof(SCROLLINFO);
7941 scrollInfo.fMask = SIF_POS;
7943 switch(uView)
7945 case LVS_ICON:
7946 case LVS_SMALLICON:
7948 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7949 * should be fixed in the future.
7951 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7952 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7953 break;
7955 case LVS_REPORT:
7956 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7958 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7959 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7960 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7962 break;
7964 case LVS_LIST:
7965 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7966 break;
7968 return 0;
7971 /***
7972 * DESCRIPTION:
7973 * ???
7975 * PARAMETER(S):
7976 * [I] infoPtr : valid pointer to the listview structure
7977 * [I] nVirtualKey : virtual key
7978 * [I] lKeyData : key data
7980 * RETURN:
7981 * Zero
7983 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7985 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7986 HWND hwndSelf = infoPtr->hwndSelf;
7987 INT nItem = -1;
7988 NMLVKEYDOWN nmKeyDown;
7990 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7992 /* send LVN_KEYDOWN notification */
7993 nmKeyDown.wVKey = nVirtualKey;
7994 nmKeyDown.flags = 0;
7995 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7996 if (!IsWindow(hwndSelf))
7997 return 0;
7999 switch (nVirtualKey)
8001 case VK_SPACE:
8002 nItem = infoPtr->nFocusedItem;
8003 break;
8005 case VK_RETURN:
8006 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8008 if (!notify(infoPtr, NM_RETURN)) return 0;
8009 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8011 break;
8013 case VK_HOME:
8014 if (infoPtr->nItemCount > 0)
8015 nItem = 0;
8016 break;
8018 case VK_END:
8019 if (infoPtr->nItemCount > 0)
8020 nItem = infoPtr->nItemCount - 1;
8021 break;
8023 case VK_LEFT:
8024 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8025 break;
8027 case VK_UP:
8028 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8029 break;
8031 case VK_RIGHT:
8032 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8033 break;
8035 case VK_DOWN:
8036 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8037 break;
8039 case VK_PRIOR:
8040 if (uView == LVS_REPORT)
8042 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8043 if (infoPtr->nFocusedItem == topidx)
8044 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8045 else
8046 nItem = topidx;
8048 else
8049 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8050 * LISTVIEW_GetCountPerRow(infoPtr);
8051 if(nItem < 0) nItem = 0;
8052 break;
8054 case VK_NEXT:
8055 if (uView == LVS_REPORT)
8057 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8058 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8059 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8060 nItem = infoPtr->nFocusedItem + cnt - 1;
8061 else
8062 nItem = topidx + cnt - 1;
8064 else
8065 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8066 * LISTVIEW_GetCountPerRow(infoPtr);
8067 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8068 break;
8071 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8072 LISTVIEW_KeySelection(infoPtr, nItem);
8074 return 0;
8077 /***
8078 * DESCRIPTION:
8079 * Kills the focus.
8081 * PARAMETER(S):
8082 * [I] infoPtr : valid pointer to the listview structure
8084 * RETURN:
8085 * Zero
8087 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8089 TRACE("()\n");
8091 /* if we did not have the focus, there's nothing to do */
8092 if (!infoPtr->bFocus) return 0;
8094 /* send NM_KILLFOCUS notification */
8095 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8097 /* if we have a focus rectagle, get rid of it */
8098 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8100 /* set window focus flag */
8101 infoPtr->bFocus = FALSE;
8103 /* invalidate the selected items before reseting focus flag */
8104 LISTVIEW_InvalidateSelectedItems(infoPtr);
8106 return 0;
8109 /***
8110 * DESCRIPTION:
8111 * Processes double click messages (left mouse button).
8113 * PARAMETER(S):
8114 * [I] infoPtr : valid pointer to the listview structure
8115 * [I] wKey : key flag
8116 * [I] x,y : mouse coordinate
8118 * RETURN:
8119 * Zero
8121 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8123 LVHITTESTINFO htInfo;
8125 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8127 /* send NM_RELEASEDCAPTURE notification */
8128 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8130 htInfo.pt.x = x;
8131 htInfo.pt.y = y;
8133 /* send NM_DBLCLK notification */
8134 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8135 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8137 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8138 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8140 return 0;
8143 /***
8144 * DESCRIPTION:
8145 * Processes mouse down messages (left mouse button).
8147 * PARAMETERS:
8148 * infoPtr [I ] valid pointer to the listview structure
8149 * wKey [I ] key flag
8150 * x,y [I ] mouse coordinate
8152 * RETURN:
8153 * Zero
8155 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8157 LVHITTESTINFO lvHitTestInfo;
8158 static BOOL bGroupSelect = TRUE;
8159 POINT pt = { x, y };
8160 INT nItem;
8162 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8164 /* send NM_RELEASEDCAPTURE notification */
8165 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8167 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8169 /* set left button down flag and record the click position */
8170 infoPtr->bLButtonDown = TRUE;
8171 infoPtr->ptClickPos = pt;
8173 lvHitTestInfo.pt.x = x;
8174 lvHitTestInfo.pt.y = y;
8176 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8177 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8178 infoPtr->nEditLabelItem = -1;
8179 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8181 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8183 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8184 if(state == 1 || state == 2)
8186 LVITEMW lvitem;
8187 state ^= 3;
8188 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8189 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8190 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8192 return 0;
8195 if (infoPtr->dwStyle & LVS_SINGLESEL)
8197 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8198 infoPtr->nEditLabelItem = nItem;
8199 else
8200 LISTVIEW_SetSelection(infoPtr, nItem);
8202 else
8204 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8206 if (bGroupSelect)
8208 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8209 LISTVIEW_SetItemFocus(infoPtr, nItem);
8210 infoPtr->nSelectionMark = nItem;
8212 else
8214 LVITEMW item;
8216 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8217 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8219 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8220 infoPtr->nSelectionMark = nItem;
8223 else if (wKey & MK_CONTROL)
8225 LVITEMW item;
8227 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8229 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8230 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8231 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8232 infoPtr->nSelectionMark = nItem;
8234 else if (wKey & MK_SHIFT)
8236 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8238 else
8240 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8241 infoPtr->nEditLabelItem = nItem;
8243 /* set selection (clears other pre-existing selections) */
8244 LISTVIEW_SetSelection(infoPtr, nItem);
8248 else
8250 /* remove all selections */
8251 LISTVIEW_DeselectAll(infoPtr);
8252 ReleaseCapture();
8255 return 0;
8258 /***
8259 * DESCRIPTION:
8260 * Processes mouse up messages (left mouse button).
8262 * PARAMETERS:
8263 * infoPtr [I ] valid pointer to the listview structure
8264 * wKey [I ] key flag
8265 * x,y [I ] mouse coordinate
8267 * RETURN:
8268 * Zero
8270 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8272 LVHITTESTINFO lvHitTestInfo;
8274 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8276 if (!infoPtr->bLButtonDown) return 0;
8278 lvHitTestInfo.pt.x = x;
8279 lvHitTestInfo.pt.y = y;
8281 /* send NM_CLICK notification */
8282 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8283 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8285 /* set left button flag */
8286 infoPtr->bLButtonDown = FALSE;
8288 /* if we clicked on a selected item, edit the label */
8289 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8290 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8292 return 0;
8295 /***
8296 * DESCRIPTION:
8297 * Destroys the listview control (called after WM_DESTROY).
8299 * PARAMETER(S):
8300 * [I] infoPtr : valid pointer to the listview structure
8302 * RETURN:
8303 * Zero
8305 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8307 TRACE("()\n");
8309 /* delete all items */
8310 LISTVIEW_DeleteAllItems(infoPtr);
8312 /* destroy data structure */
8313 DPA_Destroy(infoPtr->hdpaItems);
8314 DPA_Destroy(infoPtr->hdpaPosX);
8315 DPA_Destroy(infoPtr->hdpaPosY);
8316 DPA_Destroy(infoPtr->hdpaColumns);
8317 ranges_destroy(infoPtr->selectionRanges);
8319 /* destroy image lists */
8320 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8322 if (infoPtr->himlNormal)
8323 ImageList_Destroy(infoPtr->himlNormal);
8324 if (infoPtr->himlSmall)
8325 ImageList_Destroy(infoPtr->himlSmall);
8326 if (infoPtr->himlState)
8327 ImageList_Destroy(infoPtr->himlState);
8330 /* destroy font, bkgnd brush */
8331 infoPtr->hFont = 0;
8332 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8333 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8335 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8337 /* free listview info pointer*/
8338 Free(infoPtr);
8340 return 0;
8343 /***
8344 * DESCRIPTION:
8345 * Handles notifications from header.
8347 * PARAMETER(S):
8348 * [I] infoPtr : valid pointer to the listview structure
8349 * [I] nCtrlId : control identifier
8350 * [I] lpnmh : notification information
8352 * RETURN:
8353 * Zero
8355 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8357 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8358 HWND hwndSelf = infoPtr->hwndSelf;
8360 TRACE("(lpnmh=%p)\n", lpnmh);
8362 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8364 switch (lpnmh->hdr.code)
8366 case HDN_ITEMCHANGINGW:
8367 case HDN_ITEMCHANGINGA:
8368 return notify_forward_header(infoPtr, lpnmh);
8369 case HDN_ITEMCHANGEDW:
8370 case HDN_ITEMCHANGEDA:
8371 notify_forward_header(infoPtr, lpnmh);
8372 if (!IsWindow(hwndSelf))
8373 break;
8374 /* Fall through */
8375 case HDN_TRACKW:
8376 case HDN_TRACKA:
8378 COLUMN_INFO *lpColumnInfo;
8379 INT dx, cxy;
8381 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8383 HDITEMW hdi;
8385 hdi.mask = HDI_WIDTH;
8386 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8387 cxy = hdi.cxy;
8389 else
8390 cxy = lpnmh->pitem->cxy;
8392 /* determine how much we change since the last know position */
8393 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8394 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8395 if (dx != 0)
8397 lpColumnInfo->rcHeader.right += dx;
8398 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8399 LISTVIEW_UpdateItemSize(infoPtr);
8400 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8402 POINT ptOrigin;
8403 RECT rcCol = lpColumnInfo->rcHeader;
8405 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8406 OffsetRect(&rcCol, ptOrigin.x, 0);
8408 rcCol.top = infoPtr->rcList.top;
8409 rcCol.bottom = infoPtr->rcList.bottom;
8411 /* resizing left-aligned columns leaves most of the left side untouched */
8412 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8414 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8415 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8418 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8422 break;
8424 case HDN_ITEMCLICKW:
8425 case HDN_ITEMCLICKA:
8427 /* Handle sorting by Header Column */
8428 NMLISTVIEW nmlv;
8430 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8431 nmlv.iItem = -1;
8432 nmlv.iSubItem = lpnmh->iItem;
8433 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8435 break;
8437 case HDN_DIVIDERDBLCLICKW:
8438 case HDN_DIVIDERDBLCLICKA:
8439 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8440 break;
8443 return 0;
8446 /***
8447 * DESCRIPTION:
8448 * Paint non-client area of control.
8450 * PARAMETER(S):
8451 * [I] infoPtr : valid pointer to the listview structureof the sender
8452 * [I] region : update region
8454 * RETURN:
8455 * TRUE - frame was painted
8456 * FALSE - call default window proc
8458 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8460 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8461 HDC dc;
8462 RECT r;
8463 HRGN cliprgn;
8464 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8465 cyEdge = GetSystemMetrics (SM_CYEDGE);
8467 if (!theme) return FALSE;
8469 GetWindowRect(infoPtr->hwndSelf, &r);
8471 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8472 r.right - cxEdge, r.bottom - cyEdge);
8473 if (region != (HRGN)1)
8474 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8475 OffsetRect(&r, -r.left, -r.top);
8477 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8478 OffsetRect(&r, -r.left, -r.top);
8480 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8481 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8482 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8483 ReleaseDC(infoPtr->hwndSelf, dc);
8485 /* Call default proc to get the scrollbars etc. painted */
8486 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8488 return TRUE;
8491 /***
8492 * DESCRIPTION:
8493 * Determines the type of structure to use.
8495 * PARAMETER(S):
8496 * [I] infoPtr : valid pointer to the listview structureof the sender
8497 * [I] hwndFrom : listview window handle
8498 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8500 * RETURN:
8501 * Zero
8503 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8505 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8507 if (nCommand != NF_REQUERY) return 0;
8509 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8511 return 0;
8514 /***
8515 * DESCRIPTION:
8516 * Paints/Repaints the listview control.
8518 * PARAMETER(S):
8519 * [I] infoPtr : valid pointer to the listview structure
8520 * [I] hdc : device context handle
8522 * RETURN:
8523 * Zero
8525 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8527 TRACE("(hdc=%p)\n", hdc);
8529 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8531 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8533 infoPtr->bNoItemMetrics = FALSE;
8534 LISTVIEW_UpdateItemSize(infoPtr);
8535 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8536 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8537 LISTVIEW_UpdateScroll(infoPtr);
8539 if (hdc)
8540 LISTVIEW_Refresh(infoPtr, hdc);
8541 else
8543 PAINTSTRUCT ps;
8545 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8546 if (!hdc) return 1;
8547 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8548 LISTVIEW_Refresh(infoPtr, hdc);
8549 EndPaint(infoPtr->hwndSelf, &ps);
8552 return 0;
8556 /***
8557 * DESCRIPTION:
8558 * Paints/Repaints the listview control.
8560 * PARAMETER(S):
8561 * [I] infoPtr : valid pointer to the listview structure
8562 * [I] hdc : device context handle
8563 * [I] options : drawing options
8565 * RETURN:
8566 * Zero
8568 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8570 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8572 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8573 return 0;
8575 if (options & PRF_ERASEBKGND)
8576 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8578 if (options & PRF_CLIENT)
8579 LISTVIEW_Paint(infoPtr, hdc);
8581 return 0;
8585 /***
8586 * DESCRIPTION:
8587 * Processes double click messages (right mouse button).
8589 * PARAMETER(S):
8590 * [I] infoPtr : valid pointer to the listview structure
8591 * [I] wKey : key flag
8592 * [I] x,y : mouse coordinate
8594 * RETURN:
8595 * Zero
8597 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8599 LVHITTESTINFO lvHitTestInfo;
8601 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8603 /* send NM_RELEASEDCAPTURE notification */
8604 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8606 /* send NM_RDBLCLK notification */
8607 lvHitTestInfo.pt.x = x;
8608 lvHitTestInfo.pt.y = y;
8609 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8610 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8612 return 0;
8615 /***
8616 * DESCRIPTION:
8617 * Processes mouse down messages (right mouse button).
8619 * PARAMETER(S):
8620 * [I] infoPtr : valid pointer to the listview structure
8621 * [I] wKey : key flag
8622 * [I] x,y : mouse coordinate
8624 * RETURN:
8625 * Zero
8627 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8629 LVHITTESTINFO lvHitTestInfo;
8630 INT nItem;
8632 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8634 /* send NM_RELEASEDCAPTURE notification */
8635 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8637 /* make sure the listview control window has the focus */
8638 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8640 /* set right button down flag */
8641 infoPtr->bRButtonDown = TRUE;
8643 /* determine the index of the selected item */
8644 lvHitTestInfo.pt.x = x;
8645 lvHitTestInfo.pt.y = y;
8646 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8648 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8650 LISTVIEW_SetItemFocus(infoPtr, nItem);
8651 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8652 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8653 LISTVIEW_SetSelection(infoPtr, nItem);
8655 else
8657 LISTVIEW_DeselectAll(infoPtr);
8660 return 0;
8663 /***
8664 * DESCRIPTION:
8665 * Processes mouse up messages (right mouse button).
8667 * PARAMETER(S):
8668 * [I] infoPtr : valid pointer to the listview structure
8669 * [I] wKey : key flag
8670 * [I] x,y : mouse coordinate
8672 * RETURN:
8673 * Zero
8675 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8677 LVHITTESTINFO lvHitTestInfo;
8678 POINT pt;
8680 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8682 if (!infoPtr->bRButtonDown) return 0;
8684 /* set button flag */
8685 infoPtr->bRButtonDown = FALSE;
8687 /* Send NM_RClICK notification */
8688 lvHitTestInfo.pt.x = x;
8689 lvHitTestInfo.pt.y = y;
8690 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8691 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
8693 /* Change to screen coordinate for WM_CONTEXTMENU */
8694 pt = lvHitTestInfo.pt;
8695 ClientToScreen(infoPtr->hwndSelf, &pt);
8697 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8698 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8699 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8701 return 0;
8705 /***
8706 * DESCRIPTION:
8707 * Sets the cursor.
8709 * PARAMETER(S):
8710 * [I] infoPtr : valid pointer to the listview structure
8711 * [I] hwnd : window handle of window containing the cursor
8712 * [I] nHittest : hit-test code
8713 * [I] wMouseMsg : ideintifier of the mouse message
8715 * RETURN:
8716 * TRUE if cursor is set
8717 * FALSE otherwise
8719 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8721 LVHITTESTINFO lvHitTestInfo;
8723 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8725 if(!infoPtr->hHotCursor) return FALSE;
8727 GetCursorPos(&lvHitTestInfo.pt);
8728 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8730 SetCursor(infoPtr->hHotCursor);
8732 return TRUE;
8735 /***
8736 * DESCRIPTION:
8737 * Sets the focus.
8739 * PARAMETER(S):
8740 * [I] infoPtr : valid pointer to the listview structure
8741 * [I] hwndLoseFocus : handle of previously focused window
8743 * RETURN:
8744 * Zero
8746 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8748 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8750 /* if we have the focus already, there's nothing to do */
8751 if (infoPtr->bFocus) return 0;
8753 /* send NM_SETFOCUS notification */
8754 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
8756 /* set window focus flag */
8757 infoPtr->bFocus = TRUE;
8759 /* put the focus rect back on */
8760 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8762 /* redraw all visible selected items */
8763 LISTVIEW_InvalidateSelectedItems(infoPtr);
8765 return 0;
8768 /***
8769 * DESCRIPTION:
8770 * Sets the font.
8772 * PARAMETER(S):
8773 * [I] infoPtr : valid pointer to the listview structure
8774 * [I] fRedraw : font handle
8775 * [I] fRedraw : redraw flag
8777 * RETURN:
8778 * Zero
8780 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8782 HFONT oldFont = infoPtr->hFont;
8784 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8786 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8787 if (infoPtr->hFont == oldFont) return 0;
8789 LISTVIEW_SaveTextMetrics(infoPtr);
8791 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8792 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8794 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8796 return 0;
8799 /***
8800 * DESCRIPTION:
8801 * Message handling for WM_SETREDRAW.
8802 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8804 * PARAMETER(S):
8805 * [I] infoPtr : valid pointer to the listview structure
8806 * [I] bRedraw: state of redraw flag
8808 * RETURN:
8809 * DefWinProc return value
8811 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8813 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8815 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8816 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8818 infoPtr->bRedraw = bRedraw;
8820 if(!bRedraw) return 0;
8822 if (is_autoarrange(infoPtr))
8823 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8824 LISTVIEW_UpdateScroll(infoPtr);
8826 /* despite what the WM_SETREDRAW docs says, apps expect us
8827 * to invalidate the listview here... stupid! */
8828 LISTVIEW_InvalidateList(infoPtr);
8830 return 0;
8833 /***
8834 * DESCRIPTION:
8835 * Resizes the listview control. This function processes WM_SIZE
8836 * messages. At this time, the width and height are not used.
8838 * PARAMETER(S):
8839 * [I] infoPtr : valid pointer to the listview structure
8840 * [I] Width : new width
8841 * [I] Height : new height
8843 * RETURN:
8844 * Zero
8846 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8848 RECT rcOld = infoPtr->rcList;
8850 TRACE("(width=%d, height=%d)\n", Width, Height);
8852 LISTVIEW_UpdateSize(infoPtr);
8853 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8855 /* do not bother with display related stuff if we're not redrawing */
8856 if (!is_redrawing(infoPtr)) return 0;
8858 if (is_autoarrange(infoPtr))
8859 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8861 LISTVIEW_UpdateScroll(infoPtr);
8863 /* refresh all only for lists whose height changed significantly */
8864 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8865 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8866 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8867 LISTVIEW_InvalidateList(infoPtr);
8869 return 0;
8872 /***
8873 * DESCRIPTION:
8874 * Sets the size information.
8876 * PARAMETER(S):
8877 * [I] infoPtr : valid pointer to the listview structure
8879 * RETURN:
8880 * None
8882 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8884 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8886 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
8888 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8890 if (uView == LVS_LIST)
8892 /* Apparently the "LIST" style is supposed to have the same
8893 * number of items in a column even if there is no scroll bar.
8894 * Since if a scroll bar already exists then the bottom is already
8895 * reduced, only reduce if the scroll bar does not currently exist.
8896 * The "2" is there to mimic the native control. I think it may be
8897 * related to either padding or edges. (GLA 7/2002)
8899 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8900 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8901 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8903 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8905 HDLAYOUT hl;
8906 WINDOWPOS wp;
8908 hl.prc = &infoPtr->rcList;
8909 hl.pwpos = &wp;
8910 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
8912 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8914 infoPtr->rcList.top = max(wp.cy, 0);
8917 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
8920 /***
8921 * DESCRIPTION:
8922 * Processes WM_STYLECHANGED messages.
8924 * PARAMETER(S):
8925 * [I] infoPtr : valid pointer to the listview structure
8926 * [I] wStyleType : window style type (normal or extended)
8927 * [I] lpss : window style information
8929 * RETURN:
8930 * Zero
8932 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8933 const STYLESTRUCT *lpss)
8935 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8936 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8938 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8939 wStyleType, lpss->styleOld, lpss->styleNew);
8941 if (wStyleType != GWL_STYLE) return 0;
8943 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8944 /* what if LVS_OWNERDATA changed? */
8945 /* or LVS_SINGLESEL */
8946 /* or LVS_SORT{AS,DES}CENDING */
8948 infoPtr->dwStyle = lpss->styleNew;
8950 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8951 ((lpss->styleNew & WS_HSCROLL) == 0))
8952 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8954 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8955 ((lpss->styleNew & WS_VSCROLL) == 0))
8956 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8958 if (uNewView != uOldView)
8960 SIZE oldIconSize = infoPtr->iconSize;
8961 HIMAGELIST himl;
8963 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8964 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8966 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8967 SetRectEmpty(&infoPtr->rcFocus);
8969 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8970 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8972 if (uNewView == LVS_ICON)
8974 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8976 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8977 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8978 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8981 else if (uNewView == LVS_REPORT)
8983 HDLAYOUT hl;
8984 WINDOWPOS wp;
8986 hl.prc = &infoPtr->rcList;
8987 hl.pwpos = &wp;
8988 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
8989 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8992 LISTVIEW_UpdateItemSize(infoPtr);
8995 if (uNewView == LVS_REPORT)
8996 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8998 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8999 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9000 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9002 /* update the size of the client area */
9003 LISTVIEW_UpdateSize(infoPtr);
9005 /* add scrollbars if needed */
9006 LISTVIEW_UpdateScroll(infoPtr);
9008 /* invalidate client area + erase background */
9009 LISTVIEW_InvalidateList(infoPtr);
9011 return 0;
9014 /***
9015 * DESCRIPTION:
9016 * Window procedure of the listview control.
9019 static LRESULT WINAPI
9020 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9022 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9024 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9026 if (!infoPtr && (uMsg != WM_CREATE))
9027 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9029 switch (uMsg)
9031 case LVM_APPROXIMATEVIEWRECT:
9032 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9033 LOWORD(lParam), HIWORD(lParam));
9034 case LVM_ARRANGE:
9035 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9037 /* case LVM_CANCELEDITLABEL: */
9039 case LVM_CREATEDRAGIMAGE:
9040 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9042 case LVM_DELETEALLITEMS:
9043 return LISTVIEW_DeleteAllItems(infoPtr);
9045 case LVM_DELETECOLUMN:
9046 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9048 case LVM_DELETEITEM:
9049 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9051 case LVM_EDITLABELW:
9052 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9054 case LVM_EDITLABELA:
9055 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9057 /* case LVM_ENABLEGROUPVIEW: */
9059 case LVM_ENSUREVISIBLE:
9060 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9062 case LVM_FINDITEMW:
9063 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9065 case LVM_FINDITEMA:
9066 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9068 case LVM_GETBKCOLOR:
9069 return infoPtr->clrBk;
9071 /* case LVM_GETBKIMAGE: */
9073 case LVM_GETCALLBACKMASK:
9074 return infoPtr->uCallbackMask;
9076 case LVM_GETCOLUMNA:
9077 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9079 case LVM_GETCOLUMNW:
9080 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9082 case LVM_GETCOLUMNORDERARRAY:
9083 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9085 case LVM_GETCOLUMNWIDTH:
9086 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9088 case LVM_GETCOUNTPERPAGE:
9089 return LISTVIEW_GetCountPerPage(infoPtr);
9091 case LVM_GETEDITCONTROL:
9092 return (LRESULT)infoPtr->hwndEdit;
9094 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9095 return infoPtr->dwLvExStyle;
9097 /* case LVM_GETGROUPINFO: */
9099 /* case LVM_GETGROUPMETRICS: */
9101 case LVM_GETHEADER:
9102 return (LRESULT)infoPtr->hwndHeader;
9104 case LVM_GETHOTCURSOR:
9105 return (LRESULT)infoPtr->hHotCursor;
9107 case LVM_GETHOTITEM:
9108 return infoPtr->nHotItem;
9110 case LVM_GETHOVERTIME:
9111 return infoPtr->dwHoverTime;
9113 case LVM_GETIMAGELIST:
9114 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9116 /* case LVM_GETINSERTMARK: */
9118 /* case LVM_GETINSERTMARKCOLOR: */
9120 /* case LVM_GETINSERTMARKRECT: */
9122 case LVM_GETISEARCHSTRINGA:
9123 case LVM_GETISEARCHSTRINGW:
9124 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9125 return FALSE;
9127 case LVM_GETITEMA:
9128 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9130 case LVM_GETITEMW:
9131 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9133 case LVM_GETITEMCOUNT:
9134 return infoPtr->nItemCount;
9136 case LVM_GETITEMPOSITION:
9137 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9139 case LVM_GETITEMRECT:
9140 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9142 case LVM_GETITEMSPACING:
9143 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9145 case LVM_GETITEMSTATE:
9146 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9148 case LVM_GETITEMTEXTA:
9149 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9151 case LVM_GETITEMTEXTW:
9152 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9154 case LVM_GETNEXTITEM:
9155 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9157 case LVM_GETNUMBEROFWORKAREAS:
9158 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9159 return 1;
9161 case LVM_GETORIGIN:
9162 if (!lParam) return FALSE;
9163 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9164 return TRUE;
9166 /* case LVM_GETOUTLINECOLOR: */
9168 /* case LVM_GETSELECTEDCOLUMN: */
9170 case LVM_GETSELECTEDCOUNT:
9171 return LISTVIEW_GetSelectedCount(infoPtr);
9173 case LVM_GETSELECTIONMARK:
9174 return infoPtr->nSelectionMark;
9176 case LVM_GETSTRINGWIDTHA:
9177 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9179 case LVM_GETSTRINGWIDTHW:
9180 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9182 case LVM_GETSUBITEMRECT:
9183 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9185 case LVM_GETTEXTBKCOLOR:
9186 return infoPtr->clrTextBk;
9188 case LVM_GETTEXTCOLOR:
9189 return infoPtr->clrText;
9191 /* case LVM_GETTILEINFO: */
9193 /* case LVM_GETTILEVIEWINFO: */
9195 case LVM_GETTOOLTIPS:
9196 if( !infoPtr->hwndToolTip )
9197 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9198 return (LRESULT)infoPtr->hwndToolTip;
9200 case LVM_GETTOPINDEX:
9201 return LISTVIEW_GetTopIndex(infoPtr);
9203 /*case LVM_GETUNICODEFORMAT:
9204 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9205 return FALSE;*/
9207 /* case LVM_GETVIEW: */
9209 case LVM_GETVIEWRECT:
9210 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9212 case LVM_GETWORKAREAS:
9213 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9214 return FALSE;
9216 /* case LVM_HASGROUP: */
9218 case LVM_HITTEST:
9219 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9221 case LVM_INSERTCOLUMNA:
9222 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9224 case LVM_INSERTCOLUMNW:
9225 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9227 /* case LVM_INSERTGROUP: */
9229 /* case LVM_INSERTGROUPSORTED: */
9231 case LVM_INSERTITEMA:
9232 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9234 case LVM_INSERTITEMW:
9235 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9237 /* case LVM_INSERTMARKHITTEST: */
9239 /* case LVM_ISGROUPVIEWENABLED: */
9241 /* case LVM_MAPIDTOINDEX: */
9243 /* case LVM_MAPINDEXTOID: */
9245 /* case LVM_MOVEGROUP: */
9247 /* case LVM_MOVEITEMTOGROUP: */
9249 case LVM_REDRAWITEMS:
9250 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9252 /* case LVM_REMOVEALLGROUPS: */
9254 /* case LVM_REMOVEGROUP: */
9256 case LVM_SCROLL:
9257 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9259 case LVM_SETBKCOLOR:
9260 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9262 /* case LVM_SETBKIMAGE: */
9264 case LVM_SETCALLBACKMASK:
9265 infoPtr->uCallbackMask = (UINT)wParam;
9266 return TRUE;
9268 case LVM_SETCOLUMNA:
9269 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9271 case LVM_SETCOLUMNW:
9272 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9274 case LVM_SETCOLUMNORDERARRAY:
9275 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9277 case LVM_SETCOLUMNWIDTH:
9278 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9280 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9281 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9283 /* case LVM_SETGROUPINFO: */
9285 /* case LVM_SETGROUPMETRICS: */
9287 case LVM_SETHOTCURSOR:
9288 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9290 case LVM_SETHOTITEM:
9291 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9293 case LVM_SETHOVERTIME:
9294 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9296 case LVM_SETICONSPACING:
9297 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9299 case LVM_SETIMAGELIST:
9300 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9302 /* case LVM_SETINFOTIP: */
9304 /* case LVM_SETINSERTMARK: */
9306 /* case LVM_SETINSERTMARKCOLOR: */
9308 case LVM_SETITEMA:
9309 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9311 case LVM_SETITEMW:
9312 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9314 case LVM_SETITEMCOUNT:
9315 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9317 case LVM_SETITEMPOSITION:
9319 POINT pt;
9320 pt.x = (short)LOWORD(lParam);
9321 pt.y = (short)HIWORD(lParam);
9322 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9325 case LVM_SETITEMPOSITION32:
9326 if (lParam == 0) return FALSE;
9327 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9329 case LVM_SETITEMSTATE:
9330 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9332 case LVM_SETITEMTEXTA:
9333 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9335 case LVM_SETITEMTEXTW:
9336 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9338 /* case LVM_SETOUTLINECOLOR: */
9340 /* case LVM_SETSELECTEDCOLUMN: */
9342 case LVM_SETSELECTIONMARK:
9343 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9345 case LVM_SETTEXTBKCOLOR:
9346 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9348 case LVM_SETTEXTCOLOR:
9349 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9351 /* case LVM_SETTILEINFO: */
9353 /* case LVM_SETTILEVIEWINFO: */
9355 /* case LVM_SETTILEWIDTH: */
9357 case LVM_SETTOOLTIPS:
9358 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9360 /* case LVM_SETUNICODEFORMAT: */
9362 /* case LVM_SETVIEW: */
9364 /* case LVM_SETWORKAREAS: */
9366 /* case LVM_SORTGROUPS: */
9368 case LVM_SORTITEMS:
9369 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9371 /* LVM_SORTITEMSEX: */
9373 case LVM_SUBITEMHITTEST:
9374 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9376 case LVM_UPDATE:
9377 return LISTVIEW_Update(infoPtr, (INT)wParam);
9379 case WM_CHAR:
9380 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9382 case WM_COMMAND:
9383 return LISTVIEW_Command(infoPtr, wParam, lParam);
9385 case WM_CREATE:
9386 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9388 case WM_DESTROY:
9389 return LISTVIEW_Destroy(infoPtr);
9391 case WM_ENABLE:
9392 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9394 case WM_ERASEBKGND:
9395 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9397 case WM_GETDLGCODE:
9398 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9400 case WM_GETFONT:
9401 return (LRESULT)infoPtr->hFont;
9403 case WM_HSCROLL:
9404 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9406 case WM_KEYDOWN:
9407 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9409 case WM_KILLFOCUS:
9410 return LISTVIEW_KillFocus(infoPtr);
9412 case WM_LBUTTONDBLCLK:
9413 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9415 case WM_LBUTTONDOWN:
9416 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9418 case WM_LBUTTONUP:
9419 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9421 case WM_MOUSEMOVE:
9422 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9424 case WM_MOUSEHOVER:
9425 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9427 case WM_NCDESTROY:
9428 return LISTVIEW_NCDestroy(infoPtr);
9430 case WM_NCPAINT:
9431 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9432 return 0;
9433 goto fwd_msg;
9435 case WM_NOTIFY:
9436 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9437 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9438 else return 0;
9440 case WM_NOTIFYFORMAT:
9441 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9443 case WM_PRINTCLIENT:
9444 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9446 case WM_PAINT:
9447 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9449 case WM_RBUTTONDBLCLK:
9450 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9452 case WM_RBUTTONDOWN:
9453 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9455 case WM_RBUTTONUP:
9456 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9458 case WM_SETCURSOR:
9459 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9460 return TRUE;
9461 goto fwd_msg;
9463 case WM_SETFOCUS:
9464 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9466 case WM_SETFONT:
9467 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9469 case WM_SETREDRAW:
9470 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9472 case WM_SIZE:
9473 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9475 case WM_STYLECHANGED:
9476 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9478 case WM_SYSCOLORCHANGE:
9479 COMCTL32_RefreshSysColors();
9480 return 0;
9482 /* case WM_TIMER: */
9483 case WM_THEMECHANGED:
9484 return LISTVIEW_ThemeChanged(infoPtr);
9486 case WM_VSCROLL:
9487 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9489 case WM_MOUSEWHEEL:
9490 if (wParam & (MK_SHIFT | MK_CONTROL))
9491 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9492 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9494 case WM_WINDOWPOSCHANGED:
9495 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9497 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9498 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9499 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9501 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9503 MEASUREITEMSTRUCT mis;
9504 mis.CtlType = ODT_LISTVIEW;
9505 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9506 mis.itemID = -1;
9507 mis.itemWidth = 0;
9508 mis.itemData = 0;
9509 mis.itemHeight= infoPtr->nItemHeight;
9510 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9511 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9512 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9515 LISTVIEW_UpdateSize(infoPtr);
9516 LISTVIEW_UpdateScroll(infoPtr);
9518 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9520 /* case WM_WININICHANGE: */
9522 default:
9523 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9524 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9526 fwd_msg:
9527 /* call default window procedure */
9528 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9533 /***
9534 * DESCRIPTION:
9535 * Registers the window class.
9537 * PARAMETER(S):
9538 * None
9540 * RETURN:
9541 * None
9543 void LISTVIEW_Register(void)
9545 WNDCLASSW wndClass;
9547 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9548 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9549 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9550 wndClass.cbClsExtra = 0;
9551 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9552 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9553 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9554 wndClass.lpszClassName = WC_LISTVIEWW;
9555 RegisterClassW(&wndClass);
9558 /***
9559 * DESCRIPTION:
9560 * Unregisters the window class.
9562 * PARAMETER(S):
9563 * None
9565 * RETURN:
9566 * None
9568 void LISTVIEW_Unregister(void)
9570 UnregisterClassW(WC_LISTVIEWW, NULL);
9573 /***
9574 * DESCRIPTION:
9575 * Handle any WM_COMMAND messages
9577 * PARAMETER(S):
9578 * [I] infoPtr : valid pointer to the listview structure
9579 * [I] wParam : the first message parameter
9580 * [I] lParam : the second message parameter
9582 * RETURN:
9583 * Zero.
9585 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9587 switch (HIWORD(wParam))
9589 case EN_UPDATE:
9592 * Adjust the edit window size
9594 WCHAR buffer[1024];
9595 HDC hdc = GetDC(infoPtr->hwndEdit);
9596 HFONT hFont, hOldFont = 0;
9597 RECT rect;
9598 SIZE sz;
9599 int len;
9601 if (!infoPtr->hwndEdit || !hdc) return 0;
9602 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9603 GetWindowRect(infoPtr->hwndEdit, &rect);
9605 /* Select font to get the right dimension of the string */
9606 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9607 if(hFont != 0)
9609 hOldFont = SelectObject(hdc, hFont);
9612 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9614 TEXTMETRICW textMetric;
9616 /* Add Extra spacing for the next character */
9617 GetTextMetricsW(hdc, &textMetric);
9618 sz.cx += (textMetric.tmMaxCharWidth * 2);
9620 SetWindowPos (
9621 infoPtr->hwndEdit,
9622 HWND_TOP,
9625 sz.cx,
9626 rect.bottom - rect.top,
9627 SWP_DRAWFRAME|SWP_NOMOVE);
9629 if(hFont != 0)
9630 SelectObject(hdc, hOldFont);
9632 ReleaseDC(infoPtr->hwndEdit, hdc);
9634 break;
9637 default:
9638 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9641 return 0;
9645 /***
9646 * DESCRIPTION:
9647 * Subclassed edit control windproc function
9649 * PARAMETER(S):
9650 * [I] hwnd : the edit window handle
9651 * [I] uMsg : the message that is to be processed
9652 * [I] wParam : first message parameter
9653 * [I] lParam : second message parameter
9654 * [I] isW : TRUE if input is Unicode
9656 * RETURN:
9657 * Zero.
9659 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9661 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9662 BOOL cancel = FALSE;
9664 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9665 hwnd, uMsg, wParam, lParam, isW);
9667 switch (uMsg)
9669 case WM_GETDLGCODE:
9670 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9672 case WM_KILLFOCUS:
9673 break;
9675 case WM_DESTROY:
9677 WNDPROC editProc = infoPtr->EditWndProc;
9678 infoPtr->EditWndProc = 0;
9679 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9680 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9683 case WM_KEYDOWN:
9684 if (VK_ESCAPE == (INT)wParam)
9686 cancel = TRUE;
9687 break;
9689 else if (VK_RETURN == (INT)wParam)
9690 break;
9692 default:
9693 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9696 /* kill the edit */
9697 if (infoPtr->hwndEdit)
9699 LPWSTR buffer = NULL;
9701 infoPtr->hwndEdit = 0;
9702 if (!cancel)
9704 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9706 if (len)
9708 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9710 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9711 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9715 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9717 if (buffer) Free(buffer);
9721 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9722 return 0;
9725 /***
9726 * DESCRIPTION:
9727 * Subclassed edit control Unicode windproc function
9729 * PARAMETER(S):
9730 * [I] hwnd : the edit window handle
9731 * [I] uMsg : the message that is to be processed
9732 * [I] wParam : first message parameter
9733 * [I] lParam : second message parameter
9735 * RETURN:
9737 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9739 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9742 /***
9743 * DESCRIPTION:
9744 * Subclassed edit control ANSI windproc function
9746 * PARAMETER(S):
9747 * [I] hwnd : the edit window handle
9748 * [I] uMsg : the message that is to be processed
9749 * [I] wParam : first message parameter
9750 * [I] lParam : second message parameter
9752 * RETURN:
9754 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9756 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9759 /***
9760 * DESCRIPTION:
9761 * Creates a subclassed edit cotrol
9763 * PARAMETER(S):
9764 * [I] infoPtr : valid pointer to the listview structure
9765 * [I] text : initial text for the edit
9766 * [I] style : the window style
9767 * [I] isW : TRUE if input is Unicode
9769 * RETURN:
9771 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9772 INT x, INT y, INT width, INT height, BOOL isW)
9774 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9775 HWND hedit;
9776 SIZE sz;
9777 HDC hdc;
9778 HDC hOldFont=0;
9779 TEXTMETRICW textMetric;
9780 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9782 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9784 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9785 hdc = GetDC(infoPtr->hwndSelf);
9787 /* Select the font to get appropriate metric dimensions */
9788 if(infoPtr->hFont != 0)
9789 hOldFont = SelectObject(hdc, infoPtr->hFont);
9791 /*Get String Length in pixels */
9792 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9794 /*Add Extra spacing for the next character */
9795 GetTextMetricsW(hdc, &textMetric);
9796 sz.cx += (textMetric.tmMaxCharWidth * 2);
9798 if(infoPtr->hFont != 0)
9799 SelectObject(hdc, hOldFont);
9801 ReleaseDC(infoPtr->hwndSelf, hdc);
9802 if (isW)
9803 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9804 else
9805 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9807 if (!hedit) return 0;
9809 infoPtr->EditWndProc = (WNDPROC)
9810 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9811 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9813 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9815 return hedit;