Remove tempfile used to download Mozilla ActiveX control once it's not
[wine/testsucceed.git] / dlls / comctl32 / listview.c
blobb6f026d860b489ef9de88851dca02cc6e5433c5b
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, debugrect(&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)
569 char* buf = debug_getbuf();
570 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
571 return buf;
572 } else return "(null)";
575 static inline const char* debugpoint(const POINT *lppt)
577 if (lppt)
579 char* buf = debug_getbuf();
580 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
581 return buf;
582 } else return "(null)";
585 static inline const char* debugrect(const RECT *rect)
587 if (rect)
589 char* buf = debug_getbuf();
590 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
591 rect->left, rect->top, rect->right, rect->bottom);
592 return buf;
593 } else return "(null)";
596 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
598 char* buf = debug_getbuf(), *text = buf;
599 int len, size = DEBUG_BUFFER_SIZE;
601 if (pScrollInfo == NULL) return "(null)";
602 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
603 if (len == -1) goto end; buf += len; size -= len;
604 if (pScrollInfo->fMask & SIF_RANGE)
605 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
606 else len = 0;
607 if (len == -1) goto end; buf += len; size -= len;
608 if (pScrollInfo->fMask & SIF_PAGE)
609 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
610 else len = 0;
611 if (len == -1) goto end; buf += len; size -= len;
612 if (pScrollInfo->fMask & SIF_POS)
613 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
614 else len = 0;
615 if (len == -1) goto end; buf += len; size -= len;
616 if (pScrollInfo->fMask & SIF_TRACKPOS)
617 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
618 else len = 0;
619 if (len == -1) goto end; buf += len; size -= len;
620 goto undo;
621 end:
622 buf = text + strlen(text);
623 undo:
624 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
625 return text;
628 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
630 if (plvnm)
632 char* buf = debug_getbuf();
633 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
634 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
635 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
636 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
637 return buf;
638 } else return "(null)";
641 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
643 char* buf = debug_getbuf(), *text = buf;
644 int len, size = DEBUG_BUFFER_SIZE;
646 if (lpLVItem == NULL) return "(null)";
647 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
648 if (len == -1) goto end; buf += len; size -= len;
649 if (lpLVItem->mask & LVIF_STATE)
650 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
651 else len = 0;
652 if (len == -1) goto end; buf += len; size -= len;
653 if (lpLVItem->mask & LVIF_TEXT)
654 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
655 else len = 0;
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpLVItem->mask & LVIF_IMAGE)
658 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
659 else len = 0;
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpLVItem->mask & LVIF_PARAM)
662 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
663 else len = 0;
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpLVItem->mask & LVIF_INDENT)
666 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
667 else len = 0;
668 if (len == -1) goto end; buf += len; size -= len;
669 goto undo;
670 end:
671 buf = text + strlen(text);
672 undo:
673 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
674 return text;
677 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
679 char* buf = debug_getbuf(), *text = buf;
680 int len, size = DEBUG_BUFFER_SIZE;
682 if (lpColumn == NULL) return "(null)";
683 len = snprintf(buf, size, "{");
684 if (len == -1) goto end; buf += len; size -= len;
685 if (lpColumn->mask & LVCF_SUBITEM)
686 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
687 else len = 0;
688 if (len == -1) goto end; buf += len; size -= len;
689 if (lpColumn->mask & LVCF_FMT)
690 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
691 else len = 0;
692 if (len == -1) goto end; buf += len; size -= len;
693 if (lpColumn->mask & LVCF_WIDTH)
694 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
695 else len = 0;
696 if (len == -1) goto end; buf += len; size -= len;
697 if (lpColumn->mask & LVCF_TEXT)
698 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
699 else len = 0;
700 if (len == -1) goto end; buf += len; size -= len;
701 if (lpColumn->mask & LVCF_IMAGE)
702 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
703 else len = 0;
704 if (len == -1) goto end; buf += len; size -= len;
705 if (lpColumn->mask & LVCF_ORDER)
706 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
707 else len = 0;
708 if (len == -1) goto end; buf += len; size -= len;
709 goto undo;
710 end:
711 buf = text + strlen(text);
712 undo:
713 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
714 return text;
717 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
719 if (lpht)
721 char* buf = debug_getbuf();
722 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
723 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
724 return buf;
725 } else return "(null)";
728 /* Return the corresponding text for a given scroll value */
729 static inline LPCSTR debugscrollcode(int nScrollCode)
731 switch(nScrollCode)
733 case SB_LINELEFT: return "SB_LINELEFT";
734 case SB_LINERIGHT: return "SB_LINERIGHT";
735 case SB_PAGELEFT: return "SB_PAGELEFT";
736 case SB_PAGERIGHT: return "SB_PAGERIGHT";
737 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
738 case SB_THUMBTRACK: return "SB_THUMBTRACK";
739 case SB_ENDSCROLL: return "SB_ENDSCROLL";
740 case SB_INTERNAL: return "SB_INTERNAL";
741 default: return "unknown";
746 /******** Notification functions i************************************/
748 static LRESULT notify_forward_header(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
750 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
751 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
754 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
756 LRESULT result;
758 TRACE("(code=%d)\n", code);
760 pnmh->hwndFrom = infoPtr->hwndSelf;
761 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
762 pnmh->code = code;
763 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
764 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
766 TRACE(" <= %ld\n", result);
768 return result;
771 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
773 NMHDR nmh;
774 return notify_hdr(infoPtr, code, &nmh);
777 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
779 NMITEMACTIVATE nmia;
780 LVITEMW item;
782 if (htInfo) {
783 nmia.uNewState = 0;
784 nmia.uOldState = 0;
785 nmia.uChanged = 0;
786 nmia.uKeyFlags = 0;
788 item.mask = LVIF_PARAM|LVIF_STATE;
789 item.iItem = htInfo->iItem;
790 item.iSubItem = 0;
791 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
792 nmia.lParam = item.lParam;
793 nmia.uOldState = item.state;
794 nmia.uNewState = item.state | LVIS_ACTIVATING;
795 nmia.uChanged = LVIF_STATE;
798 nmia.iItem = htInfo->iItem;
799 nmia.iSubItem = htInfo->iSubItem;
800 nmia.ptAction = htInfo->pt;
802 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
803 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
804 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
806 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
809 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
811 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
812 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
815 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
817 NMLISTVIEW nmlv;
818 LVITEMW item;
820 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
821 ZeroMemory(&nmlv, sizeof(nmlv));
822 nmlv.iItem = lvht->iItem;
823 nmlv.iSubItem = lvht->iSubItem;
824 nmlv.ptAction = lvht->pt;
825 item.mask = LVIF_PARAM;
826 item.iItem = lvht->iItem;
827 item.iSubItem = 0;
828 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
829 return notify_listview(infoPtr, code, &nmlv);
832 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
834 NMLISTVIEW nmlv;
835 LVITEMW item;
837 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
838 nmlv.iItem = nItem;
839 item.mask = LVIF_PARAM;
840 item.iItem = nItem;
841 item.iSubItem = 0;
842 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
843 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
846 static int get_ansi_notification(INT unicodeNotificationCode)
848 switch (unicodeNotificationCode)
850 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
851 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
852 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
853 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
854 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
855 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
857 ERR("unknown notification %x\n", unicodeNotificationCode);
858 assert(FALSE);
859 return 0;
863 Send notification. depends on dispinfoW having same
864 structure as dispinfoA.
865 infoPtr : listview struct
866 notificationCode : *Unicode* notification code
867 pdi : dispinfo structure (can be unicode or ansi)
868 isW : TRUE if dispinfo is Unicode
870 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
872 BOOL bResult = FALSE;
873 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
874 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
875 LPWSTR pszTempBuf = NULL, savPszText = NULL;
877 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
879 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
880 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
883 if (convertToAnsi || convertToUnicode)
885 if (notificationCode != LVN_GETDISPINFOW)
887 cchTempBufMax = convertToUnicode ?
888 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
889 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
891 else
893 cchTempBufMax = pdi->item.cchTextMax;
894 *pdi->item.pszText = 0; /* make sure we don't process garbage */
897 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
898 if (!pszTempBuf) return FALSE;
900 if (convertToUnicode)
901 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
902 pszTempBuf, cchTempBufMax);
903 else
904 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
905 cchTempBufMax, NULL, NULL);
907 savCchTextMax = pdi->item.cchTextMax;
908 savPszText = pdi->item.pszText;
909 pdi->item.pszText = pszTempBuf;
910 pdi->item.cchTextMax = cchTempBufMax;
913 if (infoPtr->notifyFormat == NFR_ANSI)
914 realNotifCode = get_ansi_notification(notificationCode);
915 else
916 realNotifCode = notificationCode;
917 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
918 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
920 if (convertToUnicode || convertToAnsi)
922 if (convertToUnicode) /* note : pointer can be changed by app ! */
923 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
924 savCchTextMax, NULL, NULL);
925 else
926 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
927 savPszText, savCchTextMax);
928 pdi->item.pszText = savPszText; /* restores our buffer */
929 pdi->item.cchTextMax = savCchTextMax;
930 Free (pszTempBuf);
932 return bResult;
935 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
936 const RECT *rcBounds, const LVITEMW *lplvItem)
938 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
939 lpnmlvcd->nmcd.hdc = hdc;
940 lpnmlvcd->nmcd.rc = *rcBounds;
941 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
942 lpnmlvcd->clrText = infoPtr->clrText;
943 if (!lplvItem) return;
944 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
945 lpnmlvcd->iSubItem = lplvItem->iSubItem;
946 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
947 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
948 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
949 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
952 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
954 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
955 DWORD result;
957 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
958 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
959 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
960 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
961 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
962 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
963 return result;
966 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
968 /* apprently, for selected items, we have to override the returned values */
969 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
971 if (infoPtr->bFocus)
973 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
974 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
976 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
978 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
979 lpnmlvcd->clrText = comctl32_color.clrBtnText;
983 /* Set the text attributes */
984 if (lpnmlvcd->clrTextBk != CLR_NONE)
986 SetBkMode(hdc, OPAQUE);
987 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
988 SetBkColor(hdc, infoPtr->clrTextBkDefault);
989 else
990 SetBkColor(hdc,lpnmlvcd->clrTextBk);
992 else
993 SetBkMode(hdc, TRANSPARENT);
994 SetTextColor(hdc, lpnmlvcd->clrText);
997 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
999 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1002 /******** Item iterator functions **********************************/
1004 static RANGES ranges_create(int count);
1005 static void ranges_destroy(RANGES ranges);
1006 static BOOL ranges_add(RANGES ranges, RANGE range);
1007 static BOOL ranges_del(RANGES ranges, RANGE range);
1008 static void ranges_dump(RANGES ranges);
1010 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1012 RANGE range = { nItem, nItem + 1 };
1014 return ranges_add(ranges, range);
1017 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1019 RANGE range = { nItem, nItem + 1 };
1021 return ranges_del(ranges, range);
1024 /***
1025 * ITERATOR DOCUMENTATION
1027 * The iterator functions allow for easy, and convenient iteration
1028 * over items of iterest in the list. Typically, you create a
1029 * iterator, use it, and destroy it, as such:
1030 * ITERATOR i;
1032 * iterator_xxxitems(&i, ...);
1033 * while (iterator_{prev,next}(&i)
1035 * //code which uses i.nItem
1037 * iterator_destroy(&i);
1039 * where xxx is either: framed, or visible.
1040 * Note that it is important that the code destroys the iterator
1041 * after it's done with it, as the creation of the iterator may
1042 * allocate memory, which thus needs to be freed.
1044 * You can iterate both forwards, and backwards through the list,
1045 * by using iterator_next or iterator_prev respectively.
1047 * Lower numbered items are draw on top of higher number items in
1048 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1049 * items may overlap). So, to test items, you should use
1050 * iterator_next
1051 * which lists the items top to bottom (in Z-order).
1052 * For drawing items, you should use
1053 * iterator_prev
1054 * which lists the items bottom to top (in Z-order).
1055 * If you keep iterating over the items after the end-of-items
1056 * marker (-1) is returned, the iterator will start from the
1057 * beginning. Typically, you don't need to test for -1,
1058 * because iterator_{next,prev} will return TRUE if more items
1059 * are to be iterated over, or FALSE otherwise.
1061 * Note: the iterator is defined to be bidirectional. That is,
1062 * any number of prev followed by any number of next, or
1063 * five versa, should leave the iterator at the same item:
1064 * prev * n, next * n = next * n, prev * n
1066 * The iterator has a notion of an out-of-order, special item,
1067 * which sits at the start of the list. This is used in
1068 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1069 * which needs to be first, as it may overlap other items.
1071 * The code is a bit messy because we have:
1072 * - a special item to deal with
1073 * - simple range, or composite range
1074 * - empty range.
1075 * If you find bugs, or want to add features, please make sure you
1076 * always check/modify *both* iterator_prev, and iterator_next.
1079 /****
1080 * This function iterates through the items in increasing order,
1081 * but prefixed by the special item, then -1. That is:
1082 * special, 1, 2, 3, ..., n, -1.
1083 * Each item is listed only once.
1085 static inline BOOL iterator_next(ITERATOR* i)
1087 if (i->nItem == -1)
1089 i->nItem = i->nSpecial;
1090 if (i->nItem != -1) return TRUE;
1092 if (i->nItem == i->nSpecial)
1094 if (i->ranges) i->index = 0;
1095 goto pickarange;
1098 i->nItem++;
1099 testitem:
1100 if (i->nItem == i->nSpecial) i->nItem++;
1101 if (i->nItem < i->range.upper) return TRUE;
1103 pickarange:
1104 if (i->ranges)
1106 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1107 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1108 else goto end;
1110 else if (i->nItem >= i->range.upper) goto end;
1112 i->nItem = i->range.lower;
1113 if (i->nItem >= 0) goto testitem;
1114 end:
1115 i->nItem = -1;
1116 return FALSE;
1119 /****
1120 * This function iterates through the items in decreasing order,
1121 * followed by the special item, then -1. That is:
1122 * n, n-1, ..., 3, 2, 1, special, -1.
1123 * Each item is listed only once.
1125 static inline BOOL iterator_prev(ITERATOR* i)
1127 BOOL start = FALSE;
1129 if (i->nItem == -1)
1131 start = TRUE;
1132 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1133 goto pickarange;
1135 if (i->nItem == i->nSpecial)
1137 i->nItem = -1;
1138 return FALSE;
1141 testitem:
1142 i->nItem--;
1143 if (i->nItem == i->nSpecial) i->nItem--;
1144 if (i->nItem >= i->range.lower) return TRUE;
1146 pickarange:
1147 if (i->ranges)
1149 if (i->index > 0)
1150 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1151 else goto end;
1153 else if (!start && i->nItem < i->range.lower) goto end;
1155 i->nItem = i->range.upper;
1156 if (i->nItem > 0) goto testitem;
1157 end:
1158 return (i->nItem = i->nSpecial) != -1;
1161 static RANGE iterator_range(ITERATOR* i)
1163 RANGE range;
1165 if (!i->ranges) return i->range;
1167 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1169 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1170 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1172 else range.lower = range.upper = 0;
1174 return range;
1177 /***
1178 * Releases resources associated with this ierator.
1180 static inline void iterator_destroy(ITERATOR* i)
1182 ranges_destroy(i->ranges);
1185 /***
1186 * Create an empty iterator.
1188 static inline BOOL iterator_empty(ITERATOR* i)
1190 ZeroMemory(i, sizeof(*i));
1191 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1192 return TRUE;
1195 /***
1196 * Create an iterator over a range.
1198 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1200 iterator_empty(i);
1201 i->range = range;
1202 return TRUE;
1205 /***
1206 * Create an iterator over a bunch of ranges.
1207 * Please note that the iterator will take ownership of the ranges,
1208 * and will free them upon destruction.
1210 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1212 iterator_empty(i);
1213 i->ranges = ranges;
1214 return TRUE;
1217 /***
1218 * Creates an iterator over the items which intersect lprc.
1220 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1222 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1223 RECT frame = *lprc, rcItem, rcTemp;
1224 POINT Origin;
1226 /* in case we fail, we want to return an empty iterator */
1227 if (!iterator_empty(i)) return FALSE;
1229 LISTVIEW_GetOrigin(infoPtr, &Origin);
1231 TRACE("(lprc=%s)\n", debugrect(lprc));
1232 OffsetRect(&frame, -Origin.x, -Origin.y);
1234 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1236 INT nItem;
1238 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1240 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1241 if (IntersectRect(&rcTemp, &rcItem, lprc))
1242 i->nSpecial = infoPtr->nFocusedItem;
1244 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1245 /* to do better here, we need to have PosX, and PosY sorted */
1246 TRACE("building icon ranges:\n");
1247 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1249 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1250 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1251 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1252 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1253 if (IntersectRect(&rcTemp, &rcItem, &frame))
1254 ranges_additem(i->ranges, nItem);
1256 return TRUE;
1258 else if (uView == LVS_REPORT)
1260 RANGE range;
1262 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1263 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1265 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1266 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1267 if (range.upper <= range.lower) return TRUE;
1268 if (!iterator_rangeitems(i, range)) return FALSE;
1269 TRACE(" report=%s\n", debugrange(&i->range));
1271 else
1273 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1274 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1275 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1276 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1277 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1278 INT lower = nFirstCol * nPerCol + nFirstRow;
1279 RANGE item_range;
1280 INT nCol;
1282 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1283 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1285 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1287 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1288 TRACE("building list ranges:\n");
1289 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1291 item_range.lower = nCol * nPerCol + nFirstRow;
1292 if(item_range.lower >= infoPtr->nItemCount) break;
1293 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1294 TRACE(" list=%s\n", debugrange(&item_range));
1295 ranges_add(i->ranges, item_range);
1299 return TRUE;
1302 /***
1303 * Creates an iterator over the items which intersect the visible region of hdc.
1305 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1307 POINT Origin, Position;
1308 RECT rcItem, rcClip;
1309 INT rgntype;
1311 rgntype = GetClipBox(hdc, &rcClip);
1312 if (rgntype == NULLREGION) return iterator_empty(i);
1313 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1314 if (rgntype == SIMPLEREGION) return TRUE;
1316 /* first deal with the special item */
1317 if (i->nSpecial != -1)
1319 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1320 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1323 /* if we can't deal with the region, we'll just go with the simple range */
1324 LISTVIEW_GetOrigin(infoPtr, &Origin);
1325 TRACE("building visible range:\n");
1326 if (!i->ranges && i->range.lower < i->range.upper)
1328 if (!(i->ranges = ranges_create(50))) return TRUE;
1329 if (!ranges_add(i->ranges, i->range))
1331 ranges_destroy(i->ranges);
1332 i->ranges = 0;
1333 return TRUE;
1337 /* now delete the invisible items from the list */
1338 while(iterator_next(i))
1340 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1341 rcItem.left = Position.x + Origin.x;
1342 rcItem.top = Position.y + Origin.y;
1343 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1344 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1345 if (!RectVisible(hdc, &rcItem))
1346 ranges_delitem(i->ranges, i->nItem);
1348 /* the iterator should restart on the next iterator_next */
1349 TRACE("done\n");
1351 return TRUE;
1354 /******** Misc helper functions ************************************/
1356 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1357 WPARAM wParam, LPARAM lParam, BOOL isW)
1359 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1360 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1363 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1365 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1367 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1368 (uView == LVS_ICON || uView == LVS_SMALLICON);
1371 /******** Internal API functions ************************************/
1373 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1375 static COLUMN_INFO mainItem;
1377 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1378 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1379 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1382 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1384 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1387 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1389 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1392 /* Listview invalidation functions: use _only_ these functions to invalidate */
1394 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1396 return infoPtr->bRedraw;
1399 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1401 if(!is_redrawing(infoPtr)) return;
1402 TRACE(" invalidating rect=%s\n", debugrect(rect));
1403 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1406 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1408 RECT rcBox;
1410 if(!is_redrawing(infoPtr)) return;
1411 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1412 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1415 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1417 POINT Origin, Position;
1418 RECT rcBox;
1420 if(!is_redrawing(infoPtr)) return;
1421 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1422 LISTVIEW_GetOrigin(infoPtr, &Origin);
1423 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1424 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1425 rcBox.top = 0;
1426 rcBox.bottom = infoPtr->nItemHeight;
1427 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1428 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1431 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1433 LISTVIEW_InvalidateRect(infoPtr, NULL);
1436 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1438 RECT rcCol;
1440 if(!is_redrawing(infoPtr)) return;
1441 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1442 rcCol.top = infoPtr->rcList.top;
1443 rcCol.bottom = infoPtr->rcList.bottom;
1444 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1447 /***
1448 * DESCRIPTION:
1449 * Retrieves the number of items that can fit vertically in the client area.
1451 * PARAMETER(S):
1452 * [I] infoPtr : valid pointer to the listview structure
1454 * RETURN:
1455 * Number of items per row.
1457 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1459 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1461 return max(nListWidth/infoPtr->nItemWidth, 1);
1464 /***
1465 * DESCRIPTION:
1466 * Retrieves the number of items that can fit horizontally in the client
1467 * area.
1469 * PARAMETER(S):
1470 * [I] infoPtr : valid pointer to the listview structure
1472 * RETURN:
1473 * Number of items per column.
1475 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1477 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1479 return max(nListHeight / infoPtr->nItemHeight, 1);
1483 /*************************************************************************
1484 * LISTVIEW_ProcessLetterKeys
1486 * Processes keyboard messages generated by pressing the letter keys
1487 * on the keyboard.
1488 * What this does is perform a case insensitive search from the
1489 * current position with the following quirks:
1490 * - If two chars or more are pressed in quick succession we search
1491 * for the corresponding string (e.g. 'abc').
1492 * - If there is a delay we wipe away the current search string and
1493 * restart with just that char.
1494 * - If the user keeps pressing the same character, whether slowly or
1495 * fast, so that the search string is entirely composed of this
1496 * character ('aaaaa' for instance), then we search for first item
1497 * that starting with that character.
1498 * - If the user types the above character in quick succession, then
1499 * we must also search for the corresponding string ('aaaaa'), and
1500 * go to that string if there is a match.
1502 * PARAMETERS
1503 * [I] hwnd : handle to the window
1504 * [I] charCode : the character code, the actual character
1505 * [I] keyData : key data
1507 * RETURNS
1509 * Zero.
1511 * BUGS
1513 * - The current implementation has a list of characters it will
1514 * accept and it ignores averything else. In particular it will
1515 * ignore accentuated characters which seems to match what
1516 * Windows does. But I'm not sure it makes sense to follow
1517 * Windows there.
1518 * - We don't sound a beep when the search fails.
1520 * SEE ALSO
1522 * TREEVIEW_ProcessLetterKeys
1524 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1526 INT nItem;
1527 INT endidx,idx;
1528 LVITEMW item;
1529 WCHAR buffer[MAX_PATH];
1530 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1532 /* simple parameter checking */
1533 if (!charCode || !keyData) return 0;
1535 /* only allow the valid WM_CHARs through */
1536 if (!isalnum(charCode) &&
1537 charCode != '.' && charCode != '`' && charCode != '!' &&
1538 charCode != '@' && charCode != '#' && charCode != '$' &&
1539 charCode != '%' && charCode != '^' && charCode != '&' &&
1540 charCode != '*' && charCode != '(' && charCode != ')' &&
1541 charCode != '-' && charCode != '_' && charCode != '+' &&
1542 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1543 charCode != '}' && charCode != '[' && charCode != '{' &&
1544 charCode != '/' && charCode != '?' && charCode != '>' &&
1545 charCode != '<' && charCode != ',' && charCode != '~')
1546 return 0;
1548 /* if there's one item or less, there is no where to go */
1549 if (infoPtr->nItemCount <= 1) return 0;
1551 /* update the search parameters */
1552 infoPtr->lastKeyPressTimestamp = GetTickCount();
1553 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1554 if (infoPtr->nSearchParamLength < MAX_PATH)
1555 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1556 if (infoPtr->charCode != charCode)
1557 infoPtr->charCode = charCode = 0;
1558 } else {
1559 infoPtr->charCode=charCode;
1560 infoPtr->szSearchParam[0]=charCode;
1561 infoPtr->nSearchParamLength=1;
1562 /* Redundant with the 1 char string */
1563 charCode=0;
1566 /* and search from the current position */
1567 nItem=-1;
1568 if (infoPtr->nFocusedItem >= 0) {
1569 endidx=infoPtr->nFocusedItem;
1570 idx=endidx;
1571 /* if looking for single character match,
1572 * then we must always move forward
1574 if (infoPtr->nSearchParamLength == 1)
1575 idx++;
1576 } else {
1577 endidx=infoPtr->nItemCount;
1578 idx=0;
1580 do {
1581 if (idx == infoPtr->nItemCount) {
1582 if (endidx == infoPtr->nItemCount || endidx == 0)
1583 break;
1584 idx=0;
1587 /* get item */
1588 item.mask = LVIF_TEXT;
1589 item.iItem = idx;
1590 item.iSubItem = 0;
1591 item.pszText = buffer;
1592 item.cchTextMax = MAX_PATH;
1593 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1595 /* check for a match */
1596 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1597 nItem=idx;
1598 break;
1599 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1600 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1601 /* This would work but we must keep looking for a longer match */
1602 nItem=idx;
1604 idx++;
1605 } while (idx != endidx);
1607 if (nItem != -1)
1608 LISTVIEW_KeySelection(infoPtr, nItem);
1610 return 0;
1613 /*************************************************************************
1614 * LISTVIEW_UpdateHeaderSize [Internal]
1616 * Function to resize the header control
1618 * PARAMS
1619 * [I] hwnd : handle to a window
1620 * [I] nNewScrollPos : scroll pos to set
1622 * RETURNS
1623 * None.
1625 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1627 RECT winRect;
1628 POINT point[2];
1630 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1632 GetWindowRect(infoPtr->hwndHeader, &winRect);
1633 point[0].x = winRect.left;
1634 point[0].y = winRect.top;
1635 point[1].x = winRect.right;
1636 point[1].y = winRect.bottom;
1638 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1639 point[0].x = -nNewScrollPos;
1640 point[1].x += nNewScrollPos;
1642 SetWindowPos(infoPtr->hwndHeader,0,
1643 point[0].x,point[0].y,point[1].x,point[1].y,
1644 SWP_NOZORDER | SWP_NOACTIVATE);
1647 /***
1648 * DESCRIPTION:
1649 * Update the scrollbars. This functions should be called whenever
1650 * the content, size or view changes.
1652 * PARAMETER(S):
1653 * [I] infoPtr : valid pointer to the listview structure
1655 * RETURN:
1656 * None
1658 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1660 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1661 SCROLLINFO horzInfo, vertInfo;
1663 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1665 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1666 horzInfo.cbSize = sizeof(SCROLLINFO);
1667 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1669 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1670 if (uView == LVS_LIST)
1672 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1673 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1675 /* scroll by at least one column per page */
1676 if(horzInfo.nPage < infoPtr->nItemWidth)
1677 horzInfo.nPage = infoPtr->nItemWidth;
1679 horzInfo.nPage /= infoPtr->nItemWidth;
1681 else if (uView == LVS_REPORT)
1683 horzInfo.nMax = infoPtr->nItemWidth;
1685 else /* LVS_ICON, or LVS_SMALLICON */
1687 RECT rcView;
1689 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1692 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1693 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1694 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1695 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1697 /* Setting the horizontal scroll can change the listview size
1698 * (and potentially everything else) so we need to recompute
1699 * everything again for the vertical scroll
1702 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1703 vertInfo.cbSize = sizeof(SCROLLINFO);
1704 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1706 if (uView == LVS_REPORT)
1708 vertInfo.nMax = infoPtr->nItemCount;
1710 /* scroll by at least one page */
1711 if(vertInfo.nPage < infoPtr->nItemHeight)
1712 vertInfo.nPage = infoPtr->nItemHeight;
1714 vertInfo.nPage /= infoPtr->nItemHeight;
1716 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1718 RECT rcView;
1720 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1723 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1724 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1725 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1726 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1728 /* Update the Header Control */
1729 if (uView == LVS_REPORT)
1731 horzInfo.fMask = SIF_POS;
1732 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1733 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1738 /***
1739 * DESCRIPTION:
1740 * Shows/hides the focus rectangle.
1742 * PARAMETER(S):
1743 * [I] infoPtr : valid pointer to the listview structure
1744 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1746 * RETURN:
1747 * None
1749 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1751 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1752 HDC hdc;
1754 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1756 if (infoPtr->nFocusedItem < 0) return;
1758 /* we need some gymnastics in ICON mode to handle large items */
1759 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1761 RECT rcBox;
1763 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1764 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1766 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1767 return;
1771 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1773 /* for some reason, owner draw should work only in report mode */
1774 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1776 DRAWITEMSTRUCT dis;
1777 LVITEMW item;
1779 item.iItem = infoPtr->nFocusedItem;
1780 item.iSubItem = 0;
1781 item.mask = LVIF_PARAM;
1782 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1784 ZeroMemory(&dis, sizeof(dis));
1785 dis.CtlType = ODT_LISTVIEW;
1786 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1787 dis.itemID = item.iItem;
1788 dis.itemAction = ODA_FOCUS;
1789 if (fShow) dis.itemState |= ODS_FOCUS;
1790 dis.hwndItem = infoPtr->hwndSelf;
1791 dis.hDC = hdc;
1792 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1793 dis.itemData = item.lParam;
1795 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1797 else
1799 DrawFocusRect(hdc, &infoPtr->rcFocus);
1801 done:
1802 ReleaseDC(infoPtr->hwndSelf, hdc);
1805 /***
1806 * Invalidates all visible selected items.
1808 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1810 ITERATOR i;
1812 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1813 while(iterator_next(&i))
1815 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1816 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1818 iterator_destroy(&i);
1822 /***
1823 * DESCRIPTION: [INTERNAL]
1824 * Computes an item's (left,top) corner, relative to rcView.
1825 * That is, the position has NOT been made relative to the Origin.
1826 * This is deliberate, to avoid computing the Origin over, and
1827 * over again, when this function is call in a loop. Instead,
1828 * one ca factor the computation of the Origin before the loop,
1829 * and offset the value retured by this function, on every iteration.
1831 * PARAMETER(S):
1832 * [I] infoPtr : valid pointer to the listview structure
1833 * [I] nItem : item number
1834 * [O] lpptOrig : item top, left corner
1836 * RETURN:
1837 * None.
1839 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1841 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1843 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1845 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1847 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1848 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1850 else if (uView == LVS_LIST)
1852 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1853 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1854 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1856 else /* LVS_REPORT */
1858 lpptPosition->x = 0;
1859 lpptPosition->y = nItem * infoPtr->nItemHeight;
1863 /***
1864 * DESCRIPTION: [INTERNAL]
1865 * Compute the rectangles of an item. This is to localize all
1866 * the computations in one place. If you are not interested in some
1867 * of these values, simply pass in a NULL -- the fucntion is smart
1868 * enough to compute only what's necessary. The function computes
1869 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1870 * one, the BOX rectangle. This rectangle is very cheap to compute,
1871 * and is guaranteed to contain all the other rectangles. Computing
1872 * the ICON rect is also cheap, but all the others are potentaily
1873 * expensive. This gives an easy and effective optimization when
1874 * searching (like point inclusion, or rectangle intersection):
1875 * first test against the BOX, and if TRUE, test agains the desired
1876 * rectangle.
1877 * If the function does not have all the necessary information
1878 * to computed the requested rectangles, will crash with a
1879 * failed assertion. This is done so we catch all programming
1880 * errors, given that the function is called only from our code.
1882 * We have the following 'special' meanings for a few fields:
1883 * * If LVIS_FOCUSED is set, we assume the item has the focus
1884 * This is important in ICON mode, where it might get a larger
1885 * then usual rectange
1887 * Please note that subitem support works only in REPORT mode.
1889 * PARAMETER(S):
1890 * [I] infoPtr : valid pointer to the listview structure
1891 * [I] lpLVItem : item to compute the measures for
1892 * [O] lprcBox : ptr to Box rectangle
1893 * The internal LVIR_BOX rectangle
1894 * [0] lprcState : ptr to State icon rectangle
1895 * The internal LVIR_STATE rectangle
1896 * [O] lprcIcon : ptr to Icon rectangle
1897 * Same as LVM_GETITEMRECT with LVIR_ICON
1898 * [O] lprcLabel : ptr to Label rectangle
1899 * Same as LVM_GETITEMRECT with LVIR_LABEL
1901 * RETURN:
1902 * None.
1904 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1905 LPRECT lprcBox, LPRECT lprcState,
1906 LPRECT lprcIcon, LPRECT lprcLabel)
1908 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1909 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1910 RECT Box, State, Icon, Label;
1911 COLUMN_INFO *lpColumnInfo = NULL;
1913 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1915 /* Be smart and try to figure out the minimum we have to do */
1916 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1917 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1919 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1920 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1922 if (lprcLabel) doLabel = TRUE;
1923 if (doLabel || lprcIcon) doIcon = TRUE;
1924 if (doIcon || lprcState) doState = TRUE;
1926 /************************************************************/
1927 /* compute the box rectangle (it should be cheap to do) */
1928 /************************************************************/
1929 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1930 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1932 if (lpLVItem->iSubItem)
1934 Box = lpColumnInfo->rcHeader;
1936 else
1938 Box.left = 0;
1939 Box.right = infoPtr->nItemWidth;
1941 Box.top = 0;
1942 Box.bottom = infoPtr->nItemHeight;
1944 /************************************************************/
1945 /* compute STATEICON bounding box */
1946 /************************************************************/
1947 if (doState)
1949 if (uView == LVS_ICON)
1951 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1952 if (infoPtr->himlNormal)
1953 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1954 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1956 else
1958 /* we need the ident in report mode, if we don't have it, we fail */
1959 State.left = Box.left;
1960 if (uView == LVS_REPORT)
1962 if (lpLVItem->iSubItem == 0)
1964 State.left += REPORT_MARGINX;
1965 assert(lpLVItem->mask & LVIF_INDENT);
1966 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1969 State.top = Box.top;
1971 State.right = State.left;
1972 State.bottom = State.top;
1973 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1975 State.right += infoPtr->iconStateSize.cx;
1976 State.bottom += infoPtr->iconStateSize.cy;
1978 if (lprcState) *lprcState = State;
1979 TRACE(" - state=%s\n", debugrect(&State));
1981 else State.right = 0;
1983 /************************************************************/
1984 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1985 /************************************************************/
1986 if (doIcon)
1988 if (uView == LVS_ICON)
1990 Icon.left = Box.left;
1991 if (infoPtr->himlNormal)
1992 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1993 Icon.top = Box.top + ICON_TOP_PADDING;
1994 Icon.right = Icon.left;
1995 Icon.bottom = Icon.top;
1996 if (infoPtr->himlNormal)
1998 Icon.right += infoPtr->iconSize.cx;
1999 Icon.bottom += infoPtr->iconSize.cy;
2002 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2004 Icon.left = State.right;
2005 Icon.top = Box.top;
2006 Icon.right = Icon.left;
2007 if (infoPtr->himlSmall &&
2008 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2009 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2010 Icon.right += infoPtr->iconSize.cx;
2011 Icon.bottom = Icon.top + infoPtr->nItemHeight;
2013 if(lprcIcon) *lprcIcon = Icon;
2014 TRACE(" - icon=%s\n", debugrect(&Icon));
2016 else Icon.right = 0;
2018 /************************************************************/
2019 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2020 /************************************************************/
2021 if (doLabel)
2023 SIZE labelSize = { 0, 0 };
2025 /* calculate how far to the right can the label strech */
2026 Label.right = Box.right;
2027 if (uView == LVS_REPORT)
2029 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2032 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2034 labelSize.cx = infoPtr->nItemWidth;
2035 labelSize.cy = infoPtr->nItemHeight;
2036 goto calc_label;
2039 /* we need the text in non owner draw mode */
2040 assert(lpLVItem->mask & LVIF_TEXT);
2041 if (is_textT(lpLVItem->pszText, TRUE))
2043 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2044 HDC hdc = GetDC(infoPtr->hwndSelf);
2045 HFONT hOldFont = SelectObject(hdc, hFont);
2046 UINT uFormat;
2047 RECT rcText;
2049 /* compute rough rectangle where the label will go */
2050 SetRectEmpty(&rcText);
2051 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2052 rcText.bottom = infoPtr->nItemHeight;
2053 if (uView == LVS_ICON)
2054 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2056 /* now figure out the flags */
2057 if (uView == LVS_ICON)
2058 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2059 else
2060 uFormat = LV_SL_DT_FLAGS;
2062 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2064 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2065 labelSize.cy = rcText.bottom - rcText.top;
2067 SelectObject(hdc, hOldFont);
2068 ReleaseDC(infoPtr->hwndSelf, hdc);
2071 calc_label:
2072 if (uView == LVS_ICON)
2074 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2075 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2076 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2077 Label.right = Label.left + labelSize.cx;
2078 Label.bottom = Label.top + infoPtr->nItemHeight;
2079 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2081 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2082 labelSize.cy /= infoPtr->ntmHeight;
2083 labelSize.cy = max(labelSize.cy, 1);
2084 labelSize.cy *= infoPtr->ntmHeight;
2086 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2088 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2090 Label.left = Icon.right;
2091 Label.top = Box.top;
2092 Label.right = min(Label.left + labelSize.cx, Label.right);
2093 Label.bottom = Label.top + infoPtr->nItemHeight;
2096 if (lprcLabel) *lprcLabel = Label;
2097 TRACE(" - label=%s\n", debugrect(&Label));
2100 /* Fix the Box if necessary */
2101 if (lprcBox)
2103 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2104 else *lprcBox = Box;
2106 TRACE(" - box=%s\n", debugrect(&Box));
2109 /***
2110 * DESCRIPTION: [INTERNAL]
2112 * PARAMETER(S):
2113 * [I] infoPtr : valid pointer to the listview structure
2114 * [I] nItem : item number
2115 * [O] lprcBox : ptr to Box rectangle
2117 * RETURN:
2118 * None.
2120 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2122 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2123 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2124 POINT Position, Origin;
2125 LVITEMW lvItem;
2127 LISTVIEW_GetOrigin(infoPtr, &Origin);
2128 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2130 /* Be smart and try to figure out the minimum we have to do */
2131 lvItem.mask = 0;
2132 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2133 lvItem.mask |= LVIF_TEXT;
2134 lvItem.iItem = nItem;
2135 lvItem.iSubItem = 0;
2136 lvItem.pszText = szDispText;
2137 lvItem.cchTextMax = DISP_TEXT_SIZE;
2138 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2139 if (uView == LVS_ICON)
2141 lvItem.mask |= LVIF_STATE;
2142 lvItem.stateMask = LVIS_FOCUSED;
2143 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2145 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2147 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2151 /***
2152 * DESCRIPTION:
2153 * Returns the current icon position, and advances it along the top.
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_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2165 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2167 *lpPos = infoPtr->currIconPos;
2169 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2170 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2172 infoPtr->currIconPos.x = 0;
2173 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2177 /***
2178 * DESCRIPTION:
2179 * Returns the current icon position, and advances it down the left edge.
2180 * The returned position is not offset by Origin.
2182 * PARAMETER(S):
2183 * [I] infoPtr : valid pointer to the listview structure
2184 * [O] lpPos : will get the current icon position
2186 * RETURN:
2187 * None
2189 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2191 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2193 *lpPos = infoPtr->currIconPos;
2195 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2196 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2198 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2199 infoPtr->currIconPos.y = 0;
2203 /***
2204 * DESCRIPTION:
2205 * Moves an icon to the specified position.
2206 * It takes care of invalidating the item, etc.
2208 * PARAMETER(S):
2209 * [I] infoPtr : valid pointer to the listview structure
2210 * [I] nItem : the item to move
2211 * [I] lpPos : the new icon position
2212 * [I] isNew : flags the item as being new
2214 * RETURN:
2215 * Success: TRUE
2216 * Failure: FALSE
2218 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2220 POINT old;
2222 if (!isNew)
2224 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2225 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2227 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2228 LISTVIEW_InvalidateItem(infoPtr, nItem);
2231 /* Allocating a POINTER for every item is too resource intensive,
2232 * so we'll keep the (x,y) in different arrays */
2233 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2234 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2236 LISTVIEW_InvalidateItem(infoPtr, nItem);
2238 return TRUE;
2241 /***
2242 * DESCRIPTION:
2243 * Arranges listview items in icon display mode.
2245 * PARAMETER(S):
2246 * [I] infoPtr : valid pointer to the listview structure
2247 * [I] nAlignCode : alignment code
2249 * RETURN:
2250 * SUCCESS : TRUE
2251 * FAILURE : FALSE
2253 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2255 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2256 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2257 POINT pos;
2258 INT i;
2260 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2262 TRACE("nAlignCode=%d\n", nAlignCode);
2264 if (nAlignCode == LVA_DEFAULT)
2266 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2267 else nAlignCode = LVA_ALIGNTOP;
2270 switch (nAlignCode)
2272 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2273 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2274 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2275 default: return FALSE;
2278 infoPtr->bAutoarrange = TRUE;
2279 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2280 for (i = 0; i < infoPtr->nItemCount; i++)
2282 next_pos(infoPtr, &pos);
2283 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2286 return TRUE;
2289 /***
2290 * DESCRIPTION:
2291 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2293 * PARAMETER(S):
2294 * [I] infoPtr : valid pointer to the listview structure
2295 * [O] lprcView : bounding rectangle
2297 * RETURN:
2298 * SUCCESS : TRUE
2299 * FAILURE : FALSE
2301 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2303 INT i, x, y;
2305 SetRectEmpty(lprcView);
2307 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2309 case LVS_ICON:
2310 case LVS_SMALLICON:
2311 for (i = 0; i < infoPtr->nItemCount; i++)
2313 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2314 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2315 lprcView->right = max(lprcView->right, x);
2316 lprcView->bottom = max(lprcView->bottom, y);
2318 if (infoPtr->nItemCount > 0)
2320 lprcView->right += infoPtr->nItemWidth;
2321 lprcView->bottom += infoPtr->nItemHeight;
2323 break;
2325 case LVS_LIST:
2326 y = LISTVIEW_GetCountPerColumn(infoPtr);
2327 x = infoPtr->nItemCount / y;
2328 if (infoPtr->nItemCount % y) x++;
2329 lprcView->right = x * infoPtr->nItemWidth;
2330 lprcView->bottom = y * infoPtr->nItemHeight;
2331 break;
2333 case LVS_REPORT:
2334 lprcView->right = infoPtr->nItemWidth;
2335 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2336 break;
2340 /***
2341 * DESCRIPTION:
2342 * Retrieves the bounding rectangle of all the items.
2344 * PARAMETER(S):
2345 * [I] infoPtr : valid pointer to the listview structure
2346 * [O] lprcView : bounding rectangle
2348 * RETURN:
2349 * SUCCESS : TRUE
2350 * FAILURE : FALSE
2352 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2354 POINT ptOrigin;
2356 TRACE("(lprcView=%p)\n", lprcView);
2358 if (!lprcView) return FALSE;
2360 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2361 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2362 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2364 TRACE("lprcView=%s\n", debugrect(lprcView));
2366 return TRUE;
2369 /***
2370 * DESCRIPTION:
2371 * Retrieves the subitem pointer associated with the subitem index.
2373 * PARAMETER(S):
2374 * [I] hdpaSubItems : DPA handle for a specific item
2375 * [I] nSubItem : index of subitem
2377 * RETURN:
2378 * SUCCESS : subitem pointer
2379 * FAILURE : NULL
2381 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2383 SUBITEM_INFO *lpSubItem;
2384 INT i;
2386 /* we should binary search here if need be */
2387 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2389 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2390 if (lpSubItem->iSubItem == nSubItem)
2391 return lpSubItem;
2394 return NULL;
2398 /***
2399 * DESCRIPTION:
2400 * Caclulates the desired item width.
2402 * PARAMETER(S):
2403 * [I] infoPtr : valid pointer to the listview structure
2405 * RETURN:
2406 * The desired item width.
2408 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2410 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2411 INT nItemWidth = 0;
2413 TRACE("uView=%d\n", uView);
2415 if (uView == LVS_ICON)
2416 nItemWidth = infoPtr->iconSpacing.cx;
2417 else if (uView == LVS_REPORT)
2419 RECT rcHeader;
2421 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2423 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2424 nItemWidth = rcHeader.right;
2427 else /* LVS_SMALLICON, or LVS_LIST */
2429 INT i;
2431 for (i = 0; i < infoPtr->nItemCount; i++)
2432 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2434 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2435 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2437 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2440 return max(nItemWidth, 1);
2443 /***
2444 * DESCRIPTION:
2445 * Caclulates the desired item height.
2447 * PARAMETER(S):
2448 * [I] infoPtr : valid pointer to the listview structure
2450 * RETURN:
2451 * The desired item height.
2453 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2455 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2456 INT nItemHeight;
2458 TRACE("uView=%d\n", uView);
2460 if (uView == LVS_ICON)
2461 nItemHeight = infoPtr->iconSpacing.cy;
2462 else
2464 nItemHeight = infoPtr->ntmHeight;
2465 if (infoPtr->himlState)
2466 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2467 if (infoPtr->himlSmall)
2468 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2469 if (infoPtr->himlState || infoPtr->himlSmall)
2470 nItemHeight += HEIGHT_PADDING;
2471 if (infoPtr->nMeasureItemHeight > 0)
2472 nItemHeight = infoPtr->nMeasureItemHeight;
2475 return max(nItemHeight, 1);
2478 /***
2479 * DESCRIPTION:
2480 * Updates the width, and height of an item.
2482 * PARAMETER(S):
2483 * [I] infoPtr : valid pointer to the listview structure
2485 * RETURN:
2486 * None.
2488 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2490 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2491 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2495 /***
2496 * DESCRIPTION:
2497 * Retrieves and saves important text metrics info for the current
2498 * Listview font.
2500 * PARAMETER(S):
2501 * [I] infoPtr : valid pointer to the listview structure
2504 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2506 HDC hdc = GetDC(infoPtr->hwndSelf);
2507 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2508 HFONT hOldFont = SelectObject(hdc, hFont);
2509 TEXTMETRICW tm;
2510 SIZE sz;
2512 if (GetTextMetricsW(hdc, &tm))
2514 infoPtr->ntmHeight = tm.tmHeight;
2515 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2518 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2519 infoPtr->nEllipsisWidth = sz.cx;
2521 SelectObject(hdc, hOldFont);
2522 ReleaseDC(infoPtr->hwndSelf, hdc);
2524 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2527 /***
2528 * DESCRIPTION:
2529 * A compare function for ranges
2531 * PARAMETER(S)
2532 * [I] range1 : pointer to range 1;
2533 * [I] range2 : pointer to range 2;
2534 * [I] flags : flags
2536 * RETURNS:
2537 * > 0 : if range 1 > range 2
2538 * < 0 : if range 2 > range 1
2539 * = 0 : if range intersects range 2
2541 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2543 INT cmp;
2545 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2546 cmp = -1;
2547 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2548 cmp = 1;
2549 else
2550 cmp = 0;
2552 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2554 return cmp;
2557 #if DEBUG_RANGES
2558 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2559 #else
2560 #define ranges_check(ranges, desc) do { } while(0)
2561 #endif
2563 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2565 INT i;
2566 RANGE *prev, *curr;
2568 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2569 assert (ranges);
2570 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2571 ranges_dump(ranges);
2572 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2573 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2574 assert (prev->lower >= 0 && prev->lower < prev->upper);
2575 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2577 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2578 assert (prev->upper <= curr->lower);
2579 assert (curr->lower < curr->upper);
2580 prev = curr;
2582 TRACE("--- Done checking---\n");
2585 static RANGES ranges_create(int count)
2587 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2588 if (!ranges) return NULL;
2589 ranges->hdpa = DPA_Create(count);
2590 if (ranges->hdpa) return ranges;
2591 Free(ranges);
2592 return NULL;
2595 static void ranges_clear(RANGES ranges)
2597 INT i;
2599 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2600 Free(DPA_GetPtr(ranges->hdpa, i));
2601 DPA_DeleteAllPtrs(ranges->hdpa);
2605 static void ranges_destroy(RANGES ranges)
2607 if (!ranges) return;
2608 ranges_clear(ranges);
2609 DPA_Destroy(ranges->hdpa);
2610 Free(ranges);
2613 static RANGES ranges_clone(RANGES ranges)
2615 RANGES clone;
2616 INT i;
2618 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2620 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2622 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2623 if (!newrng) goto fail;
2624 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2625 DPA_SetPtr(clone->hdpa, i, newrng);
2627 return clone;
2629 fail:
2630 TRACE ("clone failed\n");
2631 ranges_destroy(clone);
2632 return NULL;
2635 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2637 INT i;
2639 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2640 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2642 return ranges;
2645 static void ranges_dump(RANGES ranges)
2647 INT i;
2649 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2650 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2653 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2655 RANGE srchrng = { nItem, nItem + 1 };
2657 TRACE("(nItem=%d)\n", nItem);
2658 ranges_check(ranges, "before contain");
2659 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2662 static INT ranges_itemcount(RANGES ranges)
2664 INT i, count = 0;
2666 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2668 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2669 count += sel->upper - sel->lower;
2672 return count;
2675 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2677 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2678 INT index;
2680 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2681 if (index == -1) return TRUE;
2683 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2685 chkrng = DPA_GetPtr(ranges->hdpa, index);
2686 if (chkrng->lower >= nItem)
2687 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2688 if (chkrng->upper > nItem)
2689 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2691 return TRUE;
2694 static BOOL ranges_add(RANGES ranges, RANGE range)
2696 RANGE srchrgn;
2697 INT index;
2699 TRACE("(%s)\n", debugrange(&range));
2700 ranges_check(ranges, "before add");
2702 /* try find overlapping regions first */
2703 srchrgn.lower = range.lower - 1;
2704 srchrgn.upper = range.upper + 1;
2705 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2707 if (index == -1)
2709 RANGE *newrgn;
2711 TRACE("Adding new range\n");
2713 /* create the brand new range to insert */
2714 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2715 if(!newrgn) goto fail;
2716 *newrgn = range;
2718 /* figure out where to insert it */
2719 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2720 TRACE("index=%d\n", index);
2721 if (index == -1) index = 0;
2723 /* and get it over with */
2724 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2726 Free(newrgn);
2727 goto fail;
2730 else
2732 RANGE *chkrgn, *mrgrgn;
2733 INT fromindex, mergeindex;
2735 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2736 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2738 chkrgn->lower = min(range.lower, chkrgn->lower);
2739 chkrgn->upper = max(range.upper, chkrgn->upper);
2741 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2743 /* merge now common anges */
2744 fromindex = 0;
2745 srchrgn.lower = chkrgn->lower - 1;
2746 srchrgn.upper = chkrgn->upper + 1;
2750 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2751 if (mergeindex == -1) break;
2752 if (mergeindex == index)
2754 fromindex = index + 1;
2755 continue;
2758 TRACE("Merge with index %i\n", mergeindex);
2760 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2761 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2762 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2763 Free(mrgrgn);
2764 DPA_DeletePtr(ranges->hdpa, mergeindex);
2765 if (mergeindex < index) index --;
2766 } while(1);
2769 ranges_check(ranges, "after add");
2770 return TRUE;
2772 fail:
2773 ranges_check(ranges, "failed add");
2774 return FALSE;
2777 static BOOL ranges_del(RANGES ranges, RANGE range)
2779 RANGE *chkrgn;
2780 INT index;
2782 TRACE("(%s)\n", debugrange(&range));
2783 ranges_check(ranges, "before del");
2785 /* we don't use DPAS_SORTED here, since we need *
2786 * to find the first overlapping range */
2787 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2788 while(index != -1)
2790 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2792 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2794 /* case 1: Same range */
2795 if ( (chkrgn->upper == range.upper) &&
2796 (chkrgn->lower == range.lower) )
2798 DPA_DeletePtr(ranges->hdpa, index);
2799 break;
2801 /* case 2: engulf */
2802 else if ( (chkrgn->upper <= range.upper) &&
2803 (chkrgn->lower >= range.lower) )
2805 DPA_DeletePtr(ranges->hdpa, index);
2807 /* case 3: overlap upper */
2808 else if ( (chkrgn->upper <= range.upper) &&
2809 (chkrgn->lower < range.lower) )
2811 chkrgn->upper = range.lower;
2813 /* case 4: overlap lower */
2814 else if ( (chkrgn->upper > range.upper) &&
2815 (chkrgn->lower >= range.lower) )
2817 chkrgn->lower = range.upper;
2818 break;
2820 /* case 5: fully internal */
2821 else
2823 RANGE tmprgn = *chkrgn, *newrgn;
2825 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2826 newrgn->lower = chkrgn->lower;
2827 newrgn->upper = range.lower;
2828 chkrgn->lower = range.upper;
2829 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2831 Free(newrgn);
2832 goto fail;
2834 chkrgn = &tmprgn;
2835 break;
2838 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2841 ranges_check(ranges, "after del");
2842 return TRUE;
2844 fail:
2845 ranges_check(ranges, "failed del");
2846 return FALSE;
2849 /***
2850 * DESCRIPTION:
2851 * Removes all selection ranges
2853 * Parameters(s):
2854 * [I] infoPtr : valid pointer to the listview structure
2855 * [I] toSkip : item range to skip removing the selection
2857 * RETURNS:
2858 * SUCCESS : TRUE
2859 * FAILURE : TRUE
2861 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2863 LVITEMW lvItem;
2864 ITERATOR i;
2865 RANGES clone;
2867 TRACE("()\n");
2869 lvItem.state = 0;
2870 lvItem.stateMask = LVIS_SELECTED;
2872 /* need to clone the DPA because callbacks can change it */
2873 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2874 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2875 while(iterator_next(&i))
2876 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2877 /* note that the iterator destructor will free the cloned range */
2878 iterator_destroy(&i);
2880 return TRUE;
2883 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2885 RANGES toSkip;
2887 if (!(toSkip = ranges_create(1))) return FALSE;
2888 if (nItem != -1) ranges_additem(toSkip, nItem);
2889 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2890 ranges_destroy(toSkip);
2891 return TRUE;
2894 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2896 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2899 /***
2900 * DESCRIPTION:
2901 * Retrieves the number of items that are marked as selected.
2903 * PARAMETER(S):
2904 * [I] infoPtr : valid pointer to the listview structure
2906 * RETURN:
2907 * Number of items selected.
2909 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2911 INT nSelectedCount = 0;
2913 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2915 INT i;
2916 for (i = 0; i < infoPtr->nItemCount; i++)
2918 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2919 nSelectedCount++;
2922 else
2923 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2925 TRACE("nSelectedCount=%d\n", nSelectedCount);
2926 return nSelectedCount;
2929 /***
2930 * DESCRIPTION:
2931 * Manages the item focus.
2933 * PARAMETER(S):
2934 * [I] infoPtr : valid pointer to the listview structure
2935 * [I] nItem : item index
2937 * RETURN:
2938 * TRUE : focused item changed
2939 * FALSE : focused item has NOT changed
2941 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2943 INT oldFocus = infoPtr->nFocusedItem;
2944 LVITEMW lvItem;
2946 if (nItem == infoPtr->nFocusedItem) return FALSE;
2948 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2949 lvItem.stateMask = LVIS_FOCUSED;
2950 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2952 return oldFocus != infoPtr->nFocusedItem;
2955 /* Helper function for LISTVIEW_ShiftIndices *only* */
2956 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2958 if (nShiftItem < nItem) return nShiftItem;
2960 if (nShiftItem > nItem) return nShiftItem + direction;
2962 if (direction > 0) return nShiftItem + direction;
2964 return min(nShiftItem, infoPtr->nItemCount - 1);
2968 * DESCRIPTION:
2969 * Updates the various indices after an item has been inserted or deleted.
2971 * PARAMETER(S):
2972 * [I] infoPtr : valid pointer to the listview structure
2973 * [I] nItem : item index
2974 * [I] direction : Direction of shift, +1 or -1.
2976 * RETURN:
2977 * None
2979 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2981 INT nNewFocus;
2982 BOOL bOldChange;
2984 /* temporarily disable change notification while shifting items */
2985 bOldChange = infoPtr->bDoChangeNotify;
2986 infoPtr->bDoChangeNotify = FALSE;
2988 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2990 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2992 assert(abs(direction) == 1);
2994 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2996 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2997 if (nNewFocus != infoPtr->nFocusedItem)
2998 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3000 /* But we are not supposed to modify nHotItem! */
3002 infoPtr->bDoChangeNotify = bOldChange;
3007 * DESCRIPTION:
3008 * Adds a block of selections.
3010 * PARAMETER(S):
3011 * [I] infoPtr : valid pointer to the listview structure
3012 * [I] nItem : item index
3014 * RETURN:
3015 * None
3017 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3019 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3020 INT nLast = max(infoPtr->nSelectionMark, nItem);
3021 NMLVODSTATECHANGE nmlv;
3022 LVITEMW item;
3023 BOOL bOldChange;
3024 INT i;
3026 /* Temporarily disable change notification
3027 * If the control is LVS_OWNERDATA, we need to send
3028 * only one LVN_ODSTATECHANGED notification.
3029 * See MSDN documentation for LVN_ITEMCHANGED.
3031 bOldChange = infoPtr->bDoChangeNotify;
3032 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3034 if (nFirst == -1) nFirst = nItem;
3036 item.state = LVIS_SELECTED;
3037 item.stateMask = LVIS_SELECTED;
3039 for (i = nFirst; i <= nLast; i++)
3040 LISTVIEW_SetItemState(infoPtr,i,&item);
3042 ZeroMemory(&nmlv, sizeof(nmlv));
3043 nmlv.iFrom = nFirst;
3044 nmlv.iTo = nLast;
3045 nmlv.uNewState = 0;
3046 nmlv.uOldState = item.state;
3048 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3049 infoPtr->bDoChangeNotify = bOldChange;
3053 /***
3054 * DESCRIPTION:
3055 * Sets a single group selection.
3057 * PARAMETER(S):
3058 * [I] infoPtr : valid pointer to the listview structure
3059 * [I] nItem : item index
3061 * RETURN:
3062 * None
3064 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3066 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3067 RANGES selection;
3068 LVITEMW item;
3069 ITERATOR i;
3071 if (!(selection = ranges_create(100))) return;
3073 item.state = LVIS_SELECTED;
3074 item.stateMask = LVIS_SELECTED;
3076 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3078 if (infoPtr->nSelectionMark == -1)
3080 infoPtr->nSelectionMark = nItem;
3081 ranges_additem(selection, nItem);
3083 else
3085 RANGE sel;
3087 sel.lower = min(infoPtr->nSelectionMark, nItem);
3088 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3089 ranges_add(selection, sel);
3092 else
3094 RECT rcItem, rcSel, rcSelMark;
3095 POINT ptItem;
3097 rcItem.left = LVIR_BOUNDS;
3098 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3099 rcSelMark.left = LVIR_BOUNDS;
3100 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3101 UnionRect(&rcSel, &rcItem, &rcSelMark);
3102 iterator_frameditems(&i, infoPtr, &rcSel);
3103 while(iterator_next(&i))
3105 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3106 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3108 iterator_destroy(&i);
3111 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3112 iterator_rangesitems(&i, selection);
3113 while(iterator_next(&i))
3114 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3115 /* this will also destroy the selection */
3116 iterator_destroy(&i);
3118 LISTVIEW_SetItemFocus(infoPtr, nItem);
3121 /***
3122 * DESCRIPTION:
3123 * Sets a single selection.
3125 * PARAMETER(S):
3126 * [I] infoPtr : valid pointer to the listview structure
3127 * [I] nItem : item index
3129 * RETURN:
3130 * None
3132 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3134 LVITEMW lvItem;
3136 TRACE("nItem=%d\n", nItem);
3138 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3140 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3141 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3142 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3144 infoPtr->nSelectionMark = nItem;
3147 /***
3148 * DESCRIPTION:
3149 * Set selection(s) with keyboard.
3151 * PARAMETER(S):
3152 * [I] infoPtr : valid pointer to the listview structure
3153 * [I] nItem : item index
3155 * RETURN:
3156 * SUCCESS : TRUE (needs to be repainted)
3157 * FAILURE : FALSE (nothing has changed)
3159 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3161 /* FIXME: pass in the state */
3162 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3163 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3164 BOOL bResult = FALSE;
3166 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3168 if (infoPtr->dwStyle & LVS_SINGLESEL)
3170 bResult = TRUE;
3171 LISTVIEW_SetSelection(infoPtr, nItem);
3173 else
3175 if (wShift)
3177 bResult = TRUE;
3178 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3180 else if (wCtrl)
3182 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3184 else
3186 bResult = TRUE;
3187 LISTVIEW_SetSelection(infoPtr, nItem);
3190 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3193 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3194 return bResult;
3197 static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3199 LVHITTESTINFO lvHitTestInfo;
3201 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3202 lvHitTestInfo.pt.x = pt.x;
3203 lvHitTestInfo.pt.y = pt.y;
3205 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3207 lpLVItem->mask = LVIF_PARAM;
3208 lpLVItem->iItem = lvHitTestInfo.iItem;
3209 lpLVItem->iSubItem = 0;
3211 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3214 /***
3215 * DESCRIPTION:
3216 * Called when the mouse is being actively tracked and has hovered for a specified
3217 * amount of time
3219 * PARAMETER(S):
3220 * [I] infoPtr : valid pointer to the listview structure
3221 * [I] fwKeys : key indicator
3222 * [I] x,y : mouse position
3224 * RETURN:
3225 * 0 if the message was processed, non-zero if there was an error
3227 * INFO:
3228 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3229 * over the item for a certain period of time.
3232 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3234 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3236 LVITEMW item;
3237 POINT pt;
3239 pt.x = x;
3240 pt.y = y;
3242 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3243 LISTVIEW_SetSelection(infoPtr, item.iItem);
3246 return 0;
3249 /***
3250 * DESCRIPTION:
3251 * Called whenever WM_MOUSEMOVE is received.
3253 * PARAMETER(S):
3254 * [I] infoPtr : valid pointer to the listview structure
3255 * [I] fwKeys : key indicator
3256 * [I] x,y : mouse position
3258 * RETURN:
3259 * 0 if the message is processed, non-zero if there was an error
3261 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3263 TRACKMOUSEEVENT trackinfo;
3265 if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3267 LVHITTESTINFO lvHitTestInfo;
3268 NMLISTVIEW nmlv;
3270 lvHitTestInfo.pt = infoPtr->ptClickPos;
3271 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3273 ZeroMemory(&nmlv, sizeof(nmlv));
3274 nmlv.iItem = lvHitTestInfo.iItem;
3275 nmlv.ptAction = infoPtr->ptClickPos;
3277 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3279 return 0;
3281 else
3282 infoPtr->bLButtonDown = FALSE;
3284 /* see if we are supposed to be tracking mouse hovering */
3285 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3286 /* fill in the trackinfo struct */
3287 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3288 trackinfo.dwFlags = TME_QUERY;
3289 trackinfo.hwndTrack = infoPtr->hwndSelf;
3290 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3292 /* see if we are already tracking this hwnd */
3293 _TrackMouseEvent(&trackinfo);
3295 if(!(trackinfo.dwFlags & TME_HOVER)) {
3296 trackinfo.dwFlags = TME_HOVER;
3298 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3299 _TrackMouseEvent(&trackinfo);
3303 return 0;
3307 /***
3308 * Tests wheather the item is assignable to a list with style lStyle
3310 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3312 if ( (lpLVItem->mask & LVIF_TEXT) &&
3313 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3314 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3316 return TRUE;
3320 /***
3321 * DESCRIPTION:
3322 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3324 * PARAMETER(S):
3325 * [I] infoPtr : valid pointer to the listview structure
3326 * [I] lpLVItem : valid pointer to new item atttributes
3327 * [I] isNew : the item being set is being inserted
3328 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3329 * [O] bChanged : will be set to TRUE if the item really changed
3331 * RETURN:
3332 * SUCCESS : TRUE
3333 * FAILURE : FALSE
3335 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3337 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3338 ITEM_INFO *lpItem;
3339 NMLISTVIEW nmlv;
3340 UINT uChanged = 0;
3341 LVITEMW item;
3343 TRACE("()\n");
3345 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3347 if (lpLVItem->mask == 0) return TRUE;
3349 if (infoPtr->dwStyle & LVS_OWNERDATA)
3351 /* a virtual listview we stores only selection and focus */
3352 if (lpLVItem->mask & ~LVIF_STATE)
3353 return FALSE;
3354 lpItem = NULL;
3356 else
3358 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3359 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3360 assert (lpItem);
3363 /* we need to get the lParam and state of the item */
3364 item.iItem = lpLVItem->iItem;
3365 item.iSubItem = lpLVItem->iSubItem;
3366 item.mask = LVIF_STATE | LVIF_PARAM;
3367 item.stateMask = ~0;
3368 item.state = 0;
3369 item.lParam = 0;
3370 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3372 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3373 /* determine what fields will change */
3374 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3375 uChanged |= LVIF_STATE;
3377 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3378 uChanged |= LVIF_IMAGE;
3380 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3381 uChanged |= LVIF_PARAM;
3383 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3384 uChanged |= LVIF_INDENT;
3386 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3387 uChanged |= LVIF_TEXT;
3389 TRACE("uChanged=0x%x\n", uChanged);
3390 if (!uChanged) return TRUE;
3391 *bChanged = TRUE;
3393 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3394 nmlv.iItem = lpLVItem->iItem;
3395 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3396 nmlv.uOldState = item.state;
3397 nmlv.uChanged = uChanged;
3398 nmlv.lParam = item.lParam;
3400 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3401 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3402 /* are enabled */
3403 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3404 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3405 return FALSE;
3407 /* copy information */
3408 if (lpLVItem->mask & LVIF_TEXT)
3409 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3411 if (lpLVItem->mask & LVIF_IMAGE)
3412 lpItem->hdr.iImage = lpLVItem->iImage;
3414 if (lpLVItem->mask & LVIF_PARAM)
3415 lpItem->lParam = lpLVItem->lParam;
3417 if (lpLVItem->mask & LVIF_INDENT)
3418 lpItem->iIndent = lpLVItem->iIndent;
3420 if (uChanged & LVIF_STATE)
3422 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3424 lpItem->state &= ~lpLVItem->stateMask;
3425 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3427 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3429 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3430 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3432 else if (lpLVItem->stateMask & LVIS_SELECTED)
3433 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3435 /* if we are asked to change focus, and we manage it, do it */
3436 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3438 if (lpLVItem->state & LVIS_FOCUSED)
3440 LISTVIEW_SetItemFocus(infoPtr, -1);
3441 infoPtr->nFocusedItem = lpLVItem->iItem;
3442 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3444 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3445 infoPtr->nFocusedItem = -1;
3449 /* if we're inserting the item, we're done */
3450 if (isNew) return TRUE;
3452 /* send LVN_ITEMCHANGED notification */
3453 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3454 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3456 return TRUE;
3459 /***
3460 * DESCRIPTION:
3461 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3463 * PARAMETER(S):
3464 * [I] infoPtr : valid pointer to the listview structure
3465 * [I] lpLVItem : valid pointer to new subitem atttributes
3466 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3467 * [O] bChanged : will be set to TRUE if the item really changed
3469 * RETURN:
3470 * SUCCESS : TRUE
3471 * FAILURE : FALSE
3473 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3475 HDPA hdpaSubItems;
3476 SUBITEM_INFO *lpSubItem;
3478 /* we do not support subitems for virtual listviews */
3479 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3481 /* set subitem only if column is present */
3482 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3484 /* First do some sanity checks */
3485 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3486 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3488 /* get the subitem structure, and create it if not there */
3489 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3490 assert (hdpaSubItems);
3492 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3493 if (!lpSubItem)
3495 SUBITEM_INFO *tmpSubItem;
3496 INT i;
3498 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3499 if (!lpSubItem) return FALSE;
3500 /* we could binary search here, if need be...*/
3501 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3503 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3504 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3506 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3508 Free(lpSubItem);
3509 return FALSE;
3511 lpSubItem->iSubItem = lpLVItem->iSubItem;
3512 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3513 *bChanged = TRUE;
3516 if (lpLVItem->mask & LVIF_IMAGE)
3517 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3519 lpSubItem->hdr.iImage = lpLVItem->iImage;
3520 *bChanged = TRUE;
3523 if (lpLVItem->mask & LVIF_TEXT)
3524 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3526 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3527 *bChanged = TRUE;
3530 return TRUE;
3533 /***
3534 * DESCRIPTION:
3535 * Sets item attributes.
3537 * PARAMETER(S):
3538 * [I] infoPtr : valid pointer to the listview structure
3539 * [I] lpLVItem : new item atttributes
3540 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3542 * RETURN:
3543 * SUCCESS : TRUE
3544 * FAILURE : FALSE
3546 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3548 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3549 LPWSTR pszText = NULL;
3550 BOOL bResult, bChanged = FALSE;
3552 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3554 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3555 return FALSE;
3557 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3558 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3560 pszText = lpLVItem->pszText;
3561 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3564 /* actually set the fields */
3565 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3567 if (lpLVItem->iSubItem)
3568 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3569 else
3570 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3572 /* redraw item, if necessary */
3573 if (bChanged && !infoPtr->bIsDrawing)
3575 /* this little optimization eliminates some nasty flicker */
3576 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3577 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3578 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3579 else
3580 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3582 /* restore text */
3583 if (pszText)
3585 textfreeT(lpLVItem->pszText, isW);
3586 ((LVITEMW *)lpLVItem)->pszText = pszText;
3589 return bResult;
3592 /***
3593 * DESCRIPTION:
3594 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3596 * PARAMETER(S):
3597 * [I] infoPtr : valid pointer to the listview structure
3599 * RETURN:
3600 * item index
3602 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3604 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3605 INT nItem = 0;
3606 SCROLLINFO scrollInfo;
3608 scrollInfo.cbSize = sizeof(SCROLLINFO);
3609 scrollInfo.fMask = SIF_POS;
3611 if (uView == LVS_LIST)
3613 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3614 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3616 else if (uView == LVS_REPORT)
3618 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3619 nItem = scrollInfo.nPos;
3621 else
3623 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3624 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3627 TRACE("nItem=%d\n", nItem);
3629 return nItem;
3633 /***
3634 * DESCRIPTION:
3635 * Erases the background of the given rectangle
3637 * PARAMETER(S):
3638 * [I] infoPtr : valid pointer to the listview structure
3639 * [I] hdc : device context handle
3640 * [I] lprcBox : clipping rectangle
3642 * RETURN:
3643 * Success: TRUE
3644 * Failure: FALSE
3646 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3648 if (!infoPtr->hBkBrush) return FALSE;
3650 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3652 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3655 /***
3656 * DESCRIPTION:
3657 * Draws an item.
3659 * PARAMETER(S):
3660 * [I] infoPtr : valid pointer to the listview structure
3661 * [I] hdc : device context handle
3662 * [I] nItem : item index
3663 * [I] nSubItem : subitem index
3664 * [I] pos : item position in client coordinates
3665 * [I] cdmode : custom draw mode
3667 * RETURN:
3668 * Success: TRUE
3669 * Failure: FALSE
3671 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3673 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3674 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3675 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3676 DWORD cdsubitemmode = CDRF_DODEFAULT;
3677 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3678 NMLVCUSTOMDRAW nmlvcd;
3679 HIMAGELIST himl;
3680 LVITEMW lvItem;
3682 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&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 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3713 /* fill in the custom draw structure */
3714 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3716 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3717 if (cdmode & CDRF_NOTIFYITEMDRAW)
3718 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3719 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3720 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3721 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3722 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3724 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3725 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3727 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3728 prepaint_setup(infoPtr, hdc, &nmlvcd);
3730 /* in full row select, subitems, will just use main item's colors */
3731 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3732 nmlvcd.clrTextBk = CLR_NONE;
3734 /* state icons */
3735 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3737 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3738 if (uStateImage)
3740 TRACE("uStateImage=%d\n", uStateImage);
3741 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3745 /* small icons */
3746 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3747 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3749 TRACE("iImage=%d\n", lvItem.iImage);
3750 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3751 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3754 /* Don't bother painting item being edited */
3755 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3757 /* draw the selection background, if we're drawing the main item */
3758 if (nSubItem == 0)
3760 rcSelect = rcLabel;
3761 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3762 rcSelect.right = rcBox.right;
3764 if (nmlvcd.clrTextBk != CLR_NONE)
3765 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3766 if(lprcFocus) *lprcFocus = rcSelect;
3769 /* figure out the text drawing flags */
3770 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3771 if (uView == LVS_ICON)
3772 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3773 else if (nSubItem)
3775 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3777 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3778 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3779 default: uFormat |= DT_LEFT;
3782 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3784 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3785 else rcLabel.left += LABEL_HOR_PADDING;
3787 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3788 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3790 postpaint:
3791 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3792 notify_postpaint(infoPtr, &nmlvcd);
3793 return TRUE;
3796 /***
3797 * DESCRIPTION:
3798 * Draws listview items when in owner draw mode.
3800 * PARAMETER(S):
3801 * [I] infoPtr : valid pointer to the listview structure
3802 * [I] hdc : device context handle
3804 * RETURN:
3805 * None
3807 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3809 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3810 DWORD cditemmode = CDRF_DODEFAULT;
3811 NMLVCUSTOMDRAW nmlvcd;
3812 POINT Origin, Position;
3813 DRAWITEMSTRUCT dis;
3814 LVITEMW item;
3816 TRACE("()\n");
3818 ZeroMemory(&dis, sizeof(dis));
3820 /* Get scroll info once before loop */
3821 LISTVIEW_GetOrigin(infoPtr, &Origin);
3823 /* iterate through the invalidated rows */
3824 while(iterator_next(i))
3826 item.iItem = i->nItem;
3827 item.iSubItem = 0;
3828 item.mask = LVIF_PARAM | LVIF_STATE;
3829 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3830 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3832 dis.CtlType = ODT_LISTVIEW;
3833 dis.CtlID = uID;
3834 dis.itemID = item.iItem;
3835 dis.itemAction = ODA_DRAWENTIRE;
3836 dis.itemState = 0;
3837 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3838 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3839 dis.hwndItem = infoPtr->hwndSelf;
3840 dis.hDC = hdc;
3841 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3842 dis.rcItem.left = Position.x + Origin.x;
3843 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3844 dis.rcItem.top = Position.y + Origin.y;
3845 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3846 dis.itemData = item.lParam;
3848 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3851 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3852 * structure for the rest. of the paint cycle
3854 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3855 if (cdmode & CDRF_NOTIFYITEMDRAW)
3856 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3858 if (!(cditemmode & CDRF_SKIPDEFAULT))
3860 prepaint_setup (infoPtr, hdc, &nmlvcd);
3861 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3864 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3865 notify_postpaint(infoPtr, &nmlvcd);
3869 /***
3870 * DESCRIPTION:
3871 * Draws listview items when in report display mode.
3873 * PARAMETER(S):
3874 * [I] infoPtr : valid pointer to the listview structure
3875 * [I] hdc : device context handle
3876 * [I] cdmode : custom draw mode
3878 * RETURN:
3879 * None
3881 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3883 INT rgntype;
3884 RECT rcClip, rcItem;
3885 POINT Origin, Position;
3886 RANGE colRange;
3887 ITERATOR j;
3889 TRACE("()\n");
3891 /* figure out what to draw */
3892 rgntype = GetClipBox(hdc, &rcClip);
3893 if (rgntype == NULLREGION) return;
3895 /* Get scroll info once before loop */
3896 LISTVIEW_GetOrigin(infoPtr, &Origin);
3898 /* narrow down the columns we need to paint */
3899 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3901 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3902 if (rcItem.right + Origin.x >= rcClip.left) break;
3904 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3906 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3907 if (rcItem.left + Origin.x < rcClip.right) break;
3909 iterator_rangeitems(&j, colRange);
3911 /* in full row select, we _have_ to draw the main item */
3912 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3913 j.nSpecial = 0;
3915 /* iterate through the invalidated rows */
3916 while(iterator_next(i))
3918 /* iterate through the invalidated columns */
3919 while(iterator_next(&j))
3921 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3922 Position.x += Origin.x;
3923 Position.y += Origin.y;
3925 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3927 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3928 rcItem.top = 0;
3929 rcItem.bottom = infoPtr->nItemHeight;
3930 OffsetRect(&rcItem, Position.x, Position.y);
3931 if (!RectVisible(hdc, &rcItem)) continue;
3934 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3937 iterator_destroy(&j);
3940 /***
3941 * DESCRIPTION:
3942 * Draws listview items when in list display mode.
3944 * PARAMETER(S):
3945 * [I] infoPtr : valid pointer to the listview structure
3946 * [I] hdc : device context handle
3947 * [I] cdmode : custom draw mode
3949 * RETURN:
3950 * None
3952 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3954 POINT Origin, Position;
3956 /* Get scroll info once before loop */
3957 LISTVIEW_GetOrigin(infoPtr, &Origin);
3959 while(iterator_prev(i))
3961 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3962 Position.x += Origin.x;
3963 Position.y += Origin.y;
3965 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3970 /***
3971 * DESCRIPTION:
3972 * Draws listview items.
3974 * PARAMETER(S):
3975 * [I] infoPtr : valid pointer to the listview structure
3976 * [I] hdc : device context handle
3978 * RETURN:
3979 * NoneX
3981 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3983 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3984 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3985 NMLVCUSTOMDRAW nmlvcd;
3986 HFONT hOldFont;
3987 DWORD cdmode;
3988 INT oldBkMode;
3989 RECT rcClient;
3990 ITERATOR i;
3992 LISTVIEW_DUMP(infoPtr);
3994 infoPtr->bIsDrawing = TRUE;
3996 /* save dc values we're gonna trash while drawing */
3997 hOldFont = SelectObject(hdc, infoPtr->hFont);
3998 oldBkMode = GetBkMode(hdc);
3999 infoPtr->clrTextBkDefault = GetBkColor(hdc);
4000 oldTextColor = GetTextColor(hdc);
4002 oldClrTextBk = infoPtr->clrTextBk;
4003 oldClrText = infoPtr->clrText;
4005 infoPtr->cditemmode = CDRF_DODEFAULT;
4007 GetClientRect(infoPtr->hwndSelf, &rcClient);
4008 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4009 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4010 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4011 prepaint_setup(infoPtr, hdc, &nmlvcd);
4013 /* Use these colors to draw the items */
4014 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4015 infoPtr->clrText = nmlvcd.clrText;
4017 /* nothing to draw */
4018 if(infoPtr->nItemCount == 0) goto enddraw;
4020 /* figure out what we need to draw */
4021 iterator_visibleitems(&i, infoPtr, hdc);
4023 /* send cache hint notification */
4024 if (infoPtr->dwStyle & LVS_OWNERDATA)
4026 RANGE range = iterator_range(&i);
4027 NMLVCACHEHINT nmlv;
4029 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4030 nmlv.iFrom = range.lower;
4031 nmlv.iTo = range.upper - 1;
4032 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4035 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4036 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4037 else
4039 if (uView == LVS_REPORT)
4040 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4041 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4042 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4044 /* if we have a focus rect, draw it */
4045 if (infoPtr->bFocus)
4046 DrawFocusRect(hdc, &infoPtr->rcFocus);
4048 iterator_destroy(&i);
4050 enddraw:
4051 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4052 notify_postpaint(infoPtr, &nmlvcd);
4054 infoPtr->clrTextBk = oldClrTextBk;
4055 infoPtr->clrText = oldClrText;
4057 SelectObject(hdc, hOldFont);
4058 SetBkMode(hdc, oldBkMode);
4059 SetBkColor(hdc, infoPtr->clrTextBkDefault);
4060 SetTextColor(hdc, oldTextColor);
4061 infoPtr->bIsDrawing = FALSE;
4065 /***
4066 * DESCRIPTION:
4067 * Calculates the approximate width and height of a given number of items.
4069 * PARAMETER(S):
4070 * [I] infoPtr : valid pointer to the listview structure
4071 * [I] nItemCount : number of items
4072 * [I] wWidth : width
4073 * [I] wHeight : height
4075 * RETURN:
4076 * Returns a DWORD. The width in the low word and the height in high word.
4078 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4079 WORD wWidth, WORD wHeight)
4081 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4082 INT nItemCountPerColumn = 1;
4083 INT nColumnCount = 0;
4084 DWORD dwViewRect = 0;
4086 if (nItemCount == -1)
4087 nItemCount = infoPtr->nItemCount;
4089 if (uView == LVS_LIST)
4091 if (wHeight == 0xFFFF)
4093 /* use current height */
4094 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4097 if (wHeight < infoPtr->nItemHeight)
4098 wHeight = infoPtr->nItemHeight;
4100 if (nItemCount > 0)
4102 if (infoPtr->nItemHeight > 0)
4104 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4105 if (nItemCountPerColumn == 0)
4106 nItemCountPerColumn = 1;
4108 if (nItemCount % nItemCountPerColumn != 0)
4109 nColumnCount = nItemCount / nItemCountPerColumn;
4110 else
4111 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4115 /* Microsoft padding magic */
4116 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4117 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4119 dwViewRect = MAKELONG(wWidth, wHeight);
4121 else if (uView == LVS_REPORT)
4123 RECT rcBox;
4125 if (infoPtr->nItemCount > 0)
4127 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4128 wWidth = rcBox.right - rcBox.left;
4129 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4131 else
4133 /* use current height and width */
4134 if (wHeight == 0xffff)
4135 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4136 if (wWidth == 0xffff)
4137 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4140 dwViewRect = MAKELONG(wWidth, wHeight);
4142 else if (uView == LVS_SMALLICON)
4143 FIXME("uView == LVS_SMALLICON: not implemented\n");
4144 else if (uView == LVS_ICON)
4145 FIXME("uView == LVS_ICON: not implemented\n");
4147 return dwViewRect;
4151 /***
4152 * DESCRIPTION:
4153 * Create a drag image list for the specified item.
4155 * PARAMETER(S):
4156 * [I] infoPtr : valid pointer to the listview structure
4157 * [I] iItem : index of item
4158 * [O] lppt : Upperr-left corner of the image
4160 * RETURN:
4161 * Returns a handle to the image list if successful, NULL otherwise.
4163 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4165 RECT rcItem;
4166 SIZE size;
4167 POINT pos;
4168 HDC hdc, hdcOrig;
4169 HBITMAP hbmp, hOldbmp;
4170 HIMAGELIST dragList = 0;
4171 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4173 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4174 return 0;
4176 rcItem.left = LVIR_BOUNDS;
4177 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4178 return 0;
4180 lppt->x = rcItem.left;
4181 lppt->y = rcItem.top;
4183 size.cx = rcItem.right - rcItem.left;
4184 size.cy = rcItem.bottom - rcItem.top;
4186 hdcOrig = GetDC(infoPtr->hwndSelf);
4187 hdc = CreateCompatibleDC(hdcOrig);
4188 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4189 hOldbmp = SelectObject(hdc, hbmp);
4191 rcItem.left = rcItem.top = 0;
4192 rcItem.right = size.cx;
4193 rcItem.bottom = size.cy;
4194 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4196 pos.x = pos.y = 0;
4197 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4199 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4200 SelectObject(hdc, hOldbmp);
4201 ImageList_Add(dragList, hbmp, 0);
4203 else
4204 SelectObject(hdc, hOldbmp);
4206 DeleteObject(hbmp);
4207 DeleteDC(hdc);
4208 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4210 TRACE("ret=%p\n", dragList);
4212 return dragList;
4216 /***
4217 * DESCRIPTION:
4218 * Removes all listview items and subitems.
4220 * PARAMETER(S):
4221 * [I] infoPtr : valid pointer to the listview structure
4223 * RETURN:
4224 * SUCCESS : TRUE
4225 * FAILURE : FALSE
4227 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4229 NMLISTVIEW nmlv;
4230 HDPA hdpaSubItems = NULL;
4231 BOOL bSuppress;
4232 ITEMHDR *hdrItem;
4233 INT i, j;
4235 TRACE("()\n");
4237 /* we do it directly, to avoid notifications */
4238 ranges_clear(infoPtr->selectionRanges);
4239 infoPtr->nSelectionMark = -1;
4240 infoPtr->nFocusedItem = -1;
4241 SetRectEmpty(&infoPtr->rcFocus);
4242 /* But we are supposed to leave nHotItem as is! */
4245 /* send LVN_DELETEALLITEMS notification */
4246 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4247 nmlv.iItem = -1;
4248 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4250 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4252 /* send LVN_DELETEITEM notification, if not suppressed */
4253 if (!bSuppress) notify_deleteitem(infoPtr, i);
4254 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4256 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4257 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4259 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4260 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4261 Free(hdrItem);
4263 DPA_Destroy(hdpaSubItems);
4264 DPA_DeletePtr(infoPtr->hdpaItems, i);
4266 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4267 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4268 infoPtr->nItemCount --;
4271 LISTVIEW_UpdateScroll(infoPtr);
4273 LISTVIEW_InvalidateList(infoPtr);
4275 return TRUE;
4278 /***
4279 * DESCRIPTION:
4280 * Scrolls, and updates the columns, when a column is changing width.
4282 * PARAMETER(S):
4283 * [I] infoPtr : valid pointer to the listview structure
4284 * [I] nColumn : column to scroll
4285 * [I] dx : amount of scroll, in pixels
4287 * RETURN:
4288 * None.
4290 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4292 COLUMN_INFO *lpColumnInfo;
4293 RECT rcOld, rcCol;
4294 POINT ptOrigin;
4295 INT nCol;
4297 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4298 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4299 rcCol = lpColumnInfo->rcHeader;
4300 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4301 rcCol.left = rcCol.right;
4303 /* ajust the other columns */
4304 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4306 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4307 lpColumnInfo->rcHeader.left += dx;
4308 lpColumnInfo->rcHeader.right += dx;
4311 /* do not update screen if not in report mode */
4312 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4314 /* if we have a focus, must first erase the focus rect */
4315 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4317 /* Need to reset the item width when inserting a new column */
4318 infoPtr->nItemWidth += dx;
4320 LISTVIEW_UpdateScroll(infoPtr);
4321 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4323 /* scroll to cover the deleted column, and invalidate for redraw */
4324 rcOld = infoPtr->rcList;
4325 rcOld.left = ptOrigin.x + rcCol.left + dx;
4326 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4328 /* we can restore focus now */
4329 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4332 /***
4333 * DESCRIPTION:
4334 * Removes a column from the listview control.
4336 * PARAMETER(S):
4337 * [I] infoPtr : valid pointer to the listview structure
4338 * [I] nColumn : column index
4340 * RETURN:
4341 * SUCCESS : TRUE
4342 * FAILURE : FALSE
4344 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4346 RECT rcCol;
4348 TRACE("nColumn=%d\n", nColumn);
4350 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4351 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4353 /* While the MSDN specifically says that column zero should not be deleted,
4354 what actually happens is that the column itself is deleted but no items or subitems
4355 are removed.
4358 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4360 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4361 return FALSE;
4363 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4364 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4366 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4368 SUBITEM_INFO *lpSubItem, *lpDelItem;
4369 HDPA hdpaSubItems;
4370 INT nItem, nSubItem, i;
4372 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4374 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4375 nSubItem = 0;
4376 lpDelItem = 0;
4377 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4379 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4380 if (lpSubItem->iSubItem == nColumn)
4382 nSubItem = i;
4383 lpDelItem = lpSubItem;
4385 else if (lpSubItem->iSubItem > nColumn)
4387 lpSubItem->iSubItem--;
4391 /* if we found our subitem, zapp it */
4392 if (nSubItem > 0)
4394 /* free string */
4395 if (is_textW(lpDelItem->hdr.pszText))
4396 Free(lpDelItem->hdr.pszText);
4398 /* free item */
4399 Free(lpDelItem);
4401 /* free dpa memory */
4402 DPA_DeletePtr(hdpaSubItems, nSubItem);
4407 /* update the other column info */
4408 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4409 LISTVIEW_InvalidateList(infoPtr);
4410 else
4411 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4413 return TRUE;
4416 /***
4417 * DESCRIPTION:
4418 * Invalidates the listview after an item's insertion or deletion.
4420 * PARAMETER(S):
4421 * [I] infoPtr : valid pointer to the listview structure
4422 * [I] nItem : item index
4423 * [I] dir : -1 if deleting, 1 if inserting
4425 * RETURN:
4426 * None
4428 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4430 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4431 INT nPerCol, nItemCol, nItemRow;
4432 RECT rcScroll;
4433 POINT Origin;
4435 /* if we don't refresh, what's the point of scrolling? */
4436 if (!is_redrawing(infoPtr)) return;
4438 assert (abs(dir) == 1);
4440 /* arrange icons if autoarrange is on */
4441 if (is_autoarrange(infoPtr))
4443 BOOL arrange = TRUE;
4444 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4445 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4446 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4449 /* scrollbars need updating */
4450 LISTVIEW_UpdateScroll(infoPtr);
4452 /* figure out the item's position */
4453 if (uView == LVS_REPORT)
4454 nPerCol = infoPtr->nItemCount + 1;
4455 else if (uView == LVS_LIST)
4456 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4457 else /* LVS_ICON, or LVS_SMALLICON */
4458 return;
4460 nItemCol = nItem / nPerCol;
4461 nItemRow = nItem % nPerCol;
4462 LISTVIEW_GetOrigin(infoPtr, &Origin);
4464 /* move the items below up a slot */
4465 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4466 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4467 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4468 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4469 OffsetRect(&rcScroll, Origin.x, Origin.y);
4470 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4471 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4473 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4474 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4475 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4478 /* report has only that column, so we're done */
4479 if (uView == LVS_REPORT) return;
4481 /* now for LISTs, we have to deal with the columns to the right */
4482 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4483 rcScroll.top = 0;
4484 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4485 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4486 OffsetRect(&rcScroll, Origin.x, Origin.y);
4487 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4488 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4489 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4492 /***
4493 * DESCRIPTION:
4494 * Removes an item from the listview control.
4496 * PARAMETER(S):
4497 * [I] infoPtr : valid pointer to the listview structure
4498 * [I] nItem : item index
4500 * RETURN:
4501 * SUCCESS : TRUE
4502 * FAILURE : FALSE
4504 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4506 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4507 LVITEMW item;
4509 TRACE("(nItem=%d)\n", nItem);
4511 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4513 /* remove selection, and focus */
4514 item.state = 0;
4515 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4516 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4518 /* send LVN_DELETEITEM notification. */
4519 notify_deleteitem(infoPtr, nItem);
4521 /* we need to do this here, because we'll be deleting stuff */
4522 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4523 LISTVIEW_InvalidateItem(infoPtr, nItem);
4525 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4527 HDPA hdpaSubItems;
4528 ITEMHDR *hdrItem;
4529 INT i;
4531 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4532 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4534 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4535 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4536 Free(hdrItem);
4538 DPA_Destroy(hdpaSubItems);
4541 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4543 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4544 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4547 infoPtr->nItemCount--;
4548 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4550 /* now is the invalidation fun */
4551 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4552 return TRUE;
4556 /***
4557 * DESCRIPTION:
4558 * Callback implementation for editlabel control
4560 * PARAMETER(S):
4561 * [I] infoPtr : valid pointer to the listview structure
4562 * [I] pszText : modified text
4563 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4565 * RETURN:
4566 * SUCCESS : TRUE
4567 * FAILURE : FALSE
4569 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4571 NMLVDISPINFOW dispInfo;
4573 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4575 ZeroMemory(&dispInfo, sizeof(dispInfo));
4576 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4577 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4578 dispInfo.item.iSubItem = 0;
4579 dispInfo.item.stateMask = ~0;
4580 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4581 /* add the text from the edit in */
4582 dispInfo.item.mask |= LVIF_TEXT;
4583 dispInfo.item.pszText = pszText;
4584 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4586 /* Do we need to update the Item Text */
4587 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4588 if (!pszText) return TRUE;
4590 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4592 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4593 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4594 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4596 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4597 return TRUE;
4601 ZeroMemory(&dispInfo, sizeof(dispInfo));
4602 dispInfo.item.mask = LVIF_TEXT;
4603 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4604 dispInfo.item.iSubItem = 0;
4605 dispInfo.item.pszText = pszText;
4606 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4607 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4610 /***
4611 * DESCRIPTION:
4612 * Begin in place editing of specified list view item
4614 * PARAMETER(S):
4615 * [I] infoPtr : valid pointer to the listview structure
4616 * [I] nItem : item index
4617 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4619 * RETURN:
4620 * SUCCESS : TRUE
4621 * FAILURE : FALSE
4623 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4625 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4626 NMLVDISPINFOW dispInfo;
4627 RECT rect;
4629 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4631 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4632 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4634 infoPtr->nEditLabelItem = nItem;
4636 /* Is the EditBox still there, if so remove it */
4637 if(infoPtr->hwndEdit != 0)
4639 SetFocus(infoPtr->hwndSelf);
4640 infoPtr->hwndEdit = 0;
4643 LISTVIEW_SetSelection(infoPtr, nItem);
4644 LISTVIEW_SetItemFocus(infoPtr, nItem);
4645 LISTVIEW_InvalidateItem(infoPtr, nItem);
4647 rect.left = LVIR_LABEL;
4648 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4650 ZeroMemory(&dispInfo, sizeof(dispInfo));
4651 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4652 dispInfo.item.iItem = nItem;
4653 dispInfo.item.iSubItem = 0;
4654 dispInfo.item.stateMask = ~0;
4655 dispInfo.item.pszText = szDispText;
4656 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4657 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4659 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4660 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4661 if (!infoPtr->hwndEdit) return 0;
4663 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4665 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4666 infoPtr->hwndEdit = 0;
4667 return 0;
4670 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4671 SetFocus(infoPtr->hwndEdit);
4672 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4673 return infoPtr->hwndEdit;
4677 /***
4678 * DESCRIPTION:
4679 * Ensures the specified item is visible, scrolling into view if necessary.
4681 * PARAMETER(S):
4682 * [I] infoPtr : valid pointer to the listview structure
4683 * [I] nItem : item index
4684 * [I] bPartial : partially or entirely visible
4686 * RETURN:
4687 * SUCCESS : TRUE
4688 * FAILURE : FALSE
4690 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4692 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4693 INT nScrollPosHeight = 0;
4694 INT nScrollPosWidth = 0;
4695 INT nHorzAdjust = 0;
4696 INT nVertAdjust = 0;
4697 INT nHorzDiff = 0;
4698 INT nVertDiff = 0;
4699 RECT rcItem, rcTemp;
4701 rcItem.left = LVIR_BOUNDS;
4702 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4704 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4706 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4708 /* scroll left/right, but in LVS_REPORT mode */
4709 if (uView == LVS_LIST)
4710 nScrollPosWidth = infoPtr->nItemWidth;
4711 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4712 nScrollPosWidth = 1;
4714 if (rcItem.left < infoPtr->rcList.left)
4716 nHorzAdjust = -1;
4717 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4719 else
4721 nHorzAdjust = 1;
4722 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4726 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4728 /* scroll up/down, but not in LVS_LIST mode */
4729 if (uView == LVS_REPORT)
4730 nScrollPosHeight = infoPtr->nItemHeight;
4731 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4732 nScrollPosHeight = 1;
4734 if (rcItem.top < infoPtr->rcList.top)
4736 nVertAdjust = -1;
4737 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4739 else
4741 nVertAdjust = 1;
4742 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4746 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4748 if (nScrollPosWidth)
4750 INT diff = nHorzDiff / nScrollPosWidth;
4751 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4752 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4755 if (nScrollPosHeight)
4757 INT diff = nVertDiff / nScrollPosHeight;
4758 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4759 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4762 return TRUE;
4765 /***
4766 * DESCRIPTION:
4767 * Searches for an item with specific characteristics.
4769 * PARAMETER(S):
4770 * [I] hwnd : window handle
4771 * [I] nStart : base item index
4772 * [I] lpFindInfo : item information to look for
4774 * RETURN:
4775 * SUCCESS : index of item
4776 * FAILURE : -1
4778 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4779 const LVFINDINFOW *lpFindInfo)
4781 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4782 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4783 BOOL bWrap = FALSE, bNearest = FALSE;
4784 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4785 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4786 POINT Position, Destination;
4787 LVITEMW lvItem;
4789 if (!lpFindInfo || nItem < 0) return -1;
4791 lvItem.mask = 0;
4792 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4794 lvItem.mask |= LVIF_TEXT;
4795 lvItem.pszText = szDispText;
4796 lvItem.cchTextMax = DISP_TEXT_SIZE;
4799 if (lpFindInfo->flags & LVFI_WRAP)
4800 bWrap = TRUE;
4802 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4803 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4805 POINT Origin;
4806 RECT rcArea;
4808 LISTVIEW_GetOrigin(infoPtr, &Origin);
4809 Destination.x = lpFindInfo->pt.x - Origin.x;
4810 Destination.y = lpFindInfo->pt.y - Origin.y;
4811 switch(lpFindInfo->vkDirection)
4813 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4814 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4815 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4816 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4817 case VK_HOME: Destination.x = Destination.y = 0; break;
4818 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4819 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4820 case VK_END:
4821 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4822 Destination.x = rcArea.right;
4823 Destination.y = rcArea.bottom;
4824 break;
4825 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4827 bNearest = TRUE;
4829 else Destination.x = Destination.y = 0;
4831 /* if LVFI_PARAM is specified, all other flags are ignored */
4832 if (lpFindInfo->flags & LVFI_PARAM)
4834 lvItem.mask |= LVIF_PARAM;
4835 bNearest = FALSE;
4836 lvItem.mask &= ~LVIF_TEXT;
4839 again:
4840 for (; nItem < nLast; nItem++)
4842 lvItem.iItem = nItem;
4843 lvItem.iSubItem = 0;
4844 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4846 if (lvItem.mask & LVIF_PARAM)
4848 if (lpFindInfo->lParam == lvItem.lParam)
4849 return nItem;
4850 else
4851 continue;
4854 if (lvItem.mask & LVIF_TEXT)
4856 if (lpFindInfo->flags & LVFI_PARTIAL)
4858 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4860 else
4862 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4866 if (!bNearest) return nItem;
4868 /* This is very inefficient. To do a good job here,
4869 * we need a sorted array of (x,y) item positions */
4870 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4872 /* compute the distance^2 to the destination */
4873 xdist = Destination.x - Position.x;
4874 ydist = Destination.y - Position.y;
4875 dist = xdist * xdist + ydist * ydist;
4877 /* remember the distance, and item if it's closer */
4878 if (dist < mindist)
4880 mindist = dist;
4881 nNearestItem = nItem;
4885 if (bWrap)
4887 nItem = 0;
4888 nLast = min(nStart + 1, infoPtr->nItemCount);
4889 bWrap = FALSE;
4890 goto again;
4893 return nNearestItem;
4896 /***
4897 * DESCRIPTION:
4898 * Searches for an item with specific characteristics.
4900 * PARAMETER(S):
4901 * [I] hwnd : window handle
4902 * [I] nStart : base item index
4903 * [I] lpFindInfo : item information to look for
4905 * RETURN:
4906 * SUCCESS : index of item
4907 * FAILURE : -1
4909 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4910 const LVFINDINFOA *lpFindInfo)
4912 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4913 LVFINDINFOW fiw;
4914 INT res;
4916 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4917 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4918 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4919 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4920 return res;
4923 /***
4924 * DESCRIPTION:
4925 * Retrieves the background image of the listview control.
4927 * PARAMETER(S):
4928 * [I] infoPtr : valid pointer to the listview structure
4929 * [O] lpBkImage : background image attributes
4931 * RETURN:
4932 * SUCCESS : TRUE
4933 * FAILURE : FALSE
4935 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4936 /* { */
4937 /* FIXME (listview, "empty stub!\n"); */
4938 /* return FALSE; */
4939 /* } */
4941 /***
4942 * DESCRIPTION:
4943 * Retrieves column attributes.
4945 * PARAMETER(S):
4946 * [I] infoPtr : valid pointer to the listview structure
4947 * [I] nColumn : column index
4948 * [IO] lpColumn : column information
4949 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4950 * otherwise it is in fact a LPLVCOLUMNA
4952 * RETURN:
4953 * SUCCESS : TRUE
4954 * FAILURE : FALSE
4956 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4958 COLUMN_INFO *lpColumnInfo;
4959 HDITEMW hdi;
4961 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4962 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4964 /* initialize memory */
4965 ZeroMemory(&hdi, sizeof(hdi));
4967 if (lpColumn->mask & LVCF_TEXT)
4969 hdi.mask |= HDI_TEXT;
4970 hdi.pszText = lpColumn->pszText;
4971 hdi.cchTextMax = lpColumn->cchTextMax;
4974 if (lpColumn->mask & LVCF_IMAGE)
4975 hdi.mask |= HDI_IMAGE;
4977 if (lpColumn->mask & LVCF_ORDER)
4978 hdi.mask |= HDI_ORDER;
4980 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4982 if (lpColumn->mask & LVCF_FMT)
4983 lpColumn->fmt = lpColumnInfo->fmt;
4985 if (lpColumn->mask & LVCF_WIDTH)
4986 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4988 if (lpColumn->mask & LVCF_IMAGE)
4989 lpColumn->iImage = hdi.iImage;
4991 if (lpColumn->mask & LVCF_ORDER)
4992 lpColumn->iOrder = hdi.iOrder;
4994 return TRUE;
4998 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5000 INT i;
5002 if (!lpiArray)
5003 return FALSE;
5005 /* FIXME: little hack */
5006 for (i = 0; i < iCount; i++)
5007 lpiArray[i] = i;
5009 return TRUE;
5012 /***
5013 * DESCRIPTION:
5014 * Retrieves the column width.
5016 * PARAMETER(S):
5017 * [I] infoPtr : valid pointer to the listview structure
5018 * [I] int : column index
5020 * RETURN:
5021 * SUCCESS : column width
5022 * FAILURE : zero
5024 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5026 INT nColumnWidth = 0;
5027 RECT rcHeader;
5029 TRACE("nColumn=%d\n", nColumn);
5031 /* we have a 'column' in LIST and REPORT mode only */
5032 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5034 case LVS_LIST:
5035 nColumnWidth = infoPtr->nItemWidth;
5036 break;
5037 case LVS_REPORT:
5038 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5039 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5040 nColumnWidth = rcHeader.right - rcHeader.left;
5041 break;
5044 TRACE("nColumnWidth=%d\n", nColumnWidth);
5045 return nColumnWidth;
5048 /***
5049 * DESCRIPTION:
5050 * In list or report display mode, retrieves the number of items that can fit
5051 * vertically in the visible area. In icon or small icon display mode,
5052 * retrieves the total number of visible items.
5054 * PARAMETER(S):
5055 * [I] infoPtr : valid pointer to the listview structure
5057 * RETURN:
5058 * Number of fully visible items.
5060 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5062 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5064 case LVS_ICON:
5065 case LVS_SMALLICON:
5066 return infoPtr->nItemCount;
5067 case LVS_REPORT:
5068 return LISTVIEW_GetCountPerColumn(infoPtr);
5069 case LVS_LIST:
5070 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5072 assert(FALSE);
5073 return 0;
5076 /***
5077 * DESCRIPTION:
5078 * Retrieves an image list handle.
5080 * PARAMETER(S):
5081 * [I] infoPtr : valid pointer to the listview structure
5082 * [I] nImageList : image list identifier
5084 * RETURN:
5085 * SUCCESS : image list handle
5086 * FAILURE : NULL
5088 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5090 switch (nImageList)
5092 case LVSIL_NORMAL: return infoPtr->himlNormal;
5093 case LVSIL_SMALL: return infoPtr->himlSmall;
5094 case LVSIL_STATE: return infoPtr->himlState;
5096 return NULL;
5099 /* LISTVIEW_GetISearchString */
5101 /***
5102 * DESCRIPTION:
5103 * Retrieves item attributes.
5105 * PARAMETER(S):
5106 * [I] hwnd : window handle
5107 * [IO] lpLVItem : item info
5108 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5109 * if FALSE, the lpLVItem is a LPLVITEMA.
5111 * NOTE:
5112 * This is the internal 'GetItem' interface -- it tries to
5113 * be smart, and avoids text copies, if possible, by modifing
5114 * lpLVItem->pszText to point to the text string. Please note
5115 * that this is not always possible (e.g. OWNERDATA), so on
5116 * entry you *must* supply valid values for pszText, and cchTextMax.
5117 * The only difference to the documented interface is that upon
5118 * return, you should use *only* the lpLVItem->pszText, rather than
5119 * the buffer pointer you provided on input. Most code already does
5120 * that, so it's not a problem.
5121 * For the two cases when the text must be copied (that is,
5122 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5124 * RETURN:
5125 * SUCCESS : TRUE
5126 * FAILURE : FALSE
5128 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5130 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5131 NMLVDISPINFOW dispInfo;
5132 ITEM_INFO *lpItem;
5133 ITEMHDR* pItemHdr;
5134 HDPA hdpaSubItems;
5135 INT isubitem;
5137 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5139 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5140 return FALSE;
5142 if (lpLVItem->mask == 0) return TRUE;
5144 /* make a local copy */
5145 isubitem = lpLVItem->iSubItem;
5147 /* a quick optimization if all we're asked is the focus state
5148 * these queries are worth optimising since they are common,
5149 * and can be answered in constant time, without the heavy accesses */
5150 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5151 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5153 lpLVItem->state = 0;
5154 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5155 lpLVItem->state |= LVIS_FOCUSED;
5156 return TRUE;
5159 ZeroMemory(&dispInfo, sizeof(dispInfo));
5161 /* if the app stores all the data, handle it separately */
5162 if (infoPtr->dwStyle & LVS_OWNERDATA)
5164 dispInfo.item.state = 0;
5166 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5167 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5169 /* NOTE: copy only fields which we _know_ are initialized, some apps
5170 * depend on the uninitialized fields being 0 */
5171 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5172 dispInfo.item.iItem = lpLVItem->iItem;
5173 dispInfo.item.iSubItem = isubitem;
5174 if (lpLVItem->mask & LVIF_TEXT)
5176 dispInfo.item.pszText = lpLVItem->pszText;
5177 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5179 if (lpLVItem->mask & LVIF_STATE)
5180 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5181 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5182 dispInfo.item.stateMask = lpLVItem->stateMask;
5183 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5185 /* full size structure expected - _WIN32IE >= 0x560 */
5186 *lpLVItem = dispInfo.item;
5188 else if (lpLVItem->mask & LVIF_INDENT)
5190 /* indent member expected - _WIN32IE >= 0x300 */
5191 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5193 else
5195 /* minimal structure expected */
5196 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5198 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5201 /* make sure lParam is zeroed out */
5202 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5204 /* we store only a little state, so if we're not asked, we're done */
5205 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5207 /* if focus is handled by us, report it */
5208 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5210 lpLVItem->state &= ~LVIS_FOCUSED;
5211 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5212 lpLVItem->state |= LVIS_FOCUSED;
5215 /* and do the same for selection, if we handle it */
5216 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5218 lpLVItem->state &= ~LVIS_SELECTED;
5219 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5220 lpLVItem->state |= LVIS_SELECTED;
5223 return TRUE;
5226 /* find the item and subitem structures before we proceed */
5227 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5228 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5229 assert (lpItem);
5231 if (isubitem)
5233 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5234 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5235 if (!lpSubItem)
5237 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5238 isubitem = 0;
5241 else
5242 pItemHdr = &lpItem->hdr;
5244 /* Do we need to query the state from the app? */
5245 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5247 dispInfo.item.mask |= LVIF_STATE;
5248 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5251 /* Do we need to enquire about the image? */
5252 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5253 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5255 dispInfo.item.mask |= LVIF_IMAGE;
5256 dispInfo.item.iImage = I_IMAGECALLBACK;
5259 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5260 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5262 dispInfo.item.mask |= LVIF_TEXT;
5263 dispInfo.item.pszText = lpLVItem->pszText;
5264 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5265 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5266 *dispInfo.item.pszText = '\0';
5269 /* If we don't have all the requested info, query the application */
5270 if (dispInfo.item.mask != 0)
5272 dispInfo.item.iItem = lpLVItem->iItem;
5273 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5274 dispInfo.item.lParam = lpItem->lParam;
5275 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5276 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5279 /* we should not store values for subitems */
5280 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5282 /* Now, handle the iImage field */
5283 if (dispInfo.item.mask & LVIF_IMAGE)
5285 lpLVItem->iImage = dispInfo.item.iImage;
5286 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5287 pItemHdr->iImage = dispInfo.item.iImage;
5289 else if (lpLVItem->mask & LVIF_IMAGE)
5291 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5292 lpLVItem->iImage = pItemHdr->iImage;
5293 else
5294 lpLVItem->iImage = 0;
5297 /* The pszText field */
5298 if (dispInfo.item.mask & LVIF_TEXT)
5300 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5301 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5303 lpLVItem->pszText = dispInfo.item.pszText;
5305 else if (lpLVItem->mask & LVIF_TEXT)
5307 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5308 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5311 /* if this is a subitem, we're done */
5312 if (isubitem) return TRUE;
5314 /* Next is the lParam field */
5315 if (dispInfo.item.mask & LVIF_PARAM)
5317 lpLVItem->lParam = dispInfo.item.lParam;
5318 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5319 lpItem->lParam = dispInfo.item.lParam;
5321 else if (lpLVItem->mask & LVIF_PARAM)
5322 lpLVItem->lParam = lpItem->lParam;
5324 /* ... the state field (this one is different due to uCallbackmask) */
5325 if (lpLVItem->mask & LVIF_STATE)
5327 lpLVItem->state = lpItem->state;
5328 if (dispInfo.item.mask & LVIF_STATE)
5330 lpLVItem->state &= ~dispInfo.item.stateMask;
5331 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5333 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5335 lpLVItem->state &= ~LVIS_FOCUSED;
5336 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5337 lpLVItem->state |= LVIS_FOCUSED;
5339 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5341 lpLVItem->state &= ~LVIS_SELECTED;
5342 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5343 lpLVItem->state |= LVIS_SELECTED;
5347 /* and last, but not least, the indent field */
5348 if (lpLVItem->mask & LVIF_INDENT)
5349 lpLVItem->iIndent = lpItem->iIndent;
5351 return TRUE;
5354 /***
5355 * DESCRIPTION:
5356 * Retrieves item attributes.
5358 * PARAMETER(S):
5359 * [I] hwnd : window handle
5360 * [IO] lpLVItem : item info
5361 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5362 * if FALSE, the lpLVItem is a LPLVITEMA.
5364 * NOTE:
5365 * This is the external 'GetItem' interface -- it properly copies
5366 * the text in the provided buffer.
5368 * RETURN:
5369 * SUCCESS : TRUE
5370 * FAILURE : FALSE
5372 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5374 LPWSTR pszText;
5375 BOOL bResult;
5377 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5378 return FALSE;
5380 pszText = lpLVItem->pszText;
5381 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5382 if (bResult && lpLVItem->pszText != pszText)
5383 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5384 lpLVItem->pszText = pszText;
5386 return bResult;
5390 /***
5391 * DESCRIPTION:
5392 * Retrieves the position (upper-left) of the listview control item.
5393 * Note that for LVS_ICON style, the upper-left is that of the icon
5394 * and not the bounding box.
5396 * PARAMETER(S):
5397 * [I] infoPtr : valid pointer to the listview structure
5398 * [I] nItem : item index
5399 * [O] lpptPosition : coordinate information
5401 * RETURN:
5402 * SUCCESS : TRUE
5403 * FAILURE : FALSE
5405 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5407 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5408 POINT Origin;
5410 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5412 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5414 LISTVIEW_GetOrigin(infoPtr, &Origin);
5415 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5417 if (uView == LVS_ICON)
5419 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5420 lpptPosition->y += ICON_TOP_PADDING;
5422 lpptPosition->x += Origin.x;
5423 lpptPosition->y += Origin.y;
5425 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5426 return TRUE;
5430 /***
5431 * DESCRIPTION:
5432 * Retrieves the bounding rectangle for a listview control item.
5434 * PARAMETER(S):
5435 * [I] infoPtr : valid pointer to the listview structure
5436 * [I] nItem : item index
5437 * [IO] lprc : bounding rectangle coordinates
5438 * lprc->left specifies the portion of the item for which the bounding
5439 * rectangle will be retrieved.
5441 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5442 * including the icon and label.
5444 * * For LVS_ICON
5445 * * Experiment shows that native control returns:
5446 * * width = min (48, length of text line)
5447 * * .left = position.x - (width - iconsize.cx)/2
5448 * * .right = .left + width
5449 * * height = #lines of text * ntmHeight + icon height + 8
5450 * * .top = position.y - 2
5451 * * .bottom = .top + height
5452 * * separation between items .y = itemSpacing.cy - height
5453 * * .x = itemSpacing.cx - width
5454 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5456 * * For LVS_ICON
5457 * * Experiment shows that native control returns:
5458 * * width = iconSize.cx + 16
5459 * * .left = position.x - (width - iconsize.cx)/2
5460 * * .right = .left + width
5461 * * height = iconSize.cy + 4
5462 * * .top = position.y - 2
5463 * * .bottom = .top + height
5464 * * separation between items .y = itemSpacing.cy - height
5465 * * .x = itemSpacing.cx - width
5466 * LVIR_LABEL Returns the bounding rectangle of the item text.
5468 * * For LVS_ICON
5469 * * Experiment shows that native control returns:
5470 * * width = text length
5471 * * .left = position.x - width/2
5472 * * .right = .left + width
5473 * * height = ntmH * linecount + 2
5474 * * .top = position.y + iconSize.cy + 6
5475 * * .bottom = .top + height
5476 * * separation between items .y = itemSpacing.cy - height
5477 * * .x = itemSpacing.cx - width
5478 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5479 * rectangles, but excludes columns in report view.
5481 * RETURN:
5482 * SUCCESS : TRUE
5483 * FAILURE : FALSE
5485 * NOTES
5486 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5487 * upon whether the window has the focus currently and on whether the item
5488 * is the one with the focus. Ensure that the control's record of which
5489 * item has the focus agrees with the items' records.
5491 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5493 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5494 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5495 BOOL doLabel = TRUE, oversizedBox = FALSE;
5496 POINT Position, Origin;
5497 LVITEMW lvItem;
5498 RECT label_rect;
5500 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5502 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5504 LISTVIEW_GetOrigin(infoPtr, &Origin);
5505 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5507 /* Be smart and try to figure out the minimum we have to do */
5508 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5509 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5510 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5511 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5512 oversizedBox = TRUE;
5514 /* get what we need from the item before hand, so we make
5515 * only one request. This can speed up things, if data
5516 * is stored on the app side */
5517 lvItem.mask = 0;
5518 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5519 if (doLabel) lvItem.mask |= LVIF_TEXT;
5520 lvItem.iItem = nItem;
5521 lvItem.iSubItem = 0;
5522 lvItem.pszText = szDispText;
5523 lvItem.cchTextMax = DISP_TEXT_SIZE;
5524 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5525 /* we got the state already up, simulate it here, to avoid a reget */
5526 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5528 lvItem.mask |= LVIF_STATE;
5529 lvItem.stateMask = LVIS_FOCUSED;
5530 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5533 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5534 lprc->left = LVIR_BOUNDS;
5535 switch(lprc->left)
5537 case LVIR_ICON:
5538 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5539 break;
5541 case LVIR_LABEL:
5542 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5543 break;
5545 case LVIR_BOUNDS:
5546 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5547 break;
5549 case LVIR_SELECTBOUNDS:
5550 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5551 UnionRect(lprc, lprc, &label_rect);
5552 break;
5554 default:
5555 WARN("Unknown value: %ld\n", lprc->left);
5556 return FALSE;
5559 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5561 TRACE(" rect=%s\n", debugrect(lprc));
5563 return TRUE;
5566 /***
5567 * DESCRIPTION:
5568 * Retrieves the spacing between listview control items.
5570 * PARAMETER(S):
5571 * [I] infoPtr : valid pointer to the listview structure
5572 * [IO] lprc : rectangle to receive the output
5573 * on input, lprc->top = nSubItem
5574 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5576 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5577 * not only those of the first column.
5578 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5580 * RETURN:
5581 * TRUE: success
5582 * FALSE: failure
5584 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5586 POINT Position;
5587 LVITEMW lvItem;
5589 if (!lprc) return FALSE;
5591 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5592 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5593 if (lprc->top == 0)
5594 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5596 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5598 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5600 lvItem.mask = 0;
5601 lvItem.iItem = nItem;
5602 lvItem.iSubItem = lprc->top;
5604 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5605 switch(lprc->left)
5607 case LVIR_ICON:
5608 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5609 break;
5611 case LVIR_LABEL:
5612 case LVIR_BOUNDS:
5613 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5614 break;
5616 default:
5617 ERR("Unknown bounds=%ld\n", lprc->left);
5618 return FALSE;
5621 OffsetRect(lprc, Position.x, Position.y);
5622 return TRUE;
5626 /***
5627 * DESCRIPTION:
5628 * Retrieves the width of a label.
5630 * PARAMETER(S):
5631 * [I] infoPtr : valid pointer to the listview structure
5633 * RETURN:
5634 * SUCCESS : string width (in pixels)
5635 * FAILURE : zero
5637 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5639 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5640 LVITEMW lvItem;
5642 TRACE("(nItem=%d)\n", nItem);
5644 lvItem.mask = LVIF_TEXT;
5645 lvItem.iItem = nItem;
5646 lvItem.iSubItem = 0;
5647 lvItem.pszText = szDispText;
5648 lvItem.cchTextMax = DISP_TEXT_SIZE;
5649 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5651 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5654 /***
5655 * DESCRIPTION:
5656 * Retrieves the spacing between listview control items.
5658 * PARAMETER(S):
5659 * [I] infoPtr : valid pointer to the listview structure
5660 * [I] bSmall : flag for small or large icon
5662 * RETURN:
5663 * Horizontal + vertical spacing
5665 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5667 LONG lResult;
5669 if (!bSmall)
5671 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5673 else
5675 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5676 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5677 else
5678 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5680 return lResult;
5683 /***
5684 * DESCRIPTION:
5685 * Retrieves the state of a listview control item.
5687 * PARAMETER(S):
5688 * [I] infoPtr : valid pointer to the listview structure
5689 * [I] nItem : item index
5690 * [I] uMask : state mask
5692 * RETURN:
5693 * State specified by the mask.
5695 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5697 LVITEMW lvItem;
5699 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5701 lvItem.iItem = nItem;
5702 lvItem.iSubItem = 0;
5703 lvItem.mask = LVIF_STATE;
5704 lvItem.stateMask = uMask;
5705 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5707 return lvItem.state & uMask;
5710 /***
5711 * DESCRIPTION:
5712 * Retrieves the text of a listview control item or subitem.
5714 * PARAMETER(S):
5715 * [I] hwnd : window handle
5716 * [I] nItem : item index
5717 * [IO] lpLVItem : item information
5718 * [I] isW : TRUE if lpLVItem is Unicode
5720 * RETURN:
5721 * SUCCESS : string length
5722 * FAILURE : 0
5724 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5726 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5728 lpLVItem->mask = LVIF_TEXT;
5729 lpLVItem->iItem = nItem;
5730 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5732 return textlenT(lpLVItem->pszText, isW);
5735 /***
5736 * DESCRIPTION:
5737 * Searches for an item based on properties + relationships.
5739 * PARAMETER(S):
5740 * [I] infoPtr : valid pointer to the listview structure
5741 * [I] nItem : item index
5742 * [I] uFlags : relationship flag
5744 * RETURN:
5745 * SUCCESS : item index
5746 * FAILURE : -1
5748 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5750 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5751 UINT uMask = 0;
5752 LVFINDINFOW lvFindInfo;
5753 INT nCountPerColumn;
5754 INT nCountPerRow;
5755 INT i;
5757 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5758 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5760 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5762 if (uFlags & LVNI_CUT)
5763 uMask |= LVIS_CUT;
5765 if (uFlags & LVNI_DROPHILITED)
5766 uMask |= LVIS_DROPHILITED;
5768 if (uFlags & LVNI_FOCUSED)
5769 uMask |= LVIS_FOCUSED;
5771 if (uFlags & LVNI_SELECTED)
5772 uMask |= LVIS_SELECTED;
5774 /* if we're asked for the focused item, that's only one,
5775 * so it's worth optimizing */
5776 if (uFlags & LVNI_FOCUSED)
5778 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5779 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5782 if (uFlags & LVNI_ABOVE)
5784 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5786 while (nItem >= 0)
5788 nItem--;
5789 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5790 return nItem;
5793 else
5795 /* Special case for autoarrange - move 'til the top of a list */
5796 if (is_autoarrange(infoPtr))
5798 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5799 while (nItem - nCountPerRow >= 0)
5801 nItem -= nCountPerRow;
5802 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5803 return nItem;
5805 return -1;
5807 lvFindInfo.flags = LVFI_NEARESTXY;
5808 lvFindInfo.vkDirection = VK_UP;
5809 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5810 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5812 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5813 return nItem;
5817 else if (uFlags & LVNI_BELOW)
5819 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5821 while (nItem < infoPtr->nItemCount)
5823 nItem++;
5824 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5825 return nItem;
5828 else
5830 /* Special case for autoarrange - move 'til the bottom of a list */
5831 if (is_autoarrange(infoPtr))
5833 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5834 while (nItem + nCountPerRow < infoPtr->nItemCount )
5836 nItem += nCountPerRow;
5837 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5838 return nItem;
5840 return -1;
5842 lvFindInfo.flags = LVFI_NEARESTXY;
5843 lvFindInfo.vkDirection = VK_DOWN;
5844 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5845 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5847 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5848 return nItem;
5852 else if (uFlags & LVNI_TOLEFT)
5854 if (uView == LVS_LIST)
5856 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5857 while (nItem - nCountPerColumn >= 0)
5859 nItem -= nCountPerColumn;
5860 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5861 return nItem;
5864 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5866 /* Special case for autoarrange - move 'ti the beginning of a row */
5867 if (is_autoarrange(infoPtr))
5869 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5870 while (nItem % nCountPerRow > 0)
5872 nItem --;
5873 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5874 return nItem;
5876 return -1;
5878 lvFindInfo.flags = LVFI_NEARESTXY;
5879 lvFindInfo.vkDirection = VK_LEFT;
5880 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5881 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5883 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5884 return nItem;
5888 else if (uFlags & LVNI_TORIGHT)
5890 if (uView == LVS_LIST)
5892 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5893 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5895 nItem += nCountPerColumn;
5896 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5897 return nItem;
5900 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5902 /* Special case for autoarrange - move 'til the end of a row */
5903 if (is_autoarrange(infoPtr))
5905 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5906 while (nItem % nCountPerRow < nCountPerRow - 1 )
5908 nItem ++;
5909 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5910 return nItem;
5912 return -1;
5914 lvFindInfo.flags = LVFI_NEARESTXY;
5915 lvFindInfo.vkDirection = VK_RIGHT;
5916 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5917 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5919 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5920 return nItem;
5924 else
5926 nItem++;
5928 /* search by index */
5929 for (i = nItem; i < infoPtr->nItemCount; i++)
5931 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5932 return i;
5936 return -1;
5939 /* LISTVIEW_GetNumberOfWorkAreas */
5941 /***
5942 * DESCRIPTION:
5943 * Retrieves the origin coordinates when in icon or small icon display mode.
5945 * PARAMETER(S):
5946 * [I] infoPtr : valid pointer to the listview structure
5947 * [O] lpptOrigin : coordinate information
5949 * RETURN:
5950 * None.
5952 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5954 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5955 INT nHorzPos = 0, nVertPos = 0;
5956 SCROLLINFO scrollInfo;
5958 scrollInfo.cbSize = sizeof(SCROLLINFO);
5959 scrollInfo.fMask = SIF_POS;
5961 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5962 nHorzPos = scrollInfo.nPos;
5963 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5964 nVertPos = scrollInfo.nPos;
5966 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5968 lpptOrigin->x = infoPtr->rcList.left;
5969 lpptOrigin->y = infoPtr->rcList.top;
5970 if (uView == LVS_LIST)
5971 nHorzPos *= infoPtr->nItemWidth;
5972 else if (uView == LVS_REPORT)
5973 nVertPos *= infoPtr->nItemHeight;
5975 lpptOrigin->x -= nHorzPos;
5976 lpptOrigin->y -= nVertPos;
5978 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5981 /***
5982 * DESCRIPTION:
5983 * Retrieves the width of a string.
5985 * PARAMETER(S):
5986 * [I] hwnd : window handle
5987 * [I] lpszText : text string to process
5988 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5990 * RETURN:
5991 * SUCCESS : string width (in pixels)
5992 * FAILURE : zero
5994 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5996 SIZE stringSize;
5998 stringSize.cx = 0;
5999 if (is_textT(lpszText, isW))
6001 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6002 HDC hdc = GetDC(infoPtr->hwndSelf);
6003 HFONT hOldFont = SelectObject(hdc, hFont);
6005 if (isW)
6006 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6007 else
6008 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6009 SelectObject(hdc, hOldFont);
6010 ReleaseDC(infoPtr->hwndSelf, hdc);
6012 return stringSize.cx;
6015 /***
6016 * DESCRIPTION:
6017 * Determines which listview item is located at the specified position.
6019 * PARAMETER(S):
6020 * [I] infoPtr : valid pointer to the listview structure
6021 * [IO] lpht : hit test information
6022 * [I] subitem : fill out iSubItem.
6023 * [I] select : return the index only if the hit selects the item
6025 * NOTE:
6026 * (mm 20001022): We must not allow iSubItem to be touched, for
6027 * an app might pass only a structure with space up to iItem!
6028 * (MS Office 97 does that for instance in the file open dialog)
6030 * RETURN:
6031 * SUCCESS : item index
6032 * FAILURE : -1
6034 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6036 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6037 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6038 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6039 POINT Origin, Position, opt;
6040 LVITEMW lvItem;
6041 ITERATOR i;
6042 INT iItem;
6044 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
6046 lpht->flags = 0;
6047 lpht->iItem = -1;
6048 if (subitem) lpht->iSubItem = 0;
6050 if (infoPtr->rcList.left > lpht->pt.x)
6051 lpht->flags |= LVHT_TOLEFT;
6052 else if (infoPtr->rcList.right < lpht->pt.x)
6053 lpht->flags |= LVHT_TORIGHT;
6055 if (infoPtr->rcList.top > lpht->pt.y)
6056 lpht->flags |= LVHT_ABOVE;
6057 else if (infoPtr->rcList.bottom < lpht->pt.y)
6058 lpht->flags |= LVHT_BELOW;
6060 TRACE("lpht->flags=0x%x\n", lpht->flags);
6061 if (lpht->flags) return -1;
6063 lpht->flags |= LVHT_NOWHERE;
6065 LISTVIEW_GetOrigin(infoPtr, &Origin);
6067 /* first deal with the large items */
6068 rcSearch.left = lpht->pt.x;
6069 rcSearch.top = lpht->pt.y;
6070 rcSearch.right = rcSearch.left + 1;
6071 rcSearch.bottom = rcSearch.top + 1;
6073 iterator_frameditems(&i, infoPtr, &rcSearch);
6074 iterator_next(&i); /* go to first item in the sequence */
6075 iItem = i.nItem;
6076 iterator_destroy(&i);
6078 TRACE("lpht->iItem=%d\n", iItem);
6079 if (iItem == -1) return -1;
6081 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6082 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6083 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6084 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6085 lvItem.iItem = iItem;
6086 lvItem.iSubItem = 0;
6087 lvItem.pszText = szDispText;
6088 lvItem.cchTextMax = DISP_TEXT_SIZE;
6089 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6090 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6092 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6093 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6094 opt.x = lpht->pt.x - Position.x - Origin.x;
6095 opt.y = lpht->pt.y - Position.y - Origin.y;
6097 if (uView == LVS_REPORT)
6098 rcBounds = rcBox;
6099 else
6100 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6101 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
6102 if (!PtInRect(&rcBounds, opt)) return -1;
6104 if (PtInRect(&rcIcon, opt))
6105 lpht->flags |= LVHT_ONITEMICON;
6106 else if (PtInRect(&rcLabel, opt))
6107 lpht->flags |= LVHT_ONITEMLABEL;
6108 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6109 lpht->flags |= LVHT_ONITEMSTATEICON;
6110 if (lpht->flags & LVHT_ONITEM)
6111 lpht->flags &= ~LVHT_NOWHERE;
6113 TRACE("lpht->flags=0x%x\n", lpht->flags);
6114 if (uView == LVS_REPORT && subitem)
6116 INT j;
6118 rcBounds.right = rcBounds.left;
6119 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6121 rcBounds.left = rcBounds.right;
6122 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6123 if (PtInRect(&rcBounds, opt))
6125 lpht->iSubItem = j;
6126 break;
6131 if (select && !(uView == LVS_REPORT &&
6132 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6133 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6135 if (uView == LVS_REPORT)
6137 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6138 UnionRect(&rcBounds, &rcBounds, &rcState);
6140 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6142 return lpht->iItem = iItem;
6146 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6147 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6148 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6149 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6150 their own sort proc. when sending LVM_SORTITEMS.
6152 /* Platform SDK:
6153 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6155 LVS_SORTXXX must be specified,
6156 LVS_OWNERDRAW is not set,
6157 <item>.pszText is not LPSTR_TEXTCALLBACK.
6159 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6160 are sorted based on item text..."
6162 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6164 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6165 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6166 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6168 /* if we're sorting descending, negate the return value */
6169 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6172 /***
6173 * DESCRIPTION:
6174 * Inserts a new item in the listview control.
6176 * PARAMETER(S):
6177 * [I] infoPtr : valid pointer to the listview structure
6178 * [I] lpLVItem : item information
6179 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6181 * RETURN:
6182 * SUCCESS : new item index
6183 * FAILURE : -1
6185 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6187 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6188 INT nItem;
6189 HDPA hdpaSubItems;
6190 NMLISTVIEW nmlv;
6191 ITEM_INFO *lpItem;
6192 BOOL is_sorted, has_changed;
6193 LVITEMW item;
6195 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6197 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6199 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6200 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6202 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6204 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6206 /* insert item in listview control data structure */
6207 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6208 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6210 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6211 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6213 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6214 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6215 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6216 if (nItem == -1) goto fail;
6217 infoPtr->nItemCount++;
6219 /* shift indices first so they don't get tangled */
6220 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6222 /* set the item attributes */
6223 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6225 /* full size structure expected - _WIN32IE >= 0x560 */
6226 item = *lpLVItem;
6228 else if (lpLVItem->mask & LVIF_INDENT)
6230 /* indent member expected - _WIN32IE >= 0x300 */
6231 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6233 else
6235 /* minimal structure expected */
6236 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6238 item.iItem = nItem;
6239 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6240 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6242 /* if we're sorted, sort the list, and update the index */
6243 if (is_sorted)
6245 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6246 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6247 assert(nItem != -1);
6250 /* make room for the position, if we are in the right mode */
6251 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6253 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6254 goto undo;
6255 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6257 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6258 goto undo;
6262 /* send LVN_INSERTITEM notification */
6263 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6264 nmlv.iItem = nItem;
6265 nmlv.lParam = lpItem->lParam;
6266 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6268 /* align items (set position of each item) */
6269 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6271 POINT pt;
6273 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6274 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6275 else
6276 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6278 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6281 /* now is the invalidation fun */
6282 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6283 return nItem;
6285 undo:
6286 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6287 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6288 infoPtr->nItemCount--;
6289 fail:
6290 DPA_DeletePtr(hdpaSubItems, 0);
6291 DPA_Destroy (hdpaSubItems);
6292 Free (lpItem);
6293 return -1;
6296 /***
6297 * DESCRIPTION:
6298 * Redraws a range of items.
6300 * PARAMETER(S):
6301 * [I] infoPtr : valid pointer to the listview structure
6302 * [I] nFirst : first item
6303 * [I] nLast : last item
6305 * RETURN:
6306 * SUCCESS : TRUE
6307 * FAILURE : FALSE
6309 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6311 INT i;
6313 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6314 max(nFirst, nLast) >= infoPtr->nItemCount)
6315 return FALSE;
6317 for (i = nFirst; i <= nLast; i++)
6318 LISTVIEW_InvalidateItem(infoPtr, i);
6320 return TRUE;
6323 /***
6324 * DESCRIPTION:
6325 * Scroll the content of a listview.
6327 * PARAMETER(S):
6328 * [I] infoPtr : valid pointer to the listview structure
6329 * [I] dx : horizontal scroll amount in pixels
6330 * [I] dy : vertical scroll amount in pixels
6332 * RETURN:
6333 * SUCCESS : TRUE
6334 * FAILURE : FALSE
6336 * COMMENTS:
6337 * If the control is in report mode (LVS_REPORT) the control can
6338 * be scrolled only in line increments. "dy" will be rounded to the
6339 * nearest number of pixels that are a whole line. Ex: if line height
6340 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6341 * is passed the the scroll will be 0. (per MSDN 7/2002)
6343 * For: (per experimentaion with native control and CSpy ListView)
6344 * LVS_ICON dy=1 = 1 pixel (vertical only)
6345 * dx ignored
6346 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6347 * dx ignored
6348 * LVS_LIST dx=1 = 1 column (horizontal only)
6349 * but will only scroll 1 column per message
6350 * no matter what the value.
6351 * dy must be 0 or FALSE returned.
6352 * LVS_REPORT dx=1 = 1 pixel
6353 * dy= see above
6356 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6358 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6359 case LVS_REPORT:
6360 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6361 dy /= infoPtr->nItemHeight;
6362 break;
6363 case LVS_LIST:
6364 if (dy != 0) return FALSE;
6365 break;
6366 default: /* icon */
6367 dx = 0;
6368 break;
6371 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6372 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6374 return TRUE;
6377 /***
6378 * DESCRIPTION:
6379 * Sets the background color.
6381 * PARAMETER(S):
6382 * [I] infoPtr : valid pointer to the listview structure
6383 * [I] clrBk : background color
6385 * RETURN:
6386 * SUCCESS : TRUE
6387 * FAILURE : FALSE
6389 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6391 TRACE("(clrBk=%lx)\n", clrBk);
6393 if(infoPtr->clrBk != clrBk) {
6394 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6395 infoPtr->clrBk = clrBk;
6396 if (clrBk == CLR_NONE)
6397 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6398 else
6399 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6400 LISTVIEW_InvalidateList(infoPtr);
6403 return TRUE;
6406 /* LISTVIEW_SetBkImage */
6408 /*** Helper for {Insert,Set}ColumnT *only* */
6409 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6411 if (lpColumn->mask & LVCF_FMT)
6413 /* format member is valid */
6414 lphdi->mask |= HDI_FORMAT;
6416 /* set text alignment (leftmost column must be left-aligned) */
6417 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6418 lphdi->fmt |= HDF_LEFT;
6419 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6420 lphdi->fmt |= HDF_RIGHT;
6421 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6422 lphdi->fmt |= HDF_CENTER;
6424 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6425 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6427 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6429 lphdi->fmt |= HDF_IMAGE;
6430 lphdi->iImage = I_IMAGECALLBACK;
6434 if (lpColumn->mask & LVCF_WIDTH)
6436 lphdi->mask |= HDI_WIDTH;
6437 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6439 /* make it fill the remainder of the controls width */
6440 RECT rcHeader;
6441 INT item_index;
6443 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6445 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6446 lphdi->cxy += rcHeader.right - rcHeader.left;
6449 /* retrieve the layout of the header */
6450 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6451 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6453 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6455 else
6456 lphdi->cxy = lpColumn->cx;
6459 if (lpColumn->mask & LVCF_TEXT)
6461 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6462 lphdi->fmt |= HDF_STRING;
6463 lphdi->pszText = lpColumn->pszText;
6464 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6467 if (lpColumn->mask & LVCF_IMAGE)
6469 lphdi->mask |= HDI_IMAGE;
6470 lphdi->iImage = lpColumn->iImage;
6473 if (lpColumn->mask & LVCF_ORDER)
6475 lphdi->mask |= HDI_ORDER;
6476 lphdi->iOrder = lpColumn->iOrder;
6481 /***
6482 * DESCRIPTION:
6483 * Inserts a new column.
6485 * PARAMETER(S):
6486 * [I] infoPtr : valid pointer to the listview structure
6487 * [I] nColumn : column index
6488 * [I] lpColumn : column information
6489 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6491 * RETURN:
6492 * SUCCESS : new column index
6493 * FAILURE : -1
6495 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6496 const LVCOLUMNW *lpColumn, BOOL isW)
6498 COLUMN_INFO *lpColumnInfo;
6499 INT nNewColumn;
6500 HDITEMW hdi;
6502 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6504 if (!lpColumn || nColumn < 0) return -1;
6505 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6507 ZeroMemory(&hdi, sizeof(HDITEMW));
6508 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6510 /* insert item in header control */
6511 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6512 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6513 (WPARAM)nColumn, (LPARAM)&hdi);
6514 if (nNewColumn == -1) return -1;
6515 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6517 /* create our own column info */
6518 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6519 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6521 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6522 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6524 /* now we have to actually adjust the data */
6525 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6527 SUBITEM_INFO *lpSubItem;
6528 HDPA hdpaSubItems;
6529 INT nItem, i;
6531 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6533 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6534 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6536 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6537 if (lpSubItem->iSubItem >= nNewColumn)
6538 lpSubItem->iSubItem++;
6543 /* make space for the new column */
6544 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6546 return nNewColumn;
6548 fail:
6549 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6550 if (lpColumnInfo)
6552 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6553 Free(lpColumnInfo);
6555 return -1;
6558 /***
6559 * DESCRIPTION:
6560 * Sets the attributes of a header item.
6562 * PARAMETER(S):
6563 * [I] infoPtr : valid pointer to the listview structure
6564 * [I] nColumn : column index
6565 * [I] lpColumn : column attributes
6566 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6568 * RETURN:
6569 * SUCCESS : TRUE
6570 * FAILURE : FALSE
6572 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6573 const LVCOLUMNW *lpColumn, BOOL isW)
6575 HDITEMW hdi, hdiget;
6576 BOOL bResult;
6578 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6580 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6582 ZeroMemory(&hdi, sizeof(HDITEMW));
6583 if (lpColumn->mask & LVCF_FMT)
6585 hdi.mask |= HDI_FORMAT;
6586 hdiget.mask = HDI_FORMAT;
6587 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6588 hdi.fmt = hdiget.fmt & HDF_STRING;
6590 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6592 /* set header item attributes */
6593 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6594 if (!bResult) return FALSE;
6596 if (lpColumn->mask & LVCF_FMT)
6598 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6599 int oldFmt = lpColumnInfo->fmt;
6601 lpColumnInfo->fmt = lpColumn->fmt;
6602 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6604 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6605 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6609 return TRUE;
6612 /***
6613 * DESCRIPTION:
6614 * Sets the column order array
6616 * PARAMETERS:
6617 * [I] infoPtr : valid pointer to the listview structure
6618 * [I] iCount : number of elements in column order array
6619 * [I] lpiArray : pointer to column order array
6621 * RETURN:
6622 * SUCCESS : TRUE
6623 * FAILURE : FALSE
6625 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6627 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6629 if (!lpiArray)
6630 return FALSE;
6632 return TRUE;
6636 /***
6637 * DESCRIPTION:
6638 * Sets the width of a column
6640 * PARAMETERS:
6641 * [I] infoPtr : valid pointer to the listview structure
6642 * [I] nColumn : column index
6643 * [I] cx : column width
6645 * RETURN:
6646 * SUCCESS : TRUE
6647 * FAILURE : FALSE
6649 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6651 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6652 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6653 INT max_cx = 0;
6654 HDITEMW hdi;
6656 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6658 /* set column width only if in report or list mode */
6659 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6661 /* take care of invalid cx values */
6662 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6663 else if (uView == LVS_LIST && cx < 1) return FALSE;
6665 /* resize all columns if in LVS_LIST mode */
6666 if(uView == LVS_LIST)
6668 infoPtr->nItemWidth = cx;
6669 LISTVIEW_InvalidateList(infoPtr);
6670 return TRUE;
6673 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6675 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6677 INT nLabelWidth;
6678 LVITEMW lvItem;
6680 lvItem.mask = LVIF_TEXT;
6681 lvItem.iItem = 0;
6682 lvItem.iSubItem = nColumn;
6683 lvItem.pszText = szDispText;
6684 lvItem.cchTextMax = DISP_TEXT_SIZE;
6685 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6687 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6688 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6689 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6691 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6692 max_cx += infoPtr->iconSize.cx;
6693 max_cx += TRAILING_LABEL_PADDING;
6696 /* autosize based on listview items width */
6697 if(cx == LVSCW_AUTOSIZE)
6698 cx = max_cx;
6699 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6701 /* if iCol is the last column make it fill the remainder of the controls width */
6702 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6704 RECT rcHeader;
6705 POINT Origin;
6707 LISTVIEW_GetOrigin(infoPtr, &Origin);
6708 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6710 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6712 else
6714 /* Despite what the MS docs say, if this is not the last
6715 column, then MS resizes the column to the width of the
6716 largest text string in the column, including headers
6717 and items. This is different from LVSCW_AUTOSIZE in that
6718 LVSCW_AUTOSIZE ignores the header string length. */
6719 cx = 0;
6721 /* retrieve header text */
6722 hdi.mask = HDI_TEXT;
6723 hdi.cchTextMax = DISP_TEXT_SIZE;
6724 hdi.pszText = szDispText;
6725 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6727 HDC hdc = GetDC(infoPtr->hwndSelf);
6728 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6729 SIZE size;
6731 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6732 cx = size.cx + TRAILING_HEADER_PADDING;
6733 /* FIXME: Take into account the header image, if one is present */
6734 SelectObject(hdc, old_font);
6735 ReleaseDC(infoPtr->hwndSelf, hdc);
6737 cx = max (cx, max_cx);
6741 if (cx < 0) return FALSE;
6743 /* call header to update the column change */
6744 hdi.mask = HDI_WIDTH;
6745 hdi.cxy = cx;
6746 TRACE("hdi.cxy=%d\n", hdi.cxy);
6747 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6750 /***
6751 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6754 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6756 HDC hdc_wnd, hdc;
6757 HBITMAP hbm_im, hbm_mask, hbm_orig;
6758 RECT rc;
6759 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6760 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6761 HIMAGELIST himl;
6763 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6764 ILC_COLOR | ILC_MASK, 2, 2);
6765 hdc_wnd = GetDC(infoPtr->hwndSelf);
6766 hdc = CreateCompatibleDC(hdc_wnd);
6767 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6768 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6769 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6771 rc.left = rc.top = 0;
6772 rc.right = GetSystemMetrics(SM_CXSMICON);
6773 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6775 hbm_orig = SelectObject(hdc, hbm_mask);
6776 FillRect(hdc, &rc, hbr_white);
6777 InflateRect(&rc, -3, -3);
6778 FillRect(hdc, &rc, hbr_black);
6780 SelectObject(hdc, hbm_im);
6781 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6782 SelectObject(hdc, hbm_orig);
6783 ImageList_Add(himl, hbm_im, hbm_mask);
6785 SelectObject(hdc, hbm_im);
6786 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6787 SelectObject(hdc, hbm_orig);
6788 ImageList_Add(himl, hbm_im, hbm_mask);
6790 DeleteObject(hbm_mask);
6791 DeleteObject(hbm_im);
6792 DeleteDC(hdc);
6794 return himl;
6797 /***
6798 * DESCRIPTION:
6799 * Sets the extended listview style.
6801 * PARAMETERS:
6802 * [I] infoPtr : valid pointer to the listview structure
6803 * [I] dwMask : mask
6804 * [I] dwStyle : style
6806 * RETURN:
6807 * SUCCESS : previous style
6808 * FAILURE : 0
6810 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6812 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6814 /* set new style */
6815 if (dwMask)
6816 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6817 else
6818 infoPtr->dwLvExStyle = dwStyle;
6820 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6822 HIMAGELIST himl = 0;
6823 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6824 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6825 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6828 return dwOldStyle;
6831 /***
6832 * DESCRIPTION:
6833 * Sets the new hot cursor used during hot tracking and hover selection.
6835 * PARAMETER(S):
6836 * [I] infoPtr : valid pointer to the listview structure
6837 * [I] hCursor : the new hot cursor handle
6839 * RETURN:
6840 * Returns the previous hot cursor
6842 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6844 HCURSOR oldCursor = infoPtr->hHotCursor;
6846 infoPtr->hHotCursor = hCursor;
6848 return oldCursor;
6852 /***
6853 * DESCRIPTION:
6854 * Sets the hot item index.
6856 * PARAMETERS:
6857 * [I] infoPtr : valid pointer to the listview structure
6858 * [I] iIndex : index
6860 * RETURN:
6861 * SUCCESS : previous hot item index
6862 * FAILURE : -1 (no hot item)
6864 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6866 INT iOldIndex = infoPtr->nHotItem;
6868 infoPtr->nHotItem = iIndex;
6870 return iOldIndex;
6874 /***
6875 * DESCRIPTION:
6876 * Sets the amount of time the cursor must hover over an item before it is selected.
6878 * PARAMETER(S):
6879 * [I] infoPtr : valid pointer to the listview structure
6880 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6882 * RETURN:
6883 * Returns the previous hover time
6885 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6887 DWORD oldHoverTime = infoPtr->dwHoverTime;
6889 infoPtr->dwHoverTime = dwHoverTime;
6891 return oldHoverTime;
6894 /***
6895 * DESCRIPTION:
6896 * Sets spacing for icons of LVS_ICON style.
6898 * PARAMETER(S):
6899 * [I] infoPtr : valid pointer to the listview structure
6900 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6901 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6903 * RETURN:
6904 * MAKELONG(oldcx, oldcy)
6906 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6908 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6909 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6911 TRACE("requested=(%d,%d)\n", cx, cy);
6913 /* this is supported only for LVS_ICON style */
6914 if (uView != LVS_ICON) return oldspacing;
6916 /* set to defaults, if instructed to */
6917 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6918 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6920 /* if 0 then compute width
6921 * FIXME: Should scan each item and determine max width of
6922 * icon or label, then make that the width */
6923 if (cx == 0)
6924 cx = infoPtr->iconSpacing.cx;
6926 /* if 0 then compute height */
6927 if (cy == 0)
6928 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6929 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6932 infoPtr->iconSpacing.cx = cx;
6933 infoPtr->iconSpacing.cy = cy;
6935 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6936 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6937 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6938 infoPtr->ntmHeight);
6940 /* these depend on the iconSpacing */
6941 LISTVIEW_UpdateItemSize(infoPtr);
6943 return oldspacing;
6946 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6948 INT cx, cy;
6950 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6952 size->cx = cx;
6953 size->cy = cy;
6955 else
6957 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6958 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6962 /***
6963 * DESCRIPTION:
6964 * Sets image lists.
6966 * PARAMETER(S):
6967 * [I] infoPtr : valid pointer to the listview structure
6968 * [I] nType : image list type
6969 * [I] himl : image list handle
6971 * RETURN:
6972 * SUCCESS : old image list
6973 * FAILURE : NULL
6975 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6977 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6978 INT oldHeight = infoPtr->nItemHeight;
6979 HIMAGELIST himlOld = 0;
6981 TRACE("(nType=%d, himl=%p\n", nType, himl);
6983 switch (nType)
6985 case LVSIL_NORMAL:
6986 himlOld = infoPtr->himlNormal;
6987 infoPtr->himlNormal = himl;
6988 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6989 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6990 break;
6992 case LVSIL_SMALL:
6993 himlOld = infoPtr->himlSmall;
6994 infoPtr->himlSmall = himl;
6995 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6996 break;
6998 case LVSIL_STATE:
6999 himlOld = infoPtr->himlState;
7000 infoPtr->himlState = himl;
7001 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7002 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7003 break;
7005 default:
7006 ERR("Unknown icon type=%d\n", nType);
7007 return NULL;
7010 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7011 if (infoPtr->nItemHeight != oldHeight)
7012 LISTVIEW_UpdateScroll(infoPtr);
7014 return himlOld;
7017 /***
7018 * DESCRIPTION:
7019 * Preallocates memory (does *not* set the actual count of items !)
7021 * PARAMETER(S):
7022 * [I] infoPtr : valid pointer to the listview structure
7023 * [I] nItems : item count (projected number of items to allocate)
7024 * [I] dwFlags : update flags
7026 * RETURN:
7027 * SUCCESS : TRUE
7028 * FAILURE : FALSE
7030 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7032 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7034 if (infoPtr->dwStyle & LVS_OWNERDATA)
7036 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7037 INT nOldCount = infoPtr->nItemCount;
7039 if (nItems < nOldCount)
7041 RANGE range = { nItems, nOldCount };
7042 ranges_del(infoPtr->selectionRanges, range);
7043 if (infoPtr->nFocusedItem >= nItems)
7045 infoPtr->nFocusedItem = -1;
7046 SetRectEmpty(&infoPtr->rcFocus);
7050 infoPtr->nItemCount = nItems;
7051 LISTVIEW_UpdateScroll(infoPtr);
7053 /* the flags are valid only in ownerdata report and list modes */
7054 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7056 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7057 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7059 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7060 LISTVIEW_InvalidateList(infoPtr);
7061 else
7063 INT nFrom, nTo;
7064 POINT Origin;
7065 RECT rcErase;
7067 LISTVIEW_GetOrigin(infoPtr, &Origin);
7068 nFrom = min(nOldCount, nItems);
7069 nTo = max(nOldCount, nItems);
7071 if (uView == LVS_REPORT)
7073 rcErase.left = 0;
7074 rcErase.top = nFrom * infoPtr->nItemHeight;
7075 rcErase.right = infoPtr->nItemWidth;
7076 rcErase.bottom = nTo * infoPtr->nItemHeight;
7077 OffsetRect(&rcErase, Origin.x, Origin.y);
7078 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7079 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7081 else /* LVS_LIST */
7083 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7085 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7086 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7087 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7088 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7089 OffsetRect(&rcErase, Origin.x, Origin.y);
7090 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7091 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7093 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7094 rcErase.top = 0;
7095 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7096 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7097 OffsetRect(&rcErase, Origin.x, Origin.y);
7098 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7099 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7103 else
7105 /* According to MSDN for non-LVS_OWNERDATA this is just
7106 * a performance issue. The control allocates its internal
7107 * data structures for the number of items specified. It
7108 * cuts down on the number of memory allocations. Therefore
7109 * we will just issue a WARN here
7111 WARN("for non-ownerdata performance option not implemented.\n");
7114 return TRUE;
7117 /***
7118 * DESCRIPTION:
7119 * Sets the position of an item.
7121 * PARAMETER(S):
7122 * [I] infoPtr : valid pointer to the listview structure
7123 * [I] nItem : item index
7124 * [I] pt : coordinate
7126 * RETURN:
7127 * SUCCESS : TRUE
7128 * FAILURE : FALSE
7130 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7132 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7133 POINT Origin;
7135 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7137 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7138 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7140 LISTVIEW_GetOrigin(infoPtr, &Origin);
7142 /* This point value seems to be an undocumented feature.
7143 * The best guess is that it means either at the origin,
7144 * or at true beginning of the list. I will assume the origin. */
7145 if ((pt.x == -1) && (pt.y == -1))
7146 pt = Origin;
7148 if (uView == LVS_ICON)
7150 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7151 pt.y -= ICON_TOP_PADDING;
7153 pt.x -= Origin.x;
7154 pt.y -= Origin.y;
7156 infoPtr->bAutoarrange = FALSE;
7158 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7161 /***
7162 * DESCRIPTION:
7163 * Sets the state of one or many items.
7165 * PARAMETER(S):
7166 * [I] infoPtr : valid pointer to the listview structure
7167 * [I] nItem : item index
7168 * [I] lpLVItem : item or subitem info
7170 * RETURN:
7171 * SUCCESS : TRUE
7172 * FAILURE : FALSE
7174 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7176 BOOL bResult = TRUE;
7177 LVITEMW lvItem;
7179 lvItem.iItem = nItem;
7180 lvItem.iSubItem = 0;
7181 lvItem.mask = LVIF_STATE;
7182 lvItem.state = lpLVItem->state;
7183 lvItem.stateMask = lpLVItem->stateMask;
7184 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7186 if (nItem == -1)
7188 /* apply to all items */
7189 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7190 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7192 else
7193 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7196 * Update selection mark
7198 * Investigation on windows 2k showed that selection mark was updated
7199 * whenever a new selection was made, but if the selected item was
7200 * unselected it was not updated.
7202 * we are probably still not 100% accurate, but this at least sets the
7203 * proper selection mark when it is needed
7206 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7207 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7209 int i;
7210 infoPtr->nSelectionMark = -1;
7211 for (i = 0; i < infoPtr->nItemCount; i++)
7213 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7215 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7217 infoPtr->nSelectionMark = i;
7218 break;
7221 else if (ranges_contain(infoPtr->selectionRanges, i))
7223 infoPtr->nSelectionMark = i;
7224 break;
7229 return bResult;
7232 /***
7233 * DESCRIPTION:
7234 * Sets the text of an item or subitem.
7236 * PARAMETER(S):
7237 * [I] hwnd : window handle
7238 * [I] nItem : item index
7239 * [I] lpLVItem : item or subitem info
7240 * [I] isW : TRUE if input is Unicode
7242 * RETURN:
7243 * SUCCESS : TRUE
7244 * FAILURE : FALSE
7246 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7248 LVITEMW lvItem;
7250 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7252 lvItem.iItem = nItem;
7253 lvItem.iSubItem = lpLVItem->iSubItem;
7254 lvItem.mask = LVIF_TEXT;
7255 lvItem.pszText = lpLVItem->pszText;
7256 lvItem.cchTextMax = lpLVItem->cchTextMax;
7258 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7260 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7263 /***
7264 * DESCRIPTION:
7265 * Set item index that marks the start of a multiple selection.
7267 * PARAMETER(S):
7268 * [I] infoPtr : valid pointer to the listview structure
7269 * [I] nIndex : index
7271 * RETURN:
7272 * Index number or -1 if there is no selection mark.
7274 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7276 INT nOldIndex = infoPtr->nSelectionMark;
7278 TRACE("(nIndex=%d)\n", nIndex);
7280 infoPtr->nSelectionMark = nIndex;
7282 return nOldIndex;
7285 /***
7286 * DESCRIPTION:
7287 * Sets the text background color.
7289 * PARAMETER(S):
7290 * [I] infoPtr : valid pointer to the listview structure
7291 * [I] clrTextBk : text background color
7293 * RETURN:
7294 * SUCCESS : TRUE
7295 * FAILURE : FALSE
7297 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7299 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7301 if (infoPtr->clrTextBk != clrTextBk)
7303 infoPtr->clrTextBk = clrTextBk;
7304 LISTVIEW_InvalidateList(infoPtr);
7307 return TRUE;
7310 /***
7311 * DESCRIPTION:
7312 * Sets the text foreground color.
7314 * PARAMETER(S):
7315 * [I] infoPtr : valid pointer to the listview structure
7316 * [I] clrText : text color
7318 * RETURN:
7319 * SUCCESS : TRUE
7320 * FAILURE : FALSE
7322 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7324 TRACE("(clrText=%lx)\n", clrText);
7326 if (infoPtr->clrText != clrText)
7328 infoPtr->clrText = clrText;
7329 LISTVIEW_InvalidateList(infoPtr);
7332 return TRUE;
7335 /***
7336 * DESCRIPTION:
7337 * Determines which listview item is located at the specified position.
7339 * PARAMETER(S):
7340 * [I] infoPtr : valid pointer to the listview structure
7341 * [I] hwndNewToolTip : handle to new ToolTip
7343 * RETURN:
7344 * old tool tip
7346 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7348 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7349 infoPtr->hwndToolTip = hwndNewToolTip;
7350 return hwndOldToolTip;
7353 /* LISTVIEW_SetUnicodeFormat */
7354 /* LISTVIEW_SetWorkAreas */
7356 /***
7357 * DESCRIPTION:
7358 * Callback internally used by LISTVIEW_SortItems()
7360 * PARAMETER(S):
7361 * [I] first : pointer to first ITEM_INFO to compare
7362 * [I] second : pointer to second ITEM_INFO to compare
7363 * [I] lParam : HWND of control
7365 * RETURN:
7366 * if first comes before second : negative
7367 * if first comes after second : positive
7368 * if first and second are equivalent : zero
7370 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7372 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7373 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7374 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7376 /* Forward the call to the client defined callback */
7377 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7380 /***
7381 * DESCRIPTION:
7382 * Sorts the listview items.
7384 * PARAMETER(S):
7385 * [I] infoPtr : valid pointer to the listview structure
7386 * [I] pfnCompare : application-defined value
7387 * [I] lParamSort : pointer to comparision callback
7389 * RETURN:
7390 * SUCCESS : TRUE
7391 * FAILURE : FALSE
7393 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7395 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7396 HDPA hdpaSubItems;
7397 ITEM_INFO *lpItem;
7398 LPVOID selectionMarkItem;
7399 LVITEMW item;
7400 int i;
7402 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7404 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7406 if (!pfnCompare) return FALSE;
7407 if (!infoPtr->hdpaItems) return FALSE;
7409 /* if there are 0 or 1 items, there is no need to sort */
7410 if (infoPtr->nItemCount < 2) return TRUE;
7412 if (infoPtr->nFocusedItem >= 0)
7414 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7415 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7416 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7418 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7419 /* clear the lpItem->state for non-selected ones */
7420 /* remove the selection ranges */
7422 infoPtr->pfnCompare = pfnCompare;
7423 infoPtr->lParamSort = lParamSort;
7424 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7426 /* Adjust selections and indices so that they are the way they should
7427 * be after the sort (otherwise, the list items move around, but
7428 * whatever is at the item's previous original position will be
7429 * selected instead)
7431 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7432 for (i=0; i < infoPtr->nItemCount; i++)
7434 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7435 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7437 if (lpItem->state & LVIS_SELECTED)
7439 item.state = LVIS_SELECTED;
7440 item.stateMask = LVIS_SELECTED;
7441 LISTVIEW_SetItemState(infoPtr, i, &item);
7443 if (lpItem->state & LVIS_FOCUSED)
7445 infoPtr->nFocusedItem = i;
7446 lpItem->state &= ~LVIS_FOCUSED;
7449 if (selectionMarkItem != NULL)
7450 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7451 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7453 /* refresh the display */
7454 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7455 LISTVIEW_InvalidateList(infoPtr);
7457 return TRUE;
7460 /***
7461 * DESCRIPTION:
7462 * Update theme handle after a theme change.
7464 * PARAMETER(S):
7465 * [I] infoPtr : valid pointer to the listview structure
7467 * RETURN:
7468 * SUCCESS : 0
7469 * FAILURE : something else
7471 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7473 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7474 CloseThemeData(theme);
7475 OpenThemeData(infoPtr->hwndSelf, themeClass);
7476 return 0;
7479 /***
7480 * DESCRIPTION:
7481 * Updates an items or rearranges the listview control.
7483 * PARAMETER(S):
7484 * [I] infoPtr : valid pointer to the listview structure
7485 * [I] nItem : item index
7487 * RETURN:
7488 * SUCCESS : TRUE
7489 * FAILURE : FALSE
7491 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7493 TRACE("(nItem=%d)\n", nItem);
7495 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7497 /* rearrange with default alignment style */
7498 if (is_autoarrange(infoPtr))
7499 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7500 else
7501 LISTVIEW_InvalidateItem(infoPtr, nItem);
7503 return TRUE;
7507 /***
7508 * DESCRIPTION:
7509 * Creates the listview control.
7511 * PARAMETER(S):
7512 * [I] hwnd : window handle
7513 * [I] lpcs : the create parameters
7515 * RETURN:
7516 * Success: 0
7517 * Failure: -1
7519 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7521 LISTVIEW_INFO *infoPtr;
7522 UINT uView = lpcs->style & LVS_TYPEMASK;
7523 LOGFONTW logFont;
7525 TRACE("(lpcs=%p)\n", lpcs);
7527 /* initialize info pointer */
7528 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7529 if (!infoPtr) return -1;
7531 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7533 infoPtr->hwndSelf = hwnd;
7534 infoPtr->dwStyle = lpcs->style;
7535 /* determine the type of structures to use */
7536 infoPtr->hwndNotify = lpcs->hwndParent;
7537 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7538 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7540 /* initialize color information */
7541 infoPtr->clrBk = CLR_NONE;
7542 infoPtr->clrText = comctl32_color.clrWindowText;
7543 infoPtr->clrTextBk = CLR_DEFAULT;
7544 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7546 /* set default values */
7547 infoPtr->nFocusedItem = -1;
7548 infoPtr->nSelectionMark = -1;
7549 infoPtr->nHotItem = -1;
7550 infoPtr->bRedraw = TRUE;
7551 infoPtr->bNoItemMetrics = TRUE;
7552 infoPtr->bDoChangeNotify = TRUE;
7553 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7554 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7555 infoPtr->nEditLabelItem = -1;
7556 infoPtr->dwHoverTime = -1; /* default system hover time */
7557 infoPtr->nMeasureItemHeight = 0;
7559 /* get default font (icon title) */
7560 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7561 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7562 infoPtr->hFont = infoPtr->hDefaultFont;
7563 LISTVIEW_SaveTextMetrics(infoPtr);
7565 /* create header */
7566 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7567 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7568 0, 0, 0, 0, hwnd, NULL,
7569 lpcs->hInstance, NULL);
7570 if (!infoPtr->hwndHeader) goto fail;
7572 /* set header unicode format */
7573 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7575 /* set header font */
7576 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7578 /* allocate memory for the data structure */
7579 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7580 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7581 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7582 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7583 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7585 /* initialize the icon sizes */
7586 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7587 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7589 /* init item size to avoid division by 0 */
7590 LISTVIEW_UpdateItemSize (infoPtr);
7592 if (uView == LVS_REPORT)
7594 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7596 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7598 else
7600 /* set HDS_HIDDEN flag to hide the header bar */
7601 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7602 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7606 OpenThemeData(hwnd, themeClass);
7608 return 0;
7610 fail:
7611 DestroyWindow(infoPtr->hwndHeader);
7612 ranges_destroy(infoPtr->selectionRanges);
7613 DPA_Destroy(infoPtr->hdpaItems);
7614 DPA_Destroy(infoPtr->hdpaPosX);
7615 DPA_Destroy(infoPtr->hdpaPosY);
7616 DPA_Destroy(infoPtr->hdpaColumns);
7617 Free(infoPtr);
7618 return -1;
7621 /***
7622 * DESCRIPTION:
7623 * Destroys the listview control.
7625 * PARAMETER(S):
7626 * [I] infoPtr : valid pointer to the listview structure
7628 * RETURN:
7629 * Success: 0
7630 * Failure: -1
7632 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7634 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7635 CloseThemeData(theme);
7636 return 0;
7639 /***
7640 * DESCRIPTION:
7641 * Enables the listview control.
7643 * PARAMETER(S):
7644 * [I] infoPtr : valid pointer to the listview structure
7645 * [I] bEnable : specifies whether to enable or disable the window
7647 * RETURN:
7648 * SUCCESS : TRUE
7649 * FAILURE : FALSE
7651 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7653 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7654 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7655 return TRUE;
7658 /***
7659 * DESCRIPTION:
7660 * Erases the background of the listview control.
7662 * PARAMETER(S):
7663 * [I] infoPtr : valid pointer to the listview structure
7664 * [I] hdc : device context handle
7666 * RETURN:
7667 * SUCCESS : TRUE
7668 * FAILURE : FALSE
7670 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7672 RECT rc;
7674 TRACE("(hdc=%p)\n", hdc);
7676 if (!GetClipBox(hdc, &rc)) return FALSE;
7678 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7682 /***
7683 * DESCRIPTION:
7684 * Helper function for LISTVIEW_[HV]Scroll *only*.
7685 * Performs vertical/horizontal scrolling by a give amount.
7687 * PARAMETER(S):
7688 * [I] infoPtr : valid pointer to the listview structure
7689 * [I] dx : amount of horizontal scroll
7690 * [I] dy : amount of vertical scroll
7692 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7694 /* now we can scroll the list */
7695 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7696 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7697 /* if we have focus, adjust rect */
7698 OffsetRect(&infoPtr->rcFocus, dx, dy);
7699 UpdateWindow(infoPtr->hwndSelf);
7702 /***
7703 * DESCRIPTION:
7704 * Performs vertical scrolling.
7706 * PARAMETER(S):
7707 * [I] infoPtr : valid pointer to the listview structure
7708 * [I] nScrollCode : scroll code
7709 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7710 * [I] hScrollWnd : scrollbar control window handle
7712 * RETURN:
7713 * Zero
7715 * NOTES:
7716 * SB_LINEUP/SB_LINEDOWN:
7717 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7718 * for LVS_REPORT is 1 line
7719 * for LVS_LIST cannot occur
7722 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7723 INT nScrollDiff, HWND hScrollWnd)
7725 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7726 INT nOldScrollPos, nNewScrollPos;
7727 SCROLLINFO scrollInfo;
7728 BOOL is_an_icon;
7730 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7731 debugscrollcode(nScrollCode), nScrollDiff);
7733 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7735 scrollInfo.cbSize = sizeof(SCROLLINFO);
7736 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7738 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7740 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7742 nOldScrollPos = scrollInfo.nPos;
7743 switch (nScrollCode)
7745 case SB_INTERNAL:
7746 break;
7748 case SB_LINEUP:
7749 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7750 break;
7752 case SB_LINEDOWN:
7753 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7754 break;
7756 case SB_PAGEUP:
7757 nScrollDiff = -scrollInfo.nPage;
7758 break;
7760 case SB_PAGEDOWN:
7761 nScrollDiff = scrollInfo.nPage;
7762 break;
7764 case SB_THUMBPOSITION:
7765 case SB_THUMBTRACK:
7766 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7767 break;
7769 default:
7770 nScrollDiff = 0;
7773 /* quit right away if pos isn't changing */
7774 if (nScrollDiff == 0) return 0;
7776 /* calculate new position, and handle overflows */
7777 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7778 if (nScrollDiff > 0) {
7779 if (nNewScrollPos < nOldScrollPos ||
7780 nNewScrollPos > scrollInfo.nMax)
7781 nNewScrollPos = scrollInfo.nMax;
7782 } else {
7783 if (nNewScrollPos > nOldScrollPos ||
7784 nNewScrollPos < scrollInfo.nMin)
7785 nNewScrollPos = scrollInfo.nMin;
7788 /* set the new position, and reread in case it changed */
7789 scrollInfo.fMask = SIF_POS;
7790 scrollInfo.nPos = nNewScrollPos;
7791 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7793 /* carry on only if it really changed */
7794 if (nNewScrollPos == nOldScrollPos) return 0;
7796 /* now adjust to client coordinates */
7797 nScrollDiff = nOldScrollPos - nNewScrollPos;
7798 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7800 /* and scroll the window */
7801 scroll_list(infoPtr, 0, nScrollDiff);
7803 return 0;
7806 /***
7807 * DESCRIPTION:
7808 * Performs horizontal scrolling.
7810 * PARAMETER(S):
7811 * [I] infoPtr : valid pointer to the listview structure
7812 * [I] nScrollCode : scroll code
7813 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7814 * [I] hScrollWnd : scrollbar control window handle
7816 * RETURN:
7817 * Zero
7819 * NOTES:
7820 * SB_LINELEFT/SB_LINERIGHT:
7821 * for LVS_ICON, LVS_SMALLICON 1 pixel
7822 * for LVS_REPORT is 1 pixel
7823 * for LVS_LIST is 1 column --> which is a 1 because the
7824 * scroll is based on columns not pixels
7827 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7828 INT nScrollDiff, HWND hScrollWnd)
7830 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7831 INT nOldScrollPos, nNewScrollPos;
7832 SCROLLINFO scrollInfo;
7834 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7835 debugscrollcode(nScrollCode), nScrollDiff);
7837 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7839 scrollInfo.cbSize = sizeof(SCROLLINFO);
7840 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7842 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7844 nOldScrollPos = scrollInfo.nPos;
7846 switch (nScrollCode)
7848 case SB_INTERNAL:
7849 break;
7851 case SB_LINELEFT:
7852 nScrollDiff = -1;
7853 break;
7855 case SB_LINERIGHT:
7856 nScrollDiff = 1;
7857 break;
7859 case SB_PAGELEFT:
7860 nScrollDiff = -scrollInfo.nPage;
7861 break;
7863 case SB_PAGERIGHT:
7864 nScrollDiff = scrollInfo.nPage;
7865 break;
7867 case SB_THUMBPOSITION:
7868 case SB_THUMBTRACK:
7869 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7870 break;
7872 default:
7873 nScrollDiff = 0;
7876 /* quit right away if pos isn't changing */
7877 if (nScrollDiff == 0) return 0;
7879 /* calculate new position, and handle overflows */
7880 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7881 if (nScrollDiff > 0) {
7882 if (nNewScrollPos < nOldScrollPos ||
7883 nNewScrollPos > scrollInfo.nMax)
7884 nNewScrollPos = scrollInfo.nMax;
7885 } else {
7886 if (nNewScrollPos > nOldScrollPos ||
7887 nNewScrollPos < scrollInfo.nMin)
7888 nNewScrollPos = scrollInfo.nMin;
7891 /* set the new position, and reread in case it changed */
7892 scrollInfo.fMask = SIF_POS;
7893 scrollInfo.nPos = nNewScrollPos;
7894 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7896 /* carry on only if it really changed */
7897 if (nNewScrollPos == nOldScrollPos) return 0;
7899 if(uView == LVS_REPORT)
7900 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7902 /* now adjust to client coordinates */
7903 nScrollDiff = nOldScrollPos - nNewScrollPos;
7904 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7906 /* and scroll the window */
7907 scroll_list(infoPtr, nScrollDiff, 0);
7909 return 0;
7912 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7914 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7915 INT gcWheelDelta = 0;
7916 INT pulScrollLines = 3;
7917 SCROLLINFO scrollInfo;
7919 TRACE("(wheelDelta=%d)\n", wheelDelta);
7921 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7922 gcWheelDelta -= wheelDelta;
7924 scrollInfo.cbSize = sizeof(SCROLLINFO);
7925 scrollInfo.fMask = SIF_POS;
7927 switch(uView)
7929 case LVS_ICON:
7930 case LVS_SMALLICON:
7932 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7933 * should be fixed in the future.
7935 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7936 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7937 break;
7939 case LVS_REPORT:
7940 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7942 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7943 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7944 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7946 break;
7948 case LVS_LIST:
7949 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7950 break;
7952 return 0;
7955 /***
7956 * DESCRIPTION:
7957 * ???
7959 * PARAMETER(S):
7960 * [I] infoPtr : valid pointer to the listview structure
7961 * [I] nVirtualKey : virtual key
7962 * [I] lKeyData : key data
7964 * RETURN:
7965 * Zero
7967 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7969 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7970 INT nItem = -1;
7971 NMLVKEYDOWN nmKeyDown;
7973 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7975 /* send LVN_KEYDOWN notification */
7976 nmKeyDown.wVKey = nVirtualKey;
7977 nmKeyDown.flags = 0;
7978 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7980 switch (nVirtualKey)
7982 case VK_RETURN:
7983 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7985 notify(infoPtr, NM_RETURN);
7986 notify(infoPtr, LVN_ITEMACTIVATE);
7988 break;
7990 case VK_HOME:
7991 if (infoPtr->nItemCount > 0)
7992 nItem = 0;
7993 break;
7995 case VK_END:
7996 if (infoPtr->nItemCount > 0)
7997 nItem = infoPtr->nItemCount - 1;
7998 break;
8000 case VK_LEFT:
8001 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8002 break;
8004 case VK_UP:
8005 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8006 break;
8008 case VK_RIGHT:
8009 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8010 break;
8012 case VK_DOWN:
8013 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8014 break;
8016 case VK_PRIOR:
8017 if (uView == LVS_REPORT)
8019 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8020 if (infoPtr->nFocusedItem == topidx)
8021 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8022 else
8023 nItem = topidx;
8025 else
8026 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8027 * LISTVIEW_GetCountPerRow(infoPtr);
8028 if(nItem < 0) nItem = 0;
8029 break;
8031 case VK_NEXT:
8032 if (uView == LVS_REPORT)
8034 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8035 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8036 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8037 nItem = infoPtr->nFocusedItem + cnt - 1;
8038 else
8039 nItem = topidx + cnt - 1;
8041 else
8042 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8043 * LISTVIEW_GetCountPerRow(infoPtr);
8044 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8045 break;
8048 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8049 LISTVIEW_KeySelection(infoPtr, nItem);
8051 return 0;
8054 /***
8055 * DESCRIPTION:
8056 * Kills the focus.
8058 * PARAMETER(S):
8059 * [I] infoPtr : valid pointer to the listview structure
8061 * RETURN:
8062 * Zero
8064 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8066 TRACE("()\n");
8068 /* if we did not have the focus, there's nothing to do */
8069 if (!infoPtr->bFocus) return 0;
8071 /* send NM_KILLFOCUS notification */
8072 notify(infoPtr, NM_KILLFOCUS);
8074 /* if we have a focus rectagle, get rid of it */
8075 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8077 /* set window focus flag */
8078 infoPtr->bFocus = FALSE;
8080 /* invalidate the selected items before reseting focus flag */
8081 LISTVIEW_InvalidateSelectedItems(infoPtr);
8083 return 0;
8086 /***
8087 * DESCRIPTION:
8088 * Processes double click messages (left mouse button).
8090 * PARAMETER(S):
8091 * [I] infoPtr : valid pointer to the listview structure
8092 * [I] wKey : key flag
8093 * [I] x,y : mouse coordinate
8095 * RETURN:
8096 * Zero
8098 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8100 LVHITTESTINFO htInfo;
8102 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8104 /* send NM_RELEASEDCAPTURE notification */
8105 notify(infoPtr, NM_RELEASEDCAPTURE);
8107 htInfo.pt.x = x;
8108 htInfo.pt.y = y;
8110 /* send NM_DBLCLK notification */
8111 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8112 notify_click(infoPtr, NM_DBLCLK, &htInfo);
8114 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8115 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8117 return 0;
8120 /***
8121 * DESCRIPTION:
8122 * Processes mouse down messages (left mouse button).
8124 * PARAMETERS:
8125 * infoPtr [I ] valid pointer to the listview structure
8126 * wKey [I ] key flag
8127 * x,y [I ] mouse coordinate
8129 * RETURN:
8130 * Zero
8132 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8134 LVHITTESTINFO lvHitTestInfo;
8135 static BOOL bGroupSelect = TRUE;
8136 POINT pt = { x, y };
8137 INT nItem;
8139 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8141 /* send NM_RELEASEDCAPTURE notification */
8142 notify(infoPtr, NM_RELEASEDCAPTURE);
8144 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8146 /* set left button down flag and record the click position */
8147 infoPtr->bLButtonDown = TRUE;
8148 infoPtr->ptClickPos = pt;
8150 lvHitTestInfo.pt.x = x;
8151 lvHitTestInfo.pt.y = y;
8153 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8154 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8155 infoPtr->nEditLabelItem = -1;
8156 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8158 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8160 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8161 if(state == 1 || state == 2)
8163 LVITEMW lvitem;
8164 state ^= 3;
8165 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8166 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8167 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8169 return 0;
8172 if (infoPtr->dwStyle & LVS_SINGLESEL)
8174 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8175 infoPtr->nEditLabelItem = nItem;
8176 else
8177 LISTVIEW_SetSelection(infoPtr, nItem);
8179 else
8181 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8183 if (bGroupSelect)
8185 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8186 LISTVIEW_SetItemFocus(infoPtr, nItem);
8187 infoPtr->nSelectionMark = nItem;
8189 else
8191 LVITEMW item;
8193 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8194 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8196 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8197 infoPtr->nSelectionMark = nItem;
8200 else if (wKey & MK_CONTROL)
8202 LVITEMW item;
8204 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8206 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8207 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8208 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8209 infoPtr->nSelectionMark = nItem;
8211 else if (wKey & MK_SHIFT)
8213 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8215 else
8217 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8218 infoPtr->nEditLabelItem = nItem;
8220 /* set selection (clears other pre-existing selections) */
8221 LISTVIEW_SetSelection(infoPtr, nItem);
8225 else
8227 /* remove all selections */
8228 LISTVIEW_DeselectAll(infoPtr);
8229 ReleaseCapture();
8232 return 0;
8235 /***
8236 * DESCRIPTION:
8237 * Processes mouse up messages (left mouse button).
8239 * PARAMETERS:
8240 * infoPtr [I ] valid pointer to the listview structure
8241 * wKey [I ] key flag
8242 * x,y [I ] mouse coordinate
8244 * RETURN:
8245 * Zero
8247 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8249 LVHITTESTINFO lvHitTestInfo;
8251 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8253 if (!infoPtr->bLButtonDown) return 0;
8255 lvHitTestInfo.pt.x = x;
8256 lvHitTestInfo.pt.y = y;
8258 /* send NM_CLICK notification */
8259 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8260 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8262 /* set left button flag */
8263 infoPtr->bLButtonDown = FALSE;
8265 /* if we clicked on a selected item, edit the label */
8266 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8267 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8269 return 0;
8272 /***
8273 * DESCRIPTION:
8274 * Destroys the listview control (called after WM_DESTROY).
8276 * PARAMETER(S):
8277 * [I] infoPtr : valid pointer to the listview structure
8279 * RETURN:
8280 * Zero
8282 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8284 TRACE("()\n");
8286 /* delete all items */
8287 LISTVIEW_DeleteAllItems(infoPtr);
8289 /* destroy data structure */
8290 DPA_Destroy(infoPtr->hdpaItems);
8291 DPA_Destroy(infoPtr->hdpaPosX);
8292 DPA_Destroy(infoPtr->hdpaPosY);
8293 DPA_Destroy(infoPtr->hdpaColumns);
8294 ranges_destroy(infoPtr->selectionRanges);
8296 /* destroy image lists */
8297 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8299 if (infoPtr->himlNormal)
8300 ImageList_Destroy(infoPtr->himlNormal);
8301 if (infoPtr->himlSmall)
8302 ImageList_Destroy(infoPtr->himlSmall);
8303 if (infoPtr->himlState)
8304 ImageList_Destroy(infoPtr->himlState);
8307 /* destroy font, bkgnd brush */
8308 infoPtr->hFont = 0;
8309 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8310 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8312 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8314 /* free listview info pointer*/
8315 Free(infoPtr);
8317 return 0;
8320 /***
8321 * DESCRIPTION:
8322 * Handles notifications from header.
8324 * PARAMETER(S):
8325 * [I] infoPtr : valid pointer to the listview structure
8326 * [I] nCtrlId : control identifier
8327 * [I] lpnmh : notification information
8329 * RETURN:
8330 * Zero
8332 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8334 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8336 TRACE("(lpnmh=%p)\n", lpnmh);
8338 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8340 switch (lpnmh->hdr.code)
8342 case HDN_ITEMCHANGINGW:
8343 case HDN_ITEMCHANGINGA:
8344 return notify_forward_header(infoPtr, lpnmh);
8345 case HDN_ITEMCHANGEDW:
8346 case HDN_ITEMCHANGEDA:
8347 notify_forward_header(infoPtr, lpnmh);
8348 /* Fall through */
8349 case HDN_TRACKW:
8350 case HDN_TRACKA:
8352 COLUMN_INFO *lpColumnInfo;
8353 INT dx, cxy;
8355 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8357 HDITEMW hdi;
8359 hdi.mask = HDI_WIDTH;
8360 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8361 cxy = hdi.cxy;
8363 else
8364 cxy = lpnmh->pitem->cxy;
8366 /* determine how much we change since the last know position */
8367 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8368 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8369 if (dx != 0)
8371 lpColumnInfo->rcHeader.right += dx;
8372 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8373 LISTVIEW_UpdateItemSize(infoPtr);
8374 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8376 POINT ptOrigin;
8377 RECT rcCol = lpColumnInfo->rcHeader;
8379 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8380 OffsetRect(&rcCol, ptOrigin.x, 0);
8382 rcCol.top = infoPtr->rcList.top;
8383 rcCol.bottom = infoPtr->rcList.bottom;
8385 /* resizing left-aligned columns leaves most of the left side untouched */
8386 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8388 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8389 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8392 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8396 break;
8398 case HDN_ITEMCLICKW:
8399 case HDN_ITEMCLICKA:
8401 /* Handle sorting by Header Column */
8402 NMLISTVIEW nmlv;
8404 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8405 nmlv.iItem = -1;
8406 nmlv.iSubItem = lpnmh->iItem;
8407 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8409 break;
8411 case HDN_DIVIDERDBLCLICKW:
8412 case HDN_DIVIDERDBLCLICKA:
8413 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8414 break;
8417 return 0;
8420 /***
8421 * DESCRIPTION:
8422 * Paint non-client area of control.
8424 * PARAMETER(S):
8425 * [I] infoPtr : valid pointer to the listview structureof the sender
8426 * [I] region : update region
8428 * RETURN:
8429 * TRUE - frame was painted
8430 * FALSE - call default window proc
8432 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8434 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8435 HDC dc;
8436 RECT r;
8437 HRGN cliprgn;
8438 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8439 cyEdge = GetSystemMetrics (SM_CYEDGE);
8441 if (!theme) return FALSE;
8443 GetWindowRect(infoPtr->hwndSelf, &r);
8445 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8446 r.right - cxEdge, r.bottom - cyEdge);
8447 if (region != (HRGN)1)
8448 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8449 OffsetRect(&r, -r.left, -r.top);
8451 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8452 OffsetRect(&r, -r.left, -r.top);
8454 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8455 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8456 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8457 ReleaseDC(infoPtr->hwndSelf, dc);
8459 /* Call default proc to get the scrollbars etc. painted */
8460 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8462 return TRUE;
8465 /***
8466 * DESCRIPTION:
8467 * Determines the type of structure to use.
8469 * PARAMETER(S):
8470 * [I] infoPtr : valid pointer to the listview structureof the sender
8471 * [I] hwndFrom : listview window handle
8472 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8474 * RETURN:
8475 * Zero
8477 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8479 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8481 if (nCommand != NF_REQUERY) return 0;
8483 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8485 return 0;
8488 /***
8489 * DESCRIPTION:
8490 * Paints/Repaints the listview control.
8492 * PARAMETER(S):
8493 * [I] infoPtr : valid pointer to the listview structure
8494 * [I] hdc : device context handle
8496 * RETURN:
8497 * Zero
8499 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8501 TRACE("(hdc=%p)\n", hdc);
8503 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8505 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8507 infoPtr->bNoItemMetrics = FALSE;
8508 LISTVIEW_UpdateItemSize(infoPtr);
8509 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8510 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8511 LISTVIEW_UpdateScroll(infoPtr);
8513 if (hdc)
8514 LISTVIEW_Refresh(infoPtr, hdc);
8515 else
8517 PAINTSTRUCT ps;
8519 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8520 if (!hdc) return 1;
8521 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8522 LISTVIEW_Refresh(infoPtr, hdc);
8523 EndPaint(infoPtr->hwndSelf, &ps);
8526 return 0;
8530 /***
8531 * DESCRIPTION:
8532 * Paints/Repaints the listview control.
8534 * PARAMETER(S):
8535 * [I] infoPtr : valid pointer to the listview structure
8536 * [I] hdc : device context handle
8537 * [I] options : drawing options
8539 * RETURN:
8540 * Zero
8542 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8544 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8546 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8547 return 0;
8549 if (options & PRF_ERASEBKGND)
8550 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8552 if (options & PRF_CLIENT)
8553 LISTVIEW_Paint(infoPtr, hdc);
8555 return 0;
8559 /***
8560 * DESCRIPTION:
8561 * Processes double click messages (right mouse button).
8563 * PARAMETER(S):
8564 * [I] infoPtr : valid pointer to the listview structure
8565 * [I] wKey : key flag
8566 * [I] x,y : mouse coordinate
8568 * RETURN:
8569 * Zero
8571 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8573 LVHITTESTINFO lvHitTestInfo;
8575 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8577 /* send NM_RELEASEDCAPTURE notification */
8578 notify(infoPtr, NM_RELEASEDCAPTURE);
8580 /* send NM_RDBLCLK notification */
8581 lvHitTestInfo.pt.x = x;
8582 lvHitTestInfo.pt.y = y;
8583 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8584 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8586 return 0;
8589 /***
8590 * DESCRIPTION:
8591 * Processes mouse down messages (right mouse button).
8593 * PARAMETER(S):
8594 * [I] infoPtr : valid pointer to the listview structure
8595 * [I] wKey : key flag
8596 * [I] x,y : mouse coordinate
8598 * RETURN:
8599 * Zero
8601 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8603 LVHITTESTINFO lvHitTestInfo;
8604 INT nItem;
8606 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8608 /* send NM_RELEASEDCAPTURE notification */
8609 notify(infoPtr, NM_RELEASEDCAPTURE);
8611 /* make sure the listview control window has the focus */
8612 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8614 /* set right button down flag */
8615 infoPtr->bRButtonDown = TRUE;
8617 /* determine the index of the selected item */
8618 lvHitTestInfo.pt.x = x;
8619 lvHitTestInfo.pt.y = y;
8620 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8622 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8624 LISTVIEW_SetItemFocus(infoPtr, nItem);
8625 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8626 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8627 LISTVIEW_SetSelection(infoPtr, nItem);
8629 else
8631 LISTVIEW_DeselectAll(infoPtr);
8634 return 0;
8637 /***
8638 * DESCRIPTION:
8639 * Processes mouse up messages (right mouse button).
8641 * PARAMETER(S):
8642 * [I] infoPtr : valid pointer to the listview structure
8643 * [I] wKey : key flag
8644 * [I] x,y : mouse coordinate
8646 * RETURN:
8647 * Zero
8649 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8651 LVHITTESTINFO lvHitTestInfo;
8652 POINT pt;
8654 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8656 if (!infoPtr->bRButtonDown) return 0;
8658 /* set button flag */
8659 infoPtr->bRButtonDown = FALSE;
8661 /* Send NM_RClICK notification */
8662 lvHitTestInfo.pt.x = x;
8663 lvHitTestInfo.pt.y = y;
8664 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8665 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8667 /* Change to screen coordinate for WM_CONTEXTMENU */
8668 pt = lvHitTestInfo.pt;
8669 ClientToScreen(infoPtr->hwndSelf, &pt);
8671 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8672 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8673 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8675 return 0;
8679 /***
8680 * DESCRIPTION:
8681 * Sets the cursor.
8683 * PARAMETER(S):
8684 * [I] infoPtr : valid pointer to the listview structure
8685 * [I] hwnd : window handle of window containing the cursor
8686 * [I] nHittest : hit-test code
8687 * [I] wMouseMsg : ideintifier of the mouse message
8689 * RETURN:
8690 * TRUE if cursor is set
8691 * FALSE otherwise
8693 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8695 LVHITTESTINFO lvHitTestInfo;
8697 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8699 if(!infoPtr->hHotCursor) return FALSE;
8701 GetCursorPos(&lvHitTestInfo.pt);
8702 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8704 SetCursor(infoPtr->hHotCursor);
8706 return TRUE;
8709 /***
8710 * DESCRIPTION:
8711 * Sets the focus.
8713 * PARAMETER(S):
8714 * [I] infoPtr : valid pointer to the listview structure
8715 * [I] hwndLoseFocus : handle of previously focused window
8717 * RETURN:
8718 * Zero
8720 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8722 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8724 /* if we have the focus already, there's nothing to do */
8725 if (infoPtr->bFocus) return 0;
8727 /* send NM_SETFOCUS notification */
8728 notify(infoPtr, NM_SETFOCUS);
8730 /* set window focus flag */
8731 infoPtr->bFocus = TRUE;
8733 /* put the focus rect back on */
8734 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8736 /* redraw all visible selected items */
8737 LISTVIEW_InvalidateSelectedItems(infoPtr);
8739 return 0;
8742 /***
8743 * DESCRIPTION:
8744 * Sets the font.
8746 * PARAMETER(S):
8747 * [I] infoPtr : valid pointer to the listview structure
8748 * [I] fRedraw : font handle
8749 * [I] fRedraw : redraw flag
8751 * RETURN:
8752 * Zero
8754 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8756 HFONT oldFont = infoPtr->hFont;
8758 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8760 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8761 if (infoPtr->hFont == oldFont) return 0;
8763 LISTVIEW_SaveTextMetrics(infoPtr);
8765 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8766 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8768 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8770 return 0;
8773 /***
8774 * DESCRIPTION:
8775 * Message handling for WM_SETREDRAW.
8776 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8778 * PARAMETER(S):
8779 * [I] infoPtr : valid pointer to the listview structure
8780 * [I] bRedraw: state of redraw flag
8782 * RETURN:
8783 * DefWinProc return value
8785 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8787 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8789 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8790 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8792 infoPtr->bRedraw = bRedraw;
8794 if(!bRedraw) return 0;
8796 if (is_autoarrange(infoPtr))
8797 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8798 LISTVIEW_UpdateScroll(infoPtr);
8800 /* despite what the WM_SETREDRAW docs says, apps expect us
8801 * to invalidate the listview here... stupid! */
8802 LISTVIEW_InvalidateList(infoPtr);
8804 return 0;
8807 /***
8808 * DESCRIPTION:
8809 * Resizes the listview control. This function processes WM_SIZE
8810 * messages. At this time, the width and height are not used.
8812 * PARAMETER(S):
8813 * [I] infoPtr : valid pointer to the listview structure
8814 * [I] Width : new width
8815 * [I] Height : new height
8817 * RETURN:
8818 * Zero
8820 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8822 RECT rcOld = infoPtr->rcList;
8824 TRACE("(width=%d, height=%d)\n", Width, Height);
8826 LISTVIEW_UpdateSize(infoPtr);
8827 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8829 /* do not bother with display related stuff if we're not redrawing */
8830 if (!is_redrawing(infoPtr)) return 0;
8832 if (is_autoarrange(infoPtr))
8833 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8835 LISTVIEW_UpdateScroll(infoPtr);
8837 /* refresh all only for lists whose height changed significantly */
8838 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8839 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8840 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8841 LISTVIEW_InvalidateList(infoPtr);
8843 return 0;
8846 /***
8847 * DESCRIPTION:
8848 * Sets the size information.
8850 * PARAMETER(S):
8851 * [I] infoPtr : valid pointer to the listview structure
8853 * RETURN:
8854 * None
8856 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8858 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8860 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8862 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8864 if (uView == LVS_LIST)
8866 /* Apparently the "LIST" style is supposed to have the same
8867 * number of items in a column even if there is no scroll bar.
8868 * Since if a scroll bar already exists then the bottom is already
8869 * reduced, only reduce if the scroll bar does not currently exist.
8870 * The "2" is there to mimic the native control. I think it may be
8871 * related to either padding or edges. (GLA 7/2002)
8873 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8874 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8875 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8877 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8879 HDLAYOUT hl;
8880 WINDOWPOS wp;
8882 hl.prc = &infoPtr->rcList;
8883 hl.pwpos = &wp;
8884 Header_Layout(infoPtr->hwndHeader, &hl);
8886 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8888 infoPtr->rcList.top = max(wp.cy, 0);
8891 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8894 /***
8895 * DESCRIPTION:
8896 * Processes WM_STYLECHANGED messages.
8898 * PARAMETER(S):
8899 * [I] infoPtr : valid pointer to the listview structure
8900 * [I] wStyleType : window style type (normal or extended)
8901 * [I] lpss : window style information
8903 * RETURN:
8904 * Zero
8906 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8907 const STYLESTRUCT *lpss)
8909 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8910 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8912 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8913 wStyleType, lpss->styleOld, lpss->styleNew);
8915 if (wStyleType != GWL_STYLE) return 0;
8917 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8918 /* what if LVS_OWNERDATA changed? */
8919 /* or LVS_SINGLESEL */
8920 /* or LVS_SORT{AS,DES}CENDING */
8922 infoPtr->dwStyle = lpss->styleNew;
8924 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8925 ((lpss->styleNew & WS_HSCROLL) == 0))
8926 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8928 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8929 ((lpss->styleNew & WS_VSCROLL) == 0))
8930 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8932 if (uNewView != uOldView)
8934 SIZE oldIconSize = infoPtr->iconSize;
8935 HIMAGELIST himl;
8937 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8938 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8940 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8941 SetRectEmpty(&infoPtr->rcFocus);
8943 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8944 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8946 if (uNewView == LVS_ICON)
8948 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8950 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8951 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8952 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8955 else if (uNewView == LVS_REPORT)
8957 HDLAYOUT hl;
8958 WINDOWPOS wp;
8960 hl.prc = &infoPtr->rcList;
8961 hl.pwpos = &wp;
8962 Header_Layout(infoPtr->hwndHeader, &hl);
8963 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8966 LISTVIEW_UpdateItemSize(infoPtr);
8969 if (uNewView == LVS_REPORT)
8970 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8972 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8973 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8974 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8976 /* update the size of the client area */
8977 LISTVIEW_UpdateSize(infoPtr);
8979 /* add scrollbars if needed */
8980 LISTVIEW_UpdateScroll(infoPtr);
8982 /* invalidate client area + erase background */
8983 LISTVIEW_InvalidateList(infoPtr);
8985 return 0;
8988 /***
8989 * DESCRIPTION:
8990 * Window procedure of the listview control.
8993 static LRESULT WINAPI
8994 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8996 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8998 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9000 if (!infoPtr && (uMsg != WM_CREATE))
9001 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9003 switch (uMsg)
9005 case LVM_APPROXIMATEVIEWRECT:
9006 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9007 LOWORD(lParam), HIWORD(lParam));
9008 case LVM_ARRANGE:
9009 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9011 /* case LVM_CANCELEDITLABEL: */
9013 case LVM_CREATEDRAGIMAGE:
9014 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9016 case LVM_DELETEALLITEMS:
9017 return LISTVIEW_DeleteAllItems(infoPtr);
9019 case LVM_DELETECOLUMN:
9020 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9022 case LVM_DELETEITEM:
9023 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9025 case LVM_EDITLABELW:
9026 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9028 case LVM_EDITLABELA:
9029 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9031 /* case LVM_ENABLEGROUPVIEW: */
9033 case LVM_ENSUREVISIBLE:
9034 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9036 case LVM_FINDITEMW:
9037 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9039 case LVM_FINDITEMA:
9040 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9042 case LVM_GETBKCOLOR:
9043 return infoPtr->clrBk;
9045 /* case LVM_GETBKIMAGE: */
9047 case LVM_GETCALLBACKMASK:
9048 return infoPtr->uCallbackMask;
9050 case LVM_GETCOLUMNA:
9051 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9053 case LVM_GETCOLUMNW:
9054 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9056 case LVM_GETCOLUMNORDERARRAY:
9057 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9059 case LVM_GETCOLUMNWIDTH:
9060 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9062 case LVM_GETCOUNTPERPAGE:
9063 return LISTVIEW_GetCountPerPage(infoPtr);
9065 case LVM_GETEDITCONTROL:
9066 return (LRESULT)infoPtr->hwndEdit;
9068 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9069 return infoPtr->dwLvExStyle;
9071 /* case LVM_GETGROUPINFO: */
9073 /* case LVM_GETGROUPMETRICS: */
9075 case LVM_GETHEADER:
9076 return (LRESULT)infoPtr->hwndHeader;
9078 case LVM_GETHOTCURSOR:
9079 return (LRESULT)infoPtr->hHotCursor;
9081 case LVM_GETHOTITEM:
9082 return infoPtr->nHotItem;
9084 case LVM_GETHOVERTIME:
9085 return infoPtr->dwHoverTime;
9087 case LVM_GETIMAGELIST:
9088 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9090 /* case LVM_GETINSERTMARK: */
9092 /* case LVM_GETINSERTMARKCOLOR: */
9094 /* case LVM_GETINSERTMARKRECT: */
9096 case LVM_GETISEARCHSTRINGA:
9097 case LVM_GETISEARCHSTRINGW:
9098 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9099 return FALSE;
9101 case LVM_GETITEMA:
9102 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9104 case LVM_GETITEMW:
9105 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9107 case LVM_GETITEMCOUNT:
9108 return infoPtr->nItemCount;
9110 case LVM_GETITEMPOSITION:
9111 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9113 case LVM_GETITEMRECT:
9114 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9116 case LVM_GETITEMSPACING:
9117 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9119 case LVM_GETITEMSTATE:
9120 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9122 case LVM_GETITEMTEXTA:
9123 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9125 case LVM_GETITEMTEXTW:
9126 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9128 case LVM_GETNEXTITEM:
9129 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9131 case LVM_GETNUMBEROFWORKAREAS:
9132 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9133 return 1;
9135 case LVM_GETORIGIN:
9136 if (!lParam) return FALSE;
9137 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9138 return TRUE;
9140 /* case LVM_GETOUTLINECOLOR: */
9142 /* case LVM_GETSELECTEDCOLUMN: */
9144 case LVM_GETSELECTEDCOUNT:
9145 return LISTVIEW_GetSelectedCount(infoPtr);
9147 case LVM_GETSELECTIONMARK:
9148 return infoPtr->nSelectionMark;
9150 case LVM_GETSTRINGWIDTHA:
9151 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9153 case LVM_GETSTRINGWIDTHW:
9154 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9156 case LVM_GETSUBITEMRECT:
9157 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9159 case LVM_GETTEXTBKCOLOR:
9160 return infoPtr->clrTextBk;
9162 case LVM_GETTEXTCOLOR:
9163 return infoPtr->clrText;
9165 /* case LVM_GETTILEINFO: */
9167 /* case LVM_GETTILEVIEWINFO: */
9169 case LVM_GETTOOLTIPS:
9170 if( !infoPtr->hwndToolTip )
9171 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9172 return (LRESULT)infoPtr->hwndToolTip;
9174 case LVM_GETTOPINDEX:
9175 return LISTVIEW_GetTopIndex(infoPtr);
9177 /*case LVM_GETUNICODEFORMAT:
9178 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9179 return FALSE;*/
9181 /* case LVM_GETVIEW: */
9183 case LVM_GETVIEWRECT:
9184 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9186 case LVM_GETWORKAREAS:
9187 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9188 return FALSE;
9190 /* case LVM_HASGROUP: */
9192 case LVM_HITTEST:
9193 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9195 case LVM_INSERTCOLUMNA:
9196 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9198 case LVM_INSERTCOLUMNW:
9199 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9201 /* case LVM_INSERTGROUP: */
9203 /* case LVM_INSERTGROUPSORTED: */
9205 case LVM_INSERTITEMA:
9206 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9208 case LVM_INSERTITEMW:
9209 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9211 /* case LVM_INSERTMARKHITTEST: */
9213 /* case LVM_ISGROUPVIEWENABLED: */
9215 /* case LVM_MAPIDTOINDEX: */
9217 /* case LVM_MAPINDEXTOID: */
9219 /* case LVM_MOVEGROUP: */
9221 /* case LVM_MOVEITEMTOGROUP: */
9223 case LVM_REDRAWITEMS:
9224 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9226 /* case LVM_REMOVEALLGROUPS: */
9228 /* case LVM_REMOVEGROUP: */
9230 case LVM_SCROLL:
9231 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9233 case LVM_SETBKCOLOR:
9234 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9236 /* case LVM_SETBKIMAGE: */
9238 case LVM_SETCALLBACKMASK:
9239 infoPtr->uCallbackMask = (UINT)wParam;
9240 return TRUE;
9242 case LVM_SETCOLUMNA:
9243 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9245 case LVM_SETCOLUMNW:
9246 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9248 case LVM_SETCOLUMNORDERARRAY:
9249 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9251 case LVM_SETCOLUMNWIDTH:
9252 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9254 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9255 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9257 /* case LVM_SETGROUPINFO: */
9259 /* case LVM_SETGROUPMETRICS: */
9261 case LVM_SETHOTCURSOR:
9262 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9264 case LVM_SETHOTITEM:
9265 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9267 case LVM_SETHOVERTIME:
9268 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9270 case LVM_SETICONSPACING:
9271 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9273 case LVM_SETIMAGELIST:
9274 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9276 /* case LVM_SETINFOTIP: */
9278 /* case LVM_SETINSERTMARK: */
9280 /* case LVM_SETINSERTMARKCOLOR: */
9282 case LVM_SETITEMA:
9283 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9285 case LVM_SETITEMW:
9286 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9288 case LVM_SETITEMCOUNT:
9289 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9291 case LVM_SETITEMPOSITION:
9293 POINT pt;
9294 pt.x = (short)LOWORD(lParam);
9295 pt.y = (short)HIWORD(lParam);
9296 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9299 case LVM_SETITEMPOSITION32:
9300 if (lParam == 0) return FALSE;
9301 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9303 case LVM_SETITEMSTATE:
9304 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9306 case LVM_SETITEMTEXTA:
9307 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9309 case LVM_SETITEMTEXTW:
9310 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9312 /* case LVM_SETOUTLINECOLOR: */
9314 /* case LVM_SETSELECTEDCOLUMN: */
9316 case LVM_SETSELECTIONMARK:
9317 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9319 case LVM_SETTEXTBKCOLOR:
9320 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9322 case LVM_SETTEXTCOLOR:
9323 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9325 /* case LVM_SETTILEINFO: */
9327 /* case LVM_SETTILEVIEWINFO: */
9329 /* case LVM_SETTILEWIDTH: */
9331 case LVM_SETTOOLTIPS:
9332 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9334 /* case LVM_SETUNICODEFORMAT: */
9336 /* case LVM_SETVIEW: */
9338 /* case LVM_SETWORKAREAS: */
9340 /* case LVM_SORTGROUPS: */
9342 case LVM_SORTITEMS:
9343 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9345 /* LVM_SORTITEMSEX: */
9347 case LVM_SUBITEMHITTEST:
9348 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9350 case LVM_UPDATE:
9351 return LISTVIEW_Update(infoPtr, (INT)wParam);
9353 case WM_CHAR:
9354 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9356 case WM_COMMAND:
9357 return LISTVIEW_Command(infoPtr, wParam, lParam);
9359 case WM_CREATE:
9360 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9362 case WM_DESTROY:
9363 return LISTVIEW_Destroy(infoPtr);
9365 case WM_ENABLE:
9366 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9368 case WM_ERASEBKGND:
9369 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9371 case WM_GETDLGCODE:
9372 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9374 case WM_GETFONT:
9375 return (LRESULT)infoPtr->hFont;
9377 case WM_HSCROLL:
9378 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9380 case WM_KEYDOWN:
9381 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9383 case WM_KILLFOCUS:
9384 return LISTVIEW_KillFocus(infoPtr);
9386 case WM_LBUTTONDBLCLK:
9387 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9389 case WM_LBUTTONDOWN:
9390 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9392 case WM_LBUTTONUP:
9393 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9395 case WM_MOUSEMOVE:
9396 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9398 case WM_MOUSEHOVER:
9399 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9401 case WM_NCDESTROY:
9402 return LISTVIEW_NCDestroy(infoPtr);
9404 case WM_NCPAINT:
9405 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9406 return 0;
9407 goto fwd_msg;
9409 case WM_NOTIFY:
9410 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9411 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9412 else return 0;
9414 case WM_NOTIFYFORMAT:
9415 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9417 case WM_PRINTCLIENT:
9418 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9420 case WM_PAINT:
9421 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9423 case WM_RBUTTONDBLCLK:
9424 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9426 case WM_RBUTTONDOWN:
9427 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9429 case WM_RBUTTONUP:
9430 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9432 case WM_SETCURSOR:
9433 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9434 return TRUE;
9435 goto fwd_msg;
9437 case WM_SETFOCUS:
9438 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9440 case WM_SETFONT:
9441 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9443 case WM_SETREDRAW:
9444 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9446 case WM_SIZE:
9447 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9449 case WM_STYLECHANGED:
9450 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9452 case WM_SYSCOLORCHANGE:
9453 COMCTL32_RefreshSysColors();
9454 return 0;
9456 /* case WM_TIMER: */
9457 case WM_THEMECHANGED:
9458 return LISTVIEW_ThemeChanged(infoPtr);
9460 case WM_VSCROLL:
9461 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9463 case WM_MOUSEWHEEL:
9464 if (wParam & (MK_SHIFT | MK_CONTROL))
9465 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9466 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9468 case WM_WINDOWPOSCHANGED:
9469 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9471 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9472 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9473 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9475 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9477 MEASUREITEMSTRUCT mis;
9478 mis.CtlType = ODT_LISTVIEW;
9479 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9480 mis.itemID = -1;
9481 mis.itemWidth = 0;
9482 mis.itemData = 0;
9483 mis.itemHeight= infoPtr->nItemHeight;
9484 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9485 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9486 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9489 LISTVIEW_UpdateSize(infoPtr);
9490 LISTVIEW_UpdateScroll(infoPtr);
9492 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9494 /* case WM_WININICHANGE: */
9496 default:
9497 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9498 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9500 fwd_msg:
9501 /* call default window procedure */
9502 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9507 /***
9508 * DESCRIPTION:
9509 * Registers the window class.
9511 * PARAMETER(S):
9512 * None
9514 * RETURN:
9515 * None
9517 void LISTVIEW_Register(void)
9519 WNDCLASSW wndClass;
9521 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9522 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9523 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9524 wndClass.cbClsExtra = 0;
9525 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9526 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9527 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9528 wndClass.lpszClassName = WC_LISTVIEWW;
9529 RegisterClassW(&wndClass);
9532 /***
9533 * DESCRIPTION:
9534 * Unregisters the window class.
9536 * PARAMETER(S):
9537 * None
9539 * RETURN:
9540 * None
9542 void LISTVIEW_Unregister(void)
9544 UnregisterClassW(WC_LISTVIEWW, NULL);
9547 /***
9548 * DESCRIPTION:
9549 * Handle any WM_COMMAND messages
9551 * PARAMETER(S):
9552 * [I] infoPtr : valid pointer to the listview structure
9553 * [I] wParam : the first message parameter
9554 * [I] lParam : the second message parameter
9556 * RETURN:
9557 * Zero.
9559 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9561 switch (HIWORD(wParam))
9563 case EN_UPDATE:
9566 * Adjust the edit window size
9568 WCHAR buffer[1024];
9569 HDC hdc = GetDC(infoPtr->hwndEdit);
9570 HFONT hFont, hOldFont = 0;
9571 RECT rect;
9572 SIZE sz;
9573 int len;
9575 if (!infoPtr->hwndEdit || !hdc) return 0;
9576 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9577 GetWindowRect(infoPtr->hwndEdit, &rect);
9579 /* Select font to get the right dimension of the string */
9580 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9581 if(hFont != 0)
9583 hOldFont = SelectObject(hdc, hFont);
9586 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9588 TEXTMETRICW textMetric;
9590 /* Add Extra spacing for the next character */
9591 GetTextMetricsW(hdc, &textMetric);
9592 sz.cx += (textMetric.tmMaxCharWidth * 2);
9594 SetWindowPos (
9595 infoPtr->hwndEdit,
9596 HWND_TOP,
9599 sz.cx,
9600 rect.bottom - rect.top,
9601 SWP_DRAWFRAME|SWP_NOMOVE);
9603 if(hFont != 0)
9604 SelectObject(hdc, hOldFont);
9606 ReleaseDC(infoPtr->hwndEdit, hdc);
9608 break;
9611 default:
9612 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9615 return 0;
9619 /***
9620 * DESCRIPTION:
9621 * Subclassed edit control windproc function
9623 * PARAMETER(S):
9624 * [I] hwnd : the edit window handle
9625 * [I] uMsg : the message that is to be processed
9626 * [I] wParam : first message parameter
9627 * [I] lParam : second message parameter
9628 * [I] isW : TRUE if input is Unicode
9630 * RETURN:
9631 * Zero.
9633 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9635 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9636 BOOL cancel = FALSE;
9638 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9639 hwnd, uMsg, wParam, lParam, isW);
9641 switch (uMsg)
9643 case WM_GETDLGCODE:
9644 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9646 case WM_KILLFOCUS:
9647 break;
9649 case WM_DESTROY:
9651 WNDPROC editProc = infoPtr->EditWndProc;
9652 infoPtr->EditWndProc = 0;
9653 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9654 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9657 case WM_KEYDOWN:
9658 if (VK_ESCAPE == (INT)wParam)
9660 cancel = TRUE;
9661 break;
9663 else if (VK_RETURN == (INT)wParam)
9664 break;
9666 default:
9667 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9670 /* kill the edit */
9671 if (infoPtr->hwndEdit)
9673 LPWSTR buffer = NULL;
9675 infoPtr->hwndEdit = 0;
9676 if (!cancel)
9678 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9680 if (len)
9682 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9684 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9685 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9689 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9691 if (buffer) Free(buffer);
9695 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9696 return 0;
9699 /***
9700 * DESCRIPTION:
9701 * Subclassed edit control Unicode windproc function
9703 * PARAMETER(S):
9704 * [I] hwnd : the edit window handle
9705 * [I] uMsg : the message that is to be processed
9706 * [I] wParam : first message parameter
9707 * [I] lParam : second message parameter
9709 * RETURN:
9711 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9713 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9716 /***
9717 * DESCRIPTION:
9718 * Subclassed edit control ANSI windproc function
9720 * PARAMETER(S):
9721 * [I] hwnd : the edit window handle
9722 * [I] uMsg : the message that is to be processed
9723 * [I] wParam : first message parameter
9724 * [I] lParam : second message parameter
9726 * RETURN:
9728 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9730 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9733 /***
9734 * DESCRIPTION:
9735 * Creates a subclassed edit cotrol
9737 * PARAMETER(S):
9738 * [I] infoPtr : valid pointer to the listview structure
9739 * [I] text : initial text for the edit
9740 * [I] style : the window style
9741 * [I] isW : TRUE if input is Unicode
9743 * RETURN:
9745 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9746 INT x, INT y, INT width, INT height, BOOL isW)
9748 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9749 HWND hedit;
9750 SIZE sz;
9751 HDC hdc;
9752 HDC hOldFont=0;
9753 TEXTMETRICW textMetric;
9754 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9756 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9758 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9759 hdc = GetDC(infoPtr->hwndSelf);
9761 /* Select the font to get appropriate metric dimensions */
9762 if(infoPtr->hFont != 0)
9763 hOldFont = SelectObject(hdc, infoPtr->hFont);
9765 /*Get String Length in pixels */
9766 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9768 /*Add Extra spacing for the next character */
9769 GetTextMetricsW(hdc, &textMetric);
9770 sz.cx += (textMetric.tmMaxCharWidth * 2);
9772 if(infoPtr->hFont != 0)
9773 SelectObject(hdc, hOldFont);
9775 ReleaseDC(infoPtr->hwndSelf, hdc);
9776 if (isW)
9777 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9778 else
9779 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9781 if (!hedit) return 0;
9783 infoPtr->EditWndProc = (WNDPROC)
9784 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9785 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9787 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9789 return hedit;