Add support for LVN_ODSTATECHANGED.
[wine/dcerpc.git] / dlls / comctl32 / listview.c
blobe935f43b23766659b4676a44531926014e1425f6
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 Oct. 21, 2002, by Dimitrie O. Paun.
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 * Features
36 * -- Hot item handling, mouse hovering
37 * -- Workareas support
38 * -- Tilemode support
39 * -- Groups support
41 * Bugs
42 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
44 * -- LVA_SNAPTOGRID not implemented
45 * -- LISTVIEW_ApproximateViewRect partially implemented
46 * -- LISTVIEW_[GS]etColumnOrderArray stubs
47 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
48 * -- LISTVIEW_SetIconSpacing is incomplete
49 * -- LISTVIEW_SortItems is broken
50 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
52 * Speedups
53 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
54 * linear in the number of items in the list, and this is
55 * unacceptable for large lists.
56 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
57 * instead of inserting in the right spot
58 * -- we should keep an ordered array of coordinates in iconic mode
59 * this would allow to frame items (iterator_frameditems),
60 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
62 * Flags
63 * -- LVIF_COLUMNS
64 * -- LVIF_GROUPID
65 * -- LVIF_NORECOMPUTE
67 * States
68 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
69 * -- LVIS_CUT
70 * -- LVIS_DROPHILITED
71 * -- LVIS_OVERLAYMASK
73 * Styles
74 * -- LVS_NOLABELWRAP
75 * -- LVS_NOSCROLL (see Q137520)
76 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
78 * Extended Styles
79 * -- LVS_EX_BORDERSELECT
80 * -- LVS_EX_FLATSB
81 * -- LVS_EX_GRIDLINES
82 * -- LVS_EX_HEADERDRAGDROP
83 * -- LVS_EX_INFOTIP
84 * -- LVS_EX_LABELTIP
85 * -- LVS_EX_MULTIWORKAREAS
86 * -- LVS_EX_ONECLICKACTIVATE
87 * -- LVS_EX_REGIONAL
88 * -- LVS_EX_SIMPLESELECT
89 * -- LVS_EX_TRACKSELECT
90 * -- LVS_EX_TWOCLICKACTIVATE
91 * -- LVS_EX_UNDERLINECOLD
92 * -- LVS_EX_UNDERLINEHOT
94 * Notifications:
95 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
96 * -- LVN_GETINFOTIP
97 * -- LVN_HOTTRACK
98 * -- LVN_MARQUEEBEGIN
99 * -- LVN_ODFINDITEM
100 * -- LVN_SETDISPINFO
101 * -- NM_HOVER
103 * Messages:
104 * -- LVM_CANCELEDITLABEL
105 * -- LVM_ENABLEGROUPVIEW
106 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
107 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
108 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
109 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
110 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
111 * -- LVM_GETINSERTMARKRECT
112 * -- LVM_GETNUMBEROFWORKAREAS
113 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
114 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
115 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
116 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
117 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
118 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
119 * -- LVM_GETVIEW, LVM_SETVIEW
120 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
121 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
122 * -- LVM_INSERTGROUPSORTED
123 * -- LVM_INSERTMARKHITTEST
124 * -- LVM_ISGROUPVIEWENABLED
125 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
126 * -- LVM_MOVEGROUP
127 * -- LVM_MOVEITEMTOGROUP
128 * -- LVM_SETINFOTIP
129 * -- LVM_SETTILEWIDTH
130 * -- LVM_SORTGROUPS
131 * -- LVM_SORTITEMSEX
133 * Known differences in message stream from native control (not known if
134 * these differences cause problems):
135 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
136 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
137 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
138 * processing for "USEDOUBLECLICKTIME".
141 #include "config.h"
142 #include "wine/port.h"
144 #include <assert.h>
145 #include <ctype.h>
146 #include <string.h>
147 #include <stdlib.h>
148 #include <stdarg.h>
149 #include <stdio.h>
151 #include "windef.h"
152 #include "winbase.h"
153 #include "winnt.h"
154 #include "wingdi.h"
155 #include "winuser.h"
156 #include "winnls.h"
157 #include "commctrl.h"
158 #include "comctl32.h"
160 #include "wine/debug.h"
161 #include "wine/unicode.h"
163 WINE_DEFAULT_DEBUG_CHANNEL(listview);
165 /* make sure you set this to 0 for production use! */
166 #define DEBUG_RANGES 1
168 typedef struct tagCOLUMN_INFO
170 RECT rcHeader; /* tracks the header's rectangle */
171 int fmt; /* same as LVCOLUMN.fmt */
172 } COLUMN_INFO;
174 typedef struct tagITEMHDR
176 LPWSTR pszText;
177 INT iImage;
178 } ITEMHDR, *LPITEMHDR;
180 typedef struct tagSUBITEM_INFO
182 ITEMHDR hdr;
183 INT iSubItem;
184 } SUBITEM_INFO;
186 typedef struct tagITEM_INFO
188 ITEMHDR hdr;
189 UINT state;
190 LPARAM lParam;
191 INT iIndent;
192 } ITEM_INFO;
194 typedef struct tagRANGE
196 INT lower;
197 INT upper;
198 } RANGE;
200 typedef struct tagRANGES
202 HDPA hdpa;
203 } *RANGES;
205 typedef struct tagITERATOR
207 INT nItem;
208 INT nSpecial;
209 RANGE range;
210 RANGES ranges;
211 INT index;
212 } ITERATOR;
214 typedef struct tagLISTVIEW_INFO
216 HWND hwndSelf;
217 HBRUSH hBkBrush;
218 COLORREF clrBk;
219 COLORREF clrText;
220 COLORREF clrTextBk;
221 COLORREF clrTextBkDefault;
222 HIMAGELIST himlNormal;
223 HIMAGELIST himlSmall;
224 HIMAGELIST himlState;
225 BOOL bLButtonDown;
226 BOOL bRButtonDown;
227 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
228 INT nItemHeight;
229 INT nItemWidth;
230 RANGES selectionRanges;
231 INT nSelectionMark;
232 INT nHotItem;
233 SHORT notifyFormat;
234 HWND hwndNotify;
235 RECT rcList; /* This rectangle is really the window
236 * client rectangle possibly reduced by the
237 * horizontal scroll bar and/or header - see
238 * LISTVIEW_UpdateSize. This rectangle offset
239 * by the LISTVIEW_GetOrigin value is in
240 * client coordinates */
241 SIZE iconSize;
242 SIZE iconSpacing;
243 SIZE iconStateSize;
244 UINT uCallbackMask;
245 HWND hwndHeader;
246 HCURSOR hHotCursor;
247 HFONT hDefaultFont;
248 HFONT hFont;
249 INT ntmHeight; /* Some cached metrics of the font used */
250 INT ntmAveCharWidth; /* by the listview to draw items */
251 BOOL bRedraw; /* Turns on/off repaints & invalidations */
252 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
253 BOOL bFocus;
254 BOOL bDoChangeNotify; /* send change notification messages? */
255 INT nFocusedItem;
256 RECT rcFocus;
257 DWORD dwStyle; /* the cached window GWL_STYLE */
258 DWORD dwLvExStyle; /* extended listview style */
259 INT nItemCount; /* the number of items in the list */
260 HDPA hdpaItems; /* array ITEM_INFO pointers */
261 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
262 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
263 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
264 POINT currIconPos; /* this is the position next icon will be placed */
265 PFNLVCOMPARE pfnCompare;
266 LPARAM lParamSort;
267 HWND hwndEdit;
268 WNDPROC EditWndProc;
269 INT nEditLabelItem;
270 DWORD dwHoverTime;
271 HWND hwndToolTip;
273 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
275 DWORD lastKeyPressTimestamp;
276 WPARAM charCode;
277 INT nSearchParamLength;
278 WCHAR szSearchParam[ MAX_PATH ];
279 BOOL bIsDrawing;
280 } LISTVIEW_INFO;
283 * constants
285 /* How many we debug buffer to allocate */
286 #define DEBUG_BUFFERS 20
287 /* The size of a single debug bbuffer */
288 #define DEBUG_BUFFER_SIZE 256
290 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
291 #define SB_INTERNAL -1
293 /* maximum size of a label */
294 #define DISP_TEXT_SIZE 512
296 /* padding for items in list and small icon display modes */
297 #define WIDTH_PADDING 12
299 /* padding for items in list, report and small icon display modes */
300 #define HEIGHT_PADDING 1
302 /* offset of items in report display mode */
303 #define REPORT_MARGINX 2
305 /* padding for icon in large icon display mode
306 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
307 * that HITTEST will see.
308 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
309 * ICON_TOP_PADDING - sum of the two above.
310 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
311 * LABEL_HOR_PADDING - between text and sides of box
312 * LABEL_VERT_PADDING - between bottom of text and end of box
314 * ICON_LR_PADDING - additional width above icon size.
315 * ICON_LR_HALF - half of the above value
317 #define ICON_TOP_PADDING_NOTHITABLE 2
318 #define ICON_TOP_PADDING_HITABLE 2
319 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
320 #define ICON_BOTTOM_PADDING 4
321 #define LABEL_HOR_PADDING 5
322 #define LABEL_VERT_PADDING 7
323 #define ICON_LR_PADDING 16
324 #define ICON_LR_HALF (ICON_LR_PADDING/2)
326 /* default label width for items in list and small icon display modes */
327 #define DEFAULT_LABEL_WIDTH 40
329 /* default column width for items in list display mode */
330 #define DEFAULT_COLUMN_WIDTH 128
332 /* Size of "line" scroll for V & H scrolls */
333 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
335 /* Padding betwen image and label */
336 #define IMAGE_PADDING 2
338 /* Padding behind the label */
339 #define TRAILING_LABEL_PADDING 12
340 #define TRAILING_HEADER_PADDING 11
342 /* Border for the icon caption */
343 #define CAPTION_BORDER 2
345 /* Standard DrawText flags */
346 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
347 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
348 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
350 /* The time in milliseconds to reset the search in the list */
351 #define KEY_DELAY 450
353 /* Dump the LISTVIEW_INFO structure to the debug channel */
354 #define LISTVIEW_DUMP(iP) do { \
355 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
356 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
357 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
358 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
359 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
360 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
361 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
362 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
363 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
364 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
365 } while(0)
368 * forward declarations
370 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
371 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
372 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
373 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
374 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
375 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
376 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
377 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
378 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
379 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
380 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
381 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
382 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
383 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
384 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
385 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
386 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
387 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
388 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
389 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
390 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
391 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
392 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
393 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
394 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
395 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
397 /******** Text handling functions *************************************/
399 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
400 * text string. The string may be ANSI or Unicode, in which case
401 * the boolean isW tells us the type of the string.
403 * The name of the function tell what type of strings it expects:
404 * W: Unicode, T: ANSI/Unicode - function of isW
407 static inline BOOL is_textW(LPCWSTR text)
409 return text != NULL && text != LPSTR_TEXTCALLBACKW;
412 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
414 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
415 return is_textW(text);
418 static inline int textlenT(LPCWSTR text, BOOL isW)
420 return !is_textT(text, isW) ? 0 :
421 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
424 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
426 if (isDestW)
427 if (isSrcW) lstrcpynW(dest, src, max);
428 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
429 else
430 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
431 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
434 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
436 LPWSTR wstr = (LPWSTR)text;
438 if (!isW && is_textT(text, isW))
440 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
441 wstr = Alloc(len * sizeof(WCHAR));
442 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
444 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
445 return wstr;
448 static inline void textfreeT(LPWSTR wstr, BOOL isW)
450 if (!isW && is_textT(wstr, isW)) Free (wstr);
454 * dest is a pointer to a Unicode string
455 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
457 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
459 BOOL bResult = TRUE;
461 if (src == LPSTR_TEXTCALLBACKW)
463 if (is_textW(*dest)) Free(*dest);
464 *dest = LPSTR_TEXTCALLBACKW;
466 else
468 LPWSTR pszText = textdupTtoW(src, isW);
469 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
470 bResult = Str_SetPtrW(dest, pszText);
471 textfreeT(pszText, isW);
473 return bResult;
477 * compares a Unicode to a Unicode/ANSI text string
479 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
481 if (!aw) return bt ? -1 : 0;
482 if (!bt) return aw ? 1 : 0;
483 if (aw == LPSTR_TEXTCALLBACKW)
484 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
485 if (bt != LPSTR_TEXTCALLBACKW)
487 LPWSTR bw = textdupTtoW(bt, isW);
488 int r = bw ? lstrcmpW(aw, bw) : 1;
489 textfreeT(bw, isW);
490 return r;
493 return 1;
496 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
498 int res;
500 n = min(min(n, strlenW(s1)), strlenW(s2));
501 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
502 return res ? res - sizeof(WCHAR) : res;
505 /******** Debugging functions *****************************************/
507 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
509 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
510 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
513 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
515 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
516 n = min(textlenT(text, isW), n);
517 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
520 static char* debug_getbuf()
522 static int index = 0;
523 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
524 return buffers[index++ % DEBUG_BUFFERS];
527 static inline const char* debugrange(const RANGE *lprng)
529 if (lprng)
531 char* buf = debug_getbuf();
532 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
533 return buf;
534 } else return "(null)";
537 static inline const char* debugpoint(const POINT *lppt)
539 if (lppt)
541 char* buf = debug_getbuf();
542 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
543 return buf;
544 } else return "(null)";
547 static inline const char* debugrect(const RECT *rect)
549 if (rect)
551 char* buf = debug_getbuf();
552 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
553 rect->left, rect->top, rect->right, rect->bottom);
554 return buf;
555 } else return "(null)";
558 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
560 char* buf = debug_getbuf(), *text = buf;
561 int len, size = DEBUG_BUFFER_SIZE;
563 if (pScrollInfo == NULL) return "(null)";
564 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
565 if (len == -1) goto end; buf += len; size -= len;
566 if (pScrollInfo->fMask & SIF_RANGE)
567 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
568 else len = 0;
569 if (len == -1) goto end; buf += len; size -= len;
570 if (pScrollInfo->fMask & SIF_PAGE)
571 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
572 else len = 0;
573 if (len == -1) goto end; buf += len; size -= len;
574 if (pScrollInfo->fMask & SIF_POS)
575 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
576 else len = 0;
577 if (len == -1) goto end; buf += len; size -= len;
578 if (pScrollInfo->fMask & SIF_TRACKPOS)
579 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
580 else len = 0;
581 if (len == -1) goto end; buf += len; size -= len;
582 goto undo;
583 end:
584 buf = text + strlen(text);
585 undo:
586 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
587 return text;
590 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
592 if (plvnm)
594 char* buf = debug_getbuf();
595 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
596 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
597 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
598 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
599 return buf;
600 } else return "(null)";
603 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
605 char* buf = debug_getbuf(), *text = buf;
606 int len, size = DEBUG_BUFFER_SIZE;
608 if (lpLVItem == NULL) return "(null)";
609 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
610 if (len == -1) goto end; buf += len; size -= len;
611 if (lpLVItem->mask & LVIF_STATE)
612 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
613 else len = 0;
614 if (len == -1) goto end; buf += len; size -= len;
615 if (lpLVItem->mask & LVIF_TEXT)
616 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
617 else len = 0;
618 if (len == -1) goto end; buf += len; size -= len;
619 if (lpLVItem->mask & LVIF_IMAGE)
620 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
621 else len = 0;
622 if (len == -1) goto end; buf += len; size -= len;
623 if (lpLVItem->mask & LVIF_PARAM)
624 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
625 else len = 0;
626 if (len == -1) goto end; buf += len; size -= len;
627 if (lpLVItem->mask & LVIF_INDENT)
628 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
629 else len = 0;
630 if (len == -1) goto end; buf += len; size -= len;
631 goto undo;
632 end:
633 buf = text + strlen(text);
634 undo:
635 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
636 return text;
639 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
641 char* buf = debug_getbuf(), *text = buf;
642 int len, size = DEBUG_BUFFER_SIZE;
644 if (lpColumn == NULL) return "(null)";
645 len = snprintf(buf, size, "{");
646 if (len == -1) goto end; buf += len; size -= len;
647 if (lpColumn->mask & LVCF_SUBITEM)
648 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
649 else len = 0;
650 if (len == -1) goto end; buf += len; size -= len;
651 if (lpColumn->mask & LVCF_FMT)
652 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
653 else len = 0;
654 if (len == -1) goto end; buf += len; size -= len;
655 if (lpColumn->mask & LVCF_WIDTH)
656 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
657 else len = 0;
658 if (len == -1) goto end; buf += len; size -= len;
659 if (lpColumn->mask & LVCF_TEXT)
660 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
661 else len = 0;
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpColumn->mask & LVCF_IMAGE)
664 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
665 else len = 0;
666 if (len == -1) goto end; buf += len; size -= len;
667 if (lpColumn->mask & LVCF_ORDER)
668 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
669 else len = 0;
670 if (len == -1) goto end; buf += len; size -= len;
671 goto undo;
672 end:
673 buf = text + strlen(text);
674 undo:
675 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
676 return text;
679 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
681 if (lpht)
683 char* buf = debug_getbuf();
684 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
685 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
686 return buf;
687 } else return "(null)";
690 /* Return the corresponding text for a given scroll value */
691 static inline LPCSTR debugscrollcode(int nScrollCode)
693 switch(nScrollCode)
695 case SB_LINELEFT: return "SB_LINELEFT";
696 case SB_LINERIGHT: return "SB_LINERIGHT";
697 case SB_PAGELEFT: return "SB_PAGELEFT";
698 case SB_PAGERIGHT: return "SB_PAGERIGHT";
699 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
700 case SB_THUMBTRACK: return "SB_THUMBTRACK";
701 case SB_ENDSCROLL: return "SB_ENDSCROLL";
702 case SB_INTERNAL: return "SB_INTERNAL";
703 default: return "unknown";
708 /******** Notification functions i************************************/
710 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
712 LRESULT result;
714 TRACE("(code=%d)\n", code);
716 pnmh->hwndFrom = infoPtr->hwndSelf;
717 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
718 pnmh->code = code;
719 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
720 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
722 TRACE(" <= %ld\n", result);
724 return result;
727 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
729 NMHDR nmh;
730 return notify_hdr(infoPtr, code, &nmh);
733 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
735 NMITEMACTIVATE nmia;
736 LVITEMW item;
738 if (htInfo) {
739 nmia.uNewState = 0;
740 nmia.uOldState = 0;
741 nmia.uChanged = 0;
742 nmia.uKeyFlags = 0;
744 item.mask = LVIF_PARAM|LVIF_STATE;
745 item.iItem = htInfo->iItem;
746 item.iSubItem = 0;
747 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
748 nmia.lParam = item.lParam;
749 nmia.uOldState = item.state;
750 nmia.uNewState = item.state | LVIS_ACTIVATING;
751 nmia.uChanged = LVIF_STATE;
754 nmia.iItem = htInfo->iItem;
755 nmia.iSubItem = htInfo->iSubItem;
756 nmia.ptAction = htInfo->pt;
758 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
759 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
760 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
762 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
765 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
767 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
768 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
771 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
773 NMLISTVIEW nmlv;
774 LVITEMW item;
776 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
777 ZeroMemory(&nmlv, sizeof(nmlv));
778 nmlv.iItem = lvht->iItem;
779 nmlv.iSubItem = lvht->iSubItem;
780 nmlv.ptAction = lvht->pt;
781 item.mask = LVIF_PARAM;
782 item.iItem = lvht->iItem;
783 item.iSubItem = 0;
784 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
785 return notify_listview(infoPtr, code, &nmlv);
788 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
790 NMLISTVIEW nmlv;
791 LVITEMW item;
793 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
794 nmlv.iItem = nItem;
795 item.mask = LVIF_PARAM;
796 item.iItem = nItem;
797 item.iSubItem = 0;
798 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
799 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
802 static int get_ansi_notification(INT unicodeNotificationCode)
804 switch (unicodeNotificationCode)
806 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
807 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
808 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
809 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
810 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
811 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
813 ERR("unknown notification %x\n", unicodeNotificationCode);
814 assert(FALSE);
815 return 0;
819 Send notification. depends on dispinfoW having same
820 structure as dispinfoA.
821 infoPtr : listview struct
822 notificationCode : *Unicode* notification code
823 pdi : dispinfo structure (can be unicode or ansi)
824 isW : TRUE if dispinfo is Unicode
826 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
828 BOOL bResult = FALSE;
829 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
830 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
831 LPWSTR pszTempBuf = NULL, savPszText = NULL;
833 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
835 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
836 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
839 if (convertToAnsi || convertToUnicode)
841 if (notificationCode != LVN_GETDISPINFOW)
843 cchTempBufMax = convertToUnicode ?
844 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
845 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
847 else
849 cchTempBufMax = pdi->item.cchTextMax;
850 *pdi->item.pszText = 0; /* make sure we don't process garbage */
853 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
854 if (!pszTempBuf) return FALSE;
856 if (convertToUnicode)
857 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
858 pszTempBuf, cchTempBufMax);
859 else
860 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
861 cchTempBufMax, NULL, NULL);
863 savCchTextMax = pdi->item.cchTextMax;
864 savPszText = pdi->item.pszText;
865 pdi->item.pszText = pszTempBuf;
866 pdi->item.cchTextMax = cchTempBufMax;
869 if (infoPtr->notifyFormat == NFR_ANSI)
870 realNotifCode = get_ansi_notification(notificationCode);
871 else
872 realNotifCode = notificationCode;
873 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
874 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
876 if (convertToUnicode || convertToAnsi)
878 if (convertToUnicode) /* note : pointer can be changed by app ! */
879 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
880 savCchTextMax, NULL, NULL);
881 else
882 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
883 savPszText, savCchTextMax);
884 pdi->item.pszText = savPszText; /* restores our buffer */
885 pdi->item.cchTextMax = savCchTextMax;
886 Free (pszTempBuf);
888 return bResult;
891 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
892 const RECT *rcBounds, const LVITEMW *lplvItem)
894 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
895 lpnmlvcd->nmcd.hdc = hdc;
896 lpnmlvcd->nmcd.rc = *rcBounds;
897 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
898 lpnmlvcd->clrText = infoPtr->clrText;
899 if (!lplvItem) return;
900 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
901 lpnmlvcd->iSubItem = lplvItem->iSubItem;
902 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
903 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
904 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
905 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
908 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
910 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
911 DWORD result;
913 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
914 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
915 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
916 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
917 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
918 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
919 return result;
922 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
924 /* apprently, for selected items, we have to override the returned values */
925 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
927 if (infoPtr->bFocus)
929 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
930 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
932 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
934 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
935 lpnmlvcd->clrText = comctl32_color.clrBtnText;
939 /* Set the text attributes */
940 if (lpnmlvcd->clrTextBk != CLR_NONE)
942 SetBkMode(hdc, OPAQUE);
943 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
944 SetBkColor(hdc, infoPtr->clrTextBkDefault);
945 else
946 SetBkColor(hdc,lpnmlvcd->clrTextBk);
948 else
949 SetBkMode(hdc, TRANSPARENT);
950 SetTextColor(hdc, lpnmlvcd->clrText);
953 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
955 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
958 /******** Item iterator functions **********************************/
960 static RANGES ranges_create(int count);
961 static void ranges_destroy(RANGES ranges);
962 static BOOL ranges_add(RANGES ranges, RANGE range);
963 static BOOL ranges_del(RANGES ranges, RANGE range);
964 static void ranges_dump(RANGES ranges);
966 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
968 RANGE range = { nItem, nItem + 1 };
970 return ranges_add(ranges, range);
973 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
975 RANGE range = { nItem, nItem + 1 };
977 return ranges_del(ranges, range);
980 /***
981 * ITERATOR DOCUMENTATION
983 * The iterator functions allow for easy, and convenient iteration
984 * over items of iterest in the list. Typically, you create a
985 * iterator, use it, and destroy it, as such:
986 * ITERATOR i;
988 * iterator_xxxitems(&i, ...);
989 * while (iterator_{prev,next}(&i)
991 * //code which uses i.nItem
993 * iterator_destroy(&i);
995 * where xxx is either: framed, or visible.
996 * Note that it is important that the code destroys the iterator
997 * after it's done with it, as the creation of the iterator may
998 * allocate memory, which thus needs to be freed.
1000 * You can iterate both forwards, and backwards through the list,
1001 * by using iterator_next or iterator_prev respectively.
1003 * Lower numbered items are draw on top of higher number items in
1004 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1005 * items may overlap). So, to test items, you should use
1006 * iterator_next
1007 * which lists the items top to bottom (in Z-order).
1008 * For drawing items, you should use
1009 * iterator_prev
1010 * which lists the items bottom to top (in Z-order).
1011 * If you keep iterating over the items after the end-of-items
1012 * marker (-1) is returned, the iterator will start from the
1013 * beginning. Typically, you don't need to test for -1,
1014 * because iterator_{next,prev} will return TRUE if more items
1015 * are to be iterated over, or FALSE otherwise.
1017 * Note: the iterator is defined to be bidirectional. That is,
1018 * any number of prev followed by any number of next, or
1019 * five versa, should leave the iterator at the same item:
1020 * prev * n, next * n = next * n, prev * n
1022 * The iterator has a notion of an out-of-order, special item,
1023 * which sits at the start of the list. This is used in
1024 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1025 * which needs to be first, as it may overlap other items.
1027 * The code is a bit messy because we have:
1028 * - a special item to deal with
1029 * - simple range, or composite range
1030 * - empty range.
1031 * If you find bugs, or want to add features, please make sure you
1032 * always check/modify *both* iterator_prev, and iterator_next.
1035 /****
1036 * This function iterates through the items in increasing order,
1037 * but prefixed by the special item, then -1. That is:
1038 * special, 1, 2, 3, ..., n, -1.
1039 * Each item is listed only once.
1041 static inline BOOL iterator_next(ITERATOR* i)
1043 if (i->nItem == -1)
1045 i->nItem = i->nSpecial;
1046 if (i->nItem != -1) return TRUE;
1048 if (i->nItem == i->nSpecial)
1050 if (i->ranges) i->index = 0;
1051 goto pickarange;
1054 i->nItem++;
1055 testitem:
1056 if (i->nItem == i->nSpecial) i->nItem++;
1057 if (i->nItem < i->range.upper) return TRUE;
1059 pickarange:
1060 if (i->ranges)
1062 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1063 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1064 else goto end;
1066 else if (i->nItem >= i->range.upper) goto end;
1068 i->nItem = i->range.lower;
1069 if (i->nItem >= 0) goto testitem;
1070 end:
1071 i->nItem = -1;
1072 return FALSE;
1075 /****
1076 * This function iterates through the items in decreasing order,
1077 * followed by the special item, then -1. That is:
1078 * n, n-1, ..., 3, 2, 1, special, -1.
1079 * Each item is listed only once.
1081 static inline BOOL iterator_prev(ITERATOR* i)
1083 BOOL start = FALSE;
1085 if (i->nItem == -1)
1087 start = TRUE;
1088 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1089 goto pickarange;
1091 if (i->nItem == i->nSpecial)
1093 i->nItem = -1;
1094 return FALSE;
1097 testitem:
1098 i->nItem--;
1099 if (i->nItem == i->nSpecial) i->nItem--;
1100 if (i->nItem >= i->range.lower) return TRUE;
1102 pickarange:
1103 if (i->ranges)
1105 if (i->index > 0)
1106 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1107 else goto end;
1109 else if (!start && i->nItem < i->range.lower) goto end;
1111 i->nItem = i->range.upper;
1112 if (i->nItem > 0) goto testitem;
1113 end:
1114 return (i->nItem = i->nSpecial) != -1;
1117 static RANGE iterator_range(ITERATOR* i)
1119 RANGE range;
1121 if (!i->ranges) return i->range;
1123 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1125 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1126 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1128 else range.lower = range.upper = 0;
1130 return range;
1133 /***
1134 * Releases resources associated with this ierator.
1136 static inline void iterator_destroy(ITERATOR* i)
1138 ranges_destroy(i->ranges);
1141 /***
1142 * Create an empty iterator.
1144 static inline BOOL iterator_empty(ITERATOR* i)
1146 ZeroMemory(i, sizeof(*i));
1147 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1148 return TRUE;
1151 /***
1152 * Create an iterator over a range.
1154 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1156 iterator_empty(i);
1157 i->range = range;
1158 return TRUE;
1161 /***
1162 * Create an iterator over a bunch of ranges.
1163 * Please note that the iterator will take ownership of the ranges,
1164 * and will free them upon destruction.
1166 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1168 iterator_empty(i);
1169 i->ranges = ranges;
1170 return TRUE;
1173 /***
1174 * Creates an iterator over the items which intersect lprc.
1176 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1178 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1179 RECT frame = *lprc, rcItem, rcTemp;
1180 POINT Origin;
1182 /* in case we fail, we want to return an empty iterator */
1183 if (!iterator_empty(i)) return FALSE;
1185 LISTVIEW_GetOrigin(infoPtr, &Origin);
1187 TRACE("(lprc=%s)\n", debugrect(lprc));
1188 OffsetRect(&frame, -Origin.x, -Origin.y);
1190 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1192 INT nItem;
1194 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1196 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1197 if (IntersectRect(&rcTemp, &rcItem, lprc))
1198 i->nSpecial = infoPtr->nFocusedItem;
1200 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1201 /* to do better here, we need to have PosX, and PosY sorted */
1202 TRACE("building icon ranges:\n");
1203 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1205 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1206 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1207 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1208 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1209 if (IntersectRect(&rcTemp, &rcItem, &frame))
1210 ranges_additem(i->ranges, nItem);
1212 return TRUE;
1214 else if (uView == LVS_REPORT)
1216 RANGE range;
1218 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1219 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1221 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1222 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1223 if (range.upper <= range.lower) return TRUE;
1224 if (!iterator_rangeitems(i, range)) return FALSE;
1225 TRACE(" report=%s\n", debugrange(&i->range));
1227 else
1229 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1230 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1231 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1232 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1233 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1234 INT lower = nFirstCol * nPerCol + nFirstRow;
1235 RANGE item_range;
1236 INT nCol;
1238 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1239 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1241 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1243 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1244 TRACE("building list ranges:\n");
1245 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1247 item_range.lower = nCol * nPerCol + nFirstRow;
1248 if(item_range.lower >= infoPtr->nItemCount) break;
1249 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1250 TRACE(" list=%s\n", debugrange(&item_range));
1251 ranges_add(i->ranges, item_range);
1255 return TRUE;
1258 /***
1259 * Creates an iterator over the items which intersect the visible region of hdc.
1261 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1263 POINT Origin, Position;
1264 RECT rcItem, rcClip;
1265 INT rgntype;
1267 rgntype = GetClipBox(hdc, &rcClip);
1268 if (rgntype == NULLREGION) return iterator_empty(i);
1269 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1270 if (rgntype == SIMPLEREGION) return TRUE;
1272 /* first deal with the special item */
1273 if (i->nSpecial != -1)
1275 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1276 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1279 /* if we can't deal with the region, we'll just go with the simple range */
1280 LISTVIEW_GetOrigin(infoPtr, &Origin);
1281 TRACE("building visible range:\n");
1282 if (!i->ranges && i->range.lower < i->range.upper)
1284 if (!(i->ranges = ranges_create(50))) return TRUE;
1285 if (!ranges_add(i->ranges, i->range))
1287 ranges_destroy(i->ranges);
1288 i->ranges = 0;
1289 return TRUE;
1293 /* now delete the invisible items from the list */
1294 while(iterator_next(i))
1296 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1297 rcItem.left = Position.x + Origin.x;
1298 rcItem.top = Position.y + Origin.y;
1299 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1300 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1301 if (!RectVisible(hdc, &rcItem))
1302 ranges_delitem(i->ranges, i->nItem);
1304 /* the iterator should restart on the next iterator_next */
1305 TRACE("done\n");
1307 return TRUE;
1310 /******** Misc helper functions ************************************/
1312 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1313 WPARAM wParam, LPARAM lParam, BOOL isW)
1315 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1316 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1319 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1321 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1323 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1324 (uView == LVS_ICON || uView == LVS_SMALLICON);
1327 /******** Internal API functions ************************************/
1329 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1331 static COLUMN_INFO mainItem;
1333 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1334 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1335 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1338 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1340 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1343 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1345 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1348 /* Listview invalidation functions: use _only_ these functions to invalidate */
1350 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1352 return infoPtr->bRedraw;
1355 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1357 if(!is_redrawing(infoPtr)) return;
1358 TRACE(" invalidating rect=%s\n", debugrect(rect));
1359 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1362 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1364 RECT rcBox;
1366 if(!is_redrawing(infoPtr)) return;
1367 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1368 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1371 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1373 POINT Origin, Position;
1374 RECT rcBox;
1376 if(!is_redrawing(infoPtr)) return;
1377 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1378 LISTVIEW_GetOrigin(infoPtr, &Origin);
1379 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1380 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1381 rcBox.top = 0;
1382 rcBox.bottom = infoPtr->nItemHeight;
1383 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1384 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1387 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1389 LISTVIEW_InvalidateRect(infoPtr, NULL);
1392 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1394 RECT rcCol;
1396 if(!is_redrawing(infoPtr)) return;
1397 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1398 rcCol.top = infoPtr->rcList.top;
1399 rcCol.bottom = infoPtr->rcList.bottom;
1400 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1403 /***
1404 * DESCRIPTION:
1405 * Retrieves the number of items that can fit vertically in the client area.
1407 * PARAMETER(S):
1408 * [I] infoPtr : valid pointer to the listview structure
1410 * RETURN:
1411 * Number of items per row.
1413 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1415 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1417 return max(nListWidth/infoPtr->nItemWidth, 1);
1420 /***
1421 * DESCRIPTION:
1422 * Retrieves the number of items that can fit horizontally in the client
1423 * area.
1425 * PARAMETER(S):
1426 * [I] infoPtr : valid pointer to the listview structure
1428 * RETURN:
1429 * Number of items per column.
1431 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1433 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1435 return max(nListHeight / infoPtr->nItemHeight, 1);
1439 /*************************************************************************
1440 * LISTVIEW_ProcessLetterKeys
1442 * Processes keyboard messages generated by pressing the letter keys
1443 * on the keyboard.
1444 * What this does is perform a case insensitive search from the
1445 * current position with the following quirks:
1446 * - If two chars or more are pressed in quick succession we search
1447 * for the corresponding string (e.g. 'abc').
1448 * - If there is a delay we wipe away the current search string and
1449 * restart with just that char.
1450 * - If the user keeps pressing the same character, whether slowly or
1451 * fast, so that the search string is entirely composed of this
1452 * character ('aaaaa' for instance), then we search for first item
1453 * that starting with that character.
1454 * - If the user types the above character in quick succession, then
1455 * we must also search for the corresponding string ('aaaaa'), and
1456 * go to that string if there is a match.
1458 * PARAMETERS
1459 * [I] hwnd : handle to the window
1460 * [I] charCode : the character code, the actual character
1461 * [I] keyData : key data
1463 * RETURNS
1465 * Zero.
1467 * BUGS
1469 * - The current implementation has a list of characters it will
1470 * accept and it ignores averything else. In particular it will
1471 * ignore accentuated characters which seems to match what
1472 * Windows does. But I'm not sure it makes sense to follow
1473 * Windows there.
1474 * - We don't sound a beep when the search fails.
1476 * SEE ALSO
1478 * TREEVIEW_ProcessLetterKeys
1480 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1482 INT nItem;
1483 INT endidx,idx;
1484 LVITEMW item;
1485 WCHAR buffer[MAX_PATH];
1486 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1488 /* simple parameter checking */
1489 if (!charCode || !keyData) return 0;
1491 /* only allow the valid WM_CHARs through */
1492 if (!isalnum(charCode) &&
1493 charCode != '.' && charCode != '`' && charCode != '!' &&
1494 charCode != '@' && charCode != '#' && charCode != '$' &&
1495 charCode != '%' && charCode != '^' && charCode != '&' &&
1496 charCode != '*' && charCode != '(' && charCode != ')' &&
1497 charCode != '-' && charCode != '_' && charCode != '+' &&
1498 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1499 charCode != '}' && charCode != '[' && charCode != '{' &&
1500 charCode != '/' && charCode != '?' && charCode != '>' &&
1501 charCode != '<' && charCode != ',' && charCode != '~')
1502 return 0;
1504 /* if there's one item or less, there is no where to go */
1505 if (infoPtr->nItemCount <= 1) return 0;
1507 /* update the search parameters */
1508 infoPtr->lastKeyPressTimestamp = GetTickCount();
1509 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1510 if (infoPtr->nSearchParamLength < MAX_PATH)
1511 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1512 if (infoPtr->charCode != charCode)
1513 infoPtr->charCode = charCode = 0;
1514 } else {
1515 infoPtr->charCode=charCode;
1516 infoPtr->szSearchParam[0]=charCode;
1517 infoPtr->nSearchParamLength=1;
1518 /* Redundant with the 1 char string */
1519 charCode=0;
1522 /* and search from the current position */
1523 nItem=-1;
1524 if (infoPtr->nFocusedItem >= 0) {
1525 endidx=infoPtr->nFocusedItem;
1526 idx=endidx;
1527 /* if looking for single character match,
1528 * then we must always move forward
1530 if (infoPtr->nSearchParamLength == 1)
1531 idx++;
1532 } else {
1533 endidx=infoPtr->nItemCount;
1534 idx=0;
1536 do {
1537 if (idx == infoPtr->nItemCount) {
1538 if (endidx == infoPtr->nItemCount || endidx == 0)
1539 break;
1540 idx=0;
1543 /* get item */
1544 item.mask = LVIF_TEXT;
1545 item.iItem = idx;
1546 item.iSubItem = 0;
1547 item.pszText = buffer;
1548 item.cchTextMax = MAX_PATH;
1549 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1551 /* check for a match */
1552 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1553 nItem=idx;
1554 break;
1555 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1556 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1557 /* This would work but we must keep looking for a longer match */
1558 nItem=idx;
1560 idx++;
1561 } while (idx != endidx);
1563 if (nItem != -1)
1564 LISTVIEW_KeySelection(infoPtr, nItem);
1566 return 0;
1569 /*************************************************************************
1570 * LISTVIEW_UpdateHeaderSize [Internal]
1572 * Function to resize the header control
1574 * PARAMS
1575 * [I] hwnd : handle to a window
1576 * [I] nNewScrollPos : scroll pos to set
1578 * RETURNS
1579 * None.
1581 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1583 RECT winRect;
1584 POINT point[2];
1586 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1588 GetWindowRect(infoPtr->hwndHeader, &winRect);
1589 point[0].x = winRect.left;
1590 point[0].y = winRect.top;
1591 point[1].x = winRect.right;
1592 point[1].y = winRect.bottom;
1594 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1595 point[0].x = -nNewScrollPos;
1596 point[1].x += nNewScrollPos;
1598 SetWindowPos(infoPtr->hwndHeader,0,
1599 point[0].x,point[0].y,point[1].x,point[1].y,
1600 SWP_NOZORDER | SWP_NOACTIVATE);
1603 /***
1604 * DESCRIPTION:
1605 * Update the scrollbars. This functions should be called whenever
1606 * the content, size or view changes.
1608 * PARAMETER(S):
1609 * [I] infoPtr : valid pointer to the listview structure
1611 * RETURN:
1612 * None
1614 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1616 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1617 SCROLLINFO horzInfo, vertInfo;
1619 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1621 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1622 horzInfo.cbSize = sizeof(SCROLLINFO);
1623 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1625 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1626 if (uView == LVS_LIST)
1628 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1629 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1631 /* scroll by at least one column per page */
1632 if(horzInfo.nPage < infoPtr->nItemWidth)
1633 horzInfo.nPage = infoPtr->nItemWidth;
1635 horzInfo.nPage /= infoPtr->nItemWidth;
1637 else if (uView == LVS_REPORT)
1639 horzInfo.nMax = infoPtr->nItemWidth;
1641 else /* LVS_ICON, or LVS_SMALLICON */
1643 RECT rcView;
1645 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1648 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1649 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1650 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1651 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1653 /* Setting the horizontal scroll can change the listview size
1654 * (and potentially everything else) so we need to recompute
1655 * everything again for the vertical scroll
1658 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1659 vertInfo.cbSize = sizeof(SCROLLINFO);
1660 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1662 if (uView == LVS_REPORT)
1664 vertInfo.nMax = infoPtr->nItemCount;
1666 /* scroll by at least one page */
1667 if(vertInfo.nPage < infoPtr->nItemHeight)
1668 vertInfo.nPage = infoPtr->nItemHeight;
1670 vertInfo.nPage /= infoPtr->nItemHeight;
1672 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1674 RECT rcView;
1676 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1679 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1680 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1681 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1682 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1684 /* Update the Header Control */
1685 if (uView == LVS_REPORT)
1687 horzInfo.fMask = SIF_POS;
1688 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1689 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1694 /***
1695 * DESCRIPTION:
1696 * Shows/hides the focus rectangle.
1698 * PARAMETER(S):
1699 * [I] infoPtr : valid pointer to the listview structure
1700 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1702 * RETURN:
1703 * None
1705 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1707 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1708 HDC hdc;
1710 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1712 if (infoPtr->nFocusedItem < 0) return;
1714 /* we need some gymnastics in ICON mode to handle large items */
1715 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1717 RECT rcBox;
1719 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1720 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1722 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1723 return;
1727 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1729 /* for some reason, owner draw should work only in report mode */
1730 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1732 DRAWITEMSTRUCT dis;
1733 LVITEMW item;
1735 item.iItem = infoPtr->nFocusedItem;
1736 item.iSubItem = 0;
1737 item.mask = LVIF_PARAM;
1738 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1740 ZeroMemory(&dis, sizeof(dis));
1741 dis.CtlType = ODT_LISTVIEW;
1742 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1743 dis.itemID = item.iItem;
1744 dis.itemAction = ODA_FOCUS;
1745 if (fShow) dis.itemState |= ODS_FOCUS;
1746 dis.hwndItem = infoPtr->hwndSelf;
1747 dis.hDC = hdc;
1748 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1749 dis.itemData = item.lParam;
1751 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1753 else
1755 DrawFocusRect(hdc, &infoPtr->rcFocus);
1757 done:
1758 ReleaseDC(infoPtr->hwndSelf, hdc);
1761 /***
1762 * Invalidates all visible selected items.
1764 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1766 ITERATOR i;
1768 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1769 while(iterator_next(&i))
1771 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1772 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1774 iterator_destroy(&i);
1778 /***
1779 * DESCRIPTION: [INTERNAL]
1780 * Computes an item's (left,top) corner, relative to rcView.
1781 * That is, the position has NOT been made relative to the Origin.
1782 * This is deliberate, to avoid computing the Origin over, and
1783 * over again, when this function is call in a loop. Instead,
1784 * one ca factor the computation of the Origin before the loop,
1785 * and offset the value retured by this function, on every iteration.
1787 * PARAMETER(S):
1788 * [I] infoPtr : valid pointer to the listview structure
1789 * [I] nItem : item number
1790 * [O] lpptOrig : item top, left corner
1792 * RETURN:
1793 * None.
1795 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1797 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1799 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1801 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1803 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1804 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1806 else if (uView == LVS_LIST)
1808 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1809 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1810 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1812 else /* LVS_REPORT */
1814 lpptPosition->x = 0;
1815 lpptPosition->y = nItem * infoPtr->nItemHeight;
1819 /***
1820 * DESCRIPTION: [INTERNAL]
1821 * Compute the rectangles of an item. This is to localize all
1822 * the computations in one place. If you are not interested in some
1823 * of these values, simply pass in a NULL -- the fucntion is smart
1824 * enough to compute only what's necessary. The function computes
1825 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1826 * one, the BOX rectangle. This rectangle is very cheap to compute,
1827 * and is guaranteed to contain all the other rectangles. Computing
1828 * the ICON rect is also cheap, but all the others are potentaily
1829 * expensive. This gives an easy and effective optimization when
1830 * searching (like point inclusion, or rectangle intersection):
1831 * first test against the BOX, and if TRUE, test agains the desired
1832 * rectangle.
1833 * If the function does not have all the necessary information
1834 * to computed the requested rectangles, will crash with a
1835 * failed assertion. This is done so we catch all programming
1836 * errors, given that the function is called only from our code.
1838 * We have the following 'special' meanings for a few fields:
1839 * * If LVIS_FOCUSED is set, we assume the item has the focus
1840 * This is important in ICON mode, where it might get a larger
1841 * then usual rectange
1843 * Please note that subitem support works only in REPORT mode.
1845 * PARAMETER(S):
1846 * [I] infoPtr : valid pointer to the listview structure
1847 * [I] lpLVItem : item to compute the measures for
1848 * [O] lprcBox : ptr to Box rectangle
1849 * The internal LVIR_BOX rectangle
1850 * [0] lprcState : ptr to State icon rectangle
1851 * The internal LVIR_STATE rectangle
1852 * [O] lprcIcon : ptr to Icon rectangle
1853 * Same as LVM_GETITEMRECT with LVIR_ICON
1854 * [O] lprcLabel : ptr to Label rectangle
1855 * Same as LVM_GETITEMRECT with LVIR_LABEL
1857 * RETURN:
1858 * None.
1860 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1861 LPRECT lprcBox, LPRECT lprcState,
1862 LPRECT lprcIcon, LPRECT lprcLabel)
1864 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1865 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1866 RECT Box, State, Icon, Label;
1867 COLUMN_INFO *lpColumnInfo = NULL;
1869 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1871 /* Be smart and try to figure out the minimum we have to do */
1872 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1873 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1875 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1876 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1878 if (lprcLabel) doLabel = TRUE;
1879 if (doLabel || lprcIcon) doIcon = TRUE;
1880 if (doIcon || lprcState) doState = TRUE;
1882 /************************************************************/
1883 /* compute the box rectangle (it should be cheap to do) */
1884 /************************************************************/
1885 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1886 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1888 if (lpLVItem->iSubItem)
1890 Box = lpColumnInfo->rcHeader;
1892 else
1894 Box.left = 0;
1895 Box.right = infoPtr->nItemWidth;
1897 Box.top = 0;
1898 Box.bottom = infoPtr->nItemHeight;
1900 /************************************************************/
1901 /* compute STATEICON bounding box */
1902 /************************************************************/
1903 if (doState)
1905 if (uView == LVS_ICON)
1907 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1908 if (infoPtr->himlNormal)
1909 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1910 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1912 else
1914 /* we need the ident in report mode, if we don't have it, we fail */
1915 State.left = Box.left;
1916 if (uView == LVS_REPORT)
1918 if (lpLVItem->iSubItem == 0)
1920 State.left += REPORT_MARGINX;
1921 assert(lpLVItem->mask & LVIF_INDENT);
1922 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1925 State.top = Box.top;
1927 State.right = State.left;
1928 State.bottom = State.top;
1929 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1931 State.right += infoPtr->iconStateSize.cx;
1932 State.bottom += infoPtr->iconStateSize.cy;
1934 if (lprcState) *lprcState = State;
1935 TRACE(" - state=%s\n", debugrect(&State));
1938 /************************************************************/
1939 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1940 /************************************************************/
1941 if (doIcon)
1943 if (uView == LVS_ICON)
1945 Icon.left = Box.left;
1946 if (infoPtr->himlNormal)
1947 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1948 Icon.top = Box.top + ICON_TOP_PADDING;
1949 Icon.right = Icon.left;
1950 Icon.bottom = Icon.top;
1951 if (infoPtr->himlNormal)
1953 Icon.right += infoPtr->iconSize.cx;
1954 Icon.bottom += infoPtr->iconSize.cy;
1957 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1959 Icon.left = State.right;
1960 Icon.top = Box.top;
1961 Icon.right = Icon.left;
1962 if (infoPtr->himlSmall &&
1963 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1964 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1965 Icon.right += infoPtr->iconSize.cx;
1966 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1968 if(lprcIcon) *lprcIcon = Icon;
1969 TRACE(" - icon=%s\n", debugrect(&Icon));
1972 /************************************************************/
1973 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1974 /************************************************************/
1975 if (doLabel)
1977 SIZE labelSize = { 0, 0 };
1979 /* calculate how far to the right can the label strech */
1980 Label.right = Box.right;
1981 if (uView == LVS_REPORT)
1983 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1986 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1988 labelSize.cx = infoPtr->nItemWidth;
1989 labelSize.cy = infoPtr->nItemHeight;
1990 goto calc_label;
1993 /* we need the text in non owner draw mode */
1994 assert(lpLVItem->mask & LVIF_TEXT);
1995 if (is_textT(lpLVItem->pszText, TRUE))
1997 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1998 HDC hdc = GetDC(infoPtr->hwndSelf);
1999 HFONT hOldFont = SelectObject(hdc, hFont);
2000 UINT uFormat;
2001 RECT rcText;
2003 /* compute rough rectangle where the label will go */
2004 SetRectEmpty(&rcText);
2005 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2006 rcText.bottom = infoPtr->nItemHeight;
2007 if (uView == LVS_ICON)
2008 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2010 /* now figure out the flags */
2011 if (uView == LVS_ICON)
2012 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2013 else
2014 uFormat = LV_SL_DT_FLAGS;
2016 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2018 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2019 labelSize.cy = rcText.bottom - rcText.top;
2021 SelectObject(hdc, hOldFont);
2022 ReleaseDC(infoPtr->hwndSelf, hdc);
2025 calc_label:
2026 if (uView == LVS_ICON)
2028 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2029 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2030 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2031 Label.right = Label.left + labelSize.cx;
2032 Label.bottom = Label.top + infoPtr->nItemHeight;
2033 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2035 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2036 labelSize.cy /= infoPtr->ntmHeight;
2037 labelSize.cy = max(labelSize.cy, 1);
2038 labelSize.cy *= infoPtr->ntmHeight;
2040 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2042 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2044 Label.left = Icon.right;
2045 Label.top = Box.top;
2046 Label.right = min(Label.left + labelSize.cx, Label.right);
2047 Label.bottom = Label.top + infoPtr->nItemHeight;
2050 if (lprcLabel) *lprcLabel = Label;
2051 TRACE(" - label=%s\n", debugrect(&Label));
2054 /* Fix the Box if necessary */
2055 if (lprcBox)
2057 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2058 else *lprcBox = Box;
2060 TRACE(" - box=%s\n", debugrect(&Box));
2063 /***
2064 * DESCRIPTION: [INTERNAL]
2066 * PARAMETER(S):
2067 * [I] infoPtr : valid pointer to the listview structure
2068 * [I] nItem : item number
2069 * [O] lprcBox : ptr to Box rectangle
2071 * RETURN:
2072 * None.
2074 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2076 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2077 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2078 POINT Position, Origin;
2079 LVITEMW lvItem;
2081 LISTVIEW_GetOrigin(infoPtr, &Origin);
2082 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2084 /* Be smart and try to figure out the minimum we have to do */
2085 lvItem.mask = 0;
2086 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2087 lvItem.mask |= LVIF_TEXT;
2088 lvItem.iItem = nItem;
2089 lvItem.iSubItem = 0;
2090 lvItem.pszText = szDispText;
2091 lvItem.cchTextMax = DISP_TEXT_SIZE;
2092 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2093 if (uView == LVS_ICON)
2095 lvItem.mask |= LVIF_STATE;
2096 lvItem.stateMask = LVIS_FOCUSED;
2097 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2099 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2101 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2105 /***
2106 * DESCRIPTION:
2107 * Returns the current icon position, and advances it along the top.
2108 * The returned position is not offset by Origin.
2110 * PARAMETER(S):
2111 * [I] infoPtr : valid pointer to the listview structure
2112 * [O] lpPos : will get the current icon position
2114 * RETURN:
2115 * None
2117 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2119 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2121 *lpPos = infoPtr->currIconPos;
2123 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2124 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2126 infoPtr->currIconPos.x = 0;
2127 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2131 /***
2132 * DESCRIPTION:
2133 * Returns the current icon position, and advances it down the left edge.
2134 * The returned position is not offset by Origin.
2136 * PARAMETER(S):
2137 * [I] infoPtr : valid pointer to the listview structure
2138 * [O] lpPos : will get the current icon position
2140 * RETURN:
2141 * None
2143 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2145 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2147 *lpPos = infoPtr->currIconPos;
2149 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2150 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2152 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2153 infoPtr->currIconPos.y = 0;
2157 /***
2158 * DESCRIPTION:
2159 * Moves an icon to the specified position.
2160 * It takes care of invalidating the item, etc.
2162 * PARAMETER(S):
2163 * [I] infoPtr : valid pointer to the listview structure
2164 * [I] nItem : the item to move
2165 * [I] lpPos : the new icon position
2166 * [I] isNew : flags the item as being new
2168 * RETURN:
2169 * Success: TRUE
2170 * Failure: FALSE
2172 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2174 POINT old;
2176 if (!isNew)
2178 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2179 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2181 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2182 LISTVIEW_InvalidateItem(infoPtr, nItem);
2185 /* Allocating a POINTER for every item is too resource intensive,
2186 * so we'll keep the (x,y) in different arrays */
2187 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2188 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2190 LISTVIEW_InvalidateItem(infoPtr, nItem);
2192 return TRUE;
2195 /***
2196 * DESCRIPTION:
2197 * Arranges listview items in icon display mode.
2199 * PARAMETER(S):
2200 * [I] infoPtr : valid pointer to the listview structure
2201 * [I] nAlignCode : alignment code
2203 * RETURN:
2204 * SUCCESS : TRUE
2205 * FAILURE : FALSE
2207 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2209 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2210 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2211 POINT pos;
2212 INT i;
2214 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2216 TRACE("nAlignCode=%d\n", nAlignCode);
2218 if (nAlignCode == LVA_DEFAULT)
2220 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2221 else nAlignCode = LVA_ALIGNTOP;
2224 switch (nAlignCode)
2226 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2227 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2228 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2229 default: return FALSE;
2232 infoPtr->bAutoarrange = TRUE;
2233 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2234 for (i = 0; i < infoPtr->nItemCount; i++)
2236 next_pos(infoPtr, &pos);
2237 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2240 return TRUE;
2243 /***
2244 * DESCRIPTION:
2245 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2247 * PARAMETER(S):
2248 * [I] infoPtr : valid pointer to the listview structure
2249 * [O] lprcView : bounding rectangle
2251 * RETURN:
2252 * SUCCESS : TRUE
2253 * FAILURE : FALSE
2255 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2257 INT i, x, y;
2259 SetRectEmpty(lprcView);
2261 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2263 case LVS_ICON:
2264 case LVS_SMALLICON:
2265 for (i = 0; i < infoPtr->nItemCount; i++)
2267 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2268 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2269 lprcView->right = max(lprcView->right, x);
2270 lprcView->bottom = max(lprcView->bottom, y);
2272 if (infoPtr->nItemCount > 0)
2274 lprcView->right += infoPtr->nItemWidth;
2275 lprcView->bottom += infoPtr->nItemHeight;
2277 break;
2279 case LVS_LIST:
2280 y = LISTVIEW_GetCountPerColumn(infoPtr);
2281 x = infoPtr->nItemCount / y;
2282 if (infoPtr->nItemCount % y) x++;
2283 lprcView->right = x * infoPtr->nItemWidth;
2284 lprcView->bottom = y * infoPtr->nItemHeight;
2285 break;
2287 case LVS_REPORT:
2288 lprcView->right = infoPtr->nItemWidth;
2289 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2290 break;
2294 /***
2295 * DESCRIPTION:
2296 * Retrieves the bounding rectangle of all the items.
2298 * PARAMETER(S):
2299 * [I] infoPtr : valid pointer to the listview structure
2300 * [O] lprcView : bounding rectangle
2302 * RETURN:
2303 * SUCCESS : TRUE
2304 * FAILURE : FALSE
2306 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2308 POINT ptOrigin;
2310 TRACE("(lprcView=%p)\n", lprcView);
2312 if (!lprcView) return FALSE;
2314 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2315 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2316 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2318 TRACE("lprcView=%s\n", debugrect(lprcView));
2320 return TRUE;
2323 /***
2324 * DESCRIPTION:
2325 * Retrieves the subitem pointer associated with the subitem index.
2327 * PARAMETER(S):
2328 * [I] hdpaSubItems : DPA handle for a specific item
2329 * [I] nSubItem : index of subitem
2331 * RETURN:
2332 * SUCCESS : subitem pointer
2333 * FAILURE : NULL
2335 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2337 SUBITEM_INFO *lpSubItem;
2338 INT i;
2340 /* we should binary search here if need be */
2341 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2343 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2344 if (lpSubItem->iSubItem == nSubItem)
2345 return lpSubItem;
2348 return NULL;
2352 /***
2353 * DESCRIPTION:
2354 * Caclulates the desired item width.
2356 * PARAMETER(S):
2357 * [I] infoPtr : valid pointer to the listview structure
2359 * RETURN:
2360 * The desired item width.
2362 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2364 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2365 INT nItemWidth = 0;
2367 TRACE("uView=%d\n", uView);
2369 if (uView == LVS_ICON)
2370 nItemWidth = infoPtr->iconSpacing.cx;
2371 else if (uView == LVS_REPORT)
2373 RECT rcHeader;
2375 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2377 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2378 nItemWidth = rcHeader.right;
2381 else /* LVS_SMALLICON, or LVS_LIST */
2383 INT i;
2385 for (i = 0; i < infoPtr->nItemCount; i++)
2386 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2388 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2389 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2391 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2394 return max(nItemWidth, 1);
2397 /***
2398 * DESCRIPTION:
2399 * Caclulates the desired item height.
2401 * PARAMETER(S):
2402 * [I] infoPtr : valid pointer to the listview structure
2404 * RETURN:
2405 * The desired item height.
2407 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2409 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2410 INT nItemHeight;
2412 TRACE("uView=%d\n", uView);
2414 if (uView == LVS_ICON)
2415 nItemHeight = infoPtr->iconSpacing.cy;
2416 else
2418 nItemHeight = infoPtr->ntmHeight;
2419 if (infoPtr->himlState)
2420 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2421 if (infoPtr->himlSmall)
2422 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2423 if (infoPtr->himlState || infoPtr->himlSmall)
2424 nItemHeight += HEIGHT_PADDING;
2427 return max(nItemHeight, 1);
2430 /***
2431 * DESCRIPTION:
2432 * Updates the width, and height of an item.
2434 * PARAMETER(S):
2435 * [I] infoPtr : valid pointer to the listview structure
2437 * RETURN:
2438 * None.
2440 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2442 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2443 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2447 /***
2448 * DESCRIPTION:
2449 * Retrieves and saves important text metrics info for the current
2450 * Listview font.
2452 * PARAMETER(S):
2453 * [I] infoPtr : valid pointer to the listview structure
2456 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2458 HDC hdc = GetDC(infoPtr->hwndSelf);
2459 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2460 HFONT hOldFont = SelectObject(hdc, hFont);
2461 TEXTMETRICW tm;
2463 if (GetTextMetricsW(hdc, &tm))
2465 infoPtr->ntmHeight = tm.tmHeight;
2466 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2468 SelectObject(hdc, hOldFont);
2469 ReleaseDC(infoPtr->hwndSelf, hdc);
2471 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2474 /***
2475 * DESCRIPTION:
2476 * A compare function for ranges
2478 * PARAMETER(S)
2479 * [I] range1 : pointer to range 1;
2480 * [I] range2 : pointer to range 2;
2481 * [I] flags : flags
2483 * RETURNS:
2484 * > 0 : if range 1 > range 2
2485 * < 0 : if range 2 > range 1
2486 * = 0 : if range intersects range 2
2488 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2490 INT cmp;
2492 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2493 cmp = -1;
2494 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2495 cmp = 1;
2496 else
2497 cmp = 0;
2499 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2501 return cmp;
2504 #if DEBUG_RANGES
2505 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2506 #else
2507 #define ranges_check(ranges, desc) do { } while(0)
2508 #endif
2510 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2512 INT i;
2513 RANGE *prev, *curr;
2515 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2516 assert (ranges);
2517 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2518 ranges_dump(ranges);
2519 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2520 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2521 assert (prev->lower >= 0 && prev->lower < prev->upper);
2522 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2524 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2525 assert (prev->upper <= curr->lower);
2526 assert (curr->lower < curr->upper);
2527 prev = curr;
2529 TRACE("--- Done checking---\n");
2532 static RANGES ranges_create(int count)
2534 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2535 if (!ranges) return NULL;
2536 ranges->hdpa = DPA_Create(count);
2537 if (ranges->hdpa) return ranges;
2538 Free(ranges);
2539 return NULL;
2542 static void ranges_clear(RANGES ranges)
2544 INT i;
2546 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2547 Free(DPA_GetPtr(ranges->hdpa, i));
2548 DPA_DeleteAllPtrs(ranges->hdpa);
2552 static void ranges_destroy(RANGES ranges)
2554 if (!ranges) return;
2555 ranges_clear(ranges);
2556 DPA_Destroy(ranges->hdpa);
2557 Free(ranges);
2560 static RANGES ranges_clone(RANGES ranges)
2562 RANGES clone;
2563 INT i;
2565 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2567 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2569 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2570 if (!newrng) goto fail;
2571 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2572 DPA_SetPtr(clone->hdpa, i, newrng);
2574 return clone;
2576 fail:
2577 TRACE ("clone failed\n");
2578 ranges_destroy(clone);
2579 return NULL;
2582 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2584 INT i;
2586 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2587 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2589 return ranges;
2592 static void ranges_dump(RANGES ranges)
2594 INT i;
2596 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2597 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2600 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2602 RANGE srchrng = { nItem, nItem + 1 };
2604 TRACE("(nItem=%d)\n", nItem);
2605 ranges_check(ranges, "before contain");
2606 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2609 static INT ranges_itemcount(RANGES ranges)
2611 INT i, count = 0;
2613 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2615 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2616 count += sel->upper - sel->lower;
2619 return count;
2622 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2624 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2625 INT index;
2627 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2628 if (index == -1) return TRUE;
2630 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2632 chkrng = DPA_GetPtr(ranges->hdpa, index);
2633 if (chkrng->lower >= nItem)
2634 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2635 if (chkrng->upper > nItem)
2636 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2638 return TRUE;
2641 static BOOL ranges_add(RANGES ranges, RANGE range)
2643 RANGE srchrgn;
2644 INT index;
2646 TRACE("(%s)\n", debugrange(&range));
2647 ranges_check(ranges, "before add");
2649 /* try find overlapping regions first */
2650 srchrgn.lower = range.lower - 1;
2651 srchrgn.upper = range.upper + 1;
2652 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2654 if (index == -1)
2656 RANGE *newrgn;
2658 TRACE("Adding new range\n");
2660 /* create the brand new range to insert */
2661 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2662 if(!newrgn) goto fail;
2663 *newrgn = range;
2665 /* figure out where to insert it */
2666 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2667 TRACE("index=%d\n", index);
2668 if (index == -1) index = 0;
2670 /* and get it over with */
2671 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2673 Free(newrgn);
2674 goto fail;
2677 else
2679 RANGE *chkrgn, *mrgrgn;
2680 INT fromindex, mergeindex;
2682 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2683 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2685 chkrgn->lower = min(range.lower, chkrgn->lower);
2686 chkrgn->upper = max(range.upper, chkrgn->upper);
2688 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2690 /* merge now common anges */
2691 fromindex = 0;
2692 srchrgn.lower = chkrgn->lower - 1;
2693 srchrgn.upper = chkrgn->upper + 1;
2697 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2698 if (mergeindex == -1) break;
2699 if (mergeindex == index)
2701 fromindex = index + 1;
2702 continue;
2705 TRACE("Merge with index %i\n", mergeindex);
2707 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2708 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2709 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2710 Free(mrgrgn);
2711 DPA_DeletePtr(ranges->hdpa, mergeindex);
2712 if (mergeindex < index) index --;
2713 } while(1);
2716 ranges_check(ranges, "after add");
2717 return TRUE;
2719 fail:
2720 ranges_check(ranges, "failed add");
2721 return FALSE;
2724 static BOOL ranges_del(RANGES ranges, RANGE range)
2726 RANGE *chkrgn;
2727 INT index;
2729 TRACE("(%s)\n", debugrange(&range));
2730 ranges_check(ranges, "before del");
2732 /* we don't use DPAS_SORTED here, since we need *
2733 * to find the first overlapping range */
2734 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2735 while(index != -1)
2737 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2739 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2741 /* case 1: Same range */
2742 if ( (chkrgn->upper == range.upper) &&
2743 (chkrgn->lower == range.lower) )
2745 DPA_DeletePtr(ranges->hdpa, index);
2746 break;
2748 /* case 2: engulf */
2749 else if ( (chkrgn->upper <= range.upper) &&
2750 (chkrgn->lower >= range.lower) )
2752 DPA_DeletePtr(ranges->hdpa, index);
2754 /* case 3: overlap upper */
2755 else if ( (chkrgn->upper <= range.upper) &&
2756 (chkrgn->lower < range.lower) )
2758 chkrgn->upper = range.lower;
2760 /* case 4: overlap lower */
2761 else if ( (chkrgn->upper > range.upper) &&
2762 (chkrgn->lower >= range.lower) )
2764 chkrgn->lower = range.upper;
2765 break;
2767 /* case 5: fully internal */
2768 else
2770 RANGE tmprgn = *chkrgn, *newrgn;
2772 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2773 newrgn->lower = chkrgn->lower;
2774 newrgn->upper = range.lower;
2775 chkrgn->lower = range.upper;
2776 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2778 Free(newrgn);
2779 goto fail;
2781 chkrgn = &tmprgn;
2782 break;
2785 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2788 ranges_check(ranges, "after del");
2789 return TRUE;
2791 fail:
2792 ranges_check(ranges, "failed del");
2793 return FALSE;
2796 /***
2797 * DESCRIPTION:
2798 * Removes all selection ranges
2800 * Parameters(s):
2801 * [I] infoPtr : valid pointer to the listview structure
2802 * [I] toSkip : item range to skip removing the selection
2804 * RETURNS:
2805 * SUCCESS : TRUE
2806 * FAILURE : TRUE
2808 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2810 LVITEMW lvItem;
2811 ITERATOR i;
2812 RANGES clone;
2814 TRACE("()\n");
2816 lvItem.state = 0;
2817 lvItem.stateMask = LVIS_SELECTED;
2819 /* need to clone the DPA because callbacks can change it */
2820 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2821 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2822 while(iterator_next(&i))
2823 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2824 /* note that the iterator destructor will free the cloned range */
2825 iterator_destroy(&i);
2827 return TRUE;
2830 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2832 RANGES toSkip;
2834 if (!(toSkip = ranges_create(1))) return FALSE;
2835 if (nItem != -1) ranges_additem(toSkip, nItem);
2836 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2837 ranges_destroy(toSkip);
2838 return TRUE;
2841 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2843 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2846 /***
2847 * DESCRIPTION:
2848 * Retrieves the number of items that are marked as selected.
2850 * PARAMETER(S):
2851 * [I] infoPtr : valid pointer to the listview structure
2853 * RETURN:
2854 * Number of items selected.
2856 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2858 INT nSelectedCount = 0;
2860 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2862 INT i;
2863 for (i = 0; i < infoPtr->nItemCount; i++)
2865 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2866 nSelectedCount++;
2869 else
2870 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2872 TRACE("nSelectedCount=%d\n", nSelectedCount);
2873 return nSelectedCount;
2876 /***
2877 * DESCRIPTION:
2878 * Manages the item focus.
2880 * PARAMETER(S):
2881 * [I] infoPtr : valid pointer to the listview structure
2882 * [I] nItem : item index
2884 * RETURN:
2885 * TRUE : focused item changed
2886 * FALSE : focused item has NOT changed
2888 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2890 INT oldFocus = infoPtr->nFocusedItem;
2891 LVITEMW lvItem;
2893 if (nItem == infoPtr->nFocusedItem) return FALSE;
2895 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2896 lvItem.stateMask = LVIS_FOCUSED;
2897 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2899 return oldFocus != infoPtr->nFocusedItem;
2902 /* Helper function for LISTVIEW_ShiftIndices *only* */
2903 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2905 if (nShiftItem < nItem) return nShiftItem;
2907 if (nShiftItem > nItem) return nShiftItem + direction;
2909 if (direction > 0) return nShiftItem + direction;
2911 return min(nShiftItem, infoPtr->nItemCount - 1);
2915 * DESCRIPTION:
2916 * Updates the various indices after an item has been inserted or deleted.
2918 * PARAMETER(S):
2919 * [I] infoPtr : valid pointer to the listview structure
2920 * [I] nItem : item index
2921 * [I] direction : Direction of shift, +1 or -1.
2923 * RETURN:
2924 * None
2926 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2928 INT nNewFocus;
2929 BOOL bOldChange;
2931 /* temporarily disable change notification while shifting items */
2932 bOldChange = infoPtr->bDoChangeNotify;
2933 infoPtr->bDoChangeNotify = FALSE;
2935 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2937 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2939 assert(abs(direction) == 1);
2941 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2943 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2944 if (nNewFocus != infoPtr->nFocusedItem)
2945 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2947 /* But we are not supposed to modify nHotItem! */
2949 infoPtr->bDoChangeNotify = bOldChange;
2954 * DESCRIPTION:
2955 * Adds a block of selections.
2957 * PARAMETER(S):
2958 * [I] infoPtr : valid pointer to the listview structure
2959 * [I] nItem : item index
2961 * RETURN:
2962 * None
2964 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2966 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2967 INT nLast = max(infoPtr->nSelectionMark, nItem);
2968 NMLVODSTATECHANGE nmlv;
2969 LVITEMW item;
2970 BOOL bOldChange;
2971 INT i;
2973 /* Temporarily disable change notification
2974 * If the control is LVS_OWNERDATA, we need to send
2975 * only one LVN_ODSTATECHANGED notification.
2976 * See MSDN documentation for LVN_ITEMCHANGED.
2978 bOldChange = infoPtr->bDoChangeNotify;
2979 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
2981 if (nFirst == -1) nFirst = nItem;
2983 item.state = LVIS_SELECTED;
2984 item.stateMask = LVIS_SELECTED;
2986 for (i = nFirst; i <= nLast; i++)
2987 LISTVIEW_SetItemState(infoPtr,i,&item);
2989 ZeroMemory(&nmlv, sizeof(nmlv));
2990 nmlv.iFrom = nFirst;
2991 nmlv.iTo = nLast;
2992 nmlv.uNewState = 0;
2993 nmlv.uOldState = item.state;
2995 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
2996 infoPtr->bDoChangeNotify = bOldChange;
3000 /***
3001 * DESCRIPTION:
3002 * Sets a single group selection.
3004 * PARAMETER(S):
3005 * [I] infoPtr : valid pointer to the listview structure
3006 * [I] nItem : item index
3008 * RETURN:
3009 * None
3011 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3013 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3014 RANGES selection;
3015 LVITEMW item;
3016 ITERATOR i;
3018 if (!(selection = ranges_create(100))) return;
3020 item.state = LVIS_SELECTED;
3021 item.stateMask = LVIS_SELECTED;
3023 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3025 if (infoPtr->nSelectionMark == -1)
3027 infoPtr->nSelectionMark = nItem;
3028 ranges_additem(selection, nItem);
3030 else
3032 RANGE sel;
3034 sel.lower = min(infoPtr->nSelectionMark, nItem);
3035 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3036 ranges_add(selection, sel);
3039 else
3041 RECT rcItem, rcSel, rcSelMark;
3042 POINT ptItem;
3044 rcItem.left = LVIR_BOUNDS;
3045 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3046 rcSelMark.left = LVIR_BOUNDS;
3047 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3048 UnionRect(&rcSel, &rcItem, &rcSelMark);
3049 iterator_frameditems(&i, infoPtr, &rcSel);
3050 while(iterator_next(&i))
3052 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3053 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3055 iterator_destroy(&i);
3058 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3059 iterator_rangesitems(&i, selection);
3060 while(iterator_next(&i))
3061 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3062 /* this will also destroy the selection */
3063 iterator_destroy(&i);
3065 LISTVIEW_SetItemFocus(infoPtr, nItem);
3068 /***
3069 * DESCRIPTION:
3070 * Sets a single selection.
3072 * PARAMETER(S):
3073 * [I] infoPtr : valid pointer to the listview structure
3074 * [I] nItem : item index
3076 * RETURN:
3077 * None
3079 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3081 LVITEMW lvItem;
3083 TRACE("nItem=%d\n", nItem);
3085 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3087 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3088 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3089 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3091 infoPtr->nSelectionMark = nItem;
3094 /***
3095 * DESCRIPTION:
3096 * Set selection(s) with keyboard.
3098 * PARAMETER(S):
3099 * [I] infoPtr : valid pointer to the listview structure
3100 * [I] nItem : item index
3102 * RETURN:
3103 * SUCCESS : TRUE (needs to be repainted)
3104 * FAILURE : FALSE (nothing has changed)
3106 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3108 /* FIXME: pass in the state */
3109 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3110 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3111 BOOL bResult = FALSE;
3113 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3115 if (infoPtr->dwStyle & LVS_SINGLESEL)
3117 bResult = TRUE;
3118 LISTVIEW_SetSelection(infoPtr, nItem);
3120 else
3122 if (wShift)
3124 bResult = TRUE;
3125 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3127 else if (wCtrl)
3129 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3131 else
3133 bResult = TRUE;
3134 LISTVIEW_SetSelection(infoPtr, nItem);
3137 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3140 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3141 return bResult;
3145 /***
3146 * DESCRIPTION:
3147 * Called when the mouse is being actively tracked and has hovered for a specified
3148 * amount of time
3150 * PARAMETER(S):
3151 * [I] infoPtr : valid pointer to the listview structure
3152 * [I] fwKeys : key indicator
3153 * [I] x,y : mouse position
3155 * RETURN:
3156 * 0 if the message was processed, non-zero if there was an error
3158 * INFO:
3159 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3160 * over the item for a certain period of time.
3163 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3165 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3166 /* FIXME: select the item!!! */
3167 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3169 return 0;
3172 /***
3173 * DESCRIPTION:
3174 * Called whenever WM_MOUSEMOVE is received.
3176 * PARAMETER(S):
3177 * [I] infoPtr : valid pointer to the listview structure
3178 * [I] fwKeys : key indicator
3179 * [I] x,y : mouse position
3181 * RETURN:
3182 * 0 if the message is processed, non-zero if there was an error
3184 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3186 TRACKMOUSEEVENT trackinfo;
3188 /* see if we are supposed to be tracking mouse hovering */
3189 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3190 /* fill in the trackinfo struct */
3191 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3192 trackinfo.dwFlags = TME_QUERY;
3193 trackinfo.hwndTrack = infoPtr->hwndSelf;
3194 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3196 /* see if we are already tracking this hwnd */
3197 _TrackMouseEvent(&trackinfo);
3199 if(!(trackinfo.dwFlags & TME_HOVER)) {
3200 trackinfo.dwFlags = TME_HOVER;
3202 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3203 _TrackMouseEvent(&trackinfo);
3207 return 0;
3211 /***
3212 * Tests wheather the item is assignable to a list with style lStyle
3214 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3216 if ( (lpLVItem->mask & LVIF_TEXT) &&
3217 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3218 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3220 return TRUE;
3224 /***
3225 * DESCRIPTION:
3226 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3228 * PARAMETER(S):
3229 * [I] infoPtr : valid pointer to the listview structure
3230 * [I] lpLVItem : valid pointer to new item atttributes
3231 * [I] isNew : the item being set is being inserted
3232 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3233 * [O] bChanged : will be set to TRUE if the item really changed
3235 * RETURN:
3236 * SUCCESS : TRUE
3237 * FAILURE : FALSE
3239 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3241 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3242 ITEM_INFO *lpItem;
3243 NMLISTVIEW nmlv;
3244 UINT uChanged = 0;
3245 LVITEMW item;
3247 TRACE("()\n");
3249 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3251 if (lpLVItem->mask == 0) return TRUE;
3253 if (infoPtr->dwStyle & LVS_OWNERDATA)
3255 /* a virtual listview we stores only selection and focus */
3256 if (lpLVItem->mask & ~LVIF_STATE)
3257 return FALSE;
3258 lpItem = NULL;
3260 else
3262 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3263 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3264 assert (lpItem);
3267 /* we need to get the lParam and state of the item */
3268 item.iItem = lpLVItem->iItem;
3269 item.iSubItem = lpLVItem->iSubItem;
3270 item.mask = LVIF_STATE | LVIF_PARAM;
3271 item.stateMask = ~0;
3272 item.state = 0;
3273 item.lParam = 0;
3274 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3276 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3277 /* determine what fields will change */
3278 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3279 uChanged |= LVIF_STATE;
3281 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3282 uChanged |= LVIF_IMAGE;
3284 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3285 uChanged |= LVIF_PARAM;
3287 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3288 uChanged |= LVIF_INDENT;
3290 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3291 uChanged |= LVIF_TEXT;
3293 TRACE("uChanged=0x%x\n", uChanged);
3294 if (!uChanged) return TRUE;
3295 *bChanged = TRUE;
3297 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3298 nmlv.iItem = lpLVItem->iItem;
3299 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3300 nmlv.uOldState = item.state;
3301 nmlv.uChanged = uChanged;
3302 nmlv.lParam = item.lParam;
3304 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3305 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3306 /* are enabled */
3307 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3308 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3309 return FALSE;
3311 /* copy information */
3312 if (lpLVItem->mask & LVIF_TEXT)
3313 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3315 if (lpLVItem->mask & LVIF_IMAGE)
3316 lpItem->hdr.iImage = lpLVItem->iImage;
3318 if (lpLVItem->mask & LVIF_PARAM)
3319 lpItem->lParam = lpLVItem->lParam;
3321 if (lpLVItem->mask & LVIF_INDENT)
3322 lpItem->iIndent = lpLVItem->iIndent;
3324 if (uChanged & LVIF_STATE)
3326 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3328 lpItem->state &= ~lpLVItem->stateMask;
3329 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3331 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3333 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3334 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3336 else if (lpLVItem->stateMask & LVIS_SELECTED)
3337 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3339 /* if we are asked to change focus, and we manage it, do it */
3340 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3342 if (lpLVItem->state & LVIS_FOCUSED)
3344 LISTVIEW_SetItemFocus(infoPtr, -1);
3345 infoPtr->nFocusedItem = lpLVItem->iItem;
3346 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3348 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3349 infoPtr->nFocusedItem = -1;
3353 /* if we're inserting the item, we're done */
3354 if (isNew) return TRUE;
3356 /* send LVN_ITEMCHANGED notification */
3357 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3358 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3360 return TRUE;
3363 /***
3364 * DESCRIPTION:
3365 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3367 * PARAMETER(S):
3368 * [I] infoPtr : valid pointer to the listview structure
3369 * [I] lpLVItem : valid pointer to new subitem atttributes
3370 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3371 * [O] bChanged : will be set to TRUE if the item really changed
3373 * RETURN:
3374 * SUCCESS : TRUE
3375 * FAILURE : FALSE
3377 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3379 HDPA hdpaSubItems;
3380 SUBITEM_INFO *lpSubItem;
3382 /* we do not support subitems for virtual listviews */
3383 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3385 /* set subitem only if column is present */
3386 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3388 /* First do some sanity checks */
3389 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3390 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3392 /* get the subitem structure, and create it if not there */
3393 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3394 assert (hdpaSubItems);
3396 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3397 if (!lpSubItem)
3399 SUBITEM_INFO *tmpSubItem;
3400 INT i;
3402 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3403 if (!lpSubItem) return FALSE;
3404 /* we could binary search here, if need be...*/
3405 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3407 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3408 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3410 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3412 Free(lpSubItem);
3413 return FALSE;
3415 lpSubItem->iSubItem = lpLVItem->iSubItem;
3416 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3417 *bChanged = TRUE;
3420 if (lpLVItem->mask & LVIF_IMAGE)
3421 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3423 lpSubItem->hdr.iImage = lpLVItem->iImage;
3424 *bChanged = TRUE;
3427 if (lpLVItem->mask & LVIF_TEXT)
3428 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3430 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3431 *bChanged = TRUE;
3434 return TRUE;
3437 /***
3438 * DESCRIPTION:
3439 * Sets item attributes.
3441 * PARAMETER(S):
3442 * [I] infoPtr : valid pointer to the listview structure
3443 * [I] lpLVItem : new item atttributes
3444 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3446 * RETURN:
3447 * SUCCESS : TRUE
3448 * FAILURE : FALSE
3450 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3452 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3453 LPWSTR pszText = NULL;
3454 BOOL bResult, bChanged = FALSE;
3456 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3458 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3459 return FALSE;
3461 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3462 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3464 pszText = lpLVItem->pszText;
3465 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3468 /* actually set the fields */
3469 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3471 if (lpLVItem->iSubItem)
3472 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3473 else
3474 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3476 /* redraw item, if necessary */
3477 if (bChanged && !infoPtr->bIsDrawing)
3479 /* this little optimization eliminates some nasty flicker */
3480 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3481 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3482 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3483 else
3484 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3486 /* restore text */
3487 if (pszText)
3489 textfreeT(lpLVItem->pszText, isW);
3490 ((LVITEMW *)lpLVItem)->pszText = pszText;
3493 return bResult;
3496 /***
3497 * DESCRIPTION:
3498 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3500 * PARAMETER(S):
3501 * [I] infoPtr : valid pointer to the listview structure
3503 * RETURN:
3504 * item index
3506 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3508 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3509 INT nItem = 0;
3510 SCROLLINFO scrollInfo;
3512 scrollInfo.cbSize = sizeof(SCROLLINFO);
3513 scrollInfo.fMask = SIF_POS;
3515 if (uView == LVS_LIST)
3517 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3518 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3520 else if (uView == LVS_REPORT)
3522 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3523 nItem = scrollInfo.nPos;
3525 else
3527 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3528 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3531 TRACE("nItem=%d\n", nItem);
3533 return nItem;
3537 /***
3538 * DESCRIPTION:
3539 * Erases the background of the given rectangle
3541 * PARAMETER(S):
3542 * [I] infoPtr : valid pointer to the listview structure
3543 * [I] hdc : device context handle
3544 * [I] lprcBox : clipping rectangle
3546 * RETURN:
3547 * Success: TRUE
3548 * Failure: FALSE
3550 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3552 if (!infoPtr->hBkBrush) return FALSE;
3554 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3556 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3559 /***
3560 * DESCRIPTION:
3561 * Draws an item.
3563 * PARAMETER(S):
3564 * [I] infoPtr : valid pointer to the listview structure
3565 * [I] hdc : device context handle
3566 * [I] nItem : item index
3567 * [I] nSubItem : subitem index
3568 * [I] pos : item position in client coordinates
3569 * [I] cdmode : custom draw mode
3571 * RETURN:
3572 * Success: TRUE
3573 * Failure: FALSE
3575 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3577 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3578 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3579 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3580 DWORD cdsubitemmode = CDRF_DODEFAULT;
3581 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3582 NMLVCUSTOMDRAW nmlvcd;
3583 HIMAGELIST himl;
3584 LVITEMW lvItem;
3586 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3588 /* get information needed for drawing the item */
3589 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3590 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3591 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3592 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3593 lvItem.iItem = nItem;
3594 lvItem.iSubItem = nSubItem;
3595 lvItem.state = 0;
3596 lvItem.lParam = 0;
3597 lvItem.cchTextMax = DISP_TEXT_SIZE;
3598 lvItem.pszText = szDispText;
3599 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3600 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3601 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3602 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3603 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3605 /* now check if we need to update the focus rectangle */
3606 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3608 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3609 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3610 OffsetRect(&rcBox, pos.x, pos.y);
3611 OffsetRect(&rcState, pos.x, pos.y);
3612 OffsetRect(&rcIcon, pos.x, pos.y);
3613 OffsetRect(&rcLabel, pos.x, pos.y);
3614 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3615 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3617 /* fill in the custom draw structure */
3618 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3620 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3621 if (cdmode & CDRF_NOTIFYITEMDRAW)
3622 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3623 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3624 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3625 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3626 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3628 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3629 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3631 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3632 prepaint_setup(infoPtr, hdc, &nmlvcd);
3634 /* in full row select, subitems, will just use main item's colors */
3635 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3636 nmlvcd.clrTextBk = CLR_NONE;
3638 /* state icons */
3639 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3641 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3642 if (uStateImage)
3644 TRACE("uStateImage=%d\n", uStateImage);
3645 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3649 /* small icons */
3650 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3651 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3653 TRACE("iImage=%d\n", lvItem.iImage);
3654 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3655 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3658 /* Don't bother painting item being edited */
3659 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3661 /* draw the selection background, if we're drawing the main item */
3662 if (nSubItem == 0)
3664 rcSelect = rcLabel;
3665 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3666 rcSelect.right = rcBox.right;
3668 if (nmlvcd.clrTextBk != CLR_NONE)
3669 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3670 if(lprcFocus) *lprcFocus = rcSelect;
3673 /* figure out the text drawing flags */
3674 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3675 if (uView == LVS_ICON)
3676 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3677 else if (nSubItem)
3679 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3681 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3682 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3683 default: uFormat |= DT_LEFT;
3686 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3688 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3689 else rcLabel.left += LABEL_HOR_PADDING;
3691 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3692 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3694 postpaint:
3695 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3696 notify_postpaint(infoPtr, &nmlvcd);
3697 return TRUE;
3700 /***
3701 * DESCRIPTION:
3702 * Draws listview items when in owner draw mode.
3704 * PARAMETER(S):
3705 * [I] infoPtr : valid pointer to the listview structure
3706 * [I] hdc : device context handle
3708 * RETURN:
3709 * None
3711 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3713 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3714 DWORD cditemmode = CDRF_DODEFAULT;
3715 NMLVCUSTOMDRAW nmlvcd;
3716 POINT Origin, Position;
3717 DRAWITEMSTRUCT dis;
3718 LVITEMW item;
3720 TRACE("()\n");
3722 ZeroMemory(&dis, sizeof(dis));
3724 /* Get scroll info once before loop */
3725 LISTVIEW_GetOrigin(infoPtr, &Origin);
3727 /* iterate through the invalidated rows */
3728 while(iterator_next(i))
3730 item.iItem = i->nItem;
3731 item.iSubItem = 0;
3732 item.mask = LVIF_PARAM | LVIF_STATE;
3733 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3734 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3736 dis.CtlType = ODT_LISTVIEW;
3737 dis.CtlID = uID;
3738 dis.itemID = item.iItem;
3739 dis.itemAction = ODA_DRAWENTIRE;
3740 dis.itemState = 0;
3741 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3742 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3743 dis.hwndItem = infoPtr->hwndSelf;
3744 dis.hDC = hdc;
3745 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3746 dis.rcItem.left = Position.x + Origin.x;
3747 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3748 dis.rcItem.top = Position.y + Origin.y;
3749 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3750 dis.itemData = item.lParam;
3752 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3755 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3756 * structure for the rest. of the paint cycle
3758 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3759 if (cdmode & CDRF_NOTIFYITEMDRAW)
3760 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3762 if (!(cditemmode & CDRF_SKIPDEFAULT))
3764 prepaint_setup (infoPtr, hdc, &nmlvcd);
3765 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3768 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3769 notify_postpaint(infoPtr, &nmlvcd);
3773 /***
3774 * DESCRIPTION:
3775 * Draws listview items when in report display mode.
3777 * PARAMETER(S):
3778 * [I] infoPtr : valid pointer to the listview structure
3779 * [I] hdc : device context handle
3780 * [I] cdmode : custom draw mode
3782 * RETURN:
3783 * None
3785 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3787 INT rgntype;
3788 RECT rcClip, rcItem;
3789 POINT Origin, Position;
3790 RANGE colRange;
3791 ITERATOR j;
3793 TRACE("()\n");
3795 /* figure out what to draw */
3796 rgntype = GetClipBox(hdc, &rcClip);
3797 if (rgntype == NULLREGION) return;
3799 /* Get scroll info once before loop */
3800 LISTVIEW_GetOrigin(infoPtr, &Origin);
3802 /* narrow down the columns we need to paint */
3803 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3805 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3806 if (rcItem.right + Origin.x >= rcClip.left) break;
3808 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3810 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3811 if (rcItem.left + Origin.x < rcClip.right) break;
3813 iterator_rangeitems(&j, colRange);
3815 /* in full row select, we _have_ to draw the main item */
3816 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3817 j.nSpecial = 0;
3819 /* iterate through the invalidated rows */
3820 while(iterator_next(i))
3822 /* iterate through the invalidated columns */
3823 while(iterator_next(&j))
3825 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3826 Position.x += Origin.x;
3827 Position.y += Origin.y;
3829 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3831 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3832 rcItem.top = 0;
3833 rcItem.bottom = infoPtr->nItemHeight;
3834 OffsetRect(&rcItem, Position.x, Position.y);
3835 if (!RectVisible(hdc, &rcItem)) continue;
3838 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3841 iterator_destroy(&j);
3844 /***
3845 * DESCRIPTION:
3846 * Draws listview items when in list display mode.
3848 * PARAMETER(S):
3849 * [I] infoPtr : valid pointer to the listview structure
3850 * [I] hdc : device context handle
3851 * [I] cdmode : custom draw mode
3853 * RETURN:
3854 * None
3856 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3858 POINT Origin, Position;
3860 /* Get scroll info once before loop */
3861 LISTVIEW_GetOrigin(infoPtr, &Origin);
3863 while(iterator_prev(i))
3865 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3866 Position.x += Origin.x;
3867 Position.y += Origin.y;
3869 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3874 /***
3875 * DESCRIPTION:
3876 * Draws listview items.
3878 * PARAMETER(S):
3879 * [I] infoPtr : valid pointer to the listview structure
3880 * [I] hdc : device context handle
3882 * RETURN:
3883 * NoneX
3885 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3887 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3888 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3889 NMLVCUSTOMDRAW nmlvcd;
3890 HFONT hOldFont;
3891 DWORD cdmode;
3892 INT oldBkMode;
3893 RECT rcClient;
3894 ITERATOR i;
3896 LISTVIEW_DUMP(infoPtr);
3898 infoPtr->bIsDrawing = TRUE;
3900 /* save dc values we're gonna trash while drawing */
3901 hOldFont = SelectObject(hdc, infoPtr->hFont);
3902 oldBkMode = GetBkMode(hdc);
3903 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3904 oldTextColor = GetTextColor(hdc);
3906 oldClrTextBk = infoPtr->clrTextBk;
3907 oldClrText = infoPtr->clrText;
3909 infoPtr->cditemmode = CDRF_DODEFAULT;
3911 GetClientRect(infoPtr->hwndSelf, &rcClient);
3912 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3913 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3914 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3915 prepaint_setup(infoPtr, hdc, &nmlvcd);
3917 /* Use these colors to draw the items */
3918 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3919 infoPtr->clrText = nmlvcd.clrText;
3921 /* nothing to draw */
3922 if(infoPtr->nItemCount == 0) goto enddraw;
3924 /* figure out what we need to draw */
3925 iterator_visibleitems(&i, infoPtr, hdc);
3927 /* send cache hint notification */
3928 if (infoPtr->dwStyle & LVS_OWNERDATA)
3930 RANGE range = iterator_range(&i);
3931 NMLVCACHEHINT nmlv;
3933 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3934 nmlv.iFrom = range.lower;
3935 nmlv.iTo = range.upper - 1;
3936 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3939 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3940 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3941 else
3943 if (uView == LVS_REPORT)
3944 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3945 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3946 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3948 /* if we have a focus rect, draw it */
3949 if (infoPtr->bFocus)
3950 DrawFocusRect(hdc, &infoPtr->rcFocus);
3952 iterator_destroy(&i);
3954 enddraw:
3955 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3956 notify_postpaint(infoPtr, &nmlvcd);
3958 infoPtr->clrTextBk = oldClrTextBk;
3959 infoPtr->clrText = oldClrText;
3961 SelectObject(hdc, hOldFont);
3962 SetBkMode(hdc, oldBkMode);
3963 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3964 SetTextColor(hdc, oldTextColor);
3965 infoPtr->bIsDrawing = FALSE;
3969 /***
3970 * DESCRIPTION:
3971 * Calculates the approximate width and height of a given number of items.
3973 * PARAMETER(S):
3974 * [I] infoPtr : valid pointer to the listview structure
3975 * [I] nItemCount : number of items
3976 * [I] wWidth : width
3977 * [I] wHeight : height
3979 * RETURN:
3980 * Returns a DWORD. The width in the low word and the height in high word.
3982 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3983 WORD wWidth, WORD wHeight)
3985 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3986 INT nItemCountPerColumn = 1;
3987 INT nColumnCount = 0;
3988 DWORD dwViewRect = 0;
3990 if (nItemCount == -1)
3991 nItemCount = infoPtr->nItemCount;
3993 if (uView == LVS_LIST)
3995 if (wHeight == 0xFFFF)
3997 /* use current height */
3998 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4001 if (wHeight < infoPtr->nItemHeight)
4002 wHeight = infoPtr->nItemHeight;
4004 if (nItemCount > 0)
4006 if (infoPtr->nItemHeight > 0)
4008 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4009 if (nItemCountPerColumn == 0)
4010 nItemCountPerColumn = 1;
4012 if (nItemCount % nItemCountPerColumn != 0)
4013 nColumnCount = nItemCount / nItemCountPerColumn;
4014 else
4015 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4019 /* Microsoft padding magic */
4020 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4021 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4023 dwViewRect = MAKELONG(wWidth, wHeight);
4025 else if (uView == LVS_REPORT)
4027 RECT rcBox;
4029 if (infoPtr->nItemCount > 0)
4031 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4032 wWidth = rcBox.right - rcBox.left;
4033 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4035 else
4037 /* use current height and width */
4038 if (wHeight == 0xffff)
4039 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4040 if (wWidth == 0xffff)
4041 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4044 dwViewRect = MAKELONG(wWidth, wHeight);
4046 else if (uView == LVS_SMALLICON)
4047 FIXME("uView == LVS_SMALLICON: not implemented\n");
4048 else if (uView == LVS_ICON)
4049 FIXME("uView == LVS_ICON: not implemented\n");
4051 return dwViewRect;
4055 /***
4056 * DESCRIPTION:
4057 * Create a drag image list for the specified item.
4059 * PARAMETER(S):
4060 * [I] infoPtr : valid pointer to the listview structure
4061 * [I] iItem : index of item
4062 * [O] lppt : Upperr-left corner of the image
4064 * RETURN:
4065 * Returns a handle to the image list if successful, NULL otherwise.
4067 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4069 RECT rcItem;
4070 SIZE size;
4071 POINT pos;
4072 HDC hdc, hdcOrig;
4073 HBITMAP hbmp, hOldbmp;
4074 HIMAGELIST dragList = 0;
4075 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4077 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4078 return 0;
4080 rcItem.left = LVIR_BOUNDS;
4081 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4082 return 0;
4084 lppt->x = rcItem.left;
4085 lppt->y = rcItem.top;
4087 size.cx = rcItem.right - rcItem.left;
4088 size.cy = rcItem.bottom - rcItem.top;
4090 hdcOrig = GetDC(infoPtr->hwndSelf);
4091 hdc = CreateCompatibleDC(hdcOrig);
4092 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4093 hOldbmp = SelectObject(hdc, hbmp);
4095 rcItem.left = rcItem.top = 0;
4096 rcItem.right = size.cx;
4097 rcItem.bottom = size.cy;
4098 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4100 pos.x = pos.y = 0;
4101 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4103 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4104 SelectObject(hdc, hOldbmp);
4105 ImageList_Add(dragList, hbmp, 0);
4107 else
4108 SelectObject(hdc, hOldbmp);
4110 DeleteObject(hbmp);
4111 DeleteDC(hdc);
4112 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4114 TRACE("ret=%p\n", dragList);
4116 return dragList;
4120 /***
4121 * DESCRIPTION:
4122 * Removes all listview items and subitems.
4124 * PARAMETER(S):
4125 * [I] infoPtr : valid pointer to the listview structure
4127 * RETURN:
4128 * SUCCESS : TRUE
4129 * FAILURE : FALSE
4131 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4133 NMLISTVIEW nmlv;
4134 HDPA hdpaSubItems = NULL;
4135 BOOL bSuppress;
4136 ITEMHDR *hdrItem;
4137 INT i, j;
4139 TRACE("()\n");
4141 /* we do it directly, to avoid notifications */
4142 ranges_clear(infoPtr->selectionRanges);
4143 infoPtr->nSelectionMark = -1;
4144 infoPtr->nFocusedItem = -1;
4145 SetRectEmpty(&infoPtr->rcFocus);
4146 /* But we are supposed to leave nHotItem as is! */
4149 /* send LVN_DELETEALLITEMS notification */
4150 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4151 nmlv.iItem = -1;
4152 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4154 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4156 /* send LVN_DELETEITEM notification, if not suppressed */
4157 if (!bSuppress) notify_deleteitem(infoPtr, i);
4158 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4160 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4161 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4163 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4164 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4165 Free(hdrItem);
4167 DPA_Destroy(hdpaSubItems);
4168 DPA_DeletePtr(infoPtr->hdpaItems, i);
4170 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4171 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4172 infoPtr->nItemCount --;
4175 LISTVIEW_UpdateScroll(infoPtr);
4177 LISTVIEW_InvalidateList(infoPtr);
4179 return TRUE;
4182 /***
4183 * DESCRIPTION:
4184 * Scrolls, and updates the columns, when a column is changing width.
4186 * PARAMETER(S):
4187 * [I] infoPtr : valid pointer to the listview structure
4188 * [I] nColumn : column to scroll
4189 * [I] dx : amount of scroll, in pixels
4191 * RETURN:
4192 * None.
4194 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4196 COLUMN_INFO *lpColumnInfo;
4197 RECT rcOld, rcCol;
4198 INT nCol;
4200 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4201 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4202 rcCol = lpColumnInfo->rcHeader;
4203 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4204 rcCol.left = rcCol.right;
4206 /* ajust the other columns */
4207 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4209 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4210 lpColumnInfo->rcHeader.left += dx;
4211 lpColumnInfo->rcHeader.right += dx;
4214 /* do not update screen if not in report mode */
4215 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4217 /* if we have a focus, must first erase the focus rect */
4218 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4220 /* Need to reset the item width when inserting a new column */
4221 infoPtr->nItemWidth += dx;
4223 LISTVIEW_UpdateScroll(infoPtr);
4225 /* scroll to cover the deleted column, and invalidate for redraw */
4226 rcOld = infoPtr->rcList;
4227 rcOld.left = rcCol.left;
4228 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4230 /* we can restore focus now */
4231 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4234 /***
4235 * DESCRIPTION:
4236 * Removes a column from the listview control.
4238 * PARAMETER(S):
4239 * [I] infoPtr : valid pointer to the listview structure
4240 * [I] nColumn : column index
4242 * RETURN:
4243 * SUCCESS : TRUE
4244 * FAILURE : FALSE
4246 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4248 RECT rcCol;
4250 TRACE("nColumn=%d\n", nColumn);
4252 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4253 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4255 /* While the MSDN specifically says that column zero should not be deleted,
4256 what actually happens is that the column itself is deleted but no items or subitems
4257 are removed.
4260 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4262 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4263 return FALSE;
4265 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4266 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4268 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4270 SUBITEM_INFO *lpSubItem, *lpDelItem;
4271 HDPA hdpaSubItems;
4272 INT nItem, nSubItem, i;
4274 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4276 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4277 nSubItem = 0;
4278 lpDelItem = 0;
4279 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4281 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4282 if (lpSubItem->iSubItem == nColumn)
4284 nSubItem = i;
4285 lpDelItem = lpSubItem;
4287 else if (lpSubItem->iSubItem > nColumn)
4289 lpSubItem->iSubItem--;
4293 /* if we found our subitem, zapp it */
4294 if (nSubItem > 0)
4296 /* free string */
4297 if (is_textW(lpDelItem->hdr.pszText))
4298 Free(lpDelItem->hdr.pszText);
4300 /* free item */
4301 Free(lpDelItem);
4303 /* free dpa memory */
4304 DPA_DeletePtr(hdpaSubItems, nSubItem);
4309 /* update the other column info */
4310 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4311 LISTVIEW_InvalidateList(infoPtr);
4312 else
4313 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4315 return TRUE;
4318 /***
4319 * DESCRIPTION:
4320 * Invalidates the listview after an item's insertion or deletion.
4322 * PARAMETER(S):
4323 * [I] infoPtr : valid pointer to the listview structure
4324 * [I] nItem : item index
4325 * [I] dir : -1 if deleting, 1 if inserting
4327 * RETURN:
4328 * None
4330 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4332 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4333 INT nPerCol, nItemCol, nItemRow;
4334 RECT rcScroll;
4335 POINT Origin;
4337 /* if we don't refresh, what's the point of scrolling? */
4338 if (!is_redrawing(infoPtr)) return;
4340 assert (abs(dir) == 1);
4342 /* arrange icons if autoarrange is on */
4343 if (is_autoarrange(infoPtr))
4345 BOOL arrange = TRUE;
4346 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4347 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4348 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4351 /* scrollbars need updating */
4352 LISTVIEW_UpdateScroll(infoPtr);
4354 /* figure out the item's position */
4355 if (uView == LVS_REPORT)
4356 nPerCol = infoPtr->nItemCount + 1;
4357 else if (uView == LVS_LIST)
4358 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4359 else /* LVS_ICON, or LVS_SMALLICON */
4360 return;
4362 nItemCol = nItem / nPerCol;
4363 nItemRow = nItem % nPerCol;
4364 LISTVIEW_GetOrigin(infoPtr, &Origin);
4366 /* move the items below up a slot */
4367 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4368 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4369 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4370 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4371 OffsetRect(&rcScroll, Origin.x, Origin.y);
4372 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4373 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4375 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4376 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4377 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4380 /* report has only that column, so we're done */
4381 if (uView == LVS_REPORT) return;
4383 /* now for LISTs, we have to deal with the columns to the right */
4384 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4385 rcScroll.top = 0;
4386 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4387 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4388 OffsetRect(&rcScroll, Origin.x, Origin.y);
4389 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4390 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4391 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4394 /***
4395 * DESCRIPTION:
4396 * Removes an item from the listview control.
4398 * PARAMETER(S):
4399 * [I] infoPtr : valid pointer to the listview structure
4400 * [I] nItem : item index
4402 * RETURN:
4403 * SUCCESS : TRUE
4404 * FAILURE : FALSE
4406 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4408 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4409 LVITEMW item;
4411 TRACE("(nItem=%d)\n", nItem);
4413 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4415 /* remove selection, and focus */
4416 item.state = 0;
4417 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4418 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4420 /* send LVN_DELETEITEM notification. */
4421 notify_deleteitem(infoPtr, nItem);
4423 /* we need to do this here, because we'll be deleting stuff */
4424 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4425 LISTVIEW_InvalidateItem(infoPtr, nItem);
4427 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4429 HDPA hdpaSubItems;
4430 ITEMHDR *hdrItem;
4431 INT i;
4433 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4434 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4436 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4437 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4438 Free(hdrItem);
4440 DPA_Destroy(hdpaSubItems);
4443 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4445 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4446 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4449 infoPtr->nItemCount--;
4450 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4452 /* now is the invalidation fun */
4453 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4454 return TRUE;
4458 /***
4459 * DESCRIPTION:
4460 * Callback implementation for editlabel control
4462 * PARAMETER(S):
4463 * [I] infoPtr : valid pointer to the listview structure
4464 * [I] pszText : modified text
4465 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4467 * RETURN:
4468 * SUCCESS : TRUE
4469 * FAILURE : FALSE
4471 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4473 NMLVDISPINFOW dispInfo;
4475 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4477 ZeroMemory(&dispInfo, sizeof(dispInfo));
4478 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4479 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4480 dispInfo.item.iSubItem = 0;
4481 dispInfo.item.stateMask = ~0;
4482 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4483 /* add the text from the edit in */
4484 dispInfo.item.mask |= LVIF_TEXT;
4485 dispInfo.item.pszText = pszText;
4486 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4488 /* Do we need to update the Item Text */
4489 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4490 if (!pszText) return TRUE;
4492 ZeroMemory(&dispInfo, sizeof(dispInfo));
4493 dispInfo.item.mask = LVIF_TEXT;
4494 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4495 dispInfo.item.iSubItem = 0;
4496 dispInfo.item.pszText = pszText;
4497 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4498 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4501 /***
4502 * DESCRIPTION:
4503 * Begin in place editing of specified list view item
4505 * PARAMETER(S):
4506 * [I] infoPtr : valid pointer to the listview structure
4507 * [I] nItem : item index
4508 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4510 * RETURN:
4511 * SUCCESS : TRUE
4512 * FAILURE : FALSE
4514 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4516 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4517 NMLVDISPINFOW dispInfo;
4518 RECT rect;
4520 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4522 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4523 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4525 infoPtr->nEditLabelItem = nItem;
4527 /* Is the EditBox still there, if so remove it */
4528 if(infoPtr->hwndEdit != 0)
4530 SetFocus(infoPtr->hwndSelf);
4531 infoPtr->hwndEdit = 0;
4534 LISTVIEW_SetSelection(infoPtr, nItem);
4535 LISTVIEW_SetItemFocus(infoPtr, nItem);
4536 LISTVIEW_InvalidateItem(infoPtr, nItem);
4538 rect.left = LVIR_LABEL;
4539 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4541 ZeroMemory(&dispInfo, sizeof(dispInfo));
4542 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4543 dispInfo.item.iItem = nItem;
4544 dispInfo.item.iSubItem = 0;
4545 dispInfo.item.stateMask = ~0;
4546 dispInfo.item.pszText = szDispText;
4547 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4548 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4550 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4551 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4552 if (!infoPtr->hwndEdit) return 0;
4554 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4556 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4557 infoPtr->hwndEdit = 0;
4558 return 0;
4561 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4562 SetFocus(infoPtr->hwndEdit);
4563 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4564 return infoPtr->hwndEdit;
4568 /***
4569 * DESCRIPTION:
4570 * Ensures the specified item is visible, scrolling into view if necessary.
4572 * PARAMETER(S):
4573 * [I] infoPtr : valid pointer to the listview structure
4574 * [I] nItem : item index
4575 * [I] bPartial : partially or entirely visible
4577 * RETURN:
4578 * SUCCESS : TRUE
4579 * FAILURE : FALSE
4581 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4583 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4584 INT nScrollPosHeight = 0;
4585 INT nScrollPosWidth = 0;
4586 INT nHorzAdjust = 0;
4587 INT nVertAdjust = 0;
4588 INT nHorzDiff = 0;
4589 INT nVertDiff = 0;
4590 RECT rcItem, rcTemp;
4592 rcItem.left = LVIR_BOUNDS;
4593 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4595 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4597 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4599 /* scroll left/right, but in LVS_REPORT mode */
4600 if (uView == LVS_LIST)
4601 nScrollPosWidth = infoPtr->nItemWidth;
4602 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4603 nScrollPosWidth = 1;
4605 if (rcItem.left < infoPtr->rcList.left)
4607 nHorzAdjust = -1;
4608 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4610 else
4612 nHorzAdjust = 1;
4613 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4617 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4619 /* scroll up/down, but not in LVS_LIST mode */
4620 if (uView == LVS_REPORT)
4621 nScrollPosHeight = infoPtr->nItemHeight;
4622 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4623 nScrollPosHeight = 1;
4625 if (rcItem.top < infoPtr->rcList.top)
4627 nVertAdjust = -1;
4628 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4630 else
4632 nVertAdjust = 1;
4633 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4637 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4639 if (nScrollPosWidth)
4641 INT diff = nHorzDiff / nScrollPosWidth;
4642 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4643 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4646 if (nScrollPosHeight)
4648 INT diff = nVertDiff / nScrollPosHeight;
4649 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4650 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4653 return TRUE;
4656 /***
4657 * DESCRIPTION:
4658 * Searches for an item with specific characteristics.
4660 * PARAMETER(S):
4661 * [I] hwnd : window handle
4662 * [I] nStart : base item index
4663 * [I] lpFindInfo : item information to look for
4665 * RETURN:
4666 * SUCCESS : index of item
4667 * FAILURE : -1
4669 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4670 const LVFINDINFOW *lpFindInfo)
4672 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4673 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4674 BOOL bWrap = FALSE, bNearest = FALSE;
4675 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4676 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4677 POINT Position, Destination;
4678 LVITEMW lvItem;
4680 if (!lpFindInfo || nItem < 0) return -1;
4682 lvItem.mask = 0;
4683 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4685 lvItem.mask |= LVIF_TEXT;
4686 lvItem.pszText = szDispText;
4687 lvItem.cchTextMax = DISP_TEXT_SIZE;
4690 if (lpFindInfo->flags & LVFI_WRAP)
4691 bWrap = TRUE;
4693 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4694 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4696 POINT Origin;
4697 RECT rcArea;
4699 LISTVIEW_GetOrigin(infoPtr, &Origin);
4700 Destination.x = lpFindInfo->pt.x - Origin.x;
4701 Destination.y = lpFindInfo->pt.y - Origin.y;
4702 switch(lpFindInfo->vkDirection)
4704 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4705 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4706 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4707 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4708 case VK_HOME: Destination.x = Destination.y = 0; break;
4709 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4710 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4711 case VK_END:
4712 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4713 Destination.x = rcArea.right;
4714 Destination.y = rcArea.bottom;
4715 break;
4716 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4718 bNearest = TRUE;
4721 /* if LVFI_PARAM is specified, all other flags are ignored */
4722 if (lpFindInfo->flags & LVFI_PARAM)
4724 lvItem.mask |= LVIF_PARAM;
4725 bNearest = FALSE;
4726 lvItem.mask &= ~LVIF_TEXT;
4729 again:
4730 for (; nItem < nLast; nItem++)
4732 lvItem.iItem = nItem;
4733 lvItem.iSubItem = 0;
4734 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4736 if (lvItem.mask & LVIF_PARAM)
4738 if (lpFindInfo->lParam == lvItem.lParam)
4739 return nItem;
4740 else
4741 continue;
4744 if (lvItem.mask & LVIF_TEXT)
4746 if (lpFindInfo->flags & LVFI_PARTIAL)
4748 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4750 else
4752 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4756 if (!bNearest) return nItem;
4758 /* This is very inefficient. To do a good job here,
4759 * we need a sorted array of (x,y) item positions */
4760 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4762 /* compute the distance^2 to the destination */
4763 xdist = Destination.x - Position.x;
4764 ydist = Destination.y - Position.y;
4765 dist = xdist * xdist + ydist * ydist;
4767 /* remember the distance, and item if it's closer */
4768 if (dist < mindist)
4770 mindist = dist;
4771 nNearestItem = nItem;
4775 if (bWrap)
4777 nItem = 0;
4778 nLast = min(nStart + 1, infoPtr->nItemCount);
4779 bWrap = FALSE;
4780 goto again;
4783 return nNearestItem;
4786 /***
4787 * DESCRIPTION:
4788 * Searches for an item with specific characteristics.
4790 * PARAMETER(S):
4791 * [I] hwnd : window handle
4792 * [I] nStart : base item index
4793 * [I] lpFindInfo : item information to look for
4795 * RETURN:
4796 * SUCCESS : index of item
4797 * FAILURE : -1
4799 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4800 const LVFINDINFOA *lpFindInfo)
4802 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4803 LVFINDINFOW fiw;
4804 INT res;
4806 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4807 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4808 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4809 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4810 return res;
4813 /***
4814 * DESCRIPTION:
4815 * Retrieves the background image of the listview control.
4817 * PARAMETER(S):
4818 * [I] infoPtr : valid pointer to the listview structure
4819 * [O] lpBkImage : background image attributes
4821 * RETURN:
4822 * SUCCESS : TRUE
4823 * FAILURE : FALSE
4825 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4826 /* { */
4827 /* FIXME (listview, "empty stub!\n"); */
4828 /* return FALSE; */
4829 /* } */
4831 /***
4832 * DESCRIPTION:
4833 * Retrieves column attributes.
4835 * PARAMETER(S):
4836 * [I] infoPtr : valid pointer to the listview structure
4837 * [I] nColumn : column index
4838 * [IO] lpColumn : column information
4839 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4840 * otherwise it is in fact a LPLVCOLUMNA
4842 * RETURN:
4843 * SUCCESS : TRUE
4844 * FAILURE : FALSE
4846 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4848 COLUMN_INFO *lpColumnInfo;
4849 HDITEMW hdi;
4851 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4852 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4854 /* initialize memory */
4855 ZeroMemory(&hdi, sizeof(hdi));
4857 if (lpColumn->mask & LVCF_TEXT)
4859 hdi.mask |= HDI_TEXT;
4860 hdi.pszText = lpColumn->pszText;
4861 hdi.cchTextMax = lpColumn->cchTextMax;
4864 if (lpColumn->mask & LVCF_IMAGE)
4865 hdi.mask |= HDI_IMAGE;
4867 if (lpColumn->mask & LVCF_ORDER)
4868 hdi.mask |= HDI_ORDER;
4870 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4872 if (lpColumn->mask & LVCF_FMT)
4873 lpColumn->fmt = lpColumnInfo->fmt;
4875 if (lpColumn->mask & LVCF_WIDTH)
4876 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4878 if (lpColumn->mask & LVCF_IMAGE)
4879 lpColumn->iImage = hdi.iImage;
4881 if (lpColumn->mask & LVCF_ORDER)
4882 lpColumn->iOrder = hdi.iOrder;
4884 return TRUE;
4888 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4890 INT i;
4892 if (!lpiArray)
4893 return FALSE;
4895 /* FIXME: little hack */
4896 for (i = 0; i < iCount; i++)
4897 lpiArray[i] = i;
4899 return TRUE;
4902 /***
4903 * DESCRIPTION:
4904 * Retrieves the column width.
4906 * PARAMETER(S):
4907 * [I] infoPtr : valid pointer to the listview structure
4908 * [I] int : column index
4910 * RETURN:
4911 * SUCCESS : column width
4912 * FAILURE : zero
4914 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4916 INT nColumnWidth = 0;
4917 RECT rcHeader;
4919 TRACE("nColumn=%d\n", nColumn);
4921 /* we have a 'column' in LIST and REPORT mode only */
4922 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4924 case LVS_LIST:
4925 nColumnWidth = infoPtr->nItemWidth;
4926 break;
4927 case LVS_REPORT:
4928 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4929 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4930 nColumnWidth = rcHeader.right - rcHeader.left;
4931 break;
4934 TRACE("nColumnWidth=%d\n", nColumnWidth);
4935 return nColumnWidth;
4938 /***
4939 * DESCRIPTION:
4940 * In list or report display mode, retrieves the number of items that can fit
4941 * vertically in the visible area. In icon or small icon display mode,
4942 * retrieves the total number of visible items.
4944 * PARAMETER(S):
4945 * [I] infoPtr : valid pointer to the listview structure
4947 * RETURN:
4948 * Number of fully visible items.
4950 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4952 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4954 case LVS_ICON:
4955 case LVS_SMALLICON:
4956 return infoPtr->nItemCount;
4957 case LVS_REPORT:
4958 return LISTVIEW_GetCountPerColumn(infoPtr);
4959 case LVS_LIST:
4960 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4962 assert(FALSE);
4963 return 0;
4966 /***
4967 * DESCRIPTION:
4968 * Retrieves an image list handle.
4970 * PARAMETER(S):
4971 * [I] infoPtr : valid pointer to the listview structure
4972 * [I] nImageList : image list identifier
4974 * RETURN:
4975 * SUCCESS : image list handle
4976 * FAILURE : NULL
4978 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4980 switch (nImageList)
4982 case LVSIL_NORMAL: return infoPtr->himlNormal;
4983 case LVSIL_SMALL: return infoPtr->himlSmall;
4984 case LVSIL_STATE: return infoPtr->himlState;
4986 return NULL;
4989 /* LISTVIEW_GetISearchString */
4991 /***
4992 * DESCRIPTION:
4993 * Retrieves item attributes.
4995 * PARAMETER(S):
4996 * [I] hwnd : window handle
4997 * [IO] lpLVItem : item info
4998 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4999 * if FALSE, the lpLVItem is a LPLVITEMA.
5001 * NOTE:
5002 * This is the internal 'GetItem' interface -- it tries to
5003 * be smart, and avoids text copies, if possible, by modifing
5004 * lpLVItem->pszText to point to the text string. Please note
5005 * that this is not always possible (e.g. OWNERDATA), so on
5006 * entry you *must* supply valid values for pszText, and cchTextMax.
5007 * The only difference to the documented interface is that upon
5008 * return, you should use *only* the lpLVItem->pszText, rather than
5009 * the buffer pointer you provided on input. Most code already does
5010 * that, so it's not a problem.
5011 * For the two cases when the text must be copied (that is,
5012 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5014 * RETURN:
5015 * SUCCESS : TRUE
5016 * FAILURE : FALSE
5018 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5020 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5021 NMLVDISPINFOW dispInfo;
5022 ITEM_INFO *lpItem;
5023 ITEMHDR* pItemHdr;
5024 HDPA hdpaSubItems;
5025 INT isubitem;
5027 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5029 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5030 return FALSE;
5032 if (lpLVItem->mask == 0) return TRUE;
5034 /* make a local copy */
5035 isubitem = lpLVItem->iSubItem;
5037 /* a quick optimization if all we're asked is the focus state
5038 * these queries are worth optimising since they are common,
5039 * and can be answered in constant time, without the heavy accesses */
5040 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5041 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5043 lpLVItem->state = 0;
5044 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5045 lpLVItem->state |= LVIS_FOCUSED;
5046 return TRUE;
5049 ZeroMemory(&dispInfo, sizeof(dispInfo));
5051 /* if the app stores all the data, handle it separately */
5052 if (infoPtr->dwStyle & LVS_OWNERDATA)
5054 dispInfo.item.state = 0;
5056 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5057 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5059 /* NOTE: copy only fields which we _know_ are initialized, some apps
5060 * depend on the uninitialized fields being 0 */
5061 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5062 dispInfo.item.iItem = lpLVItem->iItem;
5063 dispInfo.item.iSubItem = isubitem;
5064 if (lpLVItem->mask & LVIF_TEXT)
5066 dispInfo.item.pszText = lpLVItem->pszText;
5067 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5069 if (lpLVItem->mask & LVIF_STATE)
5070 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5071 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5072 dispInfo.item.stateMask = lpLVItem->stateMask;
5073 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5075 /* full size structure expected - _WIN32IE >= 0x560 */
5076 *lpLVItem = dispInfo.item;
5078 else if (lpLVItem->mask & LVIF_INDENT)
5080 /* indent member expected - _WIN32IE >= 0x300 */
5081 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5083 else
5085 /* minimal structure expected */
5086 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5088 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5091 /* make sure lParam is zeroed out */
5092 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5094 /* we store only a little state, so if we're not asked, we're done */
5095 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5097 /* if focus is handled by us, report it */
5098 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5100 lpLVItem->state &= ~LVIS_FOCUSED;
5101 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5102 lpLVItem->state |= LVIS_FOCUSED;
5105 /* and do the same for selection, if we handle it */
5106 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5108 lpLVItem->state &= ~LVIS_SELECTED;
5109 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5110 lpLVItem->state |= LVIS_SELECTED;
5113 return TRUE;
5116 /* find the item and subitem structures before we proceed */
5117 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5118 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5119 assert (lpItem);
5121 if (isubitem)
5123 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5124 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5125 if (!lpSubItem)
5127 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5128 isubitem = 0;
5131 else
5132 pItemHdr = &lpItem->hdr;
5134 /* Do we need to query the state from the app? */
5135 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5137 dispInfo.item.mask |= LVIF_STATE;
5138 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5141 /* Do we need to enquire about the image? */
5142 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5143 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5145 dispInfo.item.mask |= LVIF_IMAGE;
5146 dispInfo.item.iImage = I_IMAGECALLBACK;
5149 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5150 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5152 dispInfo.item.mask |= LVIF_TEXT;
5153 dispInfo.item.pszText = lpLVItem->pszText;
5154 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5155 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5156 *dispInfo.item.pszText = '\0';
5159 /* If we don't have all the requested info, query the application */
5160 if (dispInfo.item.mask != 0)
5162 dispInfo.item.iItem = lpLVItem->iItem;
5163 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5164 dispInfo.item.lParam = lpItem->lParam;
5165 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5166 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5169 /* we should not store values for subitems */
5170 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5172 /* Now, handle the iImage field */
5173 if (dispInfo.item.mask & LVIF_IMAGE)
5175 lpLVItem->iImage = dispInfo.item.iImage;
5176 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5177 pItemHdr->iImage = dispInfo.item.iImage;
5179 else if (lpLVItem->mask & LVIF_IMAGE)
5181 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5182 lpLVItem->iImage = pItemHdr->iImage;
5183 else
5184 lpLVItem->iImage = 0;
5187 /* The pszText field */
5188 if (dispInfo.item.mask & LVIF_TEXT)
5190 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5191 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5193 lpLVItem->pszText = dispInfo.item.pszText;
5195 else if (lpLVItem->mask & LVIF_TEXT)
5197 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5198 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5201 /* if this is a subitem, we're done */
5202 if (isubitem) return TRUE;
5204 /* Next is the lParam field */
5205 if (dispInfo.item.mask & LVIF_PARAM)
5207 lpLVItem->lParam = dispInfo.item.lParam;
5208 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5209 lpItem->lParam = dispInfo.item.lParam;
5211 else if (lpLVItem->mask & LVIF_PARAM)
5212 lpLVItem->lParam = lpItem->lParam;
5214 /* ... the state field (this one is different due to uCallbackmask) */
5215 if (lpLVItem->mask & LVIF_STATE)
5217 lpLVItem->state = lpItem->state;
5218 if (dispInfo.item.mask & LVIF_STATE)
5220 lpLVItem->state &= ~dispInfo.item.stateMask;
5221 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5223 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5225 lpLVItem->state &= ~LVIS_FOCUSED;
5226 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5227 lpLVItem->state |= LVIS_FOCUSED;
5229 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5231 lpLVItem->state &= ~LVIS_SELECTED;
5232 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5233 lpLVItem->state |= LVIS_SELECTED;
5237 /* and last, but not least, the indent field */
5238 if (lpLVItem->mask & LVIF_INDENT)
5239 lpLVItem->iIndent = lpItem->iIndent;
5241 return TRUE;
5244 /***
5245 * DESCRIPTION:
5246 * Retrieves item attributes.
5248 * PARAMETER(S):
5249 * [I] hwnd : window handle
5250 * [IO] lpLVItem : item info
5251 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5252 * if FALSE, the lpLVItem is a LPLVITEMA.
5254 * NOTE:
5255 * This is the external 'GetItem' interface -- it properly copies
5256 * the text in the provided buffer.
5258 * RETURN:
5259 * SUCCESS : TRUE
5260 * FAILURE : FALSE
5262 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5264 LPWSTR pszText;
5265 BOOL bResult;
5267 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5268 return FALSE;
5270 pszText = lpLVItem->pszText;
5271 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5272 if (bResult && lpLVItem->pszText != pszText)
5273 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5274 lpLVItem->pszText = pszText;
5276 return bResult;
5280 /***
5281 * DESCRIPTION:
5282 * Retrieves the position (upper-left) of the listview control item.
5283 * Note that for LVS_ICON style, the upper-left is that of the icon
5284 * and not the bounding box.
5286 * PARAMETER(S):
5287 * [I] infoPtr : valid pointer to the listview structure
5288 * [I] nItem : item index
5289 * [O] lpptPosition : coordinate information
5291 * RETURN:
5292 * SUCCESS : TRUE
5293 * FAILURE : FALSE
5295 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5297 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5298 POINT Origin;
5300 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5302 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5304 LISTVIEW_GetOrigin(infoPtr, &Origin);
5305 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5307 if (uView == LVS_ICON)
5309 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5310 lpptPosition->y += ICON_TOP_PADDING;
5312 lpptPosition->x += Origin.x;
5313 lpptPosition->y += Origin.y;
5315 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5316 return TRUE;
5320 /***
5321 * DESCRIPTION:
5322 * Retrieves the bounding rectangle for a listview control item.
5324 * PARAMETER(S):
5325 * [I] infoPtr : valid pointer to the listview structure
5326 * [I] nItem : item index
5327 * [IO] lprc : bounding rectangle coordinates
5328 * lprc->left specifies the portion of the item for which the bounding
5329 * rectangle will be retrieved.
5331 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5332 * including the icon and label.
5334 * * For LVS_ICON
5335 * * Experiment shows that native control returns:
5336 * * width = min (48, length of text line)
5337 * * .left = position.x - (width - iconsize.cx)/2
5338 * * .right = .left + width
5339 * * height = #lines of text * ntmHeight + icon height + 8
5340 * * .top = position.y - 2
5341 * * .bottom = .top + height
5342 * * separation between items .y = itemSpacing.cy - height
5343 * * .x = itemSpacing.cx - width
5344 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5346 * * For LVS_ICON
5347 * * Experiment shows that native control returns:
5348 * * width = iconSize.cx + 16
5349 * * .left = position.x - (width - iconsize.cx)/2
5350 * * .right = .left + width
5351 * * height = iconSize.cy + 4
5352 * * .top = position.y - 2
5353 * * .bottom = .top + height
5354 * * separation between items .y = itemSpacing.cy - height
5355 * * .x = itemSpacing.cx - width
5356 * LVIR_LABEL Returns the bounding rectangle of the item text.
5358 * * For LVS_ICON
5359 * * Experiment shows that native control returns:
5360 * * width = text length
5361 * * .left = position.x - width/2
5362 * * .right = .left + width
5363 * * height = ntmH * linecount + 2
5364 * * .top = position.y + iconSize.cy + 6
5365 * * .bottom = .top + height
5366 * * separation between items .y = itemSpacing.cy - height
5367 * * .x = itemSpacing.cx - width
5368 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5369 * rectangles, but excludes columns in report view.
5371 * RETURN:
5372 * SUCCESS : TRUE
5373 * FAILURE : FALSE
5375 * NOTES
5376 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5377 * upon whether the window has the focus currently and on whether the item
5378 * is the one with the focus. Ensure that the control's record of which
5379 * item has the focus agrees with the items' records.
5381 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5383 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5384 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5385 BOOL doLabel = TRUE, oversizedBox = FALSE;
5386 POINT Position, Origin;
5387 LVITEMW lvItem;
5388 RECT label_rect;
5390 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5392 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5394 LISTVIEW_GetOrigin(infoPtr, &Origin);
5395 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5397 /* Be smart and try to figure out the minimum we have to do */
5398 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5399 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5400 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5401 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5402 oversizedBox = TRUE;
5404 /* get what we need from the item before hand, so we make
5405 * only one request. This can speed up things, if data
5406 * is stored on the app side */
5407 lvItem.mask = 0;
5408 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5409 if (doLabel) lvItem.mask |= LVIF_TEXT;
5410 lvItem.iItem = nItem;
5411 lvItem.iSubItem = 0;
5412 lvItem.pszText = szDispText;
5413 lvItem.cchTextMax = DISP_TEXT_SIZE;
5414 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5415 /* we got the state already up, simulate it here, to avoid a reget */
5416 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5418 lvItem.mask |= LVIF_STATE;
5419 lvItem.stateMask = LVIS_FOCUSED;
5420 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5423 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5424 lprc->left = LVIR_BOUNDS;
5425 switch(lprc->left)
5427 case LVIR_ICON:
5428 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5429 break;
5431 case LVIR_LABEL:
5432 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5433 break;
5435 case LVIR_BOUNDS:
5436 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5437 break;
5439 case LVIR_SELECTBOUNDS:
5440 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5441 UnionRect(lprc, lprc, &label_rect);
5442 break;
5444 default:
5445 WARN("Unknown value: %ld\n", lprc->left);
5446 return FALSE;
5449 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5451 TRACE(" rect=%s\n", debugrect(lprc));
5453 return TRUE;
5456 /***
5457 * DESCRIPTION:
5458 * Retrieves the spacing between listview control items.
5460 * PARAMETER(S):
5461 * [I] infoPtr : valid pointer to the listview structure
5462 * [IO] lprc : rectangle to receive the output
5463 * on input, lprc->top = nSubItem
5464 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5466 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5467 * not only those of the first column.
5468 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5470 * RETURN:
5471 * TRUE: success
5472 * FALSE: failure
5474 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5476 POINT Position;
5477 LVITEMW lvItem;
5479 if (!lprc) return FALSE;
5481 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5482 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5483 if (lprc->top == 0)
5484 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5486 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5488 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5490 lvItem.mask = 0;
5491 lvItem.iItem = nItem;
5492 lvItem.iSubItem = lprc->top;
5494 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5495 switch(lprc->left)
5497 case LVIR_ICON:
5498 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5499 break;
5501 case LVIR_LABEL:
5502 case LVIR_BOUNDS:
5503 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5504 break;
5506 default:
5507 ERR("Unknown bounds=%ld\n", lprc->left);
5508 return FALSE;
5511 OffsetRect(lprc, Position.x, Position.y);
5512 return TRUE;
5516 /***
5517 * DESCRIPTION:
5518 * Retrieves the width of a label.
5520 * PARAMETER(S):
5521 * [I] infoPtr : valid pointer to the listview structure
5523 * RETURN:
5524 * SUCCESS : string width (in pixels)
5525 * FAILURE : zero
5527 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5529 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5530 LVITEMW lvItem;
5532 TRACE("(nItem=%d)\n", nItem);
5534 lvItem.mask = LVIF_TEXT;
5535 lvItem.iItem = nItem;
5536 lvItem.iSubItem = 0;
5537 lvItem.pszText = szDispText;
5538 lvItem.cchTextMax = DISP_TEXT_SIZE;
5539 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5541 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5544 /***
5545 * DESCRIPTION:
5546 * Retrieves the spacing between listview control items.
5548 * PARAMETER(S):
5549 * [I] infoPtr : valid pointer to the listview structure
5550 * [I] bSmall : flag for small or large icon
5552 * RETURN:
5553 * Horizontal + vertical spacing
5555 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5557 LONG lResult;
5559 if (!bSmall)
5561 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5563 else
5565 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5566 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5567 else
5568 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5570 return lResult;
5573 /***
5574 * DESCRIPTION:
5575 * Retrieves the state of a listview control item.
5577 * PARAMETER(S):
5578 * [I] infoPtr : valid pointer to the listview structure
5579 * [I] nItem : item index
5580 * [I] uMask : state mask
5582 * RETURN:
5583 * State specified by the mask.
5585 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5587 LVITEMW lvItem;
5589 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5591 lvItem.iItem = nItem;
5592 lvItem.iSubItem = 0;
5593 lvItem.mask = LVIF_STATE;
5594 lvItem.stateMask = uMask;
5595 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5597 return lvItem.state & uMask;
5600 /***
5601 * DESCRIPTION:
5602 * Retrieves the text of a listview control item or subitem.
5604 * PARAMETER(S):
5605 * [I] hwnd : window handle
5606 * [I] nItem : item index
5607 * [IO] lpLVItem : item information
5608 * [I] isW : TRUE if lpLVItem is Unicode
5610 * RETURN:
5611 * SUCCESS : string length
5612 * FAILURE : 0
5614 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5616 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5618 lpLVItem->mask = LVIF_TEXT;
5619 lpLVItem->iItem = nItem;
5620 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5622 return textlenT(lpLVItem->pszText, isW);
5625 /***
5626 * DESCRIPTION:
5627 * Searches for an item based on properties + relationships.
5629 * PARAMETER(S):
5630 * [I] infoPtr : valid pointer to the listview structure
5631 * [I] nItem : item index
5632 * [I] uFlags : relationship flag
5634 * RETURN:
5635 * SUCCESS : item index
5636 * FAILURE : -1
5638 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5640 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5641 UINT uMask = 0;
5642 LVFINDINFOW lvFindInfo;
5643 INT nCountPerColumn;
5644 INT nCountPerRow;
5645 INT i;
5647 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5648 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5650 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5652 if (uFlags & LVNI_CUT)
5653 uMask |= LVIS_CUT;
5655 if (uFlags & LVNI_DROPHILITED)
5656 uMask |= LVIS_DROPHILITED;
5658 if (uFlags & LVNI_FOCUSED)
5659 uMask |= LVIS_FOCUSED;
5661 if (uFlags & LVNI_SELECTED)
5662 uMask |= LVIS_SELECTED;
5664 /* if we're asked for the focused item, that's only one,
5665 * so it's worth optimizing */
5666 if (uFlags & LVNI_FOCUSED)
5668 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5669 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5672 if (uFlags & LVNI_ABOVE)
5674 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5676 while (nItem >= 0)
5678 nItem--;
5679 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5680 return nItem;
5683 else
5685 /* Special case for autoarrange - move 'til the top of a list */
5686 if (is_autoarrange(infoPtr))
5688 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5689 while (nItem - nCountPerRow >= 0)
5691 nItem -= nCountPerRow;
5692 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5693 return nItem;
5695 return -1;
5697 lvFindInfo.flags = LVFI_NEARESTXY;
5698 lvFindInfo.vkDirection = VK_UP;
5699 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5700 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5702 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5703 return nItem;
5707 else if (uFlags & LVNI_BELOW)
5709 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5711 while (nItem < infoPtr->nItemCount)
5713 nItem++;
5714 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5715 return nItem;
5718 else
5720 /* Special case for autoarrange - move 'til the bottom of a list */
5721 if (is_autoarrange(infoPtr))
5723 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5724 while (nItem + nCountPerRow < infoPtr->nItemCount )
5726 nItem += nCountPerRow;
5727 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5728 return nItem;
5730 return -1;
5732 lvFindInfo.flags = LVFI_NEARESTXY;
5733 lvFindInfo.vkDirection = VK_DOWN;
5734 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5735 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5737 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5738 return nItem;
5742 else if (uFlags & LVNI_TOLEFT)
5744 if (uView == LVS_LIST)
5746 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5747 while (nItem - nCountPerColumn >= 0)
5749 nItem -= nCountPerColumn;
5750 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5751 return nItem;
5754 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5756 /* Special case for autoarrange - move 'ti the beginning of a row */
5757 if (is_autoarrange(infoPtr))
5759 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5760 while (nItem % nCountPerRow > 0)
5762 nItem --;
5763 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5764 return nItem;
5766 return -1;
5768 lvFindInfo.flags = LVFI_NEARESTXY;
5769 lvFindInfo.vkDirection = VK_LEFT;
5770 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5771 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5773 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5774 return nItem;
5778 else if (uFlags & LVNI_TORIGHT)
5780 if (uView == LVS_LIST)
5782 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5783 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5785 nItem += nCountPerColumn;
5786 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5787 return nItem;
5790 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5792 /* Special case for autoarrange - move 'til the end of a row */
5793 if (is_autoarrange(infoPtr))
5795 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5796 while (nItem % nCountPerRow < nCountPerRow - 1 )
5798 nItem ++;
5799 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5800 return nItem;
5802 return -1;
5804 lvFindInfo.flags = LVFI_NEARESTXY;
5805 lvFindInfo.vkDirection = VK_RIGHT;
5806 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5807 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5809 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5810 return nItem;
5814 else
5816 nItem++;
5818 /* search by index */
5819 for (i = nItem; i < infoPtr->nItemCount; i++)
5821 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5822 return i;
5826 return -1;
5829 /* LISTVIEW_GetNumberOfWorkAreas */
5831 /***
5832 * DESCRIPTION:
5833 * Retrieves the origin coordinates when in icon or small icon display mode.
5835 * PARAMETER(S):
5836 * [I] infoPtr : valid pointer to the listview structure
5837 * [O] lpptOrigin : coordinate information
5839 * RETURN:
5840 * None.
5842 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5844 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5845 INT nHorzPos = 0, nVertPos = 0;
5846 SCROLLINFO scrollInfo;
5848 scrollInfo.cbSize = sizeof(SCROLLINFO);
5849 scrollInfo.fMask = SIF_POS;
5851 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5852 nHorzPos = scrollInfo.nPos;
5853 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5854 nVertPos = scrollInfo.nPos;
5856 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5858 lpptOrigin->x = infoPtr->rcList.left;
5859 lpptOrigin->y = infoPtr->rcList.top;
5860 if (uView == LVS_LIST)
5861 nHorzPos *= infoPtr->nItemWidth;
5862 else if (uView == LVS_REPORT)
5863 nVertPos *= infoPtr->nItemHeight;
5865 lpptOrigin->x -= nHorzPos;
5866 lpptOrigin->y -= nVertPos;
5868 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5871 /***
5872 * DESCRIPTION:
5873 * Retrieves the width of a string.
5875 * PARAMETER(S):
5876 * [I] hwnd : window handle
5877 * [I] lpszText : text string to process
5878 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5880 * RETURN:
5881 * SUCCESS : string width (in pixels)
5882 * FAILURE : zero
5884 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5886 SIZE stringSize;
5888 stringSize.cx = 0;
5889 if (is_textT(lpszText, isW))
5891 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5892 HDC hdc = GetDC(infoPtr->hwndSelf);
5893 HFONT hOldFont = SelectObject(hdc, hFont);
5895 if (isW)
5896 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5897 else
5898 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5899 SelectObject(hdc, hOldFont);
5900 ReleaseDC(infoPtr->hwndSelf, hdc);
5902 return stringSize.cx;
5905 /***
5906 * DESCRIPTION:
5907 * Determines which listview item is located at the specified position.
5909 * PARAMETER(S):
5910 * [I] infoPtr : valid pointer to the listview structure
5911 * [IO] lpht : hit test information
5912 * [I] subitem : fill out iSubItem.
5913 * [I] select : return the index only if the hit selects the item
5915 * NOTE:
5916 * (mm 20001022): We must not allow iSubItem to be touched, for
5917 * an app might pass only a structure with space up to iItem!
5918 * (MS Office 97 does that for instance in the file open dialog)
5920 * RETURN:
5921 * SUCCESS : item index
5922 * FAILURE : -1
5924 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5926 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5927 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5928 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5929 POINT Origin, Position, opt;
5930 LVITEMW lvItem;
5931 ITERATOR i;
5932 INT iItem;
5934 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5936 lpht->flags = 0;
5937 lpht->iItem = -1;
5938 if (subitem) lpht->iSubItem = 0;
5940 if (infoPtr->rcList.left > lpht->pt.x)
5941 lpht->flags |= LVHT_TOLEFT;
5942 else if (infoPtr->rcList.right < lpht->pt.x)
5943 lpht->flags |= LVHT_TORIGHT;
5945 if (infoPtr->rcList.top > lpht->pt.y)
5946 lpht->flags |= LVHT_ABOVE;
5947 else if (infoPtr->rcList.bottom < lpht->pt.y)
5948 lpht->flags |= LVHT_BELOW;
5950 TRACE("lpht->flags=0x%x\n", lpht->flags);
5951 if (lpht->flags) return -1;
5953 lpht->flags |= LVHT_NOWHERE;
5955 LISTVIEW_GetOrigin(infoPtr, &Origin);
5957 /* first deal with the large items */
5958 rcSearch.left = lpht->pt.x;
5959 rcSearch.top = lpht->pt.y;
5960 rcSearch.right = rcSearch.left + 1;
5961 rcSearch.bottom = rcSearch.top + 1;
5963 iterator_frameditems(&i, infoPtr, &rcSearch);
5964 iterator_next(&i); /* go to first item in the sequence */
5965 iItem = i.nItem;
5966 iterator_destroy(&i);
5968 TRACE("lpht->iItem=%d\n", iItem);
5969 if (iItem == -1) return -1;
5971 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5972 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5973 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5974 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5975 lvItem.iItem = iItem;
5976 lvItem.iSubItem = 0;
5977 lvItem.pszText = szDispText;
5978 lvItem.cchTextMax = DISP_TEXT_SIZE;
5979 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5980 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5982 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5983 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5984 opt.x = lpht->pt.x - Position.x - Origin.x;
5985 opt.y = lpht->pt.y - Position.y - Origin.y;
5987 if (uView == LVS_REPORT)
5988 rcBounds = rcBox;
5989 else
5990 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5991 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5992 if (!PtInRect(&rcBounds, opt)) return -1;
5994 if (PtInRect(&rcIcon, opt))
5995 lpht->flags |= LVHT_ONITEMICON;
5996 else if (PtInRect(&rcLabel, opt))
5997 lpht->flags |= LVHT_ONITEMLABEL;
5998 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5999 lpht->flags |= LVHT_ONITEMSTATEICON;
6000 if (lpht->flags & LVHT_ONITEM)
6001 lpht->flags &= ~LVHT_NOWHERE;
6003 TRACE("lpht->flags=0x%x\n", lpht->flags);
6004 if (uView == LVS_REPORT && subitem)
6006 INT j;
6008 rcBounds.right = rcBounds.left;
6009 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6011 rcBounds.left = rcBounds.right;
6012 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6013 if (PtInRect(&rcBounds, opt))
6015 lpht->iSubItem = j;
6016 break;
6021 if (select && !(uView == LVS_REPORT &&
6022 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6023 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6025 if (uView == LVS_REPORT)
6027 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6028 UnionRect(&rcBounds, &rcBounds, &rcState);
6030 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6032 return lpht->iItem = iItem;
6036 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6037 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6038 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6039 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6040 their own sort proc. when sending LVM_SORTITEMS.
6042 /* Platform SDK:
6043 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6045 LVS_SORTXXX must be specified,
6046 LVS_OWNERDRAW is not set,
6047 <item>.pszText is not LPSTR_TEXTCALLBACK.
6049 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6050 are sorted based on item text..."
6052 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6054 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6055 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6056 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6058 /* if we're sorting descending, negate the return value */
6059 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6062 /***
6063 * DESCRIPTION:
6064 * Inserts a new item in the listview control.
6066 * PARAMETER(S):
6067 * [I] infoPtr : valid pointer to the listview structure
6068 * [I] lpLVItem : item information
6069 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6071 * RETURN:
6072 * SUCCESS : new item index
6073 * FAILURE : -1
6075 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6077 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6078 INT nItem;
6079 HDPA hdpaSubItems;
6080 NMLISTVIEW nmlv;
6081 ITEM_INFO *lpItem;
6082 BOOL is_sorted, has_changed;
6083 LVITEMW item;
6085 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6087 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6089 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6090 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6092 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6094 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6096 /* insert item in listview control data structure */
6097 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6098 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6100 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6101 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6103 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6104 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6105 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6106 if (nItem == -1) goto fail;
6107 infoPtr->nItemCount++;
6109 /* shift indices first so they don't get tangled */
6110 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6112 /* set the item attributes */
6113 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6115 /* full size structure expected - _WIN32IE >= 0x560 */
6116 item = *lpLVItem;
6118 else if (lpLVItem->mask & LVIF_INDENT)
6120 /* indent member expected - _WIN32IE >= 0x300 */
6121 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6123 else
6125 /* minimal structure expected */
6126 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6128 item.iItem = nItem;
6129 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6130 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6132 /* if we're sorted, sort the list, and update the index */
6133 if (is_sorted)
6135 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6136 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6137 assert(nItem != -1);
6140 /* make room for the position, if we are in the right mode */
6141 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6143 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6144 goto undo;
6145 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6147 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6148 goto undo;
6152 /* send LVN_INSERTITEM notification */
6153 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6154 nmlv.iItem = nItem;
6155 nmlv.lParam = lpItem->lParam;
6156 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6158 /* align items (set position of each item) */
6159 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6161 POINT pt;
6163 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6164 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6165 else
6166 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6168 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6171 /* now is the invalidation fun */
6172 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6173 return nItem;
6175 undo:
6176 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6177 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6178 infoPtr->nItemCount--;
6179 fail:
6180 DPA_DeletePtr(hdpaSubItems, 0);
6181 DPA_Destroy (hdpaSubItems);
6182 Free (lpItem);
6183 return -1;
6186 /***
6187 * DESCRIPTION:
6188 * Redraws a range of items.
6190 * PARAMETER(S):
6191 * [I] infoPtr : valid pointer to the listview structure
6192 * [I] nFirst : first item
6193 * [I] nLast : last item
6195 * RETURN:
6196 * SUCCESS : TRUE
6197 * FAILURE : FALSE
6199 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6201 INT i;
6203 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6204 max(nFirst, nLast) >= infoPtr->nItemCount)
6205 return FALSE;
6207 for (i = nFirst; i <= nLast; i++)
6208 LISTVIEW_InvalidateItem(infoPtr, i);
6210 return TRUE;
6213 /***
6214 * DESCRIPTION:
6215 * Scroll the content of a listview.
6217 * PARAMETER(S):
6218 * [I] infoPtr : valid pointer to the listview structure
6219 * [I] dx : horizontal scroll amount in pixels
6220 * [I] dy : vertical scroll amount in pixels
6222 * RETURN:
6223 * SUCCESS : TRUE
6224 * FAILURE : FALSE
6226 * COMMENTS:
6227 * If the control is in report mode (LVS_REPORT) the control can
6228 * be scrolled only in line increments. "dy" will be rounded to the
6229 * nearest number of pixels that are a whole line. Ex: if line height
6230 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6231 * is passed the the scroll will be 0. (per MSDN 7/2002)
6233 * For: (per experimentaion with native control and CSpy ListView)
6234 * LVS_ICON dy=1 = 1 pixel (vertical only)
6235 * dx ignored
6236 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6237 * dx ignored
6238 * LVS_LIST dx=1 = 1 column (horizontal only)
6239 * but will only scroll 1 column per message
6240 * no matter what the value.
6241 * dy must be 0 or FALSE returned.
6242 * LVS_REPORT dx=1 = 1 pixel
6243 * dy= see above
6246 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6248 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6249 case LVS_REPORT:
6250 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6251 dy /= infoPtr->nItemHeight;
6252 break;
6253 case LVS_LIST:
6254 if (dy != 0) return FALSE;
6255 break;
6256 default: /* icon */
6257 dx = 0;
6258 break;
6261 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6262 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6264 return TRUE;
6267 /***
6268 * DESCRIPTION:
6269 * Sets the background color.
6271 * PARAMETER(S):
6272 * [I] infoPtr : valid pointer to the listview structure
6273 * [I] clrBk : background color
6275 * RETURN:
6276 * SUCCESS : TRUE
6277 * FAILURE : FALSE
6279 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6281 TRACE("(clrBk=%lx)\n", clrBk);
6283 if(infoPtr->clrBk != clrBk) {
6284 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6285 infoPtr->clrBk = clrBk;
6286 if (clrBk == CLR_NONE)
6287 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6288 else
6289 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6290 LISTVIEW_InvalidateList(infoPtr);
6293 return TRUE;
6296 /* LISTVIEW_SetBkImage */
6298 /*** Helper for {Insert,Set}ColumnT *only* */
6299 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6301 if (lpColumn->mask & LVCF_FMT)
6303 /* format member is valid */
6304 lphdi->mask |= HDI_FORMAT;
6306 /* set text alignment (leftmost column must be left-aligned) */
6307 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6308 lphdi->fmt |= HDF_LEFT;
6309 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6310 lphdi->fmt |= HDF_RIGHT;
6311 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6312 lphdi->fmt |= HDF_CENTER;
6314 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6315 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6317 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6319 lphdi->fmt |= HDF_IMAGE;
6320 lphdi->iImage = I_IMAGECALLBACK;
6324 if (lpColumn->mask & LVCF_WIDTH)
6326 lphdi->mask |= HDI_WIDTH;
6327 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6329 /* make it fill the remainder of the controls width */
6330 RECT rcHeader;
6331 INT item_index;
6333 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6335 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6336 lphdi->cxy += rcHeader.right - rcHeader.left;
6339 /* retrieve the layout of the header */
6340 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6341 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6343 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6345 else
6346 lphdi->cxy = lpColumn->cx;
6349 if (lpColumn->mask & LVCF_TEXT)
6351 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6352 lphdi->fmt |= HDF_STRING;
6353 lphdi->pszText = lpColumn->pszText;
6354 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6357 if (lpColumn->mask & LVCF_IMAGE)
6359 lphdi->mask |= HDI_IMAGE;
6360 lphdi->iImage = lpColumn->iImage;
6363 if (lpColumn->mask & LVCF_ORDER)
6365 lphdi->mask |= HDI_ORDER;
6366 lphdi->iOrder = lpColumn->iOrder;
6371 /***
6372 * DESCRIPTION:
6373 * Inserts a new column.
6375 * PARAMETER(S):
6376 * [I] infoPtr : valid pointer to the listview structure
6377 * [I] nColumn : column index
6378 * [I] lpColumn : column information
6379 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6381 * RETURN:
6382 * SUCCESS : new column index
6383 * FAILURE : -1
6385 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6386 const LVCOLUMNW *lpColumn, BOOL isW)
6388 COLUMN_INFO *lpColumnInfo;
6389 INT nNewColumn;
6390 HDITEMW hdi;
6392 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6394 if (!lpColumn || nColumn < 0) return -1;
6395 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6397 ZeroMemory(&hdi, sizeof(HDITEMW));
6398 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6400 /* insert item in header control */
6401 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6402 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6403 (WPARAM)nColumn, (LPARAM)&hdi);
6404 if (nNewColumn == -1) return -1;
6405 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6407 /* create our own column info */
6408 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6409 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6411 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6412 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6414 /* now we have to actually adjust the data */
6415 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6417 SUBITEM_INFO *lpSubItem;
6418 HDPA hdpaSubItems;
6419 INT nItem, i;
6421 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6423 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6424 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6426 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6427 if (lpSubItem->iSubItem >= nNewColumn)
6428 lpSubItem->iSubItem++;
6433 /* make space for the new column */
6434 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6436 return nNewColumn;
6438 fail:
6439 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6440 if (lpColumnInfo)
6442 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6443 Free(lpColumnInfo);
6445 return -1;
6448 /***
6449 * DESCRIPTION:
6450 * Sets the attributes of a header item.
6452 * PARAMETER(S):
6453 * [I] infoPtr : valid pointer to the listview structure
6454 * [I] nColumn : column index
6455 * [I] lpColumn : column attributes
6456 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6458 * RETURN:
6459 * SUCCESS : TRUE
6460 * FAILURE : FALSE
6462 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6463 const LVCOLUMNW *lpColumn, BOOL isW)
6465 HDITEMW hdi, hdiget;
6466 BOOL bResult;
6468 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6470 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6472 ZeroMemory(&hdi, sizeof(HDITEMW));
6473 if (lpColumn->mask & LVCF_FMT)
6475 hdi.mask |= HDI_FORMAT;
6476 hdiget.mask = HDI_FORMAT;
6477 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6478 hdi.fmt = hdiget.fmt & HDF_STRING;
6480 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6482 /* set header item attributes */
6483 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6484 if (!bResult) return FALSE;
6486 if (lpColumn->mask & LVCF_FMT)
6488 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6489 int oldFmt = lpColumnInfo->fmt;
6491 lpColumnInfo->fmt = lpColumn->fmt;
6492 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6494 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6495 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6499 return TRUE;
6502 /***
6503 * DESCRIPTION:
6504 * Sets the column order array
6506 * PARAMETERS:
6507 * [I] infoPtr : valid pointer to the listview structure
6508 * [I] iCount : number of elements in column order array
6509 * [I] lpiArray : pointer to column order array
6511 * RETURN:
6512 * SUCCESS : TRUE
6513 * FAILURE : FALSE
6515 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6517 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6519 if (!lpiArray)
6520 return FALSE;
6522 return TRUE;
6526 /***
6527 * DESCRIPTION:
6528 * Sets the width of a column
6530 * PARAMETERS:
6531 * [I] infoPtr : valid pointer to the listview structure
6532 * [I] nColumn : column index
6533 * [I] cx : column width
6535 * RETURN:
6536 * SUCCESS : TRUE
6537 * FAILURE : FALSE
6539 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6541 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6542 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6543 INT max_cx = 0;
6544 HDITEMW hdi;
6546 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6548 /* set column width only if in report or list mode */
6549 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6551 /* take care of invalid cx values */
6552 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6553 else if (uView == LVS_LIST && cx < 1) return FALSE;
6555 /* resize all columns if in LVS_LIST mode */
6556 if(uView == LVS_LIST)
6558 infoPtr->nItemWidth = cx;
6559 LISTVIEW_InvalidateList(infoPtr);
6560 return TRUE;
6563 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6565 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6567 INT nLabelWidth;
6568 LVITEMW lvItem;
6570 lvItem.mask = LVIF_TEXT;
6571 lvItem.iItem = 0;
6572 lvItem.iSubItem = nColumn;
6573 lvItem.pszText = szDispText;
6574 lvItem.cchTextMax = DISP_TEXT_SIZE;
6575 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6577 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6578 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6579 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6581 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6582 max_cx += infoPtr->iconSize.cx;
6583 max_cx += TRAILING_LABEL_PADDING;
6586 /* autosize based on listview items width */
6587 if(cx == LVSCW_AUTOSIZE)
6588 cx = max_cx;
6589 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6591 /* if iCol is the last column make it fill the remainder of the controls width */
6592 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6594 RECT rcHeader;
6595 POINT Origin;
6597 LISTVIEW_GetOrigin(infoPtr, &Origin);
6598 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6600 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6602 else
6604 /* Despite what the MS docs say, if this is not the last
6605 column, then MS resizes the column to the width of the
6606 largest text string in the column, including headers
6607 and items. This is different from LVSCW_AUTOSIZE in that
6608 LVSCW_AUTOSIZE ignores the header string length. */
6609 cx = 0;
6611 /* retrieve header text */
6612 hdi.mask = HDI_TEXT;
6613 hdi.cchTextMax = DISP_TEXT_SIZE;
6614 hdi.pszText = szDispText;
6615 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6617 HDC hdc = GetDC(infoPtr->hwndSelf);
6618 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6619 SIZE size;
6621 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6622 cx = size.cx + TRAILING_HEADER_PADDING;
6623 /* FIXME: Take into account the header image, if one is present */
6624 SelectObject(hdc, old_font);
6625 ReleaseDC(infoPtr->hwndSelf, hdc);
6627 cx = max (cx, max_cx);
6631 if (cx < 0) return FALSE;
6633 /* call header to update the column change */
6634 hdi.mask = HDI_WIDTH;
6635 hdi.cxy = cx;
6636 TRACE("hdi.cxy=%d\n", hdi.cxy);
6637 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6640 /***
6641 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6644 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6646 HDC hdc_wnd, hdc;
6647 HBITMAP hbm_im, hbm_mask, hbm_orig;
6648 RECT rc;
6649 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6650 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6651 HIMAGELIST himl;
6653 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6654 ILC_COLOR | ILC_MASK, 2, 2);
6655 hdc_wnd = GetDC(infoPtr->hwndSelf);
6656 hdc = CreateCompatibleDC(hdc_wnd);
6657 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6658 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6659 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6661 rc.left = rc.top = 0;
6662 rc.right = GetSystemMetrics(SM_CXSMICON);
6663 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6665 hbm_orig = SelectObject(hdc, hbm_mask);
6666 FillRect(hdc, &rc, hbr_white);
6667 InflateRect(&rc, -3, -3);
6668 FillRect(hdc, &rc, hbr_black);
6670 SelectObject(hdc, hbm_im);
6671 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6672 SelectObject(hdc, hbm_orig);
6673 ImageList_Add(himl, hbm_im, hbm_mask);
6675 SelectObject(hdc, hbm_im);
6676 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6677 SelectObject(hdc, hbm_orig);
6678 ImageList_Add(himl, hbm_im, hbm_mask);
6680 DeleteObject(hbm_mask);
6681 DeleteObject(hbm_im);
6682 DeleteDC(hdc);
6684 return himl;
6687 /***
6688 * DESCRIPTION:
6689 * Sets the extended listview style.
6691 * PARAMETERS:
6692 * [I] infoPtr : valid pointer to the listview structure
6693 * [I] dwMask : mask
6694 * [I] dwStyle : style
6696 * RETURN:
6697 * SUCCESS : previous style
6698 * FAILURE : 0
6700 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6702 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6704 /* set new style */
6705 if (dwMask)
6706 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6707 else
6708 infoPtr->dwLvExStyle = dwStyle;
6710 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6712 HIMAGELIST himl = 0;
6713 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6714 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6715 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6718 return dwOldStyle;
6721 /***
6722 * DESCRIPTION:
6723 * Sets the new hot cursor used during hot tracking and hover selection.
6725 * PARAMETER(S):
6726 * [I] infoPtr : valid pointer to the listview structure
6727 * [I} hCurosr : the new hot cursor handle
6729 * RETURN:
6730 * Returns the previous hot cursor
6732 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6734 HCURSOR oldCursor = infoPtr->hHotCursor;
6736 infoPtr->hHotCursor = hCursor;
6738 return oldCursor;
6742 /***
6743 * DESCRIPTION:
6744 * Sets the hot item index.
6746 * PARAMETERS:
6747 * [I] infoPtr : valid pointer to the listview structure
6748 * [I] iIndex : index
6750 * RETURN:
6751 * SUCCESS : previous hot item index
6752 * FAILURE : -1 (no hot item)
6754 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6756 INT iOldIndex = infoPtr->nHotItem;
6758 infoPtr->nHotItem = iIndex;
6760 return iOldIndex;
6764 /***
6765 * DESCRIPTION:
6766 * Sets the amount of time the cursor must hover over an item before it is selected.
6768 * PARAMETER(S):
6769 * [I] infoPtr : valid pointer to the listview structure
6770 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6772 * RETURN:
6773 * Returns the previous hover time
6775 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6777 DWORD oldHoverTime = infoPtr->dwHoverTime;
6779 infoPtr->dwHoverTime = dwHoverTime;
6781 return oldHoverTime;
6784 /***
6785 * DESCRIPTION:
6786 * Sets spacing for icons of LVS_ICON style.
6788 * PARAMETER(S):
6789 * [I] infoPtr : valid pointer to the listview structure
6790 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6791 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6793 * RETURN:
6794 * MAKELONG(oldcx, oldcy)
6796 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6798 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6799 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6801 TRACE("requested=(%d,%d)\n", cx, cy);
6803 /* this is supported only for LVS_ICON style */
6804 if (uView != LVS_ICON) return oldspacing;
6806 /* set to defaults, if instructed to */
6807 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6808 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6810 /* if 0 then compute width
6811 * FIXME: Should scan each item and determine max width of
6812 * icon or label, then make that the width */
6813 if (cx == 0)
6814 cx = infoPtr->iconSpacing.cx;
6816 /* if 0 then compute height */
6817 if (cy == 0)
6818 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6819 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6822 infoPtr->iconSpacing.cx = cx;
6823 infoPtr->iconSpacing.cy = cy;
6825 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6826 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6827 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6828 infoPtr->ntmHeight);
6830 /* these depend on the iconSpacing */
6831 LISTVIEW_UpdateItemSize(infoPtr);
6833 return oldspacing;
6836 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6838 INT cx, cy;
6840 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6842 size->cx = cx;
6843 size->cy = cy;
6845 else
6847 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6848 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6852 /***
6853 * DESCRIPTION:
6854 * Sets image lists.
6856 * PARAMETER(S):
6857 * [I] infoPtr : valid pointer to the listview structure
6858 * [I] nType : image list type
6859 * [I] himl : image list handle
6861 * RETURN:
6862 * SUCCESS : old image list
6863 * FAILURE : NULL
6865 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6867 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6868 INT oldHeight = infoPtr->nItemHeight;
6869 HIMAGELIST himlOld = 0;
6871 TRACE("(nType=%d, himl=%p\n", nType, himl);
6873 switch (nType)
6875 case LVSIL_NORMAL:
6876 himlOld = infoPtr->himlNormal;
6877 infoPtr->himlNormal = himl;
6878 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6879 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6880 break;
6882 case LVSIL_SMALL:
6883 himlOld = infoPtr->himlSmall;
6884 infoPtr->himlSmall = himl;
6885 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6886 break;
6888 case LVSIL_STATE:
6889 himlOld = infoPtr->himlState;
6890 infoPtr->himlState = himl;
6891 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6892 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6893 break;
6895 default:
6896 ERR("Unknown icon type=%d\n", nType);
6897 return NULL;
6900 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6901 if (infoPtr->nItemHeight != oldHeight)
6902 LISTVIEW_UpdateScroll(infoPtr);
6904 return himlOld;
6907 /***
6908 * DESCRIPTION:
6909 * Preallocates memory (does *not* set the actual count of items !)
6911 * PARAMETER(S):
6912 * [I] infoPtr : valid pointer to the listview structure
6913 * [I] nItems : item count (projected number of items to allocate)
6914 * [I] dwFlags : update flags
6916 * RETURN:
6917 * SUCCESS : TRUE
6918 * FAILURE : FALSE
6920 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6922 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6924 if (infoPtr->dwStyle & LVS_OWNERDATA)
6926 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6927 INT nOldCount = infoPtr->nItemCount;
6929 if (nItems < nOldCount)
6931 RANGE range = { nItems, nOldCount };
6932 ranges_del(infoPtr->selectionRanges, range);
6933 if (infoPtr->nFocusedItem >= nItems)
6935 infoPtr->nFocusedItem = -1;
6936 SetRectEmpty(&infoPtr->rcFocus);
6940 infoPtr->nItemCount = nItems;
6941 LISTVIEW_UpdateScroll(infoPtr);
6943 /* the flags are valid only in ownerdata report and list modes */
6944 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6946 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6947 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6949 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6950 LISTVIEW_InvalidateList(infoPtr);
6951 else
6953 INT nFrom, nTo;
6954 POINT Origin;
6955 RECT rcErase;
6957 LISTVIEW_GetOrigin(infoPtr, &Origin);
6958 nFrom = min(nOldCount, nItems);
6959 nTo = max(nOldCount, nItems);
6961 if (uView == LVS_REPORT)
6963 rcErase.left = 0;
6964 rcErase.top = nFrom * infoPtr->nItemHeight;
6965 rcErase.right = infoPtr->nItemWidth;
6966 rcErase.bottom = nTo * infoPtr->nItemHeight;
6967 OffsetRect(&rcErase, Origin.x, Origin.y);
6968 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6969 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6971 else /* LVS_LIST */
6973 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6975 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6976 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6977 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6978 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6979 OffsetRect(&rcErase, Origin.x, Origin.y);
6980 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6981 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6983 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6984 rcErase.top = 0;
6985 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6986 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6987 OffsetRect(&rcErase, Origin.x, Origin.y);
6988 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6989 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6993 else
6995 /* According to MSDN for non-LVS_OWNERDATA this is just
6996 * a performance issue. The control allocates its internal
6997 * data structures for the number of items specified. It
6998 * cuts down on the number of memory allocations. Therefore
6999 * we will just issue a WARN here
7001 WARN("for non-ownerdata performance option not implemented.\n");
7004 return TRUE;
7007 /***
7008 * DESCRIPTION:
7009 * Sets the position of an item.
7011 * PARAMETER(S):
7012 * [I] infoPtr : valid pointer to the listview structure
7013 * [I] nItem : item index
7014 * [I] pt : coordinate
7016 * RETURN:
7017 * SUCCESS : TRUE
7018 * FAILURE : FALSE
7020 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7022 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7023 POINT Origin;
7025 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7027 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7028 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7030 LISTVIEW_GetOrigin(infoPtr, &Origin);
7032 /* This point value seems to be an undocumented feature.
7033 * The best guess is that it means either at the origin,
7034 * or at true beginning of the list. I will assume the origin. */
7035 if ((pt.x == -1) && (pt.y == -1))
7036 pt = Origin;
7038 if (uView == LVS_ICON)
7040 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7041 pt.y -= ICON_TOP_PADDING;
7043 pt.x -= Origin.x;
7044 pt.y -= Origin.y;
7046 infoPtr->bAutoarrange = FALSE;
7048 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7051 /***
7052 * DESCRIPTION:
7053 * Sets the state of one or many items.
7055 * PARAMETER(S):
7056 * [I] infoPtr : valid pointer to the listview structure
7057 * [I] nItem : item index
7058 * [I] lpLVItem : item or subitem info
7060 * RETURN:
7061 * SUCCESS : TRUE
7062 * FAILURE : FALSE
7064 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7066 BOOL bResult = TRUE;
7067 LVITEMW lvItem;
7069 lvItem.iItem = nItem;
7070 lvItem.iSubItem = 0;
7071 lvItem.mask = LVIF_STATE;
7072 lvItem.state = lpLVItem->state;
7073 lvItem.stateMask = lpLVItem->stateMask;
7074 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7076 if (nItem == -1)
7078 /* apply to all items */
7079 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7080 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7082 else
7083 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7086 * Update selection mark
7088 * Investigation on windows 2k showed that selection mark was updated
7089 * whenever a new selection was made, but if the selected item was
7090 * unselected it was not updated.
7092 * we are probably still not 100% accurate, but this at least sets the
7093 * proper selection mark when it is needed
7096 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7097 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7099 int i;
7100 infoPtr->nSelectionMark = -1;
7101 for (i = 0; i < infoPtr->nItemCount; i++)
7103 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7105 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7107 infoPtr->nSelectionMark = i;
7108 break;
7111 else if (ranges_contain(infoPtr->selectionRanges, i))
7113 infoPtr->nSelectionMark = i;
7114 break;
7119 return bResult;
7122 /***
7123 * DESCRIPTION:
7124 * Sets the text of an item or subitem.
7126 * PARAMETER(S):
7127 * [I] hwnd : window handle
7128 * [I] nItem : item index
7129 * [I] lpLVItem : item or subitem info
7130 * [I] isW : TRUE if input is Unicode
7132 * RETURN:
7133 * SUCCESS : TRUE
7134 * FAILURE : FALSE
7136 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7138 LVITEMW lvItem;
7140 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7142 lvItem.iItem = nItem;
7143 lvItem.iSubItem = lpLVItem->iSubItem;
7144 lvItem.mask = LVIF_TEXT;
7145 lvItem.pszText = lpLVItem->pszText;
7146 lvItem.cchTextMax = lpLVItem->cchTextMax;
7148 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7150 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7153 /***
7154 * DESCRIPTION:
7155 * Set item index that marks the start of a multiple selection.
7157 * PARAMETER(S):
7158 * [I] infoPtr : valid pointer to the listview structure
7159 * [I] nIndex : index
7161 * RETURN:
7162 * Index number or -1 if there is no selection mark.
7164 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7166 INT nOldIndex = infoPtr->nSelectionMark;
7168 TRACE("(nIndex=%d)\n", nIndex);
7170 infoPtr->nSelectionMark = nIndex;
7172 return nOldIndex;
7175 /***
7176 * DESCRIPTION:
7177 * Sets the text background color.
7179 * PARAMETER(S):
7180 * [I] infoPtr : valid pointer to the listview structure
7181 * [I] clrTextBk : text background color
7183 * RETURN:
7184 * SUCCESS : TRUE
7185 * FAILURE : FALSE
7187 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7189 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7191 if (infoPtr->clrTextBk != clrTextBk)
7193 infoPtr->clrTextBk = clrTextBk;
7194 LISTVIEW_InvalidateList(infoPtr);
7197 return TRUE;
7200 /***
7201 * DESCRIPTION:
7202 * Sets the text foreground color.
7204 * PARAMETER(S):
7205 * [I] infoPtr : valid pointer to the listview structure
7206 * [I] clrText : text color
7208 * RETURN:
7209 * SUCCESS : TRUE
7210 * FAILURE : FALSE
7212 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7214 TRACE("(clrText=%lx)\n", clrText);
7216 if (infoPtr->clrText != clrText)
7218 infoPtr->clrText = clrText;
7219 LISTVIEW_InvalidateList(infoPtr);
7222 return TRUE;
7225 /***
7226 * DESCRIPTION:
7227 * Determines which listview item is located at the specified position.
7229 * PARAMETER(S):
7230 * [I] infoPtr : valid pointer to the listview structure
7231 * [I] hwndNewToolTip : handle to new ToolTip
7233 * RETURN:
7234 * old tool tip
7236 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7238 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7239 infoPtr->hwndToolTip = hwndNewToolTip;
7240 return hwndOldToolTip;
7243 /* LISTVIEW_SetUnicodeFormat */
7244 /* LISTVIEW_SetWorkAreas */
7246 /***
7247 * DESCRIPTION:
7248 * Callback internally used by LISTVIEW_SortItems()
7250 * PARAMETER(S):
7251 * [I] first : pointer to first ITEM_INFO to compare
7252 * [I] second : pointer to second ITEM_INFO to compare
7253 * [I] lParam : HWND of control
7255 * RETURN:
7256 * if first comes before second : negative
7257 * if first comes after second : positive
7258 * if first and second are equivalent : zero
7260 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7262 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7263 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7264 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7266 /* Forward the call to the client defined callback */
7267 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7270 /***
7271 * DESCRIPTION:
7272 * Sorts the listview items.
7274 * PARAMETER(S):
7275 * [I] infoPtr : valid pointer to the listview structure
7276 * [I] pfnCompare : application-defined value
7277 * [I] lParamSort : pointer to comparision callback
7279 * RETURN:
7280 * SUCCESS : TRUE
7281 * FAILURE : FALSE
7283 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7285 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7286 HDPA hdpaSubItems;
7287 ITEM_INFO *lpItem;
7288 LPVOID selectionMarkItem;
7289 LVITEMW item;
7290 int i;
7292 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7294 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7296 if (!pfnCompare) return FALSE;
7297 if (!infoPtr->hdpaItems) return FALSE;
7299 /* if there are 0 or 1 items, there is no need to sort */
7300 if (infoPtr->nItemCount < 2) return TRUE;
7302 if (infoPtr->nFocusedItem >= 0)
7304 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7305 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7306 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7308 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7309 /* clear the lpItem->state for non-selected ones */
7310 /* remove the selection ranges */
7312 infoPtr->pfnCompare = pfnCompare;
7313 infoPtr->lParamSort = lParamSort;
7314 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7316 /* Adjust selections and indices so that they are the way they should
7317 * be after the sort (otherwise, the list items move around, but
7318 * whatever is at the item's previous original position will be
7319 * selected instead)
7321 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7322 for (i=0; i < infoPtr->nItemCount; i++)
7324 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7325 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7327 if (lpItem->state & LVIS_SELECTED)
7329 item.state = LVIS_SELECTED;
7330 item.stateMask = LVIS_SELECTED;
7331 LISTVIEW_SetItemState(infoPtr, i, &item);
7333 if (lpItem->state & LVIS_FOCUSED)
7335 infoPtr->nFocusedItem = i;
7336 lpItem->state &= ~LVIS_FOCUSED;
7339 if (selectionMarkItem != NULL)
7340 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7341 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7343 /* refresh the display */
7344 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7345 LISTVIEW_InvalidateList(infoPtr);
7347 return TRUE;
7350 /***
7351 * DESCRIPTION:
7352 * Updates an items or rearranges the listview control.
7354 * PARAMETER(S):
7355 * [I] infoPtr : valid pointer to the listview structure
7356 * [I] nItem : item index
7358 * RETURN:
7359 * SUCCESS : TRUE
7360 * FAILURE : FALSE
7362 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7364 TRACE("(nItem=%d)\n", nItem);
7366 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7368 /* rearrange with default alignment style */
7369 if (is_autoarrange(infoPtr))
7370 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7371 else
7372 LISTVIEW_InvalidateItem(infoPtr, nItem);
7374 return TRUE;
7378 /***
7379 * DESCRIPTION:
7380 * Creates the listview control.
7382 * PARAMETER(S):
7383 * [I] hwnd : window handle
7384 * [I] lpcs : the create parameters
7386 * RETURN:
7387 * Success: 0
7388 * Failure: -1
7390 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7392 LISTVIEW_INFO *infoPtr;
7393 UINT uView = lpcs->style & LVS_TYPEMASK;
7394 LOGFONTW logFont;
7396 TRACE("(lpcs=%p)\n", lpcs);
7398 /* initialize info pointer */
7399 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7400 if (!infoPtr) return -1;
7402 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7404 infoPtr->hwndSelf = hwnd;
7405 infoPtr->dwStyle = lpcs->style;
7406 /* determine the type of structures to use */
7407 infoPtr->hwndNotify = lpcs->hwndParent;
7408 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7409 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7411 /* initialize color information */
7412 infoPtr->clrBk = CLR_NONE;
7413 infoPtr->clrText = comctl32_color.clrWindowText;
7414 infoPtr->clrTextBk = CLR_DEFAULT;
7415 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7417 /* set default values */
7418 infoPtr->nFocusedItem = -1;
7419 infoPtr->nSelectionMark = -1;
7420 infoPtr->nHotItem = -1;
7421 infoPtr->bRedraw = TRUE;
7422 infoPtr->bNoItemMetrics = TRUE;
7423 infoPtr->bDoChangeNotify = TRUE;
7424 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7425 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7426 infoPtr->nEditLabelItem = -1;
7427 infoPtr->dwHoverTime = -1; /* default system hover time */
7429 /* get default font (icon title) */
7430 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7431 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7432 infoPtr->hFont = infoPtr->hDefaultFont;
7433 LISTVIEW_SaveTextMetrics(infoPtr);
7435 /* create header */
7436 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7437 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7438 0, 0, 0, 0, hwnd, NULL,
7439 lpcs->hInstance, NULL);
7440 if (!infoPtr->hwndHeader) goto fail;
7442 /* set header unicode format */
7443 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7445 /* set header font */
7446 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7448 /* allocate memory for the data structure */
7449 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7450 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7451 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7452 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7453 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7455 /* initialize the icon sizes */
7456 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7457 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7459 /* init item size to avoid division by 0 */
7460 LISTVIEW_UpdateItemSize (infoPtr);
7462 if (uView == LVS_REPORT)
7464 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7466 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7468 else
7470 /* set HDS_HIDDEN flag to hide the header bar */
7471 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7472 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7476 return 0;
7478 fail:
7479 DestroyWindow(infoPtr->hwndHeader);
7480 ranges_destroy(infoPtr->selectionRanges);
7481 DPA_Destroy(infoPtr->hdpaItems);
7482 DPA_Destroy(infoPtr->hdpaPosX);
7483 DPA_Destroy(infoPtr->hdpaPosY);
7484 DPA_Destroy(infoPtr->hdpaColumns);
7485 Free(infoPtr);
7486 return -1;
7489 /***
7490 * DESCRIPTION:
7491 * Erases the background of the listview control.
7493 * PARAMETER(S):
7494 * [I] infoPtr : valid pointer to the listview structure
7495 * [I] hdc : device context handle
7497 * RETURN:
7498 * SUCCESS : TRUE
7499 * FAILURE : FALSE
7501 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7503 RECT rc;
7505 TRACE("(hdc=%p)\n", hdc);
7507 if (!GetClipBox(hdc, &rc)) return FALSE;
7509 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7513 /***
7514 * DESCRIPTION:
7515 * Helper function for LISTVIEW_[HV]Scroll *only*.
7516 * Performs vertical/horizontal scrolling by a give amount.
7518 * PARAMETER(S):
7519 * [I] infoPtr : valid pointer to the listview structure
7520 * [I] dx : amount of horizontal scroll
7521 * [I] dy : amount of vertical scroll
7523 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7525 /* now we can scroll the list */
7526 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7527 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7528 /* if we have focus, adjust rect */
7529 OffsetRect(&infoPtr->rcFocus, dx, dy);
7530 UpdateWindow(infoPtr->hwndSelf);
7533 /***
7534 * DESCRIPTION:
7535 * Performs vertical scrolling.
7537 * PARAMETER(S):
7538 * [I] infoPtr : valid pointer to the listview structure
7539 * [I] nScrollCode : scroll code
7540 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7541 * [I] hScrollWnd : scrollbar control window handle
7543 * RETURN:
7544 * Zero
7546 * NOTES:
7547 * SB_LINEUP/SB_LINEDOWN:
7548 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7549 * for LVS_REPORT is 1 line
7550 * for LVS_LIST cannot occur
7553 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7554 INT nScrollDiff, HWND hScrollWnd)
7556 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7557 INT nOldScrollPos, nNewScrollPos;
7558 SCROLLINFO scrollInfo;
7559 BOOL is_an_icon;
7561 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7562 debugscrollcode(nScrollCode), nScrollDiff);
7564 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7566 scrollInfo.cbSize = sizeof(SCROLLINFO);
7567 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7569 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7571 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7573 nOldScrollPos = scrollInfo.nPos;
7574 switch (nScrollCode)
7576 case SB_INTERNAL:
7577 break;
7579 case SB_LINEUP:
7580 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7581 break;
7583 case SB_LINEDOWN:
7584 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7585 break;
7587 case SB_PAGEUP:
7588 nScrollDiff = -scrollInfo.nPage;
7589 break;
7591 case SB_PAGEDOWN:
7592 nScrollDiff = scrollInfo.nPage;
7593 break;
7595 case SB_THUMBPOSITION:
7596 case SB_THUMBTRACK:
7597 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7598 break;
7600 default:
7601 nScrollDiff = 0;
7604 /* quit right away if pos isn't changing */
7605 if (nScrollDiff == 0) return 0;
7607 /* calculate new position, and handle overflows */
7608 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7609 if (nScrollDiff > 0) {
7610 if (nNewScrollPos < nOldScrollPos ||
7611 nNewScrollPos > scrollInfo.nMax)
7612 nNewScrollPos = scrollInfo.nMax;
7613 } else {
7614 if (nNewScrollPos > nOldScrollPos ||
7615 nNewScrollPos < scrollInfo.nMin)
7616 nNewScrollPos = scrollInfo.nMin;
7619 /* set the new position, and reread in case it changed */
7620 scrollInfo.fMask = SIF_POS;
7621 scrollInfo.nPos = nNewScrollPos;
7622 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7624 /* carry on only if it really changed */
7625 if (nNewScrollPos == nOldScrollPos) return 0;
7627 /* now adjust to client coordinates */
7628 nScrollDiff = nOldScrollPos - nNewScrollPos;
7629 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7631 /* and scroll the window */
7632 scroll_list(infoPtr, 0, nScrollDiff);
7634 return 0;
7637 /***
7638 * DESCRIPTION:
7639 * Performs horizontal scrolling.
7641 * PARAMETER(S):
7642 * [I] infoPtr : valid pointer to the listview structure
7643 * [I] nScrollCode : scroll code
7644 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7645 * [I] hScrollWnd : scrollbar control window handle
7647 * RETURN:
7648 * Zero
7650 * NOTES:
7651 * SB_LINELEFT/SB_LINERIGHT:
7652 * for LVS_ICON, LVS_SMALLICON 1 pixel
7653 * for LVS_REPORT is 1 pixel
7654 * for LVS_LIST is 1 column --> which is a 1 because the
7655 * scroll is based on columns not pixels
7658 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7659 INT nScrollDiff, HWND hScrollWnd)
7661 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7662 INT nOldScrollPos, nNewScrollPos;
7663 SCROLLINFO scrollInfo;
7665 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7666 debugscrollcode(nScrollCode), nScrollDiff);
7668 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7670 scrollInfo.cbSize = sizeof(SCROLLINFO);
7671 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7673 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7675 nOldScrollPos = scrollInfo.nPos;
7677 switch (nScrollCode)
7679 case SB_INTERNAL:
7680 break;
7682 case SB_LINELEFT:
7683 nScrollDiff = -1;
7684 break;
7686 case SB_LINERIGHT:
7687 nScrollDiff = 1;
7688 break;
7690 case SB_PAGELEFT:
7691 nScrollDiff = -scrollInfo.nPage;
7692 break;
7694 case SB_PAGERIGHT:
7695 nScrollDiff = scrollInfo.nPage;
7696 break;
7698 case SB_THUMBPOSITION:
7699 case SB_THUMBTRACK:
7700 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7701 break;
7703 default:
7704 nScrollDiff = 0;
7707 /* quit right away if pos isn't changing */
7708 if (nScrollDiff == 0) return 0;
7710 /* calculate new position, and handle overflows */
7711 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7712 if (nScrollDiff > 0) {
7713 if (nNewScrollPos < nOldScrollPos ||
7714 nNewScrollPos > scrollInfo.nMax)
7715 nNewScrollPos = scrollInfo.nMax;
7716 } else {
7717 if (nNewScrollPos > nOldScrollPos ||
7718 nNewScrollPos < scrollInfo.nMin)
7719 nNewScrollPos = scrollInfo.nMin;
7722 /* set the new position, and reread in case it changed */
7723 scrollInfo.fMask = SIF_POS;
7724 scrollInfo.nPos = nNewScrollPos;
7725 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7727 /* carry on only if it really changed */
7728 if (nNewScrollPos == nOldScrollPos) return 0;
7730 if(uView == LVS_REPORT)
7731 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7733 /* now adjust to client coordinates */
7734 nScrollDiff = nOldScrollPos - nNewScrollPos;
7735 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7737 /* and scroll the window */
7738 scroll_list(infoPtr, nScrollDiff, 0);
7740 return 0;
7743 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7745 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7746 INT gcWheelDelta = 0;
7747 INT pulScrollLines = 3;
7748 SCROLLINFO scrollInfo;
7750 TRACE("(wheelDelta=%d)\n", wheelDelta);
7752 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7753 gcWheelDelta -= wheelDelta;
7755 scrollInfo.cbSize = sizeof(SCROLLINFO);
7756 scrollInfo.fMask = SIF_POS;
7758 switch(uView)
7760 case LVS_ICON:
7761 case LVS_SMALLICON:
7763 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7764 * should be fixed in the future.
7766 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7767 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7768 break;
7770 case LVS_REPORT:
7771 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7773 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7774 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7775 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7777 break;
7779 case LVS_LIST:
7780 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7781 break;
7783 return 0;
7786 /***
7787 * DESCRIPTION:
7788 * ???
7790 * PARAMETER(S):
7791 * [I] infoPtr : valid pointer to the listview structure
7792 * [I] nVirtualKey : virtual key
7793 * [I] lKeyData : key data
7795 * RETURN:
7796 * Zero
7798 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7800 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7801 INT nItem = -1;
7802 NMLVKEYDOWN nmKeyDown;
7804 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7806 /* send LVN_KEYDOWN notification */
7807 nmKeyDown.wVKey = nVirtualKey;
7808 nmKeyDown.flags = 0;
7809 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7811 switch (nVirtualKey)
7813 case VK_RETURN:
7814 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7816 notify(infoPtr, NM_RETURN);
7817 notify(infoPtr, LVN_ITEMACTIVATE);
7819 break;
7821 case VK_HOME:
7822 if (infoPtr->nItemCount > 0)
7823 nItem = 0;
7824 break;
7826 case VK_END:
7827 if (infoPtr->nItemCount > 0)
7828 nItem = infoPtr->nItemCount - 1;
7829 break;
7831 case VK_LEFT:
7832 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7833 break;
7835 case VK_UP:
7836 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7837 break;
7839 case VK_RIGHT:
7840 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7841 break;
7843 case VK_DOWN:
7844 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7845 break;
7847 case VK_PRIOR:
7848 if (uView == LVS_REPORT)
7849 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7850 else
7851 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7852 * LISTVIEW_GetCountPerRow(infoPtr);
7853 if(nItem < 0) nItem = 0;
7854 break;
7856 case VK_NEXT:
7857 if (uView == LVS_REPORT)
7858 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7859 else
7860 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7861 * LISTVIEW_GetCountPerRow(infoPtr);
7862 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7863 break;
7866 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7867 LISTVIEW_KeySelection(infoPtr, nItem);
7869 return 0;
7872 /***
7873 * DESCRIPTION:
7874 * Kills the focus.
7876 * PARAMETER(S):
7877 * [I] infoPtr : valid pointer to the listview structure
7879 * RETURN:
7880 * Zero
7882 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7884 TRACE("()\n");
7886 /* if we did not have the focus, there's nothing to do */
7887 if (!infoPtr->bFocus) return 0;
7889 /* send NM_KILLFOCUS notification */
7890 notify(infoPtr, NM_KILLFOCUS);
7892 /* if we have a focus rectagle, get rid of it */
7893 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7895 /* set window focus flag */
7896 infoPtr->bFocus = FALSE;
7898 /* invalidate the selected items before reseting focus flag */
7899 LISTVIEW_InvalidateSelectedItems(infoPtr);
7901 return 0;
7905 /***
7906 * DESCRIPTION:
7907 * Track mouse/dragging
7909 * PARAMETER(S):
7910 * [I] infoPtr : valid pointer to the listview structure
7911 * [I] pt : mouse coordinate
7913 * RETURN:
7914 * Zero
7916 static LRESULT LISTVIEW_TrackMouse(LISTVIEW_INFO *infoPtr, POINT pt)
7918 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
7919 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
7920 RECT r;
7921 MSG msg;
7923 TRACE("\n");
7925 r.top = pt.y - cyDrag;
7926 r.left = pt.x - cxDrag;
7927 r.bottom = pt.y + cyDrag;
7928 r.right = pt.x + cxDrag;
7930 SetCapture(infoPtr->hwndSelf);
7932 while (1)
7934 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
7936 if (msg.message == WM_MOUSEMOVE)
7938 pt.x = (short)LOWORD(msg.lParam);
7939 pt.y = (short)HIWORD(msg.lParam);
7940 if (PtInRect(&r, pt))
7941 continue;
7942 else
7944 ReleaseCapture();
7945 return 1;
7948 else if (msg.message >= WM_LBUTTONDOWN &&
7949 msg.message <= WM_RBUTTONDBLCLK)
7951 break;
7954 DispatchMessageW(&msg);
7957 if (GetCapture() != infoPtr->hwndSelf)
7958 return 0;
7961 ReleaseCapture();
7962 return 0;
7966 /***
7967 * DESCRIPTION:
7968 * Processes double click messages (left mouse button).
7970 * PARAMETER(S):
7971 * [I] infoPtr : valid pointer to the listview structure
7972 * [I] wKey : key flag
7973 * [I] x,y : mouse coordinate
7975 * RETURN:
7976 * Zero
7978 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
7980 LVHITTESTINFO htInfo;
7982 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
7984 /* send NM_RELEASEDCAPTURE notification */
7985 notify(infoPtr, NM_RELEASEDCAPTURE);
7987 htInfo.pt.x = x;
7988 htInfo.pt.y = y;
7990 /* send NM_DBLCLK notification */
7991 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7992 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7994 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7995 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
7997 return 0;
8000 /***
8001 * DESCRIPTION:
8002 * Processes mouse down messages (left mouse button).
8004 * PARAMETER(S):
8005 * [I] infoPtr : valid pointer to the listview structure
8006 * [I] wKey : key flag
8007 * [I] x,y : mouse coordinate
8009 * RETURN:
8010 * Zero
8012 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8014 LVHITTESTINFO lvHitTestInfo;
8015 static BOOL bGroupSelect = TRUE;
8016 POINT pt = { x, y };
8017 INT nItem;
8019 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8021 /* send NM_RELEASEDCAPTURE notification */
8022 notify(infoPtr, NM_RELEASEDCAPTURE);
8024 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8026 /* set left button down flag */
8027 infoPtr->bLButtonDown = TRUE;
8029 lvHitTestInfo.pt.x = x;
8030 lvHitTestInfo.pt.y = y;
8032 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8033 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8034 infoPtr->nEditLabelItem = -1;
8035 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8037 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8039 DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
8040 if(state == 1 || state == 2)
8042 LVITEMW lvitem;
8043 state ^= 3;
8044 lvitem.state = state << 12;
8045 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8046 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8048 return 0;
8050 if (LISTVIEW_TrackMouse(infoPtr, lvHitTestInfo.pt))
8052 NMLISTVIEW nmlv;
8054 ZeroMemory(&nmlv, sizeof(nmlv));
8055 nmlv.iItem = nItem;
8056 nmlv.ptAction.x = lvHitTestInfo.pt.x;
8057 nmlv.ptAction.y = lvHitTestInfo.pt.y;
8059 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
8061 return 0;
8064 if (infoPtr->dwStyle & LVS_SINGLESEL)
8066 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8067 infoPtr->nEditLabelItem = nItem;
8068 else
8069 LISTVIEW_SetSelection(infoPtr, nItem);
8071 else
8073 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8075 if (bGroupSelect)
8077 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8078 LISTVIEW_SetItemFocus(infoPtr, nItem);
8079 infoPtr->nSelectionMark = nItem;
8081 else
8083 LVITEMW item;
8085 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8086 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8088 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8089 infoPtr->nSelectionMark = nItem;
8092 else if (wKey & MK_CONTROL)
8094 LVITEMW item;
8096 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8098 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8099 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8100 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8101 infoPtr->nSelectionMark = nItem;
8103 else if (wKey & MK_SHIFT)
8105 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8107 else
8109 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8110 infoPtr->nEditLabelItem = nItem;
8112 /* set selection (clears other pre-existing selections) */
8113 LISTVIEW_SetSelection(infoPtr, nItem);
8117 else
8119 /* remove all selections */
8120 LISTVIEW_DeselectAll(infoPtr);
8121 ReleaseCapture();
8124 return 0;
8127 /***
8128 * DESCRIPTION:
8129 * Processes mouse up messages (left mouse button).
8131 * PARAMETER(S):
8132 * [I] infoPtr : valid pointer to the listview structure
8133 * [I] wKey : key flag
8134 * [I] x,y : mouse coordinate
8136 * RETURN:
8137 * Zero
8139 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8141 LVHITTESTINFO lvHitTestInfo;
8143 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8145 if (!infoPtr->bLButtonDown) return 0;
8147 lvHitTestInfo.pt.x = x;
8148 lvHitTestInfo.pt.y = y;
8150 /* send NM_CLICK notification */
8151 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8152 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8154 /* set left button flag */
8155 infoPtr->bLButtonDown = FALSE;
8157 /* if we clicked on a selected item, edit the label */
8158 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8159 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8161 return 0;
8164 /***
8165 * DESCRIPTION:
8166 * Destroys the listview control (called after WM_DESTROY).
8168 * PARAMETER(S):
8169 * [I] infoPtr : valid pointer to the listview structure
8171 * RETURN:
8172 * Zero
8174 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8176 TRACE("()\n");
8178 /* delete all items */
8179 LISTVIEW_DeleteAllItems(infoPtr);
8181 /* destroy data structure */
8182 DPA_Destroy(infoPtr->hdpaItems);
8183 DPA_Destroy(infoPtr->hdpaPosX);
8184 DPA_Destroy(infoPtr->hdpaPosY);
8185 DPA_Destroy(infoPtr->hdpaColumns);
8186 ranges_destroy(infoPtr->selectionRanges);
8188 /* destroy image lists */
8189 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8191 if (infoPtr->himlNormal)
8192 ImageList_Destroy(infoPtr->himlNormal);
8193 if (infoPtr->himlSmall)
8194 ImageList_Destroy(infoPtr->himlSmall);
8195 if (infoPtr->himlState)
8196 ImageList_Destroy(infoPtr->himlState);
8199 /* destroy font, bkgnd brush */
8200 infoPtr->hFont = 0;
8201 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8202 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8204 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8206 /* free listview info pointer*/
8207 Free(infoPtr);
8209 return 0;
8212 /***
8213 * DESCRIPTION:
8214 * Handles notifications from header.
8216 * PARAMETER(S):
8217 * [I] infoPtr : valid pointer to the listview structure
8218 * [I] nCtrlId : control identifier
8219 * [I] lpnmh : notification information
8221 * RETURN:
8222 * Zero
8224 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8226 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8228 TRACE("(lpnmh=%p)\n", lpnmh);
8230 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8232 switch (lpnmh->hdr.code)
8234 case HDN_TRACKW:
8235 case HDN_TRACKA:
8236 case HDN_ITEMCHANGEDW:
8237 case HDN_ITEMCHANGEDA:
8239 COLUMN_INFO *lpColumnInfo;
8240 INT dx, cxy;
8242 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8244 HDITEMW hdi;
8246 hdi.mask = HDI_WIDTH;
8247 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8248 cxy = hdi.cxy;
8250 else
8251 cxy = lpnmh->pitem->cxy;
8253 /* determine how much we change since the last know position */
8254 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8255 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8256 if (dx != 0)
8258 RECT rcCol = lpColumnInfo->rcHeader;
8260 lpColumnInfo->rcHeader.right += dx;
8261 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8262 LISTVIEW_UpdateItemSize(infoPtr);
8263 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8265 /* this trick works for left aligned columns only */
8266 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8268 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8269 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8271 rcCol.top = infoPtr->rcList.top;
8272 rcCol.bottom = infoPtr->rcList.bottom;
8273 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8277 break;
8279 case HDN_ITEMCLICKW:
8280 case HDN_ITEMCLICKA:
8282 /* Handle sorting by Header Column */
8283 NMLISTVIEW nmlv;
8285 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8286 nmlv.iItem = -1;
8287 nmlv.iSubItem = lpnmh->iItem;
8288 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8290 break;
8293 return 0;
8296 /***
8297 * DESCRIPTION:
8298 * Determines the type of structure to use.
8300 * PARAMETER(S):
8301 * [I] infoPtr : valid pointer to the listview structureof the sender
8302 * [I] hwndFrom : listview window handle
8303 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8305 * RETURN:
8306 * Zero
8308 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8310 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8312 if (nCommand != NF_REQUERY) return 0;
8314 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8316 return 0;
8319 /***
8320 * DESCRIPTION:
8321 * Paints/Repaints the listview control.
8323 * PARAMETER(S):
8324 * [I] infoPtr : valid pointer to the listview structure
8325 * [I] hdc : device context handle
8327 * RETURN:
8328 * Zero
8330 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8332 TRACE("(hdc=%p)\n", hdc);
8334 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8336 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8338 infoPtr->bNoItemMetrics = FALSE;
8339 LISTVIEW_UpdateItemSize(infoPtr);
8340 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8341 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8342 LISTVIEW_UpdateScroll(infoPtr);
8344 if (hdc)
8345 LISTVIEW_Refresh(infoPtr, hdc);
8346 else
8348 PAINTSTRUCT ps;
8350 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8351 if (!hdc) return 1;
8352 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8353 LISTVIEW_Refresh(infoPtr, hdc);
8354 EndPaint(infoPtr->hwndSelf, &ps);
8357 return 0;
8361 /***
8362 * DESCRIPTION:
8363 * Paints/Repaints the listview control.
8365 * PARAMETER(S):
8366 * [I] infoPtr : valid pointer to the listview structure
8367 * [I] hdc : device context handle
8368 * [I] options : drawing options
8370 * RETURN:
8371 * Zero
8373 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8375 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8377 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8378 return 0;
8380 if (options & PRF_ERASEBKGND)
8381 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8383 if (options & PRF_CLIENT)
8384 LISTVIEW_Paint(infoPtr, hdc);
8386 return 0;
8390 /***
8391 * DESCRIPTION:
8392 * Processes double click messages (right mouse button).
8394 * PARAMETER(S):
8395 * [I] infoPtr : valid pointer to the listview structure
8396 * [I] wKey : key flag
8397 * [I] x,y : mouse coordinate
8399 * RETURN:
8400 * Zero
8402 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8404 LVHITTESTINFO lvHitTestInfo;
8406 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8408 /* send NM_RELEASEDCAPTURE notification */
8409 notify(infoPtr, NM_RELEASEDCAPTURE);
8411 /* send NM_RDBLCLK notification */
8412 lvHitTestInfo.pt.x = x;
8413 lvHitTestInfo.pt.y = y;
8414 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8415 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8417 return 0;
8420 /***
8421 * DESCRIPTION:
8422 * Processes mouse down messages (right mouse button).
8424 * PARAMETER(S):
8425 * [I] infoPtr : valid pointer to the listview structure
8426 * [I] wKey : key flag
8427 * [I] x,y : mouse coordinate
8429 * RETURN:
8430 * Zero
8432 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8434 LVHITTESTINFO lvHitTestInfo;
8435 INT nItem;
8437 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8439 /* send NM_RELEASEDCAPTURE notification */
8440 notify(infoPtr, NM_RELEASEDCAPTURE);
8442 /* make sure the listview control window has the focus */
8443 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8445 /* set right button down flag */
8446 infoPtr->bRButtonDown = TRUE;
8448 /* determine the index of the selected item */
8449 lvHitTestInfo.pt.x = x;
8450 lvHitTestInfo.pt.y = y;
8451 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8453 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8455 LISTVIEW_SetItemFocus(infoPtr, nItem);
8456 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8457 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8458 LISTVIEW_SetSelection(infoPtr, nItem);
8460 else
8462 LISTVIEW_DeselectAll(infoPtr);
8465 return 0;
8468 /***
8469 * DESCRIPTION:
8470 * Processes mouse up messages (right mouse button).
8472 * PARAMETER(S):
8473 * [I] infoPtr : valid pointer to the listview structure
8474 * [I] wKey : key flag
8475 * [I] x,y : mouse coordinate
8477 * RETURN:
8478 * Zero
8480 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8482 LVHITTESTINFO lvHitTestInfo;
8483 POINT pt;
8485 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8487 if (!infoPtr->bRButtonDown) return 0;
8489 /* set button flag */
8490 infoPtr->bRButtonDown = FALSE;
8492 /* Send NM_RClICK notification */
8493 lvHitTestInfo.pt.x = x;
8494 lvHitTestInfo.pt.y = y;
8495 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8496 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8498 /* Change to screen coordinate for WM_CONTEXTMENU */
8499 pt = lvHitTestInfo.pt;
8500 ClientToScreen(infoPtr->hwndSelf, &pt);
8502 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8503 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8504 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8506 return 0;
8510 /***
8511 * DESCRIPTION:
8512 * Sets the cursor.
8514 * PARAMETER(S):
8515 * [I] infoPtr : valid pointer to the listview structure
8516 * [I] hwnd : window handle of window containing the cursor
8517 * [I] nHittest : hit-test code
8518 * [I] wMouseMsg : ideintifier of the mouse message
8520 * RETURN:
8521 * TRUE if cursor is set
8522 * FALSE otherwise
8524 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8526 LVHITTESTINFO lvHitTestInfo;
8528 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8530 if(!infoPtr->hHotCursor) return FALSE;
8532 GetCursorPos(&lvHitTestInfo.pt);
8533 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8535 SetCursor(infoPtr->hHotCursor);
8537 return TRUE;
8540 /***
8541 * DESCRIPTION:
8542 * Sets the focus.
8544 * PARAMETER(S):
8545 * [I] infoPtr : valid pointer to the listview structure
8546 * [I] hwndLoseFocus : handle of previously focused window
8548 * RETURN:
8549 * Zero
8551 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8553 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8555 /* if we have the focus already, there's nothing to do */
8556 if (infoPtr->bFocus) return 0;
8558 /* send NM_SETFOCUS notification */
8559 notify(infoPtr, NM_SETFOCUS);
8561 /* set window focus flag */
8562 infoPtr->bFocus = TRUE;
8564 /* put the focus rect back on */
8565 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8567 /* redraw all visible selected items */
8568 LISTVIEW_InvalidateSelectedItems(infoPtr);
8570 return 0;
8573 /***
8574 * DESCRIPTION:
8575 * Sets the font.
8577 * PARAMETER(S):
8578 * [I] infoPtr : valid pointer to the listview structure
8579 * [I] fRedraw : font handle
8580 * [I] fRedraw : redraw flag
8582 * RETURN:
8583 * Zero
8585 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8587 HFONT oldFont = infoPtr->hFont;
8589 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8591 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8592 if (infoPtr->hFont == oldFont) return 0;
8594 LISTVIEW_SaveTextMetrics(infoPtr);
8596 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8597 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8599 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8601 return 0;
8604 /***
8605 * DESCRIPTION:
8606 * Message handling for WM_SETREDRAW.
8607 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8609 * PARAMETER(S):
8610 * [I] infoPtr : valid pointer to the listview structure
8611 * [I] bRedraw: state of redraw flag
8613 * RETURN:
8614 * DefWinProc return value
8616 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8618 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8620 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8621 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8623 infoPtr->bRedraw = bRedraw;
8625 if(!bRedraw) return 0;
8627 if (is_autoarrange(infoPtr))
8628 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8629 LISTVIEW_UpdateScroll(infoPtr);
8631 /* despite what the WM_SETREDRAW docs says, apps expect us
8632 * to invalidate the listview here... stupid! */
8633 LISTVIEW_InvalidateList(infoPtr);
8635 return 0;
8638 /***
8639 * DESCRIPTION:
8640 * Resizes the listview control. This function processes WM_SIZE
8641 * messages. At this time, the width and height are not used.
8643 * PARAMETER(S):
8644 * [I] infoPtr : valid pointer to the listview structure
8645 * [I] Width : new width
8646 * [I] Height : new height
8648 * RETURN:
8649 * Zero
8651 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8653 RECT rcOld = infoPtr->rcList;
8655 TRACE("(width=%d, height=%d)\n", Width, Height);
8657 LISTVIEW_UpdateSize(infoPtr);
8658 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8660 /* do not bother with display related stuff if we're not redrawing */
8661 if (!is_redrawing(infoPtr)) return 0;
8663 if (is_autoarrange(infoPtr))
8664 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8666 LISTVIEW_UpdateScroll(infoPtr);
8668 /* refresh all only for lists whose height changed significantly */
8669 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8670 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8671 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8672 LISTVIEW_InvalidateList(infoPtr);
8674 return 0;
8677 /***
8678 * DESCRIPTION:
8679 * Sets the size information.
8681 * PARAMETER(S):
8682 * [I] infoPtr : valid pointer to the listview structure
8684 * RETURN:
8685 * None
8687 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8689 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8691 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8693 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8695 if (uView == LVS_LIST)
8697 /* Apparently the "LIST" style is supposed to have the same
8698 * number of items in a column even if there is no scroll bar.
8699 * Since if a scroll bar already exists then the bottom is already
8700 * reduced, only reduce if the scroll bar does not currently exist.
8701 * The "2" is there to mimic the native control. I think it may be
8702 * related to either padding or edges. (GLA 7/2002)
8704 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8705 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8706 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8708 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8710 HDLAYOUT hl;
8711 WINDOWPOS wp;
8713 hl.prc = &infoPtr->rcList;
8714 hl.pwpos = &wp;
8715 Header_Layout(infoPtr->hwndHeader, &hl);
8717 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8719 infoPtr->rcList.top = max(wp.cy, 0);
8722 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8725 /***
8726 * DESCRIPTION:
8727 * Processes WM_STYLECHANGED messages.
8729 * PARAMETER(S):
8730 * [I] infoPtr : valid pointer to the listview structure
8731 * [I] wStyleType : window style type (normal or extended)
8732 * [I] lpss : window style information
8734 * RETURN:
8735 * Zero
8737 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8738 const STYLESTRUCT *lpss)
8740 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8741 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8743 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8744 wStyleType, lpss->styleOld, lpss->styleNew);
8746 if (wStyleType != GWL_STYLE) return 0;
8748 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8749 /* what if LVS_OWNERDATA changed? */
8750 /* or LVS_SINGLESEL */
8751 /* or LVS_SORT{AS,DES}CENDING */
8753 infoPtr->dwStyle = lpss->styleNew;
8755 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8756 ((lpss->styleNew & WS_HSCROLL) == 0))
8757 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8759 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8760 ((lpss->styleNew & WS_VSCROLL) == 0))
8761 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8763 if (uNewView != uOldView)
8765 SIZE oldIconSize = infoPtr->iconSize;
8766 HIMAGELIST himl;
8768 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8769 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8771 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8772 SetRectEmpty(&infoPtr->rcFocus);
8774 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8775 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8777 if (uNewView == LVS_ICON)
8779 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8781 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8782 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8783 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8786 else if (uNewView == LVS_REPORT)
8788 HDLAYOUT hl;
8789 WINDOWPOS wp;
8791 hl.prc = &infoPtr->rcList;
8792 hl.pwpos = &wp;
8793 Header_Layout(infoPtr->hwndHeader, &hl);
8794 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8797 LISTVIEW_UpdateItemSize(infoPtr);
8800 if (uNewView == LVS_REPORT)
8801 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8803 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8804 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8805 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8807 /* update the size of the client area */
8808 LISTVIEW_UpdateSize(infoPtr);
8810 /* add scrollbars if needed */
8811 LISTVIEW_UpdateScroll(infoPtr);
8813 /* invalidate client area + erase background */
8814 LISTVIEW_InvalidateList(infoPtr);
8816 return 0;
8819 /***
8820 * DESCRIPTION:
8821 * Window procedure of the listview control.
8824 static LRESULT WINAPI
8825 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8827 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8829 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8831 if (!infoPtr && (uMsg != WM_CREATE))
8832 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8834 switch (uMsg)
8836 case LVM_APPROXIMATEVIEWRECT:
8837 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8838 LOWORD(lParam), HIWORD(lParam));
8839 case LVM_ARRANGE:
8840 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8842 /* case LVM_CANCELEDITLABEL: */
8844 case LVM_CREATEDRAGIMAGE:
8845 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8847 case LVM_DELETEALLITEMS:
8848 return LISTVIEW_DeleteAllItems(infoPtr);
8850 case LVM_DELETECOLUMN:
8851 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8853 case LVM_DELETEITEM:
8854 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8856 case LVM_EDITLABELW:
8857 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8859 case LVM_EDITLABELA:
8860 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8862 /* case LVM_ENABLEGROUPVIEW: */
8864 case LVM_ENSUREVISIBLE:
8865 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8867 case LVM_FINDITEMW:
8868 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8870 case LVM_FINDITEMA:
8871 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8873 case LVM_GETBKCOLOR:
8874 return infoPtr->clrBk;
8876 /* case LVM_GETBKIMAGE: */
8878 case LVM_GETCALLBACKMASK:
8879 return infoPtr->uCallbackMask;
8881 case LVM_GETCOLUMNA:
8882 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8884 case LVM_GETCOLUMNW:
8885 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8887 case LVM_GETCOLUMNORDERARRAY:
8888 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8890 case LVM_GETCOLUMNWIDTH:
8891 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8893 case LVM_GETCOUNTPERPAGE:
8894 return LISTVIEW_GetCountPerPage(infoPtr);
8896 case LVM_GETEDITCONTROL:
8897 return (LRESULT)infoPtr->hwndEdit;
8899 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8900 return infoPtr->dwLvExStyle;
8902 /* case LVM_GETGROUPINFO: */
8904 /* case LVM_GETGROUPMETRICS: */
8906 case LVM_GETHEADER:
8907 return (LRESULT)infoPtr->hwndHeader;
8909 case LVM_GETHOTCURSOR:
8910 return (LRESULT)infoPtr->hHotCursor;
8912 case LVM_GETHOTITEM:
8913 return infoPtr->nHotItem;
8915 case LVM_GETHOVERTIME:
8916 return infoPtr->dwHoverTime;
8918 case LVM_GETIMAGELIST:
8919 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8921 /* case LVM_GETINSERTMARK: */
8923 /* case LVM_GETINSERTMARKCOLOR: */
8925 /* case LVM_GETINSERTMARKRECT: */
8927 case LVM_GETISEARCHSTRINGA:
8928 case LVM_GETISEARCHSTRINGW:
8929 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8930 return FALSE;
8932 case LVM_GETITEMA:
8933 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8935 case LVM_GETITEMW:
8936 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8938 case LVM_GETITEMCOUNT:
8939 return infoPtr->nItemCount;
8941 case LVM_GETITEMPOSITION:
8942 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8944 case LVM_GETITEMRECT:
8945 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8947 case LVM_GETITEMSPACING:
8948 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8950 case LVM_GETITEMSTATE:
8951 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8953 case LVM_GETITEMTEXTA:
8954 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8956 case LVM_GETITEMTEXTW:
8957 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8959 case LVM_GETNEXTITEM:
8960 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8962 case LVM_GETNUMBEROFWORKAREAS:
8963 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8964 return 1;
8966 case LVM_GETORIGIN:
8967 if (!lParam) return FALSE;
8968 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8969 return TRUE;
8971 /* case LVM_GETOUTLINECOLOR: */
8973 /* case LVM_GETSELECTEDCOLUMN: */
8975 case LVM_GETSELECTEDCOUNT:
8976 return LISTVIEW_GetSelectedCount(infoPtr);
8978 case LVM_GETSELECTIONMARK:
8979 return infoPtr->nSelectionMark;
8981 case LVM_GETSTRINGWIDTHA:
8982 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8984 case LVM_GETSTRINGWIDTHW:
8985 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8987 case LVM_GETSUBITEMRECT:
8988 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8990 case LVM_GETTEXTBKCOLOR:
8991 return infoPtr->clrTextBk;
8993 case LVM_GETTEXTCOLOR:
8994 return infoPtr->clrText;
8996 /* case LVM_GETTILEINFO: */
8998 /* case LVM_GETTILEVIEWINFO: */
9000 case LVM_GETTOOLTIPS:
9001 if( !infoPtr->hwndToolTip )
9002 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9003 return (LRESULT)infoPtr->hwndToolTip;
9005 case LVM_GETTOPINDEX:
9006 return LISTVIEW_GetTopIndex(infoPtr);
9008 /*case LVM_GETUNICODEFORMAT:
9009 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9010 return FALSE;*/
9012 /* case LVM_GETVIEW: */
9014 case LVM_GETVIEWRECT:
9015 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9017 case LVM_GETWORKAREAS:
9018 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9019 return FALSE;
9021 /* case LVM_HASGROUP: */
9023 case LVM_HITTEST:
9024 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9026 case LVM_INSERTCOLUMNA:
9027 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9029 case LVM_INSERTCOLUMNW:
9030 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9032 /* case LVM_INSERTGROUP: */
9034 /* case LVM_INSERTGROUPSORTED: */
9036 case LVM_INSERTITEMA:
9037 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9039 case LVM_INSERTITEMW:
9040 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9042 /* case LVM_INSERTMARKHITTEST: */
9044 /* case LVM_ISGROUPVIEWENABLED: */
9046 /* case LVM_MAPIDTOINDEX: */
9048 /* case LVM_MAPINDEXTOID: */
9050 /* case LVM_MOVEGROUP: */
9052 /* case LVM_MOVEITEMTOGROUP: */
9054 case LVM_REDRAWITEMS:
9055 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9057 /* case LVM_REMOVEALLGROUPS: */
9059 /* case LVM_REMOVEGROUP: */
9061 case LVM_SCROLL:
9062 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9064 case LVM_SETBKCOLOR:
9065 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9067 /* case LVM_SETBKIMAGE: */
9069 case LVM_SETCALLBACKMASK:
9070 infoPtr->uCallbackMask = (UINT)wParam;
9071 return TRUE;
9073 case LVM_SETCOLUMNA:
9074 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9076 case LVM_SETCOLUMNW:
9077 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9079 case LVM_SETCOLUMNORDERARRAY:
9080 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9082 case LVM_SETCOLUMNWIDTH:
9083 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9085 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9086 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9088 /* case LVM_SETGROUPINFO: */
9090 /* case LVM_SETGROUPMETRICS: */
9092 case LVM_SETHOTCURSOR:
9093 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9095 case LVM_SETHOTITEM:
9096 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9098 case LVM_SETHOVERTIME:
9099 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9101 case LVM_SETICONSPACING:
9102 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9104 case LVM_SETIMAGELIST:
9105 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9107 /* case LVM_SETINFOTIP: */
9109 /* case LVM_SETINSERTMARK: */
9111 /* case LVM_SETINSERTMARKCOLOR: */
9113 case LVM_SETITEMA:
9114 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9116 case LVM_SETITEMW:
9117 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9119 case LVM_SETITEMCOUNT:
9120 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9122 case LVM_SETITEMPOSITION:
9124 POINT pt;
9125 pt.x = (short)LOWORD(lParam);
9126 pt.y = (short)HIWORD(lParam);
9127 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9130 case LVM_SETITEMPOSITION32:
9131 if (lParam == 0) return FALSE;
9132 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9134 case LVM_SETITEMSTATE:
9135 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9137 case LVM_SETITEMTEXTA:
9138 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9140 case LVM_SETITEMTEXTW:
9141 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9143 /* case LVM_SETOUTLINECOLOR: */
9145 /* case LVM_SETSELECTEDCOLUMN: */
9147 case LVM_SETSELECTIONMARK:
9148 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9150 case LVM_SETTEXTBKCOLOR:
9151 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9153 case LVM_SETTEXTCOLOR:
9154 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9156 /* case LVM_SETTILEINFO: */
9158 /* case LVM_SETTILEVIEWINFO: */
9160 /* case LVM_SETTILEWIDTH: */
9162 case LVM_SETTOOLTIPS:
9163 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9165 /* case LVM_SETUNICODEFORMAT: */
9167 /* case LVM_SETVIEW: */
9169 /* case LVM_SETWORKAREAS: */
9171 /* case LVM_SORTGROUPS: */
9173 case LVM_SORTITEMS:
9174 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9176 /* LVM_SORTITEMSEX: */
9178 case LVM_SUBITEMHITTEST:
9179 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9181 case LVM_UPDATE:
9182 return LISTVIEW_Update(infoPtr, (INT)wParam);
9184 case WM_CHAR:
9185 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9187 case WM_COMMAND:
9188 return LISTVIEW_Command(infoPtr, wParam, lParam);
9190 case WM_CREATE:
9191 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9193 case WM_ERASEBKGND:
9194 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9196 case WM_GETDLGCODE:
9197 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9199 case WM_GETFONT:
9200 return (LRESULT)infoPtr->hFont;
9202 case WM_HSCROLL:
9203 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9205 case WM_KEYDOWN:
9206 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9208 case WM_KILLFOCUS:
9209 return LISTVIEW_KillFocus(infoPtr);
9211 case WM_LBUTTONDBLCLK:
9212 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9214 case WM_LBUTTONDOWN:
9215 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9217 case WM_LBUTTONUP:
9218 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9220 case WM_MOUSEMOVE:
9221 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9223 case WM_MOUSEHOVER:
9224 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9226 case WM_NCDESTROY:
9227 return LISTVIEW_NCDestroy(infoPtr);
9229 case WM_NOTIFY:
9230 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9231 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9232 else return 0;
9234 case WM_NOTIFYFORMAT:
9235 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9237 case WM_PRINTCLIENT:
9238 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9240 case WM_PAINT:
9241 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9243 case WM_RBUTTONDBLCLK:
9244 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9246 case WM_RBUTTONDOWN:
9247 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9249 case WM_RBUTTONUP:
9250 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9252 case WM_SETCURSOR:
9253 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9254 return TRUE;
9255 goto fwd_msg;
9257 case WM_SETFOCUS:
9258 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9260 case WM_SETFONT:
9261 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9263 case WM_SETREDRAW:
9264 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9266 case WM_SIZE:
9267 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9269 case WM_STYLECHANGED:
9270 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9272 case WM_SYSCOLORCHANGE:
9273 COMCTL32_RefreshSysColors();
9274 return 0;
9276 /* case WM_TIMER: */
9278 case WM_VSCROLL:
9279 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9281 case WM_MOUSEWHEEL:
9282 if (wParam & (MK_SHIFT | MK_CONTROL))
9283 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9284 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9286 case WM_WINDOWPOSCHANGED:
9287 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9289 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9290 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9291 LISTVIEW_UpdateSize(infoPtr);
9292 LISTVIEW_UpdateScroll(infoPtr);
9294 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9296 /* case WM_WININICHANGE: */
9298 default:
9299 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9300 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9302 fwd_msg:
9303 /* call default window procedure */
9304 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9309 /***
9310 * DESCRIPTION:
9311 * Registers the window class.
9313 * PARAMETER(S):
9314 * None
9316 * RETURN:
9317 * None
9319 void LISTVIEW_Register(void)
9321 WNDCLASSW wndClass;
9323 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9324 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9325 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9326 wndClass.cbClsExtra = 0;
9327 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9328 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9329 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9330 wndClass.lpszClassName = WC_LISTVIEWW;
9331 RegisterClassW(&wndClass);
9334 /***
9335 * DESCRIPTION:
9336 * Unregisters the window class.
9338 * PARAMETER(S):
9339 * None
9341 * RETURN:
9342 * None
9344 void LISTVIEW_Unregister(void)
9346 UnregisterClassW(WC_LISTVIEWW, NULL);
9349 /***
9350 * DESCRIPTION:
9351 * Handle any WM_COMMAND messages
9353 * PARAMETER(S):
9354 * [I] infoPtr : valid pointer to the listview structure
9355 * [I] wParam : the first message parameter
9356 * [I] lParam : the second message parameter
9358 * RETURN:
9359 * Zero.
9361 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9363 switch (HIWORD(wParam))
9365 case EN_UPDATE:
9368 * Adjust the edit window size
9370 WCHAR buffer[1024];
9371 HDC hdc = GetDC(infoPtr->hwndEdit);
9372 HFONT hFont, hOldFont = 0;
9373 RECT rect;
9374 SIZE sz;
9375 int len;
9377 if (!infoPtr->hwndEdit || !hdc) return 0;
9378 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9379 GetWindowRect(infoPtr->hwndEdit, &rect);
9381 /* Select font to get the right dimension of the string */
9382 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9383 if(hFont != 0)
9385 hOldFont = SelectObject(hdc, hFont);
9388 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9390 TEXTMETRICW textMetric;
9392 /* Add Extra spacing for the next character */
9393 GetTextMetricsW(hdc, &textMetric);
9394 sz.cx += (textMetric.tmMaxCharWidth * 2);
9396 SetWindowPos (
9397 infoPtr->hwndEdit,
9398 HWND_TOP,
9401 sz.cx,
9402 rect.bottom - rect.top,
9403 SWP_DRAWFRAME|SWP_NOMOVE);
9405 if(hFont != 0)
9406 SelectObject(hdc, hOldFont);
9408 ReleaseDC(infoPtr->hwndSelf, hdc);
9410 break;
9413 default:
9414 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9417 return 0;
9421 /***
9422 * DESCRIPTION:
9423 * Subclassed edit control windproc function
9425 * PARAMETER(S):
9426 * [I] hwnd : the edit window handle
9427 * [I] uMsg : the message that is to be processed
9428 * [I] wParam : first message parameter
9429 * [I] lParam : second message parameter
9430 * [I] isW : TRUE if input is Unicode
9432 * RETURN:
9433 * Zero.
9435 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9437 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9438 BOOL cancel = FALSE;
9440 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9441 hwnd, uMsg, wParam, lParam, isW);
9443 switch (uMsg)
9445 case WM_GETDLGCODE:
9446 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9448 case WM_KILLFOCUS:
9449 break;
9451 case WM_DESTROY:
9453 WNDPROC editProc = infoPtr->EditWndProc;
9454 infoPtr->EditWndProc = 0;
9455 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9456 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9459 case WM_KEYDOWN:
9460 if (VK_ESCAPE == (INT)wParam)
9462 cancel = TRUE;
9463 break;
9465 else if (VK_RETURN == (INT)wParam)
9466 break;
9468 default:
9469 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9472 /* kill the edit */
9473 if (infoPtr->hwndEdit)
9475 LPWSTR buffer = NULL;
9477 infoPtr->hwndEdit = 0;
9478 if (!cancel)
9480 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9482 if (len)
9484 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9486 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9487 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9491 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9493 if (buffer) Free(buffer);
9497 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9498 return 0;
9501 /***
9502 * DESCRIPTION:
9503 * Subclassed edit control Unicode windproc function
9505 * PARAMETER(S):
9506 * [I] hwnd : the edit window handle
9507 * [I] uMsg : the message that is to be processed
9508 * [I] wParam : first message parameter
9509 * [I] lParam : second message parameter
9511 * RETURN:
9513 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9515 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9518 /***
9519 * DESCRIPTION:
9520 * Subclassed edit control ANSI windproc function
9522 * PARAMETER(S):
9523 * [I] hwnd : the edit window handle
9524 * [I] uMsg : the message that is to be processed
9525 * [I] wParam : first message parameter
9526 * [I] lParam : second message parameter
9528 * RETURN:
9530 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9532 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9535 /***
9536 * DESCRIPTION:
9537 * Creates a subclassed edit cotrol
9539 * PARAMETER(S):
9540 * [I] infoPtr : valid pointer to the listview structure
9541 * [I] text : initial text for the edit
9542 * [I] style : the window style
9543 * [I] isW : TRUE if input is Unicode
9545 * RETURN:
9547 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9548 INT x, INT y, INT width, INT height, BOOL isW)
9550 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9551 HWND hedit;
9552 SIZE sz;
9553 HDC hdc;
9554 HDC hOldFont=0;
9555 TEXTMETRICW textMetric;
9556 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9558 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9560 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9561 hdc = GetDC(infoPtr->hwndSelf);
9563 /* Select the font to get appropriate metric dimensions */
9564 if(infoPtr->hFont != 0)
9565 hOldFont = SelectObject(hdc, infoPtr->hFont);
9567 /*Get String Length in pixels */
9568 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9570 /*Add Extra spacing for the next character */
9571 GetTextMetricsW(hdc, &textMetric);
9572 sz.cx += (textMetric.tmMaxCharWidth * 2);
9574 if(infoPtr->hFont != 0)
9575 SelectObject(hdc, hOldFont);
9577 ReleaseDC(infoPtr->hwndSelf, hdc);
9578 if (isW)
9579 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9580 else
9581 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9583 if (!hedit) return 0;
9585 infoPtr->EditWndProc = (WNDPROC)
9586 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9587 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9589 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9591 return hedit;