We no longer use the .exe.spec.c files.
[wine/multimedia.git] / dlls / comctl32 / listview.c
blobcfc92ed2e884d3fb9722b1096aca5343274437bf
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 belive 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 CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs.
44 * -- in LISTVIEW_AddGroupSelection, se whould send LVN_ODSTATECHANGED
45 * -- LVA_SNAPTOGRID not implemented
46 * -- LISTVIEW_ApproximateViewRect partially implemented
47 * -- LISTVIEW_[GS]etColumnOrderArray stubs
48 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
49 * -- LISTVIEW_SetIconSpacing is incomplete
50 * -- LISTVIEW_SortItems is broken
51 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
53 * Speedups
54 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
55 * linear in the number of items in the list, and this is
56 * unacceptable for large lists.
57 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
58 * instead of inserting in the right spot
59 * -- we should keep an ordered array of coordinates in iconic mode
60 * this would allow to frame items (iterator_frameditems),
61 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
63 * Flags
64 * -- LVIF_COLUMNS
65 * -- LVIF_GROUPID
66 * -- LVIF_NORECOMPUTE
68 * States
69 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
70 * -- LVIS_CUT
71 * -- LVIS_DROPHILITED
72 * -- LVIS_OVERLAYMASK
74 * Styles
75 * -- LVS_NOLABELWRAP
76 * -- LVS_NOSCROLL (see Q137520)
77 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
79 * Extended Styles
80 * -- LVS_EX_BORDERSELECT
81 * -- LVS_EX_FLATSB
82 * -- LVS_EX_GRIDLINES
83 * -- LVS_EX_HEADERDRAGDROP
84 * -- LVS_EX_INFOTIP
85 * -- LVS_EX_LABELTIP
86 * -- LVS_EX_MULTIWORKAREAS
87 * -- LVS_EX_ONECLICKACTIVATE
88 * -- LVS_EX_REGIONAL
89 * -- LVS_EX_SIMPLESELECT
90 * -- LVS_EX_TRACKSELECT
91 * -- LVS_EX_TWOCLICKACTIVATE
92 * -- LVS_EX_UNDERLINECOLD
93 * -- LVS_EX_UNDERLINEHOT
95 * Notifications:
96 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
97 * -- LVN_GETINFOTIP
98 * -- LVN_HOTTRACK
99 * -- LVN_MARQUEEBEGIN
100 * -- LVN_ODFINDITEM
101 * -- LVN_ODSTATECHANGED
102 * -- LVN_SETDISPINFO
103 * -- NM_HOVER
105 * Messages:
106 * -- LVM_CANCELEDITLABEL
107 * -- LVM_ENABLEGROUPVIEW
108 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
109 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
110 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
111 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
112 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
113 * -- LVM_GETINSERTMARKRECT
114 * -- LVM_GETNUMBEROFWORKAREAS
115 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
116 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
117 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
118 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
119 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
120 * -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
121 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
122 * -- LVM_GETVIEW, LVM_SETVIEW
123 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
124 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
125 * -- LVM_INSERTGROUPSORTED
126 * -- LVM_INSERTMARKHITTEST
127 * -- LVM_ISGROUPVIEWENABLED
128 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
129 * -- LVM_MOVEGROUP
130 * -- LVM_MOVEITEMTOGROUP
131 * -- LVM_SETINFOTIP
132 * -- LVM_SETTILEWIDTH
133 * -- LVM_SORTGROUPS
134 * -- LVM_SORTITEMSEX
136 * Known differences in message stream from native control (not known if
137 * these differences cause problems):
138 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
139 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
140 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
141 * processing for "USEDOUBLECLICKTIME".
144 #include "config.h"
145 #include "wine/port.h"
147 #include <assert.h>
148 #include <ctype.h>
149 #include <string.h>
150 #include <stdlib.h>
151 #include <stdarg.h>
152 #include <stdio.h>
154 #include "windef.h"
155 #include "winbase.h"
156 #include "winnt.h"
157 #include "wingdi.h"
158 #include "winuser.h"
159 #include "winnls.h"
160 #include "commctrl.h"
161 #include "comctl32.h"
163 #include "wine/debug.h"
164 #include "wine/unicode.h"
166 WINE_DEFAULT_DEBUG_CHANNEL(listview);
168 /* make sure you set this to 0 for production use! */
169 #define DEBUG_RANGES 1
171 typedef struct tagCOLUMN_INFO
173 RECT rcHeader; /* tracks the header's rectangle */
174 int fmt; /* same as LVCOLUMN.fmt */
175 } COLUMN_INFO;
177 typedef struct tagITEMHDR
179 LPWSTR pszText;
180 INT iImage;
181 } ITEMHDR, *LPITEMHDR;
183 typedef struct tagSUBITEM_INFO
185 ITEMHDR hdr;
186 INT iSubItem;
187 } SUBITEM_INFO;
189 typedef struct tagITEM_INFO
191 ITEMHDR hdr;
192 UINT state;
193 LPARAM lParam;
194 INT iIndent;
195 } ITEM_INFO;
197 typedef struct tagRANGE
199 INT lower;
200 INT upper;
201 } RANGE;
203 typedef struct tagRANGES
205 HDPA hdpa;
206 } *RANGES;
208 typedef struct tagITERATOR
210 INT nItem;
211 INT nSpecial;
212 RANGE range;
213 RANGES ranges;
214 INT index;
215 } ITERATOR;
217 typedef struct tagLISTVIEW_INFO
219 HWND hwndSelf;
220 HBRUSH hBkBrush;
221 COLORREF clrBk;
222 COLORREF clrText;
223 COLORREF clrTextBk;
224 COLORREF clrTextBkDefault;
225 HIMAGELIST himlNormal;
226 HIMAGELIST himlSmall;
227 HIMAGELIST himlState;
228 BOOL bLButtonDown;
229 BOOL bRButtonDown;
230 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
231 INT nItemHeight;
232 INT nItemWidth;
233 RANGES selectionRanges;
234 INT nSelectionMark;
235 INT nHotItem;
236 SHORT notifyFormat;
237 HWND hwndNotify;
238 RECT rcList; /* This rectangle is really the window
239 * client rectangle possibly reduced by the
240 * horizontal scroll bar and/or header - see
241 * LISTVIEW_UpdateSize. This rectangle offset
242 * by the LISTVIEW_GetOrigin value is in
243 * client coordinates */
244 SIZE iconSize;
245 SIZE iconSpacing;
246 SIZE iconStateSize;
247 UINT uCallbackMask;
248 HWND hwndHeader;
249 HCURSOR hHotCursor;
250 HFONT hDefaultFont;
251 HFONT hFont;
252 INT ntmHeight; /* Some cached metrics of the font used */
253 INT ntmAveCharWidth; /* by the listview to draw items */
254 BOOL bRedraw; /* Turns on/off repaints & invalidations */
255 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
256 BOOL bFocus;
257 BOOL bDoChangeNotify; /* send change notification messages? */
258 INT nFocusedItem;
259 RECT rcFocus;
260 DWORD dwStyle; /* the cached window GWL_STYLE */
261 DWORD dwLvExStyle; /* extended listview style */
262 INT nItemCount; /* the number of items in the list */
263 HDPA hdpaItems; /* array ITEM_INFO pointers */
264 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
265 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
266 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
267 POINT currIconPos; /* this is the position next icon will be placed */
268 PFNLVCOMPARE pfnCompare;
269 LPARAM lParamSort;
270 HWND hwndEdit;
271 WNDPROC EditWndProc;
272 INT nEditLabelItem;
273 DWORD dwHoverTime;
274 HWND hwndToolTip;
276 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
278 DWORD lastKeyPressTimestamp;
279 WPARAM charCode;
280 INT nSearchParamLength;
281 WCHAR szSearchParam[ MAX_PATH ];
282 BOOL bIsDrawing;
283 } LISTVIEW_INFO;
286 * constants
288 /* How many we debug buffer to allocate */
289 #define DEBUG_BUFFERS 20
290 /* The size of a single debug bbuffer */
291 #define DEBUG_BUFFER_SIZE 256
293 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
294 #define SB_INTERNAL -1
296 /* maximum size of a label */
297 #define DISP_TEXT_SIZE 512
299 /* padding for items in list and small icon display modes */
300 #define WIDTH_PADDING 12
302 /* padding for items in list, report and small icon display modes */
303 #define HEIGHT_PADDING 1
305 /* offset of items in report display mode */
306 #define REPORT_MARGINX 2
308 /* padding for icon in large icon display mode
309 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
310 * that HITTEST will see.
311 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
312 * ICON_TOP_PADDING - sum of the two above.
313 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
314 * LABEL_HOR_PADDING - between text and sides of box
315 * LABEL_VERT_PADDING - between bottom of text and end of box
317 * ICON_LR_PADDING - additional width above icon size.
318 * ICON_LR_HALF - half of the above value
320 #define ICON_TOP_PADDING_NOTHITABLE 2
321 #define ICON_TOP_PADDING_HITABLE 2
322 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
323 #define ICON_BOTTOM_PADDING 4
324 #define LABEL_HOR_PADDING 5
325 #define LABEL_VERT_PADDING 7
326 #define ICON_LR_PADDING 16
327 #define ICON_LR_HALF (ICON_LR_PADDING/2)
329 /* default label width for items in list and small icon display modes */
330 #define DEFAULT_LABEL_WIDTH 40
332 /* default column width for items in list display mode */
333 #define DEFAULT_COLUMN_WIDTH 128
335 /* Size of "line" scroll for V & H scrolls */
336 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
338 /* Padding betwen image and label */
339 #define IMAGE_PADDING 2
341 /* Padding behind the label */
342 #define TRAILING_LABEL_PADDING 12
343 #define TRAILING_HEADER_PADDING 11
345 /* Border for the icon caption */
346 #define CAPTION_BORDER 2
348 /* Standard DrawText flags */
349 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
350 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
351 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
353 /* The time in milliseconds to reset the search in the list */
354 #define KEY_DELAY 450
356 /* Dump the LISTVIEW_INFO structure to the debug channel */
357 #define LISTVIEW_DUMP(iP) do { \
358 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
359 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
360 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
361 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
362 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
363 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
364 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
365 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
366 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
367 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
368 } while(0)
371 * forward declarations
373 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
374 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
375 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
376 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
377 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
378 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
379 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
380 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
381 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
382 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
383 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
384 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
385 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
386 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
387 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
388 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
389 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
390 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
391 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
392 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
393 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
394 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
395 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
396 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
397 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
398 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
400 /******** Text handling functions *************************************/
402 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
403 * text string. The string may be ANSI or Unicode, in which case
404 * the boolean isW tells us the type of the string.
406 * The name of the function tell what type of strings it expects:
407 * W: Unicode, T: ANSI/Unicode - function of isW
410 static inline BOOL is_textW(LPCWSTR text)
412 return text != NULL && text != LPSTR_TEXTCALLBACKW;
415 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
417 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
418 return is_textW(text);
421 static inline int textlenT(LPCWSTR text, BOOL isW)
423 return !is_textT(text, isW) ? 0 :
424 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
427 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
429 if (isDestW)
430 if (isSrcW) lstrcpynW(dest, src, max);
431 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
432 else
433 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
434 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
437 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
439 LPWSTR wstr = (LPWSTR)text;
441 if (!isW && is_textT(text, isW))
443 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
444 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
445 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
447 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
448 return wstr;
451 static inline void textfreeT(LPWSTR wstr, BOOL isW)
453 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
457 * dest is a pointer to a Unicode string
458 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
460 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
462 BOOL bResult = TRUE;
464 if (src == LPSTR_TEXTCALLBACKW)
466 if (is_textW(*dest)) Free(*dest);
467 *dest = LPSTR_TEXTCALLBACKW;
469 else
471 LPWSTR pszText = textdupTtoW(src, isW);
472 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
473 bResult = Str_SetPtrW(dest, pszText);
474 textfreeT(pszText, isW);
476 return bResult;
480 * compares a Unicode to a Unicode/ANSI text string
482 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
484 if (!aw) return bt ? -1 : 0;
485 if (!bt) return aw ? 1 : 0;
486 if (aw == LPSTR_TEXTCALLBACKW)
487 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
488 if (bt != LPSTR_TEXTCALLBACKW)
490 LPWSTR bw = textdupTtoW(bt, isW);
491 int r = bw ? lstrcmpW(aw, bw) : 1;
492 textfreeT(bw, isW);
493 return r;
496 return 1;
499 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
501 int res;
503 n = min(min(n, strlenW(s1)), strlenW(s2));
504 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
505 return res ? res - sizeof(WCHAR) : res;
508 /******** Debugging functions *****************************************/
510 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
512 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
513 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
516 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
518 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
519 n = min(textlenT(text, isW), n);
520 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
523 static char* debug_getbuf()
525 static int index = 0;
526 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
527 return buffers[index++ % DEBUG_BUFFERS];
530 static inline const char* debugrange(const RANGE *lprng)
532 if (lprng)
534 char* buf = debug_getbuf();
535 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
536 return buf;
537 } else return "(null)";
540 static inline const char* debugpoint(const POINT *lppt)
542 if (lppt)
544 char* buf = debug_getbuf();
545 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
546 return buf;
547 } else return "(null)";
550 static inline const char* debugrect(const RECT *rect)
552 if (rect)
554 char* buf = debug_getbuf();
555 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
556 rect->left, rect->top, rect->right, rect->bottom);
557 return buf;
558 } else return "(null)";
561 static const char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
563 char* buf = debug_getbuf(), *text = buf;
564 int len, size = DEBUG_BUFFER_SIZE;
566 if (pScrollInfo == NULL) return "(null)";
567 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
568 if (len == -1) goto end; buf += len; size -= len;
569 if (pScrollInfo->fMask & SIF_RANGE)
570 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
571 else len = 0;
572 if (len == -1) goto end; buf += len; size -= len;
573 if (pScrollInfo->fMask & SIF_PAGE)
574 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
575 else len = 0;
576 if (len == -1) goto end; buf += len; size -= len;
577 if (pScrollInfo->fMask & SIF_POS)
578 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
579 else len = 0;
580 if (len == -1) goto end; buf += len; size -= len;
581 if (pScrollInfo->fMask & SIF_TRACKPOS)
582 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
583 else len = 0;
584 if (len == -1) goto end; buf += len; size -= len;
585 goto undo;
586 end:
587 buf = text + strlen(text);
588 undo:
589 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
590 return text;
593 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
595 if (plvnm)
597 char* buf = debug_getbuf();
598 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
599 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
600 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
601 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
602 return buf;
603 } else return "(null)";
606 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
608 char* buf = debug_getbuf(), *text = buf;
609 int len, size = DEBUG_BUFFER_SIZE;
611 if (lpLVItem == NULL) return "(null)";
612 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
613 if (len == -1) goto end; buf += len; size -= len;
614 if (lpLVItem->mask & LVIF_STATE)
615 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
616 else len = 0;
617 if (len == -1) goto end; buf += len; size -= len;
618 if (lpLVItem->mask & LVIF_TEXT)
619 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
620 else len = 0;
621 if (len == -1) goto end; buf += len; size -= len;
622 if (lpLVItem->mask & LVIF_IMAGE)
623 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
624 else len = 0;
625 if (len == -1) goto end; buf += len; size -= len;
626 if (lpLVItem->mask & LVIF_PARAM)
627 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
628 else len = 0;
629 if (len == -1) goto end; buf += len; size -= len;
630 if (lpLVItem->mask & LVIF_INDENT)
631 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
632 else len = 0;
633 if (len == -1) goto end; buf += len; size -= len;
634 goto undo;
635 end:
636 buf = text + strlen(text);
637 undo:
638 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
639 return text;
642 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
644 char* buf = debug_getbuf(), *text = buf;
645 int len, size = DEBUG_BUFFER_SIZE;
647 if (lpColumn == NULL) return "(null)";
648 len = snprintf(buf, size, "{");
649 if (len == -1) goto end; buf += len; size -= len;
650 if (lpColumn->mask & LVCF_SUBITEM)
651 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
652 else len = 0;
653 if (len == -1) goto end; buf += len; size -= len;
654 if (lpColumn->mask & LVCF_FMT)
655 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
656 else len = 0;
657 if (len == -1) goto end; buf += len; size -= len;
658 if (lpColumn->mask & LVCF_WIDTH)
659 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
660 else len = 0;
661 if (len == -1) goto end; buf += len; size -= len;
662 if (lpColumn->mask & LVCF_TEXT)
663 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
664 else len = 0;
665 if (len == -1) goto end; buf += len; size -= len;
666 if (lpColumn->mask & LVCF_IMAGE)
667 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
668 else len = 0;
669 if (len == -1) goto end; buf += len; size -= len;
670 if (lpColumn->mask & LVCF_ORDER)
671 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
672 else len = 0;
673 if (len == -1) goto end; buf += len; size -= len;
674 goto undo;
675 end:
676 buf = text + strlen(text);
677 undo:
678 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
679 return text;
682 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
684 if (lpht)
686 char* buf = debug_getbuf();
687 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
688 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
689 return buf;
690 } else return "(null)";
693 /* Return the corresponding text for a given scroll value */
694 static inline LPCSTR debugscrollcode(int nScrollCode)
696 switch(nScrollCode)
698 case SB_LINELEFT: return "SB_LINELEFT";
699 case SB_LINERIGHT: return "SB_LINERIGHT";
700 case SB_PAGELEFT: return "SB_PAGELEFT";
701 case SB_PAGERIGHT: return "SB_PAGERIGHT";
702 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
703 case SB_THUMBTRACK: return "SB_THUMBTRACK";
704 case SB_ENDSCROLL: return "SB_ENDSCROLL";
705 case SB_INTERNAL: return "SB_INTERNAL";
706 default: return "unknown";
711 /******** Notification functions i************************************/
713 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
715 LRESULT result;
717 TRACE("(code=%d)\n", code);
719 pnmh->hwndFrom = infoPtr->hwndSelf;
720 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
721 pnmh->code = code;
722 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
723 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
725 TRACE(" <= %ld\n", result);
727 return result;
730 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
732 NMHDR nmh;
733 return notify_hdr(infoPtr, code, &nmh);
736 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
738 NMITEMACTIVATE nmia;
739 LVITEMW item;
741 if (htInfo) {
742 nmia.uNewState = 0;
743 nmia.uOldState = 0;
744 nmia.uChanged = 0;
745 nmia.uKeyFlags = 0;
747 item.mask = LVIF_PARAM|LVIF_STATE;
748 item.iItem = htInfo->iItem;
749 item.iSubItem = 0;
750 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
751 nmia.lParam = item.lParam;
752 nmia.uOldState = item.state;
753 nmia.uNewState = item.state | LVIS_ACTIVATING;
754 nmia.uChanged = LVIF_STATE;
757 nmia.iItem = htInfo->iItem;
758 nmia.iSubItem = htInfo->iSubItem;
759 nmia.ptAction = htInfo->pt;
761 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
762 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
763 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
765 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
768 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
770 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
771 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
774 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
776 NMLISTVIEW nmlv;
777 LVITEMW item;
779 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
780 ZeroMemory(&nmlv, sizeof(nmlv));
781 nmlv.iItem = lvht->iItem;
782 nmlv.iSubItem = lvht->iSubItem;
783 nmlv.ptAction = lvht->pt;
784 item.mask = LVIF_PARAM;
785 item.iItem = lvht->iItem;
786 item.iSubItem = 0;
787 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
788 return notify_listview(infoPtr, code, &nmlv);
791 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
793 NMLISTVIEW nmlv;
794 LVITEMW item;
796 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
797 nmlv.iItem = nItem;
798 item.mask = LVIF_PARAM;
799 item.iItem = nItem;
800 item.iSubItem = 0;
801 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
802 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
805 static int get_ansi_notification(INT unicodeNotificationCode)
807 switch (unicodeNotificationCode)
809 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
810 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
811 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
812 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
813 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
814 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
816 ERR("unknown notification %x\n", unicodeNotificationCode);
817 assert(FALSE);
818 return 0;
822 With testing on Windows 2000 it looks like the notify format
823 has nothing to do with this message. It ALWAYS seems to be
824 in ansi format.
826 infoPtr : listview struct
827 notificationCode : *Unicode* notification code
828 pdi : dispinfo structure (can be unicode or ansi)
829 isW : TRUE if dispinfo is Unicode
831 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
833 BOOL bResult = FALSE;
834 BOOL convertToAnsi = FALSE;
835 INT cchTempBufMax = 0, savCchTextMax = 0;
836 LPWSTR pszTempBuf = NULL, savPszText = NULL;
838 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
839 convertToAnsi = isW;
841 if (convertToAnsi)
843 if (notificationCode != LVN_GETDISPINFOW)
845 cchTempBufMax = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText,
846 -1, NULL, 0, NULL, NULL);
848 else
850 cchTempBufMax = pdi->item.cchTextMax;
851 *pdi->item.pszText = 0; /* make sure we don't process garbage */
854 pszTempBuf = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) *
855 cchTempBufMax);
856 if (!pszTempBuf) return FALSE;
858 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR)
859 pszTempBuf, cchTempBufMax, NULL, NULL);
861 savCchTextMax = pdi->item.cchTextMax;
862 savPszText = pdi->item.pszText;
863 pdi->item.pszText = pszTempBuf;
864 pdi->item.cchTextMax = cchTempBufMax;
867 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat !=
868 NFR_ANSI));
870 bResult = notify_hdr(infoPtr, get_ansi_notification(notificationCode),
871 (LPNMHDR)pdi);
873 if (convertToAnsi)
875 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
876 savPszText, savCchTextMax);
877 pdi->item.pszText = savPszText; /* restores our buffer */
878 pdi->item.cchTextMax = savCchTextMax;
879 HeapFree(GetProcessHeap(), 0, pszTempBuf);
881 return bResult;
884 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
885 const RECT *rcBounds, const LVITEMW *lplvItem)
887 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
888 lpnmlvcd->nmcd.hdc = hdc;
889 lpnmlvcd->nmcd.rc = *rcBounds;
890 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
891 lpnmlvcd->clrText = infoPtr->clrText;
892 if (!lplvItem) return;
893 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
894 lpnmlvcd->iSubItem = lplvItem->iSubItem;
895 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
896 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
897 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
898 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
901 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
903 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
904 DWORD result;
906 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
907 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
908 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
909 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
910 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
911 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
912 return result;
915 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
917 /* apprently, for selected items, we have to override the returned values */
918 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
920 if (infoPtr->bFocus)
922 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
923 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
925 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
927 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
928 lpnmlvcd->clrText = comctl32_color.clrBtnText;
932 /* Set the text attributes */
933 if (lpnmlvcd->clrTextBk != CLR_NONE)
935 SetBkMode(hdc, OPAQUE);
936 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
937 SetBkColor(hdc, infoPtr->clrTextBkDefault);
938 else
939 SetBkColor(hdc,lpnmlvcd->clrTextBk);
941 else
942 SetBkMode(hdc, TRANSPARENT);
943 SetTextColor(hdc, lpnmlvcd->clrText);
946 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
948 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
951 /******** Item iterator functions **********************************/
953 static RANGES ranges_create(int count);
954 static void ranges_destroy(RANGES ranges);
955 static BOOL ranges_add(RANGES ranges, RANGE range);
956 static BOOL ranges_del(RANGES ranges, RANGE range);
957 static void ranges_dump(RANGES ranges);
959 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
961 RANGE range = { nItem, nItem + 1 };
963 return ranges_add(ranges, range);
966 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
968 RANGE range = { nItem, nItem + 1 };
970 return ranges_del(ranges, range);
973 /***
974 * ITERATOR DOCUMENTATION
976 * The iterator functions allow for easy, and convenient iteration
977 * over items of iterest in the list. Typically, you create a
978 * iterator, use it, and destroy it, as such:
979 * ITERATOR i;
981 * iterator_xxxitems(&i, ...);
982 * while (iterator_{prev,next}(&i)
984 * //code which uses i.nItem
986 * iterator_destroy(&i);
988 * where xxx is either: framed, or visible.
989 * Note that it is important that the code destroys the iterator
990 * after it's done with it, as the creation of the iterator may
991 * allocate memory, which thus needs to be freed.
993 * You can iterate both forwards, and backwards through the list,
994 * by using iterator_next or iterator_prev respectively.
996 * Lower numbered items are draw on top of higher number items in
997 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
998 * items may overlap). So, to test items, you should use
999 * iterator_next
1000 * which lists the items top to bottom (in Z-order).
1001 * For drawing items, you should use
1002 * iterator_prev
1003 * which lists the items bottom to top (in Z-order).
1004 * If you keep iterating over the items after the end-of-items
1005 * marker (-1) is returned, the iterator will start from the
1006 * beginning. Typically, you don't need to test for -1,
1007 * because iterator_{next,prev} will return TRUE if more items
1008 * are to be iterated over, or FALSE otherwise.
1010 * Note: the iterator is defined to be bidirectional. That is,
1011 * any number of prev followed by any number of next, or
1012 * five versa, should leave the iterator at the same item:
1013 * prev * n, next * n = next * n, prev * n
1015 * The iterator has a notion of a out-of-order, special item,
1016 * which sits at the start of the list. This is used in
1017 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1018 * which needs to be first, as it may overlap other items.
1020 * The code is a bit messy because we have:
1021 * - a special item to deal with
1022 * - simple range, or composite range
1023 * - empty range.
1024 * If you find bugs, or want to add features, please make sure you
1025 * always check/modify *both* iterator_prev, and iterator_next.
1028 /****
1029 * This function iterates through the items in increasing order,
1030 * but prefixed by the special item, then -1. That is:
1031 * special, 1, 2, 3, ..., n, -1.
1032 * Each item is listed only once.
1034 static inline BOOL iterator_next(ITERATOR* i)
1036 if (i->nItem == -1)
1038 i->nItem = i->nSpecial;
1039 if (i->nItem != -1) return TRUE;
1041 if (i->nItem == i->nSpecial)
1043 if (i->ranges) i->index = 0;
1044 goto pickarange;
1047 i->nItem++;
1048 testitem:
1049 if (i->nItem == i->nSpecial) i->nItem++;
1050 if (i->nItem < i->range.upper) return TRUE;
1052 pickarange:
1053 if (i->ranges)
1055 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1056 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1057 else goto end;
1059 else if (i->nItem >= i->range.upper) goto end;
1061 i->nItem = i->range.lower;
1062 if (i->nItem >= 0) goto testitem;
1063 end:
1064 i->nItem = -1;
1065 return FALSE;
1068 /****
1069 * This function iterates through the items in decreasing order,
1070 * followed by the special item, then -1. That is:
1071 * n, n-1, ..., 3, 2, 1, special, -1.
1072 * Each item is listed only once.
1074 static inline BOOL iterator_prev(ITERATOR* i)
1076 BOOL start = FALSE;
1078 if (i->nItem == -1)
1080 start = TRUE;
1081 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1082 goto pickarange;
1084 if (i->nItem == i->nSpecial)
1086 i->nItem = -1;
1087 return FALSE;
1090 testitem:
1091 i->nItem--;
1092 if (i->nItem == i->nSpecial) i->nItem--;
1093 if (i->nItem >= i->range.lower) return TRUE;
1095 pickarange:
1096 if (i->ranges)
1098 if (i->index > 0)
1099 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1100 else goto end;
1102 else if (!start && i->nItem < i->range.lower) goto end;
1104 i->nItem = i->range.upper;
1105 if (i->nItem > 0) goto testitem;
1106 end:
1107 return (i->nItem = i->nSpecial) != -1;
1110 static RANGE iterator_range(ITERATOR* i)
1112 RANGE range;
1114 if (!i->ranges) return i->range;
1116 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1117 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1118 return range;
1121 /***
1122 * Releases resources associated with this ierator.
1124 static inline void iterator_destroy(ITERATOR* i)
1126 ranges_destroy(i->ranges);
1129 /***
1130 * Create an empty iterator.
1132 static inline BOOL iterator_empty(ITERATOR* i)
1134 ZeroMemory(i, sizeof(*i));
1135 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1136 return TRUE;
1139 /***
1140 * Create an iterator over a range.
1142 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1144 iterator_empty(i);
1145 i->range = range;
1146 return TRUE;
1149 /***
1150 * Create an iterator over a bunch of ranges.
1151 * Please note that the iterator will take ownership of the ranges,
1152 * and will free them upon destruction.
1154 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1156 iterator_empty(i);
1157 i->ranges = ranges;
1158 return TRUE;
1161 /***
1162 * Creates an iterator over the items which intersect lprc.
1164 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1166 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1167 RECT frame = *lprc, rcItem, rcTemp;
1168 POINT Origin;
1170 /* in case we fail, we want to return an empty iterator */
1171 if (!iterator_empty(i)) return FALSE;
1173 LISTVIEW_GetOrigin(infoPtr, &Origin);
1175 TRACE("(lprc=%s)\n", debugrect(lprc));
1176 OffsetRect(&frame, -Origin.x, -Origin.y);
1178 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1180 INT nItem;
1182 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1184 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1185 if (IntersectRect(&rcTemp, &rcItem, lprc))
1186 i->nSpecial = infoPtr->nFocusedItem;
1188 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1189 /* to do better here, we need to have PosX, and PosY sorted */
1190 TRACE("building icon ranges:\n");
1191 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1193 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1194 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1195 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1196 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1197 if (IntersectRect(&rcTemp, &rcItem, &frame))
1198 ranges_additem(i->ranges, nItem);
1200 return TRUE;
1202 else if (uView == LVS_REPORT)
1204 RANGE range;
1206 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1207 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1209 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1210 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1211 if (range.upper <= range.lower) return TRUE;
1212 if (!iterator_rangeitems(i, range)) return FALSE;
1213 TRACE(" report=%s\n", debugrange(&i->range));
1215 else
1217 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1218 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1219 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1220 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1221 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1222 INT lower = nFirstCol * nPerCol + nFirstRow;
1223 RANGE item_range;
1224 INT nCol;
1226 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1227 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1229 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1231 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1232 TRACE("building list ranges:\n");
1233 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1235 item_range.lower = nCol * nPerCol + nFirstRow;
1236 if(item_range.lower >= infoPtr->nItemCount) break;
1237 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1238 TRACE(" list=%s\n", debugrange(&item_range));
1239 ranges_add(i->ranges, item_range);
1243 return TRUE;
1246 /***
1247 * Creates an iterator over the items which intersect the visible region of hdc.
1249 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1251 POINT Origin, Position;
1252 RECT rcItem, rcClip;
1253 INT rgntype;
1255 rgntype = GetClipBox(hdc, &rcClip);
1256 if (rgntype == NULLREGION) return iterator_empty(i);
1257 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1258 if (rgntype == SIMPLEREGION) return TRUE;
1260 /* first deal with the special item */
1261 if (i->nSpecial != -1)
1263 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1264 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1267 /* if we can't deal with the region, we'll just go with the simple range */
1268 LISTVIEW_GetOrigin(infoPtr, &Origin);
1269 TRACE("building visible range:\n");
1270 if (!i->ranges && i->range.lower < i->range.upper)
1272 if (!(i->ranges = ranges_create(50))) return TRUE;
1273 if (!ranges_add(i->ranges, i->range))
1275 ranges_destroy(i->ranges);
1276 i->ranges = 0;
1277 return TRUE;
1281 /* now delete the invisible items from the list */
1282 while(iterator_next(i))
1284 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1285 rcItem.left = Position.x + Origin.x;
1286 rcItem.top = Position.y + Origin.y;
1287 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1288 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1289 if (!RectVisible(hdc, &rcItem))
1290 ranges_delitem(i->ranges, i->nItem);
1292 /* the iterator should restart on the next iterator_next */
1293 TRACE("done\n");
1295 return TRUE;
1298 /******** Misc helper functions ************************************/
1300 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1301 WPARAM wParam, LPARAM lParam, BOOL isW)
1303 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1304 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1307 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1309 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1311 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1312 (uView == LVS_ICON || uView == LVS_SMALLICON);
1315 /******** Internal API functions ************************************/
1317 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1319 static COLUMN_INFO mainItem;
1321 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1322 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1323 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1326 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1328 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1331 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1333 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1336 /* Listview invalidation functions: use _only_ these functions to invalidate */
1338 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1340 return infoPtr->bRedraw;
1343 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1345 if(!is_redrawing(infoPtr)) return;
1346 TRACE(" invalidating rect=%s\n", debugrect(rect));
1347 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1350 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1352 RECT rcBox;
1354 if(!is_redrawing(infoPtr)) return;
1355 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1356 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1359 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1361 POINT Origin, Position;
1362 RECT rcBox;
1364 if(!is_redrawing(infoPtr)) return;
1365 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1366 LISTVIEW_GetOrigin(infoPtr, &Origin);
1367 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1368 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1369 rcBox.top = 0;
1370 rcBox.bottom = infoPtr->nItemHeight;
1371 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1372 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1375 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1377 LISTVIEW_InvalidateRect(infoPtr, NULL);
1380 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1382 RECT rcCol;
1384 if(!is_redrawing(infoPtr)) return;
1385 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1386 rcCol.top = infoPtr->rcList.top;
1387 rcCol.bottom = infoPtr->rcList.bottom;
1388 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1391 /***
1392 * DESCRIPTION:
1393 * Retrieves the number of items that can fit vertically in the client area.
1395 * PARAMETER(S):
1396 * [I] infoPtr : valid pointer to the listview structure
1398 * RETURN:
1399 * Number of items per row.
1401 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1403 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1405 return max(nListWidth/infoPtr->nItemWidth, 1);
1408 /***
1409 * DESCRIPTION:
1410 * Retrieves the number of items that can fit horizontally in the client
1411 * area.
1413 * PARAMETER(S):
1414 * [I] infoPtr : valid pointer to the listview structure
1416 * RETURN:
1417 * Number of items per column.
1419 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1421 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1423 return max(nListHeight / infoPtr->nItemHeight, 1);
1427 /*************************************************************************
1428 * LISTVIEW_ProcessLetterKeys
1430 * Processes keyboard messages generated by pressing the letter keys
1431 * on the keyboard.
1432 * What this does is perform a case insensitive search from the
1433 * current position with the following quirks:
1434 * - If two chars or more are pressed in quick succession we search
1435 * for the corresponding string (e.g. 'abc').
1436 * - If there is a delay we wipe away the current search string and
1437 * restart with just that char.
1438 * - If the user keeps pressing the same character, whether slowly or
1439 * fast, so that the search string is entirely composed of this
1440 * character ('aaaaa' for instance), then we search for first item
1441 * that starting with that character.
1442 * - If the user types the above character in quick succession, then
1443 * we must also search for the corresponding string ('aaaaa'), and
1444 * go to that string if there is a match.
1446 * PARAMETERS
1447 * [I] hwnd : handle to the window
1448 * [I] charCode : the character code, the actual character
1449 * [I] keyData : key data
1451 * RETURNS
1453 * Zero.
1455 * BUGS
1457 * - The current implementation has a list of characters it will
1458 * accept and it ignores averything else. In particular it will
1459 * ignore accentuated characters which seems to match what
1460 * Windows does. But I'm not sure it makes sense to follow
1461 * Windows there.
1462 * - We don't sound a beep when the search fails.
1464 * SEE ALSO
1466 * TREEVIEW_ProcessLetterKeys
1468 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1470 INT nItem;
1471 INT endidx,idx;
1472 LVITEMW item;
1473 WCHAR buffer[MAX_PATH];
1474 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1476 /* simple parameter checking */
1477 if (!charCode || !keyData) return 0;
1479 /* only allow the valid WM_CHARs through */
1480 if (!isalnum(charCode) &&
1481 charCode != '.' && charCode != '`' && charCode != '!' &&
1482 charCode != '@' && charCode != '#' && charCode != '$' &&
1483 charCode != '%' && charCode != '^' && charCode != '&' &&
1484 charCode != '*' && charCode != '(' && charCode != ')' &&
1485 charCode != '-' && charCode != '_' && charCode != '+' &&
1486 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1487 charCode != '}' && charCode != '[' && charCode != '{' &&
1488 charCode != '/' && charCode != '?' && charCode != '>' &&
1489 charCode != '<' && charCode != ',' && charCode != '~')
1490 return 0;
1492 /* if there's one item or less, there is no where to go */
1493 if (infoPtr->nItemCount <= 1) return 0;
1495 /* update the search parameters */
1496 infoPtr->lastKeyPressTimestamp = GetTickCount();
1497 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1498 if (infoPtr->nSearchParamLength < MAX_PATH)
1499 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1500 if (infoPtr->charCode != charCode)
1501 infoPtr->charCode = charCode = 0;
1502 } else {
1503 infoPtr->charCode=charCode;
1504 infoPtr->szSearchParam[0]=charCode;
1505 infoPtr->nSearchParamLength=1;
1506 /* Redundant with the 1 char string */
1507 charCode=0;
1510 /* and search from the current position */
1511 nItem=-1;
1512 if (infoPtr->nFocusedItem >= 0) {
1513 endidx=infoPtr->nFocusedItem;
1514 idx=endidx;
1515 /* if looking for single character match,
1516 * then we must always move forward
1518 if (infoPtr->nSearchParamLength == 1)
1519 idx++;
1520 } else {
1521 endidx=infoPtr->nItemCount;
1522 idx=0;
1524 do {
1525 if (idx == infoPtr->nItemCount) {
1526 if (endidx == infoPtr->nItemCount || endidx == 0)
1527 break;
1528 idx=0;
1531 /* get item */
1532 item.mask = LVIF_TEXT;
1533 item.iItem = idx;
1534 item.iSubItem = 0;
1535 item.pszText = buffer;
1536 item.cchTextMax = MAX_PATH;
1537 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1539 /* check for a match */
1540 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1541 nItem=idx;
1542 break;
1543 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1544 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1545 /* This would work but we must keep looking for a longer match */
1546 nItem=idx;
1548 idx++;
1549 } while (idx != endidx);
1551 if (nItem != -1)
1552 LISTVIEW_KeySelection(infoPtr, nItem);
1554 return 0;
1557 /*************************************************************************
1558 * LISTVIEW_UpdateHeaderSize [Internal]
1560 * Function to resize the header control
1562 * PARAMS
1563 * [I] hwnd : handle to a window
1564 * [I] nNewScrollPos : scroll pos to set
1566 * RETURNS
1567 * None.
1569 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1571 RECT winRect;
1572 POINT point[2];
1574 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1576 GetWindowRect(infoPtr->hwndHeader, &winRect);
1577 point[0].x = winRect.left;
1578 point[0].y = winRect.top;
1579 point[1].x = winRect.right;
1580 point[1].y = winRect.bottom;
1582 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1583 point[0].x = -nNewScrollPos;
1584 point[1].x += nNewScrollPos;
1586 SetWindowPos(infoPtr->hwndHeader,0,
1587 point[0].x,point[0].y,point[1].x,point[1].y,
1588 SWP_NOZORDER | SWP_NOACTIVATE);
1591 /***
1592 * DESCRIPTION:
1593 * Update the scrollbars. This functions should be called whenever
1594 * the content, size or view changes.
1596 * PARAMETER(S):
1597 * [I] infoPtr : valid pointer to the listview structure
1599 * RETURN:
1600 * None
1602 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1604 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1605 SCROLLINFO horzInfo, vertInfo;
1607 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1609 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1610 horzInfo.cbSize = sizeof(SCROLLINFO);
1611 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1613 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1614 if (uView == LVS_LIST)
1616 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1617 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1619 /* scroll by at least one column per page */
1620 if(horzInfo.nPage < infoPtr->nItemWidth)
1621 horzInfo.nPage = infoPtr->nItemWidth;
1623 horzInfo.nPage /= infoPtr->nItemWidth;
1625 else if (uView == LVS_REPORT)
1627 horzInfo.nMax = infoPtr->nItemWidth;
1629 else /* LVS_ICON, or LVS_SMALLICON */
1631 RECT rcView;
1633 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1636 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1637 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1638 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1639 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1641 /* Setting the horizontal scroll can change the listview size
1642 * (and potentially everything else) so we need to recompute
1643 * everything again for the vertical scroll
1646 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1647 vertInfo.cbSize = sizeof(SCROLLINFO);
1648 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1650 if (uView == LVS_REPORT)
1652 vertInfo.nMax = infoPtr->nItemCount;
1654 /* scroll by at least one page */
1655 if(vertInfo.nPage < infoPtr->nItemHeight)
1656 vertInfo.nPage = infoPtr->nItemHeight;
1658 vertInfo.nPage /= infoPtr->nItemHeight;
1660 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1662 RECT rcView;
1664 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1667 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1668 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1669 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1670 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1672 /* Update the Header Control */
1673 if (uView == LVS_REPORT)
1675 horzInfo.fMask = SIF_POS;
1676 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1677 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1682 /***
1683 * DESCRIPTION:
1684 * Shows/hides the focus rectangle.
1686 * PARAMETER(S):
1687 * [I] infoPtr : valid pointer to the listview structure
1688 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1690 * RETURN:
1691 * None
1693 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1695 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1696 HDC hdc;
1698 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1700 if (infoPtr->nFocusedItem < 0) return;
1702 /* we need some gymnastics in ICON mode to handle large items */
1703 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1705 RECT rcBox;
1707 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1708 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1710 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1711 return;
1715 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1717 /* for some reason, owner draw should work only in report mode */
1718 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1720 DRAWITEMSTRUCT dis;
1721 LVITEMW item;
1723 item.iItem = infoPtr->nFocusedItem;
1724 item.iSubItem = 0;
1725 item.mask = LVIF_PARAM;
1726 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1728 ZeroMemory(&dis, sizeof(dis));
1729 dis.CtlType = ODT_LISTVIEW;
1730 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1731 dis.itemID = item.iItem;
1732 dis.itemAction = ODA_FOCUS;
1733 if (fShow) dis.itemState |= ODS_FOCUS;
1734 dis.hwndItem = infoPtr->hwndSelf;
1735 dis.hDC = hdc;
1736 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1737 dis.itemData = item.lParam;
1739 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1741 else
1743 DrawFocusRect(hdc, &infoPtr->rcFocus);
1745 done:
1746 ReleaseDC(infoPtr->hwndSelf, hdc);
1749 /***
1750 * Invalidates all visible selected items.
1752 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1754 ITERATOR i;
1756 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1757 while(iterator_next(&i))
1759 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1760 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1762 iterator_destroy(&i);
1766 /***
1767 * DESCRIPTION: [INTERNAL]
1768 * Computes an item's (left,top) corner, relative to rcView.
1769 * That is, the position has NOT been made relative to the Origin.
1770 * This is deliberate, to avoid computing the Origin over, and
1771 * over again, when this function is call in a loop. Instead,
1772 * one ca factor the computation of the Origin before the loop,
1773 * and offset the value retured by this function, on every iteration.
1775 * PARAMETER(S):
1776 * [I] infoPtr : valid pointer to the listview structure
1777 * [I] nItem : item number
1778 * [O] lpptOrig : item top, left corner
1780 * RETURN:
1781 * None.
1783 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1785 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1787 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1789 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1791 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1792 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1794 else if (uView == LVS_LIST)
1796 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1797 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1798 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1800 else /* LVS_REPORT */
1802 lpptPosition->x = 0;
1803 lpptPosition->y = nItem * infoPtr->nItemHeight;
1807 /***
1808 * DESCRIPTION: [INTERNAL]
1809 * Compute the rectangles of an item. This is to localize all
1810 * the computations in one place. If you are not interested in some
1811 * of these values, simply pass in a NULL -- the fucntion is smart
1812 * enough to compute only what's necessary. The function computes
1813 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1814 * one, the BOX rectangle. This rectangle is very cheap to compute,
1815 * and is guaranteed to contain all the other rectangles. Computing
1816 * the ICON rect is also cheap, but all the others are potentaily
1817 * expensive. This gives an easy and effective optimization when
1818 * searching (like point inclusion, or rectangle intersection):
1819 * first test against the BOX, and if TRUE, test agains the desired
1820 * rectangle.
1821 * If the function does not have all the necessary information
1822 * to computed the requested rectangles, will crash with a
1823 * failed assertion. This is done so we catch all programming
1824 * errors, given that the function is called only from our code.
1826 * We have the following 'special' meanings for a few fields:
1827 * * If LVIS_FOCUSED is set, we assume the item has the focus
1828 * This is important in ICON mode, where it might get a larger
1829 * then usual rectange
1831 * Please note that subitem support works only in REPORT mode.
1833 * PARAMETER(S):
1834 * [I] infoPtr : valid pointer to the listview structure
1835 * [I] lpLVItem : item to compute the measures for
1836 * [O] lprcBox : ptr to Box rectangle
1837 * The internal LVIR_BOX rectangle
1838 * [0] lprcState : ptr to State icon rectangle
1839 * The internal LVIR_STATE rectangle
1840 * [O] lprcIcon : ptr to Icon rectangle
1841 * Same as LVM_GETITEMRECT with LVIR_ICON
1842 * [O] lprcLabel : ptr to Label rectangle
1843 * Same as LVM_GETITEMRECT with LVIR_LABEL
1845 * RETURN:
1846 * None.
1848 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1849 LPRECT lprcBox, LPRECT lprcState,
1850 LPRECT lprcIcon, LPRECT lprcLabel)
1852 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1853 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1854 RECT Box, State, Icon, Label;
1855 COLUMN_INFO *lpColumnInfo = NULL;
1857 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1859 /* Be smart and try to figure out the minimum we have to do */
1860 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1861 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1863 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1864 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1866 if (lprcLabel) doLabel = TRUE;
1867 if (doLabel || lprcIcon) doIcon = TRUE;
1868 if (doIcon || lprcState) doState = TRUE;
1870 /************************************************************/
1871 /* compute the box rectangle (it should be cheap to do) */
1872 /************************************************************/
1873 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1874 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1876 if (lpLVItem->iSubItem)
1878 Box = lpColumnInfo->rcHeader;
1880 else
1882 Box.left = 0;
1883 Box.right = infoPtr->nItemWidth;
1885 Box.top = 0;
1886 Box.bottom = infoPtr->nItemHeight;
1888 /************************************************************/
1889 /* compute STATEICON bounding box */
1890 /************************************************************/
1891 if (doState)
1893 if (uView == LVS_ICON)
1895 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1896 if (infoPtr->himlNormal)
1897 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1898 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1900 else
1902 /* we need the ident in report mode, if we don't have it, we fail */
1903 State.left = Box.left;
1904 if (uView == LVS_REPORT)
1906 if (lpLVItem->iSubItem == 0)
1908 State.left += REPORT_MARGINX;
1909 assert(lpLVItem->mask & LVIF_INDENT);
1910 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1913 State.top = Box.top;
1915 State.right = State.left;
1916 State.bottom = State.top;
1917 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1919 State.right += infoPtr->iconStateSize.cx;
1920 State.bottom += infoPtr->iconStateSize.cy;
1922 if (lprcState) *lprcState = State;
1923 TRACE(" - state=%s\n", debugrect(&State));
1926 /************************************************************/
1927 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1928 /************************************************************/
1929 if (doIcon)
1931 if (uView == LVS_ICON)
1933 Icon.left = Box.left;
1934 if (infoPtr->himlNormal)
1935 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1936 Icon.top = Box.top + ICON_TOP_PADDING;
1937 Icon.right = Icon.left;
1938 Icon.bottom = Icon.top;
1939 if (infoPtr->himlNormal)
1941 Icon.right += infoPtr->iconSize.cx;
1942 Icon.bottom += infoPtr->iconSize.cy;
1945 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1947 Icon.left = State.right;
1948 Icon.top = Box.top;
1949 Icon.right = Icon.left;
1950 if (infoPtr->himlSmall &&
1951 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1952 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1953 Icon.right += infoPtr->iconSize.cx;
1954 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1956 if(lprcIcon) *lprcIcon = Icon;
1957 TRACE(" - icon=%s\n", debugrect(&Icon));
1960 /************************************************************/
1961 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1962 /************************************************************/
1963 if (doLabel)
1965 SIZE labelSize = { 0, 0 };
1967 /* calculate how far to the right can the label strech */
1968 Label.right = Box.right;
1969 if (uView == LVS_REPORT)
1971 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1974 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1976 labelSize.cx = infoPtr->nItemWidth;
1977 labelSize.cy = infoPtr->nItemHeight;
1978 goto calc_label;
1981 /* we need the text in non owner draw mode */
1982 assert(lpLVItem->mask & LVIF_TEXT);
1983 if (is_textT(lpLVItem->pszText, TRUE))
1985 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1986 HDC hdc = GetDC(infoPtr->hwndSelf);
1987 HFONT hOldFont = SelectObject(hdc, hFont);
1988 UINT uFormat;
1989 RECT rcText;
1991 /* compute rough rectangle where the label will go */
1992 SetRectEmpty(&rcText);
1993 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1994 rcText.bottom = infoPtr->nItemHeight;
1995 if (uView == LVS_ICON)
1996 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1998 /* now figure out the flags */
1999 if (uView == LVS_ICON)
2000 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2001 else
2002 uFormat = LV_SL_DT_FLAGS;
2004 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2006 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2007 labelSize.cy = rcText.bottom - rcText.top;
2009 SelectObject(hdc, hOldFont);
2010 ReleaseDC(infoPtr->hwndSelf, hdc);
2013 calc_label:
2014 if (uView == LVS_ICON)
2016 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2017 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2018 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2019 Label.right = Label.left + labelSize.cx;
2020 Label.bottom = Label.top + infoPtr->nItemHeight;
2021 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2023 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2024 labelSize.cy /= infoPtr->ntmHeight;
2025 labelSize.cy = max(labelSize.cy, 1);
2026 labelSize.cy *= infoPtr->ntmHeight;
2028 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2030 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2032 Label.left = Icon.right;
2033 Label.top = Box.top;
2034 Label.right = min(Label.left + labelSize.cx, Label.right);
2035 Label.bottom = Label.top + infoPtr->nItemHeight;
2038 if (lprcLabel) *lprcLabel = Label;
2039 TRACE(" - label=%s\n", debugrect(&Label));
2042 /* Fix the Box if necessary */
2043 if (lprcBox)
2045 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2046 else *lprcBox = Box;
2048 TRACE(" - box=%s\n", debugrect(&Box));
2051 /***
2052 * DESCRIPTION: [INTERNAL]
2054 * PARAMETER(S):
2055 * [I] infoPtr : valid pointer to the listview structure
2056 * [I] nItem : item number
2057 * [O] lprcBox : ptr to Box rectangle
2059 * RETURN:
2060 * None.
2062 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2064 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2065 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2066 POINT Position, Origin;
2067 LVITEMW lvItem;
2069 LISTVIEW_GetOrigin(infoPtr, &Origin);
2070 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2072 /* Be smart and try to figure out the minimum we have to do */
2073 lvItem.mask = 0;
2074 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2075 lvItem.mask |= LVIF_TEXT;
2076 lvItem.iItem = nItem;
2077 lvItem.iSubItem = 0;
2078 lvItem.pszText = szDispText;
2079 lvItem.cchTextMax = DISP_TEXT_SIZE;
2080 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2081 if (uView == LVS_ICON)
2083 lvItem.mask |= LVIF_STATE;
2084 lvItem.stateMask = LVIS_FOCUSED;
2085 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2087 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2089 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2093 /***
2094 * DESCRIPTION:
2095 * Returns the current icon position, and advances it along the top.
2096 * The returned position is not offset by Origin.
2098 * PARAMETER(S):
2099 * [I] infoPtr : valid pointer to the listview structure
2100 * [O] lpPos : will get the current icon position
2102 * RETURN:
2103 * None
2105 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2107 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2109 *lpPos = infoPtr->currIconPos;
2111 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2112 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2114 infoPtr->currIconPos.x = 0;
2115 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2119 /***
2120 * DESCRIPTION:
2121 * Returns the current icon position, and advances it down the left edge.
2122 * The returned position is not offset by Origin.
2124 * PARAMETER(S):
2125 * [I] infoPtr : valid pointer to the listview structure
2126 * [O] lpPos : will get the current icon position
2128 * RETURN:
2129 * None
2131 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2133 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2135 *lpPos = infoPtr->currIconPos;
2137 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2138 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2140 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2141 infoPtr->currIconPos.y = 0;
2145 /***
2146 * DESCRIPTION:
2147 * Moves an icon to the specified position.
2148 * It takes care of invalidating the item, etc.
2150 * PARAMETER(S):
2151 * [I] infoPtr : valid pointer to the listview structure
2152 * [I] nItem : the item to move
2153 * [I] lpPos : the new icon position
2154 * [I] isNew : flags the item as being new
2156 * RETURN:
2157 * Success: TRUE
2158 * Failure: FALSE
2160 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2162 POINT old;
2164 if (!isNew)
2166 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2167 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2169 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2170 LISTVIEW_InvalidateItem(infoPtr, nItem);
2173 /* Allocating a POINTER for every item is too resource intensive,
2174 * so we'll keep the (x,y) in different arrays */
2175 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2176 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2178 LISTVIEW_InvalidateItem(infoPtr, nItem);
2180 return TRUE;
2183 /***
2184 * DESCRIPTION:
2185 * Arranges listview items in icon display mode.
2187 * PARAMETER(S):
2188 * [I] infoPtr : valid pointer to the listview structure
2189 * [I] nAlignCode : alignment code
2191 * RETURN:
2192 * SUCCESS : TRUE
2193 * FAILURE : FALSE
2195 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2197 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2198 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2199 POINT pos;
2200 INT i;
2202 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2204 TRACE("nAlignCode=%d\n", nAlignCode);
2206 if (nAlignCode == LVA_DEFAULT)
2208 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2209 else nAlignCode = LVA_ALIGNTOP;
2212 switch (nAlignCode)
2214 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2215 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2216 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2217 default: return FALSE;
2220 infoPtr->bAutoarrange = TRUE;
2221 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2222 for (i = 0; i < infoPtr->nItemCount; i++)
2224 next_pos(infoPtr, &pos);
2225 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2228 return TRUE;
2231 /***
2232 * DESCRIPTION:
2233 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2235 * PARAMETER(S):
2236 * [I] infoPtr : valid pointer to the listview structure
2237 * [O] lprcView : bounding rectangle
2239 * RETURN:
2240 * SUCCESS : TRUE
2241 * FAILURE : FALSE
2243 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2245 INT i, x, y;
2247 SetRectEmpty(lprcView);
2249 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2251 case LVS_ICON:
2252 case LVS_SMALLICON:
2253 for (i = 0; i < infoPtr->nItemCount; i++)
2255 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2256 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2257 lprcView->right = max(lprcView->right, x);
2258 lprcView->bottom = max(lprcView->bottom, y);
2260 if (infoPtr->nItemCount > 0)
2262 lprcView->right += infoPtr->nItemWidth;
2263 lprcView->bottom += infoPtr->nItemHeight;
2265 break;
2267 case LVS_LIST:
2268 y = LISTVIEW_GetCountPerColumn(infoPtr);
2269 x = infoPtr->nItemCount / y;
2270 if (infoPtr->nItemCount % y) x++;
2271 lprcView->right = x * infoPtr->nItemWidth;
2272 lprcView->bottom = y * infoPtr->nItemHeight;
2273 break;
2275 case LVS_REPORT:
2276 lprcView->right = infoPtr->nItemWidth;
2277 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2278 break;
2282 /***
2283 * DESCRIPTION:
2284 * Retrieves the bounding rectangle of all the items.
2286 * PARAMETER(S):
2287 * [I] infoPtr : valid pointer to the listview structure
2288 * [O] lprcView : bounding rectangle
2290 * RETURN:
2291 * SUCCESS : TRUE
2292 * FAILURE : FALSE
2294 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2296 POINT ptOrigin;
2298 TRACE("(lprcView=%p)\n", lprcView);
2300 if (!lprcView) return FALSE;
2302 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2303 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2304 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2306 TRACE("lprcView=%s\n", debugrect(lprcView));
2308 return TRUE;
2311 /***
2312 * DESCRIPTION:
2313 * Retrieves the subitem pointer associated with the subitem index.
2315 * PARAMETER(S):
2316 * [I] hdpaSubItems : DPA handle for a specific item
2317 * [I] nSubItem : index of subitem
2319 * RETURN:
2320 * SUCCESS : subitem pointer
2321 * FAILURE : NULL
2323 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2325 SUBITEM_INFO *lpSubItem;
2326 INT i;
2328 /* we should binary search here if need be */
2329 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2331 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2332 if (lpSubItem->iSubItem == nSubItem)
2333 return lpSubItem;
2336 return NULL;
2340 /***
2341 * DESCRIPTION:
2342 * Caclulates the desired item width.
2344 * PARAMETER(S):
2345 * [I] infoPtr : valid pointer to the listview structure
2347 * RETURN:
2348 * The desired item width.
2350 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2352 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2353 INT nItemWidth = 0;
2355 TRACE("uView=%d\n", uView);
2357 if (uView == LVS_ICON)
2358 nItemWidth = infoPtr->iconSpacing.cx;
2359 else if (uView == LVS_REPORT)
2361 RECT rcHeader;
2363 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2365 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2366 nItemWidth = rcHeader.right;
2369 else /* LVS_SMALLICON, or LVS_LIST */
2371 INT i;
2373 for (i = 0; i < infoPtr->nItemCount; i++)
2374 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2376 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2377 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2379 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2382 return max(nItemWidth, 1);
2385 /***
2386 * DESCRIPTION:
2387 * Caclulates the desired item height.
2389 * PARAMETER(S):
2390 * [I] infoPtr : valid pointer to the listview structure
2392 * RETURN:
2393 * The desired item height.
2395 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2397 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2398 INT nItemHeight;
2400 TRACE("uView=%d\n", uView);
2402 if (uView == LVS_ICON)
2403 nItemHeight = infoPtr->iconSpacing.cy;
2404 else
2406 nItemHeight = infoPtr->ntmHeight;
2407 if (infoPtr->himlState)
2408 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2409 if (infoPtr->himlSmall)
2410 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2411 if (infoPtr->himlState || infoPtr->himlSmall)
2412 nItemHeight += HEIGHT_PADDING;
2415 return max(nItemHeight, 1);
2418 /***
2419 * DESCRIPTION:
2420 * Updates the width, and height of an item.
2422 * PARAMETER(S):
2423 * [I] infoPtr : valid pointer to the listview structure
2425 * RETURN:
2426 * None.
2428 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2430 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2431 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2435 /***
2436 * DESCRIPTION:
2437 * Retrieves and saves important text metrics info for the current
2438 * Listview font.
2440 * PARAMETER(S):
2441 * [I] infoPtr : valid pointer to the listview structure
2444 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2446 HDC hdc = GetDC(infoPtr->hwndSelf);
2447 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2448 HFONT hOldFont = SelectObject(hdc, hFont);
2449 TEXTMETRICW tm;
2451 if (GetTextMetricsW(hdc, &tm))
2453 infoPtr->ntmHeight = tm.tmHeight;
2454 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2456 SelectObject(hdc, hOldFont);
2457 ReleaseDC(infoPtr->hwndSelf, hdc);
2459 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2462 /***
2463 * DESCRIPTION:
2464 * A compare function for ranges
2466 * PARAMETER(S)
2467 * [I] range1 : pointer to range 1;
2468 * [I] range2 : pointer to range 2;
2469 * [I] flags : flags
2471 * RETURNS:
2472 * > 0 : if range 1 > range 2
2473 * < 0 : if range 2 > range 1
2474 * = 0 : if range intersects range 2
2476 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2478 INT cmp;
2480 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2481 cmp = -1;
2482 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2483 cmp = 1;
2484 else
2485 cmp = 0;
2487 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2489 return cmp;
2492 #if DEBUG_RANGES
2493 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2494 #else
2495 #define ranges_check(ranges, desc) do { } while(0)
2496 #endif
2498 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2500 INT i;
2501 RANGE *prev, *curr;
2503 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2504 assert (ranges);
2505 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2506 ranges_dump(ranges);
2507 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2508 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2509 assert (prev->lower >= 0 && prev->lower < prev->upper);
2510 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2512 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2513 assert (prev->upper <= curr->lower);
2514 assert (curr->lower < curr->upper);
2515 prev = curr;
2517 TRACE("--- Done checking---\n");
2520 static RANGES ranges_create(int count)
2522 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2523 if (!ranges) return NULL;
2524 ranges->hdpa = DPA_Create(count);
2525 if (ranges->hdpa) return ranges;
2526 Free(ranges);
2527 return NULL;
2530 static void ranges_clear(RANGES ranges)
2532 INT i;
2534 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2535 Free(DPA_GetPtr(ranges->hdpa, i));
2536 DPA_DeleteAllPtrs(ranges->hdpa);
2540 static void ranges_destroy(RANGES ranges)
2542 if (!ranges) return;
2543 ranges_clear(ranges);
2544 DPA_Destroy(ranges->hdpa);
2545 Free(ranges);
2548 static RANGES ranges_clone(RANGES ranges)
2550 RANGES clone;
2551 INT i;
2553 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2555 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2557 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2558 if (!newrng) goto fail;
2559 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2560 DPA_SetPtr(clone->hdpa, i, newrng);
2562 return clone;
2564 fail:
2565 TRACE ("clone failed\n");
2566 ranges_destroy(clone);
2567 return NULL;
2570 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2572 INT i;
2574 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2575 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2577 return ranges;
2580 static void ranges_dump(RANGES ranges)
2582 INT i;
2584 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2585 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2588 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2590 RANGE srchrng = { nItem, nItem + 1 };
2592 TRACE("(nItem=%d)\n", nItem);
2593 ranges_check(ranges, "before contain");
2594 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2597 static INT ranges_itemcount(RANGES ranges)
2599 INT i, count = 0;
2601 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2603 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2604 count += sel->upper - sel->lower;
2607 return count;
2610 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2612 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2613 INT index;
2615 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2616 if (index == -1) return TRUE;
2618 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2620 chkrng = DPA_GetPtr(ranges->hdpa, index);
2621 if (chkrng->lower >= nItem)
2622 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2623 if (chkrng->upper > nItem)
2624 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2626 return TRUE;
2629 static BOOL ranges_add(RANGES ranges, RANGE range)
2631 RANGE srchrgn;
2632 INT index;
2634 TRACE("(%s)\n", debugrange(&range));
2635 ranges_check(ranges, "before add");
2637 /* try find overlapping regions first */
2638 srchrgn.lower = range.lower - 1;
2639 srchrgn.upper = range.upper + 1;
2640 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2642 if (index == -1)
2644 RANGE *newrgn;
2646 TRACE("Adding new range\n");
2648 /* create the brand new range to insert */
2649 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2650 if(!newrgn) goto fail;
2651 *newrgn = range;
2653 /* figure out where to insert it */
2654 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2655 TRACE("index=%d\n", index);
2656 if (index == -1) index = 0;
2658 /* and get it over with */
2659 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2661 Free(newrgn);
2662 goto fail;
2665 else
2667 RANGE *chkrgn, *mrgrgn;
2668 INT fromindex, mergeindex;
2670 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2671 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2673 chkrgn->lower = min(range.lower, chkrgn->lower);
2674 chkrgn->upper = max(range.upper, chkrgn->upper);
2676 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2678 /* merge now common anges */
2679 fromindex = 0;
2680 srchrgn.lower = chkrgn->lower - 1;
2681 srchrgn.upper = chkrgn->upper + 1;
2685 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2686 if (mergeindex == -1) break;
2687 if (mergeindex == index)
2689 fromindex = index + 1;
2690 continue;
2693 TRACE("Merge with index %i\n", mergeindex);
2695 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2696 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2697 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2698 Free(mrgrgn);
2699 DPA_DeletePtr(ranges->hdpa, mergeindex);
2700 if (mergeindex < index) index --;
2701 } while(1);
2704 ranges_check(ranges, "after add");
2705 return TRUE;
2707 fail:
2708 ranges_check(ranges, "failed add");
2709 return FALSE;
2712 static BOOL ranges_del(RANGES ranges, RANGE range)
2714 RANGE *chkrgn;
2715 INT index;
2717 TRACE("(%s)\n", debugrange(&range));
2718 ranges_check(ranges, "before del");
2720 /* we don't use DPAS_SORTED here, since we need *
2721 * to find the first overlapping range */
2722 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2723 while(index != -1)
2725 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2727 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2729 /* case 1: Same range */
2730 if ( (chkrgn->upper == range.upper) &&
2731 (chkrgn->lower == range.lower) )
2733 DPA_DeletePtr(ranges->hdpa, index);
2734 break;
2736 /* case 2: engulf */
2737 else if ( (chkrgn->upper <= range.upper) &&
2738 (chkrgn->lower >= range.lower) )
2740 DPA_DeletePtr(ranges->hdpa, index);
2742 /* case 3: overlap upper */
2743 else if ( (chkrgn->upper <= range.upper) &&
2744 (chkrgn->lower < range.lower) )
2746 chkrgn->upper = range.lower;
2748 /* case 4: overlap lower */
2749 else if ( (chkrgn->upper > range.upper) &&
2750 (chkrgn->lower >= range.lower) )
2752 chkrgn->lower = range.upper;
2753 break;
2755 /* case 5: fully internal */
2756 else
2758 RANGE tmprgn = *chkrgn, *newrgn;
2760 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2761 newrgn->lower = chkrgn->lower;
2762 newrgn->upper = range.lower;
2763 chkrgn->lower = range.upper;
2764 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2766 Free(newrgn);
2767 goto fail;
2769 chkrgn = &tmprgn;
2770 break;
2773 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2776 ranges_check(ranges, "after del");
2777 return TRUE;
2779 fail:
2780 ranges_check(ranges, "failed del");
2781 return FALSE;
2784 /***
2785 * DESCRIPTION:
2786 * Removes all selection ranges
2788 * Parameters(s):
2789 * [I] infoPtr : valid pointer to the listview structure
2790 * [I] toSkip : item range to skip removing the selection
2792 * RETURNS:
2793 * SUCCESS : TRUE
2794 * FAILURE : TRUE
2796 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2798 LVITEMW lvItem;
2799 ITERATOR i;
2800 RANGES clone;
2802 TRACE("()\n");
2804 lvItem.state = 0;
2805 lvItem.stateMask = LVIS_SELECTED;
2807 /* need to clone the DPA because callbacks can change it */
2808 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2809 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2810 while(iterator_next(&i))
2811 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2812 /* note that the iterator destructor will free the cloned range */
2813 iterator_destroy(&i);
2815 return TRUE;
2818 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2820 RANGES toSkip;
2822 if (!(toSkip = ranges_create(1))) return FALSE;
2823 if (nItem != -1) ranges_additem(toSkip, nItem);
2824 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2825 ranges_destroy(toSkip);
2826 return TRUE;
2829 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2831 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2834 /***
2835 * DESCRIPTION:
2836 * Retrieves the number of items that are marked as selected.
2838 * PARAMETER(S):
2839 * [I] infoPtr : valid pointer to the listview structure
2841 * RETURN:
2842 * Number of items selected.
2844 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2846 INT nSelectedCount = 0;
2848 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2850 INT i;
2851 for (i = 0; i < infoPtr->nItemCount; i++)
2853 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2854 nSelectedCount++;
2857 else
2858 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2860 TRACE("nSelectedCount=%d\n", nSelectedCount);
2861 return nSelectedCount;
2864 /***
2865 * DESCRIPTION:
2866 * Manages the item focus.
2868 * PARAMETER(S):
2869 * [I] infoPtr : valid pointer to the listview structure
2870 * [I] nItem : item index
2872 * RETURN:
2873 * TRUE : focused item changed
2874 * FALSE : focused item has NOT changed
2876 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2878 INT oldFocus = infoPtr->nFocusedItem;
2879 LVITEMW lvItem;
2881 if (nItem == infoPtr->nFocusedItem) return FALSE;
2883 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2884 lvItem.stateMask = LVIS_FOCUSED;
2885 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2887 return oldFocus != infoPtr->nFocusedItem;
2890 /* Helper function for LISTVIEW_ShiftIndices *only* */
2891 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2893 if (nShiftItem < nItem) return nShiftItem;
2895 if (nShiftItem > nItem) return nShiftItem + direction;
2897 if (direction > 0) return nShiftItem + direction;
2899 return min(nShiftItem, infoPtr->nItemCount - 1);
2903 * DESCRIPTION:
2904 * Updates the various indices after an item has been inserted or deleted.
2906 * PARAMETER(S):
2907 * [I] infoPtr : valid pointer to the listview structure
2908 * [I] nItem : item index
2909 * [I] direction : Direction of shift, +1 or -1.
2911 * RETURN:
2912 * None
2914 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2916 INT nNewFocus;
2917 BOOL bOldChange;
2919 /* temporarily disable change notification while shifting items */
2920 bOldChange = infoPtr->bDoChangeNotify;
2921 infoPtr->bDoChangeNotify = FALSE;
2923 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2925 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2927 assert(abs(direction) == 1);
2929 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2931 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2932 if (nNewFocus != infoPtr->nFocusedItem)
2933 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2935 /* But we are not supposed to modify nHotItem! */
2937 infoPtr->bDoChangeNotify = bOldChange;
2942 * DESCRIPTION:
2943 * Adds a block of selections.
2945 * PARAMETER(S):
2946 * [I] infoPtr : valid pointer to the listview structure
2947 * [I] nItem : item index
2949 * RETURN:
2950 * None
2952 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2954 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2955 INT nLast = max(infoPtr->nSelectionMark, nItem);
2956 INT i;
2957 LVITEMW item;
2959 if (nFirst == -1) nFirst = nItem;
2961 item.state = LVIS_SELECTED;
2962 item.stateMask = LVIS_SELECTED;
2964 /* FIXME: this is not correct LVS_OWNERDATA
2965 * setting the item states individually will generate
2966 * a LVN_ITEMCHANGED notification for each one. Instead,
2967 * we have to send a LVN_ODSTATECHANGED notification.
2968 * See MSDN documentation for LVN_ITEMCHANGED.
2970 for (i = nFirst; i <= nLast; i++)
2971 LISTVIEW_SetItemState(infoPtr,i,&item);
2975 /***
2976 * DESCRIPTION:
2977 * Sets a single group selection.
2979 * PARAMETER(S):
2980 * [I] infoPtr : valid pointer to the listview structure
2981 * [I] nItem : item index
2983 * RETURN:
2984 * None
2986 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2988 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2989 RANGES selection;
2990 LVITEMW item;
2991 ITERATOR i;
2993 if (!(selection = ranges_create(100))) return;
2995 item.state = LVIS_SELECTED;
2996 item.stateMask = LVIS_SELECTED;
2998 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3000 if (infoPtr->nSelectionMark == -1)
3002 infoPtr->nSelectionMark = nItem;
3003 ranges_additem(selection, nItem);
3005 else
3007 RANGE sel;
3009 sel.lower = min(infoPtr->nSelectionMark, nItem);
3010 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3011 ranges_add(selection, sel);
3014 else
3016 RECT rcItem, rcSel, rcSelMark;
3017 POINT ptItem;
3019 rcItem.left = LVIR_BOUNDS;
3020 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3021 rcSelMark.left = LVIR_BOUNDS;
3022 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3023 UnionRect(&rcSel, &rcItem, &rcSelMark);
3024 iterator_frameditems(&i, infoPtr, &rcSel);
3025 while(iterator_next(&i))
3027 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3028 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3030 iterator_destroy(&i);
3033 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3034 iterator_rangesitems(&i, selection);
3035 while(iterator_next(&i))
3036 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3037 /* this will also destroy the selection */
3038 iterator_destroy(&i);
3040 LISTVIEW_SetItemFocus(infoPtr, nItem);
3043 /***
3044 * DESCRIPTION:
3045 * Sets a single selection.
3047 * PARAMETER(S):
3048 * [I] infoPtr : valid pointer to the listview structure
3049 * [I] nItem : item index
3051 * RETURN:
3052 * None
3054 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3056 LVITEMW lvItem;
3058 TRACE("nItem=%d\n", nItem);
3060 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3062 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3063 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3064 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3066 infoPtr->nSelectionMark = nItem;
3069 /***
3070 * DESCRIPTION:
3071 * Set selection(s) with keyboard.
3073 * PARAMETER(S):
3074 * [I] infoPtr : valid pointer to the listview structure
3075 * [I] nItem : item index
3077 * RETURN:
3078 * SUCCESS : TRUE (needs to be repainted)
3079 * FAILURE : FALSE (nothing has changed)
3081 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3083 /* FIXME: pass in the state */
3084 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3085 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3086 BOOL bResult = FALSE;
3088 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3090 if (infoPtr->dwStyle & LVS_SINGLESEL)
3092 bResult = TRUE;
3093 LISTVIEW_SetSelection(infoPtr, nItem);
3095 else
3097 if (wShift)
3099 bResult = TRUE;
3100 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3102 else if (wCtrl)
3104 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3106 else
3108 bResult = TRUE;
3109 LISTVIEW_SetSelection(infoPtr, nItem);
3112 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3115 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3116 return bResult;
3120 /***
3121 * DESCRIPTION:
3122 * Called when the mouse is being actively tracked and has hovered for a specified
3123 * amount of time
3125 * PARAMETER(S):
3126 * [I] infoPtr : valid pointer to the listview structure
3127 * [I] fwKeys : key indicator
3128 * [I] pts : mouse position
3130 * RETURN:
3131 * 0 if the message was processed, non-zero if there was an error
3133 * INFO:
3134 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3135 * over the item for a certain period of time.
3138 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3140 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3141 /* FIXME: select the item!!! */
3142 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3144 return 0;
3147 /***
3148 * DESCRIPTION:
3149 * Called whenever WM_MOUSEMOVE is received.
3151 * PARAMETER(S):
3152 * [I] infoPtr : valid pointer to the listview structure
3153 * [I] fwKeys : key indicator
3154 * [I] pts : mouse position
3156 * RETURN:
3157 * 0 if the message is processed, non-zero if there was an error
3159 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3161 TRACKMOUSEEVENT trackinfo;
3163 /* see if we are supposed to be tracking mouse hovering */
3164 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3165 /* fill in the trackinfo struct */
3166 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3167 trackinfo.dwFlags = TME_QUERY;
3168 trackinfo.hwndTrack = infoPtr->hwndSelf;
3169 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3171 /* see if we are already tracking this hwnd */
3172 _TrackMouseEvent(&trackinfo);
3174 if(!(trackinfo.dwFlags & TME_HOVER)) {
3175 trackinfo.dwFlags = TME_HOVER;
3177 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3178 _TrackMouseEvent(&trackinfo);
3182 return 0;
3186 /***
3187 * Tests wheather the item is assignable to a list with style lStyle
3189 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3191 if ( (lpLVItem->mask & LVIF_TEXT) &&
3192 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3193 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3195 return TRUE;
3199 /***
3200 * DESCRIPTION:
3201 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3203 * PARAMETER(S):
3204 * [I] infoPtr : valid pointer to the listview structure
3205 * [I] lpLVItem : valid pointer to new item atttributes
3206 * [I] isNew : the item being set is being inserted
3207 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3208 * [O] bChanged : will be set to TRUE if the item really changed
3210 * RETURN:
3211 * SUCCESS : TRUE
3212 * FAILURE : FALSE
3214 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3216 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3217 ITEM_INFO *lpItem;
3218 NMLISTVIEW nmlv;
3219 UINT uChanged = 0;
3220 LVITEMW item;
3222 TRACE("()\n");
3224 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3226 if (lpLVItem->mask == 0) return TRUE;
3228 if (infoPtr->dwStyle & LVS_OWNERDATA)
3230 /* a virtual listview we stores only selection and focus */
3231 if (lpLVItem->mask & ~LVIF_STATE)
3232 return FALSE;
3233 lpItem = NULL;
3235 else
3237 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3238 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3239 assert (lpItem);
3242 /* we need to get the lParam and state of the item */
3243 item.iItem = lpLVItem->iItem;
3244 item.iSubItem = lpLVItem->iSubItem;
3245 item.mask = LVIF_STATE | LVIF_PARAM;
3246 item.stateMask = ~0;
3247 item.state = 0;
3248 item.lParam = 0;
3249 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3251 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3252 /* determine what fields will change */
3253 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3254 uChanged |= LVIF_STATE;
3256 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3257 uChanged |= LVIF_IMAGE;
3259 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3260 uChanged |= LVIF_PARAM;
3262 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3263 uChanged |= LVIF_INDENT;
3265 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3266 uChanged |= LVIF_TEXT;
3268 TRACE("uChanged=0x%x\n", uChanged);
3269 if (!uChanged) return TRUE;
3270 *bChanged = TRUE;
3272 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3273 nmlv.iItem = lpLVItem->iItem;
3274 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3275 nmlv.uOldState = item.state;
3276 nmlv.uChanged = uChanged;
3277 nmlv.lParam = item.lParam;
3279 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3280 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3281 /* are enabled */
3282 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3283 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3284 return FALSE;
3286 /* copy information */
3287 if (lpLVItem->mask & LVIF_TEXT)
3288 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3290 if (lpLVItem->mask & LVIF_IMAGE)
3291 lpItem->hdr.iImage = lpLVItem->iImage;
3293 if (lpLVItem->mask & LVIF_PARAM)
3294 lpItem->lParam = lpLVItem->lParam;
3296 if (lpLVItem->mask & LVIF_INDENT)
3297 lpItem->iIndent = lpLVItem->iIndent;
3299 if (uChanged & LVIF_STATE)
3301 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3303 lpItem->state &= ~lpLVItem->stateMask;
3304 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3306 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3308 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3309 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3311 else if (lpLVItem->stateMask & LVIS_SELECTED)
3312 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3314 /* if we are asked to change focus, and we manage it, do it */
3315 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3317 if (lpLVItem->state & LVIS_FOCUSED)
3319 LISTVIEW_SetItemFocus(infoPtr, -1);
3320 infoPtr->nFocusedItem = lpLVItem->iItem;
3321 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3323 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3324 infoPtr->nFocusedItem = -1;
3328 /* if we're inserting the item, we're done */
3329 if (isNew) return TRUE;
3331 /* send LVN_ITEMCHANGED notification */
3332 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3333 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3335 return TRUE;
3338 /***
3339 * DESCRIPTION:
3340 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3342 * PARAMETER(S):
3343 * [I] infoPtr : valid pointer to the listview structure
3344 * [I] lpLVItem : valid pointer to new subitem atttributes
3345 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3346 * [O] bChanged : will be set to TRUE if the item really changed
3348 * RETURN:
3349 * SUCCESS : TRUE
3350 * FAILURE : FALSE
3352 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3354 HDPA hdpaSubItems;
3355 SUBITEM_INFO *lpSubItem;
3357 /* we do not support subitems for virtual listviews */
3358 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3360 /* set subitem only if column is present */
3361 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3363 /* First do some sanity checks */
3364 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3365 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3367 /* get the subitem structure, and create it if not there */
3368 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3369 assert (hdpaSubItems);
3371 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3372 if (!lpSubItem)
3374 SUBITEM_INFO *tmpSubItem;
3375 INT i;
3377 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3378 if (!lpSubItem) return FALSE;
3379 /* we could binary search here, if need be...*/
3380 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3382 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3383 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3385 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3387 Free(lpSubItem);
3388 return FALSE;
3390 lpSubItem->iSubItem = lpLVItem->iSubItem;
3391 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3392 *bChanged = TRUE;
3395 if (lpLVItem->mask & LVIF_IMAGE)
3396 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3398 lpSubItem->hdr.iImage = lpLVItem->iImage;
3399 *bChanged = TRUE;
3402 if (lpLVItem->mask & LVIF_TEXT)
3403 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3405 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3406 *bChanged = TRUE;
3409 return TRUE;
3412 /***
3413 * DESCRIPTION:
3414 * Sets item attributes.
3416 * PARAMETER(S):
3417 * [I] infoPtr : valid pointer to the listview structure
3418 * [I] lpLVItem : new item atttributes
3419 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3421 * RETURN:
3422 * SUCCESS : TRUE
3423 * FAILURE : FALSE
3425 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3427 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3428 LPWSTR pszText = NULL;
3429 BOOL bResult, bChanged = FALSE;
3431 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3433 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3434 return FALSE;
3436 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3437 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3439 pszText = lpLVItem->pszText;
3440 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3443 /* actually set the fields */
3444 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3446 if (lpLVItem->iSubItem)
3447 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3448 else
3449 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3451 /* redraw item, if necessary */
3452 if (bChanged && !infoPtr->bIsDrawing)
3454 /* this little optimization eliminates some nasty flicker */
3455 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3456 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3457 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3458 else
3459 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3461 /* restore text */
3462 if (pszText)
3464 textfreeT(lpLVItem->pszText, isW);
3465 ((LVITEMW *)lpLVItem)->pszText = pszText;
3468 return bResult;
3471 /***
3472 * DESCRIPTION:
3473 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3475 * PARAMETER(S):
3476 * [I] infoPtr : valid pointer to the listview structure
3478 * RETURN:
3479 * item index
3481 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3483 LONG lStyle = infoPtr->dwStyle;
3484 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3485 INT nItem = 0;
3486 SCROLLINFO scrollInfo;
3488 scrollInfo.cbSize = sizeof(SCROLLINFO);
3489 scrollInfo.fMask = SIF_POS;
3491 if (uView == LVS_LIST)
3493 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3494 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3496 else if (uView == LVS_REPORT)
3498 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3499 nItem = scrollInfo.nPos;
3501 else
3503 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3504 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3507 TRACE("nItem=%d\n", nItem);
3509 return nItem;
3513 /***
3514 * DESCRIPTION:
3515 * Erases the background of the given rectangle
3517 * PARAMETER(S):
3518 * [I] infoPtr : valid pointer to the listview structure
3519 * [I] hdc : device context handle
3520 * [I] lprcBox : clipping rectangle
3522 * RETURN:
3523 * Success: TRUE
3524 * Failure: FALSE
3526 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3528 if (!infoPtr->hBkBrush) return FALSE;
3530 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3532 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3535 /***
3536 * DESCRIPTION:
3537 * Draws an item.
3539 * PARAMETER(S):
3540 * [I] infoPtr : valid pointer to the listview structure
3541 * [I] hdc : device context handle
3542 * [I] nItem : item index
3543 * [I] nSubItem : subitem index
3544 * [I] pos : item position in client coordinates
3545 * [I] cdmode : custom draw mode
3547 * RETURN:
3548 * Success: TRUE
3549 * Failure: FALSE
3551 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3553 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3554 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3555 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3556 DWORD cdsubitemmode = CDRF_DODEFAULT;
3557 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3558 NMLVCUSTOMDRAW nmlvcd;
3559 HIMAGELIST himl;
3560 LVITEMW lvItem;
3562 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3564 /* get information needed for drawing the item */
3565 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3566 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3567 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3568 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3569 lvItem.iItem = nItem;
3570 lvItem.iSubItem = nSubItem;
3571 lvItem.state = 0;
3572 lvItem.lParam = 0;
3573 lvItem.cchTextMax = DISP_TEXT_SIZE;
3574 lvItem.pszText = szDispText;
3575 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3576 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3577 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3578 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3579 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3581 /* now check if we need to update the focus rectangle */
3582 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3584 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3585 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3586 OffsetRect(&rcBox, pos.x, pos.y);
3587 OffsetRect(&rcState, pos.x, pos.y);
3588 OffsetRect(&rcIcon, pos.x, pos.y);
3589 OffsetRect(&rcLabel, pos.x, pos.y);
3590 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3591 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3593 /* fill in the custom draw structure */
3594 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3596 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3597 if (cdmode & CDRF_NOTIFYITEMDRAW)
3598 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3599 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3600 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3601 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3602 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3604 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3605 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3607 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3608 prepaint_setup(infoPtr, hdc, &nmlvcd);
3610 /* in full row select, subitems, will just use main item's colors */
3611 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3612 nmlvcd.clrTextBk = CLR_NONE;
3614 /* state icons */
3615 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3617 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3618 if (uStateImage)
3620 TRACE("uStateImage=%d\n", uStateImage);
3621 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3625 /* small icons */
3626 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3627 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3629 TRACE("iImage=%d\n", lvItem.iImage);
3630 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3631 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3634 /* Don't bother painting item being edited */
3635 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3637 /* draw the selection background, if we're drawing the main item */
3638 if (nSubItem == 0)
3640 rcSelect = rcLabel;
3641 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3642 rcSelect.right = rcBox.right;
3644 if (nmlvcd.clrTextBk != CLR_NONE)
3645 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3646 if(lprcFocus) *lprcFocus = rcSelect;
3649 /* figure out the text drawing flags */
3650 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3651 if (uView == LVS_ICON)
3652 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3653 else if (nSubItem)
3655 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3657 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3658 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3659 default: uFormat |= DT_LEFT;
3662 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3664 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3665 else rcLabel.left += LABEL_HOR_PADDING;
3667 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3668 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3670 postpaint:
3671 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3672 notify_postpaint(infoPtr, &nmlvcd);
3673 return TRUE;
3676 /***
3677 * DESCRIPTION:
3678 * Draws listview items when in owner draw mode.
3680 * PARAMETER(S):
3681 * [I] infoPtr : valid pointer to the listview structure
3682 * [I] hdc : device context handle
3684 * RETURN:
3685 * None
3687 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3689 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3690 DWORD cditemmode = CDRF_DODEFAULT;
3691 NMLVCUSTOMDRAW nmlvcd;
3692 POINT Origin, Position;
3693 DRAWITEMSTRUCT dis;
3694 LVITEMW item;
3696 TRACE("()\n");
3698 ZeroMemory(&dis, sizeof(dis));
3700 /* Get scroll info once before loop */
3701 LISTVIEW_GetOrigin(infoPtr, &Origin);
3703 /* iterate through the invalidated rows */
3704 while(iterator_next(i))
3706 item.iItem = i->nItem;
3707 item.iSubItem = 0;
3708 item.mask = LVIF_PARAM | LVIF_STATE;
3709 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3710 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3712 dis.CtlType = ODT_LISTVIEW;
3713 dis.CtlID = uID;
3714 dis.itemID = item.iItem;
3715 dis.itemAction = ODA_DRAWENTIRE;
3716 dis.itemState = 0;
3717 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3718 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3719 dis.hwndItem = infoPtr->hwndSelf;
3720 dis.hDC = hdc;
3721 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3722 dis.rcItem.left = Position.x + Origin.x;
3723 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3724 dis.rcItem.top = Position.y + Origin.y;
3725 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3726 dis.itemData = item.lParam;
3728 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3731 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3732 * structure for the rest. of the paint cycle
3734 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3735 if (cdmode & CDRF_NOTIFYITEMDRAW)
3736 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3738 if (!(cditemmode & CDRF_SKIPDEFAULT))
3740 prepaint_setup (infoPtr, hdc, &nmlvcd);
3741 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3744 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3745 notify_postpaint(infoPtr, &nmlvcd);
3749 /***
3750 * DESCRIPTION:
3751 * Draws listview items when in report display mode.
3753 * PARAMETER(S):
3754 * [I] infoPtr : valid pointer to the listview structure
3755 * [I] hdc : device context handle
3756 * [I] cdmode : custom draw mode
3758 * RETURN:
3759 * None
3761 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3763 INT rgntype;
3764 RECT rcClip, rcItem;
3765 POINT Origin, Position;
3766 RANGE colRange;
3767 ITERATOR j;
3769 TRACE("()\n");
3771 /* figure out what to draw */
3772 rgntype = GetClipBox(hdc, &rcClip);
3773 if (rgntype == NULLREGION) return;
3775 /* Get scroll info once before loop */
3776 LISTVIEW_GetOrigin(infoPtr, &Origin);
3778 /* narrow down the columns we need to paint */
3779 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3781 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3782 if (rcItem.right + Origin.x >= rcClip.left) break;
3784 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3786 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3787 if (rcItem.left + Origin.x < rcClip.right) break;
3789 iterator_rangeitems(&j, colRange);
3791 /* in full row select, we _have_ to draw the main item */
3792 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3793 j.nSpecial = 0;
3795 /* iterate through the invalidated rows */
3796 while(iterator_next(i))
3798 /* iterate through the invalidated columns */
3799 while(iterator_next(&j))
3801 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3802 Position.x += Origin.x;
3803 Position.y += Origin.y;
3805 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3807 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3808 rcItem.top = 0;
3809 rcItem.bottom = infoPtr->nItemHeight;
3810 OffsetRect(&rcItem, Position.x, Position.y);
3811 if (!RectVisible(hdc, &rcItem)) continue;
3814 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3817 iterator_destroy(&j);
3820 /***
3821 * DESCRIPTION:
3822 * Draws listview items when in list display mode.
3824 * PARAMETER(S):
3825 * [I] infoPtr : valid pointer to the listview structure
3826 * [I] hdc : device context handle
3827 * [I] cdmode : custom draw mode
3829 * RETURN:
3830 * None
3832 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3834 POINT Origin, Position;
3836 /* Get scroll info once before loop */
3837 LISTVIEW_GetOrigin(infoPtr, &Origin);
3839 while(iterator_prev(i))
3841 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3842 Position.x += Origin.x;
3843 Position.y += Origin.y;
3845 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3850 /***
3851 * DESCRIPTION:
3852 * Draws listview items.
3854 * PARAMETER(S):
3855 * [I] infoPtr : valid pointer to the listview structure
3856 * [I] hdc : device context handle
3858 * RETURN:
3859 * NoneX
3861 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3863 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3864 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3865 NMLVCUSTOMDRAW nmlvcd;
3866 HFONT hOldFont;
3867 DWORD cdmode;
3868 INT oldBkMode;
3869 RECT rcClient;
3870 ITERATOR i;
3872 LISTVIEW_DUMP(infoPtr);
3874 infoPtr->bIsDrawing = TRUE;
3876 /* save dc values we're gonna trash while drawing */
3877 hOldFont = SelectObject(hdc, infoPtr->hFont);
3878 oldBkMode = GetBkMode(hdc);
3879 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3880 oldTextColor = GetTextColor(hdc);
3882 oldClrTextBk = infoPtr->clrTextBk;
3883 oldClrText = infoPtr->clrText;
3885 infoPtr->cditemmode = CDRF_DODEFAULT;
3887 GetClientRect(infoPtr->hwndSelf, &rcClient);
3888 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3889 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3890 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3891 prepaint_setup(infoPtr, hdc, &nmlvcd);
3893 /* Use these colors to draw the items */
3894 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3895 infoPtr->clrText = nmlvcd.clrText;
3897 /* nothing to draw */
3898 if(infoPtr->nItemCount == 0) goto enddraw;
3900 /* figure out what we need to draw */
3901 iterator_visibleitems(&i, infoPtr, hdc);
3903 /* send cache hint notification */
3904 if (infoPtr->dwStyle & LVS_OWNERDATA)
3906 RANGE range = iterator_range(&i);
3907 NMLVCACHEHINT nmlv;
3909 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3910 nmlv.iFrom = range.lower;
3911 nmlv.iTo = range.upper - 1;
3912 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3915 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3916 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3917 else
3919 if (uView == LVS_REPORT)
3920 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3921 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3922 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3924 /* if we have a focus rect, draw it */
3925 if (infoPtr->bFocus)
3926 DrawFocusRect(hdc, &infoPtr->rcFocus);
3928 iterator_destroy(&i);
3930 enddraw:
3931 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3932 notify_postpaint(infoPtr, &nmlvcd);
3934 infoPtr->clrTextBk = oldClrTextBk;
3935 infoPtr->clrText = oldClrText;
3937 SelectObject(hdc, hOldFont);
3938 SetBkMode(hdc, oldBkMode);
3939 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3940 SetTextColor(hdc, oldTextColor);
3941 infoPtr->bIsDrawing = FALSE;
3945 /***
3946 * DESCRIPTION:
3947 * Calculates the approximate width and height of a given number of items.
3949 * PARAMETER(S):
3950 * [I] infoPtr : valid pointer to the listview structure
3951 * [I] nItemCount : number of items
3952 * [I] wWidth : width
3953 * [I] wHeight : height
3955 * RETURN:
3956 * Returns a DWORD. The width in the low word and the height in high word.
3958 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3959 WORD wWidth, WORD wHeight)
3961 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3962 INT nItemCountPerColumn = 1;
3963 INT nColumnCount = 0;
3964 DWORD dwViewRect = 0;
3966 if (nItemCount == -1)
3967 nItemCount = infoPtr->nItemCount;
3969 if (uView == LVS_LIST)
3971 if (wHeight == 0xFFFF)
3973 /* use current height */
3974 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3977 if (wHeight < infoPtr->nItemHeight)
3978 wHeight = infoPtr->nItemHeight;
3980 if (nItemCount > 0)
3982 if (infoPtr->nItemHeight > 0)
3984 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3985 if (nItemCountPerColumn == 0)
3986 nItemCountPerColumn = 1;
3988 if (nItemCount % nItemCountPerColumn != 0)
3989 nColumnCount = nItemCount / nItemCountPerColumn;
3990 else
3991 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3995 /* Microsoft padding magic */
3996 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3997 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3999 dwViewRect = MAKELONG(wWidth, wHeight);
4001 else if (uView == LVS_REPORT)
4003 RECT rcBox;
4005 if (infoPtr->nItemCount > 0)
4007 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4008 wWidth = rcBox.right - rcBox.left;
4009 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4011 else
4013 /* use current height and width */
4014 if (wHeight == 0xffff)
4015 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4016 if (wWidth == 0xffff)
4017 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4020 dwViewRect = MAKELONG(wWidth, wHeight);
4022 else if (uView == LVS_SMALLICON)
4023 FIXME("uView == LVS_SMALLICON: not implemented\n");
4024 else if (uView == LVS_ICON)
4025 FIXME("uView == LVS_ICON: not implemented\n");
4027 return dwViewRect;
4031 /***
4032 * DESCRIPTION:
4033 * Create a drag image list for the specified item.
4035 * PARAMETER(S):
4036 * [I] infoPtr : valid pointer to the listview structure
4037 * [I] iItem : index of item
4038 * [O] lppt : Upperr-left corner of the image
4040 * RETURN:
4041 * Returns a handle to the image list if successful, NULL otherwise.
4043 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4045 RECT rcItem;
4046 SIZE size;
4047 POINT pos;
4048 HDC hdc, hdcOrig;
4049 HBITMAP hbmp, hOldbmp;
4050 HIMAGELIST dragList = 0;
4051 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4053 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4054 return 0;
4056 rcItem.left = LVIR_BOUNDS;
4057 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4058 return 0;
4060 lppt->x = rcItem.left;
4061 lppt->y = rcItem.top;
4063 size.cx = rcItem.right - rcItem.left;
4064 size.cy = rcItem.bottom - rcItem.top;
4066 hdcOrig = GetDC(infoPtr->hwndSelf);
4067 hdc = CreateCompatibleDC(hdcOrig);
4068 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4069 hOldbmp = SelectObject(hdc, hbmp);
4071 rcItem.left = rcItem.top = 0;
4072 rcItem.right = size.cx;
4073 rcItem.bottom = size.cy;
4074 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4076 pos.x = pos.y = 0;
4077 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4079 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4080 SelectObject(hdc, hOldbmp);
4081 ImageList_Add(dragList, hbmp, 0);
4083 else
4084 SelectObject(hdc, hOldbmp);
4086 DeleteObject(hbmp);
4087 DeleteDC(hdc);
4088 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4090 TRACE("ret=%p\n", dragList);
4092 return dragList;
4096 /***
4097 * DESCRIPTION:
4098 * Removes all listview items and subitems.
4100 * PARAMETER(S):
4101 * [I] infoPtr : valid pointer to the listview structure
4103 * RETURN:
4104 * SUCCESS : TRUE
4105 * FAILURE : FALSE
4107 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4109 NMLISTVIEW nmlv;
4110 HDPA hdpaSubItems = NULL;
4111 BOOL bSuppress;
4112 ITEMHDR *hdrItem;
4113 INT i, j;
4115 TRACE("()\n");
4117 /* we do it directly, to avoid notifications */
4118 ranges_clear(infoPtr->selectionRanges);
4119 infoPtr->nSelectionMark = -1;
4120 infoPtr->nFocusedItem = -1;
4121 SetRectEmpty(&infoPtr->rcFocus);
4122 /* But we are supposed to leave nHotItem as is! */
4125 /* send LVN_DELETEALLITEMS notification */
4126 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4127 nmlv.iItem = -1;
4128 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4130 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4132 /* send LVN_DELETEITEM notification, if not supressed */
4133 if (!bSuppress) notify_deleteitem(infoPtr, i);
4134 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4136 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4137 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4139 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4140 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4141 Free(hdrItem);
4143 DPA_Destroy(hdpaSubItems);
4144 DPA_DeletePtr(infoPtr->hdpaItems, i);
4146 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4147 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4148 infoPtr->nItemCount --;
4151 LISTVIEW_UpdateScroll(infoPtr);
4153 LISTVIEW_InvalidateList(infoPtr);
4155 return TRUE;
4158 /***
4159 * DESCRIPTION:
4160 * Scrolls, and updates the columns, when a column is changing width.
4162 * PARAMETER(S):
4163 * [I] infoPtr : valid pointer to the listview structure
4164 * [I] nColumn : column to scroll
4165 * [I] dx : amount of scroll, in pixels
4167 * RETURN:
4168 * None.
4170 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4172 COLUMN_INFO *lpColumnInfo;
4173 RECT rcOld, rcCol;
4174 INT nCol;
4176 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4177 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4178 rcCol = lpColumnInfo->rcHeader;
4179 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4180 rcCol.left = rcCol.right;
4182 /* ajust the other columns */
4183 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4185 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4186 lpColumnInfo->rcHeader.left += dx;
4187 lpColumnInfo->rcHeader.right += dx;
4190 /* do not update screen if not in report mode */
4191 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4193 /* if we have a focus, must first erase the focus rect */
4194 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4196 /* Need to reset the item width when inserting a new column */
4197 infoPtr->nItemWidth += dx;
4199 LISTVIEW_UpdateScroll(infoPtr);
4201 /* scroll to cover the deleted column, and invalidate for redraw */
4202 rcOld = infoPtr->rcList;
4203 rcOld.left = rcCol.left;
4204 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4206 /* we can restore focus now */
4207 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4210 /***
4211 * DESCRIPTION:
4212 * Removes a column from the listview control.
4214 * PARAMETER(S):
4215 * [I] infoPtr : valid pointer to the listview structure
4216 * [I] nColumn : column index
4218 * RETURN:
4219 * SUCCESS : TRUE
4220 * FAILURE : FALSE
4222 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4224 RECT rcCol;
4226 TRACE("nColumn=%d\n", nColumn);
4228 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4229 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4231 /* While the MSDN specifically says that column zero should not be deleted,
4232 it does in fact work on WinNT, and at least one app depends on it. On
4233 WinNT, deleting column zero deletes the last column of items but the
4234 first header. Since no app will ever depend on that bizarre behavior,
4235 we just delete the last column including the header.
4237 if (nColumn == 0)
4238 nColumn = DPA_GetPtrCount(infoPtr->hdpaColumns) - 1;
4240 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4242 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4243 return FALSE;
4245 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4246 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4248 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4250 SUBITEM_INFO *lpSubItem, *lpDelItem;
4251 HDPA hdpaSubItems;
4252 INT nItem, nSubItem, i;
4254 if (nColumn == 0)
4255 return LISTVIEW_DeleteAllItems(infoPtr);
4257 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4259 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4260 nSubItem = 0;
4261 lpDelItem = 0;
4262 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4264 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4265 if (lpSubItem->iSubItem == nColumn)
4267 nSubItem = i;
4268 lpDelItem = lpSubItem;
4270 else if (lpSubItem->iSubItem > nColumn)
4272 lpSubItem->iSubItem--;
4276 /* if we found our subitem, zapp it */
4277 if (nSubItem > 0)
4279 /* free string */
4280 if (is_textW(lpDelItem->hdr.pszText))
4281 Free(lpDelItem->hdr.pszText);
4283 /* free item */
4284 Free(lpDelItem);
4286 /* free dpa memory */
4287 DPA_DeletePtr(hdpaSubItems, nSubItem);
4292 /* update the other column info */
4293 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4295 return TRUE;
4298 /***
4299 * DESCRIPTION:
4300 * Invalidates the listview after an item's insertion or deletion.
4302 * PARAMETER(S):
4303 * [I] infoPtr : valid pointer to the listview structure
4304 * [I] nItem : item index
4305 * [I] dir : -1 if deleting, 1 if inserting
4307 * RETURN:
4308 * None
4310 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4312 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4313 INT nPerCol, nItemCol, nItemRow;
4314 RECT rcScroll;
4315 POINT Origin;
4317 /* if we don't refresh, what's the point of scrolling? */
4318 if (!is_redrawing(infoPtr)) return;
4320 assert (abs(dir) == 1);
4322 /* arrange icons if autoarrange is on */
4323 if (is_autoarrange(infoPtr))
4325 BOOL arrange = TRUE;
4326 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4327 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4328 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4331 /* scrollbars need updating */
4332 LISTVIEW_UpdateScroll(infoPtr);
4334 /* figure out the item's position */
4335 if (uView == LVS_REPORT)
4336 nPerCol = infoPtr->nItemCount + 1;
4337 else if (uView == LVS_LIST)
4338 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4339 else /* LVS_ICON, or LVS_SMALLICON */
4340 return;
4342 nItemCol = nItem / nPerCol;
4343 nItemRow = nItem % nPerCol;
4344 LISTVIEW_GetOrigin(infoPtr, &Origin);
4346 /* move the items below up a slot */
4347 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4348 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4349 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4350 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4351 OffsetRect(&rcScroll, Origin.x, Origin.y);
4352 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4353 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4355 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4356 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4357 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4360 /* report has only that column, so we're done */
4361 if (uView == LVS_REPORT) return;
4363 /* now for LISTs, we have to deal with the columns to the right */
4364 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4365 rcScroll.top = 0;
4366 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4367 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4368 OffsetRect(&rcScroll, Origin.x, Origin.y);
4369 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4370 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4371 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4374 /***
4375 * DESCRIPTION:
4376 * Removes an item from the listview control.
4378 * PARAMETER(S):
4379 * [I] infoPtr : valid pointer to the listview structure
4380 * [I] nItem : item index
4382 * RETURN:
4383 * SUCCESS : TRUE
4384 * FAILURE : FALSE
4386 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4388 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4389 LVITEMW item;
4391 TRACE("(nItem=%d)\n", nItem);
4393 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4395 /* remove selection, and focus */
4396 item.state = 0;
4397 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4398 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4400 /* send LVN_DELETEITEM notification. */
4401 notify_deleteitem(infoPtr, nItem);
4403 /* we need to do this here, because we'll be deleting stuff */
4404 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4405 LISTVIEW_InvalidateItem(infoPtr, nItem);
4407 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4409 HDPA hdpaSubItems;
4410 ITEMHDR *hdrItem;
4411 INT i;
4413 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4414 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4416 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4417 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4418 Free(hdrItem);
4420 DPA_Destroy(hdpaSubItems);
4423 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4425 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4426 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4429 infoPtr->nItemCount--;
4430 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4432 /* now is the invalidation fun */
4433 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4434 return TRUE;
4438 /***
4439 * DESCRIPTION:
4440 * Callback implementation for editlabel control
4442 * PARAMETER(S):
4443 * [I] infoPtr : valid pointer to the listview structure
4444 * [I] pszText : modified text
4445 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4447 * RETURN:
4448 * SUCCESS : TRUE
4449 * FAILURE : FALSE
4451 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4453 NMLVDISPINFOW dispInfo;
4455 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4457 ZeroMemory(&dispInfo, sizeof(dispInfo));
4458 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4459 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4460 dispInfo.item.iSubItem = 0;
4461 dispInfo.item.stateMask = ~0;
4462 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4463 /* add the text from the edit in */
4464 dispInfo.item.mask |= LVIF_TEXT;
4465 dispInfo.item.pszText = pszText;
4466 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4468 /* Do we need to update the Item Text */
4469 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4470 if (!pszText) return TRUE;
4472 ZeroMemory(&dispInfo, sizeof(dispInfo));
4473 dispInfo.item.mask = LVIF_TEXT;
4474 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4475 dispInfo.item.iSubItem = 0;
4476 dispInfo.item.pszText = pszText;
4477 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4478 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4481 /***
4482 * DESCRIPTION:
4483 * Begin in place editing of specified list view item
4485 * PARAMETER(S):
4486 * [I] infoPtr : valid pointer to the listview structure
4487 * [I] nItem : item index
4488 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4490 * RETURN:
4491 * SUCCESS : TRUE
4492 * FAILURE : FALSE
4494 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4496 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4497 NMLVDISPINFOW dispInfo;
4498 RECT rect;
4500 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4502 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4503 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4505 infoPtr->nEditLabelItem = nItem;
4507 /* Is the EditBox still there, if so remove it */
4508 if(infoPtr->hwndEdit != 0)
4510 SetFocus(infoPtr->hwndSelf);
4511 infoPtr->hwndEdit = 0;
4514 LISTVIEW_SetSelection(infoPtr, nItem);
4515 LISTVIEW_SetItemFocus(infoPtr, nItem);
4516 LISTVIEW_InvalidateItem(infoPtr, nItem);
4518 rect.left = LVIR_LABEL;
4519 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4521 ZeroMemory(&dispInfo, sizeof(dispInfo));
4522 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4523 dispInfo.item.iItem = nItem;
4524 dispInfo.item.iSubItem = 0;
4525 dispInfo.item.stateMask = ~0;
4526 dispInfo.item.pszText = szDispText;
4527 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4528 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4530 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4531 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4532 if (!infoPtr->hwndEdit) return 0;
4534 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4536 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4537 infoPtr->hwndEdit = 0;
4538 return 0;
4541 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4542 SetFocus(infoPtr->hwndEdit);
4543 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4544 return infoPtr->hwndEdit;
4548 /***
4549 * DESCRIPTION:
4550 * Ensures the specified item is visible, scrolling into view if necessary.
4552 * PARAMETER(S):
4553 * [I] infoPtr : valid pointer to the listview structure
4554 * [I] nItem : item index
4555 * [I] bPartial : partially or entirely visible
4557 * RETURN:
4558 * SUCCESS : TRUE
4559 * FAILURE : FALSE
4561 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4563 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4564 INT nScrollPosHeight = 0;
4565 INT nScrollPosWidth = 0;
4566 INT nHorzAdjust = 0;
4567 INT nVertAdjust = 0;
4568 INT nHorzDiff = 0;
4569 INT nVertDiff = 0;
4570 RECT rcItem, rcTemp;
4572 rcItem.left = LVIR_BOUNDS;
4573 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4575 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4577 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4579 /* scroll left/right, but in LVS_REPORT mode */
4580 if (uView == LVS_LIST)
4581 nScrollPosWidth = infoPtr->nItemWidth;
4582 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4583 nScrollPosWidth = 1;
4585 if (rcItem.left < infoPtr->rcList.left)
4587 nHorzAdjust = -1;
4588 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4590 else
4592 nHorzAdjust = 1;
4593 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4597 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4599 /* scroll up/down, but not in LVS_LIST mode */
4600 if (uView == LVS_REPORT)
4601 nScrollPosHeight = infoPtr->nItemHeight;
4602 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4603 nScrollPosHeight = 1;
4605 if (rcItem.top < infoPtr->rcList.top)
4607 nVertAdjust = -1;
4608 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4610 else
4612 nVertAdjust = 1;
4613 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4617 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4619 if (nScrollPosWidth)
4621 INT diff = nHorzDiff / nScrollPosWidth;
4622 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4623 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4626 if (nScrollPosHeight)
4628 INT diff = nVertDiff / nScrollPosHeight;
4629 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4630 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4633 return TRUE;
4636 /***
4637 * DESCRIPTION:
4638 * Searches for an item with specific characteristics.
4640 * PARAMETER(S):
4641 * [I] hwnd : window handle
4642 * [I] nStart : base item index
4643 * [I] lpFindInfo : item information to look for
4645 * RETURN:
4646 * SUCCESS : index of item
4647 * FAILURE : -1
4649 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4650 const LVFINDINFOW *lpFindInfo)
4652 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4653 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4654 BOOL bWrap = FALSE, bNearest = FALSE;
4655 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4656 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4657 POINT Position, Destination;
4658 LVITEMW lvItem;
4660 if (!lpFindInfo || nItem < 0) return -1;
4662 lvItem.mask = 0;
4663 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4665 lvItem.mask |= LVIF_TEXT;
4666 lvItem.pszText = szDispText;
4667 lvItem.cchTextMax = DISP_TEXT_SIZE;
4670 if (lpFindInfo->flags & LVFI_WRAP)
4671 bWrap = TRUE;
4673 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4674 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4676 POINT Origin;
4677 RECT rcArea;
4679 LISTVIEW_GetOrigin(infoPtr, &Origin);
4680 Destination.x = lpFindInfo->pt.x - Origin.x;
4681 Destination.y = lpFindInfo->pt.y - Origin.y;
4682 switch(lpFindInfo->vkDirection)
4684 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4685 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4686 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4687 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4688 case VK_HOME: Destination.x = Destination.y = 0; break;
4689 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4690 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4691 case VK_END:
4692 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4693 Destination.x = rcArea.right;
4694 Destination.y = rcArea.bottom;
4695 break;
4696 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4698 bNearest = TRUE;
4701 /* if LVFI_PARAM is specified, all other flags are ignored */
4702 if (lpFindInfo->flags & LVFI_PARAM)
4704 lvItem.mask |= LVIF_PARAM;
4705 bNearest = FALSE;
4706 lvItem.mask &= ~LVIF_TEXT;
4709 again:
4710 for (; nItem < nLast; nItem++)
4712 lvItem.iItem = nItem;
4713 lvItem.iSubItem = 0;
4714 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4716 if (lvItem.mask & LVIF_PARAM)
4718 if (lpFindInfo->lParam == lvItem.lParam)
4719 return nItem;
4720 else
4721 continue;
4724 if (lvItem.mask & LVIF_TEXT)
4726 if (lpFindInfo->flags & LVFI_PARTIAL)
4728 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4730 else
4732 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4736 if (!bNearest) return nItem;
4738 /* This is very inefficient. To do a good job here,
4739 * we need a sorted array of (x,y) item positions */
4740 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4742 /* compute the distance^2 to the destination */
4743 xdist = Destination.x - Position.x;
4744 ydist = Destination.y - Position.y;
4745 dist = xdist * xdist + ydist * ydist;
4747 /* remember the distance, and item if it's closer */
4748 if (dist < mindist)
4750 mindist = dist;
4751 nNearestItem = nItem;
4755 if (bWrap)
4757 nItem = 0;
4758 nLast = min(nStart + 1, infoPtr->nItemCount);
4759 bWrap = FALSE;
4760 goto again;
4763 return nNearestItem;
4766 /***
4767 * DESCRIPTION:
4768 * Searches for an item with specific characteristics.
4770 * PARAMETER(S):
4771 * [I] hwnd : window handle
4772 * [I] nStart : base item index
4773 * [I] lpFindInfo : item information to look for
4775 * RETURN:
4776 * SUCCESS : index of item
4777 * FAILURE : -1
4779 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4780 const LVFINDINFOA *lpFindInfo)
4782 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4783 LVFINDINFOW fiw;
4784 INT res;
4786 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4787 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4788 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4789 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4790 return res;
4793 /***
4794 * DESCRIPTION:
4795 * Retrieves the background image of the listview control.
4797 * PARAMETER(S):
4798 * [I] infoPtr : valid pointer to the listview structure
4799 * [O] lpBkImage : background image attributes
4801 * RETURN:
4802 * SUCCESS : TRUE
4803 * FAILURE : FALSE
4805 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4806 /* { */
4807 /* FIXME (listview, "empty stub!\n"); */
4808 /* return FALSE; */
4809 /* } */
4811 /***
4812 * DESCRIPTION:
4813 * Retrieves column attributes.
4815 * PARAMETER(S):
4816 * [I] infoPtr : valid pointer to the listview structure
4817 * [I] nColumn : column index
4818 * [IO] lpColumn : column information
4819 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4820 * otherwise it is in fact a LPLVCOLUMNA
4822 * RETURN:
4823 * SUCCESS : TRUE
4824 * FAILURE : FALSE
4826 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4828 COLUMN_INFO *lpColumnInfo;
4829 HDITEMW hdi;
4831 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4832 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4834 /* initialize memory */
4835 ZeroMemory(&hdi, sizeof(hdi));
4837 if (lpColumn->mask & LVCF_TEXT)
4839 hdi.mask |= HDI_TEXT;
4840 hdi.pszText = lpColumn->pszText;
4841 hdi.cchTextMax = lpColumn->cchTextMax;
4844 if (lpColumn->mask & LVCF_IMAGE)
4845 hdi.mask |= HDI_IMAGE;
4847 if (lpColumn->mask & LVCF_ORDER)
4848 hdi.mask |= HDI_ORDER;
4850 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4852 if (lpColumn->mask & LVCF_FMT)
4853 lpColumn->fmt = lpColumnInfo->fmt;
4855 if (lpColumn->mask & LVCF_WIDTH)
4856 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4858 if (lpColumn->mask & LVCF_IMAGE)
4859 lpColumn->iImage = hdi.iImage;
4861 if (lpColumn->mask & LVCF_ORDER)
4862 lpColumn->iOrder = hdi.iOrder;
4864 return TRUE;
4868 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4870 INT i;
4872 if (!lpiArray)
4873 return FALSE;
4875 /* FIXME: little hack */
4876 for (i = 0; i < iCount; i++)
4877 lpiArray[i] = i;
4879 return TRUE;
4882 /***
4883 * DESCRIPTION:
4884 * Retrieves the column width.
4886 * PARAMETER(S):
4887 * [I] infoPtr : valid pointer to the listview structure
4888 * [I] int : column index
4890 * RETURN:
4891 * SUCCESS : column width
4892 * FAILURE : zero
4894 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4896 INT nColumnWidth = 0;
4897 RECT rcHeader;
4899 TRACE("nColumn=%d\n", nColumn);
4901 /* we have a 'column' in LIST and REPORT mode only */
4902 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4904 case LVS_LIST:
4905 nColumnWidth = infoPtr->nItemWidth;
4906 break;
4907 case LVS_REPORT:
4908 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4909 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4910 nColumnWidth = rcHeader.right - rcHeader.left;
4911 break;
4914 TRACE("nColumnWidth=%d\n", nColumnWidth);
4915 return nColumnWidth;
4918 /***
4919 * DESCRIPTION:
4920 * In list or report display mode, retrieves the number of items that can fit
4921 * vertically in the visible area. In icon or small icon display mode,
4922 * retrieves the total number of visible items.
4924 * PARAMETER(S):
4925 * [I] infoPtr : valid pointer to the listview structure
4927 * RETURN:
4928 * Number of fully visible items.
4930 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4932 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4934 case LVS_ICON:
4935 case LVS_SMALLICON:
4936 return infoPtr->nItemCount;
4937 case LVS_REPORT:
4938 return LISTVIEW_GetCountPerColumn(infoPtr);
4939 case LVS_LIST:
4940 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4942 assert(FALSE);
4943 return 0;
4946 /***
4947 * DESCRIPTION:
4948 * Retrieves an image list handle.
4950 * PARAMETER(S):
4951 * [I] infoPtr : valid pointer to the listview structure
4952 * [I] nImageList : image list identifier
4954 * RETURN:
4955 * SUCCESS : image list handle
4956 * FAILURE : NULL
4958 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4960 switch (nImageList)
4962 case LVSIL_NORMAL: return infoPtr->himlNormal;
4963 case LVSIL_SMALL: return infoPtr->himlSmall;
4964 case LVSIL_STATE: return infoPtr->himlState;
4966 return NULL;
4969 /* LISTVIEW_GetISearchString */
4971 /***
4972 * DESCRIPTION:
4973 * Retrieves item attributes.
4975 * PARAMETER(S):
4976 * [I] hwnd : window handle
4977 * [IO] lpLVItem : item info
4978 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4979 * if FALSE, the lpLVItem is a LPLVITEMA.
4981 * NOTE:
4982 * This is the internal 'GetItem' interface -- it tries to
4983 * be smart, and avoids text copies, if possible, by modifing
4984 * lpLVItem->pszText to point to the text string. Please note
4985 * that this is not always possible (e.g. OWNERDATA), so on
4986 * entry you *must* supply valid values for pszText, and cchTextMax.
4987 * The only difference to the documented interface is that upon
4988 * return, you should use *only* the lpLVItem->pszText, rather than
4989 * the buffer pointer you provided on input. Most code already does
4990 * that, so it's not a problem.
4991 * For the two cases when the text must be copied (that is,
4992 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
4994 * RETURN:
4995 * SUCCESS : TRUE
4996 * FAILURE : FALSE
4998 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5000 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5001 NMLVDISPINFOW dispInfo;
5002 ITEM_INFO *lpItem;
5003 ITEMHDR* pItemHdr;
5004 HDPA hdpaSubItems;
5005 INT isubitem;
5007 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5009 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5010 return FALSE;
5012 if (lpLVItem->mask == 0) return TRUE;
5014 /* make a local copy */
5015 isubitem = lpLVItem->iSubItem;
5017 /* a quick optimization if all we're asked is the focus state
5018 * these queries are worth optimising since they are common,
5019 * and can be answered in constant time, without the heavy accesses */
5020 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5021 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5023 lpLVItem->state = 0;
5024 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5025 lpLVItem->state |= LVIS_FOCUSED;
5026 return TRUE;
5029 ZeroMemory(&dispInfo, sizeof(dispInfo));
5031 /* if the app stores all the data, handle it separately */
5032 if (infoPtr->dwStyle & LVS_OWNERDATA)
5034 dispInfo.item.state = 0;
5036 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5037 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5039 /* NOTE: copy only fields which we _know_ are initialized, some apps
5040 * depend on the uninitialized fields being 0 */
5041 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5042 dispInfo.item.iItem = lpLVItem->iItem;
5043 dispInfo.item.iSubItem = isubitem;
5044 if (lpLVItem->mask & LVIF_TEXT)
5046 dispInfo.item.pszText = lpLVItem->pszText;
5047 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5049 if (lpLVItem->mask & LVIF_STATE)
5050 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5051 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5052 dispInfo.item.stateMask = lpLVItem->stateMask;
5053 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5055 /* full size structure expected - _WIN32IE >= 0x560 */
5056 *lpLVItem = dispInfo.item;
5058 else if (lpLVItem->mask & LVIF_INDENT)
5060 /* indent member expected - _WIN32IE >= 0x300 */
5061 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5063 else
5065 /* minimal structure expected */
5066 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5068 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5071 /* make sure lParam is zeroed out */
5072 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5074 /* we store only a little state, so if we're not asked, we're done */
5075 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5077 /* if focus is handled by us, report it */
5078 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5080 lpLVItem->state &= ~LVIS_FOCUSED;
5081 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5082 lpLVItem->state |= LVIS_FOCUSED;
5085 /* and do the same for selection, if we handle it */
5086 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5088 lpLVItem->state &= ~LVIS_SELECTED;
5089 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5090 lpLVItem->state |= LVIS_SELECTED;
5093 return TRUE;
5096 /* find the item and subitem structures before we proceed */
5097 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5098 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5099 assert (lpItem);
5101 if (isubitem)
5103 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5104 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5105 if (!lpSubItem)
5107 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5108 isubitem = 0;
5111 else
5112 pItemHdr = &lpItem->hdr;
5114 /* Do we need to query the state from the app? */
5115 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5117 dispInfo.item.mask |= LVIF_STATE;
5118 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5121 /* Do we need to enquire about the image? */
5122 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5123 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5125 dispInfo.item.mask |= LVIF_IMAGE;
5126 dispInfo.item.iImage = I_IMAGECALLBACK;
5129 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5130 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5132 dispInfo.item.mask |= LVIF_TEXT;
5133 dispInfo.item.pszText = lpLVItem->pszText;
5134 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5135 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5136 *dispInfo.item.pszText = '\0';
5139 /* If we don't have all the requested info, query the application */
5140 if (dispInfo.item.mask != 0)
5142 dispInfo.item.iItem = lpLVItem->iItem;
5143 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5144 dispInfo.item.lParam = lpItem->lParam;
5145 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5146 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5149 /* we should not store values for subitems */
5150 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5152 /* Now, handle the iImage field */
5153 if (dispInfo.item.mask & LVIF_IMAGE)
5155 lpLVItem->iImage = dispInfo.item.iImage;
5156 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5157 pItemHdr->iImage = dispInfo.item.iImage;
5159 else if (lpLVItem->mask & LVIF_IMAGE)
5161 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5162 lpLVItem->iImage = pItemHdr->iImage;
5163 else
5164 lpLVItem->iImage = 0;
5167 /* The pszText field */
5168 if (dispInfo.item.mask & LVIF_TEXT)
5170 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5171 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5173 lpLVItem->pszText = dispInfo.item.pszText;
5175 else if (lpLVItem->mask & LVIF_TEXT)
5177 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5178 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5181 /* if this is a subitem, we're done */
5182 if (isubitem) return TRUE;
5184 /* Next is the lParam field */
5185 if (dispInfo.item.mask & LVIF_PARAM)
5187 lpLVItem->lParam = dispInfo.item.lParam;
5188 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5189 lpItem->lParam = dispInfo.item.lParam;
5191 else if (lpLVItem->mask & LVIF_PARAM)
5192 lpLVItem->lParam = lpItem->lParam;
5194 /* ... the state field (this one is different due to uCallbackmask) */
5195 if (lpLVItem->mask & LVIF_STATE)
5197 lpLVItem->state = lpItem->state;
5198 if (dispInfo.item.mask & LVIF_STATE)
5200 lpLVItem->state &= ~dispInfo.item.stateMask;
5201 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5203 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5205 lpLVItem->state &= ~LVIS_FOCUSED;
5206 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5207 lpLVItem->state |= LVIS_FOCUSED;
5209 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5211 lpLVItem->state &= ~LVIS_SELECTED;
5212 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5213 lpLVItem->state |= LVIS_SELECTED;
5217 /* and last, but not least, the indent field */
5218 if (lpLVItem->mask & LVIF_INDENT)
5219 lpLVItem->iIndent = lpItem->iIndent;
5221 return TRUE;
5224 /***
5225 * DESCRIPTION:
5226 * Retrieves item attributes.
5228 * PARAMETER(S):
5229 * [I] hwnd : window handle
5230 * [IO] lpLVItem : item info
5231 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5232 * if FALSE, the lpLVItem is a LPLVITEMA.
5234 * NOTE:
5235 * This is the external 'GetItem' interface -- it properly copies
5236 * the text in the provided buffer.
5238 * RETURN:
5239 * SUCCESS : TRUE
5240 * FAILURE : FALSE
5242 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5244 LPWSTR pszText;
5245 BOOL bResult;
5247 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5248 return FALSE;
5250 pszText = lpLVItem->pszText;
5251 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5252 if (bResult && lpLVItem->pszText != pszText)
5253 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5254 lpLVItem->pszText = pszText;
5256 return bResult;
5260 /***
5261 * DESCRIPTION:
5262 * Retrieves the position (upper-left) of the listview control item.
5263 * Note that for LVS_ICON style, the upper-left is that of the icon
5264 * and not the bounding box.
5266 * PARAMETER(S):
5267 * [I] infoPtr : valid pointer to the listview structure
5268 * [I] nItem : item index
5269 * [O] lpptPosition : coordinate information
5271 * RETURN:
5272 * SUCCESS : TRUE
5273 * FAILURE : FALSE
5275 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5277 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5278 POINT Origin;
5280 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5282 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5284 LISTVIEW_GetOrigin(infoPtr, &Origin);
5285 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5287 if (uView == LVS_ICON)
5289 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5290 lpptPosition->y += ICON_TOP_PADDING;
5292 lpptPosition->x += Origin.x;
5293 lpptPosition->y += Origin.y;
5295 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5296 return TRUE;
5300 /***
5301 * DESCRIPTION:
5302 * Retrieves the bounding rectangle for a listview control item.
5304 * PARAMETER(S):
5305 * [I] infoPtr : valid pointer to the listview structure
5306 * [I] nItem : item index
5307 * [IO] lprc : bounding rectangle coordinates
5308 * lprc->left specifies the portion of the item for which the bounding
5309 * rectangle will be retrieved.
5311 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5312 * including the icon and label.
5314 * * For LVS_ICON
5315 * * Experiment shows that native control returns:
5316 * * width = min (48, length of text line)
5317 * * .left = position.x - (width - iconsize.cx)/2
5318 * * .right = .left + width
5319 * * height = #lines of text * ntmHeight + icon height + 8
5320 * * .top = position.y - 2
5321 * * .bottom = .top + height
5322 * * separation between items .y = itemSpacing.cy - height
5323 * * .x = itemSpacing.cx - width
5324 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5326 * * For LVS_ICON
5327 * * Experiment shows that native control returns:
5328 * * width = iconSize.cx + 16
5329 * * .left = position.x - (width - iconsize.cx)/2
5330 * * .right = .left + width
5331 * * height = iconSize.cy + 4
5332 * * .top = position.y - 2
5333 * * .bottom = .top + height
5334 * * separation between items .y = itemSpacing.cy - height
5335 * * .x = itemSpacing.cx - width
5336 * LVIR_LABEL Returns the bounding rectangle of the item text.
5338 * * For LVS_ICON
5339 * * Experiment shows that native control returns:
5340 * * width = text length
5341 * * .left = position.x - width/2
5342 * * .right = .left + width
5343 * * height = ntmH * linecount + 2
5344 * * .top = position.y + iconSize.cy + 6
5345 * * .bottom = .top + height
5346 * * separation between items .y = itemSpacing.cy - height
5347 * * .x = itemSpacing.cx - width
5348 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5349 * rectangles, but excludes columns in report view.
5351 * RETURN:
5352 * SUCCESS : TRUE
5353 * FAILURE : FALSE
5355 * NOTES
5356 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5357 * upon whether the window has the focus currently and on whether the item
5358 * is the one with the focus. Ensure that the control's record of which
5359 * item has the focus agrees with the items' records.
5361 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5363 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5364 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5365 BOOL doLabel = TRUE, oversizedBox = FALSE;
5366 POINT Position, Origin;
5367 LVITEMW lvItem;
5368 RECT label_rect;
5370 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5372 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5374 LISTVIEW_GetOrigin(infoPtr, &Origin);
5375 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5377 /* Be smart and try to figure out the minimum we have to do */
5378 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5379 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5380 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5381 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5382 oversizedBox = TRUE;
5384 /* get what we need from the item before hand, so we make
5385 * only one request. This can speed up things, if data
5386 * is stored on the app side */
5387 lvItem.mask = 0;
5388 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5389 if (doLabel) lvItem.mask |= LVIF_TEXT;
5390 lvItem.iItem = nItem;
5391 lvItem.iSubItem = 0;
5392 lvItem.pszText = szDispText;
5393 lvItem.cchTextMax = DISP_TEXT_SIZE;
5394 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5395 /* we got the state already up, simulate it here, to avoid a reget */
5396 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5398 lvItem.mask |= LVIF_STATE;
5399 lvItem.stateMask = LVIS_FOCUSED;
5400 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5403 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5404 lprc->left = LVIR_BOUNDS;
5405 switch(lprc->left)
5407 case LVIR_ICON:
5408 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5409 break;
5411 case LVIR_LABEL:
5412 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5413 break;
5415 case LVIR_BOUNDS:
5416 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5417 break;
5419 case LVIR_SELECTBOUNDS:
5420 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5421 UnionRect(lprc, lprc, &label_rect);
5422 break;
5424 default:
5425 WARN("Unknown value: %ld\n", lprc->left);
5426 return FALSE;
5429 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5431 TRACE(" rect=%s\n", debugrect(lprc));
5433 return TRUE;
5436 /***
5437 * DESCRIPTION:
5438 * Retrieves the spacing between listview control items.
5440 * PARAMETER(S):
5441 * [I] infoPtr : valid pointer to the listview structure
5442 * [IO] lprc : rectangle to receive the output
5443 * on input, lprc->top = nSubItem
5444 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5446 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5447 * not only those of the first column.
5448 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5450 * RETURN:
5451 * TRUE: success
5452 * FALSE: failure
5454 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5456 POINT Position;
5457 LVITEMW lvItem;
5459 if (!lprc) return FALSE;
5461 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5462 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5463 if (lprc->top == 0)
5464 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5466 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5468 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5470 lvItem.mask = 0;
5471 lvItem.iItem = nItem;
5472 lvItem.iSubItem = lprc->top;
5474 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5475 switch(lprc->left)
5477 case LVIR_ICON:
5478 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5479 break;
5481 case LVIR_LABEL:
5482 case LVIR_BOUNDS:
5483 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5484 break;
5486 default:
5487 ERR("Unknown bounds=%ld\n", lprc->left);
5488 return FALSE;
5491 OffsetRect(lprc, Position.x, Position.y);
5492 return TRUE;
5496 /***
5497 * DESCRIPTION:
5498 * Retrieves the width of a label.
5500 * PARAMETER(S):
5501 * [I] infoPtr : valid pointer to the listview structure
5503 * RETURN:
5504 * SUCCESS : string width (in pixels)
5505 * FAILURE : zero
5507 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5509 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5510 LVITEMW lvItem;
5512 TRACE("(nItem=%d)\n", nItem);
5514 lvItem.mask = LVIF_TEXT;
5515 lvItem.iItem = nItem;
5516 lvItem.iSubItem = 0;
5517 lvItem.pszText = szDispText;
5518 lvItem.cchTextMax = DISP_TEXT_SIZE;
5519 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5521 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5524 /***
5525 * DESCRIPTION:
5526 * Retrieves the spacing between listview control items.
5528 * PARAMETER(S):
5529 * [I] infoPtr : valid pointer to the listview structure
5530 * [I] bSmall : flag for small or large icon
5532 * RETURN:
5533 * Horizontal + vertical spacing
5535 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5537 LONG lResult;
5539 if (!bSmall)
5541 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5543 else
5545 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5546 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5547 else
5548 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5550 return lResult;
5553 /***
5554 * DESCRIPTION:
5555 * Retrieves the state of a listview control item.
5557 * PARAMETER(S):
5558 * [I] infoPtr : valid pointer to the listview structure
5559 * [I] nItem : item index
5560 * [I] uMask : state mask
5562 * RETURN:
5563 * State specified by the mask.
5565 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5567 LVITEMW lvItem;
5569 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5571 lvItem.iItem = nItem;
5572 lvItem.iSubItem = 0;
5573 lvItem.mask = LVIF_STATE;
5574 lvItem.stateMask = uMask;
5575 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5577 return lvItem.state & uMask;
5580 /***
5581 * DESCRIPTION:
5582 * Retrieves the text of a listview control item or subitem.
5584 * PARAMETER(S):
5585 * [I] hwnd : window handle
5586 * [I] nItem : item index
5587 * [IO] lpLVItem : item information
5588 * [I] isW : TRUE if lpLVItem is Unicode
5590 * RETURN:
5591 * SUCCESS : string length
5592 * FAILURE : 0
5594 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5596 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5598 lpLVItem->mask = LVIF_TEXT;
5599 lpLVItem->iItem = nItem;
5600 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5602 return textlenT(lpLVItem->pszText, isW);
5605 /***
5606 * DESCRIPTION:
5607 * Searches for an item based on properties + relationships.
5609 * PARAMETER(S):
5610 * [I] infoPtr : valid pointer to the listview structure
5611 * [I] nItem : item index
5612 * [I] uFlags : relationship flag
5614 * RETURN:
5615 * SUCCESS : item index
5616 * FAILURE : -1
5618 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5620 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5621 UINT uMask = 0;
5622 LVFINDINFOW lvFindInfo;
5623 INT nCountPerColumn;
5624 INT nCountPerRow;
5625 INT i;
5627 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5628 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5630 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5632 if (uFlags & LVNI_CUT)
5633 uMask |= LVIS_CUT;
5635 if (uFlags & LVNI_DROPHILITED)
5636 uMask |= LVIS_DROPHILITED;
5638 if (uFlags & LVNI_FOCUSED)
5639 uMask |= LVIS_FOCUSED;
5641 if (uFlags & LVNI_SELECTED)
5642 uMask |= LVIS_SELECTED;
5644 /* if we're asked for the focused item, that's only one,
5645 * so it's worth optimizing */
5646 if (uFlags & LVNI_FOCUSED)
5648 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5649 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5652 if (uFlags & LVNI_ABOVE)
5654 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5656 while (nItem >= 0)
5658 nItem--;
5659 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5660 return nItem;
5663 else
5665 /* Special case for autoarrange - move 'til the top of a list */
5666 if (is_autoarrange(infoPtr))
5668 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5669 while (nItem - nCountPerRow >= 0)
5671 nItem -= nCountPerRow;
5672 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5673 return nItem;
5675 return -1;
5677 lvFindInfo.flags = LVFI_NEARESTXY;
5678 lvFindInfo.vkDirection = VK_UP;
5679 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5680 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5682 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5683 return nItem;
5687 else if (uFlags & LVNI_BELOW)
5689 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5691 while (nItem < infoPtr->nItemCount)
5693 nItem++;
5694 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5695 return nItem;
5698 else
5700 /* Special case for autoarrange - move 'til the bottom of a list */
5701 if (is_autoarrange(infoPtr))
5703 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5704 while (nItem + nCountPerRow < infoPtr->nItemCount )
5706 nItem += nCountPerRow;
5707 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5708 return nItem;
5710 return -1;
5712 lvFindInfo.flags = LVFI_NEARESTXY;
5713 lvFindInfo.vkDirection = VK_DOWN;
5714 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5715 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5717 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5718 return nItem;
5722 else if (uFlags & LVNI_TOLEFT)
5724 if (uView == LVS_LIST)
5726 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5727 while (nItem - nCountPerColumn >= 0)
5729 nItem -= nCountPerColumn;
5730 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5731 return nItem;
5734 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5736 /* Special case for autoarrange - move 'ti the beginning of a row */
5737 if (is_autoarrange(infoPtr))
5739 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5740 while (nItem % nCountPerRow > 0)
5742 nItem --;
5743 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5744 return nItem;
5746 return -1;
5748 lvFindInfo.flags = LVFI_NEARESTXY;
5749 lvFindInfo.vkDirection = VK_LEFT;
5750 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5751 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5753 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5754 return nItem;
5758 else if (uFlags & LVNI_TORIGHT)
5760 if (uView == LVS_LIST)
5762 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5763 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5765 nItem += nCountPerColumn;
5766 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5767 return nItem;
5770 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5772 /* Special case for autoarrange - move 'til the end of a row */
5773 if (is_autoarrange(infoPtr))
5775 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5776 while (nItem % nCountPerRow < nCountPerRow - 1 )
5778 nItem ++;
5779 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5780 return nItem;
5782 return -1;
5784 lvFindInfo.flags = LVFI_NEARESTXY;
5785 lvFindInfo.vkDirection = VK_RIGHT;
5786 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5787 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5789 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5790 return nItem;
5794 else
5796 nItem++;
5798 /* search by index */
5799 for (i = nItem; i < infoPtr->nItemCount; i++)
5801 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5802 return i;
5806 return -1;
5809 /* LISTVIEW_GetNumberOfWorkAreas */
5811 /***
5812 * DESCRIPTION:
5813 * Retrieves the origin coordinates when in icon or small icon display mode.
5815 * PARAMETER(S):
5816 * [I] infoPtr : valid pointer to the listview structure
5817 * [O] lpptOrigin : coordinate information
5819 * RETURN:
5820 * None.
5822 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5824 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5825 INT nHorzPos = 0, nVertPos = 0;
5826 SCROLLINFO scrollInfo;
5828 scrollInfo.cbSize = sizeof(SCROLLINFO);
5829 scrollInfo.fMask = SIF_POS;
5831 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5832 nHorzPos = scrollInfo.nPos;
5833 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5834 nVertPos = scrollInfo.nPos;
5836 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5838 lpptOrigin->x = infoPtr->rcList.left;
5839 lpptOrigin->y = infoPtr->rcList.top;
5840 if (uView == LVS_LIST)
5841 nHorzPos *= infoPtr->nItemWidth;
5842 else if (uView == LVS_REPORT)
5843 nVertPos *= infoPtr->nItemHeight;
5845 lpptOrigin->x -= nHorzPos;
5846 lpptOrigin->y -= nVertPos;
5848 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5851 /***
5852 * DESCRIPTION:
5853 * Retrieves the width of a string.
5855 * PARAMETER(S):
5856 * [I] hwnd : window handle
5857 * [I] lpszText : text string to process
5858 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5860 * RETURN:
5861 * SUCCESS : string width (in pixels)
5862 * FAILURE : zero
5864 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5866 SIZE stringSize;
5868 stringSize.cx = 0;
5869 if (is_textT(lpszText, isW))
5871 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5872 HDC hdc = GetDC(infoPtr->hwndSelf);
5873 HFONT hOldFont = SelectObject(hdc, hFont);
5875 if (isW)
5876 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5877 else
5878 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5879 SelectObject(hdc, hOldFont);
5880 ReleaseDC(infoPtr->hwndSelf, hdc);
5882 return stringSize.cx;
5885 /***
5886 * DESCRIPTION:
5887 * Determines which listview item is located at the specified position.
5889 * PARAMETER(S):
5890 * [I] infoPtr : valid pointer to the listview structure
5891 * [IO] lpht : hit test information
5892 * [I] subitem : fill out iSubItem.
5893 * [I] select : return the index only if the hit selects the item
5895 * NOTE:
5896 * (mm 20001022): We must not allow iSubItem to be touched, for
5897 * an app might pass only a structure with space up to iItem!
5898 * (MS Office 97 does that for instance in the file open dialog)
5900 * RETURN:
5901 * SUCCESS : item index
5902 * FAILURE : -1
5904 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5906 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5907 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5908 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5909 POINT Origin, Position, opt;
5910 LVITEMW lvItem;
5911 ITERATOR i;
5912 INT iItem;
5914 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5916 lpht->flags = 0;
5917 lpht->iItem = -1;
5918 if (subitem) lpht->iSubItem = 0;
5920 if (infoPtr->rcList.left > lpht->pt.x)
5921 lpht->flags |= LVHT_TOLEFT;
5922 else if (infoPtr->rcList.right < lpht->pt.x)
5923 lpht->flags |= LVHT_TORIGHT;
5925 if (infoPtr->rcList.top > lpht->pt.y)
5926 lpht->flags |= LVHT_ABOVE;
5927 else if (infoPtr->rcList.bottom < lpht->pt.y)
5928 lpht->flags |= LVHT_BELOW;
5930 TRACE("lpht->flags=0x%x\n", lpht->flags);
5931 if (lpht->flags) return -1;
5933 lpht->flags |= LVHT_NOWHERE;
5935 LISTVIEW_GetOrigin(infoPtr, &Origin);
5937 /* first deal with the large items */
5938 rcSearch.left = lpht->pt.x;
5939 rcSearch.top = lpht->pt.y;
5940 rcSearch.right = rcSearch.left + 1;
5941 rcSearch.bottom = rcSearch.top + 1;
5943 iterator_frameditems(&i, infoPtr, &rcSearch);
5944 iterator_next(&i); /* go to first item in the sequence */
5945 iItem = i.nItem;
5946 iterator_destroy(&i);
5948 TRACE("lpht->iItem=%d\n", iItem);
5949 if (iItem == -1) return -1;
5951 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5952 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5953 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5954 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5955 lvItem.iItem = iItem;
5956 lvItem.iSubItem = 0;
5957 lvItem.pszText = szDispText;
5958 lvItem.cchTextMax = DISP_TEXT_SIZE;
5959 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5960 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5962 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5963 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5964 opt.x = lpht->pt.x - Position.x - Origin.x;
5965 opt.y = lpht->pt.y - Position.y - Origin.y;
5967 if (uView == LVS_REPORT)
5968 rcBounds = rcBox;
5969 else
5970 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5971 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5972 if (!PtInRect(&rcBounds, opt)) return -1;
5974 if (PtInRect(&rcIcon, opt))
5975 lpht->flags |= LVHT_ONITEMICON;
5976 else if (PtInRect(&rcLabel, opt))
5977 lpht->flags |= LVHT_ONITEMLABEL;
5978 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5979 lpht->flags |= LVHT_ONITEMSTATEICON;
5980 if (lpht->flags & LVHT_ONITEM)
5981 lpht->flags &= ~LVHT_NOWHERE;
5983 TRACE("lpht->flags=0x%x\n", lpht->flags);
5984 if (uView == LVS_REPORT && subitem)
5986 INT j;
5988 rcBounds.right = rcBounds.left;
5989 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
5991 rcBounds.left = rcBounds.right;
5992 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5993 if (PtInRect(&rcBounds, opt))
5995 lpht->iSubItem = j;
5996 break;
6001 if (select && !(uView == LVS_REPORT &&
6002 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6003 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6005 if (uView == LVS_REPORT)
6007 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6008 UnionRect(&rcBounds, &rcBounds, &rcState);
6010 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6012 return lpht->iItem = iItem;
6016 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6017 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6018 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6019 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6020 their own sort proc. when sending LVM_SORTITEMS.
6022 /* Platform SDK:
6023 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6025 LVS_SORTXXX must be specified,
6026 LVS_OWNERDRAW is not set,
6027 <item>.pszText is not LPSTR_TEXTCALLBACK.
6029 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6030 are sorted based on item text..."
6032 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6034 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6035 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6036 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6038 /* if we're sorting descending, negate the return value */
6039 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6042 /***
6043 * nESCRIPTION:
6044 * Inserts a new item in the listview control.
6046 * PARAMETER(S):
6047 * [I] infoPtr : valid pointer to the listview structure
6048 * [I] lpLVItem : item information
6049 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6051 * RETURN:
6052 * SUCCESS : new item index
6053 * FAILURE : -1
6055 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6057 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6058 INT nItem;
6059 HDPA hdpaSubItems;
6060 NMLISTVIEW nmlv;
6061 ITEM_INFO *lpItem;
6062 BOOL is_sorted, has_changed;
6063 LVITEMW item;
6065 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6067 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6069 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6070 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6072 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6074 if ( !(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO))) )
6075 return -1;
6077 /* insert item in listview control data structure */
6078 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6079 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6081 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6082 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6084 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6085 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6086 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6087 if (nItem == -1) goto fail;
6088 infoPtr->nItemCount++;
6090 /* shift indices first so they don't get tangled */
6091 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6093 /* set the item attributes */
6094 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6096 /* full size structure expected - _WIN32IE >= 0x560 */
6097 item = *lpLVItem;
6099 else if (lpLVItem->mask & LVIF_INDENT)
6101 /* indent member expected - _WIN32IE >= 0x300 */
6102 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6104 else
6106 /* minimal structure expected */
6107 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6109 item.iItem = nItem;
6110 item.state &= ~LVIS_STATEIMAGEMASK;
6111 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6113 /* if we're sorted, sort the list, and update the index */
6114 if (is_sorted)
6116 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6117 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6118 assert(nItem != -1);
6121 /* make room for the position, if we are in the right mode */
6122 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6124 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6125 goto undo;
6126 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6128 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6129 goto undo;
6133 /* send LVN_INSERTITEM notification */
6134 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6135 nmlv.iItem = nItem;
6136 nmlv.lParam = lpItem->lParam;
6137 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6139 /* align items (set position of each item) */
6140 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6142 POINT pt;
6144 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6145 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6146 else
6147 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6149 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6152 /* now is the invalidation fun */
6153 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6154 return nItem;
6156 undo:
6157 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6158 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6159 infoPtr->nItemCount--;
6160 fail:
6161 DPA_DeletePtr(hdpaSubItems, 0);
6162 DPA_Destroy (hdpaSubItems);
6163 Free (lpItem);
6164 return -1;
6167 /***
6168 * DESCRIPTION:
6169 * Redraws a range of items.
6171 * PARAMETER(S):
6172 * [I] infoPtr : valid pointer to the listview structure
6173 * [I] nFirst : first item
6174 * [I] nLast : last item
6176 * RETURN:
6177 * SUCCESS : TRUE
6178 * FAILURE : FALSE
6180 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6182 INT i;
6184 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6185 max(nFirst, nLast) >= infoPtr->nItemCount)
6186 return FALSE;
6188 for (i = nFirst; i <= nLast; i++)
6189 LISTVIEW_InvalidateItem(infoPtr, i);
6191 return TRUE;
6194 /***
6195 * DESCRIPTION:
6196 * Scroll the content of a listview.
6198 * PARAMETER(S):
6199 * [I] infoPtr : valid pointer to the listview structure
6200 * [I] dx : horizontal scroll amount in pixels
6201 * [I] dy : vertical scroll amount in pixels
6203 * RETURN:
6204 * SUCCESS : TRUE
6205 * FAILURE : FALSE
6207 * COMMENTS:
6208 * If the control is in report mode (LVS_REPORT) the control can
6209 * be scrolled only in line increments. "dy" will be rounded to the
6210 * nearest number of pixels that are a whole line. Ex: if line height
6211 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6212 * is passed the the scroll will be 0. (per MSDN 7/2002)
6214 * For: (per experimentaion with native control and CSpy ListView)
6215 * LVS_ICON dy=1 = 1 pixel (vertical only)
6216 * dx ignored
6217 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6218 * dx ignored
6219 * LVS_LIST dx=1 = 1 column (horizontal only)
6220 * but will only scroll 1 column per message
6221 * no matter what the value.
6222 * dy must be 0 or FALSE returned.
6223 * LVS_REPORT dx=1 = 1 pixel
6224 * dy= see above
6227 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6229 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6230 case LVS_REPORT:
6231 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6232 dy /= infoPtr->nItemHeight;
6233 break;
6234 case LVS_LIST:
6235 if (dy != 0) return FALSE;
6236 break;
6237 default: /* icon */
6238 dx = 0;
6239 break;
6242 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6243 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6245 return TRUE;
6248 /***
6249 * DESCRIPTION:
6250 * Sets the background color.
6252 * PARAMETER(S):
6253 * [I] infoPtr : valid pointer to the listview structure
6254 * [I] clrBk : background color
6256 * RETURN:
6257 * SUCCESS : TRUE
6258 * FAILURE : FALSE
6260 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6262 TRACE("(clrBk=%lx)\n", clrBk);
6264 if(infoPtr->clrBk != clrBk) {
6265 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6266 infoPtr->clrBk = clrBk;
6267 if (clrBk == CLR_NONE)
6268 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6269 else
6270 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6271 LISTVIEW_InvalidateList(infoPtr);
6274 return TRUE;
6277 /* LISTVIEW_SetBkImage */
6279 /*** Helper for {Insert,Set}ColumnT *only* */
6280 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6282 if (lpColumn->mask & LVCF_FMT)
6284 /* format member is valid */
6285 lphdi->mask |= HDI_FORMAT;
6287 /* set text alignment (leftmost column must be left-aligned) */
6288 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6289 lphdi->fmt |= HDF_LEFT;
6290 else if (lpColumn->fmt & LVCFMT_RIGHT)
6291 lphdi->fmt |= HDF_RIGHT;
6292 else if (lpColumn->fmt & LVCFMT_CENTER)
6293 lphdi->fmt |= HDF_CENTER;
6295 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6296 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6298 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6300 lphdi->fmt |= HDF_IMAGE;
6301 lphdi->iImage = I_IMAGECALLBACK;
6305 if (lpColumn->mask & LVCF_WIDTH)
6307 lphdi->mask |= HDI_WIDTH;
6308 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6310 /* make it fill the remainder of the controls width */
6311 RECT rcHeader;
6312 INT item_index;
6314 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6316 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6317 lphdi->cxy += rcHeader.right - rcHeader.left;
6320 /* retrieve the layout of the header */
6321 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6322 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6324 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6326 else
6327 lphdi->cxy = lpColumn->cx;
6330 if (lpColumn->mask & LVCF_TEXT)
6332 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6333 lphdi->fmt |= HDF_STRING;
6334 lphdi->pszText = lpColumn->pszText;
6335 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6338 if (lpColumn->mask & LVCF_IMAGE)
6340 lphdi->mask |= HDI_IMAGE;
6341 lphdi->iImage = lpColumn->iImage;
6344 if (lpColumn->mask & LVCF_ORDER)
6346 lphdi->mask |= HDI_ORDER;
6347 lphdi->iOrder = lpColumn->iOrder;
6352 /***
6353 * DESCRIPTION:
6354 * Inserts a new column.
6356 * PARAMETER(S):
6357 * [I] infoPtr : valid pointer to the listview structure
6358 * [I] nColumn : column index
6359 * [I] lpColumn : column information
6360 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6362 * RETURN:
6363 * SUCCESS : new column index
6364 * FAILURE : -1
6366 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6367 const LVCOLUMNW *lpColumn, BOOL isW)
6369 COLUMN_INFO *lpColumnInfo;
6370 INT nNewColumn;
6371 HDITEMW hdi;
6373 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6375 if (!lpColumn || nColumn < 0) return -1;
6376 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6378 ZeroMemory(&hdi, sizeof(HDITEMW));
6379 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6381 /* insert item in header control */
6382 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6383 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6384 (WPARAM)nColumn, (LPARAM)&hdi);
6385 if (nNewColumn == -1) return -1;
6386 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6388 /* create our own column info */
6389 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6390 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6392 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6393 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6395 /* now we have to actually adjust the data */
6396 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6398 SUBITEM_INFO *lpSubItem;
6399 HDPA hdpaSubItems;
6400 INT nItem, i;
6402 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6404 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6405 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6407 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6408 if (lpSubItem->iSubItem >= nNewColumn)
6409 lpSubItem->iSubItem++;
6414 /* make space for the new column */
6415 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6417 return nNewColumn;
6419 fail:
6420 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6421 if (lpColumnInfo)
6423 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6424 Free(lpColumnInfo);
6426 return -1;
6429 /***
6430 * DESCRIPTION:
6431 * Sets the attributes of a header item.
6433 * PARAMETER(S):
6434 * [I] infoPtr : valid pointer to the listview structure
6435 * [I] nColumn : column index
6436 * [I] lpColumn : column attributes
6437 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6439 * RETURN:
6440 * SUCCESS : TRUE
6441 * FAILURE : FALSE
6443 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6444 const LVCOLUMNW *lpColumn, BOOL isW)
6446 HDITEMW hdi, hdiget;
6447 BOOL bResult;
6449 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6451 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6453 ZeroMemory(&hdi, sizeof(HDITEMW));
6454 if (lpColumn->mask & LVCF_FMT)
6456 hdi.mask |= HDI_FORMAT;
6457 hdiget.mask = HDI_FORMAT;
6458 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6459 hdi.fmt = hdiget.fmt & HDF_STRING;
6461 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6463 /* set header item attributes */
6464 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6465 if (!bResult) return FALSE;
6467 if (lpColumn->mask & LVCF_FMT)
6469 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6470 int oldFmt = lpColumnInfo->fmt;
6472 lpColumnInfo->fmt = lpColumn->fmt;
6473 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6475 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6476 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6480 return TRUE;
6483 /***
6484 * DESCRIPTION:
6485 * Sets the column order array
6487 * PARAMETERS:
6488 * [I] infoPtr : valid pointer to the listview structure
6489 * [I] iCount : number of elements in column order array
6490 * [I] lpiArray : pointer to column order array
6492 * RETURN:
6493 * SUCCESS : TRUE
6494 * FAILURE : FALSE
6496 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6498 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6500 if (!lpiArray)
6501 return FALSE;
6503 return TRUE;
6507 /***
6508 * DESCRIPTION:
6509 * Sets the width of a column
6511 * PARAMETERS:
6512 * [I] infoPtr : valid pointer to the listview structure
6513 * [I] nColumn : column index
6514 * [I] cx : column width
6516 * RETURN:
6517 * SUCCESS : TRUE
6518 * FAILURE : FALSE
6520 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6522 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6523 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6524 INT max_cx = 0;
6525 HDITEMW hdi;
6527 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6529 /* set column width only if in report or list mode */
6530 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6532 /* take care of invalid cx values */
6533 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6534 else if (uView == LVS_LIST && cx < 1) return FALSE;
6536 /* resize all columns if in LVS_LIST mode */
6537 if(uView == LVS_LIST)
6539 infoPtr->nItemWidth = cx;
6540 LISTVIEW_InvalidateList(infoPtr);
6541 return TRUE;
6544 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6546 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6548 INT nLabelWidth;
6549 LVITEMW lvItem;
6551 lvItem.mask = LVIF_TEXT;
6552 lvItem.iItem = 0;
6553 lvItem.iSubItem = nColumn;
6554 lvItem.pszText = szDispText;
6555 lvItem.cchTextMax = DISP_TEXT_SIZE;
6556 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6558 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6559 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6560 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6562 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6563 max_cx += infoPtr->iconSize.cx;
6564 max_cx += TRAILING_LABEL_PADDING;
6567 /* autosize based on listview items width */
6568 if(cx == LVSCW_AUTOSIZE)
6569 cx = max_cx;
6570 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6572 /* if iCol is the last column make it fill the remainder of the controls width */
6573 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6575 RECT rcHeader;
6576 POINT Origin;
6578 LISTVIEW_GetOrigin(infoPtr, &Origin);
6579 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6581 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6583 else
6585 /* Despite what the MS docs say, if this is not the last
6586 column, then MS resizes the column to the width of the
6587 largest text string in the column, including headers
6588 and items. This is different from LVSCW_AUTOSIZE in that
6589 LVSCW_AUTOSIZE ignores the header string length. */
6590 cx = 0;
6592 /* retrieve header text */
6593 hdi.mask = HDI_TEXT;
6594 hdi.cchTextMax = DISP_TEXT_SIZE;
6595 hdi.pszText = szDispText;
6596 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6598 HDC hdc = GetDC(infoPtr->hwndSelf);
6599 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6600 SIZE size;
6602 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6603 cx = size.cx + TRAILING_HEADER_PADDING;
6604 /* FIXME: Take into account the header image, if one is present */
6605 SelectObject(hdc, old_font);
6606 ReleaseDC(infoPtr->hwndSelf, hdc);
6608 cx = max (cx, max_cx);
6612 if (cx < 0) return FALSE;
6614 /* call header to update the column change */
6615 hdi.mask = HDI_WIDTH;
6616 hdi.cxy = cx;
6617 TRACE("hdi.cxy=%d\n", hdi.cxy);
6618 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6621 /***
6622 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6625 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6627 HDC hdc_wnd, hdc;
6628 HBITMAP hbm_im, hbm_mask, hbm_orig;
6629 RECT rc;
6630 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6631 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6632 HIMAGELIST himl;
6634 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6635 ILC_COLOR | ILC_MASK, 2, 2);
6636 hdc_wnd = GetDC(infoPtr->hwndSelf);
6637 hdc = CreateCompatibleDC(hdc_wnd);
6638 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6639 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6640 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6642 rc.left = rc.top = 0;
6643 rc.right = GetSystemMetrics(SM_CXSMICON);
6644 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6646 hbm_orig = SelectObject(hdc, hbm_mask);
6647 FillRect(hdc, &rc, hbr_white);
6648 InflateRect(&rc, -3, -3);
6649 FillRect(hdc, &rc, hbr_black);
6651 SelectObject(hdc, hbm_im);
6652 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6653 SelectObject(hdc, hbm_orig);
6654 ImageList_Add(himl, hbm_im, hbm_mask);
6656 SelectObject(hdc, hbm_im);
6657 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6658 SelectObject(hdc, hbm_orig);
6659 ImageList_Add(himl, hbm_im, hbm_mask);
6661 DeleteObject(hbm_mask);
6662 DeleteObject(hbm_im);
6663 DeleteDC(hdc);
6665 return himl;
6668 /***
6669 * DESCRIPTION:
6670 * Sets the extended listview style.
6672 * PARAMETERS:
6673 * [I] infoPtr : valid pointer to the listview structure
6674 * [I] dwMask : mask
6675 * [I] dwStyle : style
6677 * RETURN:
6678 * SUCCESS : previous style
6679 * FAILURE : 0
6681 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6683 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6685 /* set new style */
6686 if (dwMask)
6687 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6688 else
6689 infoPtr->dwLvExStyle = dwStyle;
6691 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6693 HIMAGELIST himl = 0;
6694 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6695 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6696 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6699 return dwOldStyle;
6702 /***
6703 * DESCRIPTION:
6704 * Sets the new hot cursor used during hot tracking and hover selection.
6706 * PARAMETER(S):
6707 * [I] infoPtr : valid pointer to the listview structure
6708 * [I} hCurosr : the new hot cursor handle
6710 * RETURN:
6711 * Returns the previous hot cursor
6713 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6715 HCURSOR oldCursor = infoPtr->hHotCursor;
6717 infoPtr->hHotCursor = hCursor;
6719 return oldCursor;
6723 /***
6724 * DESCRIPTION:
6725 * Sets the hot item index.
6727 * PARAMETERS:
6728 * [I] infoPtr : valid pointer to the listview structure
6729 * [I] iIndex : index
6731 * RETURN:
6732 * SUCCESS : previous hot item index
6733 * FAILURE : -1 (no hot item)
6735 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6737 INT iOldIndex = infoPtr->nHotItem;
6739 infoPtr->nHotItem = iIndex;
6741 return iOldIndex;
6745 /***
6746 * DESCRIPTION:
6747 * Sets the amount of time the cursor must hover over an item before it is selected.
6749 * PARAMETER(S):
6750 * [I] infoPtr : valid pointer to the listview structure
6751 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6753 * RETURN:
6754 * Returns the previous hover time
6756 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6758 DWORD oldHoverTime = infoPtr->dwHoverTime;
6760 infoPtr->dwHoverTime = dwHoverTime;
6762 return oldHoverTime;
6765 /***
6766 * DESCRIPTION:
6767 * Sets spacing for icons of LVS_ICON style.
6769 * PARAMETER(S):
6770 * [I] infoPtr : valid pointer to the listview structure
6771 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6772 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6774 * RETURN:
6775 * MAKELONG(oldcx, oldcy)
6777 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6779 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6780 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6782 TRACE("requested=(%d,%d)\n", cx, cy);
6784 /* this is supported only for LVS_ICON style */
6785 if (uView != LVS_ICON) return oldspacing;
6787 /* set to defaults, if instructed to */
6788 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6789 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6791 /* if 0 then compute width
6792 * FIXME: Should scan each item and determine max width of
6793 * icon or label, then make that the width */
6794 if (cx == 0)
6795 cx = infoPtr->iconSpacing.cx;
6797 /* if 0 then compute height */
6798 if (cy == 0)
6799 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6800 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6803 infoPtr->iconSpacing.cx = cx;
6804 infoPtr->iconSpacing.cy = cy;
6806 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6807 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6808 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6809 infoPtr->ntmHeight);
6811 /* these depend on the iconSpacing */
6812 LISTVIEW_UpdateItemSize(infoPtr);
6814 return oldspacing;
6817 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6819 INT cx, cy;
6821 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6823 size->cx = cx;
6824 size->cy = cy;
6826 else
6828 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6829 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6833 /***
6834 * DESCRIPTION:
6835 * Sets image lists.
6837 * PARAMETER(S):
6838 * [I] infoPtr : valid pointer to the listview structure
6839 * [I] nType : image list type
6840 * [I] himl : image list handle
6842 * RETURN:
6843 * SUCCESS : old image list
6844 * FAILURE : NULL
6846 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6848 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6849 INT oldHeight = infoPtr->nItemHeight;
6850 HIMAGELIST himlOld = 0;
6852 TRACE("(nType=%d, himl=%p\n", nType, himl);
6854 switch (nType)
6856 case LVSIL_NORMAL:
6857 himlOld = infoPtr->himlNormal;
6858 infoPtr->himlNormal = himl;
6859 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6860 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6861 break;
6863 case LVSIL_SMALL:
6864 himlOld = infoPtr->himlSmall;
6865 infoPtr->himlSmall = himl;
6866 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6867 break;
6869 case LVSIL_STATE:
6870 himlOld = infoPtr->himlState;
6871 infoPtr->himlState = himl;
6872 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6873 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6874 break;
6876 default:
6877 ERR("Unknown icon type=%d\n", nType);
6878 return NULL;
6881 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6882 if (infoPtr->nItemHeight != oldHeight)
6883 LISTVIEW_UpdateScroll(infoPtr);
6885 return himlOld;
6888 /***
6889 * DESCRIPTION:
6890 * Preallocates memory (does *not* set the actual count of items !)
6892 * PARAMETER(S):
6893 * [I] infoPtr : valid pointer to the listview structure
6894 * [I] nItems : item count (projected number of items to allocate)
6895 * [I] dwFlags : update flags
6897 * RETURN:
6898 * SUCCESS : TRUE
6899 * FAILURE : FALSE
6901 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6903 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6905 if (infoPtr->dwStyle & LVS_OWNERDATA)
6907 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6908 INT nOldCount = infoPtr->nItemCount;
6910 if (nItems < nOldCount)
6912 RANGE range = { nItems, nOldCount };
6913 ranges_del(infoPtr->selectionRanges, range);
6914 if (infoPtr->nFocusedItem >= nItems)
6916 infoPtr->nFocusedItem = -1;
6917 SetRectEmpty(&infoPtr->rcFocus);
6921 infoPtr->nItemCount = nItems;
6922 LISTVIEW_UpdateScroll(infoPtr);
6924 /* the flags are valid only in ownerdata report and list modes */
6925 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6927 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6928 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6930 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6931 LISTVIEW_InvalidateList(infoPtr);
6932 else
6934 INT nFrom, nTo;
6935 POINT Origin;
6936 RECT rcErase;
6938 LISTVIEW_GetOrigin(infoPtr, &Origin);
6939 nFrom = min(nOldCount, nItems);
6940 nTo = max(nOldCount, nItems);
6942 if (uView == LVS_REPORT)
6944 rcErase.left = 0;
6945 rcErase.top = nFrom * infoPtr->nItemHeight;
6946 rcErase.right = infoPtr->nItemWidth;
6947 rcErase.bottom = nTo * infoPtr->nItemHeight;
6948 OffsetRect(&rcErase, Origin.x, Origin.y);
6949 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6950 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6952 else /* LVS_LIST */
6954 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6956 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6957 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6958 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6959 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6960 OffsetRect(&rcErase, Origin.x, Origin.y);
6961 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6962 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6964 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6965 rcErase.top = 0;
6966 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6967 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6968 OffsetRect(&rcErase, Origin.x, Origin.y);
6969 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6970 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6974 else
6976 /* According to MSDN for non-LVS_OWNERDATA this is just
6977 * a performance issue. The control allocates its internal
6978 * data structures for the number of items specified. It
6979 * cuts down on the number of memory allocations. Therefore
6980 * we will just issue a WARN here
6982 WARN("for non-ownerdata performance option not implemented.\n");
6985 return TRUE;
6988 /***
6989 * DESCRIPTION:
6990 * Sets the position of an item.
6992 * PARAMETER(S):
6993 * [I] infoPtr : valid pointer to the listview structure
6994 * [I] nItem : item index
6995 * [I] pt : coordinate
6997 * RETURN:
6998 * SUCCESS : TRUE
6999 * FAILURE : FALSE
7001 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7003 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7004 POINT Origin;
7006 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7008 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7009 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7011 LISTVIEW_GetOrigin(infoPtr, &Origin);
7013 /* This point value seems to be an undocumented feature.
7014 * The best guess is that it means either at the origin,
7015 * or at true beginning of the list. I will assume the origin. */
7016 if ((pt.x == -1) && (pt.y == -1))
7017 pt = Origin;
7019 if (uView == LVS_ICON)
7021 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7022 pt.y -= ICON_TOP_PADDING;
7024 pt.x -= Origin.x;
7025 pt.y -= Origin.y;
7027 infoPtr->bAutoarrange = FALSE;
7029 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7032 /***
7033 * DESCRIPTION:
7034 * Sets the state of one or many items.
7036 * PARAMETER(S):
7037 * [I] infoPtr : valid pointer to the listview structure
7038 * [I] nItem : item index
7039 * [I] lpLVItem : item or subitem info
7041 * RETURN:
7042 * SUCCESS : TRUE
7043 * FAILURE : FALSE
7045 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7047 BOOL bResult = TRUE;
7048 LVITEMW lvItem;
7050 lvItem.iItem = nItem;
7051 lvItem.iSubItem = 0;
7052 lvItem.mask = LVIF_STATE;
7053 lvItem.state = lpLVItem->state;
7054 lvItem.stateMask = lpLVItem->stateMask;
7055 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7057 if (nItem == -1)
7059 /* apply to all items */
7060 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7061 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7063 else
7064 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7067 *update selection mark
7069 * Investigation on windows 2k showed that selection mark was updated
7070 * whenever a new selection was made, but if the selected item was
7071 * unselected it was not updated.
7073 * we are probably still not 100% accurate, but this at least sets the
7074 * proper selection mark when it is needed
7077 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7078 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7080 int i;
7081 infoPtr->nSelectionMark = -1;
7082 for (i = 0; i < infoPtr->nItemCount; i++)
7084 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7086 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7088 infoPtr->nSelectionMark = i;
7089 break;
7092 else if (ranges_contain(infoPtr->selectionRanges, i))
7094 infoPtr->nSelectionMark = i;
7095 break;
7100 return bResult;
7103 /***
7104 * DESCRIPTION:
7105 * Sets the text of an item or subitem.
7107 * PARAMETER(S):
7108 * [I] hwnd : window handle
7109 * [I] nItem : item index
7110 * [I] lpLVItem : item or subitem info
7111 * [I] isW : TRUE if input is Unicode
7113 * RETURN:
7114 * SUCCESS : TRUE
7115 * FAILURE : FALSE
7117 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7119 LVITEMW lvItem;
7121 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7123 lvItem.iItem = nItem;
7124 lvItem.iSubItem = lpLVItem->iSubItem;
7125 lvItem.mask = LVIF_TEXT;
7126 lvItem.pszText = lpLVItem->pszText;
7127 lvItem.cchTextMax = lpLVItem->cchTextMax;
7129 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7131 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7134 /***
7135 * DESCRIPTION:
7136 * Set item index that marks the start of a multiple selection.
7138 * PARAMETER(S):
7139 * [I] infoPtr : valid pointer to the listview structure
7140 * [I] nIndex : index
7142 * RETURN:
7143 * Index number or -1 if there is no selection mark.
7145 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7147 INT nOldIndex = infoPtr->nSelectionMark;
7149 TRACE("(nIndex=%d)\n", nIndex);
7151 infoPtr->nSelectionMark = nIndex;
7153 return nOldIndex;
7156 /***
7157 * DESCRIPTION:
7158 * Sets the text background color.
7160 * PARAMETER(S):
7161 * [I] infoPtr : valid pointer to the listview structure
7162 * [I] clrTextBk : text background color
7164 * RETURN:
7165 * SUCCESS : TRUE
7166 * FAILURE : FALSE
7168 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7170 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7172 if (infoPtr->clrTextBk != clrTextBk)
7174 infoPtr->clrTextBk = clrTextBk;
7175 LISTVIEW_InvalidateList(infoPtr);
7178 return TRUE;
7181 /***
7182 * DESCRIPTION:
7183 * Sets the text foreground color.
7185 * PARAMETER(S):
7186 * [I] infoPtr : valid pointer to the listview structure
7187 * [I] clrText : text color
7189 * RETURN:
7190 * SUCCESS : TRUE
7191 * FAILURE : FALSE
7193 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7195 TRACE("(clrText=%lx)\n", clrText);
7197 if (infoPtr->clrText != clrText)
7199 infoPtr->clrText = clrText;
7200 LISTVIEW_InvalidateList(infoPtr);
7203 return TRUE;
7206 /***
7207 * DESCRIPTION:
7208 * Determines which listview item is located at the specified position.
7210 * PARAMETER(S):
7211 * [I] infoPtr : valid pointer to the listview structure
7212 * [I] hwndNewToolTip : handle to new ToolTip
7214 * RETURN:
7215 * old tool tip
7217 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7219 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7220 infoPtr->hwndToolTip = hwndNewToolTip;
7221 return hwndOldToolTip;
7224 /* LISTVIEW_SetUnicodeFormat */
7225 /* LISTVIEW_SetWorkAreas */
7227 /***
7228 * DESCRIPTION:
7229 * Callback internally used by LISTVIEW_SortItems()
7231 * PARAMETER(S):
7232 * [I] first : pointer to first ITEM_INFO to compare
7233 * [I] second : pointer to second ITEM_INFO to compare
7234 * [I] lParam : HWND of control
7236 * RETURN:
7237 * if first comes before second : negative
7238 * if first comes after second : positive
7239 * if first and second are equivalent : zero
7241 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7243 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7244 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7245 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7247 /* Forward the call to the client defined callback */
7248 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7251 /***
7252 * DESCRIPTION:
7253 * Sorts the listview items.
7255 * PARAMETER(S):
7256 * [I] infoPtr : valid pointer to the listview structure
7257 * [I] pfnCompare : application-defined value
7258 * [I] lParamSort : pointer to comparision callback
7260 * RETURN:
7261 * SUCCESS : TRUE
7262 * FAILURE : FALSE
7264 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7266 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7267 HDPA hdpaSubItems;
7268 ITEM_INFO *lpItem;
7269 LPVOID selectionMarkItem;
7270 LVITEMW item;
7271 int i;
7273 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7275 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7277 if (!infoPtr->hdpaItems) return FALSE;
7279 /* if there are 0 or 1 items, there is no need to sort */
7280 if (infoPtr->nItemCount < 2) return TRUE;
7282 if (infoPtr->nFocusedItem >= 0)
7284 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7285 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7286 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7288 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7289 /* clear the lpItem->state for non-selected ones */
7290 /* remove the selection ranges */
7292 infoPtr->pfnCompare = pfnCompare;
7293 infoPtr->lParamSort = lParamSort;
7294 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7296 /* Adjust selections and indices so that they are the way they should
7297 * be after the sort (otherwise, the list items move around, but
7298 * whatever is at the item's previous original position will be
7299 * selected instead)
7301 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7302 for (i=0; i < infoPtr->nItemCount; i++)
7304 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7305 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7307 if (lpItem->state & LVIS_SELECTED)
7309 item.state = LVIS_SELECTED;
7310 item.stateMask = LVIS_SELECTED;
7311 LISTVIEW_SetItemState(infoPtr, i, &item);
7313 if (lpItem->state & LVIS_FOCUSED)
7315 infoPtr->nFocusedItem = i;
7316 lpItem->state &= ~LVIS_FOCUSED;
7319 if (selectionMarkItem != NULL)
7320 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7321 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7323 /* refresh the display */
7324 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7325 LISTVIEW_InvalidateList(infoPtr);
7327 return TRUE;
7330 /***
7331 * DESCRIPTION:
7332 * Updates an items or rearranges the listview control.
7334 * PARAMETER(S):
7335 * [I] infoPtr : valid pointer to the listview structure
7336 * [I] nItem : item index
7338 * RETURN:
7339 * SUCCESS : TRUE
7340 * FAILURE : FALSE
7342 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7344 TRACE("(nItem=%d)\n", nItem);
7346 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7348 /* rearrange with default alignment style */
7349 if (is_autoarrange(infoPtr))
7350 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7351 else
7352 LISTVIEW_InvalidateItem(infoPtr, nItem);
7354 return TRUE;
7358 /***
7359 * DESCRIPTION:
7360 * Creates the listview control.
7362 * PARAMETER(S):
7363 * [I] hwnd : window handle
7364 * [I] lpcs : the create parameters
7366 * RETURN:
7367 * Success: 0
7368 * Failure: -1
7370 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7372 LISTVIEW_INFO *infoPtr;
7373 UINT uView = lpcs->style & LVS_TYPEMASK;
7374 LOGFONTW logFont;
7376 TRACE("(lpcs=%p)\n", lpcs);
7378 /* initialize info pointer */
7379 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7380 if (!infoPtr) return -1;
7382 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7384 infoPtr->hwndSelf = hwnd;
7385 infoPtr->dwStyle = lpcs->style;
7386 /* determine the type of structures to use */
7387 infoPtr->hwndNotify = lpcs->hwndParent;
7388 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7389 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7391 /* initialize color information */
7392 infoPtr->clrBk = CLR_NONE;
7393 infoPtr->clrText = comctl32_color.clrWindowText;
7394 infoPtr->clrTextBk = CLR_DEFAULT;
7395 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7397 /* set default values */
7398 infoPtr->nFocusedItem = -1;
7399 infoPtr->nSelectionMark = -1;
7400 infoPtr->nHotItem = -1;
7401 infoPtr->bRedraw = TRUE;
7402 infoPtr->bNoItemMetrics = TRUE;
7403 infoPtr->bDoChangeNotify = TRUE;
7404 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7405 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7406 infoPtr->nEditLabelItem = -1;
7407 infoPtr->dwHoverTime = -1; /* default system hover time */
7409 /* get default font (icon title) */
7410 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7411 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7412 infoPtr->hFont = infoPtr->hDefaultFont;
7413 LISTVIEW_SaveTextMetrics(infoPtr);
7415 /* create header */
7416 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7417 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7418 0, 0, 0, 0, hwnd, NULL,
7419 lpcs->hInstance, NULL);
7420 if (!infoPtr->hwndHeader) goto fail;
7422 /* set header unicode format */
7423 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7425 /* set header font */
7426 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7428 /* allocate memory for the data structure */
7429 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7430 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7431 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7432 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7433 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7435 /* initialize the icon sizes */
7436 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7437 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7439 /* init item size to avoid division by 0 */
7440 LISTVIEW_UpdateItemSize (infoPtr);
7442 if (uView == LVS_REPORT)
7444 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7446 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7448 else
7450 /* set HDS_HIDDEN flag to hide the header bar */
7451 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7452 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7456 return 0;
7458 fail:
7459 DestroyWindow(infoPtr->hwndHeader);
7460 ranges_destroy(infoPtr->selectionRanges);
7461 DPA_Destroy(infoPtr->hdpaItems);
7462 DPA_Destroy(infoPtr->hdpaPosX);
7463 DPA_Destroy(infoPtr->hdpaPosY);
7464 DPA_Destroy(infoPtr->hdpaColumns);
7465 Free(infoPtr);
7466 return -1;
7469 /***
7470 * DESCRIPTION:
7471 * Erases the background of the listview control.
7473 * PARAMETER(S):
7474 * [I] infoPtr : valid pointer to the listview structure
7475 * [I] hdc : device context handle
7477 * RETURN:
7478 * SUCCESS : TRUE
7479 * FAILURE : FALSE
7481 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7483 RECT rc;
7485 TRACE("(hdc=%p)\n", hdc);
7487 if (!GetClipBox(hdc, &rc)) return FALSE;
7489 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7493 /***
7494 * DESCRIPTION:
7495 * Helper function for LISTVIEW_[HV]Scroll *only*.
7496 * Performs vertical/horizontal scrolling by a give amount.
7498 * PARAMETER(S):
7499 * [I] infoPtr : valid pointer to the listview structure
7500 * [I] dx : amount of horizontal scroll
7501 * [I] dy : amount of vertical scroll
7503 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7505 /* now we can scroll the list */
7506 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7507 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7508 /* if we have focus, adjust rect */
7509 OffsetRect(&infoPtr->rcFocus, dx, dy);
7510 UpdateWindow(infoPtr->hwndSelf);
7513 /***
7514 * DESCRIPTION:
7515 * Performs vertical scrolling.
7517 * PARAMETER(S):
7518 * [I] infoPtr : valid pointer to the listview structure
7519 * [I] nScrollCode : scroll code
7520 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7521 * [I] hScrollWnd : scrollbar control window handle
7523 * RETURN:
7524 * Zero
7526 * NOTES:
7527 * SB_LINEUP/SB_LINEDOWN:
7528 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7529 * for LVS_REPORT is 1 line
7530 * for LVS_LIST cannot occur
7533 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7534 INT nScrollDiff, HWND hScrollWnd)
7536 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7537 INT nOldScrollPos, nNewScrollPos;
7538 SCROLLINFO scrollInfo;
7539 BOOL is_an_icon;
7541 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7542 debugscrollcode(nScrollCode), nScrollDiff);
7544 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7546 scrollInfo.cbSize = sizeof(SCROLLINFO);
7547 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7549 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7551 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7553 nOldScrollPos = scrollInfo.nPos;
7554 switch (nScrollCode)
7556 case SB_INTERNAL:
7557 break;
7559 case SB_LINEUP:
7560 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7561 break;
7563 case SB_LINEDOWN:
7564 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7565 break;
7567 case SB_PAGEUP:
7568 nScrollDiff = -scrollInfo.nPage;
7569 break;
7571 case SB_PAGEDOWN:
7572 nScrollDiff = scrollInfo.nPage;
7573 break;
7575 case SB_THUMBPOSITION:
7576 case SB_THUMBTRACK:
7577 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7578 break;
7580 default:
7581 nScrollDiff = 0;
7584 /* quit right away if pos isn't changing */
7585 if (nScrollDiff == 0) return 0;
7587 /* calculate new position, and handle overflows */
7588 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7589 if (nScrollDiff > 0) {
7590 if (nNewScrollPos < nOldScrollPos ||
7591 nNewScrollPos > scrollInfo.nMax)
7592 nNewScrollPos = scrollInfo.nMax;
7593 } else {
7594 if (nNewScrollPos > nOldScrollPos ||
7595 nNewScrollPos < scrollInfo.nMin)
7596 nNewScrollPos = scrollInfo.nMin;
7599 /* set the new position, and reread in case it changed */
7600 scrollInfo.fMask = SIF_POS;
7601 scrollInfo.nPos = nNewScrollPos;
7602 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7604 /* carry on only if it really changed */
7605 if (nNewScrollPos == nOldScrollPos) return 0;
7607 /* now adjust to client coordinates */
7608 nScrollDiff = nOldScrollPos - nNewScrollPos;
7609 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7611 /* and scroll the window */
7612 scroll_list(infoPtr, 0, nScrollDiff);
7614 return 0;
7617 /***
7618 * DESCRIPTION:
7619 * Performs horizontal scrolling.
7621 * PARAMETER(S):
7622 * [I] infoPtr : valid pointer to the listview structure
7623 * [I] nScrollCode : scroll code
7624 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7625 * [I] hScrollWnd : scrollbar control window handle
7627 * RETURN:
7628 * Zero
7630 * NOTES:
7631 * SB_LINELEFT/SB_LINERIGHT:
7632 * for LVS_ICON, LVS_SMALLICON 1 pixel
7633 * for LVS_REPORT is 1 pixel
7634 * for LVS_LIST is 1 column --> which is a 1 because the
7635 * scroll is based on columns not pixels
7638 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7639 INT nScrollDiff, HWND hScrollWnd)
7641 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7642 INT nOldScrollPos, nNewScrollPos;
7643 SCROLLINFO scrollInfo;
7645 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7646 debugscrollcode(nScrollCode), nScrollDiff);
7648 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7650 scrollInfo.cbSize = sizeof(SCROLLINFO);
7651 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7653 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7655 nOldScrollPos = scrollInfo.nPos;
7657 switch (nScrollCode)
7659 case SB_INTERNAL:
7660 break;
7662 case SB_LINELEFT:
7663 nScrollDiff = -1;
7664 break;
7666 case SB_LINERIGHT:
7667 nScrollDiff = 1;
7668 break;
7670 case SB_PAGELEFT:
7671 nScrollDiff = -scrollInfo.nPage;
7672 break;
7674 case SB_PAGERIGHT:
7675 nScrollDiff = scrollInfo.nPage;
7676 break;
7678 case SB_THUMBPOSITION:
7679 case SB_THUMBTRACK:
7680 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7681 break;
7683 default:
7684 nScrollDiff = 0;
7687 /* quit right away if pos isn't changing */
7688 if (nScrollDiff == 0) return 0;
7690 /* calculate new position, and handle overflows */
7691 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7692 if (nScrollDiff > 0) {
7693 if (nNewScrollPos < nOldScrollPos ||
7694 nNewScrollPos > scrollInfo.nMax)
7695 nNewScrollPos = scrollInfo.nMax;
7696 } else {
7697 if (nNewScrollPos > nOldScrollPos ||
7698 nNewScrollPos < scrollInfo.nMin)
7699 nNewScrollPos = scrollInfo.nMin;
7702 /* set the new position, and reread in case it changed */
7703 scrollInfo.fMask = SIF_POS;
7704 scrollInfo.nPos = nNewScrollPos;
7705 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7707 /* carry on only if it really changed */
7708 if (nNewScrollPos == nOldScrollPos) return 0;
7710 if(uView == LVS_REPORT)
7711 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7713 /* now adjust to client coordinates */
7714 nScrollDiff = nOldScrollPos - nNewScrollPos;
7715 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7717 /* and scroll the window */
7718 scroll_list(infoPtr, nScrollDiff, 0);
7720 return 0;
7723 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7725 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7726 INT gcWheelDelta = 0;
7727 UINT pulScrollLines = 3;
7728 SCROLLINFO scrollInfo;
7730 TRACE("(wheelDelta=%d)\n", wheelDelta);
7732 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7733 gcWheelDelta -= wheelDelta;
7735 scrollInfo.cbSize = sizeof(SCROLLINFO);
7736 scrollInfo.fMask = SIF_POS;
7738 switch(uView)
7740 case LVS_ICON:
7741 case LVS_SMALLICON:
7743 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7744 * should be fixed in the future.
7746 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7747 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7748 break;
7750 case LVS_REPORT:
7751 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7753 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7754 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7755 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7757 break;
7759 case LVS_LIST:
7760 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7761 break;
7763 return 0;
7766 /***
7767 * DESCRIPTION:
7768 * ???
7770 * PARAMETER(S):
7771 * [I] infoPtr : valid pointer to the listview structure
7772 * [I] nVirtualKey : virtual key
7773 * [I] lKeyData : key data
7775 * RETURN:
7776 * Zero
7778 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7780 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7781 INT nItem = -1;
7782 NMLVKEYDOWN nmKeyDown;
7784 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7786 /* send LVN_KEYDOWN notification */
7787 nmKeyDown.wVKey = nVirtualKey;
7788 nmKeyDown.flags = 0;
7789 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7791 switch (nVirtualKey)
7793 case VK_RETURN:
7794 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7796 notify(infoPtr, NM_RETURN);
7797 notify(infoPtr, LVN_ITEMACTIVATE);
7799 break;
7801 case VK_HOME:
7802 if (infoPtr->nItemCount > 0)
7803 nItem = 0;
7804 break;
7806 case VK_END:
7807 if (infoPtr->nItemCount > 0)
7808 nItem = infoPtr->nItemCount - 1;
7809 break;
7811 case VK_LEFT:
7812 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7813 break;
7815 case VK_UP:
7816 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7817 break;
7819 case VK_RIGHT:
7820 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7821 break;
7823 case VK_DOWN:
7824 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7825 break;
7827 case VK_PRIOR:
7828 if (uView == LVS_REPORT)
7829 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7830 else
7831 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7832 * LISTVIEW_GetCountPerRow(infoPtr);
7833 if(nItem < 0) nItem = 0;
7834 break;
7836 case VK_NEXT:
7837 if (uView == LVS_REPORT)
7838 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7839 else
7840 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7841 * LISTVIEW_GetCountPerRow(infoPtr);
7842 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7843 break;
7846 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7847 LISTVIEW_KeySelection(infoPtr, nItem);
7849 return 0;
7852 /***
7853 * DESCRIPTION:
7854 * Kills the focus.
7856 * PARAMETER(S):
7857 * [I] infoPtr : valid pointer to the listview structure
7859 * RETURN:
7860 * Zero
7862 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7864 TRACE("()\n");
7866 /* if we did not have the focus, there's nothing to do */
7867 if (!infoPtr->bFocus) return 0;
7869 /* send NM_KILLFOCUS notification */
7870 notify(infoPtr, NM_KILLFOCUS);
7872 /* if we have a focus rectagle, get rid of it */
7873 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7875 /* set window focus flag */
7876 infoPtr->bFocus = FALSE;
7878 /* invalidate the selected items before reseting focus flag */
7879 LISTVIEW_InvalidateSelectedItems(infoPtr);
7881 return 0;
7885 /***
7886 * DESCRIPTION:
7887 * Track mouse/dragging
7889 * PARAMETER(S):
7890 * [I] infoPtr : valid pointer to the listview structure
7891 * [I] pt : mouse coordinate
7893 * RETURN:
7894 * Zero
7896 static LRESULT LISTVIEW_TrackMouse(LISTVIEW_INFO *infoPtr, POINT pt)
7898 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
7899 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
7900 RECT r;
7901 MSG msg;
7903 TRACE("\n");
7905 r.top = pt.y - cyDrag;
7906 r.left = pt.x - cxDrag;
7907 r.bottom = pt.y + cyDrag;
7908 r.right = pt.x + cxDrag;
7910 SetCapture(infoPtr->hwndSelf);
7912 while (1)
7914 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
7916 if (msg.message == WM_MOUSEMOVE)
7918 pt.x = (short)LOWORD(msg.lParam);
7919 pt.y = (short)HIWORD(msg.lParam);
7920 if (PtInRect(&r, pt))
7921 continue;
7922 else
7924 ReleaseCapture();
7925 return 1;
7928 else if (msg.message >= WM_LBUTTONDOWN &&
7929 msg.message <= WM_RBUTTONDBLCLK)
7931 break;
7934 DispatchMessageW(&msg);
7937 if (GetCapture() != infoPtr->hwndSelf)
7938 return 0;
7941 ReleaseCapture();
7942 return 0;
7946 /***
7947 * DESCRIPTION:
7948 * Processes double click messages (left mouse button).
7950 * PARAMETER(S):
7951 * [I] infoPtr : valid pointer to the listview structure
7952 * [I] wKey : key flag
7953 * [I] pts : mouse coordinate
7955 * RETURN:
7956 * Zero
7958 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7960 LVHITTESTINFO htInfo;
7962 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7964 /* send NM_RELEASEDCAPTURE notification */
7965 notify(infoPtr, NM_RELEASEDCAPTURE);
7967 htInfo.pt.x = pts.x;
7968 htInfo.pt.y = pts.y;
7970 /* send NM_DBLCLK notification */
7971 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7972 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7974 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7975 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
7977 return 0;
7980 /***
7981 * DESCRIPTION:
7982 * Processes mouse down messages (left mouse button).
7984 * PARAMETER(S):
7985 * [I] infoPtr : valid pointer to the listview structure
7986 * [I] wKey : key flag
7987 * [I] pts : mouse coordinate
7989 * RETURN:
7990 * Zero
7992 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7994 LVHITTESTINFO lvHitTestInfo;
7995 static BOOL bGroupSelect = TRUE;
7996 POINT pt = { pts.x, pts.y };
7997 INT nItem;
7999 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8001 /* send NM_RELEASEDCAPTURE notification */
8002 notify(infoPtr, NM_RELEASEDCAPTURE);
8004 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8006 /* set left button down flag */
8007 infoPtr->bLButtonDown = TRUE;
8009 lvHitTestInfo.pt.x = pts.x;
8010 lvHitTestInfo.pt.y = pts.y;
8012 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8013 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8014 infoPtr->nEditLabelItem = -1;
8015 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8017 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8019 DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
8020 if(state == 1 || state == 2)
8022 LVITEMW lvitem;
8023 state ^= 3;
8024 lvitem.state = state << 12;
8025 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8026 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8028 return 0;
8030 if (LISTVIEW_TrackMouse(infoPtr, lvHitTestInfo.pt))
8032 NMLISTVIEW nmlv;
8034 ZeroMemory(&nmlv, sizeof(nmlv));
8035 nmlv.iItem = nItem;
8036 nmlv.ptAction.x = lvHitTestInfo.pt.x;
8037 nmlv.ptAction.y = lvHitTestInfo.pt.y;
8039 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
8041 return 0;
8044 if (infoPtr->dwStyle & LVS_SINGLESEL)
8046 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8047 infoPtr->nEditLabelItem = nItem;
8048 else
8049 LISTVIEW_SetSelection(infoPtr, nItem);
8051 else
8053 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8055 if (bGroupSelect)
8057 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8058 LISTVIEW_SetItemFocus(infoPtr, nItem);
8059 infoPtr->nSelectionMark = nItem;
8061 else
8063 LVITEMW item;
8065 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8066 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8068 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8069 infoPtr->nSelectionMark = nItem;
8072 else if (wKey & MK_CONTROL)
8074 LVITEMW item;
8076 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8078 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8079 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8080 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8081 infoPtr->nSelectionMark = nItem;
8083 else if (wKey & MK_SHIFT)
8085 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8087 else
8089 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8090 infoPtr->nEditLabelItem = nItem;
8092 /* set selection (clears other pre-existing selections) */
8093 LISTVIEW_SetSelection(infoPtr, nItem);
8097 else
8099 /* remove all selections */
8100 LISTVIEW_DeselectAll(infoPtr);
8101 ReleaseCapture();
8104 return 0;
8107 /***
8108 * DESCRIPTION:
8109 * Processes mouse up messages (left mouse button).
8111 * PARAMETER(S):
8112 * [I] infoPtr : valid pointer to the listview structure
8113 * [I] wKey : key flag
8114 * [I] pts : mouse coordinate
8116 * RETURN:
8117 * Zero
8119 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8121 LVHITTESTINFO lvHitTestInfo;
8123 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8125 if (!infoPtr->bLButtonDown) return 0;
8127 lvHitTestInfo.pt.x = pts.x;
8128 lvHitTestInfo.pt.y = pts.y;
8130 /* send NM_CLICK notification */
8131 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8132 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8134 /* set left button flag */
8135 infoPtr->bLButtonDown = FALSE;
8137 /* if we clicked on a selected item, edit the label */
8138 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8139 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8141 return 0;
8144 /***
8145 * DESCRIPTION:
8146 * Destroys the listview control (called after WM_DESTROY).
8148 * PARAMETER(S):
8149 * [I] infoPtr : valid pointer to the listview structure
8151 * RETURN:
8152 * Zero
8154 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8156 TRACE("()\n");
8158 /* delete all items */
8159 LISTVIEW_DeleteAllItems(infoPtr);
8161 /* destroy data structure */
8162 DPA_Destroy(infoPtr->hdpaItems);
8163 DPA_Destroy(infoPtr->hdpaPosX);
8164 DPA_Destroy(infoPtr->hdpaPosY);
8165 DPA_Destroy(infoPtr->hdpaColumns);
8166 ranges_destroy(infoPtr->selectionRanges);
8168 /* destroy image lists */
8169 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8171 if (infoPtr->himlNormal)
8172 ImageList_Destroy(infoPtr->himlNormal);
8173 if (infoPtr->himlSmall)
8174 ImageList_Destroy(infoPtr->himlSmall);
8175 if (infoPtr->himlState)
8176 ImageList_Destroy(infoPtr->himlState);
8179 /* destroy font, bkgnd brush */
8180 infoPtr->hFont = 0;
8181 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8182 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8184 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
8186 /* free listview info pointer*/
8187 Free(infoPtr);
8189 return 0;
8192 /***
8193 * DESCRIPTION:
8194 * Handles notifications from header.
8196 * PARAMETER(S):
8197 * [I] infoPtr : valid pointer to the listview structure
8198 * [I] nCtrlId : control identifier
8199 * [I] lpnmh : notification information
8201 * RETURN:
8202 * Zero
8204 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8206 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8208 TRACE("(lpnmh=%p)\n", lpnmh);
8210 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8212 switch (lpnmh->hdr.code)
8214 case HDN_TRACKW:
8215 case HDN_TRACKA:
8216 case HDN_ITEMCHANGEDW:
8217 case HDN_ITEMCHANGEDA:
8219 COLUMN_INFO *lpColumnInfo;
8220 INT dx, cxy;
8222 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8224 HDITEMW hdi;
8226 hdi.mask = HDI_WIDTH;
8227 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8228 cxy = hdi.cxy;
8230 else
8231 cxy = lpnmh->pitem->cxy;
8233 /* determine how much we change since the last know position */
8234 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8235 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8236 if (dx != 0)
8238 RECT rcCol = lpColumnInfo->rcHeader;
8240 lpColumnInfo->rcHeader.right += dx;
8241 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8242 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8244 /* this trick works for left aligned columns only */
8245 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8247 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8248 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8250 rcCol.top = infoPtr->rcList.top;
8251 rcCol.bottom = infoPtr->rcList.bottom;
8252 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8256 break;
8258 case HDN_ITEMCLICKW:
8259 case HDN_ITEMCLICKA:
8261 /* Handle sorting by Header Column */
8262 NMLISTVIEW nmlv;
8264 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8265 nmlv.iItem = -1;
8266 nmlv.iSubItem = lpnmh->iItem;
8267 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8269 break;
8272 return 0;
8275 /***
8276 * DESCRIPTION:
8277 * Determines the type of structure to use.
8279 * PARAMETER(S):
8280 * [I] infoPtr : valid pointer to the listview structureof the sender
8281 * [I] hwndFrom : listview window handle
8282 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8284 * RETURN:
8285 * Zero
8287 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8289 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8291 if (nCommand != NF_REQUERY) return 0;
8293 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8295 return 0;
8298 /***
8299 * DESCRIPTION:
8300 * Paints/Repaints the listview control.
8302 * PARAMETER(S):
8303 * [I] infoPtr : valid pointer to the listview structure
8304 * [I] hdc : device context handle
8306 * RETURN:
8307 * Zero
8309 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8311 TRACE("(hdc=%p)\n", hdc);
8313 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8315 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8317 infoPtr->bNoItemMetrics = FALSE;
8318 LISTVIEW_UpdateItemSize(infoPtr);
8319 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8320 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8321 LISTVIEW_UpdateScroll(infoPtr);
8323 if (hdc)
8324 LISTVIEW_Refresh(infoPtr, hdc);
8325 else
8327 PAINTSTRUCT ps;
8329 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8330 if (!hdc) return 1;
8331 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8332 LISTVIEW_Refresh(infoPtr, hdc);
8333 EndPaint(infoPtr->hwndSelf, &ps);
8336 return 0;
8339 /***
8340 * DESCRIPTION:
8341 * Processes double click messages (right mouse button).
8343 * PARAMETER(S):
8344 * [I] infoPtr : valid pointer to the listview structure
8345 * [I] wKey : key flag
8346 * [I] pts : mouse coordinate
8348 * RETURN:
8349 * Zero
8351 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8353 LVHITTESTINFO lvHitTestInfo;
8355 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8357 /* send NM_RELEASEDCAPTURE notification */
8358 notify(infoPtr, NM_RELEASEDCAPTURE);
8360 /* send NM_RDBLCLK notification */
8361 lvHitTestInfo.pt.x = pts.x;
8362 lvHitTestInfo.pt.y = pts.y;
8363 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8364 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8366 return 0;
8369 /***
8370 * DESCRIPTION:
8371 * Processes mouse down messages (right mouse button).
8373 * PARAMETER(S):
8374 * [I] infoPtr : valid pointer to the listview structure
8375 * [I] wKey : key flag
8376 * [I] pts : mouse coordinate
8378 * RETURN:
8379 * Zero
8381 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8383 LVHITTESTINFO lvHitTestInfo;
8384 INT nItem;
8386 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8388 /* send NM_RELEASEDCAPTURE notification */
8389 notify(infoPtr, NM_RELEASEDCAPTURE);
8391 /* make sure the listview control window has the focus */
8392 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8394 /* set right button down flag */
8395 infoPtr->bRButtonDown = TRUE;
8397 /* determine the index of the selected item */
8398 lvHitTestInfo.pt.x = pts.x;
8399 lvHitTestInfo.pt.y = pts.y;
8400 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8402 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8404 LISTVIEW_SetItemFocus(infoPtr, nItem);
8405 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8406 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8407 LISTVIEW_SetSelection(infoPtr, nItem);
8409 else
8411 LISTVIEW_DeselectAll(infoPtr);
8414 return 0;
8417 /***
8418 * DESCRIPTION:
8419 * Processes mouse up messages (right mouse button).
8421 * PARAMETER(S):
8422 * [I] infoPtr : valid pointer to the listview structure
8423 * [I] wKey : key flag
8424 * [I] pts : mouse coordinate
8426 * RETURN:
8427 * Zero
8429 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8431 LVHITTESTINFO lvHitTestInfo;
8432 POINT pt;
8434 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8436 if (!infoPtr->bRButtonDown) return 0;
8438 /* set button flag */
8439 infoPtr->bRButtonDown = FALSE;
8441 /* Send NM_RClICK notification */
8442 lvHitTestInfo.pt.x = pts.x;
8443 lvHitTestInfo.pt.y = pts.y;
8444 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8445 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8447 /* Change to screen coordinate for WM_CONTEXTMENU */
8448 pt = lvHitTestInfo.pt;
8449 ClientToScreen(infoPtr->hwndSelf, &pt);
8451 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8452 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8453 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8455 return 0;
8459 /***
8460 * DESCRIPTION:
8461 * Sets the cursor.
8463 * PARAMETER(S):
8464 * [I] infoPtr : valid pointer to the listview structure
8465 * [I] hwnd : window handle of window containing the cursor
8466 * [I] nHittest : hit-test code
8467 * [I] wMouseMsg : ideintifier of the mouse message
8469 * RETURN:
8470 * TRUE if cursor is set
8471 * FALSE otherwise
8473 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8475 LVHITTESTINFO lvHitTestInfo;
8477 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8479 if(!infoPtr->hHotCursor) return FALSE;
8481 GetCursorPos(&lvHitTestInfo.pt);
8482 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8484 SetCursor(infoPtr->hHotCursor);
8486 return TRUE;
8489 /***
8490 * DESCRIPTION:
8491 * Sets the focus.
8493 * PARAMETER(S):
8494 * [I] infoPtr : valid pointer to the listview structure
8495 * [I] hwndLoseFocus : handle of previously focused window
8497 * RETURN:
8498 * Zero
8500 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8502 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8504 /* if we have the focus already, there's nothing to do */
8505 if (infoPtr->bFocus) return 0;
8507 /* send NM_SETFOCUS notification */
8508 notify(infoPtr, NM_SETFOCUS);
8510 /* set window focus flag */
8511 infoPtr->bFocus = TRUE;
8513 /* put the focus rect back on */
8514 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8516 /* redraw all visible selected items */
8517 LISTVIEW_InvalidateSelectedItems(infoPtr);
8519 return 0;
8522 /***
8523 * DESCRIPTION:
8524 * Sets the font.
8526 * PARAMETER(S):
8527 * [I] infoPtr : valid pointer to the listview structure
8528 * [I] fRedraw : font handle
8529 * [I] fRedraw : redraw flag
8531 * RETURN:
8532 * Zero
8534 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8536 HFONT oldFont = infoPtr->hFont;
8538 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8540 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8541 if (infoPtr->hFont == oldFont) return 0;
8543 LISTVIEW_SaveTextMetrics(infoPtr);
8545 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8546 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8548 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8550 return 0;
8553 /***
8554 * DESCRIPTION:
8555 * Message handling for WM_SETREDRAW.
8556 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8558 * PARAMETER(S):
8559 * [I] infoPtr : valid pointer to the listview structure
8560 * [I] bRedraw: state of redraw flag
8562 * RETURN:
8563 * DefWinProc return value
8565 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8567 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8569 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8570 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8572 infoPtr->bRedraw = bRedraw;
8574 if(!bRedraw) return 0;
8576 if (is_autoarrange(infoPtr))
8577 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8578 LISTVIEW_UpdateScroll(infoPtr);
8580 /* despite what the WM_SETREDRAW docs says, apps expect us
8581 * to invalidate the listview here... stupid! */
8582 LISTVIEW_InvalidateList(infoPtr);
8584 return 0;
8587 /***
8588 * DESCRIPTION:
8589 * Resizes the listview control. This function processes WM_SIZE
8590 * messages. At this time, the width and height are not used.
8592 * PARAMETER(S):
8593 * [I] infoPtr : valid pointer to the listview structure
8594 * [I] Width : new width
8595 * [I] Height : new height
8597 * RETURN:
8598 * Zero
8600 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8602 RECT rcOld = infoPtr->rcList;
8604 TRACE("(width=%d, height=%d)\n", Width, Height);
8606 LISTVIEW_UpdateSize(infoPtr);
8607 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8609 /* do not bother with display related stuff if we're not redrawing */
8610 if (!is_redrawing(infoPtr)) return 0;
8612 if (is_autoarrange(infoPtr))
8613 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8615 LISTVIEW_UpdateScroll(infoPtr);
8617 /* refresh all only for lists whose height changed significantly */
8618 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8619 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8620 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8621 LISTVIEW_InvalidateList(infoPtr);
8623 return 0;
8626 /***
8627 * DESCRIPTION:
8628 * Sets the size information.
8630 * PARAMETER(S):
8631 * [I] infoPtr : valid pointer to the listview structure
8633 * RETURN:
8634 * None
8636 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8638 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8640 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8642 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8644 if (uView == LVS_LIST)
8646 /* Apparently the "LIST" style is supposed to have the same
8647 * number of items in a column even if there is no scroll bar.
8648 * Since if a scroll bar already exists then the bottom is already
8649 * reduced, only reduce if the scroll bar does not currently exist.
8650 * The "2" is there to mimic the native control. I think it may be
8651 * related to either padding or edges. (GLA 7/2002)
8653 if (!(infoPtr->dwStyle & WS_HSCROLL))
8654 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8655 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8657 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8659 HDLAYOUT hl;
8660 WINDOWPOS wp;
8662 hl.prc = &infoPtr->rcList;
8663 hl.pwpos = &wp;
8664 Header_Layout(infoPtr->hwndHeader, &hl);
8666 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8668 infoPtr->rcList.top = max(wp.cy, 0);
8671 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8674 /***
8675 * DESCRIPTION:
8676 * Processes WM_STYLECHANGED messages.
8678 * PARAMETER(S):
8679 * [I] infoPtr : valid pointer to the listview structure
8680 * [I] wStyleType : window style type (normal or extended)
8681 * [I] lpss : window style information
8683 * RETURN:
8684 * Zero
8686 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8687 const STYLESTRUCT *lpss)
8689 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8690 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8692 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8693 wStyleType, lpss->styleOld, lpss->styleNew);
8695 if (wStyleType != GWL_STYLE) return 0;
8697 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8698 /* what if LVS_OWNERDATA changed? */
8699 /* or LVS_SINGLESEL */
8700 /* or LVS_SORT{AS,DES}CENDING */
8702 infoPtr->dwStyle = lpss->styleNew;
8704 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8705 ((lpss->styleNew & WS_HSCROLL) == 0))
8706 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8708 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8709 ((lpss->styleNew & WS_VSCROLL) == 0))
8710 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8712 if (uNewView != uOldView)
8714 SIZE oldIconSize = infoPtr->iconSize;
8715 HIMAGELIST himl;
8717 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8718 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8720 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8721 SetRectEmpty(&infoPtr->rcFocus);
8723 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8724 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8726 if (uNewView == LVS_ICON)
8728 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8730 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8731 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8732 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8735 else if (uNewView == LVS_REPORT)
8737 HDLAYOUT hl;
8738 WINDOWPOS wp;
8740 hl.prc = &infoPtr->rcList;
8741 hl.pwpos = &wp;
8742 Header_Layout(infoPtr->hwndHeader, &hl);
8743 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8746 LISTVIEW_UpdateItemSize(infoPtr);
8749 if (uNewView == LVS_REPORT)
8750 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8752 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8753 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8754 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8756 /* update the size of the client area */
8757 LISTVIEW_UpdateSize(infoPtr);
8759 /* add scrollbars if needed */
8760 LISTVIEW_UpdateScroll(infoPtr);
8762 /* invalidate client area + erase background */
8763 LISTVIEW_InvalidateList(infoPtr);
8765 return 0;
8768 /***
8769 * DESCRIPTION:
8770 * Window procedure of the listview control.
8773 static LRESULT WINAPI
8774 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8776 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8778 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8780 if (!infoPtr && (uMsg != WM_CREATE))
8781 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8783 if (infoPtr)
8785 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8788 switch (uMsg)
8790 case LVM_APPROXIMATEVIEWRECT:
8791 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8792 LOWORD(lParam), HIWORD(lParam));
8793 case LVM_ARRANGE:
8794 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8796 /* case LVM_CANCELEDITLABEL: */
8798 case LVM_CREATEDRAGIMAGE:
8799 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8801 case LVM_DELETEALLITEMS:
8802 return LISTVIEW_DeleteAllItems(infoPtr);
8804 case LVM_DELETECOLUMN:
8805 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8807 case LVM_DELETEITEM:
8808 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8810 case LVM_EDITLABELW:
8811 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8813 case LVM_EDITLABELA:
8814 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8816 /* case LVM_ENABLEGROUPVIEW: */
8818 case LVM_ENSUREVISIBLE:
8819 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8821 case LVM_FINDITEMW:
8822 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8824 case LVM_FINDITEMA:
8825 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8827 case LVM_GETBKCOLOR:
8828 return infoPtr->clrBk;
8830 /* case LVM_GETBKIMAGE: */
8832 case LVM_GETCALLBACKMASK:
8833 return infoPtr->uCallbackMask;
8835 case LVM_GETCOLUMNA:
8836 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8838 case LVM_GETCOLUMNW:
8839 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8841 case LVM_GETCOLUMNORDERARRAY:
8842 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8844 case LVM_GETCOLUMNWIDTH:
8845 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8847 case LVM_GETCOUNTPERPAGE:
8848 return LISTVIEW_GetCountPerPage(infoPtr);
8850 case LVM_GETEDITCONTROL:
8851 return (LRESULT)infoPtr->hwndEdit;
8853 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8854 return infoPtr->dwLvExStyle;
8856 /* case LVM_GETGROUPINFO: */
8858 /* case LVM_GETGROUPMETRICS: */
8860 case LVM_GETHEADER:
8861 return (LRESULT)infoPtr->hwndHeader;
8863 case LVM_GETHOTCURSOR:
8864 return (LRESULT)infoPtr->hHotCursor;
8866 case LVM_GETHOTITEM:
8867 return infoPtr->nHotItem;
8869 case LVM_GETHOVERTIME:
8870 return infoPtr->dwHoverTime;
8872 case LVM_GETIMAGELIST:
8873 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8875 /* case LVM_GETINSERTMARK: */
8877 /* case LVM_GETINSERTMARKCOLOR: */
8879 /* case LVM_GETINSERTMARKRECT: */
8881 case LVM_GETISEARCHSTRINGA:
8882 case LVM_GETISEARCHSTRINGW:
8883 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8884 return FALSE;
8886 case LVM_GETITEMA:
8887 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8889 case LVM_GETITEMW:
8890 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8892 case LVM_GETITEMCOUNT:
8893 return infoPtr->nItemCount;
8895 case LVM_GETITEMPOSITION:
8896 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8898 case LVM_GETITEMRECT:
8899 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8901 case LVM_GETITEMSPACING:
8902 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8904 case LVM_GETITEMSTATE:
8905 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8907 case LVM_GETITEMTEXTA:
8908 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8910 case LVM_GETITEMTEXTW:
8911 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8913 case LVM_GETNEXTITEM:
8914 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8916 case LVM_GETNUMBEROFWORKAREAS:
8917 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8918 return 1;
8920 case LVM_GETORIGIN:
8921 if (!lParam) return FALSE;
8922 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8923 return TRUE;
8925 /* case LVM_GETOUTLINECOLOR: */
8927 /* case LVM_GETSELECTEDCOLUMN: */
8929 case LVM_GETSELECTEDCOUNT:
8930 return LISTVIEW_GetSelectedCount(infoPtr);
8932 case LVM_GETSELECTIONMARK:
8933 return infoPtr->nSelectionMark;
8935 case LVM_GETSTRINGWIDTHA:
8936 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8938 case LVM_GETSTRINGWIDTHW:
8939 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8941 case LVM_GETSUBITEMRECT:
8942 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8944 case LVM_GETTEXTBKCOLOR:
8945 return infoPtr->clrTextBk;
8947 case LVM_GETTEXTCOLOR:
8948 return infoPtr->clrText;
8950 /* case LVM_GETTILEINFO: */
8952 /* case LVM_GETTILEVIEWINFO: */
8954 case LVM_GETTOOLTIPS:
8955 return (LRESULT)infoPtr->hwndToolTip;
8957 case LVM_GETTOPINDEX:
8958 return LISTVIEW_GetTopIndex(infoPtr);
8960 /*case LVM_GETUNICODEFORMAT:
8961 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8962 return FALSE;*/
8964 /* case LVM_GETVIEW: */
8966 case LVM_GETVIEWRECT:
8967 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8969 case LVM_GETWORKAREAS:
8970 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8971 return FALSE;
8973 /* case LVM_HASGROUP: */
8975 case LVM_HITTEST:
8976 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8978 case LVM_INSERTCOLUMNA:
8979 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8981 case LVM_INSERTCOLUMNW:
8982 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8984 /* case LVM_INSERTGROUP: */
8986 /* case LVM_INSERTGROUPSORTED: */
8988 case LVM_INSERTITEMA:
8989 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8991 case LVM_INSERTITEMW:
8992 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8994 /* case LVM_INSERTMARKHITTEST: */
8996 /* case LVM_ISGROUPVIEWENABLED: */
8998 /* case LVM_MAPIDTOINDEX: */
9000 /* case LVM_MAPINDEXTOID: */
9002 /* case LVM_MOVEGROUP: */
9004 /* case LVM_MOVEITEMTOGROUP: */
9006 case LVM_REDRAWITEMS:
9007 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9009 /* case LVM_REMOVEALLGROUPS: */
9011 /* case LVM_REMOVEGROUP: */
9013 case LVM_SCROLL:
9014 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9016 case LVM_SETBKCOLOR:
9017 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9019 /* case LVM_SETBKIMAGE: */
9021 case LVM_SETCALLBACKMASK:
9022 infoPtr->uCallbackMask = (UINT)wParam;
9023 return TRUE;
9025 case LVM_SETCOLUMNA:
9026 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9028 case LVM_SETCOLUMNW:
9029 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9031 case LVM_SETCOLUMNORDERARRAY:
9032 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9034 case LVM_SETCOLUMNWIDTH:
9035 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9037 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9038 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9040 /* case LVM_SETGROUPINFO: */
9042 /* case LVM_SETGROUPMETRICS: */
9044 case LVM_SETHOTCURSOR:
9045 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9047 case LVM_SETHOTITEM:
9048 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9050 case LVM_SETHOVERTIME:
9051 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9053 case LVM_SETICONSPACING:
9054 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9056 case LVM_SETIMAGELIST:
9057 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9059 /* case LVM_SETINFOTIP: */
9061 /* case LVM_SETINSERTMARK: */
9063 /* case LVM_SETINSERTMARKCOLOR: */
9065 case LVM_SETITEMA:
9066 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9068 case LVM_SETITEMW:
9069 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9071 case LVM_SETITEMCOUNT:
9072 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9074 case LVM_SETITEMPOSITION:
9076 POINT pt;
9077 pt.x = (short)LOWORD(lParam);
9078 pt.y = (short)HIWORD(lParam);
9079 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9082 case LVM_SETITEMPOSITION32:
9083 if (lParam == 0) return FALSE;
9084 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9086 case LVM_SETITEMSTATE:
9087 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9089 case LVM_SETITEMTEXTA:
9090 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9092 case LVM_SETITEMTEXTW:
9093 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9095 /* case LVM_SETOUTLINECOLOR: */
9097 /* case LVM_SETSELECTEDCOLUMN: */
9099 case LVM_SETSELECTIONMARK:
9100 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9102 case LVM_SETTEXTBKCOLOR:
9103 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9105 case LVM_SETTEXTCOLOR:
9106 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9108 /* case LVM_SETTILEINFO: */
9110 /* case LVM_SETTILEVIEWINFO: */
9112 /* case LVM_SETTILEWIDTH: */
9114 case LVM_SETTOOLTIPS:
9115 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9117 /* case LVM_SETUNICODEFORMAT: */
9119 /* case LVM_SETVIEW: */
9121 /* case LVM_SETWORKAREAS: */
9123 /* case LVM_SORTGROUPS: */
9125 case LVM_SORTITEMS:
9126 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9128 /* LVM_SORTITEMSEX: */
9130 case LVM_SUBITEMHITTEST:
9131 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9133 case LVM_UPDATE:
9134 return LISTVIEW_Update(infoPtr, (INT)wParam);
9136 case WM_CHAR:
9137 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9139 case WM_COMMAND:
9140 return LISTVIEW_Command(infoPtr, wParam, lParam);
9142 case WM_CREATE:
9143 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9145 case WM_ERASEBKGND:
9146 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9148 case WM_GETDLGCODE:
9149 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9151 case WM_GETFONT:
9152 return (LRESULT)infoPtr->hFont;
9154 case WM_HSCROLL:
9155 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9157 case WM_KEYDOWN:
9158 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9160 case WM_KILLFOCUS:
9161 return LISTVIEW_KillFocus(infoPtr);
9163 case WM_LBUTTONDBLCLK:
9164 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9166 case WM_LBUTTONDOWN:
9167 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9169 case WM_LBUTTONUP:
9170 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9172 case WM_MOUSEMOVE:
9173 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9175 case WM_MOUSEHOVER:
9176 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9178 case WM_NCDESTROY:
9179 return LISTVIEW_NCDestroy(infoPtr);
9181 case WM_NOTIFY:
9182 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9183 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9184 else return 0;
9186 case WM_NOTIFYFORMAT:
9187 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9189 case WM_PAINT:
9190 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9192 case WM_RBUTTONDBLCLK:
9193 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9195 case WM_RBUTTONDOWN:
9196 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9198 case WM_RBUTTONUP:
9199 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9201 case WM_SETCURSOR:
9202 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9203 return TRUE;
9204 goto fwd_msg;
9206 case WM_SETFOCUS:
9207 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9209 case WM_SETFONT:
9210 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9212 case WM_SETREDRAW:
9213 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9215 case WM_SIZE:
9216 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9218 case WM_STYLECHANGED:
9219 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9221 case WM_SYSCOLORCHANGE:
9222 COMCTL32_RefreshSysColors();
9223 return 0;
9225 /* case WM_TIMER: */
9227 case WM_VSCROLL:
9228 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9230 case WM_MOUSEWHEEL:
9231 if (wParam & (MK_SHIFT | MK_CONTROL))
9232 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9233 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9235 case WM_WINDOWPOSCHANGED:
9236 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9238 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9239 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9240 LISTVIEW_UpdateSize(infoPtr);
9241 LISTVIEW_UpdateScroll(infoPtr);
9243 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9245 /* case WM_WININICHANGE: */
9247 default:
9248 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9249 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9251 fwd_msg:
9252 /* call default window procedure */
9253 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9256 return 0;
9259 /***
9260 * DESCRIPTION:
9261 * Registers the window class.
9263 * PARAMETER(S):
9264 * None
9266 * RETURN:
9267 * None
9269 void LISTVIEW_Register(void)
9271 WNDCLASSW wndClass;
9273 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9274 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9275 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9276 wndClass.cbClsExtra = 0;
9277 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9278 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9279 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9280 wndClass.lpszClassName = WC_LISTVIEWW;
9281 RegisterClassW(&wndClass);
9284 /***
9285 * DESCRIPTION:
9286 * Unregisters the window class.
9288 * PARAMETER(S):
9289 * None
9291 * RETURN:
9292 * None
9294 void LISTVIEW_Unregister(void)
9296 UnregisterClassW(WC_LISTVIEWW, NULL);
9299 /***
9300 * DESCRIPTION:
9301 * Handle any WM_COMMAND messages
9303 * PARAMETER(S):
9304 * [I] infoPtr : valid pointer to the listview structure
9305 * [I] wParam : the first message parameter
9306 * [I] lParam : the second message parameter
9308 * RETURN:
9309 * Zero.
9311 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9313 switch (HIWORD(wParam))
9315 case EN_UPDATE:
9318 * Adjust the edit window size
9320 WCHAR buffer[1024];
9321 HDC hdc = GetDC(infoPtr->hwndEdit);
9322 HFONT hFont, hOldFont = 0;
9323 RECT rect;
9324 SIZE sz;
9325 int len;
9327 if (!infoPtr->hwndEdit || !hdc) return 0;
9328 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9329 GetWindowRect(infoPtr->hwndEdit, &rect);
9331 /* Select font to get the right dimension of the string */
9332 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9333 if(hFont != 0)
9335 hOldFont = SelectObject(hdc, hFont);
9338 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9340 TEXTMETRICW textMetric;
9342 /* Add Extra spacing for the next character */
9343 GetTextMetricsW(hdc, &textMetric);
9344 sz.cx += (textMetric.tmMaxCharWidth * 2);
9346 SetWindowPos (
9347 infoPtr->hwndEdit,
9348 HWND_TOP,
9351 sz.cx,
9352 rect.bottom - rect.top,
9353 SWP_DRAWFRAME|SWP_NOMOVE);
9355 if(hFont != 0)
9356 SelectObject(hdc, hOldFont);
9358 ReleaseDC(infoPtr->hwndSelf, hdc);
9360 break;
9363 default:
9364 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9367 return 0;
9371 /***
9372 * DESCRIPTION:
9373 * Subclassed edit control windproc function
9375 * PARAMETER(S):
9376 * [I] hwnd : the edit window handle
9377 * [I] uMsg : the message that is to be processed
9378 * [I] wParam : first message parameter
9379 * [I] lParam : second message parameter
9380 * [I] isW : TRUE if input is Unicode
9382 * RETURN:
9383 * Zero.
9385 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9387 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9388 BOOL cancel = FALSE;
9390 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9391 hwnd, uMsg, wParam, lParam, isW);
9393 switch (uMsg)
9395 case WM_GETDLGCODE:
9396 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9398 case WM_KILLFOCUS:
9399 break;
9401 case WM_DESTROY:
9403 WNDPROC editProc = infoPtr->EditWndProc;
9404 infoPtr->EditWndProc = 0;
9405 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9406 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9409 case WM_KEYDOWN:
9410 if (VK_ESCAPE == (INT)wParam)
9412 cancel = TRUE;
9413 break;
9415 else if (VK_RETURN == (INT)wParam)
9416 break;
9418 default:
9419 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9422 /* kill the edit */
9423 if (infoPtr->hwndEdit)
9425 LPWSTR buffer = NULL;
9427 infoPtr->hwndEdit = 0;
9428 if (!cancel)
9430 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9432 if (len)
9434 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9436 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9437 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9441 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9443 if (buffer) Free(buffer);
9447 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9448 return 0;
9451 /***
9452 * DESCRIPTION:
9453 * Subclassed edit control Unicode windproc function
9455 * PARAMETER(S):
9456 * [I] hwnd : the edit window handle
9457 * [I] uMsg : the message that is to be processed
9458 * [I] wParam : first message parameter
9459 * [I] lParam : second message parameter
9461 * RETURN:
9463 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9465 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9468 /***
9469 * DESCRIPTION:
9470 * Subclassed edit control ANSI windproc function
9472 * PARAMETER(S):
9473 * [I] hwnd : the edit window handle
9474 * [I] uMsg : the message that is to be processed
9475 * [I] wParam : first message parameter
9476 * [I] lParam : second message parameter
9478 * RETURN:
9480 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9482 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9485 /***
9486 * DESCRIPTION:
9487 * Creates a subclassed edit cotrol
9489 * PARAMETER(S):
9490 * [I] infoPtr : valid pointer to the listview structure
9491 * [I] text : initial text for the edit
9492 * [I] style : the window style
9493 * [I] isW : TRUE if input is Unicode
9495 * RETURN:
9497 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9498 INT x, INT y, INT width, INT height, BOOL isW)
9500 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9501 HWND hedit;
9502 SIZE sz;
9503 HDC hdc;
9504 HDC hOldFont=0;
9505 TEXTMETRICW textMetric;
9506 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9508 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9510 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9511 hdc = GetDC(infoPtr->hwndSelf);
9513 /* Select the font to get appropriate metric dimensions */
9514 if(infoPtr->hFont != 0)
9515 hOldFont = SelectObject(hdc, infoPtr->hFont);
9517 /*Get String Length in pixels */
9518 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9520 /*Add Extra spacing for the next character */
9521 GetTextMetricsW(hdc, &textMetric);
9522 sz.cx += (textMetric.tmMaxCharWidth * 2);
9524 if(infoPtr->hFont != 0)
9525 SelectObject(hdc, hOldFont);
9527 ReleaseDC(infoPtr->hwndSelf, hdc);
9528 if (isW)
9529 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9530 else
9531 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9533 if (!hedit) return 0;
9535 infoPtr->EditWndProc = (WNDPROC)
9536 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9537 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9539 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9541 return hedit;