Release 20050930.
[wine/gsoc-2012-control.git] / dlls / comctl32 / listview.c
blobcc3234c0233b0fbc8257641c3c0689f9d203196a
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;
3282 /* see if we are supposed to be tracking mouse hovering */
3283 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3284 /* fill in the trackinfo struct */
3285 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3286 trackinfo.dwFlags = TME_QUERY;
3287 trackinfo.hwndTrack = infoPtr->hwndSelf;
3288 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3290 /* see if we are already tracking this hwnd */
3291 _TrackMouseEvent(&trackinfo);
3293 if(!(trackinfo.dwFlags & TME_HOVER)) {
3294 trackinfo.dwFlags = TME_HOVER;
3296 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3297 _TrackMouseEvent(&trackinfo);
3301 return 0;
3305 /***
3306 * Tests wheather the item is assignable to a list with style lStyle
3308 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3310 if ( (lpLVItem->mask & LVIF_TEXT) &&
3311 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3312 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3314 return TRUE;
3318 /***
3319 * DESCRIPTION:
3320 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3322 * PARAMETER(S):
3323 * [I] infoPtr : valid pointer to the listview structure
3324 * [I] lpLVItem : valid pointer to new item atttributes
3325 * [I] isNew : the item being set is being inserted
3326 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3327 * [O] bChanged : will be set to TRUE if the item really changed
3329 * RETURN:
3330 * SUCCESS : TRUE
3331 * FAILURE : FALSE
3333 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3335 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3336 ITEM_INFO *lpItem;
3337 NMLISTVIEW nmlv;
3338 UINT uChanged = 0;
3339 LVITEMW item;
3341 TRACE("()\n");
3343 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3345 if (lpLVItem->mask == 0) return TRUE;
3347 if (infoPtr->dwStyle & LVS_OWNERDATA)
3349 /* a virtual listview we stores only selection and focus */
3350 if (lpLVItem->mask & ~LVIF_STATE)
3351 return FALSE;
3352 lpItem = NULL;
3354 else
3356 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3357 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3358 assert (lpItem);
3361 /* we need to get the lParam and state of the item */
3362 item.iItem = lpLVItem->iItem;
3363 item.iSubItem = lpLVItem->iSubItem;
3364 item.mask = LVIF_STATE | LVIF_PARAM;
3365 item.stateMask = ~0;
3366 item.state = 0;
3367 item.lParam = 0;
3368 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3370 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3371 /* determine what fields will change */
3372 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3373 uChanged |= LVIF_STATE;
3375 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3376 uChanged |= LVIF_IMAGE;
3378 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3379 uChanged |= LVIF_PARAM;
3381 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3382 uChanged |= LVIF_INDENT;
3384 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3385 uChanged |= LVIF_TEXT;
3387 TRACE("uChanged=0x%x\n", uChanged);
3388 if (!uChanged) return TRUE;
3389 *bChanged = TRUE;
3391 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3392 nmlv.iItem = lpLVItem->iItem;
3393 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3394 nmlv.uOldState = item.state;
3395 nmlv.uChanged = uChanged;
3396 nmlv.lParam = item.lParam;
3398 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3399 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3400 /* are enabled */
3401 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3402 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3403 return FALSE;
3405 /* copy information */
3406 if (lpLVItem->mask & LVIF_TEXT)
3407 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3409 if (lpLVItem->mask & LVIF_IMAGE)
3410 lpItem->hdr.iImage = lpLVItem->iImage;
3412 if (lpLVItem->mask & LVIF_PARAM)
3413 lpItem->lParam = lpLVItem->lParam;
3415 if (lpLVItem->mask & LVIF_INDENT)
3416 lpItem->iIndent = lpLVItem->iIndent;
3418 if (uChanged & LVIF_STATE)
3420 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3422 lpItem->state &= ~lpLVItem->stateMask;
3423 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3425 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3427 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3428 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3430 else if (lpLVItem->stateMask & LVIS_SELECTED)
3431 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3433 /* if we are asked to change focus, and we manage it, do it */
3434 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3436 if (lpLVItem->state & LVIS_FOCUSED)
3438 LISTVIEW_SetItemFocus(infoPtr, -1);
3439 infoPtr->nFocusedItem = lpLVItem->iItem;
3440 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3442 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3443 infoPtr->nFocusedItem = -1;
3447 /* if we're inserting the item, we're done */
3448 if (isNew) return TRUE;
3450 /* send LVN_ITEMCHANGED notification */
3451 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3452 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3454 return TRUE;
3457 /***
3458 * DESCRIPTION:
3459 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3461 * PARAMETER(S):
3462 * [I] infoPtr : valid pointer to the listview structure
3463 * [I] lpLVItem : valid pointer to new subitem atttributes
3464 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3465 * [O] bChanged : will be set to TRUE if the item really changed
3467 * RETURN:
3468 * SUCCESS : TRUE
3469 * FAILURE : FALSE
3471 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3473 HDPA hdpaSubItems;
3474 SUBITEM_INFO *lpSubItem;
3476 /* we do not support subitems for virtual listviews */
3477 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3479 /* set subitem only if column is present */
3480 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3482 /* First do some sanity checks */
3483 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3484 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3486 /* get the subitem structure, and create it if not there */
3487 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3488 assert (hdpaSubItems);
3490 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3491 if (!lpSubItem)
3493 SUBITEM_INFO *tmpSubItem;
3494 INT i;
3496 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3497 if (!lpSubItem) return FALSE;
3498 /* we could binary search here, if need be...*/
3499 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3501 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3502 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3504 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3506 Free(lpSubItem);
3507 return FALSE;
3509 lpSubItem->iSubItem = lpLVItem->iSubItem;
3510 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3511 *bChanged = TRUE;
3514 if (lpLVItem->mask & LVIF_IMAGE)
3515 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3517 lpSubItem->hdr.iImage = lpLVItem->iImage;
3518 *bChanged = TRUE;
3521 if (lpLVItem->mask & LVIF_TEXT)
3522 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3524 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3525 *bChanged = TRUE;
3528 return TRUE;
3531 /***
3532 * DESCRIPTION:
3533 * Sets item attributes.
3535 * PARAMETER(S):
3536 * [I] infoPtr : valid pointer to the listview structure
3537 * [I] lpLVItem : new item atttributes
3538 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3540 * RETURN:
3541 * SUCCESS : TRUE
3542 * FAILURE : FALSE
3544 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3546 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3547 LPWSTR pszText = NULL;
3548 BOOL bResult, bChanged = FALSE;
3550 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3552 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3553 return FALSE;
3555 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3556 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3558 pszText = lpLVItem->pszText;
3559 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3562 /* actually set the fields */
3563 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3565 if (lpLVItem->iSubItem)
3566 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3567 else
3568 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3570 /* redraw item, if necessary */
3571 if (bChanged && !infoPtr->bIsDrawing)
3573 /* this little optimization eliminates some nasty flicker */
3574 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3575 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3576 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3577 else
3578 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3580 /* restore text */
3581 if (pszText)
3583 textfreeT(lpLVItem->pszText, isW);
3584 ((LVITEMW *)lpLVItem)->pszText = pszText;
3587 return bResult;
3590 /***
3591 * DESCRIPTION:
3592 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3594 * PARAMETER(S):
3595 * [I] infoPtr : valid pointer to the listview structure
3597 * RETURN:
3598 * item index
3600 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3602 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3603 INT nItem = 0;
3604 SCROLLINFO scrollInfo;
3606 scrollInfo.cbSize = sizeof(SCROLLINFO);
3607 scrollInfo.fMask = SIF_POS;
3609 if (uView == LVS_LIST)
3611 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3612 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3614 else if (uView == LVS_REPORT)
3616 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3617 nItem = scrollInfo.nPos;
3619 else
3621 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3622 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3625 TRACE("nItem=%d\n", nItem);
3627 return nItem;
3631 /***
3632 * DESCRIPTION:
3633 * Erases the background of the given rectangle
3635 * PARAMETER(S):
3636 * [I] infoPtr : valid pointer to the listview structure
3637 * [I] hdc : device context handle
3638 * [I] lprcBox : clipping rectangle
3640 * RETURN:
3641 * Success: TRUE
3642 * Failure: FALSE
3644 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3646 if (!infoPtr->hBkBrush) return FALSE;
3648 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3650 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3653 /***
3654 * DESCRIPTION:
3655 * Draws an item.
3657 * PARAMETER(S):
3658 * [I] infoPtr : valid pointer to the listview structure
3659 * [I] hdc : device context handle
3660 * [I] nItem : item index
3661 * [I] nSubItem : subitem index
3662 * [I] pos : item position in client coordinates
3663 * [I] cdmode : custom draw mode
3665 * RETURN:
3666 * Success: TRUE
3667 * Failure: FALSE
3669 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3671 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3672 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3673 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3674 DWORD cdsubitemmode = CDRF_DODEFAULT;
3675 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3676 NMLVCUSTOMDRAW nmlvcd;
3677 HIMAGELIST himl;
3678 LVITEMW lvItem;
3680 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3682 /* get information needed for drawing the item */
3683 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3684 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3685 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3686 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3687 lvItem.iItem = nItem;
3688 lvItem.iSubItem = nSubItem;
3689 lvItem.state = 0;
3690 lvItem.lParam = 0;
3691 lvItem.cchTextMax = DISP_TEXT_SIZE;
3692 lvItem.pszText = szDispText;
3693 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3694 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3695 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3696 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3697 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3699 /* now check if we need to update the focus rectangle */
3700 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3702 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3703 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3704 OffsetRect(&rcBox, pos.x, pos.y);
3705 OffsetRect(&rcState, pos.x, pos.y);
3706 OffsetRect(&rcIcon, pos.x, pos.y);
3707 OffsetRect(&rcLabel, pos.x, pos.y);
3708 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3709 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3711 /* fill in the custom draw structure */
3712 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3714 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3715 if (cdmode & CDRF_NOTIFYITEMDRAW)
3716 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3717 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3718 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3719 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3720 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3722 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3723 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3725 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3726 prepaint_setup(infoPtr, hdc, &nmlvcd);
3728 /* in full row select, subitems, will just use main item's colors */
3729 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3730 nmlvcd.clrTextBk = CLR_NONE;
3732 /* state icons */
3733 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3735 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3736 if (uStateImage)
3738 TRACE("uStateImage=%d\n", uStateImage);
3739 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3743 /* small icons */
3744 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3745 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3747 TRACE("iImage=%d\n", lvItem.iImage);
3748 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3749 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3752 /* Don't bother painting item being edited */
3753 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3755 /* draw the selection background, if we're drawing the main item */
3756 if (nSubItem == 0)
3758 rcSelect = rcLabel;
3759 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3760 rcSelect.right = rcBox.right;
3762 if (nmlvcd.clrTextBk != CLR_NONE)
3763 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3764 if(lprcFocus) *lprcFocus = rcSelect;
3767 /* figure out the text drawing flags */
3768 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3769 if (uView == LVS_ICON)
3770 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3771 else if (nSubItem)
3773 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3775 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3776 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3777 default: uFormat |= DT_LEFT;
3780 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3782 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3783 else rcLabel.left += LABEL_HOR_PADDING;
3785 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3786 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3788 postpaint:
3789 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3790 notify_postpaint(infoPtr, &nmlvcd);
3791 return TRUE;
3794 /***
3795 * DESCRIPTION:
3796 * Draws listview items when in owner draw mode.
3798 * PARAMETER(S):
3799 * [I] infoPtr : valid pointer to the listview structure
3800 * [I] hdc : device context handle
3802 * RETURN:
3803 * None
3805 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3807 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3808 DWORD cditemmode = CDRF_DODEFAULT;
3809 NMLVCUSTOMDRAW nmlvcd;
3810 POINT Origin, Position;
3811 DRAWITEMSTRUCT dis;
3812 LVITEMW item;
3814 TRACE("()\n");
3816 ZeroMemory(&dis, sizeof(dis));
3818 /* Get scroll info once before loop */
3819 LISTVIEW_GetOrigin(infoPtr, &Origin);
3821 /* iterate through the invalidated rows */
3822 while(iterator_next(i))
3824 item.iItem = i->nItem;
3825 item.iSubItem = 0;
3826 item.mask = LVIF_PARAM | LVIF_STATE;
3827 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3828 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3830 dis.CtlType = ODT_LISTVIEW;
3831 dis.CtlID = uID;
3832 dis.itemID = item.iItem;
3833 dis.itemAction = ODA_DRAWENTIRE;
3834 dis.itemState = 0;
3835 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3836 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3837 dis.hwndItem = infoPtr->hwndSelf;
3838 dis.hDC = hdc;
3839 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3840 dis.rcItem.left = Position.x + Origin.x;
3841 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3842 dis.rcItem.top = Position.y + Origin.y;
3843 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3844 dis.itemData = item.lParam;
3846 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3849 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3850 * structure for the rest. of the paint cycle
3852 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3853 if (cdmode & CDRF_NOTIFYITEMDRAW)
3854 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3856 if (!(cditemmode & CDRF_SKIPDEFAULT))
3858 prepaint_setup (infoPtr, hdc, &nmlvcd);
3859 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3862 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3863 notify_postpaint(infoPtr, &nmlvcd);
3867 /***
3868 * DESCRIPTION:
3869 * Draws listview items when in report display mode.
3871 * PARAMETER(S):
3872 * [I] infoPtr : valid pointer to the listview structure
3873 * [I] hdc : device context handle
3874 * [I] cdmode : custom draw mode
3876 * RETURN:
3877 * None
3879 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3881 INT rgntype;
3882 RECT rcClip, rcItem;
3883 POINT Origin, Position;
3884 RANGE colRange;
3885 ITERATOR j;
3887 TRACE("()\n");
3889 /* figure out what to draw */
3890 rgntype = GetClipBox(hdc, &rcClip);
3891 if (rgntype == NULLREGION) return;
3893 /* Get scroll info once before loop */
3894 LISTVIEW_GetOrigin(infoPtr, &Origin);
3896 /* narrow down the columns we need to paint */
3897 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3899 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3900 if (rcItem.right + Origin.x >= rcClip.left) break;
3902 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3904 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3905 if (rcItem.left + Origin.x < rcClip.right) break;
3907 iterator_rangeitems(&j, colRange);
3909 /* in full row select, we _have_ to draw the main item */
3910 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3911 j.nSpecial = 0;
3913 /* iterate through the invalidated rows */
3914 while(iterator_next(i))
3916 /* iterate through the invalidated columns */
3917 while(iterator_next(&j))
3919 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3920 Position.x += Origin.x;
3921 Position.y += Origin.y;
3923 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3925 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3926 rcItem.top = 0;
3927 rcItem.bottom = infoPtr->nItemHeight;
3928 OffsetRect(&rcItem, Position.x, Position.y);
3929 if (!RectVisible(hdc, &rcItem)) continue;
3932 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3935 iterator_destroy(&j);
3938 /***
3939 * DESCRIPTION:
3940 * Draws listview items when in list display mode.
3942 * PARAMETER(S):
3943 * [I] infoPtr : valid pointer to the listview structure
3944 * [I] hdc : device context handle
3945 * [I] cdmode : custom draw mode
3947 * RETURN:
3948 * None
3950 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3952 POINT Origin, Position;
3954 /* Get scroll info once before loop */
3955 LISTVIEW_GetOrigin(infoPtr, &Origin);
3957 while(iterator_prev(i))
3959 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3960 Position.x += Origin.x;
3961 Position.y += Origin.y;
3963 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3968 /***
3969 * DESCRIPTION:
3970 * Draws listview items.
3972 * PARAMETER(S):
3973 * [I] infoPtr : valid pointer to the listview structure
3974 * [I] hdc : device context handle
3976 * RETURN:
3977 * NoneX
3979 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3981 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3982 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3983 NMLVCUSTOMDRAW nmlvcd;
3984 HFONT hOldFont;
3985 DWORD cdmode;
3986 INT oldBkMode;
3987 RECT rcClient;
3988 ITERATOR i;
3990 LISTVIEW_DUMP(infoPtr);
3992 infoPtr->bIsDrawing = TRUE;
3994 /* save dc values we're gonna trash while drawing */
3995 hOldFont = SelectObject(hdc, infoPtr->hFont);
3996 oldBkMode = GetBkMode(hdc);
3997 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3998 oldTextColor = GetTextColor(hdc);
4000 oldClrTextBk = infoPtr->clrTextBk;
4001 oldClrText = infoPtr->clrText;
4003 infoPtr->cditemmode = CDRF_DODEFAULT;
4005 GetClientRect(infoPtr->hwndSelf, &rcClient);
4006 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4007 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4008 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4009 prepaint_setup(infoPtr, hdc, &nmlvcd);
4011 /* Use these colors to draw the items */
4012 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4013 infoPtr->clrText = nmlvcd.clrText;
4015 /* nothing to draw */
4016 if(infoPtr->nItemCount == 0) goto enddraw;
4018 /* figure out what we need to draw */
4019 iterator_visibleitems(&i, infoPtr, hdc);
4021 /* send cache hint notification */
4022 if (infoPtr->dwStyle & LVS_OWNERDATA)
4024 RANGE range = iterator_range(&i);
4025 NMLVCACHEHINT nmlv;
4027 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4028 nmlv.iFrom = range.lower;
4029 nmlv.iTo = range.upper - 1;
4030 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4033 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4034 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4035 else
4037 if (uView == LVS_REPORT)
4038 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4039 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4040 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4042 /* if we have a focus rect, draw it */
4043 if (infoPtr->bFocus)
4044 DrawFocusRect(hdc, &infoPtr->rcFocus);
4046 iterator_destroy(&i);
4048 enddraw:
4049 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4050 notify_postpaint(infoPtr, &nmlvcd);
4052 infoPtr->clrTextBk = oldClrTextBk;
4053 infoPtr->clrText = oldClrText;
4055 SelectObject(hdc, hOldFont);
4056 SetBkMode(hdc, oldBkMode);
4057 SetBkColor(hdc, infoPtr->clrTextBkDefault);
4058 SetTextColor(hdc, oldTextColor);
4059 infoPtr->bIsDrawing = FALSE;
4063 /***
4064 * DESCRIPTION:
4065 * Calculates the approximate width and height of a given number of items.
4067 * PARAMETER(S):
4068 * [I] infoPtr : valid pointer to the listview structure
4069 * [I] nItemCount : number of items
4070 * [I] wWidth : width
4071 * [I] wHeight : height
4073 * RETURN:
4074 * Returns a DWORD. The width in the low word and the height in high word.
4076 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4077 WORD wWidth, WORD wHeight)
4079 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4080 INT nItemCountPerColumn = 1;
4081 INT nColumnCount = 0;
4082 DWORD dwViewRect = 0;
4084 if (nItemCount == -1)
4085 nItemCount = infoPtr->nItemCount;
4087 if (uView == LVS_LIST)
4089 if (wHeight == 0xFFFF)
4091 /* use current height */
4092 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4095 if (wHeight < infoPtr->nItemHeight)
4096 wHeight = infoPtr->nItemHeight;
4098 if (nItemCount > 0)
4100 if (infoPtr->nItemHeight > 0)
4102 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4103 if (nItemCountPerColumn == 0)
4104 nItemCountPerColumn = 1;
4106 if (nItemCount % nItemCountPerColumn != 0)
4107 nColumnCount = nItemCount / nItemCountPerColumn;
4108 else
4109 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4113 /* Microsoft padding magic */
4114 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4115 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4117 dwViewRect = MAKELONG(wWidth, wHeight);
4119 else if (uView == LVS_REPORT)
4121 RECT rcBox;
4123 if (infoPtr->nItemCount > 0)
4125 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4126 wWidth = rcBox.right - rcBox.left;
4127 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4129 else
4131 /* use current height and width */
4132 if (wHeight == 0xffff)
4133 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4134 if (wWidth == 0xffff)
4135 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4138 dwViewRect = MAKELONG(wWidth, wHeight);
4140 else if (uView == LVS_SMALLICON)
4141 FIXME("uView == LVS_SMALLICON: not implemented\n");
4142 else if (uView == LVS_ICON)
4143 FIXME("uView == LVS_ICON: not implemented\n");
4145 return dwViewRect;
4149 /***
4150 * DESCRIPTION:
4151 * Create a drag image list for the specified item.
4153 * PARAMETER(S):
4154 * [I] infoPtr : valid pointer to the listview structure
4155 * [I] iItem : index of item
4156 * [O] lppt : Upperr-left corner of the image
4158 * RETURN:
4159 * Returns a handle to the image list if successful, NULL otherwise.
4161 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4163 RECT rcItem;
4164 SIZE size;
4165 POINT pos;
4166 HDC hdc, hdcOrig;
4167 HBITMAP hbmp, hOldbmp;
4168 HIMAGELIST dragList = 0;
4169 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4171 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4172 return 0;
4174 rcItem.left = LVIR_BOUNDS;
4175 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4176 return 0;
4178 lppt->x = rcItem.left;
4179 lppt->y = rcItem.top;
4181 size.cx = rcItem.right - rcItem.left;
4182 size.cy = rcItem.bottom - rcItem.top;
4184 hdcOrig = GetDC(infoPtr->hwndSelf);
4185 hdc = CreateCompatibleDC(hdcOrig);
4186 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4187 hOldbmp = SelectObject(hdc, hbmp);
4189 rcItem.left = rcItem.top = 0;
4190 rcItem.right = size.cx;
4191 rcItem.bottom = size.cy;
4192 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4194 pos.x = pos.y = 0;
4195 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4197 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4198 SelectObject(hdc, hOldbmp);
4199 ImageList_Add(dragList, hbmp, 0);
4201 else
4202 SelectObject(hdc, hOldbmp);
4204 DeleteObject(hbmp);
4205 DeleteDC(hdc);
4206 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4208 TRACE("ret=%p\n", dragList);
4210 return dragList;
4214 /***
4215 * DESCRIPTION:
4216 * Removes all listview items and subitems.
4218 * PARAMETER(S):
4219 * [I] infoPtr : valid pointer to the listview structure
4221 * RETURN:
4222 * SUCCESS : TRUE
4223 * FAILURE : FALSE
4225 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4227 NMLISTVIEW nmlv;
4228 HDPA hdpaSubItems = NULL;
4229 BOOL bSuppress;
4230 ITEMHDR *hdrItem;
4231 INT i, j;
4233 TRACE("()\n");
4235 /* we do it directly, to avoid notifications */
4236 ranges_clear(infoPtr->selectionRanges);
4237 infoPtr->nSelectionMark = -1;
4238 infoPtr->nFocusedItem = -1;
4239 SetRectEmpty(&infoPtr->rcFocus);
4240 /* But we are supposed to leave nHotItem as is! */
4243 /* send LVN_DELETEALLITEMS notification */
4244 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4245 nmlv.iItem = -1;
4246 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4248 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4250 /* send LVN_DELETEITEM notification, if not suppressed */
4251 if (!bSuppress) notify_deleteitem(infoPtr, i);
4252 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4254 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4255 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4257 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4258 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4259 Free(hdrItem);
4261 DPA_Destroy(hdpaSubItems);
4262 DPA_DeletePtr(infoPtr->hdpaItems, i);
4264 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4265 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4266 infoPtr->nItemCount --;
4269 LISTVIEW_UpdateScroll(infoPtr);
4271 LISTVIEW_InvalidateList(infoPtr);
4273 return TRUE;
4276 /***
4277 * DESCRIPTION:
4278 * Scrolls, and updates the columns, when a column is changing width.
4280 * PARAMETER(S):
4281 * [I] infoPtr : valid pointer to the listview structure
4282 * [I] nColumn : column to scroll
4283 * [I] dx : amount of scroll, in pixels
4285 * RETURN:
4286 * None.
4288 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4290 COLUMN_INFO *lpColumnInfo;
4291 RECT rcOld, rcCol;
4292 POINT ptOrigin;
4293 INT nCol;
4295 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4296 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4297 rcCol = lpColumnInfo->rcHeader;
4298 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4299 rcCol.left = rcCol.right;
4301 /* ajust the other columns */
4302 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4304 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4305 lpColumnInfo->rcHeader.left += dx;
4306 lpColumnInfo->rcHeader.right += dx;
4309 /* do not update screen if not in report mode */
4310 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4312 /* if we have a focus, must first erase the focus rect */
4313 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4315 /* Need to reset the item width when inserting a new column */
4316 infoPtr->nItemWidth += dx;
4318 LISTVIEW_UpdateScroll(infoPtr);
4319 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4321 /* scroll to cover the deleted column, and invalidate for redraw */
4322 rcOld = infoPtr->rcList;
4323 rcOld.left = ptOrigin.x + rcCol.left + dx;
4324 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4326 /* we can restore focus now */
4327 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4330 /***
4331 * DESCRIPTION:
4332 * Removes a column from the listview control.
4334 * PARAMETER(S):
4335 * [I] infoPtr : valid pointer to the listview structure
4336 * [I] nColumn : column index
4338 * RETURN:
4339 * SUCCESS : TRUE
4340 * FAILURE : FALSE
4342 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4344 RECT rcCol;
4346 TRACE("nColumn=%d\n", nColumn);
4348 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4349 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4351 /* While the MSDN specifically says that column zero should not be deleted,
4352 what actually happens is that the column itself is deleted but no items or subitems
4353 are removed.
4356 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4358 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4359 return FALSE;
4361 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4362 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4364 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4366 SUBITEM_INFO *lpSubItem, *lpDelItem;
4367 HDPA hdpaSubItems;
4368 INT nItem, nSubItem, i;
4370 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4372 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4373 nSubItem = 0;
4374 lpDelItem = 0;
4375 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4377 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4378 if (lpSubItem->iSubItem == nColumn)
4380 nSubItem = i;
4381 lpDelItem = lpSubItem;
4383 else if (lpSubItem->iSubItem > nColumn)
4385 lpSubItem->iSubItem--;
4389 /* if we found our subitem, zapp it */
4390 if (nSubItem > 0)
4392 /* free string */
4393 if (is_textW(lpDelItem->hdr.pszText))
4394 Free(lpDelItem->hdr.pszText);
4396 /* free item */
4397 Free(lpDelItem);
4399 /* free dpa memory */
4400 DPA_DeletePtr(hdpaSubItems, nSubItem);
4405 /* update the other column info */
4406 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4407 LISTVIEW_InvalidateList(infoPtr);
4408 else
4409 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4411 return TRUE;
4414 /***
4415 * DESCRIPTION:
4416 * Invalidates the listview after an item's insertion or deletion.
4418 * PARAMETER(S):
4419 * [I] infoPtr : valid pointer to the listview structure
4420 * [I] nItem : item index
4421 * [I] dir : -1 if deleting, 1 if inserting
4423 * RETURN:
4424 * None
4426 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4428 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4429 INT nPerCol, nItemCol, nItemRow;
4430 RECT rcScroll;
4431 POINT Origin;
4433 /* if we don't refresh, what's the point of scrolling? */
4434 if (!is_redrawing(infoPtr)) return;
4436 assert (abs(dir) == 1);
4438 /* arrange icons if autoarrange is on */
4439 if (is_autoarrange(infoPtr))
4441 BOOL arrange = TRUE;
4442 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4443 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4444 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4447 /* scrollbars need updating */
4448 LISTVIEW_UpdateScroll(infoPtr);
4450 /* figure out the item's position */
4451 if (uView == LVS_REPORT)
4452 nPerCol = infoPtr->nItemCount + 1;
4453 else if (uView == LVS_LIST)
4454 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4455 else /* LVS_ICON, or LVS_SMALLICON */
4456 return;
4458 nItemCol = nItem / nPerCol;
4459 nItemRow = nItem % nPerCol;
4460 LISTVIEW_GetOrigin(infoPtr, &Origin);
4462 /* move the items below up a slot */
4463 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4464 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4465 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4466 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4467 OffsetRect(&rcScroll, Origin.x, Origin.y);
4468 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4469 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4471 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4472 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4473 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4476 /* report has only that column, so we're done */
4477 if (uView == LVS_REPORT) return;
4479 /* now for LISTs, we have to deal with the columns to the right */
4480 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4481 rcScroll.top = 0;
4482 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4483 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4484 OffsetRect(&rcScroll, Origin.x, Origin.y);
4485 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4486 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4487 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4490 /***
4491 * DESCRIPTION:
4492 * Removes an item from the listview control.
4494 * PARAMETER(S):
4495 * [I] infoPtr : valid pointer to the listview structure
4496 * [I] nItem : item index
4498 * RETURN:
4499 * SUCCESS : TRUE
4500 * FAILURE : FALSE
4502 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4504 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4505 LVITEMW item;
4507 TRACE("(nItem=%d)\n", nItem);
4509 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4511 /* remove selection, and focus */
4512 item.state = 0;
4513 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4514 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4516 /* send LVN_DELETEITEM notification. */
4517 notify_deleteitem(infoPtr, nItem);
4519 /* we need to do this here, because we'll be deleting stuff */
4520 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4521 LISTVIEW_InvalidateItem(infoPtr, nItem);
4523 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4525 HDPA hdpaSubItems;
4526 ITEMHDR *hdrItem;
4527 INT i;
4529 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4530 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4532 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4533 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4534 Free(hdrItem);
4536 DPA_Destroy(hdpaSubItems);
4539 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4541 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4542 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4545 infoPtr->nItemCount--;
4546 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4548 /* now is the invalidation fun */
4549 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4550 return TRUE;
4554 /***
4555 * DESCRIPTION:
4556 * Callback implementation for editlabel control
4558 * PARAMETER(S):
4559 * [I] infoPtr : valid pointer to the listview structure
4560 * [I] pszText : modified text
4561 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4563 * RETURN:
4564 * SUCCESS : TRUE
4565 * FAILURE : FALSE
4567 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4569 NMLVDISPINFOW dispInfo;
4571 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4573 ZeroMemory(&dispInfo, sizeof(dispInfo));
4574 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4575 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4576 dispInfo.item.iSubItem = 0;
4577 dispInfo.item.stateMask = ~0;
4578 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4579 /* add the text from the edit in */
4580 dispInfo.item.mask |= LVIF_TEXT;
4581 dispInfo.item.pszText = pszText;
4582 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4584 /* Do we need to update the Item Text */
4585 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4586 if (!pszText) return TRUE;
4588 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4590 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4591 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4592 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4594 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4595 return TRUE;
4599 ZeroMemory(&dispInfo, sizeof(dispInfo));
4600 dispInfo.item.mask = LVIF_TEXT;
4601 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4602 dispInfo.item.iSubItem = 0;
4603 dispInfo.item.pszText = pszText;
4604 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4605 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4608 /***
4609 * DESCRIPTION:
4610 * Begin in place editing of specified list view item
4612 * PARAMETER(S):
4613 * [I] infoPtr : valid pointer to the listview structure
4614 * [I] nItem : item index
4615 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4617 * RETURN:
4618 * SUCCESS : TRUE
4619 * FAILURE : FALSE
4621 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4623 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4624 NMLVDISPINFOW dispInfo;
4625 RECT rect;
4627 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4629 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4630 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4632 infoPtr->nEditLabelItem = nItem;
4634 /* Is the EditBox still there, if so remove it */
4635 if(infoPtr->hwndEdit != 0)
4637 SetFocus(infoPtr->hwndSelf);
4638 infoPtr->hwndEdit = 0;
4641 LISTVIEW_SetSelection(infoPtr, nItem);
4642 LISTVIEW_SetItemFocus(infoPtr, nItem);
4643 LISTVIEW_InvalidateItem(infoPtr, nItem);
4645 rect.left = LVIR_LABEL;
4646 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4648 ZeroMemory(&dispInfo, sizeof(dispInfo));
4649 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4650 dispInfo.item.iItem = nItem;
4651 dispInfo.item.iSubItem = 0;
4652 dispInfo.item.stateMask = ~0;
4653 dispInfo.item.pszText = szDispText;
4654 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4655 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4657 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4658 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4659 if (!infoPtr->hwndEdit) return 0;
4661 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4663 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4664 infoPtr->hwndEdit = 0;
4665 return 0;
4668 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4669 SetFocus(infoPtr->hwndEdit);
4670 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4671 return infoPtr->hwndEdit;
4675 /***
4676 * DESCRIPTION:
4677 * Ensures the specified item is visible, scrolling into view if necessary.
4679 * PARAMETER(S):
4680 * [I] infoPtr : valid pointer to the listview structure
4681 * [I] nItem : item index
4682 * [I] bPartial : partially or entirely visible
4684 * RETURN:
4685 * SUCCESS : TRUE
4686 * FAILURE : FALSE
4688 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4690 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4691 INT nScrollPosHeight = 0;
4692 INT nScrollPosWidth = 0;
4693 INT nHorzAdjust = 0;
4694 INT nVertAdjust = 0;
4695 INT nHorzDiff = 0;
4696 INT nVertDiff = 0;
4697 RECT rcItem, rcTemp;
4699 rcItem.left = LVIR_BOUNDS;
4700 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4702 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4704 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4706 /* scroll left/right, but in LVS_REPORT mode */
4707 if (uView == LVS_LIST)
4708 nScrollPosWidth = infoPtr->nItemWidth;
4709 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4710 nScrollPosWidth = 1;
4712 if (rcItem.left < infoPtr->rcList.left)
4714 nHorzAdjust = -1;
4715 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4717 else
4719 nHorzAdjust = 1;
4720 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4724 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4726 /* scroll up/down, but not in LVS_LIST mode */
4727 if (uView == LVS_REPORT)
4728 nScrollPosHeight = infoPtr->nItemHeight;
4729 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4730 nScrollPosHeight = 1;
4732 if (rcItem.top < infoPtr->rcList.top)
4734 nVertAdjust = -1;
4735 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4737 else
4739 nVertAdjust = 1;
4740 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4744 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4746 if (nScrollPosWidth)
4748 INT diff = nHorzDiff / nScrollPosWidth;
4749 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4750 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4753 if (nScrollPosHeight)
4755 INT diff = nVertDiff / nScrollPosHeight;
4756 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4757 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4760 return TRUE;
4763 /***
4764 * DESCRIPTION:
4765 * Searches for an item with specific characteristics.
4767 * PARAMETER(S):
4768 * [I] hwnd : window handle
4769 * [I] nStart : base item index
4770 * [I] lpFindInfo : item information to look for
4772 * RETURN:
4773 * SUCCESS : index of item
4774 * FAILURE : -1
4776 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4777 const LVFINDINFOW *lpFindInfo)
4779 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4780 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4781 BOOL bWrap = FALSE, bNearest = FALSE;
4782 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4783 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4784 POINT Position, Destination;
4785 LVITEMW lvItem;
4787 if (!lpFindInfo || nItem < 0) return -1;
4789 lvItem.mask = 0;
4790 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4792 lvItem.mask |= LVIF_TEXT;
4793 lvItem.pszText = szDispText;
4794 lvItem.cchTextMax = DISP_TEXT_SIZE;
4797 if (lpFindInfo->flags & LVFI_WRAP)
4798 bWrap = TRUE;
4800 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4801 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4803 POINT Origin;
4804 RECT rcArea;
4806 LISTVIEW_GetOrigin(infoPtr, &Origin);
4807 Destination.x = lpFindInfo->pt.x - Origin.x;
4808 Destination.y = lpFindInfo->pt.y - Origin.y;
4809 switch(lpFindInfo->vkDirection)
4811 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4812 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4813 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4814 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4815 case VK_HOME: Destination.x = Destination.y = 0; break;
4816 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4817 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4818 case VK_END:
4819 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4820 Destination.x = rcArea.right;
4821 Destination.y = rcArea.bottom;
4822 break;
4823 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4825 bNearest = TRUE;
4827 else Destination.x = Destination.y = 0;
4829 /* if LVFI_PARAM is specified, all other flags are ignored */
4830 if (lpFindInfo->flags & LVFI_PARAM)
4832 lvItem.mask |= LVIF_PARAM;
4833 bNearest = FALSE;
4834 lvItem.mask &= ~LVIF_TEXT;
4837 again:
4838 for (; nItem < nLast; nItem++)
4840 lvItem.iItem = nItem;
4841 lvItem.iSubItem = 0;
4842 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4844 if (lvItem.mask & LVIF_PARAM)
4846 if (lpFindInfo->lParam == lvItem.lParam)
4847 return nItem;
4848 else
4849 continue;
4852 if (lvItem.mask & LVIF_TEXT)
4854 if (lpFindInfo->flags & LVFI_PARTIAL)
4856 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4858 else
4860 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4864 if (!bNearest) return nItem;
4866 /* This is very inefficient. To do a good job here,
4867 * we need a sorted array of (x,y) item positions */
4868 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4870 /* compute the distance^2 to the destination */
4871 xdist = Destination.x - Position.x;
4872 ydist = Destination.y - Position.y;
4873 dist = xdist * xdist + ydist * ydist;
4875 /* remember the distance, and item if it's closer */
4876 if (dist < mindist)
4878 mindist = dist;
4879 nNearestItem = nItem;
4883 if (bWrap)
4885 nItem = 0;
4886 nLast = min(nStart + 1, infoPtr->nItemCount);
4887 bWrap = FALSE;
4888 goto again;
4891 return nNearestItem;
4894 /***
4895 * DESCRIPTION:
4896 * Searches for an item with specific characteristics.
4898 * PARAMETER(S):
4899 * [I] hwnd : window handle
4900 * [I] nStart : base item index
4901 * [I] lpFindInfo : item information to look for
4903 * RETURN:
4904 * SUCCESS : index of item
4905 * FAILURE : -1
4907 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4908 const LVFINDINFOA *lpFindInfo)
4910 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4911 LVFINDINFOW fiw;
4912 INT res;
4914 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4915 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4916 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4917 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4918 return res;
4921 /***
4922 * DESCRIPTION:
4923 * Retrieves the background image of the listview control.
4925 * PARAMETER(S):
4926 * [I] infoPtr : valid pointer to the listview structure
4927 * [O] lpBkImage : background image attributes
4929 * RETURN:
4930 * SUCCESS : TRUE
4931 * FAILURE : FALSE
4933 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4934 /* { */
4935 /* FIXME (listview, "empty stub!\n"); */
4936 /* return FALSE; */
4937 /* } */
4939 /***
4940 * DESCRIPTION:
4941 * Retrieves column attributes.
4943 * PARAMETER(S):
4944 * [I] infoPtr : valid pointer to the listview structure
4945 * [I] nColumn : column index
4946 * [IO] lpColumn : column information
4947 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4948 * otherwise it is in fact a LPLVCOLUMNA
4950 * RETURN:
4951 * SUCCESS : TRUE
4952 * FAILURE : FALSE
4954 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4956 COLUMN_INFO *lpColumnInfo;
4957 HDITEMW hdi;
4959 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4960 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4962 /* initialize memory */
4963 ZeroMemory(&hdi, sizeof(hdi));
4965 if (lpColumn->mask & LVCF_TEXT)
4967 hdi.mask |= HDI_TEXT;
4968 hdi.pszText = lpColumn->pszText;
4969 hdi.cchTextMax = lpColumn->cchTextMax;
4972 if (lpColumn->mask & LVCF_IMAGE)
4973 hdi.mask |= HDI_IMAGE;
4975 if (lpColumn->mask & LVCF_ORDER)
4976 hdi.mask |= HDI_ORDER;
4978 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4980 if (lpColumn->mask & LVCF_FMT)
4981 lpColumn->fmt = lpColumnInfo->fmt;
4983 if (lpColumn->mask & LVCF_WIDTH)
4984 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4986 if (lpColumn->mask & LVCF_IMAGE)
4987 lpColumn->iImage = hdi.iImage;
4989 if (lpColumn->mask & LVCF_ORDER)
4990 lpColumn->iOrder = hdi.iOrder;
4992 return TRUE;
4996 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4998 INT i;
5000 if (!lpiArray)
5001 return FALSE;
5003 /* FIXME: little hack */
5004 for (i = 0; i < iCount; i++)
5005 lpiArray[i] = i;
5007 return TRUE;
5010 /***
5011 * DESCRIPTION:
5012 * Retrieves the column width.
5014 * PARAMETER(S):
5015 * [I] infoPtr : valid pointer to the listview structure
5016 * [I] int : column index
5018 * RETURN:
5019 * SUCCESS : column width
5020 * FAILURE : zero
5022 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5024 INT nColumnWidth = 0;
5025 RECT rcHeader;
5027 TRACE("nColumn=%d\n", nColumn);
5029 /* we have a 'column' in LIST and REPORT mode only */
5030 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5032 case LVS_LIST:
5033 nColumnWidth = infoPtr->nItemWidth;
5034 break;
5035 case LVS_REPORT:
5036 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5037 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5038 nColumnWidth = rcHeader.right - rcHeader.left;
5039 break;
5042 TRACE("nColumnWidth=%d\n", nColumnWidth);
5043 return nColumnWidth;
5046 /***
5047 * DESCRIPTION:
5048 * In list or report display mode, retrieves the number of items that can fit
5049 * vertically in the visible area. In icon or small icon display mode,
5050 * retrieves the total number of visible items.
5052 * PARAMETER(S):
5053 * [I] infoPtr : valid pointer to the listview structure
5055 * RETURN:
5056 * Number of fully visible items.
5058 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5060 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5062 case LVS_ICON:
5063 case LVS_SMALLICON:
5064 return infoPtr->nItemCount;
5065 case LVS_REPORT:
5066 return LISTVIEW_GetCountPerColumn(infoPtr);
5067 case LVS_LIST:
5068 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5070 assert(FALSE);
5071 return 0;
5074 /***
5075 * DESCRIPTION:
5076 * Retrieves an image list handle.
5078 * PARAMETER(S):
5079 * [I] infoPtr : valid pointer to the listview structure
5080 * [I] nImageList : image list identifier
5082 * RETURN:
5083 * SUCCESS : image list handle
5084 * FAILURE : NULL
5086 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5088 switch (nImageList)
5090 case LVSIL_NORMAL: return infoPtr->himlNormal;
5091 case LVSIL_SMALL: return infoPtr->himlSmall;
5092 case LVSIL_STATE: return infoPtr->himlState;
5094 return NULL;
5097 /* LISTVIEW_GetISearchString */
5099 /***
5100 * DESCRIPTION:
5101 * Retrieves item attributes.
5103 * PARAMETER(S):
5104 * [I] hwnd : window handle
5105 * [IO] lpLVItem : item info
5106 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5107 * if FALSE, the lpLVItem is a LPLVITEMA.
5109 * NOTE:
5110 * This is the internal 'GetItem' interface -- it tries to
5111 * be smart, and avoids text copies, if possible, by modifing
5112 * lpLVItem->pszText to point to the text string. Please note
5113 * that this is not always possible (e.g. OWNERDATA), so on
5114 * entry you *must* supply valid values for pszText, and cchTextMax.
5115 * The only difference to the documented interface is that upon
5116 * return, you should use *only* the lpLVItem->pszText, rather than
5117 * the buffer pointer you provided on input. Most code already does
5118 * that, so it's not a problem.
5119 * For the two cases when the text must be copied (that is,
5120 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5122 * RETURN:
5123 * SUCCESS : TRUE
5124 * FAILURE : FALSE
5126 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5128 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5129 NMLVDISPINFOW dispInfo;
5130 ITEM_INFO *lpItem;
5131 ITEMHDR* pItemHdr;
5132 HDPA hdpaSubItems;
5133 INT isubitem;
5135 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5137 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5138 return FALSE;
5140 if (lpLVItem->mask == 0) return TRUE;
5142 /* make a local copy */
5143 isubitem = lpLVItem->iSubItem;
5145 /* a quick optimization if all we're asked is the focus state
5146 * these queries are worth optimising since they are common,
5147 * and can be answered in constant time, without the heavy accesses */
5148 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5149 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5151 lpLVItem->state = 0;
5152 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5153 lpLVItem->state |= LVIS_FOCUSED;
5154 return TRUE;
5157 ZeroMemory(&dispInfo, sizeof(dispInfo));
5159 /* if the app stores all the data, handle it separately */
5160 if (infoPtr->dwStyle & LVS_OWNERDATA)
5162 dispInfo.item.state = 0;
5164 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5165 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5167 /* NOTE: copy only fields which we _know_ are initialized, some apps
5168 * depend on the uninitialized fields being 0 */
5169 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5170 dispInfo.item.iItem = lpLVItem->iItem;
5171 dispInfo.item.iSubItem = isubitem;
5172 if (lpLVItem->mask & LVIF_TEXT)
5174 dispInfo.item.pszText = lpLVItem->pszText;
5175 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5177 if (lpLVItem->mask & LVIF_STATE)
5178 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5179 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5180 dispInfo.item.stateMask = lpLVItem->stateMask;
5181 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5183 /* full size structure expected - _WIN32IE >= 0x560 */
5184 *lpLVItem = dispInfo.item;
5186 else if (lpLVItem->mask & LVIF_INDENT)
5188 /* indent member expected - _WIN32IE >= 0x300 */
5189 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5191 else
5193 /* minimal structure expected */
5194 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5196 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5199 /* make sure lParam is zeroed out */
5200 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5202 /* we store only a little state, so if we're not asked, we're done */
5203 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5205 /* if focus is handled by us, report it */
5206 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5208 lpLVItem->state &= ~LVIS_FOCUSED;
5209 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5210 lpLVItem->state |= LVIS_FOCUSED;
5213 /* and do the same for selection, if we handle it */
5214 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5216 lpLVItem->state &= ~LVIS_SELECTED;
5217 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5218 lpLVItem->state |= LVIS_SELECTED;
5221 return TRUE;
5224 /* find the item and subitem structures before we proceed */
5225 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5226 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5227 assert (lpItem);
5229 if (isubitem)
5231 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5232 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5233 if (!lpSubItem)
5235 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5236 isubitem = 0;
5239 else
5240 pItemHdr = &lpItem->hdr;
5242 /* Do we need to query the state from the app? */
5243 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5245 dispInfo.item.mask |= LVIF_STATE;
5246 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5249 /* Do we need to enquire about the image? */
5250 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5251 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5253 dispInfo.item.mask |= LVIF_IMAGE;
5254 dispInfo.item.iImage = I_IMAGECALLBACK;
5257 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5258 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5260 dispInfo.item.mask |= LVIF_TEXT;
5261 dispInfo.item.pszText = lpLVItem->pszText;
5262 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5263 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5264 *dispInfo.item.pszText = '\0';
5267 /* If we don't have all the requested info, query the application */
5268 if (dispInfo.item.mask != 0)
5270 dispInfo.item.iItem = lpLVItem->iItem;
5271 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5272 dispInfo.item.lParam = lpItem->lParam;
5273 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5274 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5277 /* we should not store values for subitems */
5278 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5280 /* Now, handle the iImage field */
5281 if (dispInfo.item.mask & LVIF_IMAGE)
5283 lpLVItem->iImage = dispInfo.item.iImage;
5284 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5285 pItemHdr->iImage = dispInfo.item.iImage;
5287 else if (lpLVItem->mask & LVIF_IMAGE)
5289 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5290 lpLVItem->iImage = pItemHdr->iImage;
5291 else
5292 lpLVItem->iImage = 0;
5295 /* The pszText field */
5296 if (dispInfo.item.mask & LVIF_TEXT)
5298 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5299 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5301 lpLVItem->pszText = dispInfo.item.pszText;
5303 else if (lpLVItem->mask & LVIF_TEXT)
5305 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5306 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5309 /* if this is a subitem, we're done */
5310 if (isubitem) return TRUE;
5312 /* Next is the lParam field */
5313 if (dispInfo.item.mask & LVIF_PARAM)
5315 lpLVItem->lParam = dispInfo.item.lParam;
5316 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5317 lpItem->lParam = dispInfo.item.lParam;
5319 else if (lpLVItem->mask & LVIF_PARAM)
5320 lpLVItem->lParam = lpItem->lParam;
5322 /* ... the state field (this one is different due to uCallbackmask) */
5323 if (lpLVItem->mask & LVIF_STATE)
5325 lpLVItem->state = lpItem->state;
5326 if (dispInfo.item.mask & LVIF_STATE)
5328 lpLVItem->state &= ~dispInfo.item.stateMask;
5329 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5331 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5333 lpLVItem->state &= ~LVIS_FOCUSED;
5334 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5335 lpLVItem->state |= LVIS_FOCUSED;
5337 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5339 lpLVItem->state &= ~LVIS_SELECTED;
5340 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5341 lpLVItem->state |= LVIS_SELECTED;
5345 /* and last, but not least, the indent field */
5346 if (lpLVItem->mask & LVIF_INDENT)
5347 lpLVItem->iIndent = lpItem->iIndent;
5349 return TRUE;
5352 /***
5353 * DESCRIPTION:
5354 * Retrieves item attributes.
5356 * PARAMETER(S):
5357 * [I] hwnd : window handle
5358 * [IO] lpLVItem : item info
5359 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5360 * if FALSE, the lpLVItem is a LPLVITEMA.
5362 * NOTE:
5363 * This is the external 'GetItem' interface -- it properly copies
5364 * the text in the provided buffer.
5366 * RETURN:
5367 * SUCCESS : TRUE
5368 * FAILURE : FALSE
5370 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5372 LPWSTR pszText;
5373 BOOL bResult;
5375 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5376 return FALSE;
5378 pszText = lpLVItem->pszText;
5379 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5380 if (bResult && lpLVItem->pszText != pszText)
5381 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5382 lpLVItem->pszText = pszText;
5384 return bResult;
5388 /***
5389 * DESCRIPTION:
5390 * Retrieves the position (upper-left) of the listview control item.
5391 * Note that for LVS_ICON style, the upper-left is that of the icon
5392 * and not the bounding box.
5394 * PARAMETER(S):
5395 * [I] infoPtr : valid pointer to the listview structure
5396 * [I] nItem : item index
5397 * [O] lpptPosition : coordinate information
5399 * RETURN:
5400 * SUCCESS : TRUE
5401 * FAILURE : FALSE
5403 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5405 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5406 POINT Origin;
5408 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5410 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5412 LISTVIEW_GetOrigin(infoPtr, &Origin);
5413 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5415 if (uView == LVS_ICON)
5417 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5418 lpptPosition->y += ICON_TOP_PADDING;
5420 lpptPosition->x += Origin.x;
5421 lpptPosition->y += Origin.y;
5423 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5424 return TRUE;
5428 /***
5429 * DESCRIPTION:
5430 * Retrieves the bounding rectangle for a listview control item.
5432 * PARAMETER(S):
5433 * [I] infoPtr : valid pointer to the listview structure
5434 * [I] nItem : item index
5435 * [IO] lprc : bounding rectangle coordinates
5436 * lprc->left specifies the portion of the item for which the bounding
5437 * rectangle will be retrieved.
5439 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5440 * including the icon and label.
5442 * * For LVS_ICON
5443 * * Experiment shows that native control returns:
5444 * * width = min (48, length of text line)
5445 * * .left = position.x - (width - iconsize.cx)/2
5446 * * .right = .left + width
5447 * * height = #lines of text * ntmHeight + icon height + 8
5448 * * .top = position.y - 2
5449 * * .bottom = .top + height
5450 * * separation between items .y = itemSpacing.cy - height
5451 * * .x = itemSpacing.cx - width
5452 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5454 * * For LVS_ICON
5455 * * Experiment shows that native control returns:
5456 * * width = iconSize.cx + 16
5457 * * .left = position.x - (width - iconsize.cx)/2
5458 * * .right = .left + width
5459 * * height = iconSize.cy + 4
5460 * * .top = position.y - 2
5461 * * .bottom = .top + height
5462 * * separation between items .y = itemSpacing.cy - height
5463 * * .x = itemSpacing.cx - width
5464 * LVIR_LABEL Returns the bounding rectangle of the item text.
5466 * * For LVS_ICON
5467 * * Experiment shows that native control returns:
5468 * * width = text length
5469 * * .left = position.x - width/2
5470 * * .right = .left + width
5471 * * height = ntmH * linecount + 2
5472 * * .top = position.y + iconSize.cy + 6
5473 * * .bottom = .top + height
5474 * * separation between items .y = itemSpacing.cy - height
5475 * * .x = itemSpacing.cx - width
5476 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5477 * rectangles, but excludes columns in report view.
5479 * RETURN:
5480 * SUCCESS : TRUE
5481 * FAILURE : FALSE
5483 * NOTES
5484 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5485 * upon whether the window has the focus currently and on whether the item
5486 * is the one with the focus. Ensure that the control's record of which
5487 * item has the focus agrees with the items' records.
5489 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5491 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5492 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5493 BOOL doLabel = TRUE, oversizedBox = FALSE;
5494 POINT Position, Origin;
5495 LVITEMW lvItem;
5496 RECT label_rect;
5498 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5500 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5502 LISTVIEW_GetOrigin(infoPtr, &Origin);
5503 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5505 /* Be smart and try to figure out the minimum we have to do */
5506 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5507 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5508 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5509 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5510 oversizedBox = TRUE;
5512 /* get what we need from the item before hand, so we make
5513 * only one request. This can speed up things, if data
5514 * is stored on the app side */
5515 lvItem.mask = 0;
5516 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5517 if (doLabel) lvItem.mask |= LVIF_TEXT;
5518 lvItem.iItem = nItem;
5519 lvItem.iSubItem = 0;
5520 lvItem.pszText = szDispText;
5521 lvItem.cchTextMax = DISP_TEXT_SIZE;
5522 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5523 /* we got the state already up, simulate it here, to avoid a reget */
5524 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5526 lvItem.mask |= LVIF_STATE;
5527 lvItem.stateMask = LVIS_FOCUSED;
5528 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5531 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5532 lprc->left = LVIR_BOUNDS;
5533 switch(lprc->left)
5535 case LVIR_ICON:
5536 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5537 break;
5539 case LVIR_LABEL:
5540 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5541 break;
5543 case LVIR_BOUNDS:
5544 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5545 break;
5547 case LVIR_SELECTBOUNDS:
5548 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5549 UnionRect(lprc, lprc, &label_rect);
5550 break;
5552 default:
5553 WARN("Unknown value: %ld\n", lprc->left);
5554 return FALSE;
5557 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5559 TRACE(" rect=%s\n", debugrect(lprc));
5561 return TRUE;
5564 /***
5565 * DESCRIPTION:
5566 * Retrieves the spacing between listview control items.
5568 * PARAMETER(S):
5569 * [I] infoPtr : valid pointer to the listview structure
5570 * [IO] lprc : rectangle to receive the output
5571 * on input, lprc->top = nSubItem
5572 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5574 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5575 * not only those of the first column.
5576 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5578 * RETURN:
5579 * TRUE: success
5580 * FALSE: failure
5582 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5584 POINT Position;
5585 LVITEMW lvItem;
5587 if (!lprc) return FALSE;
5589 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5590 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5591 if (lprc->top == 0)
5592 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5594 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5596 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5598 lvItem.mask = 0;
5599 lvItem.iItem = nItem;
5600 lvItem.iSubItem = lprc->top;
5602 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5603 switch(lprc->left)
5605 case LVIR_ICON:
5606 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5607 break;
5609 case LVIR_LABEL:
5610 case LVIR_BOUNDS:
5611 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5612 break;
5614 default:
5615 ERR("Unknown bounds=%ld\n", lprc->left);
5616 return FALSE;
5619 OffsetRect(lprc, Position.x, Position.y);
5620 return TRUE;
5624 /***
5625 * DESCRIPTION:
5626 * Retrieves the width of a label.
5628 * PARAMETER(S):
5629 * [I] infoPtr : valid pointer to the listview structure
5631 * RETURN:
5632 * SUCCESS : string width (in pixels)
5633 * FAILURE : zero
5635 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5637 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5638 LVITEMW lvItem;
5640 TRACE("(nItem=%d)\n", nItem);
5642 lvItem.mask = LVIF_TEXT;
5643 lvItem.iItem = nItem;
5644 lvItem.iSubItem = 0;
5645 lvItem.pszText = szDispText;
5646 lvItem.cchTextMax = DISP_TEXT_SIZE;
5647 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5649 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5652 /***
5653 * DESCRIPTION:
5654 * Retrieves the spacing between listview control items.
5656 * PARAMETER(S):
5657 * [I] infoPtr : valid pointer to the listview structure
5658 * [I] bSmall : flag for small or large icon
5660 * RETURN:
5661 * Horizontal + vertical spacing
5663 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5665 LONG lResult;
5667 if (!bSmall)
5669 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5671 else
5673 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5674 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5675 else
5676 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5678 return lResult;
5681 /***
5682 * DESCRIPTION:
5683 * Retrieves the state of a listview control item.
5685 * PARAMETER(S):
5686 * [I] infoPtr : valid pointer to the listview structure
5687 * [I] nItem : item index
5688 * [I] uMask : state mask
5690 * RETURN:
5691 * State specified by the mask.
5693 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5695 LVITEMW lvItem;
5697 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5699 lvItem.iItem = nItem;
5700 lvItem.iSubItem = 0;
5701 lvItem.mask = LVIF_STATE;
5702 lvItem.stateMask = uMask;
5703 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5705 return lvItem.state & uMask;
5708 /***
5709 * DESCRIPTION:
5710 * Retrieves the text of a listview control item or subitem.
5712 * PARAMETER(S):
5713 * [I] hwnd : window handle
5714 * [I] nItem : item index
5715 * [IO] lpLVItem : item information
5716 * [I] isW : TRUE if lpLVItem is Unicode
5718 * RETURN:
5719 * SUCCESS : string length
5720 * FAILURE : 0
5722 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5724 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5726 lpLVItem->mask = LVIF_TEXT;
5727 lpLVItem->iItem = nItem;
5728 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5730 return textlenT(lpLVItem->pszText, isW);
5733 /***
5734 * DESCRIPTION:
5735 * Searches for an item based on properties + relationships.
5737 * PARAMETER(S):
5738 * [I] infoPtr : valid pointer to the listview structure
5739 * [I] nItem : item index
5740 * [I] uFlags : relationship flag
5742 * RETURN:
5743 * SUCCESS : item index
5744 * FAILURE : -1
5746 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5748 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5749 UINT uMask = 0;
5750 LVFINDINFOW lvFindInfo;
5751 INT nCountPerColumn;
5752 INT nCountPerRow;
5753 INT i;
5755 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5756 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5758 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5760 if (uFlags & LVNI_CUT)
5761 uMask |= LVIS_CUT;
5763 if (uFlags & LVNI_DROPHILITED)
5764 uMask |= LVIS_DROPHILITED;
5766 if (uFlags & LVNI_FOCUSED)
5767 uMask |= LVIS_FOCUSED;
5769 if (uFlags & LVNI_SELECTED)
5770 uMask |= LVIS_SELECTED;
5772 /* if we're asked for the focused item, that's only one,
5773 * so it's worth optimizing */
5774 if (uFlags & LVNI_FOCUSED)
5776 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5777 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5780 if (uFlags & LVNI_ABOVE)
5782 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5784 while (nItem >= 0)
5786 nItem--;
5787 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5788 return nItem;
5791 else
5793 /* Special case for autoarrange - move 'til the top of a list */
5794 if (is_autoarrange(infoPtr))
5796 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5797 while (nItem - nCountPerRow >= 0)
5799 nItem -= nCountPerRow;
5800 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5801 return nItem;
5803 return -1;
5805 lvFindInfo.flags = LVFI_NEARESTXY;
5806 lvFindInfo.vkDirection = VK_UP;
5807 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5808 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5810 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5811 return nItem;
5815 else if (uFlags & LVNI_BELOW)
5817 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5819 while (nItem < infoPtr->nItemCount)
5821 nItem++;
5822 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5823 return nItem;
5826 else
5828 /* Special case for autoarrange - move 'til the bottom of a list */
5829 if (is_autoarrange(infoPtr))
5831 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5832 while (nItem + nCountPerRow < infoPtr->nItemCount )
5834 nItem += nCountPerRow;
5835 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5836 return nItem;
5838 return -1;
5840 lvFindInfo.flags = LVFI_NEARESTXY;
5841 lvFindInfo.vkDirection = VK_DOWN;
5842 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5843 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5845 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5846 return nItem;
5850 else if (uFlags & LVNI_TOLEFT)
5852 if (uView == LVS_LIST)
5854 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5855 while (nItem - nCountPerColumn >= 0)
5857 nItem -= nCountPerColumn;
5858 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5859 return nItem;
5862 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5864 /* Special case for autoarrange - move 'ti the beginning of a row */
5865 if (is_autoarrange(infoPtr))
5867 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5868 while (nItem % nCountPerRow > 0)
5870 nItem --;
5871 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5872 return nItem;
5874 return -1;
5876 lvFindInfo.flags = LVFI_NEARESTXY;
5877 lvFindInfo.vkDirection = VK_LEFT;
5878 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5879 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5881 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5882 return nItem;
5886 else if (uFlags & LVNI_TORIGHT)
5888 if (uView == LVS_LIST)
5890 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5891 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5893 nItem += nCountPerColumn;
5894 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5895 return nItem;
5898 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5900 /* Special case for autoarrange - move 'til the end of a row */
5901 if (is_autoarrange(infoPtr))
5903 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5904 while (nItem % nCountPerRow < nCountPerRow - 1 )
5906 nItem ++;
5907 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5908 return nItem;
5910 return -1;
5912 lvFindInfo.flags = LVFI_NEARESTXY;
5913 lvFindInfo.vkDirection = VK_RIGHT;
5914 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5915 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5917 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5918 return nItem;
5922 else
5924 nItem++;
5926 /* search by index */
5927 for (i = nItem; i < infoPtr->nItemCount; i++)
5929 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5930 return i;
5934 return -1;
5937 /* LISTVIEW_GetNumberOfWorkAreas */
5939 /***
5940 * DESCRIPTION:
5941 * Retrieves the origin coordinates when in icon or small icon display mode.
5943 * PARAMETER(S):
5944 * [I] infoPtr : valid pointer to the listview structure
5945 * [O] lpptOrigin : coordinate information
5947 * RETURN:
5948 * None.
5950 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5952 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5953 INT nHorzPos = 0, nVertPos = 0;
5954 SCROLLINFO scrollInfo;
5956 scrollInfo.cbSize = sizeof(SCROLLINFO);
5957 scrollInfo.fMask = SIF_POS;
5959 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5960 nHorzPos = scrollInfo.nPos;
5961 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5962 nVertPos = scrollInfo.nPos;
5964 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5966 lpptOrigin->x = infoPtr->rcList.left;
5967 lpptOrigin->y = infoPtr->rcList.top;
5968 if (uView == LVS_LIST)
5969 nHorzPos *= infoPtr->nItemWidth;
5970 else if (uView == LVS_REPORT)
5971 nVertPos *= infoPtr->nItemHeight;
5973 lpptOrigin->x -= nHorzPos;
5974 lpptOrigin->y -= nVertPos;
5976 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5979 /***
5980 * DESCRIPTION:
5981 * Retrieves the width of a string.
5983 * PARAMETER(S):
5984 * [I] hwnd : window handle
5985 * [I] lpszText : text string to process
5986 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5988 * RETURN:
5989 * SUCCESS : string width (in pixels)
5990 * FAILURE : zero
5992 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5994 SIZE stringSize;
5996 stringSize.cx = 0;
5997 if (is_textT(lpszText, isW))
5999 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6000 HDC hdc = GetDC(infoPtr->hwndSelf);
6001 HFONT hOldFont = SelectObject(hdc, hFont);
6003 if (isW)
6004 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6005 else
6006 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6007 SelectObject(hdc, hOldFont);
6008 ReleaseDC(infoPtr->hwndSelf, hdc);
6010 return stringSize.cx;
6013 /***
6014 * DESCRIPTION:
6015 * Determines which listview item is located at the specified position.
6017 * PARAMETER(S):
6018 * [I] infoPtr : valid pointer to the listview structure
6019 * [IO] lpht : hit test information
6020 * [I] subitem : fill out iSubItem.
6021 * [I] select : return the index only if the hit selects the item
6023 * NOTE:
6024 * (mm 20001022): We must not allow iSubItem to be touched, for
6025 * an app might pass only a structure with space up to iItem!
6026 * (MS Office 97 does that for instance in the file open dialog)
6028 * RETURN:
6029 * SUCCESS : item index
6030 * FAILURE : -1
6032 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6034 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6035 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6036 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6037 POINT Origin, Position, opt;
6038 LVITEMW lvItem;
6039 ITERATOR i;
6040 INT iItem;
6042 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
6044 lpht->flags = 0;
6045 lpht->iItem = -1;
6046 if (subitem) lpht->iSubItem = 0;
6048 if (infoPtr->rcList.left > lpht->pt.x)
6049 lpht->flags |= LVHT_TOLEFT;
6050 else if (infoPtr->rcList.right < lpht->pt.x)
6051 lpht->flags |= LVHT_TORIGHT;
6053 if (infoPtr->rcList.top > lpht->pt.y)
6054 lpht->flags |= LVHT_ABOVE;
6055 else if (infoPtr->rcList.bottom < lpht->pt.y)
6056 lpht->flags |= LVHT_BELOW;
6058 TRACE("lpht->flags=0x%x\n", lpht->flags);
6059 if (lpht->flags) return -1;
6061 lpht->flags |= LVHT_NOWHERE;
6063 LISTVIEW_GetOrigin(infoPtr, &Origin);
6065 /* first deal with the large items */
6066 rcSearch.left = lpht->pt.x;
6067 rcSearch.top = lpht->pt.y;
6068 rcSearch.right = rcSearch.left + 1;
6069 rcSearch.bottom = rcSearch.top + 1;
6071 iterator_frameditems(&i, infoPtr, &rcSearch);
6072 iterator_next(&i); /* go to first item in the sequence */
6073 iItem = i.nItem;
6074 iterator_destroy(&i);
6076 TRACE("lpht->iItem=%d\n", iItem);
6077 if (iItem == -1) return -1;
6079 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6080 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6081 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6082 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6083 lvItem.iItem = iItem;
6084 lvItem.iSubItem = 0;
6085 lvItem.pszText = szDispText;
6086 lvItem.cchTextMax = DISP_TEXT_SIZE;
6087 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6088 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6090 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6091 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6092 opt.x = lpht->pt.x - Position.x - Origin.x;
6093 opt.y = lpht->pt.y - Position.y - Origin.y;
6095 if (uView == LVS_REPORT)
6096 rcBounds = rcBox;
6097 else
6098 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6099 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
6100 if (!PtInRect(&rcBounds, opt)) return -1;
6102 if (PtInRect(&rcIcon, opt))
6103 lpht->flags |= LVHT_ONITEMICON;
6104 else if (PtInRect(&rcLabel, opt))
6105 lpht->flags |= LVHT_ONITEMLABEL;
6106 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6107 lpht->flags |= LVHT_ONITEMSTATEICON;
6108 if (lpht->flags & LVHT_ONITEM)
6109 lpht->flags &= ~LVHT_NOWHERE;
6111 TRACE("lpht->flags=0x%x\n", lpht->flags);
6112 if (uView == LVS_REPORT && subitem)
6114 INT j;
6116 rcBounds.right = rcBounds.left;
6117 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6119 rcBounds.left = rcBounds.right;
6120 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6121 if (PtInRect(&rcBounds, opt))
6123 lpht->iSubItem = j;
6124 break;
6129 if (select && !(uView == LVS_REPORT &&
6130 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6131 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6133 if (uView == LVS_REPORT)
6135 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6136 UnionRect(&rcBounds, &rcBounds, &rcState);
6138 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6140 return lpht->iItem = iItem;
6144 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6145 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6146 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6147 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6148 their own sort proc. when sending LVM_SORTITEMS.
6150 /* Platform SDK:
6151 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6153 LVS_SORTXXX must be specified,
6154 LVS_OWNERDRAW is not set,
6155 <item>.pszText is not LPSTR_TEXTCALLBACK.
6157 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6158 are sorted based on item text..."
6160 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6162 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6163 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6164 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6166 /* if we're sorting descending, negate the return value */
6167 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6170 /***
6171 * DESCRIPTION:
6172 * Inserts a new item in the listview control.
6174 * PARAMETER(S):
6175 * [I] infoPtr : valid pointer to the listview structure
6176 * [I] lpLVItem : item information
6177 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6179 * RETURN:
6180 * SUCCESS : new item index
6181 * FAILURE : -1
6183 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6185 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6186 INT nItem;
6187 HDPA hdpaSubItems;
6188 NMLISTVIEW nmlv;
6189 ITEM_INFO *lpItem;
6190 BOOL is_sorted, has_changed;
6191 LVITEMW item;
6193 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6195 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6197 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6198 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6200 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6202 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6204 /* insert item in listview control data structure */
6205 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6206 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6208 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6209 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6211 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6212 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6213 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6214 if (nItem == -1) goto fail;
6215 infoPtr->nItemCount++;
6217 /* shift indices first so they don't get tangled */
6218 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6220 /* set the item attributes */
6221 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6223 /* full size structure expected - _WIN32IE >= 0x560 */
6224 item = *lpLVItem;
6226 else if (lpLVItem->mask & LVIF_INDENT)
6228 /* indent member expected - _WIN32IE >= 0x300 */
6229 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6231 else
6233 /* minimal structure expected */
6234 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6236 item.iItem = nItem;
6237 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6238 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6240 /* if we're sorted, sort the list, and update the index */
6241 if (is_sorted)
6243 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6244 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6245 assert(nItem != -1);
6248 /* make room for the position, if we are in the right mode */
6249 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6251 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6252 goto undo;
6253 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6255 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6256 goto undo;
6260 /* send LVN_INSERTITEM notification */
6261 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6262 nmlv.iItem = nItem;
6263 nmlv.lParam = lpItem->lParam;
6264 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6266 /* align items (set position of each item) */
6267 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6269 POINT pt;
6271 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6272 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6273 else
6274 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6276 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6279 /* now is the invalidation fun */
6280 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6281 return nItem;
6283 undo:
6284 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6285 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6286 infoPtr->nItemCount--;
6287 fail:
6288 DPA_DeletePtr(hdpaSubItems, 0);
6289 DPA_Destroy (hdpaSubItems);
6290 Free (lpItem);
6291 return -1;
6294 /***
6295 * DESCRIPTION:
6296 * Redraws a range of items.
6298 * PARAMETER(S):
6299 * [I] infoPtr : valid pointer to the listview structure
6300 * [I] nFirst : first item
6301 * [I] nLast : last item
6303 * RETURN:
6304 * SUCCESS : TRUE
6305 * FAILURE : FALSE
6307 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6309 INT i;
6311 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6312 max(nFirst, nLast) >= infoPtr->nItemCount)
6313 return FALSE;
6315 for (i = nFirst; i <= nLast; i++)
6316 LISTVIEW_InvalidateItem(infoPtr, i);
6318 return TRUE;
6321 /***
6322 * DESCRIPTION:
6323 * Scroll the content of a listview.
6325 * PARAMETER(S):
6326 * [I] infoPtr : valid pointer to the listview structure
6327 * [I] dx : horizontal scroll amount in pixels
6328 * [I] dy : vertical scroll amount in pixels
6330 * RETURN:
6331 * SUCCESS : TRUE
6332 * FAILURE : FALSE
6334 * COMMENTS:
6335 * If the control is in report mode (LVS_REPORT) the control can
6336 * be scrolled only in line increments. "dy" will be rounded to the
6337 * nearest number of pixels that are a whole line. Ex: if line height
6338 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6339 * is passed the the scroll will be 0. (per MSDN 7/2002)
6341 * For: (per experimentaion with native control and CSpy ListView)
6342 * LVS_ICON dy=1 = 1 pixel (vertical only)
6343 * dx ignored
6344 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6345 * dx ignored
6346 * LVS_LIST dx=1 = 1 column (horizontal only)
6347 * but will only scroll 1 column per message
6348 * no matter what the value.
6349 * dy must be 0 or FALSE returned.
6350 * LVS_REPORT dx=1 = 1 pixel
6351 * dy= see above
6354 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6356 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6357 case LVS_REPORT:
6358 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6359 dy /= infoPtr->nItemHeight;
6360 break;
6361 case LVS_LIST:
6362 if (dy != 0) return FALSE;
6363 break;
6364 default: /* icon */
6365 dx = 0;
6366 break;
6369 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6370 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6372 return TRUE;
6375 /***
6376 * DESCRIPTION:
6377 * Sets the background color.
6379 * PARAMETER(S):
6380 * [I] infoPtr : valid pointer to the listview structure
6381 * [I] clrBk : background color
6383 * RETURN:
6384 * SUCCESS : TRUE
6385 * FAILURE : FALSE
6387 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6389 TRACE("(clrBk=%lx)\n", clrBk);
6391 if(infoPtr->clrBk != clrBk) {
6392 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6393 infoPtr->clrBk = clrBk;
6394 if (clrBk == CLR_NONE)
6395 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6396 else
6397 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6398 LISTVIEW_InvalidateList(infoPtr);
6401 return TRUE;
6404 /* LISTVIEW_SetBkImage */
6406 /*** Helper for {Insert,Set}ColumnT *only* */
6407 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6409 if (lpColumn->mask & LVCF_FMT)
6411 /* format member is valid */
6412 lphdi->mask |= HDI_FORMAT;
6414 /* set text alignment (leftmost column must be left-aligned) */
6415 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6416 lphdi->fmt |= HDF_LEFT;
6417 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6418 lphdi->fmt |= HDF_RIGHT;
6419 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6420 lphdi->fmt |= HDF_CENTER;
6422 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6423 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6425 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6427 lphdi->fmt |= HDF_IMAGE;
6428 lphdi->iImage = I_IMAGECALLBACK;
6432 if (lpColumn->mask & LVCF_WIDTH)
6434 lphdi->mask |= HDI_WIDTH;
6435 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6437 /* make it fill the remainder of the controls width */
6438 RECT rcHeader;
6439 INT item_index;
6441 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6443 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6444 lphdi->cxy += rcHeader.right - rcHeader.left;
6447 /* retrieve the layout of the header */
6448 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6449 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6451 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6453 else
6454 lphdi->cxy = lpColumn->cx;
6457 if (lpColumn->mask & LVCF_TEXT)
6459 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6460 lphdi->fmt |= HDF_STRING;
6461 lphdi->pszText = lpColumn->pszText;
6462 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6465 if (lpColumn->mask & LVCF_IMAGE)
6467 lphdi->mask |= HDI_IMAGE;
6468 lphdi->iImage = lpColumn->iImage;
6471 if (lpColumn->mask & LVCF_ORDER)
6473 lphdi->mask |= HDI_ORDER;
6474 lphdi->iOrder = lpColumn->iOrder;
6479 /***
6480 * DESCRIPTION:
6481 * Inserts a new column.
6483 * PARAMETER(S):
6484 * [I] infoPtr : valid pointer to the listview structure
6485 * [I] nColumn : column index
6486 * [I] lpColumn : column information
6487 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6489 * RETURN:
6490 * SUCCESS : new column index
6491 * FAILURE : -1
6493 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6494 const LVCOLUMNW *lpColumn, BOOL isW)
6496 COLUMN_INFO *lpColumnInfo;
6497 INT nNewColumn;
6498 HDITEMW hdi;
6500 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6502 if (!lpColumn || nColumn < 0) return -1;
6503 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6505 ZeroMemory(&hdi, sizeof(HDITEMW));
6506 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6508 /* insert item in header control */
6509 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6510 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6511 (WPARAM)nColumn, (LPARAM)&hdi);
6512 if (nNewColumn == -1) return -1;
6513 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6515 /* create our own column info */
6516 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6517 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6519 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6520 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6522 /* now we have to actually adjust the data */
6523 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6525 SUBITEM_INFO *lpSubItem;
6526 HDPA hdpaSubItems;
6527 INT nItem, i;
6529 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6531 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6532 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6534 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6535 if (lpSubItem->iSubItem >= nNewColumn)
6536 lpSubItem->iSubItem++;
6541 /* make space for the new column */
6542 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6544 return nNewColumn;
6546 fail:
6547 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6548 if (lpColumnInfo)
6550 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6551 Free(lpColumnInfo);
6553 return -1;
6556 /***
6557 * DESCRIPTION:
6558 * Sets the attributes of a header item.
6560 * PARAMETER(S):
6561 * [I] infoPtr : valid pointer to the listview structure
6562 * [I] nColumn : column index
6563 * [I] lpColumn : column attributes
6564 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6566 * RETURN:
6567 * SUCCESS : TRUE
6568 * FAILURE : FALSE
6570 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6571 const LVCOLUMNW *lpColumn, BOOL isW)
6573 HDITEMW hdi, hdiget;
6574 BOOL bResult;
6576 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6578 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6580 ZeroMemory(&hdi, sizeof(HDITEMW));
6581 if (lpColumn->mask & LVCF_FMT)
6583 hdi.mask |= HDI_FORMAT;
6584 hdiget.mask = HDI_FORMAT;
6585 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6586 hdi.fmt = hdiget.fmt & HDF_STRING;
6588 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6590 /* set header item attributes */
6591 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6592 if (!bResult) return FALSE;
6594 if (lpColumn->mask & LVCF_FMT)
6596 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6597 int oldFmt = lpColumnInfo->fmt;
6599 lpColumnInfo->fmt = lpColumn->fmt;
6600 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6602 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6603 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6607 return TRUE;
6610 /***
6611 * DESCRIPTION:
6612 * Sets the column order array
6614 * PARAMETERS:
6615 * [I] infoPtr : valid pointer to the listview structure
6616 * [I] iCount : number of elements in column order array
6617 * [I] lpiArray : pointer to column order array
6619 * RETURN:
6620 * SUCCESS : TRUE
6621 * FAILURE : FALSE
6623 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6625 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6627 if (!lpiArray)
6628 return FALSE;
6630 return TRUE;
6634 /***
6635 * DESCRIPTION:
6636 * Sets the width of a column
6638 * PARAMETERS:
6639 * [I] infoPtr : valid pointer to the listview structure
6640 * [I] nColumn : column index
6641 * [I] cx : column width
6643 * RETURN:
6644 * SUCCESS : TRUE
6645 * FAILURE : FALSE
6647 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6649 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6650 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6651 INT max_cx = 0;
6652 HDITEMW hdi;
6654 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6656 /* set column width only if in report or list mode */
6657 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6659 /* take care of invalid cx values */
6660 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6661 else if (uView == LVS_LIST && cx < 1) return FALSE;
6663 /* resize all columns if in LVS_LIST mode */
6664 if(uView == LVS_LIST)
6666 infoPtr->nItemWidth = cx;
6667 LISTVIEW_InvalidateList(infoPtr);
6668 return TRUE;
6671 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6673 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6675 INT nLabelWidth;
6676 LVITEMW lvItem;
6678 lvItem.mask = LVIF_TEXT;
6679 lvItem.iItem = 0;
6680 lvItem.iSubItem = nColumn;
6681 lvItem.pszText = szDispText;
6682 lvItem.cchTextMax = DISP_TEXT_SIZE;
6683 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6685 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6686 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6687 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6689 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6690 max_cx += infoPtr->iconSize.cx;
6691 max_cx += TRAILING_LABEL_PADDING;
6694 /* autosize based on listview items width */
6695 if(cx == LVSCW_AUTOSIZE)
6696 cx = max_cx;
6697 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6699 /* if iCol is the last column make it fill the remainder of the controls width */
6700 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6702 RECT rcHeader;
6703 POINT Origin;
6705 LISTVIEW_GetOrigin(infoPtr, &Origin);
6706 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6708 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6710 else
6712 /* Despite what the MS docs say, if this is not the last
6713 column, then MS resizes the column to the width of the
6714 largest text string in the column, including headers
6715 and items. This is different from LVSCW_AUTOSIZE in that
6716 LVSCW_AUTOSIZE ignores the header string length. */
6717 cx = 0;
6719 /* retrieve header text */
6720 hdi.mask = HDI_TEXT;
6721 hdi.cchTextMax = DISP_TEXT_SIZE;
6722 hdi.pszText = szDispText;
6723 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6725 HDC hdc = GetDC(infoPtr->hwndSelf);
6726 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6727 SIZE size;
6729 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6730 cx = size.cx + TRAILING_HEADER_PADDING;
6731 /* FIXME: Take into account the header image, if one is present */
6732 SelectObject(hdc, old_font);
6733 ReleaseDC(infoPtr->hwndSelf, hdc);
6735 cx = max (cx, max_cx);
6739 if (cx < 0) return FALSE;
6741 /* call header to update the column change */
6742 hdi.mask = HDI_WIDTH;
6743 hdi.cxy = cx;
6744 TRACE("hdi.cxy=%d\n", hdi.cxy);
6745 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6748 /***
6749 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6752 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6754 HDC hdc_wnd, hdc;
6755 HBITMAP hbm_im, hbm_mask, hbm_orig;
6756 RECT rc;
6757 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6758 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6759 HIMAGELIST himl;
6761 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6762 ILC_COLOR | ILC_MASK, 2, 2);
6763 hdc_wnd = GetDC(infoPtr->hwndSelf);
6764 hdc = CreateCompatibleDC(hdc_wnd);
6765 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6766 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6767 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6769 rc.left = rc.top = 0;
6770 rc.right = GetSystemMetrics(SM_CXSMICON);
6771 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6773 hbm_orig = SelectObject(hdc, hbm_mask);
6774 FillRect(hdc, &rc, hbr_white);
6775 InflateRect(&rc, -3, -3);
6776 FillRect(hdc, &rc, hbr_black);
6778 SelectObject(hdc, hbm_im);
6779 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6780 SelectObject(hdc, hbm_orig);
6781 ImageList_Add(himl, hbm_im, hbm_mask);
6783 SelectObject(hdc, hbm_im);
6784 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6785 SelectObject(hdc, hbm_orig);
6786 ImageList_Add(himl, hbm_im, hbm_mask);
6788 DeleteObject(hbm_mask);
6789 DeleteObject(hbm_im);
6790 DeleteDC(hdc);
6792 return himl;
6795 /***
6796 * DESCRIPTION:
6797 * Sets the extended listview style.
6799 * PARAMETERS:
6800 * [I] infoPtr : valid pointer to the listview structure
6801 * [I] dwMask : mask
6802 * [I] dwStyle : style
6804 * RETURN:
6805 * SUCCESS : previous style
6806 * FAILURE : 0
6808 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6810 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6812 /* set new style */
6813 if (dwMask)
6814 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6815 else
6816 infoPtr->dwLvExStyle = dwStyle;
6818 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6820 HIMAGELIST himl = 0;
6821 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6822 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6823 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6826 return dwOldStyle;
6829 /***
6830 * DESCRIPTION:
6831 * Sets the new hot cursor used during hot tracking and hover selection.
6833 * PARAMETER(S):
6834 * [I] infoPtr : valid pointer to the listview structure
6835 * [I] hCursor : the new hot cursor handle
6837 * RETURN:
6838 * Returns the previous hot cursor
6840 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6842 HCURSOR oldCursor = infoPtr->hHotCursor;
6844 infoPtr->hHotCursor = hCursor;
6846 return oldCursor;
6850 /***
6851 * DESCRIPTION:
6852 * Sets the hot item index.
6854 * PARAMETERS:
6855 * [I] infoPtr : valid pointer to the listview structure
6856 * [I] iIndex : index
6858 * RETURN:
6859 * SUCCESS : previous hot item index
6860 * FAILURE : -1 (no hot item)
6862 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6864 INT iOldIndex = infoPtr->nHotItem;
6866 infoPtr->nHotItem = iIndex;
6868 return iOldIndex;
6872 /***
6873 * DESCRIPTION:
6874 * Sets the amount of time the cursor must hover over an item before it is selected.
6876 * PARAMETER(S):
6877 * [I] infoPtr : valid pointer to the listview structure
6878 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6880 * RETURN:
6881 * Returns the previous hover time
6883 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6885 DWORD oldHoverTime = infoPtr->dwHoverTime;
6887 infoPtr->dwHoverTime = dwHoverTime;
6889 return oldHoverTime;
6892 /***
6893 * DESCRIPTION:
6894 * Sets spacing for icons of LVS_ICON style.
6896 * PARAMETER(S):
6897 * [I] infoPtr : valid pointer to the listview structure
6898 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6899 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6901 * RETURN:
6902 * MAKELONG(oldcx, oldcy)
6904 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6906 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6907 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6909 TRACE("requested=(%d,%d)\n", cx, cy);
6911 /* this is supported only for LVS_ICON style */
6912 if (uView != LVS_ICON) return oldspacing;
6914 /* set to defaults, if instructed to */
6915 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6916 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6918 /* if 0 then compute width
6919 * FIXME: Should scan each item and determine max width of
6920 * icon or label, then make that the width */
6921 if (cx == 0)
6922 cx = infoPtr->iconSpacing.cx;
6924 /* if 0 then compute height */
6925 if (cy == 0)
6926 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6927 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6930 infoPtr->iconSpacing.cx = cx;
6931 infoPtr->iconSpacing.cy = cy;
6933 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6934 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6935 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6936 infoPtr->ntmHeight);
6938 /* these depend on the iconSpacing */
6939 LISTVIEW_UpdateItemSize(infoPtr);
6941 return oldspacing;
6944 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6946 INT cx, cy;
6948 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6950 size->cx = cx;
6951 size->cy = cy;
6953 else
6955 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6956 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6960 /***
6961 * DESCRIPTION:
6962 * Sets image lists.
6964 * PARAMETER(S):
6965 * [I] infoPtr : valid pointer to the listview structure
6966 * [I] nType : image list type
6967 * [I] himl : image list handle
6969 * RETURN:
6970 * SUCCESS : old image list
6971 * FAILURE : NULL
6973 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6975 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6976 INT oldHeight = infoPtr->nItemHeight;
6977 HIMAGELIST himlOld = 0;
6979 TRACE("(nType=%d, himl=%p\n", nType, himl);
6981 switch (nType)
6983 case LVSIL_NORMAL:
6984 himlOld = infoPtr->himlNormal;
6985 infoPtr->himlNormal = himl;
6986 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6987 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6988 break;
6990 case LVSIL_SMALL:
6991 himlOld = infoPtr->himlSmall;
6992 infoPtr->himlSmall = himl;
6993 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6994 break;
6996 case LVSIL_STATE:
6997 himlOld = infoPtr->himlState;
6998 infoPtr->himlState = himl;
6999 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7000 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7001 break;
7003 default:
7004 ERR("Unknown icon type=%d\n", nType);
7005 return NULL;
7008 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7009 if (infoPtr->nItemHeight != oldHeight)
7010 LISTVIEW_UpdateScroll(infoPtr);
7012 return himlOld;
7015 /***
7016 * DESCRIPTION:
7017 * Preallocates memory (does *not* set the actual count of items !)
7019 * PARAMETER(S):
7020 * [I] infoPtr : valid pointer to the listview structure
7021 * [I] nItems : item count (projected number of items to allocate)
7022 * [I] dwFlags : update flags
7024 * RETURN:
7025 * SUCCESS : TRUE
7026 * FAILURE : FALSE
7028 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7030 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7032 if (infoPtr->dwStyle & LVS_OWNERDATA)
7034 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7035 INT nOldCount = infoPtr->nItemCount;
7037 if (nItems < nOldCount)
7039 RANGE range = { nItems, nOldCount };
7040 ranges_del(infoPtr->selectionRanges, range);
7041 if (infoPtr->nFocusedItem >= nItems)
7043 infoPtr->nFocusedItem = -1;
7044 SetRectEmpty(&infoPtr->rcFocus);
7048 infoPtr->nItemCount = nItems;
7049 LISTVIEW_UpdateScroll(infoPtr);
7051 /* the flags are valid only in ownerdata report and list modes */
7052 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7054 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7055 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7057 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7058 LISTVIEW_InvalidateList(infoPtr);
7059 else
7061 INT nFrom, nTo;
7062 POINT Origin;
7063 RECT rcErase;
7065 LISTVIEW_GetOrigin(infoPtr, &Origin);
7066 nFrom = min(nOldCount, nItems);
7067 nTo = max(nOldCount, nItems);
7069 if (uView == LVS_REPORT)
7071 rcErase.left = 0;
7072 rcErase.top = nFrom * infoPtr->nItemHeight;
7073 rcErase.right = infoPtr->nItemWidth;
7074 rcErase.bottom = nTo * infoPtr->nItemHeight;
7075 OffsetRect(&rcErase, Origin.x, Origin.y);
7076 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7077 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7079 else /* LVS_LIST */
7081 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7083 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7084 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7085 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7086 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7087 OffsetRect(&rcErase, Origin.x, Origin.y);
7088 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7089 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7091 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7092 rcErase.top = 0;
7093 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7094 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7095 OffsetRect(&rcErase, Origin.x, Origin.y);
7096 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7097 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7101 else
7103 /* According to MSDN for non-LVS_OWNERDATA this is just
7104 * a performance issue. The control allocates its internal
7105 * data structures for the number of items specified. It
7106 * cuts down on the number of memory allocations. Therefore
7107 * we will just issue a WARN here
7109 WARN("for non-ownerdata performance option not implemented.\n");
7112 return TRUE;
7115 /***
7116 * DESCRIPTION:
7117 * Sets the position of an item.
7119 * PARAMETER(S):
7120 * [I] infoPtr : valid pointer to the listview structure
7121 * [I] nItem : item index
7122 * [I] pt : coordinate
7124 * RETURN:
7125 * SUCCESS : TRUE
7126 * FAILURE : FALSE
7128 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7130 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7131 POINT Origin;
7133 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7135 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7136 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7138 LISTVIEW_GetOrigin(infoPtr, &Origin);
7140 /* This point value seems to be an undocumented feature.
7141 * The best guess is that it means either at the origin,
7142 * or at true beginning of the list. I will assume the origin. */
7143 if ((pt.x == -1) && (pt.y == -1))
7144 pt = Origin;
7146 if (uView == LVS_ICON)
7148 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7149 pt.y -= ICON_TOP_PADDING;
7151 pt.x -= Origin.x;
7152 pt.y -= Origin.y;
7154 infoPtr->bAutoarrange = FALSE;
7156 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7159 /***
7160 * DESCRIPTION:
7161 * Sets the state of one or many items.
7163 * PARAMETER(S):
7164 * [I] infoPtr : valid pointer to the listview structure
7165 * [I] nItem : item index
7166 * [I] lpLVItem : item or subitem info
7168 * RETURN:
7169 * SUCCESS : TRUE
7170 * FAILURE : FALSE
7172 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7174 BOOL bResult = TRUE;
7175 LVITEMW lvItem;
7177 lvItem.iItem = nItem;
7178 lvItem.iSubItem = 0;
7179 lvItem.mask = LVIF_STATE;
7180 lvItem.state = lpLVItem->state;
7181 lvItem.stateMask = lpLVItem->stateMask;
7182 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7184 if (nItem == -1)
7186 /* apply to all items */
7187 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7188 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7190 else
7191 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7194 * Update selection mark
7196 * Investigation on windows 2k showed that selection mark was updated
7197 * whenever a new selection was made, but if the selected item was
7198 * unselected it was not updated.
7200 * we are probably still not 100% accurate, but this at least sets the
7201 * proper selection mark when it is needed
7204 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7205 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7207 int i;
7208 infoPtr->nSelectionMark = -1;
7209 for (i = 0; i < infoPtr->nItemCount; i++)
7211 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7213 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7215 infoPtr->nSelectionMark = i;
7216 break;
7219 else if (ranges_contain(infoPtr->selectionRanges, i))
7221 infoPtr->nSelectionMark = i;
7222 break;
7227 return bResult;
7230 /***
7231 * DESCRIPTION:
7232 * Sets the text of an item or subitem.
7234 * PARAMETER(S):
7235 * [I] hwnd : window handle
7236 * [I] nItem : item index
7237 * [I] lpLVItem : item or subitem info
7238 * [I] isW : TRUE if input is Unicode
7240 * RETURN:
7241 * SUCCESS : TRUE
7242 * FAILURE : FALSE
7244 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7246 LVITEMW lvItem;
7248 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7250 lvItem.iItem = nItem;
7251 lvItem.iSubItem = lpLVItem->iSubItem;
7252 lvItem.mask = LVIF_TEXT;
7253 lvItem.pszText = lpLVItem->pszText;
7254 lvItem.cchTextMax = lpLVItem->cchTextMax;
7256 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7258 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7261 /***
7262 * DESCRIPTION:
7263 * Set item index that marks the start of a multiple selection.
7265 * PARAMETER(S):
7266 * [I] infoPtr : valid pointer to the listview structure
7267 * [I] nIndex : index
7269 * RETURN:
7270 * Index number or -1 if there is no selection mark.
7272 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7274 INT nOldIndex = infoPtr->nSelectionMark;
7276 TRACE("(nIndex=%d)\n", nIndex);
7278 infoPtr->nSelectionMark = nIndex;
7280 return nOldIndex;
7283 /***
7284 * DESCRIPTION:
7285 * Sets the text background color.
7287 * PARAMETER(S):
7288 * [I] infoPtr : valid pointer to the listview structure
7289 * [I] clrTextBk : text background color
7291 * RETURN:
7292 * SUCCESS : TRUE
7293 * FAILURE : FALSE
7295 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7297 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7299 if (infoPtr->clrTextBk != clrTextBk)
7301 infoPtr->clrTextBk = clrTextBk;
7302 LISTVIEW_InvalidateList(infoPtr);
7305 return TRUE;
7308 /***
7309 * DESCRIPTION:
7310 * Sets the text foreground color.
7312 * PARAMETER(S):
7313 * [I] infoPtr : valid pointer to the listview structure
7314 * [I] clrText : text color
7316 * RETURN:
7317 * SUCCESS : TRUE
7318 * FAILURE : FALSE
7320 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7322 TRACE("(clrText=%lx)\n", clrText);
7324 if (infoPtr->clrText != clrText)
7326 infoPtr->clrText = clrText;
7327 LISTVIEW_InvalidateList(infoPtr);
7330 return TRUE;
7333 /***
7334 * DESCRIPTION:
7335 * Determines which listview item is located at the specified position.
7337 * PARAMETER(S):
7338 * [I] infoPtr : valid pointer to the listview structure
7339 * [I] hwndNewToolTip : handle to new ToolTip
7341 * RETURN:
7342 * old tool tip
7344 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7346 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7347 infoPtr->hwndToolTip = hwndNewToolTip;
7348 return hwndOldToolTip;
7351 /* LISTVIEW_SetUnicodeFormat */
7352 /* LISTVIEW_SetWorkAreas */
7354 /***
7355 * DESCRIPTION:
7356 * Callback internally used by LISTVIEW_SortItems()
7358 * PARAMETER(S):
7359 * [I] first : pointer to first ITEM_INFO to compare
7360 * [I] second : pointer to second ITEM_INFO to compare
7361 * [I] lParam : HWND of control
7363 * RETURN:
7364 * if first comes before second : negative
7365 * if first comes after second : positive
7366 * if first and second are equivalent : zero
7368 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7370 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7371 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7372 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7374 /* Forward the call to the client defined callback */
7375 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7378 /***
7379 * DESCRIPTION:
7380 * Sorts the listview items.
7382 * PARAMETER(S):
7383 * [I] infoPtr : valid pointer to the listview structure
7384 * [I] pfnCompare : application-defined value
7385 * [I] lParamSort : pointer to comparision callback
7387 * RETURN:
7388 * SUCCESS : TRUE
7389 * FAILURE : FALSE
7391 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7393 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7394 HDPA hdpaSubItems;
7395 ITEM_INFO *lpItem;
7396 LPVOID selectionMarkItem;
7397 LVITEMW item;
7398 int i;
7400 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7402 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7404 if (!pfnCompare) return FALSE;
7405 if (!infoPtr->hdpaItems) return FALSE;
7407 /* if there are 0 or 1 items, there is no need to sort */
7408 if (infoPtr->nItemCount < 2) return TRUE;
7410 if (infoPtr->nFocusedItem >= 0)
7412 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7413 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7414 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7416 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7417 /* clear the lpItem->state for non-selected ones */
7418 /* remove the selection ranges */
7420 infoPtr->pfnCompare = pfnCompare;
7421 infoPtr->lParamSort = lParamSort;
7422 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7424 /* Adjust selections and indices so that they are the way they should
7425 * be after the sort (otherwise, the list items move around, but
7426 * whatever is at the item's previous original position will be
7427 * selected instead)
7429 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7430 for (i=0; i < infoPtr->nItemCount; i++)
7432 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7433 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7435 if (lpItem->state & LVIS_SELECTED)
7437 item.state = LVIS_SELECTED;
7438 item.stateMask = LVIS_SELECTED;
7439 LISTVIEW_SetItemState(infoPtr, i, &item);
7441 if (lpItem->state & LVIS_FOCUSED)
7443 infoPtr->nFocusedItem = i;
7444 lpItem->state &= ~LVIS_FOCUSED;
7447 if (selectionMarkItem != NULL)
7448 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7449 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7451 /* refresh the display */
7452 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7453 LISTVIEW_InvalidateList(infoPtr);
7455 return TRUE;
7458 /***
7459 * DESCRIPTION:
7460 * Update theme handle after a theme change.
7462 * PARAMETER(S):
7463 * [I] infoPtr : valid pointer to the listview structure
7465 * RETURN:
7466 * SUCCESS : 0
7467 * FAILURE : something else
7469 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7471 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7472 CloseThemeData(theme);
7473 OpenThemeData(infoPtr->hwndSelf, themeClass);
7474 return 0;
7477 /***
7478 * DESCRIPTION:
7479 * Updates an items or rearranges the listview control.
7481 * PARAMETER(S):
7482 * [I] infoPtr : valid pointer to the listview structure
7483 * [I] nItem : item index
7485 * RETURN:
7486 * SUCCESS : TRUE
7487 * FAILURE : FALSE
7489 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7491 TRACE("(nItem=%d)\n", nItem);
7493 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7495 /* rearrange with default alignment style */
7496 if (is_autoarrange(infoPtr))
7497 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7498 else
7499 LISTVIEW_InvalidateItem(infoPtr, nItem);
7501 return TRUE;
7505 /***
7506 * DESCRIPTION:
7507 * Creates the listview control.
7509 * PARAMETER(S):
7510 * [I] hwnd : window handle
7511 * [I] lpcs : the create parameters
7513 * RETURN:
7514 * Success: 0
7515 * Failure: -1
7517 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7519 LISTVIEW_INFO *infoPtr;
7520 UINT uView = lpcs->style & LVS_TYPEMASK;
7521 LOGFONTW logFont;
7523 TRACE("(lpcs=%p)\n", lpcs);
7525 /* initialize info pointer */
7526 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7527 if (!infoPtr) return -1;
7529 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7531 infoPtr->hwndSelf = hwnd;
7532 infoPtr->dwStyle = lpcs->style;
7533 /* determine the type of structures to use */
7534 infoPtr->hwndNotify = lpcs->hwndParent;
7535 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7536 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7538 /* initialize color information */
7539 infoPtr->clrBk = CLR_NONE;
7540 infoPtr->clrText = comctl32_color.clrWindowText;
7541 infoPtr->clrTextBk = CLR_DEFAULT;
7542 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7544 /* set default values */
7545 infoPtr->nFocusedItem = -1;
7546 infoPtr->nSelectionMark = -1;
7547 infoPtr->nHotItem = -1;
7548 infoPtr->bRedraw = TRUE;
7549 infoPtr->bNoItemMetrics = TRUE;
7550 infoPtr->bDoChangeNotify = TRUE;
7551 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7552 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7553 infoPtr->nEditLabelItem = -1;
7554 infoPtr->dwHoverTime = -1; /* default system hover time */
7555 infoPtr->nMeasureItemHeight = 0;
7557 /* get default font (icon title) */
7558 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7559 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7560 infoPtr->hFont = infoPtr->hDefaultFont;
7561 LISTVIEW_SaveTextMetrics(infoPtr);
7563 /* create header */
7564 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7565 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7566 0, 0, 0, 0, hwnd, NULL,
7567 lpcs->hInstance, NULL);
7568 if (!infoPtr->hwndHeader) goto fail;
7570 /* set header unicode format */
7571 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7573 /* set header font */
7574 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7576 /* allocate memory for the data structure */
7577 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7578 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7579 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7580 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7581 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7583 /* initialize the icon sizes */
7584 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7585 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7587 /* init item size to avoid division by 0 */
7588 LISTVIEW_UpdateItemSize (infoPtr);
7590 if (uView == LVS_REPORT)
7592 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7594 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7596 else
7598 /* set HDS_HIDDEN flag to hide the header bar */
7599 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7600 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7604 OpenThemeData(hwnd, themeClass);
7606 return 0;
7608 fail:
7609 DestroyWindow(infoPtr->hwndHeader);
7610 ranges_destroy(infoPtr->selectionRanges);
7611 DPA_Destroy(infoPtr->hdpaItems);
7612 DPA_Destroy(infoPtr->hdpaPosX);
7613 DPA_Destroy(infoPtr->hdpaPosY);
7614 DPA_Destroy(infoPtr->hdpaColumns);
7615 Free(infoPtr);
7616 return -1;
7619 /***
7620 * DESCRIPTION:
7621 * Destroys the listview control.
7623 * PARAMETER(S):
7624 * [I] infoPtr : valid pointer to the listview structure
7626 * RETURN:
7627 * Success: 0
7628 * Failure: -1
7630 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7632 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7633 CloseThemeData(theme);
7634 return 0;
7637 /***
7638 * DESCRIPTION:
7639 * Enables the listview control.
7641 * PARAMETER(S):
7642 * [I] infoPtr : valid pointer to the listview structure
7643 * [I] bEnable : specifies whether to enable or disable the window
7645 * RETURN:
7646 * SUCCESS : TRUE
7647 * FAILURE : FALSE
7649 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7651 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7652 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7653 return TRUE;
7656 /***
7657 * DESCRIPTION:
7658 * Erases the background of the listview control.
7660 * PARAMETER(S):
7661 * [I] infoPtr : valid pointer to the listview structure
7662 * [I] hdc : device context handle
7664 * RETURN:
7665 * SUCCESS : TRUE
7666 * FAILURE : FALSE
7668 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7670 RECT rc;
7672 TRACE("(hdc=%p)\n", hdc);
7674 if (!GetClipBox(hdc, &rc)) return FALSE;
7676 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7680 /***
7681 * DESCRIPTION:
7682 * Helper function for LISTVIEW_[HV]Scroll *only*.
7683 * Performs vertical/horizontal scrolling by a give amount.
7685 * PARAMETER(S):
7686 * [I] infoPtr : valid pointer to the listview structure
7687 * [I] dx : amount of horizontal scroll
7688 * [I] dy : amount of vertical scroll
7690 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7692 /* now we can scroll the list */
7693 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7694 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7695 /* if we have focus, adjust rect */
7696 OffsetRect(&infoPtr->rcFocus, dx, dy);
7697 UpdateWindow(infoPtr->hwndSelf);
7700 /***
7701 * DESCRIPTION:
7702 * Performs vertical scrolling.
7704 * PARAMETER(S):
7705 * [I] infoPtr : valid pointer to the listview structure
7706 * [I] nScrollCode : scroll code
7707 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7708 * [I] hScrollWnd : scrollbar control window handle
7710 * RETURN:
7711 * Zero
7713 * NOTES:
7714 * SB_LINEUP/SB_LINEDOWN:
7715 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7716 * for LVS_REPORT is 1 line
7717 * for LVS_LIST cannot occur
7720 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7721 INT nScrollDiff, HWND hScrollWnd)
7723 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7724 INT nOldScrollPos, nNewScrollPos;
7725 SCROLLINFO scrollInfo;
7726 BOOL is_an_icon;
7728 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7729 debugscrollcode(nScrollCode), nScrollDiff);
7731 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7733 scrollInfo.cbSize = sizeof(SCROLLINFO);
7734 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7736 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7738 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7740 nOldScrollPos = scrollInfo.nPos;
7741 switch (nScrollCode)
7743 case SB_INTERNAL:
7744 break;
7746 case SB_LINEUP:
7747 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7748 break;
7750 case SB_LINEDOWN:
7751 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7752 break;
7754 case SB_PAGEUP:
7755 nScrollDiff = -scrollInfo.nPage;
7756 break;
7758 case SB_PAGEDOWN:
7759 nScrollDiff = scrollInfo.nPage;
7760 break;
7762 case SB_THUMBPOSITION:
7763 case SB_THUMBTRACK:
7764 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7765 break;
7767 default:
7768 nScrollDiff = 0;
7771 /* quit right away if pos isn't changing */
7772 if (nScrollDiff == 0) return 0;
7774 /* calculate new position, and handle overflows */
7775 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7776 if (nScrollDiff > 0) {
7777 if (nNewScrollPos < nOldScrollPos ||
7778 nNewScrollPos > scrollInfo.nMax)
7779 nNewScrollPos = scrollInfo.nMax;
7780 } else {
7781 if (nNewScrollPos > nOldScrollPos ||
7782 nNewScrollPos < scrollInfo.nMin)
7783 nNewScrollPos = scrollInfo.nMin;
7786 /* set the new position, and reread in case it changed */
7787 scrollInfo.fMask = SIF_POS;
7788 scrollInfo.nPos = nNewScrollPos;
7789 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7791 /* carry on only if it really changed */
7792 if (nNewScrollPos == nOldScrollPos) return 0;
7794 /* now adjust to client coordinates */
7795 nScrollDiff = nOldScrollPos - nNewScrollPos;
7796 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7798 /* and scroll the window */
7799 scroll_list(infoPtr, 0, nScrollDiff);
7801 return 0;
7804 /***
7805 * DESCRIPTION:
7806 * Performs horizontal scrolling.
7808 * PARAMETER(S):
7809 * [I] infoPtr : valid pointer to the listview structure
7810 * [I] nScrollCode : scroll code
7811 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7812 * [I] hScrollWnd : scrollbar control window handle
7814 * RETURN:
7815 * Zero
7817 * NOTES:
7818 * SB_LINELEFT/SB_LINERIGHT:
7819 * for LVS_ICON, LVS_SMALLICON 1 pixel
7820 * for LVS_REPORT is 1 pixel
7821 * for LVS_LIST is 1 column --> which is a 1 because the
7822 * scroll is based on columns not pixels
7825 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7826 INT nScrollDiff, HWND hScrollWnd)
7828 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7829 INT nOldScrollPos, nNewScrollPos;
7830 SCROLLINFO scrollInfo;
7832 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7833 debugscrollcode(nScrollCode), nScrollDiff);
7835 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7837 scrollInfo.cbSize = sizeof(SCROLLINFO);
7838 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7840 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7842 nOldScrollPos = scrollInfo.nPos;
7844 switch (nScrollCode)
7846 case SB_INTERNAL:
7847 break;
7849 case SB_LINELEFT:
7850 nScrollDiff = -1;
7851 break;
7853 case SB_LINERIGHT:
7854 nScrollDiff = 1;
7855 break;
7857 case SB_PAGELEFT:
7858 nScrollDiff = -scrollInfo.nPage;
7859 break;
7861 case SB_PAGERIGHT:
7862 nScrollDiff = scrollInfo.nPage;
7863 break;
7865 case SB_THUMBPOSITION:
7866 case SB_THUMBTRACK:
7867 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7868 break;
7870 default:
7871 nScrollDiff = 0;
7874 /* quit right away if pos isn't changing */
7875 if (nScrollDiff == 0) return 0;
7877 /* calculate new position, and handle overflows */
7878 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7879 if (nScrollDiff > 0) {
7880 if (nNewScrollPos < nOldScrollPos ||
7881 nNewScrollPos > scrollInfo.nMax)
7882 nNewScrollPos = scrollInfo.nMax;
7883 } else {
7884 if (nNewScrollPos > nOldScrollPos ||
7885 nNewScrollPos < scrollInfo.nMin)
7886 nNewScrollPos = scrollInfo.nMin;
7889 /* set the new position, and reread in case it changed */
7890 scrollInfo.fMask = SIF_POS;
7891 scrollInfo.nPos = nNewScrollPos;
7892 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7894 /* carry on only if it really changed */
7895 if (nNewScrollPos == nOldScrollPos) return 0;
7897 if(uView == LVS_REPORT)
7898 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7900 /* now adjust to client coordinates */
7901 nScrollDiff = nOldScrollPos - nNewScrollPos;
7902 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7904 /* and scroll the window */
7905 scroll_list(infoPtr, nScrollDiff, 0);
7907 return 0;
7910 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7912 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7913 INT gcWheelDelta = 0;
7914 INT pulScrollLines = 3;
7915 SCROLLINFO scrollInfo;
7917 TRACE("(wheelDelta=%d)\n", wheelDelta);
7919 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7920 gcWheelDelta -= wheelDelta;
7922 scrollInfo.cbSize = sizeof(SCROLLINFO);
7923 scrollInfo.fMask = SIF_POS;
7925 switch(uView)
7927 case LVS_ICON:
7928 case LVS_SMALLICON:
7930 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7931 * should be fixed in the future.
7933 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7934 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7935 break;
7937 case LVS_REPORT:
7938 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7940 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7941 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7942 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7944 break;
7946 case LVS_LIST:
7947 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7948 break;
7950 return 0;
7953 /***
7954 * DESCRIPTION:
7955 * ???
7957 * PARAMETER(S):
7958 * [I] infoPtr : valid pointer to the listview structure
7959 * [I] nVirtualKey : virtual key
7960 * [I] lKeyData : key data
7962 * RETURN:
7963 * Zero
7965 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7967 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7968 INT nItem = -1;
7969 NMLVKEYDOWN nmKeyDown;
7971 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7973 /* send LVN_KEYDOWN notification */
7974 nmKeyDown.wVKey = nVirtualKey;
7975 nmKeyDown.flags = 0;
7976 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7978 switch (nVirtualKey)
7980 case VK_RETURN:
7981 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7983 notify(infoPtr, NM_RETURN);
7984 notify(infoPtr, LVN_ITEMACTIVATE);
7986 break;
7988 case VK_HOME:
7989 if (infoPtr->nItemCount > 0)
7990 nItem = 0;
7991 break;
7993 case VK_END:
7994 if (infoPtr->nItemCount > 0)
7995 nItem = infoPtr->nItemCount - 1;
7996 break;
7998 case VK_LEFT:
7999 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8000 break;
8002 case VK_UP:
8003 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8004 break;
8006 case VK_RIGHT:
8007 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8008 break;
8010 case VK_DOWN:
8011 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8012 break;
8014 case VK_PRIOR:
8015 if (uView == LVS_REPORT)
8016 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
8017 else
8018 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8019 * LISTVIEW_GetCountPerRow(infoPtr);
8020 if(nItem < 0) nItem = 0;
8021 break;
8023 case VK_NEXT:
8024 if (uView == LVS_REPORT)
8025 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
8026 else
8027 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8028 * LISTVIEW_GetCountPerRow(infoPtr);
8029 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8030 break;
8033 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8034 LISTVIEW_KeySelection(infoPtr, nItem);
8036 return 0;
8039 /***
8040 * DESCRIPTION:
8041 * Kills the focus.
8043 * PARAMETER(S):
8044 * [I] infoPtr : valid pointer to the listview structure
8046 * RETURN:
8047 * Zero
8049 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8051 TRACE("()\n");
8053 /* if we did not have the focus, there's nothing to do */
8054 if (!infoPtr->bFocus) return 0;
8056 /* send NM_KILLFOCUS notification */
8057 notify(infoPtr, NM_KILLFOCUS);
8059 /* if we have a focus rectagle, get rid of it */
8060 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8062 /* set window focus flag */
8063 infoPtr->bFocus = FALSE;
8065 /* invalidate the selected items before reseting focus flag */
8066 LISTVIEW_InvalidateSelectedItems(infoPtr);
8068 return 0;
8071 /***
8072 * DESCRIPTION:
8073 * Processes double click messages (left mouse button).
8075 * PARAMETER(S):
8076 * [I] infoPtr : valid pointer to the listview structure
8077 * [I] wKey : key flag
8078 * [I] x,y : mouse coordinate
8080 * RETURN:
8081 * Zero
8083 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8085 LVHITTESTINFO htInfo;
8087 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8089 /* send NM_RELEASEDCAPTURE notification */
8090 notify(infoPtr, NM_RELEASEDCAPTURE);
8092 htInfo.pt.x = x;
8093 htInfo.pt.y = y;
8095 /* send NM_DBLCLK notification */
8096 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8097 notify_click(infoPtr, NM_DBLCLK, &htInfo);
8099 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8100 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8102 return 0;
8105 /***
8106 * DESCRIPTION:
8107 * Processes mouse down messages (left mouse button).
8109 * PARAMETERS:
8110 * infoPtr [I ] valid pointer to the listview structure
8111 * wKey [I ] key flag
8112 * x,y [I ] mouse coordinate
8114 * RETURN:
8115 * Zero
8117 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8119 LVHITTESTINFO lvHitTestInfo;
8120 static BOOL bGroupSelect = TRUE;
8121 POINT pt = { x, y };
8122 INT nItem;
8124 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8126 /* send NM_RELEASEDCAPTURE notification */
8127 notify(infoPtr, NM_RELEASEDCAPTURE);
8129 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8131 /* set left button down flag and record the click position */
8132 infoPtr->bLButtonDown = TRUE;
8133 infoPtr->ptClickPos = pt;
8135 lvHitTestInfo.pt.x = x;
8136 lvHitTestInfo.pt.y = y;
8138 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8139 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8140 infoPtr->nEditLabelItem = -1;
8141 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8143 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8145 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8146 if(state == 1 || state == 2)
8148 LVITEMW lvitem;
8149 state ^= 3;
8150 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8151 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8152 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8154 return 0;
8157 if (infoPtr->dwStyle & LVS_SINGLESEL)
8159 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8160 infoPtr->nEditLabelItem = nItem;
8161 else
8162 LISTVIEW_SetSelection(infoPtr, nItem);
8164 else
8166 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8168 if (bGroupSelect)
8170 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8171 LISTVIEW_SetItemFocus(infoPtr, nItem);
8172 infoPtr->nSelectionMark = nItem;
8174 else
8176 LVITEMW item;
8178 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8179 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8181 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8182 infoPtr->nSelectionMark = nItem;
8185 else if (wKey & MK_CONTROL)
8187 LVITEMW item;
8189 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8191 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8192 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8193 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8194 infoPtr->nSelectionMark = nItem;
8196 else if (wKey & MK_SHIFT)
8198 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8200 else
8202 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8203 infoPtr->nEditLabelItem = nItem;
8205 /* set selection (clears other pre-existing selections) */
8206 LISTVIEW_SetSelection(infoPtr, nItem);
8210 else
8212 /* remove all selections */
8213 LISTVIEW_DeselectAll(infoPtr);
8214 ReleaseCapture();
8217 return 0;
8220 /***
8221 * DESCRIPTION:
8222 * Processes mouse up messages (left mouse button).
8224 * PARAMETERS:
8225 * infoPtr [I ] valid pointer to the listview structure
8226 * wKey [I ] key flag
8227 * x,y [I ] mouse coordinate
8229 * RETURN:
8230 * Zero
8232 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8234 LVHITTESTINFO lvHitTestInfo;
8236 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8238 if (!infoPtr->bLButtonDown) return 0;
8240 lvHitTestInfo.pt.x = x;
8241 lvHitTestInfo.pt.y = y;
8243 /* send NM_CLICK notification */
8244 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8245 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8247 /* set left button flag */
8248 infoPtr->bLButtonDown = FALSE;
8250 /* if we clicked on a selected item, edit the label */
8251 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8252 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8254 return 0;
8257 /***
8258 * DESCRIPTION:
8259 * Destroys the listview control (called after WM_DESTROY).
8261 * PARAMETER(S):
8262 * [I] infoPtr : valid pointer to the listview structure
8264 * RETURN:
8265 * Zero
8267 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8269 TRACE("()\n");
8271 /* delete all items */
8272 LISTVIEW_DeleteAllItems(infoPtr);
8274 /* destroy data structure */
8275 DPA_Destroy(infoPtr->hdpaItems);
8276 DPA_Destroy(infoPtr->hdpaPosX);
8277 DPA_Destroy(infoPtr->hdpaPosY);
8278 DPA_Destroy(infoPtr->hdpaColumns);
8279 ranges_destroy(infoPtr->selectionRanges);
8281 /* destroy image lists */
8282 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8284 if (infoPtr->himlNormal)
8285 ImageList_Destroy(infoPtr->himlNormal);
8286 if (infoPtr->himlSmall)
8287 ImageList_Destroy(infoPtr->himlSmall);
8288 if (infoPtr->himlState)
8289 ImageList_Destroy(infoPtr->himlState);
8292 /* destroy font, bkgnd brush */
8293 infoPtr->hFont = 0;
8294 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8295 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8297 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8299 /* free listview info pointer*/
8300 Free(infoPtr);
8302 return 0;
8305 /***
8306 * DESCRIPTION:
8307 * Handles notifications from header.
8309 * PARAMETER(S):
8310 * [I] infoPtr : valid pointer to the listview structure
8311 * [I] nCtrlId : control identifier
8312 * [I] lpnmh : notification information
8314 * RETURN:
8315 * Zero
8317 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8319 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8321 TRACE("(lpnmh=%p)\n", lpnmh);
8323 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8325 switch (lpnmh->hdr.code)
8327 case HDN_ITEMCHANGINGW:
8328 case HDN_ITEMCHANGINGA:
8329 return notify_forward_header(infoPtr, lpnmh);
8330 case HDN_ITEMCHANGEDW:
8331 case HDN_ITEMCHANGEDA:
8332 notify_forward_header(infoPtr, lpnmh);
8333 /* Fall through */
8334 case HDN_TRACKW:
8335 case HDN_TRACKA:
8337 COLUMN_INFO *lpColumnInfo;
8338 INT dx, cxy;
8340 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8342 HDITEMW hdi;
8344 hdi.mask = HDI_WIDTH;
8345 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8346 cxy = hdi.cxy;
8348 else
8349 cxy = lpnmh->pitem->cxy;
8351 /* determine how much we change since the last know position */
8352 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8353 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8354 if (dx != 0)
8356 lpColumnInfo->rcHeader.right += dx;
8357 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8358 LISTVIEW_UpdateItemSize(infoPtr);
8359 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8361 POINT ptOrigin;
8362 RECT rcCol = lpColumnInfo->rcHeader;
8364 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8365 OffsetRect(&rcCol, ptOrigin.x, 0);
8367 rcCol.top = infoPtr->rcList.top;
8368 rcCol.bottom = infoPtr->rcList.bottom;
8370 /* resizing left-aligned columns leaves most of the left side untouched */
8371 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8373 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8374 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8377 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8381 break;
8383 case HDN_ITEMCLICKW:
8384 case HDN_ITEMCLICKA:
8386 /* Handle sorting by Header Column */
8387 NMLISTVIEW nmlv;
8389 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8390 nmlv.iItem = -1;
8391 nmlv.iSubItem = lpnmh->iItem;
8392 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8394 break;
8396 case HDN_DIVIDERDBLCLICKW:
8397 case HDN_DIVIDERDBLCLICKA:
8398 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8399 break;
8402 return 0;
8405 /***
8406 * DESCRIPTION:
8407 * Paint non-client area of control.
8409 * PARAMETER(S):
8410 * [I] infoPtr : valid pointer to the listview structureof the sender
8411 * [I] region : update region
8413 * RETURN:
8414 * TRUE - frame was painted
8415 * FALSE - call default window proc
8417 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8419 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8420 HDC dc;
8421 RECT r;
8422 HRGN cliprgn;
8423 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8424 cyEdge = GetSystemMetrics (SM_CYEDGE);
8426 if (!theme) return FALSE;
8428 GetWindowRect(infoPtr->hwndSelf, &r);
8430 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8431 r.right - cxEdge, r.bottom - cyEdge);
8432 if (region != (HRGN)1)
8433 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8434 OffsetRect(&r, -r.left, -r.top);
8436 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8437 OffsetRect(&r, -r.left, -r.top);
8439 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8440 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8441 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8442 ReleaseDC(infoPtr->hwndSelf, dc);
8444 /* Call default proc to get the scrollbars etc. painted */
8445 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8447 return TRUE;
8450 /***
8451 * DESCRIPTION:
8452 * Determines the type of structure to use.
8454 * PARAMETER(S):
8455 * [I] infoPtr : valid pointer to the listview structureof the sender
8456 * [I] hwndFrom : listview window handle
8457 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8459 * RETURN:
8460 * Zero
8462 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8464 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8466 if (nCommand != NF_REQUERY) return 0;
8468 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8470 return 0;
8473 /***
8474 * DESCRIPTION:
8475 * Paints/Repaints the listview control.
8477 * PARAMETER(S):
8478 * [I] infoPtr : valid pointer to the listview structure
8479 * [I] hdc : device context handle
8481 * RETURN:
8482 * Zero
8484 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8486 TRACE("(hdc=%p)\n", hdc);
8488 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8490 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8492 infoPtr->bNoItemMetrics = FALSE;
8493 LISTVIEW_UpdateItemSize(infoPtr);
8494 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8495 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8496 LISTVIEW_UpdateScroll(infoPtr);
8498 if (hdc)
8499 LISTVIEW_Refresh(infoPtr, hdc);
8500 else
8502 PAINTSTRUCT ps;
8504 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8505 if (!hdc) return 1;
8506 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8507 LISTVIEW_Refresh(infoPtr, hdc);
8508 EndPaint(infoPtr->hwndSelf, &ps);
8511 return 0;
8515 /***
8516 * DESCRIPTION:
8517 * Paints/Repaints the listview control.
8519 * PARAMETER(S):
8520 * [I] infoPtr : valid pointer to the listview structure
8521 * [I] hdc : device context handle
8522 * [I] options : drawing options
8524 * RETURN:
8525 * Zero
8527 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8529 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8531 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8532 return 0;
8534 if (options & PRF_ERASEBKGND)
8535 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8537 if (options & PRF_CLIENT)
8538 LISTVIEW_Paint(infoPtr, hdc);
8540 return 0;
8544 /***
8545 * DESCRIPTION:
8546 * Processes double click messages (right mouse button).
8548 * PARAMETER(S):
8549 * [I] infoPtr : valid pointer to the listview structure
8550 * [I] wKey : key flag
8551 * [I] x,y : mouse coordinate
8553 * RETURN:
8554 * Zero
8556 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8558 LVHITTESTINFO lvHitTestInfo;
8560 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8562 /* send NM_RELEASEDCAPTURE notification */
8563 notify(infoPtr, NM_RELEASEDCAPTURE);
8565 /* send NM_RDBLCLK notification */
8566 lvHitTestInfo.pt.x = x;
8567 lvHitTestInfo.pt.y = y;
8568 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8569 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8571 return 0;
8574 /***
8575 * DESCRIPTION:
8576 * Processes mouse down messages (right mouse button).
8578 * PARAMETER(S):
8579 * [I] infoPtr : valid pointer to the listview structure
8580 * [I] wKey : key flag
8581 * [I] x,y : mouse coordinate
8583 * RETURN:
8584 * Zero
8586 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8588 LVHITTESTINFO lvHitTestInfo;
8589 INT nItem;
8591 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8593 /* send NM_RELEASEDCAPTURE notification */
8594 notify(infoPtr, NM_RELEASEDCAPTURE);
8596 /* make sure the listview control window has the focus */
8597 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8599 /* set right button down flag */
8600 infoPtr->bRButtonDown = TRUE;
8602 /* determine the index of the selected item */
8603 lvHitTestInfo.pt.x = x;
8604 lvHitTestInfo.pt.y = y;
8605 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8607 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8609 LISTVIEW_SetItemFocus(infoPtr, nItem);
8610 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8611 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8612 LISTVIEW_SetSelection(infoPtr, nItem);
8614 else
8616 LISTVIEW_DeselectAll(infoPtr);
8619 return 0;
8622 /***
8623 * DESCRIPTION:
8624 * Processes mouse up messages (right mouse button).
8626 * PARAMETER(S):
8627 * [I] infoPtr : valid pointer to the listview structure
8628 * [I] wKey : key flag
8629 * [I] x,y : mouse coordinate
8631 * RETURN:
8632 * Zero
8634 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8636 LVHITTESTINFO lvHitTestInfo;
8637 POINT pt;
8639 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8641 if (!infoPtr->bRButtonDown) return 0;
8643 /* set button flag */
8644 infoPtr->bRButtonDown = FALSE;
8646 /* Send NM_RClICK notification */
8647 lvHitTestInfo.pt.x = x;
8648 lvHitTestInfo.pt.y = y;
8649 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8650 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8652 /* Change to screen coordinate for WM_CONTEXTMENU */
8653 pt = lvHitTestInfo.pt;
8654 ClientToScreen(infoPtr->hwndSelf, &pt);
8656 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8657 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8658 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8660 return 0;
8664 /***
8665 * DESCRIPTION:
8666 * Sets the cursor.
8668 * PARAMETER(S):
8669 * [I] infoPtr : valid pointer to the listview structure
8670 * [I] hwnd : window handle of window containing the cursor
8671 * [I] nHittest : hit-test code
8672 * [I] wMouseMsg : ideintifier of the mouse message
8674 * RETURN:
8675 * TRUE if cursor is set
8676 * FALSE otherwise
8678 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8680 LVHITTESTINFO lvHitTestInfo;
8682 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8684 if(!infoPtr->hHotCursor) return FALSE;
8686 GetCursorPos(&lvHitTestInfo.pt);
8687 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8689 SetCursor(infoPtr->hHotCursor);
8691 return TRUE;
8694 /***
8695 * DESCRIPTION:
8696 * Sets the focus.
8698 * PARAMETER(S):
8699 * [I] infoPtr : valid pointer to the listview structure
8700 * [I] hwndLoseFocus : handle of previously focused window
8702 * RETURN:
8703 * Zero
8705 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8707 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8709 /* if we have the focus already, there's nothing to do */
8710 if (infoPtr->bFocus) return 0;
8712 /* send NM_SETFOCUS notification */
8713 notify(infoPtr, NM_SETFOCUS);
8715 /* set window focus flag */
8716 infoPtr->bFocus = TRUE;
8718 /* put the focus rect back on */
8719 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8721 /* redraw all visible selected items */
8722 LISTVIEW_InvalidateSelectedItems(infoPtr);
8724 return 0;
8727 /***
8728 * DESCRIPTION:
8729 * Sets the font.
8731 * PARAMETER(S):
8732 * [I] infoPtr : valid pointer to the listview structure
8733 * [I] fRedraw : font handle
8734 * [I] fRedraw : redraw flag
8736 * RETURN:
8737 * Zero
8739 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8741 HFONT oldFont = infoPtr->hFont;
8743 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8745 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8746 if (infoPtr->hFont == oldFont) return 0;
8748 LISTVIEW_SaveTextMetrics(infoPtr);
8750 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8751 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8753 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8755 return 0;
8758 /***
8759 * DESCRIPTION:
8760 * Message handling for WM_SETREDRAW.
8761 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8763 * PARAMETER(S):
8764 * [I] infoPtr : valid pointer to the listview structure
8765 * [I] bRedraw: state of redraw flag
8767 * RETURN:
8768 * DefWinProc return value
8770 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8772 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8774 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8775 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8777 infoPtr->bRedraw = bRedraw;
8779 if(!bRedraw) return 0;
8781 if (is_autoarrange(infoPtr))
8782 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8783 LISTVIEW_UpdateScroll(infoPtr);
8785 /* despite what the WM_SETREDRAW docs says, apps expect us
8786 * to invalidate the listview here... stupid! */
8787 LISTVIEW_InvalidateList(infoPtr);
8789 return 0;
8792 /***
8793 * DESCRIPTION:
8794 * Resizes the listview control. This function processes WM_SIZE
8795 * messages. At this time, the width and height are not used.
8797 * PARAMETER(S):
8798 * [I] infoPtr : valid pointer to the listview structure
8799 * [I] Width : new width
8800 * [I] Height : new height
8802 * RETURN:
8803 * Zero
8805 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8807 RECT rcOld = infoPtr->rcList;
8809 TRACE("(width=%d, height=%d)\n", Width, Height);
8811 LISTVIEW_UpdateSize(infoPtr);
8812 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8814 /* do not bother with display related stuff if we're not redrawing */
8815 if (!is_redrawing(infoPtr)) return 0;
8817 if (is_autoarrange(infoPtr))
8818 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8820 LISTVIEW_UpdateScroll(infoPtr);
8822 /* refresh all only for lists whose height changed significantly */
8823 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8824 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8825 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8826 LISTVIEW_InvalidateList(infoPtr);
8828 return 0;
8831 /***
8832 * DESCRIPTION:
8833 * Sets the size information.
8835 * PARAMETER(S):
8836 * [I] infoPtr : valid pointer to the listview structure
8838 * RETURN:
8839 * None
8841 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8843 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8845 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8847 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8849 if (uView == LVS_LIST)
8851 /* Apparently the "LIST" style is supposed to have the same
8852 * number of items in a column even if there is no scroll bar.
8853 * Since if a scroll bar already exists then the bottom is already
8854 * reduced, only reduce if the scroll bar does not currently exist.
8855 * The "2" is there to mimic the native control. I think it may be
8856 * related to either padding or edges. (GLA 7/2002)
8858 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8859 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8860 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8862 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8864 HDLAYOUT hl;
8865 WINDOWPOS wp;
8867 hl.prc = &infoPtr->rcList;
8868 hl.pwpos = &wp;
8869 Header_Layout(infoPtr->hwndHeader, &hl);
8871 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8873 infoPtr->rcList.top = max(wp.cy, 0);
8876 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8879 /***
8880 * DESCRIPTION:
8881 * Processes WM_STYLECHANGED messages.
8883 * PARAMETER(S):
8884 * [I] infoPtr : valid pointer to the listview structure
8885 * [I] wStyleType : window style type (normal or extended)
8886 * [I] lpss : window style information
8888 * RETURN:
8889 * Zero
8891 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8892 const STYLESTRUCT *lpss)
8894 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8895 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8897 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8898 wStyleType, lpss->styleOld, lpss->styleNew);
8900 if (wStyleType != GWL_STYLE) return 0;
8902 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8903 /* what if LVS_OWNERDATA changed? */
8904 /* or LVS_SINGLESEL */
8905 /* or LVS_SORT{AS,DES}CENDING */
8907 infoPtr->dwStyle = lpss->styleNew;
8909 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8910 ((lpss->styleNew & WS_HSCROLL) == 0))
8911 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8913 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8914 ((lpss->styleNew & WS_VSCROLL) == 0))
8915 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8917 if (uNewView != uOldView)
8919 SIZE oldIconSize = infoPtr->iconSize;
8920 HIMAGELIST himl;
8922 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8923 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8925 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8926 SetRectEmpty(&infoPtr->rcFocus);
8928 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8929 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8931 if (uNewView == LVS_ICON)
8933 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8935 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8936 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8937 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8940 else if (uNewView == LVS_REPORT)
8942 HDLAYOUT hl;
8943 WINDOWPOS wp;
8945 hl.prc = &infoPtr->rcList;
8946 hl.pwpos = &wp;
8947 Header_Layout(infoPtr->hwndHeader, &hl);
8948 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8951 LISTVIEW_UpdateItemSize(infoPtr);
8954 if (uNewView == LVS_REPORT)
8955 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8957 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8958 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8959 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8961 /* update the size of the client area */
8962 LISTVIEW_UpdateSize(infoPtr);
8964 /* add scrollbars if needed */
8965 LISTVIEW_UpdateScroll(infoPtr);
8967 /* invalidate client area + erase background */
8968 LISTVIEW_InvalidateList(infoPtr);
8970 return 0;
8973 /***
8974 * DESCRIPTION:
8975 * Window procedure of the listview control.
8978 static LRESULT WINAPI
8979 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8981 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8983 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8985 if (!infoPtr && (uMsg != WM_CREATE))
8986 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8988 switch (uMsg)
8990 case LVM_APPROXIMATEVIEWRECT:
8991 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8992 LOWORD(lParam), HIWORD(lParam));
8993 case LVM_ARRANGE:
8994 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8996 /* case LVM_CANCELEDITLABEL: */
8998 case LVM_CREATEDRAGIMAGE:
8999 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9001 case LVM_DELETEALLITEMS:
9002 return LISTVIEW_DeleteAllItems(infoPtr);
9004 case LVM_DELETECOLUMN:
9005 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9007 case LVM_DELETEITEM:
9008 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9010 case LVM_EDITLABELW:
9011 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9013 case LVM_EDITLABELA:
9014 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9016 /* case LVM_ENABLEGROUPVIEW: */
9018 case LVM_ENSUREVISIBLE:
9019 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9021 case LVM_FINDITEMW:
9022 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9024 case LVM_FINDITEMA:
9025 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9027 case LVM_GETBKCOLOR:
9028 return infoPtr->clrBk;
9030 /* case LVM_GETBKIMAGE: */
9032 case LVM_GETCALLBACKMASK:
9033 return infoPtr->uCallbackMask;
9035 case LVM_GETCOLUMNA:
9036 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9038 case LVM_GETCOLUMNW:
9039 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9041 case LVM_GETCOLUMNORDERARRAY:
9042 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9044 case LVM_GETCOLUMNWIDTH:
9045 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9047 case LVM_GETCOUNTPERPAGE:
9048 return LISTVIEW_GetCountPerPage(infoPtr);
9050 case LVM_GETEDITCONTROL:
9051 return (LRESULT)infoPtr->hwndEdit;
9053 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9054 return infoPtr->dwLvExStyle;
9056 /* case LVM_GETGROUPINFO: */
9058 /* case LVM_GETGROUPMETRICS: */
9060 case LVM_GETHEADER:
9061 return (LRESULT)infoPtr->hwndHeader;
9063 case LVM_GETHOTCURSOR:
9064 return (LRESULT)infoPtr->hHotCursor;
9066 case LVM_GETHOTITEM:
9067 return infoPtr->nHotItem;
9069 case LVM_GETHOVERTIME:
9070 return infoPtr->dwHoverTime;
9072 case LVM_GETIMAGELIST:
9073 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9075 /* case LVM_GETINSERTMARK: */
9077 /* case LVM_GETINSERTMARKCOLOR: */
9079 /* case LVM_GETINSERTMARKRECT: */
9081 case LVM_GETISEARCHSTRINGA:
9082 case LVM_GETISEARCHSTRINGW:
9083 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9084 return FALSE;
9086 case LVM_GETITEMA:
9087 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9089 case LVM_GETITEMW:
9090 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9092 case LVM_GETITEMCOUNT:
9093 return infoPtr->nItemCount;
9095 case LVM_GETITEMPOSITION:
9096 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9098 case LVM_GETITEMRECT:
9099 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9101 case LVM_GETITEMSPACING:
9102 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9104 case LVM_GETITEMSTATE:
9105 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9107 case LVM_GETITEMTEXTA:
9108 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9110 case LVM_GETITEMTEXTW:
9111 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9113 case LVM_GETNEXTITEM:
9114 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9116 case LVM_GETNUMBEROFWORKAREAS:
9117 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9118 return 1;
9120 case LVM_GETORIGIN:
9121 if (!lParam) return FALSE;
9122 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9123 return TRUE;
9125 /* case LVM_GETOUTLINECOLOR: */
9127 /* case LVM_GETSELECTEDCOLUMN: */
9129 case LVM_GETSELECTEDCOUNT:
9130 return LISTVIEW_GetSelectedCount(infoPtr);
9132 case LVM_GETSELECTIONMARK:
9133 return infoPtr->nSelectionMark;
9135 case LVM_GETSTRINGWIDTHA:
9136 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9138 case LVM_GETSTRINGWIDTHW:
9139 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9141 case LVM_GETSUBITEMRECT:
9142 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9144 case LVM_GETTEXTBKCOLOR:
9145 return infoPtr->clrTextBk;
9147 case LVM_GETTEXTCOLOR:
9148 return infoPtr->clrText;
9150 /* case LVM_GETTILEINFO: */
9152 /* case LVM_GETTILEVIEWINFO: */
9154 case LVM_GETTOOLTIPS:
9155 if( !infoPtr->hwndToolTip )
9156 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9157 return (LRESULT)infoPtr->hwndToolTip;
9159 case LVM_GETTOPINDEX:
9160 return LISTVIEW_GetTopIndex(infoPtr);
9162 /*case LVM_GETUNICODEFORMAT:
9163 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9164 return FALSE;*/
9166 /* case LVM_GETVIEW: */
9168 case LVM_GETVIEWRECT:
9169 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9171 case LVM_GETWORKAREAS:
9172 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9173 return FALSE;
9175 /* case LVM_HASGROUP: */
9177 case LVM_HITTEST:
9178 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9180 case LVM_INSERTCOLUMNA:
9181 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9183 case LVM_INSERTCOLUMNW:
9184 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9186 /* case LVM_INSERTGROUP: */
9188 /* case LVM_INSERTGROUPSORTED: */
9190 case LVM_INSERTITEMA:
9191 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9193 case LVM_INSERTITEMW:
9194 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9196 /* case LVM_INSERTMARKHITTEST: */
9198 /* case LVM_ISGROUPVIEWENABLED: */
9200 /* case LVM_MAPIDTOINDEX: */
9202 /* case LVM_MAPINDEXTOID: */
9204 /* case LVM_MOVEGROUP: */
9206 /* case LVM_MOVEITEMTOGROUP: */
9208 case LVM_REDRAWITEMS:
9209 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9211 /* case LVM_REMOVEALLGROUPS: */
9213 /* case LVM_REMOVEGROUP: */
9215 case LVM_SCROLL:
9216 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9218 case LVM_SETBKCOLOR:
9219 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9221 /* case LVM_SETBKIMAGE: */
9223 case LVM_SETCALLBACKMASK:
9224 infoPtr->uCallbackMask = (UINT)wParam;
9225 return TRUE;
9227 case LVM_SETCOLUMNA:
9228 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9230 case LVM_SETCOLUMNW:
9231 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9233 case LVM_SETCOLUMNORDERARRAY:
9234 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9236 case LVM_SETCOLUMNWIDTH:
9237 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9239 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9240 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9242 /* case LVM_SETGROUPINFO: */
9244 /* case LVM_SETGROUPMETRICS: */
9246 case LVM_SETHOTCURSOR:
9247 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9249 case LVM_SETHOTITEM:
9250 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9252 case LVM_SETHOVERTIME:
9253 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9255 case LVM_SETICONSPACING:
9256 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9258 case LVM_SETIMAGELIST:
9259 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9261 /* case LVM_SETINFOTIP: */
9263 /* case LVM_SETINSERTMARK: */
9265 /* case LVM_SETINSERTMARKCOLOR: */
9267 case LVM_SETITEMA:
9268 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9270 case LVM_SETITEMW:
9271 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9273 case LVM_SETITEMCOUNT:
9274 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9276 case LVM_SETITEMPOSITION:
9278 POINT pt;
9279 pt.x = (short)LOWORD(lParam);
9280 pt.y = (short)HIWORD(lParam);
9281 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9284 case LVM_SETITEMPOSITION32:
9285 if (lParam == 0) return FALSE;
9286 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9288 case LVM_SETITEMSTATE:
9289 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9291 case LVM_SETITEMTEXTA:
9292 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9294 case LVM_SETITEMTEXTW:
9295 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9297 /* case LVM_SETOUTLINECOLOR: */
9299 /* case LVM_SETSELECTEDCOLUMN: */
9301 case LVM_SETSELECTIONMARK:
9302 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9304 case LVM_SETTEXTBKCOLOR:
9305 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9307 case LVM_SETTEXTCOLOR:
9308 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9310 /* case LVM_SETTILEINFO: */
9312 /* case LVM_SETTILEVIEWINFO: */
9314 /* case LVM_SETTILEWIDTH: */
9316 case LVM_SETTOOLTIPS:
9317 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9319 /* case LVM_SETUNICODEFORMAT: */
9321 /* case LVM_SETVIEW: */
9323 /* case LVM_SETWORKAREAS: */
9325 /* case LVM_SORTGROUPS: */
9327 case LVM_SORTITEMS:
9328 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9330 /* LVM_SORTITEMSEX: */
9332 case LVM_SUBITEMHITTEST:
9333 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9335 case LVM_UPDATE:
9336 return LISTVIEW_Update(infoPtr, (INT)wParam);
9338 case WM_CHAR:
9339 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9341 case WM_COMMAND:
9342 return LISTVIEW_Command(infoPtr, wParam, lParam);
9344 case WM_CREATE:
9345 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9347 case WM_DESTROY:
9348 return LISTVIEW_Destroy(infoPtr);
9350 case WM_ENABLE:
9351 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9353 case WM_ERASEBKGND:
9354 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9356 case WM_GETDLGCODE:
9357 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9359 case WM_GETFONT:
9360 return (LRESULT)infoPtr->hFont;
9362 case WM_HSCROLL:
9363 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9365 case WM_KEYDOWN:
9366 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9368 case WM_KILLFOCUS:
9369 return LISTVIEW_KillFocus(infoPtr);
9371 case WM_LBUTTONDBLCLK:
9372 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9374 case WM_LBUTTONDOWN:
9375 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9377 case WM_LBUTTONUP:
9378 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9380 case WM_MOUSEMOVE:
9381 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9383 case WM_MOUSEHOVER:
9384 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9386 case WM_NCDESTROY:
9387 return LISTVIEW_NCDestroy(infoPtr);
9389 case WM_NCPAINT:
9390 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9391 return 0;
9392 goto fwd_msg;
9394 case WM_NOTIFY:
9395 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9396 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9397 else return 0;
9399 case WM_NOTIFYFORMAT:
9400 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9402 case WM_PRINTCLIENT:
9403 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9405 case WM_PAINT:
9406 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9408 case WM_RBUTTONDBLCLK:
9409 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9411 case WM_RBUTTONDOWN:
9412 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9414 case WM_RBUTTONUP:
9415 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9417 case WM_SETCURSOR:
9418 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9419 return TRUE;
9420 goto fwd_msg;
9422 case WM_SETFOCUS:
9423 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9425 case WM_SETFONT:
9426 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9428 case WM_SETREDRAW:
9429 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9431 case WM_SIZE:
9432 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9434 case WM_STYLECHANGED:
9435 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9437 case WM_SYSCOLORCHANGE:
9438 COMCTL32_RefreshSysColors();
9439 return 0;
9441 /* case WM_TIMER: */
9442 case WM_THEMECHANGED:
9443 return LISTVIEW_ThemeChanged(infoPtr);
9445 case WM_VSCROLL:
9446 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9448 case WM_MOUSEWHEEL:
9449 if (wParam & (MK_SHIFT | MK_CONTROL))
9450 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9451 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9453 case WM_WINDOWPOSCHANGED:
9454 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9456 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9457 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9458 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9460 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9462 MEASUREITEMSTRUCT mis;
9463 mis.CtlType = ODT_LISTVIEW;
9464 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9465 mis.itemID = -1;
9466 mis.itemWidth = 0;
9467 mis.itemData = 0;
9468 mis.itemHeight= infoPtr->nItemHeight;
9469 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9470 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9471 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9474 LISTVIEW_UpdateSize(infoPtr);
9475 LISTVIEW_UpdateScroll(infoPtr);
9477 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9479 /* case WM_WININICHANGE: */
9481 default:
9482 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9483 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9485 fwd_msg:
9486 /* call default window procedure */
9487 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9492 /***
9493 * DESCRIPTION:
9494 * Registers the window class.
9496 * PARAMETER(S):
9497 * None
9499 * RETURN:
9500 * None
9502 void LISTVIEW_Register(void)
9504 WNDCLASSW wndClass;
9506 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9507 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9508 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9509 wndClass.cbClsExtra = 0;
9510 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9511 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9512 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9513 wndClass.lpszClassName = WC_LISTVIEWW;
9514 RegisterClassW(&wndClass);
9517 /***
9518 * DESCRIPTION:
9519 * Unregisters the window class.
9521 * PARAMETER(S):
9522 * None
9524 * RETURN:
9525 * None
9527 void LISTVIEW_Unregister(void)
9529 UnregisterClassW(WC_LISTVIEWW, NULL);
9532 /***
9533 * DESCRIPTION:
9534 * Handle any WM_COMMAND messages
9536 * PARAMETER(S):
9537 * [I] infoPtr : valid pointer to the listview structure
9538 * [I] wParam : the first message parameter
9539 * [I] lParam : the second message parameter
9541 * RETURN:
9542 * Zero.
9544 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9546 switch (HIWORD(wParam))
9548 case EN_UPDATE:
9551 * Adjust the edit window size
9553 WCHAR buffer[1024];
9554 HDC hdc = GetDC(infoPtr->hwndEdit);
9555 HFONT hFont, hOldFont = 0;
9556 RECT rect;
9557 SIZE sz;
9558 int len;
9560 if (!infoPtr->hwndEdit || !hdc) return 0;
9561 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9562 GetWindowRect(infoPtr->hwndEdit, &rect);
9564 /* Select font to get the right dimension of the string */
9565 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9566 if(hFont != 0)
9568 hOldFont = SelectObject(hdc, hFont);
9571 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9573 TEXTMETRICW textMetric;
9575 /* Add Extra spacing for the next character */
9576 GetTextMetricsW(hdc, &textMetric);
9577 sz.cx += (textMetric.tmMaxCharWidth * 2);
9579 SetWindowPos (
9580 infoPtr->hwndEdit,
9581 HWND_TOP,
9584 sz.cx,
9585 rect.bottom - rect.top,
9586 SWP_DRAWFRAME|SWP_NOMOVE);
9588 if(hFont != 0)
9589 SelectObject(hdc, hOldFont);
9591 ReleaseDC(infoPtr->hwndEdit, hdc);
9593 break;
9596 default:
9597 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9600 return 0;
9604 /***
9605 * DESCRIPTION:
9606 * Subclassed edit control windproc function
9608 * PARAMETER(S):
9609 * [I] hwnd : the edit window handle
9610 * [I] uMsg : the message that is to be processed
9611 * [I] wParam : first message parameter
9612 * [I] lParam : second message parameter
9613 * [I] isW : TRUE if input is Unicode
9615 * RETURN:
9616 * Zero.
9618 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9620 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9621 BOOL cancel = FALSE;
9623 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9624 hwnd, uMsg, wParam, lParam, isW);
9626 switch (uMsg)
9628 case WM_GETDLGCODE:
9629 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9631 case WM_KILLFOCUS:
9632 break;
9634 case WM_DESTROY:
9636 WNDPROC editProc = infoPtr->EditWndProc;
9637 infoPtr->EditWndProc = 0;
9638 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9639 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9642 case WM_KEYDOWN:
9643 if (VK_ESCAPE == (INT)wParam)
9645 cancel = TRUE;
9646 break;
9648 else if (VK_RETURN == (INT)wParam)
9649 break;
9651 default:
9652 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9655 /* kill the edit */
9656 if (infoPtr->hwndEdit)
9658 LPWSTR buffer = NULL;
9660 infoPtr->hwndEdit = 0;
9661 if (!cancel)
9663 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9665 if (len)
9667 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9669 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9670 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9674 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9676 if (buffer) Free(buffer);
9680 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9681 return 0;
9684 /***
9685 * DESCRIPTION:
9686 * Subclassed edit control Unicode windproc function
9688 * PARAMETER(S):
9689 * [I] hwnd : the edit window handle
9690 * [I] uMsg : the message that is to be processed
9691 * [I] wParam : first message parameter
9692 * [I] lParam : second message parameter
9694 * RETURN:
9696 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9698 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9701 /***
9702 * DESCRIPTION:
9703 * Subclassed edit control ANSI windproc function
9705 * PARAMETER(S):
9706 * [I] hwnd : the edit window handle
9707 * [I] uMsg : the message that is to be processed
9708 * [I] wParam : first message parameter
9709 * [I] lParam : second message parameter
9711 * RETURN:
9713 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9715 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9718 /***
9719 * DESCRIPTION:
9720 * Creates a subclassed edit cotrol
9722 * PARAMETER(S):
9723 * [I] infoPtr : valid pointer to the listview structure
9724 * [I] text : initial text for the edit
9725 * [I] style : the window style
9726 * [I] isW : TRUE if input is Unicode
9728 * RETURN:
9730 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9731 INT x, INT y, INT width, INT height, BOOL isW)
9733 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9734 HWND hedit;
9735 SIZE sz;
9736 HDC hdc;
9737 HDC hOldFont=0;
9738 TEXTMETRICW textMetric;
9739 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9741 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9743 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9744 hdc = GetDC(infoPtr->hwndSelf);
9746 /* Select the font to get appropriate metric dimensions */
9747 if(infoPtr->hFont != 0)
9748 hOldFont = SelectObject(hdc, infoPtr->hFont);
9750 /*Get String Length in pixels */
9751 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9753 /*Add Extra spacing for the next character */
9754 GetTextMetricsW(hdc, &textMetric);
9755 sz.cx += (textMetric.tmMaxCharWidth * 2);
9757 if(infoPtr->hFont != 0)
9758 SelectObject(hdc, hOldFont);
9760 ReleaseDC(infoPtr->hwndSelf, hdc);
9761 if (isW)
9762 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9763 else
9764 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9766 if (!hedit) return 0;
9768 infoPtr->EditWndProc = (WNDPROC)
9769 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9770 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9772 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9774 return hedit;