Use the LVCFMT_{LEFT,RIGHT,CENTER} enumeration flags properly.
[wine/dcerpc.git] / dlls / comctl32 / listview.c
blobfa8a07ba42489b0d427d50ff26ba3fccf9fe99bf
1 /*
2 * Listview control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * NOTES
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on Oct. 21, 2002, by Dimitrie O. Paun.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
33 * TODO:
35 * Features
36 * -- Hot item handling, mouse hovering
37 * -- Workareas support
38 * -- Tilemode support
39 * -- Groups support
41 * Bugs
42 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs.
44 * -- in LISTVIEW_AddGroupSelection, we would 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_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
121 * -- LVM_GETVIEW, LVM_SETVIEW
122 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
123 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
124 * -- LVM_INSERTGROUPSORTED
125 * -- LVM_INSERTMARKHITTEST
126 * -- LVM_ISGROUPVIEWENABLED
127 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
128 * -- LVM_MOVEGROUP
129 * -- LVM_MOVEITEMTOGROUP
130 * -- LVM_SETINFOTIP
131 * -- LVM_SETTILEWIDTH
132 * -- LVM_SORTGROUPS
133 * -- LVM_SORTITEMSEX
135 * Known differences in message stream from native control (not known if
136 * these differences cause problems):
137 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
138 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
139 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
140 * processing for "USEDOUBLECLICKTIME".
143 #include "config.h"
144 #include "wine/port.h"
146 #include <assert.h>
147 #include <ctype.h>
148 #include <string.h>
149 #include <stdlib.h>
150 #include <stdarg.h>
151 #include <stdio.h>
153 #include "windef.h"
154 #include "winbase.h"
155 #include "winnt.h"
156 #include "wingdi.h"
157 #include "winuser.h"
158 #include "winnls.h"
159 #include "commctrl.h"
160 #include "comctl32.h"
162 #include "wine/debug.h"
163 #include "wine/unicode.h"
165 WINE_DEFAULT_DEBUG_CHANNEL(listview);
167 /* make sure you set this to 0 for production use! */
168 #define DEBUG_RANGES 1
170 typedef struct tagCOLUMN_INFO
172 RECT rcHeader; /* tracks the header's rectangle */
173 int fmt; /* same as LVCOLUMN.fmt */
174 } COLUMN_INFO;
176 typedef struct tagITEMHDR
178 LPWSTR pszText;
179 INT iImage;
180 } ITEMHDR, *LPITEMHDR;
182 typedef struct tagSUBITEM_INFO
184 ITEMHDR hdr;
185 INT iSubItem;
186 } SUBITEM_INFO;
188 typedef struct tagITEM_INFO
190 ITEMHDR hdr;
191 UINT state;
192 LPARAM lParam;
193 INT iIndent;
194 } ITEM_INFO;
196 typedef struct tagRANGE
198 INT lower;
199 INT upper;
200 } RANGE;
202 typedef struct tagRANGES
204 HDPA hdpa;
205 } *RANGES;
207 typedef struct tagITERATOR
209 INT nItem;
210 INT nSpecial;
211 RANGE range;
212 RANGES ranges;
213 INT index;
214 } ITERATOR;
216 typedef struct tagLISTVIEW_INFO
218 HWND hwndSelf;
219 HBRUSH hBkBrush;
220 COLORREF clrBk;
221 COLORREF clrText;
222 COLORREF clrTextBk;
223 COLORREF clrTextBkDefault;
224 HIMAGELIST himlNormal;
225 HIMAGELIST himlSmall;
226 HIMAGELIST himlState;
227 BOOL bLButtonDown;
228 BOOL bRButtonDown;
229 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
230 INT nItemHeight;
231 INT nItemWidth;
232 RANGES selectionRanges;
233 INT nSelectionMark;
234 INT nHotItem;
235 SHORT notifyFormat;
236 HWND hwndNotify;
237 RECT rcList; /* This rectangle is really the window
238 * client rectangle possibly reduced by the
239 * horizontal scroll bar and/or header - see
240 * LISTVIEW_UpdateSize. This rectangle offset
241 * by the LISTVIEW_GetOrigin value is in
242 * client coordinates */
243 SIZE iconSize;
244 SIZE iconSpacing;
245 SIZE iconStateSize;
246 UINT uCallbackMask;
247 HWND hwndHeader;
248 HCURSOR hHotCursor;
249 HFONT hDefaultFont;
250 HFONT hFont;
251 INT ntmHeight; /* Some cached metrics of the font used */
252 INT ntmAveCharWidth; /* by the listview to draw items */
253 BOOL bRedraw; /* Turns on/off repaints & invalidations */
254 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
255 BOOL bFocus;
256 BOOL bDoChangeNotify; /* send change notification messages? */
257 INT nFocusedItem;
258 RECT rcFocus;
259 DWORD dwStyle; /* the cached window GWL_STYLE */
260 DWORD dwLvExStyle; /* extended listview style */
261 INT nItemCount; /* the number of items in the list */
262 HDPA hdpaItems; /* array ITEM_INFO pointers */
263 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
264 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
265 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
266 POINT currIconPos; /* this is the position next icon will be placed */
267 PFNLVCOMPARE pfnCompare;
268 LPARAM lParamSort;
269 HWND hwndEdit;
270 WNDPROC EditWndProc;
271 INT nEditLabelItem;
272 DWORD dwHoverTime;
273 HWND hwndToolTip;
275 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
277 DWORD lastKeyPressTimestamp;
278 WPARAM charCode;
279 INT nSearchParamLength;
280 WCHAR szSearchParam[ MAX_PATH ];
281 BOOL bIsDrawing;
282 } LISTVIEW_INFO;
285 * constants
287 /* How many we debug buffer to allocate */
288 #define DEBUG_BUFFERS 20
289 /* The size of a single debug bbuffer */
290 #define DEBUG_BUFFER_SIZE 256
292 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
293 #define SB_INTERNAL -1
295 /* maximum size of a label */
296 #define DISP_TEXT_SIZE 512
298 /* padding for items in list and small icon display modes */
299 #define WIDTH_PADDING 12
301 /* padding for items in list, report and small icon display modes */
302 #define HEIGHT_PADDING 1
304 /* offset of items in report display mode */
305 #define REPORT_MARGINX 2
307 /* padding for icon in large icon display mode
308 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
309 * that HITTEST will see.
310 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
311 * ICON_TOP_PADDING - sum of the two above.
312 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
313 * LABEL_HOR_PADDING - between text and sides of box
314 * LABEL_VERT_PADDING - between bottom of text and end of box
316 * ICON_LR_PADDING - additional width above icon size.
317 * ICON_LR_HALF - half of the above value
319 #define ICON_TOP_PADDING_NOTHITABLE 2
320 #define ICON_TOP_PADDING_HITABLE 2
321 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
322 #define ICON_BOTTOM_PADDING 4
323 #define LABEL_HOR_PADDING 5
324 #define LABEL_VERT_PADDING 7
325 #define ICON_LR_PADDING 16
326 #define ICON_LR_HALF (ICON_LR_PADDING/2)
328 /* default label width for items in list and small icon display modes */
329 #define DEFAULT_LABEL_WIDTH 40
331 /* default column width for items in list display mode */
332 #define DEFAULT_COLUMN_WIDTH 128
334 /* Size of "line" scroll for V & H scrolls */
335 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
337 /* Padding betwen image and label */
338 #define IMAGE_PADDING 2
340 /* Padding behind the label */
341 #define TRAILING_LABEL_PADDING 12
342 #define TRAILING_HEADER_PADDING 11
344 /* Border for the icon caption */
345 #define CAPTION_BORDER 2
347 /* Standard DrawText flags */
348 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
349 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
350 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
352 /* The time in milliseconds to reset the search in the list */
353 #define KEY_DELAY 450
355 /* Dump the LISTVIEW_INFO structure to the debug channel */
356 #define LISTVIEW_DUMP(iP) do { \
357 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
358 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
359 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
360 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
361 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
362 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
363 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
364 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
365 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
366 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
367 } while(0)
370 * forward declarations
372 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
373 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
374 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
375 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
376 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
377 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
378 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
379 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
380 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
381 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
382 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
383 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
384 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
385 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
386 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
387 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
388 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
389 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
390 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
391 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
392 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
393 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
394 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
395 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
396 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
397 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
399 /******** Text handling functions *************************************/
401 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
402 * text string. The string may be ANSI or Unicode, in which case
403 * the boolean isW tells us the type of the string.
405 * The name of the function tell what type of strings it expects:
406 * W: Unicode, T: ANSI/Unicode - function of isW
409 static inline BOOL is_textW(LPCWSTR text)
411 return text != NULL && text != LPSTR_TEXTCALLBACKW;
414 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
416 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
417 return is_textW(text);
420 static inline int textlenT(LPCWSTR text, BOOL isW)
422 return !is_textT(text, isW) ? 0 :
423 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
426 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
428 if (isDestW)
429 if (isSrcW) lstrcpynW(dest, src, max);
430 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
431 else
432 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
433 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
436 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
438 LPWSTR wstr = (LPWSTR)text;
440 if (!isW && is_textT(text, isW))
442 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
443 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
444 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
446 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
447 return wstr;
450 static inline void textfreeT(LPWSTR wstr, BOOL isW)
452 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
456 * dest is a pointer to a Unicode string
457 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
459 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
461 BOOL bResult = TRUE;
463 if (src == LPSTR_TEXTCALLBACKW)
465 if (is_textW(*dest)) Free(*dest);
466 *dest = LPSTR_TEXTCALLBACKW;
468 else
470 LPWSTR pszText = textdupTtoW(src, isW);
471 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
472 bResult = Str_SetPtrW(dest, pszText);
473 textfreeT(pszText, isW);
475 return bResult;
479 * compares a Unicode to a Unicode/ANSI text string
481 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
483 if (!aw) return bt ? -1 : 0;
484 if (!bt) return aw ? 1 : 0;
485 if (aw == LPSTR_TEXTCALLBACKW)
486 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
487 if (bt != LPSTR_TEXTCALLBACKW)
489 LPWSTR bw = textdupTtoW(bt, isW);
490 int r = bw ? lstrcmpW(aw, bw) : 1;
491 textfreeT(bw, isW);
492 return r;
495 return 1;
498 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
500 int res;
502 n = min(min(n, strlenW(s1)), strlenW(s2));
503 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
504 return res ? res - sizeof(WCHAR) : res;
507 /******** Debugging functions *****************************************/
509 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
511 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
512 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
515 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
517 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
518 n = min(textlenT(text, isW), n);
519 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
522 static char* debug_getbuf()
524 static int index = 0;
525 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
526 return buffers[index++ % DEBUG_BUFFERS];
529 static inline const char* debugrange(const RANGE *lprng)
531 if (lprng)
533 char* buf = debug_getbuf();
534 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
535 return buf;
536 } else return "(null)";
539 static inline const char* debugpoint(const POINT *lppt)
541 if (lppt)
543 char* buf = debug_getbuf();
544 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
545 return buf;
546 } else return "(null)";
549 static inline const char* debugrect(const RECT *rect)
551 if (rect)
553 char* buf = debug_getbuf();
554 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
555 rect->left, rect->top, rect->right, rect->bottom);
556 return buf;
557 } else return "(null)";
560 static const char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
562 char* buf = debug_getbuf(), *text = buf;
563 int len, size = DEBUG_BUFFER_SIZE;
565 if (pScrollInfo == NULL) return "(null)";
566 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
567 if (len == -1) goto end; buf += len; size -= len;
568 if (pScrollInfo->fMask & SIF_RANGE)
569 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
570 else len = 0;
571 if (len == -1) goto end; buf += len; size -= len;
572 if (pScrollInfo->fMask & SIF_PAGE)
573 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
574 else len = 0;
575 if (len == -1) goto end; buf += len; size -= len;
576 if (pScrollInfo->fMask & SIF_POS)
577 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
578 else len = 0;
579 if (len == -1) goto end; buf += len; size -= len;
580 if (pScrollInfo->fMask & SIF_TRACKPOS)
581 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
582 else len = 0;
583 if (len == -1) goto end; buf += len; size -= len;
584 goto undo;
585 end:
586 buf = text + strlen(text);
587 undo:
588 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
589 return text;
592 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
594 if (plvnm)
596 char* buf = debug_getbuf();
597 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
598 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
599 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
600 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
601 return buf;
602 } else return "(null)";
605 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
607 char* buf = debug_getbuf(), *text = buf;
608 int len, size = DEBUG_BUFFER_SIZE;
610 if (lpLVItem == NULL) return "(null)";
611 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
612 if (len == -1) goto end; buf += len; size -= len;
613 if (lpLVItem->mask & LVIF_STATE)
614 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
615 else len = 0;
616 if (len == -1) goto end; buf += len; size -= len;
617 if (lpLVItem->mask & LVIF_TEXT)
618 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
619 else len = 0;
620 if (len == -1) goto end; buf += len; size -= len;
621 if (lpLVItem->mask & LVIF_IMAGE)
622 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
623 else len = 0;
624 if (len == -1) goto end; buf += len; size -= len;
625 if (lpLVItem->mask & LVIF_PARAM)
626 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
627 else len = 0;
628 if (len == -1) goto end; buf += len; size -= len;
629 if (lpLVItem->mask & LVIF_INDENT)
630 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
631 else len = 0;
632 if (len == -1) goto end; buf += len; size -= len;
633 goto undo;
634 end:
635 buf = text + strlen(text);
636 undo:
637 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
638 return text;
641 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
643 char* buf = debug_getbuf(), *text = buf;
644 int len, size = DEBUG_BUFFER_SIZE;
646 if (lpColumn == NULL) return "(null)";
647 len = snprintf(buf, size, "{");
648 if (len == -1) goto end; buf += len; size -= len;
649 if (lpColumn->mask & LVCF_SUBITEM)
650 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
651 else len = 0;
652 if (len == -1) goto end; buf += len; size -= len;
653 if (lpColumn->mask & LVCF_FMT)
654 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
655 else len = 0;
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpColumn->mask & LVCF_WIDTH)
658 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
659 else len = 0;
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpColumn->mask & LVCF_TEXT)
662 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
663 else len = 0;
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpColumn->mask & LVCF_IMAGE)
666 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
667 else len = 0;
668 if (len == -1) goto end; buf += len; size -= len;
669 if (lpColumn->mask & LVCF_ORDER)
670 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
671 else len = 0;
672 if (len == -1) goto end; buf += len; size -= len;
673 goto undo;
674 end:
675 buf = text + strlen(text);
676 undo:
677 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
678 return text;
681 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
683 if (lpht)
685 char* buf = debug_getbuf();
686 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
687 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
688 return buf;
689 } else return "(null)";
692 /* Return the corresponding text for a given scroll value */
693 static inline LPCSTR debugscrollcode(int nScrollCode)
695 switch(nScrollCode)
697 case SB_LINELEFT: return "SB_LINELEFT";
698 case SB_LINERIGHT: return "SB_LINERIGHT";
699 case SB_PAGELEFT: return "SB_PAGELEFT";
700 case SB_PAGERIGHT: return "SB_PAGERIGHT";
701 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
702 case SB_THUMBTRACK: return "SB_THUMBTRACK";
703 case SB_ENDSCROLL: return "SB_ENDSCROLL";
704 case SB_INTERNAL: return "SB_INTERNAL";
705 default: return "unknown";
710 /******** Notification functions i************************************/
712 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
714 LRESULT result;
716 TRACE("(code=%d)\n", code);
718 pnmh->hwndFrom = infoPtr->hwndSelf;
719 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
720 pnmh->code = code;
721 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
722 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
724 TRACE(" <= %ld\n", result);
726 return result;
729 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
731 NMHDR nmh;
732 return notify_hdr(infoPtr, code, &nmh);
735 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
737 NMITEMACTIVATE nmia;
738 LVITEMW item;
740 if (htInfo) {
741 nmia.uNewState = 0;
742 nmia.uOldState = 0;
743 nmia.uChanged = 0;
744 nmia.uKeyFlags = 0;
746 item.mask = LVIF_PARAM|LVIF_STATE;
747 item.iItem = htInfo->iItem;
748 item.iSubItem = 0;
749 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
750 nmia.lParam = item.lParam;
751 nmia.uOldState = item.state;
752 nmia.uNewState = item.state | LVIS_ACTIVATING;
753 nmia.uChanged = LVIF_STATE;
756 nmia.iItem = htInfo->iItem;
757 nmia.iSubItem = htInfo->iSubItem;
758 nmia.ptAction = htInfo->pt;
760 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
761 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
762 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
764 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
767 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
769 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
770 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
773 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
775 NMLISTVIEW nmlv;
776 LVITEMW item;
778 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
779 ZeroMemory(&nmlv, sizeof(nmlv));
780 nmlv.iItem = lvht->iItem;
781 nmlv.iSubItem = lvht->iSubItem;
782 nmlv.ptAction = lvht->pt;
783 item.mask = LVIF_PARAM;
784 item.iItem = lvht->iItem;
785 item.iSubItem = 0;
786 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
787 return notify_listview(infoPtr, code, &nmlv);
790 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
792 NMLISTVIEW nmlv;
793 LVITEMW item;
795 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
796 nmlv.iItem = nItem;
797 item.mask = LVIF_PARAM;
798 item.iItem = nItem;
799 item.iSubItem = 0;
800 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
801 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
804 static int get_ansi_notification(INT unicodeNotificationCode)
806 switch (unicodeNotificationCode)
808 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
809 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
810 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
811 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
812 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
813 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
815 ERR("unknown notification %x\n", unicodeNotificationCode);
816 assert(FALSE);
817 return 0;
821 Send notification. depends on dispinfoW having same
822 structure as dispinfoA.
823 infoPtr : listview struct
824 notificationCode : *Unicode* notification code
825 pdi : dispinfo structure (can be unicode or ansi)
826 isW : TRUE if dispinfo is Unicode
828 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
830 BOOL bResult = FALSE;
831 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
832 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
833 LPWSTR pszTempBuf = NULL, savPszText = NULL;
835 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
837 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
838 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
841 if (convertToAnsi || convertToUnicode)
843 if (notificationCode != LVN_GETDISPINFOW)
845 cchTempBufMax = convertToUnicode ?
846 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
847 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
849 else
851 cchTempBufMax = pdi->item.cchTextMax;
852 *pdi->item.pszText = 0; /* make sure we don't process garbage */
855 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
856 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
857 if (!pszTempBuf) return FALSE;
859 if (convertToUnicode)
860 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
861 pszTempBuf, cchTempBufMax);
862 else
863 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
864 cchTempBufMax, NULL, NULL);
866 savCchTextMax = pdi->item.cchTextMax;
867 savPszText = pdi->item.pszText;
868 pdi->item.pszText = pszTempBuf;
869 pdi->item.cchTextMax = cchTempBufMax;
872 if (infoPtr->notifyFormat == NFR_ANSI)
873 realNotifCode = get_ansi_notification(notificationCode);
874 else
875 realNotifCode = notificationCode;
876 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
877 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
879 if (convertToUnicode || convertToAnsi)
881 if (convertToUnicode) /* note : pointer can be changed by app ! */
882 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
883 savCchTextMax, NULL, NULL);
884 else
885 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
886 savPszText, savCchTextMax);
887 pdi->item.pszText = savPszText; /* restores our buffer */
888 pdi->item.cchTextMax = savCchTextMax;
889 HeapFree(GetProcessHeap(), 0, pszTempBuf);
891 return bResult;
894 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
895 const RECT *rcBounds, const LVITEMW *lplvItem)
897 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
898 lpnmlvcd->nmcd.hdc = hdc;
899 lpnmlvcd->nmcd.rc = *rcBounds;
900 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
901 lpnmlvcd->clrText = infoPtr->clrText;
902 if (!lplvItem) return;
903 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
904 lpnmlvcd->iSubItem = lplvItem->iSubItem;
905 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
906 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
907 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
908 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
911 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
913 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
914 DWORD result;
916 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
917 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
918 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
919 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
920 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
921 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
922 return result;
925 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
927 /* apprently, for selected items, we have to override the returned values */
928 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
930 if (infoPtr->bFocus)
932 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
933 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
935 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
937 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
938 lpnmlvcd->clrText = comctl32_color.clrBtnText;
942 /* Set the text attributes */
943 if (lpnmlvcd->clrTextBk != CLR_NONE)
945 SetBkMode(hdc, OPAQUE);
946 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
947 SetBkColor(hdc, infoPtr->clrTextBkDefault);
948 else
949 SetBkColor(hdc,lpnmlvcd->clrTextBk);
951 else
952 SetBkMode(hdc, TRANSPARENT);
953 SetTextColor(hdc, lpnmlvcd->clrText);
956 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
958 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
961 /******** Item iterator functions **********************************/
963 static RANGES ranges_create(int count);
964 static void ranges_destroy(RANGES ranges);
965 static BOOL ranges_add(RANGES ranges, RANGE range);
966 static BOOL ranges_del(RANGES ranges, RANGE range);
967 static void ranges_dump(RANGES ranges);
969 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
971 RANGE range = { nItem, nItem + 1 };
973 return ranges_add(ranges, range);
976 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
978 RANGE range = { nItem, nItem + 1 };
980 return ranges_del(ranges, range);
983 /***
984 * ITERATOR DOCUMENTATION
986 * The iterator functions allow for easy, and convenient iteration
987 * over items of iterest in the list. Typically, you create a
988 * iterator, use it, and destroy it, as such:
989 * ITERATOR i;
991 * iterator_xxxitems(&i, ...);
992 * while (iterator_{prev,next}(&i)
994 * //code which uses i.nItem
996 * iterator_destroy(&i);
998 * where xxx is either: framed, or visible.
999 * Note that it is important that the code destroys the iterator
1000 * after it's done with it, as the creation of the iterator may
1001 * allocate memory, which thus needs to be freed.
1003 * You can iterate both forwards, and backwards through the list,
1004 * by using iterator_next or iterator_prev respectively.
1006 * Lower numbered items are draw on top of higher number items in
1007 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1008 * items may overlap). So, to test items, you should use
1009 * iterator_next
1010 * which lists the items top to bottom (in Z-order).
1011 * For drawing items, you should use
1012 * iterator_prev
1013 * which lists the items bottom to top (in Z-order).
1014 * If you keep iterating over the items after the end-of-items
1015 * marker (-1) is returned, the iterator will start from the
1016 * beginning. Typically, you don't need to test for -1,
1017 * because iterator_{next,prev} will return TRUE if more items
1018 * are to be iterated over, or FALSE otherwise.
1020 * Note: the iterator is defined to be bidirectional. That is,
1021 * any number of prev followed by any number of next, or
1022 * five versa, should leave the iterator at the same item:
1023 * prev * n, next * n = next * n, prev * n
1025 * The iterator has a notion of a out-of-order, special item,
1026 * which sits at the start of the list. This is used in
1027 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1028 * which needs to be first, as it may overlap other items.
1030 * The code is a bit messy because we have:
1031 * - a special item to deal with
1032 * - simple range, or composite range
1033 * - empty range.
1034 * If you find bugs, or want to add features, please make sure you
1035 * always check/modify *both* iterator_prev, and iterator_next.
1038 /****
1039 * This function iterates through the items in increasing order,
1040 * but prefixed by the special item, then -1. That is:
1041 * special, 1, 2, 3, ..., n, -1.
1042 * Each item is listed only once.
1044 static inline BOOL iterator_next(ITERATOR* i)
1046 if (i->nItem == -1)
1048 i->nItem = i->nSpecial;
1049 if (i->nItem != -1) return TRUE;
1051 if (i->nItem == i->nSpecial)
1053 if (i->ranges) i->index = 0;
1054 goto pickarange;
1057 i->nItem++;
1058 testitem:
1059 if (i->nItem == i->nSpecial) i->nItem++;
1060 if (i->nItem < i->range.upper) return TRUE;
1062 pickarange:
1063 if (i->ranges)
1065 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1066 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1067 else goto end;
1069 else if (i->nItem >= i->range.upper) goto end;
1071 i->nItem = i->range.lower;
1072 if (i->nItem >= 0) goto testitem;
1073 end:
1074 i->nItem = -1;
1075 return FALSE;
1078 /****
1079 * This function iterates through the items in decreasing order,
1080 * followed by the special item, then -1. That is:
1081 * n, n-1, ..., 3, 2, 1, special, -1.
1082 * Each item is listed only once.
1084 static inline BOOL iterator_prev(ITERATOR* i)
1086 BOOL start = FALSE;
1088 if (i->nItem == -1)
1090 start = TRUE;
1091 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1092 goto pickarange;
1094 if (i->nItem == i->nSpecial)
1096 i->nItem = -1;
1097 return FALSE;
1100 testitem:
1101 i->nItem--;
1102 if (i->nItem == i->nSpecial) i->nItem--;
1103 if (i->nItem >= i->range.lower) return TRUE;
1105 pickarange:
1106 if (i->ranges)
1108 if (i->index > 0)
1109 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1110 else goto end;
1112 else if (!start && i->nItem < i->range.lower) goto end;
1114 i->nItem = i->range.upper;
1115 if (i->nItem > 0) goto testitem;
1116 end:
1117 return (i->nItem = i->nSpecial) != -1;
1120 static RANGE iterator_range(ITERATOR* i)
1122 RANGE range;
1124 if (!i->ranges) return i->range;
1126 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1128 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1129 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1131 else range.lower = range.upper = 0;
1133 return range;
1136 /***
1137 * Releases resources associated with this ierator.
1139 static inline void iterator_destroy(ITERATOR* i)
1141 ranges_destroy(i->ranges);
1144 /***
1145 * Create an empty iterator.
1147 static inline BOOL iterator_empty(ITERATOR* i)
1149 ZeroMemory(i, sizeof(*i));
1150 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1151 return TRUE;
1154 /***
1155 * Create an iterator over a range.
1157 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1159 iterator_empty(i);
1160 i->range = range;
1161 return TRUE;
1164 /***
1165 * Create an iterator over a bunch of ranges.
1166 * Please note that the iterator will take ownership of the ranges,
1167 * and will free them upon destruction.
1169 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1171 iterator_empty(i);
1172 i->ranges = ranges;
1173 return TRUE;
1176 /***
1177 * Creates an iterator over the items which intersect lprc.
1179 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1181 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1182 RECT frame = *lprc, rcItem, rcTemp;
1183 POINT Origin;
1185 /* in case we fail, we want to return an empty iterator */
1186 if (!iterator_empty(i)) return FALSE;
1188 LISTVIEW_GetOrigin(infoPtr, &Origin);
1190 TRACE("(lprc=%s)\n", debugrect(lprc));
1191 OffsetRect(&frame, -Origin.x, -Origin.y);
1193 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1195 INT nItem;
1197 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1199 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1200 if (IntersectRect(&rcTemp, &rcItem, lprc))
1201 i->nSpecial = infoPtr->nFocusedItem;
1203 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1204 /* to do better here, we need to have PosX, and PosY sorted */
1205 TRACE("building icon ranges:\n");
1206 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1208 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1209 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1210 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1211 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1212 if (IntersectRect(&rcTemp, &rcItem, &frame))
1213 ranges_additem(i->ranges, nItem);
1215 return TRUE;
1217 else if (uView == LVS_REPORT)
1219 RANGE range;
1221 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1222 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1224 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1225 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1226 if (range.upper <= range.lower) return TRUE;
1227 if (!iterator_rangeitems(i, range)) return FALSE;
1228 TRACE(" report=%s\n", debugrange(&i->range));
1230 else
1232 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1233 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1234 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1235 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1236 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1237 INT lower = nFirstCol * nPerCol + nFirstRow;
1238 RANGE item_range;
1239 INT nCol;
1241 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1242 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1244 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1246 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1247 TRACE("building list ranges:\n");
1248 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1250 item_range.lower = nCol * nPerCol + nFirstRow;
1251 if(item_range.lower >= infoPtr->nItemCount) break;
1252 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1253 TRACE(" list=%s\n", debugrange(&item_range));
1254 ranges_add(i->ranges, item_range);
1258 return TRUE;
1261 /***
1262 * Creates an iterator over the items which intersect the visible region of hdc.
1264 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1266 POINT Origin, Position;
1267 RECT rcItem, rcClip;
1268 INT rgntype;
1270 rgntype = GetClipBox(hdc, &rcClip);
1271 if (rgntype == NULLREGION) return iterator_empty(i);
1272 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1273 if (rgntype == SIMPLEREGION) return TRUE;
1275 /* first deal with the special item */
1276 if (i->nSpecial != -1)
1278 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1279 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1282 /* if we can't deal with the region, we'll just go with the simple range */
1283 LISTVIEW_GetOrigin(infoPtr, &Origin);
1284 TRACE("building visible range:\n");
1285 if (!i->ranges && i->range.lower < i->range.upper)
1287 if (!(i->ranges = ranges_create(50))) return TRUE;
1288 if (!ranges_add(i->ranges, i->range))
1290 ranges_destroy(i->ranges);
1291 i->ranges = 0;
1292 return TRUE;
1296 /* now delete the invisible items from the list */
1297 while(iterator_next(i))
1299 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1300 rcItem.left = Position.x + Origin.x;
1301 rcItem.top = Position.y + Origin.y;
1302 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1303 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1304 if (!RectVisible(hdc, &rcItem))
1305 ranges_delitem(i->ranges, i->nItem);
1307 /* the iterator should restart on the next iterator_next */
1308 TRACE("done\n");
1310 return TRUE;
1313 /******** Misc helper functions ************************************/
1315 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1316 WPARAM wParam, LPARAM lParam, BOOL isW)
1318 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1319 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1322 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1324 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1326 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1327 (uView == LVS_ICON || uView == LVS_SMALLICON);
1330 /******** Internal API functions ************************************/
1332 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1334 static COLUMN_INFO mainItem;
1336 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1337 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1338 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1341 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1343 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1346 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1348 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1351 /* Listview invalidation functions: use _only_ these functions to invalidate */
1353 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1355 return infoPtr->bRedraw;
1358 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1360 if(!is_redrawing(infoPtr)) return;
1361 TRACE(" invalidating rect=%s\n", debugrect(rect));
1362 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1365 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1367 RECT rcBox;
1369 if(!is_redrawing(infoPtr)) return;
1370 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1371 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1374 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1376 POINT Origin, Position;
1377 RECT rcBox;
1379 if(!is_redrawing(infoPtr)) return;
1380 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1381 LISTVIEW_GetOrigin(infoPtr, &Origin);
1382 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1383 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1384 rcBox.top = 0;
1385 rcBox.bottom = infoPtr->nItemHeight;
1386 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1387 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1390 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1392 LISTVIEW_InvalidateRect(infoPtr, NULL);
1395 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1397 RECT rcCol;
1399 if(!is_redrawing(infoPtr)) return;
1400 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1401 rcCol.top = infoPtr->rcList.top;
1402 rcCol.bottom = infoPtr->rcList.bottom;
1403 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1406 /***
1407 * DESCRIPTION:
1408 * Retrieves the number of items that can fit vertically in the client area.
1410 * PARAMETER(S):
1411 * [I] infoPtr : valid pointer to the listview structure
1413 * RETURN:
1414 * Number of items per row.
1416 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1418 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1420 return max(nListWidth/infoPtr->nItemWidth, 1);
1423 /***
1424 * DESCRIPTION:
1425 * Retrieves the number of items that can fit horizontally in the client
1426 * area.
1428 * PARAMETER(S):
1429 * [I] infoPtr : valid pointer to the listview structure
1431 * RETURN:
1432 * Number of items per column.
1434 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1436 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1438 return max(nListHeight / infoPtr->nItemHeight, 1);
1442 /*************************************************************************
1443 * LISTVIEW_ProcessLetterKeys
1445 * Processes keyboard messages generated by pressing the letter keys
1446 * on the keyboard.
1447 * What this does is perform a case insensitive search from the
1448 * current position with the following quirks:
1449 * - If two chars or more are pressed in quick succession we search
1450 * for the corresponding string (e.g. 'abc').
1451 * - If there is a delay we wipe away the current search string and
1452 * restart with just that char.
1453 * - If the user keeps pressing the same character, whether slowly or
1454 * fast, so that the search string is entirely composed of this
1455 * character ('aaaaa' for instance), then we search for first item
1456 * that starting with that character.
1457 * - If the user types the above character in quick succession, then
1458 * we must also search for the corresponding string ('aaaaa'), and
1459 * go to that string if there is a match.
1461 * PARAMETERS
1462 * [I] hwnd : handle to the window
1463 * [I] charCode : the character code, the actual character
1464 * [I] keyData : key data
1466 * RETURNS
1468 * Zero.
1470 * BUGS
1472 * - The current implementation has a list of characters it will
1473 * accept and it ignores averything else. In particular it will
1474 * ignore accentuated characters which seems to match what
1475 * Windows does. But I'm not sure it makes sense to follow
1476 * Windows there.
1477 * - We don't sound a beep when the search fails.
1479 * SEE ALSO
1481 * TREEVIEW_ProcessLetterKeys
1483 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1485 INT nItem;
1486 INT endidx,idx;
1487 LVITEMW item;
1488 WCHAR buffer[MAX_PATH];
1489 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1491 /* simple parameter checking */
1492 if (!charCode || !keyData) return 0;
1494 /* only allow the valid WM_CHARs through */
1495 if (!isalnum(charCode) &&
1496 charCode != '.' && charCode != '`' && charCode != '!' &&
1497 charCode != '@' && charCode != '#' && charCode != '$' &&
1498 charCode != '%' && charCode != '^' && charCode != '&' &&
1499 charCode != '*' && charCode != '(' && charCode != ')' &&
1500 charCode != '-' && charCode != '_' && charCode != '+' &&
1501 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1502 charCode != '}' && charCode != '[' && charCode != '{' &&
1503 charCode != '/' && charCode != '?' && charCode != '>' &&
1504 charCode != '<' && charCode != ',' && charCode != '~')
1505 return 0;
1507 /* if there's one item or less, there is no where to go */
1508 if (infoPtr->nItemCount <= 1) return 0;
1510 /* update the search parameters */
1511 infoPtr->lastKeyPressTimestamp = GetTickCount();
1512 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1513 if (infoPtr->nSearchParamLength < MAX_PATH)
1514 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1515 if (infoPtr->charCode != charCode)
1516 infoPtr->charCode = charCode = 0;
1517 } else {
1518 infoPtr->charCode=charCode;
1519 infoPtr->szSearchParam[0]=charCode;
1520 infoPtr->nSearchParamLength=1;
1521 /* Redundant with the 1 char string */
1522 charCode=0;
1525 /* and search from the current position */
1526 nItem=-1;
1527 if (infoPtr->nFocusedItem >= 0) {
1528 endidx=infoPtr->nFocusedItem;
1529 idx=endidx;
1530 /* if looking for single character match,
1531 * then we must always move forward
1533 if (infoPtr->nSearchParamLength == 1)
1534 idx++;
1535 } else {
1536 endidx=infoPtr->nItemCount;
1537 idx=0;
1539 do {
1540 if (idx == infoPtr->nItemCount) {
1541 if (endidx == infoPtr->nItemCount || endidx == 0)
1542 break;
1543 idx=0;
1546 /* get item */
1547 item.mask = LVIF_TEXT;
1548 item.iItem = idx;
1549 item.iSubItem = 0;
1550 item.pszText = buffer;
1551 item.cchTextMax = MAX_PATH;
1552 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1554 /* check for a match */
1555 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1556 nItem=idx;
1557 break;
1558 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1559 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1560 /* This would work but we must keep looking for a longer match */
1561 nItem=idx;
1563 idx++;
1564 } while (idx != endidx);
1566 if (nItem != -1)
1567 LISTVIEW_KeySelection(infoPtr, nItem);
1569 return 0;
1572 /*************************************************************************
1573 * LISTVIEW_UpdateHeaderSize [Internal]
1575 * Function to resize the header control
1577 * PARAMS
1578 * [I] hwnd : handle to a window
1579 * [I] nNewScrollPos : scroll pos to set
1581 * RETURNS
1582 * None.
1584 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1586 RECT winRect;
1587 POINT point[2];
1589 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1591 GetWindowRect(infoPtr->hwndHeader, &winRect);
1592 point[0].x = winRect.left;
1593 point[0].y = winRect.top;
1594 point[1].x = winRect.right;
1595 point[1].y = winRect.bottom;
1597 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1598 point[0].x = -nNewScrollPos;
1599 point[1].x += nNewScrollPos;
1601 SetWindowPos(infoPtr->hwndHeader,0,
1602 point[0].x,point[0].y,point[1].x,point[1].y,
1603 SWP_NOZORDER | SWP_NOACTIVATE);
1606 /***
1607 * DESCRIPTION:
1608 * Update the scrollbars. This functions should be called whenever
1609 * the content, size or view changes.
1611 * PARAMETER(S):
1612 * [I] infoPtr : valid pointer to the listview structure
1614 * RETURN:
1615 * None
1617 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1619 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1620 SCROLLINFO horzInfo, vertInfo;
1622 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1624 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1625 horzInfo.cbSize = sizeof(SCROLLINFO);
1626 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1628 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1629 if (uView == LVS_LIST)
1631 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1632 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1634 /* scroll by at least one column per page */
1635 if(horzInfo.nPage < infoPtr->nItemWidth)
1636 horzInfo.nPage = infoPtr->nItemWidth;
1638 horzInfo.nPage /= infoPtr->nItemWidth;
1640 else if (uView == LVS_REPORT)
1642 horzInfo.nMax = infoPtr->nItemWidth;
1644 else /* LVS_ICON, or LVS_SMALLICON */
1646 RECT rcView;
1648 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1651 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1652 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1653 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1654 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1656 /* Setting the horizontal scroll can change the listview size
1657 * (and potentially everything else) so we need to recompute
1658 * everything again for the vertical scroll
1661 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1662 vertInfo.cbSize = sizeof(SCROLLINFO);
1663 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1665 if (uView == LVS_REPORT)
1667 vertInfo.nMax = infoPtr->nItemCount;
1669 /* scroll by at least one page */
1670 if(vertInfo.nPage < infoPtr->nItemHeight)
1671 vertInfo.nPage = infoPtr->nItemHeight;
1673 vertInfo.nPage /= infoPtr->nItemHeight;
1675 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1677 RECT rcView;
1679 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1682 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1683 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1684 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1685 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1687 /* Update the Header Control */
1688 if (uView == LVS_REPORT)
1690 horzInfo.fMask = SIF_POS;
1691 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1692 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1697 /***
1698 * DESCRIPTION:
1699 * Shows/hides the focus rectangle.
1701 * PARAMETER(S):
1702 * [I] infoPtr : valid pointer to the listview structure
1703 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1705 * RETURN:
1706 * None
1708 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1710 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1711 HDC hdc;
1713 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1715 if (infoPtr->nFocusedItem < 0) return;
1717 /* we need some gymnastics in ICON mode to handle large items */
1718 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1720 RECT rcBox;
1722 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1723 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1725 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1726 return;
1730 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1732 /* for some reason, owner draw should work only in report mode */
1733 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1735 DRAWITEMSTRUCT dis;
1736 LVITEMW item;
1738 item.iItem = infoPtr->nFocusedItem;
1739 item.iSubItem = 0;
1740 item.mask = LVIF_PARAM;
1741 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1743 ZeroMemory(&dis, sizeof(dis));
1744 dis.CtlType = ODT_LISTVIEW;
1745 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1746 dis.itemID = item.iItem;
1747 dis.itemAction = ODA_FOCUS;
1748 if (fShow) dis.itemState |= ODS_FOCUS;
1749 dis.hwndItem = infoPtr->hwndSelf;
1750 dis.hDC = hdc;
1751 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1752 dis.itemData = item.lParam;
1754 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1756 else
1758 DrawFocusRect(hdc, &infoPtr->rcFocus);
1760 done:
1761 ReleaseDC(infoPtr->hwndSelf, hdc);
1764 /***
1765 * Invalidates all visible selected items.
1767 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1769 ITERATOR i;
1771 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1772 while(iterator_next(&i))
1774 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1775 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1777 iterator_destroy(&i);
1781 /***
1782 * DESCRIPTION: [INTERNAL]
1783 * Computes an item's (left,top) corner, relative to rcView.
1784 * That is, the position has NOT been made relative to the Origin.
1785 * This is deliberate, to avoid computing the Origin over, and
1786 * over again, when this function is call in a loop. Instead,
1787 * one ca factor the computation of the Origin before the loop,
1788 * and offset the value retured by this function, on every iteration.
1790 * PARAMETER(S):
1791 * [I] infoPtr : valid pointer to the listview structure
1792 * [I] nItem : item number
1793 * [O] lpptOrig : item top, left corner
1795 * RETURN:
1796 * None.
1798 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1800 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1802 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1804 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1806 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1807 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1809 else if (uView == LVS_LIST)
1811 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1812 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1813 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1815 else /* LVS_REPORT */
1817 lpptPosition->x = 0;
1818 lpptPosition->y = nItem * infoPtr->nItemHeight;
1822 /***
1823 * DESCRIPTION: [INTERNAL]
1824 * Compute the rectangles of an item. This is to localize all
1825 * the computations in one place. If you are not interested in some
1826 * of these values, simply pass in a NULL -- the fucntion is smart
1827 * enough to compute only what's necessary. The function computes
1828 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1829 * one, the BOX rectangle. This rectangle is very cheap to compute,
1830 * and is guaranteed to contain all the other rectangles. Computing
1831 * the ICON rect is also cheap, but all the others are potentaily
1832 * expensive. This gives an easy and effective optimization when
1833 * searching (like point inclusion, or rectangle intersection):
1834 * first test against the BOX, and if TRUE, test agains the desired
1835 * rectangle.
1836 * If the function does not have all the necessary information
1837 * to computed the requested rectangles, will crash with a
1838 * failed assertion. This is done so we catch all programming
1839 * errors, given that the function is called only from our code.
1841 * We have the following 'special' meanings for a few fields:
1842 * * If LVIS_FOCUSED is set, we assume the item has the focus
1843 * This is important in ICON mode, where it might get a larger
1844 * then usual rectange
1846 * Please note that subitem support works only in REPORT mode.
1848 * PARAMETER(S):
1849 * [I] infoPtr : valid pointer to the listview structure
1850 * [I] lpLVItem : item to compute the measures for
1851 * [O] lprcBox : ptr to Box rectangle
1852 * The internal LVIR_BOX rectangle
1853 * [0] lprcState : ptr to State icon rectangle
1854 * The internal LVIR_STATE rectangle
1855 * [O] lprcIcon : ptr to Icon rectangle
1856 * Same as LVM_GETITEMRECT with LVIR_ICON
1857 * [O] lprcLabel : ptr to Label rectangle
1858 * Same as LVM_GETITEMRECT with LVIR_LABEL
1860 * RETURN:
1861 * None.
1863 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1864 LPRECT lprcBox, LPRECT lprcState,
1865 LPRECT lprcIcon, LPRECT lprcLabel)
1867 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1868 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1869 RECT Box, State, Icon, Label;
1870 COLUMN_INFO *lpColumnInfo = NULL;
1872 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1874 /* Be smart and try to figure out the minimum we have to do */
1875 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1876 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1878 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1879 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1881 if (lprcLabel) doLabel = TRUE;
1882 if (doLabel || lprcIcon) doIcon = TRUE;
1883 if (doIcon || lprcState) doState = TRUE;
1885 /************************************************************/
1886 /* compute the box rectangle (it should be cheap to do) */
1887 /************************************************************/
1888 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1889 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1891 if (lpLVItem->iSubItem)
1893 Box = lpColumnInfo->rcHeader;
1895 else
1897 Box.left = 0;
1898 Box.right = infoPtr->nItemWidth;
1900 Box.top = 0;
1901 Box.bottom = infoPtr->nItemHeight;
1903 /************************************************************/
1904 /* compute STATEICON bounding box */
1905 /************************************************************/
1906 if (doState)
1908 if (uView == LVS_ICON)
1910 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1911 if (infoPtr->himlNormal)
1912 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1913 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1915 else
1917 /* we need the ident in report mode, if we don't have it, we fail */
1918 State.left = Box.left;
1919 if (uView == LVS_REPORT)
1921 if (lpLVItem->iSubItem == 0)
1923 State.left += REPORT_MARGINX;
1924 assert(lpLVItem->mask & LVIF_INDENT);
1925 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1928 State.top = Box.top;
1930 State.right = State.left;
1931 State.bottom = State.top;
1932 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1934 State.right += infoPtr->iconStateSize.cx;
1935 State.bottom += infoPtr->iconStateSize.cy;
1937 if (lprcState) *lprcState = State;
1938 TRACE(" - state=%s\n", debugrect(&State));
1941 /************************************************************/
1942 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1943 /************************************************************/
1944 if (doIcon)
1946 if (uView == LVS_ICON)
1948 Icon.left = Box.left;
1949 if (infoPtr->himlNormal)
1950 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1951 Icon.top = Box.top + ICON_TOP_PADDING;
1952 Icon.right = Icon.left;
1953 Icon.bottom = Icon.top;
1954 if (infoPtr->himlNormal)
1956 Icon.right += infoPtr->iconSize.cx;
1957 Icon.bottom += infoPtr->iconSize.cy;
1960 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1962 Icon.left = State.right;
1963 Icon.top = Box.top;
1964 Icon.right = Icon.left;
1965 if (infoPtr->himlSmall &&
1966 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1967 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1968 Icon.right += infoPtr->iconSize.cx;
1969 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1971 if(lprcIcon) *lprcIcon = Icon;
1972 TRACE(" - icon=%s\n", debugrect(&Icon));
1975 /************************************************************/
1976 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1977 /************************************************************/
1978 if (doLabel)
1980 SIZE labelSize = { 0, 0 };
1982 /* calculate how far to the right can the label strech */
1983 Label.right = Box.right;
1984 if (uView == LVS_REPORT)
1986 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1989 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1991 labelSize.cx = infoPtr->nItemWidth;
1992 labelSize.cy = infoPtr->nItemHeight;
1993 goto calc_label;
1996 /* we need the text in non owner draw mode */
1997 assert(lpLVItem->mask & LVIF_TEXT);
1998 if (is_textT(lpLVItem->pszText, TRUE))
2000 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2001 HDC hdc = GetDC(infoPtr->hwndSelf);
2002 HFONT hOldFont = SelectObject(hdc, hFont);
2003 UINT uFormat;
2004 RECT rcText;
2006 /* compute rough rectangle where the label will go */
2007 SetRectEmpty(&rcText);
2008 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2009 rcText.bottom = infoPtr->nItemHeight;
2010 if (uView == LVS_ICON)
2011 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2013 /* now figure out the flags */
2014 if (uView == LVS_ICON)
2015 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2016 else
2017 uFormat = LV_SL_DT_FLAGS;
2019 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2021 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2022 labelSize.cy = rcText.bottom - rcText.top;
2024 SelectObject(hdc, hOldFont);
2025 ReleaseDC(infoPtr->hwndSelf, hdc);
2028 calc_label:
2029 if (uView == LVS_ICON)
2031 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2032 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2033 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2034 Label.right = Label.left + labelSize.cx;
2035 Label.bottom = Label.top + infoPtr->nItemHeight;
2036 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2038 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2039 labelSize.cy /= infoPtr->ntmHeight;
2040 labelSize.cy = max(labelSize.cy, 1);
2041 labelSize.cy *= infoPtr->ntmHeight;
2043 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2045 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2047 Label.left = Icon.right;
2048 Label.top = Box.top;
2049 Label.right = min(Label.left + labelSize.cx, Label.right);
2050 Label.bottom = Label.top + infoPtr->nItemHeight;
2053 if (lprcLabel) *lprcLabel = Label;
2054 TRACE(" - label=%s\n", debugrect(&Label));
2057 /* Fix the Box if necessary */
2058 if (lprcBox)
2060 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2061 else *lprcBox = Box;
2063 TRACE(" - box=%s\n", debugrect(&Box));
2066 /***
2067 * DESCRIPTION: [INTERNAL]
2069 * PARAMETER(S):
2070 * [I] infoPtr : valid pointer to the listview structure
2071 * [I] nItem : item number
2072 * [O] lprcBox : ptr to Box rectangle
2074 * RETURN:
2075 * None.
2077 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2079 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2080 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2081 POINT Position, Origin;
2082 LVITEMW lvItem;
2084 LISTVIEW_GetOrigin(infoPtr, &Origin);
2085 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2087 /* Be smart and try to figure out the minimum we have to do */
2088 lvItem.mask = 0;
2089 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2090 lvItem.mask |= LVIF_TEXT;
2091 lvItem.iItem = nItem;
2092 lvItem.iSubItem = 0;
2093 lvItem.pszText = szDispText;
2094 lvItem.cchTextMax = DISP_TEXT_SIZE;
2095 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2096 if (uView == LVS_ICON)
2098 lvItem.mask |= LVIF_STATE;
2099 lvItem.stateMask = LVIS_FOCUSED;
2100 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2102 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2104 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2108 /***
2109 * DESCRIPTION:
2110 * Returns the current icon position, and advances it along the top.
2111 * The returned position is not offset by Origin.
2113 * PARAMETER(S):
2114 * [I] infoPtr : valid pointer to the listview structure
2115 * [O] lpPos : will get the current icon position
2117 * RETURN:
2118 * None
2120 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2122 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2124 *lpPos = infoPtr->currIconPos;
2126 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2127 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2129 infoPtr->currIconPos.x = 0;
2130 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2134 /***
2135 * DESCRIPTION:
2136 * Returns the current icon position, and advances it down the left edge.
2137 * The returned position is not offset by Origin.
2139 * PARAMETER(S):
2140 * [I] infoPtr : valid pointer to the listview structure
2141 * [O] lpPos : will get the current icon position
2143 * RETURN:
2144 * None
2146 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2148 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2150 *lpPos = infoPtr->currIconPos;
2152 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2153 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2155 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2156 infoPtr->currIconPos.y = 0;
2160 /***
2161 * DESCRIPTION:
2162 * Moves an icon to the specified position.
2163 * It takes care of invalidating the item, etc.
2165 * PARAMETER(S):
2166 * [I] infoPtr : valid pointer to the listview structure
2167 * [I] nItem : the item to move
2168 * [I] lpPos : the new icon position
2169 * [I] isNew : flags the item as being new
2171 * RETURN:
2172 * Success: TRUE
2173 * Failure: FALSE
2175 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2177 POINT old;
2179 if (!isNew)
2181 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2182 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2184 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2185 LISTVIEW_InvalidateItem(infoPtr, nItem);
2188 /* Allocating a POINTER for every item is too resource intensive,
2189 * so we'll keep the (x,y) in different arrays */
2190 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2191 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2193 LISTVIEW_InvalidateItem(infoPtr, nItem);
2195 return TRUE;
2198 /***
2199 * DESCRIPTION:
2200 * Arranges listview items in icon display mode.
2202 * PARAMETER(S):
2203 * [I] infoPtr : valid pointer to the listview structure
2204 * [I] nAlignCode : alignment code
2206 * RETURN:
2207 * SUCCESS : TRUE
2208 * FAILURE : FALSE
2210 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2212 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2213 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2214 POINT pos;
2215 INT i;
2217 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2219 TRACE("nAlignCode=%d\n", nAlignCode);
2221 if (nAlignCode == LVA_DEFAULT)
2223 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2224 else nAlignCode = LVA_ALIGNTOP;
2227 switch (nAlignCode)
2229 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2230 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2231 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2232 default: return FALSE;
2235 infoPtr->bAutoarrange = TRUE;
2236 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2237 for (i = 0; i < infoPtr->nItemCount; i++)
2239 next_pos(infoPtr, &pos);
2240 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2243 return TRUE;
2246 /***
2247 * DESCRIPTION:
2248 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2250 * PARAMETER(S):
2251 * [I] infoPtr : valid pointer to the listview structure
2252 * [O] lprcView : bounding rectangle
2254 * RETURN:
2255 * SUCCESS : TRUE
2256 * FAILURE : FALSE
2258 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2260 INT i, x, y;
2262 SetRectEmpty(lprcView);
2264 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2266 case LVS_ICON:
2267 case LVS_SMALLICON:
2268 for (i = 0; i < infoPtr->nItemCount; i++)
2270 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2271 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2272 lprcView->right = max(lprcView->right, x);
2273 lprcView->bottom = max(lprcView->bottom, y);
2275 if (infoPtr->nItemCount > 0)
2277 lprcView->right += infoPtr->nItemWidth;
2278 lprcView->bottom += infoPtr->nItemHeight;
2280 break;
2282 case LVS_LIST:
2283 y = LISTVIEW_GetCountPerColumn(infoPtr);
2284 x = infoPtr->nItemCount / y;
2285 if (infoPtr->nItemCount % y) x++;
2286 lprcView->right = x * infoPtr->nItemWidth;
2287 lprcView->bottom = y * infoPtr->nItemHeight;
2288 break;
2290 case LVS_REPORT:
2291 lprcView->right = infoPtr->nItemWidth;
2292 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2293 break;
2297 /***
2298 * DESCRIPTION:
2299 * Retrieves the bounding rectangle of all the items.
2301 * PARAMETER(S):
2302 * [I] infoPtr : valid pointer to the listview structure
2303 * [O] lprcView : bounding rectangle
2305 * RETURN:
2306 * SUCCESS : TRUE
2307 * FAILURE : FALSE
2309 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2311 POINT ptOrigin;
2313 TRACE("(lprcView=%p)\n", lprcView);
2315 if (!lprcView) return FALSE;
2317 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2318 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2319 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2321 TRACE("lprcView=%s\n", debugrect(lprcView));
2323 return TRUE;
2326 /***
2327 * DESCRIPTION:
2328 * Retrieves the subitem pointer associated with the subitem index.
2330 * PARAMETER(S):
2331 * [I] hdpaSubItems : DPA handle for a specific item
2332 * [I] nSubItem : index of subitem
2334 * RETURN:
2335 * SUCCESS : subitem pointer
2336 * FAILURE : NULL
2338 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2340 SUBITEM_INFO *lpSubItem;
2341 INT i;
2343 /* we should binary search here if need be */
2344 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2346 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2347 if (lpSubItem->iSubItem == nSubItem)
2348 return lpSubItem;
2351 return NULL;
2355 /***
2356 * DESCRIPTION:
2357 * Caclulates the desired item width.
2359 * PARAMETER(S):
2360 * [I] infoPtr : valid pointer to the listview structure
2362 * RETURN:
2363 * The desired item width.
2365 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2367 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2368 INT nItemWidth = 0;
2370 TRACE("uView=%d\n", uView);
2372 if (uView == LVS_ICON)
2373 nItemWidth = infoPtr->iconSpacing.cx;
2374 else if (uView == LVS_REPORT)
2376 RECT rcHeader;
2378 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2380 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2381 nItemWidth = rcHeader.right;
2384 else /* LVS_SMALLICON, or LVS_LIST */
2386 INT i;
2388 for (i = 0; i < infoPtr->nItemCount; i++)
2389 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2391 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2392 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2394 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2397 return max(nItemWidth, 1);
2400 /***
2401 * DESCRIPTION:
2402 * Caclulates the desired item height.
2404 * PARAMETER(S):
2405 * [I] infoPtr : valid pointer to the listview structure
2407 * RETURN:
2408 * The desired item height.
2410 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2412 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2413 INT nItemHeight;
2415 TRACE("uView=%d\n", uView);
2417 if (uView == LVS_ICON)
2418 nItemHeight = infoPtr->iconSpacing.cy;
2419 else
2421 nItemHeight = infoPtr->ntmHeight;
2422 if (infoPtr->himlState)
2423 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2424 if (infoPtr->himlSmall)
2425 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2426 if (infoPtr->himlState || infoPtr->himlSmall)
2427 nItemHeight += HEIGHT_PADDING;
2430 return max(nItemHeight, 1);
2433 /***
2434 * DESCRIPTION:
2435 * Updates the width, and height of an item.
2437 * PARAMETER(S):
2438 * [I] infoPtr : valid pointer to the listview structure
2440 * RETURN:
2441 * None.
2443 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2445 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2446 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2450 /***
2451 * DESCRIPTION:
2452 * Retrieves and saves important text metrics info for the current
2453 * Listview font.
2455 * PARAMETER(S):
2456 * [I] infoPtr : valid pointer to the listview structure
2459 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2461 HDC hdc = GetDC(infoPtr->hwndSelf);
2462 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2463 HFONT hOldFont = SelectObject(hdc, hFont);
2464 TEXTMETRICW tm;
2466 if (GetTextMetricsW(hdc, &tm))
2468 infoPtr->ntmHeight = tm.tmHeight;
2469 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2471 SelectObject(hdc, hOldFont);
2472 ReleaseDC(infoPtr->hwndSelf, hdc);
2474 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2477 /***
2478 * DESCRIPTION:
2479 * A compare function for ranges
2481 * PARAMETER(S)
2482 * [I] range1 : pointer to range 1;
2483 * [I] range2 : pointer to range 2;
2484 * [I] flags : flags
2486 * RETURNS:
2487 * > 0 : if range 1 > range 2
2488 * < 0 : if range 2 > range 1
2489 * = 0 : if range intersects range 2
2491 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2493 INT cmp;
2495 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2496 cmp = -1;
2497 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2498 cmp = 1;
2499 else
2500 cmp = 0;
2502 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2504 return cmp;
2507 #if DEBUG_RANGES
2508 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2509 #else
2510 #define ranges_check(ranges, desc) do { } while(0)
2511 #endif
2513 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2515 INT i;
2516 RANGE *prev, *curr;
2518 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2519 assert (ranges);
2520 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2521 ranges_dump(ranges);
2522 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2523 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2524 assert (prev->lower >= 0 && prev->lower < prev->upper);
2525 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2527 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2528 assert (prev->upper <= curr->lower);
2529 assert (curr->lower < curr->upper);
2530 prev = curr;
2532 TRACE("--- Done checking---\n");
2535 static RANGES ranges_create(int count)
2537 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2538 if (!ranges) return NULL;
2539 ranges->hdpa = DPA_Create(count);
2540 if (ranges->hdpa) return ranges;
2541 Free(ranges);
2542 return NULL;
2545 static void ranges_clear(RANGES ranges)
2547 INT i;
2549 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2550 Free(DPA_GetPtr(ranges->hdpa, i));
2551 DPA_DeleteAllPtrs(ranges->hdpa);
2555 static void ranges_destroy(RANGES ranges)
2557 if (!ranges) return;
2558 ranges_clear(ranges);
2559 DPA_Destroy(ranges->hdpa);
2560 Free(ranges);
2563 static RANGES ranges_clone(RANGES ranges)
2565 RANGES clone;
2566 INT i;
2568 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2570 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2572 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2573 if (!newrng) goto fail;
2574 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2575 DPA_SetPtr(clone->hdpa, i, newrng);
2577 return clone;
2579 fail:
2580 TRACE ("clone failed\n");
2581 ranges_destroy(clone);
2582 return NULL;
2585 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2587 INT i;
2589 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2590 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2592 return ranges;
2595 static void ranges_dump(RANGES ranges)
2597 INT i;
2599 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2600 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2603 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2605 RANGE srchrng = { nItem, nItem + 1 };
2607 TRACE("(nItem=%d)\n", nItem);
2608 ranges_check(ranges, "before contain");
2609 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2612 static INT ranges_itemcount(RANGES ranges)
2614 INT i, count = 0;
2616 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2618 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2619 count += sel->upper - sel->lower;
2622 return count;
2625 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2627 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2628 INT index;
2630 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2631 if (index == -1) return TRUE;
2633 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2635 chkrng = DPA_GetPtr(ranges->hdpa, index);
2636 if (chkrng->lower >= nItem)
2637 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2638 if (chkrng->upper > nItem)
2639 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2641 return TRUE;
2644 static BOOL ranges_add(RANGES ranges, RANGE range)
2646 RANGE srchrgn;
2647 INT index;
2649 TRACE("(%s)\n", debugrange(&range));
2650 ranges_check(ranges, "before add");
2652 /* try find overlapping regions first */
2653 srchrgn.lower = range.lower - 1;
2654 srchrgn.upper = range.upper + 1;
2655 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2657 if (index == -1)
2659 RANGE *newrgn;
2661 TRACE("Adding new range\n");
2663 /* create the brand new range to insert */
2664 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2665 if(!newrgn) goto fail;
2666 *newrgn = range;
2668 /* figure out where to insert it */
2669 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2670 TRACE("index=%d\n", index);
2671 if (index == -1) index = 0;
2673 /* and get it over with */
2674 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2676 Free(newrgn);
2677 goto fail;
2680 else
2682 RANGE *chkrgn, *mrgrgn;
2683 INT fromindex, mergeindex;
2685 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2686 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2688 chkrgn->lower = min(range.lower, chkrgn->lower);
2689 chkrgn->upper = max(range.upper, chkrgn->upper);
2691 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2693 /* merge now common anges */
2694 fromindex = 0;
2695 srchrgn.lower = chkrgn->lower - 1;
2696 srchrgn.upper = chkrgn->upper + 1;
2700 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2701 if (mergeindex == -1) break;
2702 if (mergeindex == index)
2704 fromindex = index + 1;
2705 continue;
2708 TRACE("Merge with index %i\n", mergeindex);
2710 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2711 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2712 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2713 Free(mrgrgn);
2714 DPA_DeletePtr(ranges->hdpa, mergeindex);
2715 if (mergeindex < index) index --;
2716 } while(1);
2719 ranges_check(ranges, "after add");
2720 return TRUE;
2722 fail:
2723 ranges_check(ranges, "failed add");
2724 return FALSE;
2727 static BOOL ranges_del(RANGES ranges, RANGE range)
2729 RANGE *chkrgn;
2730 INT index;
2732 TRACE("(%s)\n", debugrange(&range));
2733 ranges_check(ranges, "before del");
2735 /* we don't use DPAS_SORTED here, since we need *
2736 * to find the first overlapping range */
2737 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2738 while(index != -1)
2740 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2742 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2744 /* case 1: Same range */
2745 if ( (chkrgn->upper == range.upper) &&
2746 (chkrgn->lower == range.lower) )
2748 DPA_DeletePtr(ranges->hdpa, index);
2749 break;
2751 /* case 2: engulf */
2752 else if ( (chkrgn->upper <= range.upper) &&
2753 (chkrgn->lower >= range.lower) )
2755 DPA_DeletePtr(ranges->hdpa, index);
2757 /* case 3: overlap upper */
2758 else if ( (chkrgn->upper <= range.upper) &&
2759 (chkrgn->lower < range.lower) )
2761 chkrgn->upper = range.lower;
2763 /* case 4: overlap lower */
2764 else if ( (chkrgn->upper > range.upper) &&
2765 (chkrgn->lower >= range.lower) )
2767 chkrgn->lower = range.upper;
2768 break;
2770 /* case 5: fully internal */
2771 else
2773 RANGE tmprgn = *chkrgn, *newrgn;
2775 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2776 newrgn->lower = chkrgn->lower;
2777 newrgn->upper = range.lower;
2778 chkrgn->lower = range.upper;
2779 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2781 Free(newrgn);
2782 goto fail;
2784 chkrgn = &tmprgn;
2785 break;
2788 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2791 ranges_check(ranges, "after del");
2792 return TRUE;
2794 fail:
2795 ranges_check(ranges, "failed del");
2796 return FALSE;
2799 /***
2800 * DESCRIPTION:
2801 * Removes all selection ranges
2803 * Parameters(s):
2804 * [I] infoPtr : valid pointer to the listview structure
2805 * [I] toSkip : item range to skip removing the selection
2807 * RETURNS:
2808 * SUCCESS : TRUE
2809 * FAILURE : TRUE
2811 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2813 LVITEMW lvItem;
2814 ITERATOR i;
2815 RANGES clone;
2817 TRACE("()\n");
2819 lvItem.state = 0;
2820 lvItem.stateMask = LVIS_SELECTED;
2822 /* need to clone the DPA because callbacks can change it */
2823 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2824 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2825 while(iterator_next(&i))
2826 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2827 /* note that the iterator destructor will free the cloned range */
2828 iterator_destroy(&i);
2830 return TRUE;
2833 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2835 RANGES toSkip;
2837 if (!(toSkip = ranges_create(1))) return FALSE;
2838 if (nItem != -1) ranges_additem(toSkip, nItem);
2839 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2840 ranges_destroy(toSkip);
2841 return TRUE;
2844 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2846 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2849 /***
2850 * DESCRIPTION:
2851 * Retrieves the number of items that are marked as selected.
2853 * PARAMETER(S):
2854 * [I] infoPtr : valid pointer to the listview structure
2856 * RETURN:
2857 * Number of items selected.
2859 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2861 INT nSelectedCount = 0;
2863 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2865 INT i;
2866 for (i = 0; i < infoPtr->nItemCount; i++)
2868 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2869 nSelectedCount++;
2872 else
2873 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2875 TRACE("nSelectedCount=%d\n", nSelectedCount);
2876 return nSelectedCount;
2879 /***
2880 * DESCRIPTION:
2881 * Manages the item focus.
2883 * PARAMETER(S):
2884 * [I] infoPtr : valid pointer to the listview structure
2885 * [I] nItem : item index
2887 * RETURN:
2888 * TRUE : focused item changed
2889 * FALSE : focused item has NOT changed
2891 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2893 INT oldFocus = infoPtr->nFocusedItem;
2894 LVITEMW lvItem;
2896 if (nItem == infoPtr->nFocusedItem) return FALSE;
2898 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2899 lvItem.stateMask = LVIS_FOCUSED;
2900 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2902 return oldFocus != infoPtr->nFocusedItem;
2905 /* Helper function for LISTVIEW_ShiftIndices *only* */
2906 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2908 if (nShiftItem < nItem) return nShiftItem;
2910 if (nShiftItem > nItem) return nShiftItem + direction;
2912 if (direction > 0) return nShiftItem + direction;
2914 return min(nShiftItem, infoPtr->nItemCount - 1);
2918 * DESCRIPTION:
2919 * Updates the various indices after an item has been inserted or deleted.
2921 * PARAMETER(S):
2922 * [I] infoPtr : valid pointer to the listview structure
2923 * [I] nItem : item index
2924 * [I] direction : Direction of shift, +1 or -1.
2926 * RETURN:
2927 * None
2929 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2931 INT nNewFocus;
2932 BOOL bOldChange;
2934 /* temporarily disable change notification while shifting items */
2935 bOldChange = infoPtr->bDoChangeNotify;
2936 infoPtr->bDoChangeNotify = FALSE;
2938 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2940 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2942 assert(abs(direction) == 1);
2944 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2946 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2947 if (nNewFocus != infoPtr->nFocusedItem)
2948 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2950 /* But we are not supposed to modify nHotItem! */
2952 infoPtr->bDoChangeNotify = bOldChange;
2957 * DESCRIPTION:
2958 * Adds a block of selections.
2960 * PARAMETER(S):
2961 * [I] infoPtr : valid pointer to the listview structure
2962 * [I] nItem : item index
2964 * RETURN:
2965 * None
2967 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2969 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2970 INT nLast = max(infoPtr->nSelectionMark, nItem);
2971 INT i;
2972 LVITEMW item;
2974 if (nFirst == -1) nFirst = nItem;
2976 item.state = LVIS_SELECTED;
2977 item.stateMask = LVIS_SELECTED;
2979 /* FIXME: this is not correct LVS_OWNERDATA
2980 * setting the item states individually will generate
2981 * a LVN_ITEMCHANGED notification for each one. Instead,
2982 * we have to send a LVN_ODSTATECHANGED notification.
2983 * See MSDN documentation for LVN_ITEMCHANGED.
2985 for (i = nFirst; i <= nLast; i++)
2986 LISTVIEW_SetItemState(infoPtr,i,&item);
2990 /***
2991 * DESCRIPTION:
2992 * Sets a single group selection.
2994 * PARAMETER(S):
2995 * [I] infoPtr : valid pointer to the listview structure
2996 * [I] nItem : item index
2998 * RETURN:
2999 * None
3001 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3003 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3004 RANGES selection;
3005 LVITEMW item;
3006 ITERATOR i;
3008 if (!(selection = ranges_create(100))) return;
3010 item.state = LVIS_SELECTED;
3011 item.stateMask = LVIS_SELECTED;
3013 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3015 if (infoPtr->nSelectionMark == -1)
3017 infoPtr->nSelectionMark = nItem;
3018 ranges_additem(selection, nItem);
3020 else
3022 RANGE sel;
3024 sel.lower = min(infoPtr->nSelectionMark, nItem);
3025 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3026 ranges_add(selection, sel);
3029 else
3031 RECT rcItem, rcSel, rcSelMark;
3032 POINT ptItem;
3034 rcItem.left = LVIR_BOUNDS;
3035 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3036 rcSelMark.left = LVIR_BOUNDS;
3037 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3038 UnionRect(&rcSel, &rcItem, &rcSelMark);
3039 iterator_frameditems(&i, infoPtr, &rcSel);
3040 while(iterator_next(&i))
3042 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3043 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3045 iterator_destroy(&i);
3048 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3049 iterator_rangesitems(&i, selection);
3050 while(iterator_next(&i))
3051 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3052 /* this will also destroy the selection */
3053 iterator_destroy(&i);
3055 LISTVIEW_SetItemFocus(infoPtr, nItem);
3058 /***
3059 * DESCRIPTION:
3060 * Sets a single selection.
3062 * PARAMETER(S):
3063 * [I] infoPtr : valid pointer to the listview structure
3064 * [I] nItem : item index
3066 * RETURN:
3067 * None
3069 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3071 LVITEMW lvItem;
3073 TRACE("nItem=%d\n", nItem);
3075 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3077 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3078 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3079 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3081 infoPtr->nSelectionMark = nItem;
3084 /***
3085 * DESCRIPTION:
3086 * Set selection(s) with keyboard.
3088 * PARAMETER(S):
3089 * [I] infoPtr : valid pointer to the listview structure
3090 * [I] nItem : item index
3092 * RETURN:
3093 * SUCCESS : TRUE (needs to be repainted)
3094 * FAILURE : FALSE (nothing has changed)
3096 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3098 /* FIXME: pass in the state */
3099 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3100 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3101 BOOL bResult = FALSE;
3103 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3105 if (infoPtr->dwStyle & LVS_SINGLESEL)
3107 bResult = TRUE;
3108 LISTVIEW_SetSelection(infoPtr, nItem);
3110 else
3112 if (wShift)
3114 bResult = TRUE;
3115 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3117 else if (wCtrl)
3119 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3121 else
3123 bResult = TRUE;
3124 LISTVIEW_SetSelection(infoPtr, nItem);
3127 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3130 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3131 return bResult;
3135 /***
3136 * DESCRIPTION:
3137 * Called when the mouse is being actively tracked and has hovered for a specified
3138 * amount of time
3140 * PARAMETER(S):
3141 * [I] infoPtr : valid pointer to the listview structure
3142 * [I] fwKeys : key indicator
3143 * [I] x,y : mouse position
3145 * RETURN:
3146 * 0 if the message was processed, non-zero if there was an error
3148 * INFO:
3149 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3150 * over the item for a certain period of time.
3153 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3155 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3156 /* FIXME: select the item!!! */
3157 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3159 return 0;
3162 /***
3163 * DESCRIPTION:
3164 * Called whenever WM_MOUSEMOVE is received.
3166 * PARAMETER(S):
3167 * [I] infoPtr : valid pointer to the listview structure
3168 * [I] fwKeys : key indicator
3169 * [I] x,y : mouse position
3171 * RETURN:
3172 * 0 if the message is processed, non-zero if there was an error
3174 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3176 TRACKMOUSEEVENT trackinfo;
3178 /* see if we are supposed to be tracking mouse hovering */
3179 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3180 /* fill in the trackinfo struct */
3181 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3182 trackinfo.dwFlags = TME_QUERY;
3183 trackinfo.hwndTrack = infoPtr->hwndSelf;
3184 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3186 /* see if we are already tracking this hwnd */
3187 _TrackMouseEvent(&trackinfo);
3189 if(!(trackinfo.dwFlags & TME_HOVER)) {
3190 trackinfo.dwFlags = TME_HOVER;
3192 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3193 _TrackMouseEvent(&trackinfo);
3197 return 0;
3201 /***
3202 * Tests wheather the item is assignable to a list with style lStyle
3204 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3206 if ( (lpLVItem->mask & LVIF_TEXT) &&
3207 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3208 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3210 return TRUE;
3214 /***
3215 * DESCRIPTION:
3216 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3218 * PARAMETER(S):
3219 * [I] infoPtr : valid pointer to the listview structure
3220 * [I] lpLVItem : valid pointer to new item atttributes
3221 * [I] isNew : the item being set is being inserted
3222 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3223 * [O] bChanged : will be set to TRUE if the item really changed
3225 * RETURN:
3226 * SUCCESS : TRUE
3227 * FAILURE : FALSE
3229 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3231 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3232 ITEM_INFO *lpItem;
3233 NMLISTVIEW nmlv;
3234 UINT uChanged = 0;
3235 LVITEMW item;
3237 TRACE("()\n");
3239 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3241 if (lpLVItem->mask == 0) return TRUE;
3243 if (infoPtr->dwStyle & LVS_OWNERDATA)
3245 /* a virtual listview we stores only selection and focus */
3246 if (lpLVItem->mask & ~LVIF_STATE)
3247 return FALSE;
3248 lpItem = NULL;
3250 else
3252 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3253 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3254 assert (lpItem);
3257 /* we need to get the lParam and state of the item */
3258 item.iItem = lpLVItem->iItem;
3259 item.iSubItem = lpLVItem->iSubItem;
3260 item.mask = LVIF_STATE | LVIF_PARAM;
3261 item.stateMask = ~0;
3262 item.state = 0;
3263 item.lParam = 0;
3264 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3266 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3267 /* determine what fields will change */
3268 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3269 uChanged |= LVIF_STATE;
3271 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3272 uChanged |= LVIF_IMAGE;
3274 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3275 uChanged |= LVIF_PARAM;
3277 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3278 uChanged |= LVIF_INDENT;
3280 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3281 uChanged |= LVIF_TEXT;
3283 TRACE("uChanged=0x%x\n", uChanged);
3284 if (!uChanged) return TRUE;
3285 *bChanged = TRUE;
3287 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3288 nmlv.iItem = lpLVItem->iItem;
3289 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3290 nmlv.uOldState = item.state;
3291 nmlv.uChanged = uChanged;
3292 nmlv.lParam = item.lParam;
3294 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3295 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3296 /* are enabled */
3297 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3298 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3299 return FALSE;
3301 /* copy information */
3302 if (lpLVItem->mask & LVIF_TEXT)
3303 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3305 if (lpLVItem->mask & LVIF_IMAGE)
3306 lpItem->hdr.iImage = lpLVItem->iImage;
3308 if (lpLVItem->mask & LVIF_PARAM)
3309 lpItem->lParam = lpLVItem->lParam;
3311 if (lpLVItem->mask & LVIF_INDENT)
3312 lpItem->iIndent = lpLVItem->iIndent;
3314 if (uChanged & LVIF_STATE)
3316 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3318 lpItem->state &= ~lpLVItem->stateMask;
3319 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3321 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3323 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3324 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3326 else if (lpLVItem->stateMask & LVIS_SELECTED)
3327 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3329 /* if we are asked to change focus, and we manage it, do it */
3330 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3332 if (lpLVItem->state & LVIS_FOCUSED)
3334 LISTVIEW_SetItemFocus(infoPtr, -1);
3335 infoPtr->nFocusedItem = lpLVItem->iItem;
3336 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3338 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3339 infoPtr->nFocusedItem = -1;
3343 /* if we're inserting the item, we're done */
3344 if (isNew) return TRUE;
3346 /* send LVN_ITEMCHANGED notification */
3347 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3348 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3350 return TRUE;
3353 /***
3354 * DESCRIPTION:
3355 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3357 * PARAMETER(S):
3358 * [I] infoPtr : valid pointer to the listview structure
3359 * [I] lpLVItem : valid pointer to new subitem atttributes
3360 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3361 * [O] bChanged : will be set to TRUE if the item really changed
3363 * RETURN:
3364 * SUCCESS : TRUE
3365 * FAILURE : FALSE
3367 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3369 HDPA hdpaSubItems;
3370 SUBITEM_INFO *lpSubItem;
3372 /* we do not support subitems for virtual listviews */
3373 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3375 /* set subitem only if column is present */
3376 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3378 /* First do some sanity checks */
3379 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3380 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3382 /* get the subitem structure, and create it if not there */
3383 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3384 assert (hdpaSubItems);
3386 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3387 if (!lpSubItem)
3389 SUBITEM_INFO *tmpSubItem;
3390 INT i;
3392 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3393 if (!lpSubItem) return FALSE;
3394 /* we could binary search here, if need be...*/
3395 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3397 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3398 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3400 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3402 Free(lpSubItem);
3403 return FALSE;
3405 lpSubItem->iSubItem = lpLVItem->iSubItem;
3406 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3407 *bChanged = TRUE;
3410 if (lpLVItem->mask & LVIF_IMAGE)
3411 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3413 lpSubItem->hdr.iImage = lpLVItem->iImage;
3414 *bChanged = TRUE;
3417 if (lpLVItem->mask & LVIF_TEXT)
3418 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3420 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3421 *bChanged = TRUE;
3424 return TRUE;
3427 /***
3428 * DESCRIPTION:
3429 * Sets item attributes.
3431 * PARAMETER(S):
3432 * [I] infoPtr : valid pointer to the listview structure
3433 * [I] lpLVItem : new item atttributes
3434 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3436 * RETURN:
3437 * SUCCESS : TRUE
3438 * FAILURE : FALSE
3440 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3442 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3443 LPWSTR pszText = NULL;
3444 BOOL bResult, bChanged = FALSE;
3446 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3448 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3449 return FALSE;
3451 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3452 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3454 pszText = lpLVItem->pszText;
3455 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3458 /* actually set the fields */
3459 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3461 if (lpLVItem->iSubItem)
3462 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3463 else
3464 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3466 /* redraw item, if necessary */
3467 if (bChanged && !infoPtr->bIsDrawing)
3469 /* this little optimization eliminates some nasty flicker */
3470 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3471 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3472 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3473 else
3474 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3476 /* restore text */
3477 if (pszText)
3479 textfreeT(lpLVItem->pszText, isW);
3480 ((LVITEMW *)lpLVItem)->pszText = pszText;
3483 return bResult;
3486 /***
3487 * DESCRIPTION:
3488 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3490 * PARAMETER(S):
3491 * [I] infoPtr : valid pointer to the listview structure
3493 * RETURN:
3494 * item index
3496 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3498 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3499 INT nItem = 0;
3500 SCROLLINFO scrollInfo;
3502 scrollInfo.cbSize = sizeof(SCROLLINFO);
3503 scrollInfo.fMask = SIF_POS;
3505 if (uView == LVS_LIST)
3507 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3508 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3510 else if (uView == LVS_REPORT)
3512 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3513 nItem = scrollInfo.nPos;
3515 else
3517 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3518 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3521 TRACE("nItem=%d\n", nItem);
3523 return nItem;
3527 /***
3528 * DESCRIPTION:
3529 * Erases the background of the given rectangle
3531 * PARAMETER(S):
3532 * [I] infoPtr : valid pointer to the listview structure
3533 * [I] hdc : device context handle
3534 * [I] lprcBox : clipping rectangle
3536 * RETURN:
3537 * Success: TRUE
3538 * Failure: FALSE
3540 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3542 if (!infoPtr->hBkBrush) return FALSE;
3544 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3546 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3549 /***
3550 * DESCRIPTION:
3551 * Draws an item.
3553 * PARAMETER(S):
3554 * [I] infoPtr : valid pointer to the listview structure
3555 * [I] hdc : device context handle
3556 * [I] nItem : item index
3557 * [I] nSubItem : subitem index
3558 * [I] pos : item position in client coordinates
3559 * [I] cdmode : custom draw mode
3561 * RETURN:
3562 * Success: TRUE
3563 * Failure: FALSE
3565 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3567 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3568 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3569 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3570 DWORD cdsubitemmode = CDRF_DODEFAULT;
3571 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3572 NMLVCUSTOMDRAW nmlvcd;
3573 HIMAGELIST himl;
3574 LVITEMW lvItem;
3576 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3578 /* get information needed for drawing the item */
3579 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3580 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3581 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3582 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3583 lvItem.iItem = nItem;
3584 lvItem.iSubItem = nSubItem;
3585 lvItem.state = 0;
3586 lvItem.lParam = 0;
3587 lvItem.cchTextMax = DISP_TEXT_SIZE;
3588 lvItem.pszText = szDispText;
3589 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3590 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3591 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3592 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3593 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3595 /* now check if we need to update the focus rectangle */
3596 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3598 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3599 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3600 OffsetRect(&rcBox, pos.x, pos.y);
3601 OffsetRect(&rcState, pos.x, pos.y);
3602 OffsetRect(&rcIcon, pos.x, pos.y);
3603 OffsetRect(&rcLabel, pos.x, pos.y);
3604 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3605 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3607 /* fill in the custom draw structure */
3608 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3610 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3611 if (cdmode & CDRF_NOTIFYITEMDRAW)
3612 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3613 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3614 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3615 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3616 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3618 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3619 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3621 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3622 prepaint_setup(infoPtr, hdc, &nmlvcd);
3624 /* in full row select, subitems, will just use main item's colors */
3625 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3626 nmlvcd.clrTextBk = CLR_NONE;
3628 /* state icons */
3629 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3631 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3632 if (uStateImage)
3634 TRACE("uStateImage=%d\n", uStateImage);
3635 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3639 /* small icons */
3640 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3641 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3643 TRACE("iImage=%d\n", lvItem.iImage);
3644 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3645 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3648 /* Don't bother painting item being edited */
3649 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3651 /* draw the selection background, if we're drawing the main item */
3652 if (nSubItem == 0)
3654 rcSelect = rcLabel;
3655 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3656 rcSelect.right = rcBox.right;
3658 if (nmlvcd.clrTextBk != CLR_NONE)
3659 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3660 if(lprcFocus) *lprcFocus = rcSelect;
3663 /* figure out the text drawing flags */
3664 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3665 if (uView == LVS_ICON)
3666 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3667 else if (nSubItem)
3669 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3671 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3672 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3673 default: uFormat |= DT_LEFT;
3676 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3678 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3679 else rcLabel.left += LABEL_HOR_PADDING;
3681 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3682 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3684 postpaint:
3685 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3686 notify_postpaint(infoPtr, &nmlvcd);
3687 return TRUE;
3690 /***
3691 * DESCRIPTION:
3692 * Draws listview items when in owner draw mode.
3694 * PARAMETER(S):
3695 * [I] infoPtr : valid pointer to the listview structure
3696 * [I] hdc : device context handle
3698 * RETURN:
3699 * None
3701 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3703 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3704 DWORD cditemmode = CDRF_DODEFAULT;
3705 NMLVCUSTOMDRAW nmlvcd;
3706 POINT Origin, Position;
3707 DRAWITEMSTRUCT dis;
3708 LVITEMW item;
3710 TRACE("()\n");
3712 ZeroMemory(&dis, sizeof(dis));
3714 /* Get scroll info once before loop */
3715 LISTVIEW_GetOrigin(infoPtr, &Origin);
3717 /* iterate through the invalidated rows */
3718 while(iterator_next(i))
3720 item.iItem = i->nItem;
3721 item.iSubItem = 0;
3722 item.mask = LVIF_PARAM | LVIF_STATE;
3723 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3724 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3726 dis.CtlType = ODT_LISTVIEW;
3727 dis.CtlID = uID;
3728 dis.itemID = item.iItem;
3729 dis.itemAction = ODA_DRAWENTIRE;
3730 dis.itemState = 0;
3731 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3732 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3733 dis.hwndItem = infoPtr->hwndSelf;
3734 dis.hDC = hdc;
3735 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3736 dis.rcItem.left = Position.x + Origin.x;
3737 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3738 dis.rcItem.top = Position.y + Origin.y;
3739 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3740 dis.itemData = item.lParam;
3742 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3745 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3746 * structure for the rest. of the paint cycle
3748 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3749 if (cdmode & CDRF_NOTIFYITEMDRAW)
3750 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3752 if (!(cditemmode & CDRF_SKIPDEFAULT))
3754 prepaint_setup (infoPtr, hdc, &nmlvcd);
3755 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3758 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3759 notify_postpaint(infoPtr, &nmlvcd);
3763 /***
3764 * DESCRIPTION:
3765 * Draws listview items when in report display mode.
3767 * PARAMETER(S):
3768 * [I] infoPtr : valid pointer to the listview structure
3769 * [I] hdc : device context handle
3770 * [I] cdmode : custom draw mode
3772 * RETURN:
3773 * None
3775 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3777 INT rgntype;
3778 RECT rcClip, rcItem;
3779 POINT Origin, Position;
3780 RANGE colRange;
3781 ITERATOR j;
3783 TRACE("()\n");
3785 /* figure out what to draw */
3786 rgntype = GetClipBox(hdc, &rcClip);
3787 if (rgntype == NULLREGION) return;
3789 /* Get scroll info once before loop */
3790 LISTVIEW_GetOrigin(infoPtr, &Origin);
3792 /* narrow down the columns we need to paint */
3793 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3795 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3796 if (rcItem.right + Origin.x >= rcClip.left) break;
3798 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3800 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3801 if (rcItem.left + Origin.x < rcClip.right) break;
3803 iterator_rangeitems(&j, colRange);
3805 /* in full row select, we _have_ to draw the main item */
3806 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3807 j.nSpecial = 0;
3809 /* iterate through the invalidated rows */
3810 while(iterator_next(i))
3812 /* iterate through the invalidated columns */
3813 while(iterator_next(&j))
3815 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3816 Position.x += Origin.x;
3817 Position.y += Origin.y;
3819 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3821 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3822 rcItem.top = 0;
3823 rcItem.bottom = infoPtr->nItemHeight;
3824 OffsetRect(&rcItem, Position.x, Position.y);
3825 if (!RectVisible(hdc, &rcItem)) continue;
3828 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3831 iterator_destroy(&j);
3834 /***
3835 * DESCRIPTION:
3836 * Draws listview items when in list display mode.
3838 * PARAMETER(S):
3839 * [I] infoPtr : valid pointer to the listview structure
3840 * [I] hdc : device context handle
3841 * [I] cdmode : custom draw mode
3843 * RETURN:
3844 * None
3846 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3848 POINT Origin, Position;
3850 /* Get scroll info once before loop */
3851 LISTVIEW_GetOrigin(infoPtr, &Origin);
3853 while(iterator_prev(i))
3855 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3856 Position.x += Origin.x;
3857 Position.y += Origin.y;
3859 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3864 /***
3865 * DESCRIPTION:
3866 * Draws listview items.
3868 * PARAMETER(S):
3869 * [I] infoPtr : valid pointer to the listview structure
3870 * [I] hdc : device context handle
3872 * RETURN:
3873 * NoneX
3875 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3877 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3878 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3879 NMLVCUSTOMDRAW nmlvcd;
3880 HFONT hOldFont;
3881 DWORD cdmode;
3882 INT oldBkMode;
3883 RECT rcClient;
3884 ITERATOR i;
3886 LISTVIEW_DUMP(infoPtr);
3888 infoPtr->bIsDrawing = TRUE;
3890 /* save dc values we're gonna trash while drawing */
3891 hOldFont = SelectObject(hdc, infoPtr->hFont);
3892 oldBkMode = GetBkMode(hdc);
3893 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3894 oldTextColor = GetTextColor(hdc);
3896 oldClrTextBk = infoPtr->clrTextBk;
3897 oldClrText = infoPtr->clrText;
3899 infoPtr->cditemmode = CDRF_DODEFAULT;
3901 GetClientRect(infoPtr->hwndSelf, &rcClient);
3902 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3903 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3904 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3905 prepaint_setup(infoPtr, hdc, &nmlvcd);
3907 /* Use these colors to draw the items */
3908 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3909 infoPtr->clrText = nmlvcd.clrText;
3911 /* nothing to draw */
3912 if(infoPtr->nItemCount == 0) goto enddraw;
3914 /* figure out what we need to draw */
3915 iterator_visibleitems(&i, infoPtr, hdc);
3917 /* send cache hint notification */
3918 if (infoPtr->dwStyle & LVS_OWNERDATA)
3920 RANGE range = iterator_range(&i);
3921 NMLVCACHEHINT nmlv;
3923 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3924 nmlv.iFrom = range.lower;
3925 nmlv.iTo = range.upper - 1;
3926 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3929 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3930 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3931 else
3933 if (uView == LVS_REPORT)
3934 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3935 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3936 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3938 /* if we have a focus rect, draw it */
3939 if (infoPtr->bFocus)
3940 DrawFocusRect(hdc, &infoPtr->rcFocus);
3942 iterator_destroy(&i);
3944 enddraw:
3945 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3946 notify_postpaint(infoPtr, &nmlvcd);
3948 infoPtr->clrTextBk = oldClrTextBk;
3949 infoPtr->clrText = oldClrText;
3951 SelectObject(hdc, hOldFont);
3952 SetBkMode(hdc, oldBkMode);
3953 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3954 SetTextColor(hdc, oldTextColor);
3955 infoPtr->bIsDrawing = FALSE;
3959 /***
3960 * DESCRIPTION:
3961 * Calculates the approximate width and height of a given number of items.
3963 * PARAMETER(S):
3964 * [I] infoPtr : valid pointer to the listview structure
3965 * [I] nItemCount : number of items
3966 * [I] wWidth : width
3967 * [I] wHeight : height
3969 * RETURN:
3970 * Returns a DWORD. The width in the low word and the height in high word.
3972 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3973 WORD wWidth, WORD wHeight)
3975 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3976 INT nItemCountPerColumn = 1;
3977 INT nColumnCount = 0;
3978 DWORD dwViewRect = 0;
3980 if (nItemCount == -1)
3981 nItemCount = infoPtr->nItemCount;
3983 if (uView == LVS_LIST)
3985 if (wHeight == 0xFFFF)
3987 /* use current height */
3988 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3991 if (wHeight < infoPtr->nItemHeight)
3992 wHeight = infoPtr->nItemHeight;
3994 if (nItemCount > 0)
3996 if (infoPtr->nItemHeight > 0)
3998 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3999 if (nItemCountPerColumn == 0)
4000 nItemCountPerColumn = 1;
4002 if (nItemCount % nItemCountPerColumn != 0)
4003 nColumnCount = nItemCount / nItemCountPerColumn;
4004 else
4005 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4009 /* Microsoft padding magic */
4010 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4011 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4013 dwViewRect = MAKELONG(wWidth, wHeight);
4015 else if (uView == LVS_REPORT)
4017 RECT rcBox;
4019 if (infoPtr->nItemCount > 0)
4021 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4022 wWidth = rcBox.right - rcBox.left;
4023 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4025 else
4027 /* use current height and width */
4028 if (wHeight == 0xffff)
4029 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4030 if (wWidth == 0xffff)
4031 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4034 dwViewRect = MAKELONG(wWidth, wHeight);
4036 else if (uView == LVS_SMALLICON)
4037 FIXME("uView == LVS_SMALLICON: not implemented\n");
4038 else if (uView == LVS_ICON)
4039 FIXME("uView == LVS_ICON: not implemented\n");
4041 return dwViewRect;
4045 /***
4046 * DESCRIPTION:
4047 * Create a drag image list for the specified item.
4049 * PARAMETER(S):
4050 * [I] infoPtr : valid pointer to the listview structure
4051 * [I] iItem : index of item
4052 * [O] lppt : Upperr-left corner of the image
4054 * RETURN:
4055 * Returns a handle to the image list if successful, NULL otherwise.
4057 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4059 RECT rcItem;
4060 SIZE size;
4061 POINT pos;
4062 HDC hdc, hdcOrig;
4063 HBITMAP hbmp, hOldbmp;
4064 HIMAGELIST dragList = 0;
4065 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4067 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4068 return 0;
4070 rcItem.left = LVIR_BOUNDS;
4071 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4072 return 0;
4074 lppt->x = rcItem.left;
4075 lppt->y = rcItem.top;
4077 size.cx = rcItem.right - rcItem.left;
4078 size.cy = rcItem.bottom - rcItem.top;
4080 hdcOrig = GetDC(infoPtr->hwndSelf);
4081 hdc = CreateCompatibleDC(hdcOrig);
4082 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4083 hOldbmp = SelectObject(hdc, hbmp);
4085 rcItem.left = rcItem.top = 0;
4086 rcItem.right = size.cx;
4087 rcItem.bottom = size.cy;
4088 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4090 pos.x = pos.y = 0;
4091 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4093 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4094 SelectObject(hdc, hOldbmp);
4095 ImageList_Add(dragList, hbmp, 0);
4097 else
4098 SelectObject(hdc, hOldbmp);
4100 DeleteObject(hbmp);
4101 DeleteDC(hdc);
4102 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4104 TRACE("ret=%p\n", dragList);
4106 return dragList;
4110 /***
4111 * DESCRIPTION:
4112 * Removes all listview items and subitems.
4114 * PARAMETER(S):
4115 * [I] infoPtr : valid pointer to the listview structure
4117 * RETURN:
4118 * SUCCESS : TRUE
4119 * FAILURE : FALSE
4121 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4123 NMLISTVIEW nmlv;
4124 HDPA hdpaSubItems = NULL;
4125 BOOL bSuppress;
4126 ITEMHDR *hdrItem;
4127 INT i, j;
4129 TRACE("()\n");
4131 /* we do it directly, to avoid notifications */
4132 ranges_clear(infoPtr->selectionRanges);
4133 infoPtr->nSelectionMark = -1;
4134 infoPtr->nFocusedItem = -1;
4135 SetRectEmpty(&infoPtr->rcFocus);
4136 /* But we are supposed to leave nHotItem as is! */
4139 /* send LVN_DELETEALLITEMS notification */
4140 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4141 nmlv.iItem = -1;
4142 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4144 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4146 /* send LVN_DELETEITEM notification, if not supressed */
4147 if (!bSuppress) notify_deleteitem(infoPtr, i);
4148 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4150 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4151 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4153 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4154 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4155 Free(hdrItem);
4157 DPA_Destroy(hdpaSubItems);
4158 DPA_DeletePtr(infoPtr->hdpaItems, i);
4160 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4161 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4162 infoPtr->nItemCount --;
4165 LISTVIEW_UpdateScroll(infoPtr);
4167 LISTVIEW_InvalidateList(infoPtr);
4169 return TRUE;
4172 /***
4173 * DESCRIPTION:
4174 * Scrolls, and updates the columns, when a column is changing width.
4176 * PARAMETER(S):
4177 * [I] infoPtr : valid pointer to the listview structure
4178 * [I] nColumn : column to scroll
4179 * [I] dx : amount of scroll, in pixels
4181 * RETURN:
4182 * None.
4184 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4186 COLUMN_INFO *lpColumnInfo;
4187 RECT rcOld, rcCol;
4188 INT nCol;
4190 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4191 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4192 rcCol = lpColumnInfo->rcHeader;
4193 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4194 rcCol.left = rcCol.right;
4196 /* ajust the other columns */
4197 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4199 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4200 lpColumnInfo->rcHeader.left += dx;
4201 lpColumnInfo->rcHeader.right += dx;
4204 /* do not update screen if not in report mode */
4205 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4207 /* if we have a focus, must first erase the focus rect */
4208 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4210 /* Need to reset the item width when inserting a new column */
4211 infoPtr->nItemWidth += dx;
4213 LISTVIEW_UpdateScroll(infoPtr);
4215 /* scroll to cover the deleted column, and invalidate for redraw */
4216 rcOld = infoPtr->rcList;
4217 rcOld.left = rcCol.left;
4218 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4220 /* we can restore focus now */
4221 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4224 /***
4225 * DESCRIPTION:
4226 * Removes a column from the listview control.
4228 * PARAMETER(S):
4229 * [I] infoPtr : valid pointer to the listview structure
4230 * [I] nColumn : column index
4232 * RETURN:
4233 * SUCCESS : TRUE
4234 * FAILURE : FALSE
4236 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4238 RECT rcCol;
4240 TRACE("nColumn=%d\n", nColumn);
4242 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4243 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4245 /* While the MSDN specifically says that column zero should not be deleted,
4246 what actually happens is that the column itself is deleted but no items or subitems
4247 are removed.
4250 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4252 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4253 return FALSE;
4255 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4256 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4258 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4260 SUBITEM_INFO *lpSubItem, *lpDelItem;
4261 HDPA hdpaSubItems;
4262 INT nItem, nSubItem, i;
4264 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4266 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4267 nSubItem = 0;
4268 lpDelItem = 0;
4269 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4271 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4272 if (lpSubItem->iSubItem == nColumn)
4274 nSubItem = i;
4275 lpDelItem = lpSubItem;
4277 else if (lpSubItem->iSubItem > nColumn)
4279 lpSubItem->iSubItem--;
4283 /* if we found our subitem, zapp it */
4284 if (nSubItem > 0)
4286 /* free string */
4287 if (is_textW(lpDelItem->hdr.pszText))
4288 Free(lpDelItem->hdr.pszText);
4290 /* free item */
4291 Free(lpDelItem);
4293 /* free dpa memory */
4294 DPA_DeletePtr(hdpaSubItems, nSubItem);
4299 /* update the other column info */
4300 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4301 LISTVIEW_InvalidateList(infoPtr);
4302 else
4303 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4305 return TRUE;
4308 /***
4309 * DESCRIPTION:
4310 * Invalidates the listview after an item's insertion or deletion.
4312 * PARAMETER(S):
4313 * [I] infoPtr : valid pointer to the listview structure
4314 * [I] nItem : item index
4315 * [I] dir : -1 if deleting, 1 if inserting
4317 * RETURN:
4318 * None
4320 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4322 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4323 INT nPerCol, nItemCol, nItemRow;
4324 RECT rcScroll;
4325 POINT Origin;
4327 /* if we don't refresh, what's the point of scrolling? */
4328 if (!is_redrawing(infoPtr)) return;
4330 assert (abs(dir) == 1);
4332 /* arrange icons if autoarrange is on */
4333 if (is_autoarrange(infoPtr))
4335 BOOL arrange = TRUE;
4336 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4337 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4338 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4341 /* scrollbars need updating */
4342 LISTVIEW_UpdateScroll(infoPtr);
4344 /* figure out the item's position */
4345 if (uView == LVS_REPORT)
4346 nPerCol = infoPtr->nItemCount + 1;
4347 else if (uView == LVS_LIST)
4348 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4349 else /* LVS_ICON, or LVS_SMALLICON */
4350 return;
4352 nItemCol = nItem / nPerCol;
4353 nItemRow = nItem % nPerCol;
4354 LISTVIEW_GetOrigin(infoPtr, &Origin);
4356 /* move the items below up a slot */
4357 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4358 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4359 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4360 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4361 OffsetRect(&rcScroll, Origin.x, Origin.y);
4362 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4363 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4365 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4366 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4367 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4370 /* report has only that column, so we're done */
4371 if (uView == LVS_REPORT) return;
4373 /* now for LISTs, we have to deal with the columns to the right */
4374 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4375 rcScroll.top = 0;
4376 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4377 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4378 OffsetRect(&rcScroll, Origin.x, Origin.y);
4379 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4380 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4381 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4384 /***
4385 * DESCRIPTION:
4386 * Removes an item from the listview control.
4388 * PARAMETER(S):
4389 * [I] infoPtr : valid pointer to the listview structure
4390 * [I] nItem : item index
4392 * RETURN:
4393 * SUCCESS : TRUE
4394 * FAILURE : FALSE
4396 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4398 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4399 LVITEMW item;
4401 TRACE("(nItem=%d)\n", nItem);
4403 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4405 /* remove selection, and focus */
4406 item.state = 0;
4407 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4408 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4410 /* send LVN_DELETEITEM notification. */
4411 notify_deleteitem(infoPtr, nItem);
4413 /* we need to do this here, because we'll be deleting stuff */
4414 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4415 LISTVIEW_InvalidateItem(infoPtr, nItem);
4417 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4419 HDPA hdpaSubItems;
4420 ITEMHDR *hdrItem;
4421 INT i;
4423 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4424 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4426 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4427 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4428 Free(hdrItem);
4430 DPA_Destroy(hdpaSubItems);
4433 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4435 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4436 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4439 infoPtr->nItemCount--;
4440 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4442 /* now is the invalidation fun */
4443 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4444 return TRUE;
4448 /***
4449 * DESCRIPTION:
4450 * Callback implementation for editlabel control
4452 * PARAMETER(S):
4453 * [I] infoPtr : valid pointer to the listview structure
4454 * [I] pszText : modified text
4455 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4457 * RETURN:
4458 * SUCCESS : TRUE
4459 * FAILURE : FALSE
4461 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4463 NMLVDISPINFOW dispInfo;
4465 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4467 ZeroMemory(&dispInfo, sizeof(dispInfo));
4468 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4469 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4470 dispInfo.item.iSubItem = 0;
4471 dispInfo.item.stateMask = ~0;
4472 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4473 /* add the text from the edit in */
4474 dispInfo.item.mask |= LVIF_TEXT;
4475 dispInfo.item.pszText = pszText;
4476 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4478 /* Do we need to update the Item Text */
4479 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4480 if (!pszText) return TRUE;
4482 ZeroMemory(&dispInfo, sizeof(dispInfo));
4483 dispInfo.item.mask = LVIF_TEXT;
4484 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4485 dispInfo.item.iSubItem = 0;
4486 dispInfo.item.pszText = pszText;
4487 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4488 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4491 /***
4492 * DESCRIPTION:
4493 * Begin in place editing of specified list view item
4495 * PARAMETER(S):
4496 * [I] infoPtr : valid pointer to the listview structure
4497 * [I] nItem : item index
4498 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4500 * RETURN:
4501 * SUCCESS : TRUE
4502 * FAILURE : FALSE
4504 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4506 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4507 NMLVDISPINFOW dispInfo;
4508 RECT rect;
4510 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4512 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4513 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4515 infoPtr->nEditLabelItem = nItem;
4517 /* Is the EditBox still there, if so remove it */
4518 if(infoPtr->hwndEdit != 0)
4520 SetFocus(infoPtr->hwndSelf);
4521 infoPtr->hwndEdit = 0;
4524 LISTVIEW_SetSelection(infoPtr, nItem);
4525 LISTVIEW_SetItemFocus(infoPtr, nItem);
4526 LISTVIEW_InvalidateItem(infoPtr, nItem);
4528 rect.left = LVIR_LABEL;
4529 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4531 ZeroMemory(&dispInfo, sizeof(dispInfo));
4532 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4533 dispInfo.item.iItem = nItem;
4534 dispInfo.item.iSubItem = 0;
4535 dispInfo.item.stateMask = ~0;
4536 dispInfo.item.pszText = szDispText;
4537 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4538 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4540 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4541 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4542 if (!infoPtr->hwndEdit) return 0;
4544 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4546 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4547 infoPtr->hwndEdit = 0;
4548 return 0;
4551 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4552 SetFocus(infoPtr->hwndEdit);
4553 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4554 return infoPtr->hwndEdit;
4558 /***
4559 * DESCRIPTION:
4560 * Ensures the specified item is visible, scrolling into view if necessary.
4562 * PARAMETER(S):
4563 * [I] infoPtr : valid pointer to the listview structure
4564 * [I] nItem : item index
4565 * [I] bPartial : partially or entirely visible
4567 * RETURN:
4568 * SUCCESS : TRUE
4569 * FAILURE : FALSE
4571 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4573 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4574 INT nScrollPosHeight = 0;
4575 INT nScrollPosWidth = 0;
4576 INT nHorzAdjust = 0;
4577 INT nVertAdjust = 0;
4578 INT nHorzDiff = 0;
4579 INT nVertDiff = 0;
4580 RECT rcItem, rcTemp;
4582 rcItem.left = LVIR_BOUNDS;
4583 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4585 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4587 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4589 /* scroll left/right, but in LVS_REPORT mode */
4590 if (uView == LVS_LIST)
4591 nScrollPosWidth = infoPtr->nItemWidth;
4592 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4593 nScrollPosWidth = 1;
4595 if (rcItem.left < infoPtr->rcList.left)
4597 nHorzAdjust = -1;
4598 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4600 else
4602 nHorzAdjust = 1;
4603 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4607 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4609 /* scroll up/down, but not in LVS_LIST mode */
4610 if (uView == LVS_REPORT)
4611 nScrollPosHeight = infoPtr->nItemHeight;
4612 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4613 nScrollPosHeight = 1;
4615 if (rcItem.top < infoPtr->rcList.top)
4617 nVertAdjust = -1;
4618 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4620 else
4622 nVertAdjust = 1;
4623 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4627 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4629 if (nScrollPosWidth)
4631 INT diff = nHorzDiff / nScrollPosWidth;
4632 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4633 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4636 if (nScrollPosHeight)
4638 INT diff = nVertDiff / nScrollPosHeight;
4639 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4640 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4643 return TRUE;
4646 /***
4647 * DESCRIPTION:
4648 * Searches for an item with specific characteristics.
4650 * PARAMETER(S):
4651 * [I] hwnd : window handle
4652 * [I] nStart : base item index
4653 * [I] lpFindInfo : item information to look for
4655 * RETURN:
4656 * SUCCESS : index of item
4657 * FAILURE : -1
4659 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4660 const LVFINDINFOW *lpFindInfo)
4662 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4663 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4664 BOOL bWrap = FALSE, bNearest = FALSE;
4665 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4666 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4667 POINT Position, Destination;
4668 LVITEMW lvItem;
4670 if (!lpFindInfo || nItem < 0) return -1;
4672 lvItem.mask = 0;
4673 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4675 lvItem.mask |= LVIF_TEXT;
4676 lvItem.pszText = szDispText;
4677 lvItem.cchTextMax = DISP_TEXT_SIZE;
4680 if (lpFindInfo->flags & LVFI_WRAP)
4681 bWrap = TRUE;
4683 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4684 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4686 POINT Origin;
4687 RECT rcArea;
4689 LISTVIEW_GetOrigin(infoPtr, &Origin);
4690 Destination.x = lpFindInfo->pt.x - Origin.x;
4691 Destination.y = lpFindInfo->pt.y - Origin.y;
4692 switch(lpFindInfo->vkDirection)
4694 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4695 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4696 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4697 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4698 case VK_HOME: Destination.x = Destination.y = 0; break;
4699 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4700 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4701 case VK_END:
4702 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4703 Destination.x = rcArea.right;
4704 Destination.y = rcArea.bottom;
4705 break;
4706 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4708 bNearest = TRUE;
4711 /* if LVFI_PARAM is specified, all other flags are ignored */
4712 if (lpFindInfo->flags & LVFI_PARAM)
4714 lvItem.mask |= LVIF_PARAM;
4715 bNearest = FALSE;
4716 lvItem.mask &= ~LVIF_TEXT;
4719 again:
4720 for (; nItem < nLast; nItem++)
4722 lvItem.iItem = nItem;
4723 lvItem.iSubItem = 0;
4724 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4726 if (lvItem.mask & LVIF_PARAM)
4728 if (lpFindInfo->lParam == lvItem.lParam)
4729 return nItem;
4730 else
4731 continue;
4734 if (lvItem.mask & LVIF_TEXT)
4736 if (lpFindInfo->flags & LVFI_PARTIAL)
4738 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4740 else
4742 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4746 if (!bNearest) return nItem;
4748 /* This is very inefficient. To do a good job here,
4749 * we need a sorted array of (x,y) item positions */
4750 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4752 /* compute the distance^2 to the destination */
4753 xdist = Destination.x - Position.x;
4754 ydist = Destination.y - Position.y;
4755 dist = xdist * xdist + ydist * ydist;
4757 /* remember the distance, and item if it's closer */
4758 if (dist < mindist)
4760 mindist = dist;
4761 nNearestItem = nItem;
4765 if (bWrap)
4767 nItem = 0;
4768 nLast = min(nStart + 1, infoPtr->nItemCount);
4769 bWrap = FALSE;
4770 goto again;
4773 return nNearestItem;
4776 /***
4777 * DESCRIPTION:
4778 * Searches for an item with specific characteristics.
4780 * PARAMETER(S):
4781 * [I] hwnd : window handle
4782 * [I] nStart : base item index
4783 * [I] lpFindInfo : item information to look for
4785 * RETURN:
4786 * SUCCESS : index of item
4787 * FAILURE : -1
4789 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4790 const LVFINDINFOA *lpFindInfo)
4792 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4793 LVFINDINFOW fiw;
4794 INT res;
4796 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4797 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4798 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4799 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4800 return res;
4803 /***
4804 * DESCRIPTION:
4805 * Retrieves the background image of the listview control.
4807 * PARAMETER(S):
4808 * [I] infoPtr : valid pointer to the listview structure
4809 * [O] lpBkImage : background image attributes
4811 * RETURN:
4812 * SUCCESS : TRUE
4813 * FAILURE : FALSE
4815 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4816 /* { */
4817 /* FIXME (listview, "empty stub!\n"); */
4818 /* return FALSE; */
4819 /* } */
4821 /***
4822 * DESCRIPTION:
4823 * Retrieves column attributes.
4825 * PARAMETER(S):
4826 * [I] infoPtr : valid pointer to the listview structure
4827 * [I] nColumn : column index
4828 * [IO] lpColumn : column information
4829 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4830 * otherwise it is in fact a LPLVCOLUMNA
4832 * RETURN:
4833 * SUCCESS : TRUE
4834 * FAILURE : FALSE
4836 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4838 COLUMN_INFO *lpColumnInfo;
4839 HDITEMW hdi;
4841 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4842 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4844 /* initialize memory */
4845 ZeroMemory(&hdi, sizeof(hdi));
4847 if (lpColumn->mask & LVCF_TEXT)
4849 hdi.mask |= HDI_TEXT;
4850 hdi.pszText = lpColumn->pszText;
4851 hdi.cchTextMax = lpColumn->cchTextMax;
4854 if (lpColumn->mask & LVCF_IMAGE)
4855 hdi.mask |= HDI_IMAGE;
4857 if (lpColumn->mask & LVCF_ORDER)
4858 hdi.mask |= HDI_ORDER;
4860 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4862 if (lpColumn->mask & LVCF_FMT)
4863 lpColumn->fmt = lpColumnInfo->fmt;
4865 if (lpColumn->mask & LVCF_WIDTH)
4866 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4868 if (lpColumn->mask & LVCF_IMAGE)
4869 lpColumn->iImage = hdi.iImage;
4871 if (lpColumn->mask & LVCF_ORDER)
4872 lpColumn->iOrder = hdi.iOrder;
4874 return TRUE;
4878 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4880 INT i;
4882 if (!lpiArray)
4883 return FALSE;
4885 /* FIXME: little hack */
4886 for (i = 0; i < iCount; i++)
4887 lpiArray[i] = i;
4889 return TRUE;
4892 /***
4893 * DESCRIPTION:
4894 * Retrieves the column width.
4896 * PARAMETER(S):
4897 * [I] infoPtr : valid pointer to the listview structure
4898 * [I] int : column index
4900 * RETURN:
4901 * SUCCESS : column width
4902 * FAILURE : zero
4904 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4906 INT nColumnWidth = 0;
4907 RECT rcHeader;
4909 TRACE("nColumn=%d\n", nColumn);
4911 /* we have a 'column' in LIST and REPORT mode only */
4912 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4914 case LVS_LIST:
4915 nColumnWidth = infoPtr->nItemWidth;
4916 break;
4917 case LVS_REPORT:
4918 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4919 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4920 nColumnWidth = rcHeader.right - rcHeader.left;
4921 break;
4924 TRACE("nColumnWidth=%d\n", nColumnWidth);
4925 return nColumnWidth;
4928 /***
4929 * DESCRIPTION:
4930 * In list or report display mode, retrieves the number of items that can fit
4931 * vertically in the visible area. In icon or small icon display mode,
4932 * retrieves the total number of visible items.
4934 * PARAMETER(S):
4935 * [I] infoPtr : valid pointer to the listview structure
4937 * RETURN:
4938 * Number of fully visible items.
4940 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4942 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4944 case LVS_ICON:
4945 case LVS_SMALLICON:
4946 return infoPtr->nItemCount;
4947 case LVS_REPORT:
4948 return LISTVIEW_GetCountPerColumn(infoPtr);
4949 case LVS_LIST:
4950 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4952 assert(FALSE);
4953 return 0;
4956 /***
4957 * DESCRIPTION:
4958 * Retrieves an image list handle.
4960 * PARAMETER(S):
4961 * [I] infoPtr : valid pointer to the listview structure
4962 * [I] nImageList : image list identifier
4964 * RETURN:
4965 * SUCCESS : image list handle
4966 * FAILURE : NULL
4968 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4970 switch (nImageList)
4972 case LVSIL_NORMAL: return infoPtr->himlNormal;
4973 case LVSIL_SMALL: return infoPtr->himlSmall;
4974 case LVSIL_STATE: return infoPtr->himlState;
4976 return NULL;
4979 /* LISTVIEW_GetISearchString */
4981 /***
4982 * DESCRIPTION:
4983 * Retrieves item attributes.
4985 * PARAMETER(S):
4986 * [I] hwnd : window handle
4987 * [IO] lpLVItem : item info
4988 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4989 * if FALSE, the lpLVItem is a LPLVITEMA.
4991 * NOTE:
4992 * This is the internal 'GetItem' interface -- it tries to
4993 * be smart, and avoids text copies, if possible, by modifing
4994 * lpLVItem->pszText to point to the text string. Please note
4995 * that this is not always possible (e.g. OWNERDATA), so on
4996 * entry you *must* supply valid values for pszText, and cchTextMax.
4997 * The only difference to the documented interface is that upon
4998 * return, you should use *only* the lpLVItem->pszText, rather than
4999 * the buffer pointer you provided on input. Most code already does
5000 * that, so it's not a problem.
5001 * For the two cases when the text must be copied (that is,
5002 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5004 * RETURN:
5005 * SUCCESS : TRUE
5006 * FAILURE : FALSE
5008 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5010 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5011 NMLVDISPINFOW dispInfo;
5012 ITEM_INFO *lpItem;
5013 ITEMHDR* pItemHdr;
5014 HDPA hdpaSubItems;
5015 INT isubitem;
5017 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5019 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5020 return FALSE;
5022 if (lpLVItem->mask == 0) return TRUE;
5024 /* make a local copy */
5025 isubitem = lpLVItem->iSubItem;
5027 /* a quick optimization if all we're asked is the focus state
5028 * these queries are worth optimising since they are common,
5029 * and can be answered in constant time, without the heavy accesses */
5030 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5031 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5033 lpLVItem->state = 0;
5034 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5035 lpLVItem->state |= LVIS_FOCUSED;
5036 return TRUE;
5039 ZeroMemory(&dispInfo, sizeof(dispInfo));
5041 /* if the app stores all the data, handle it separately */
5042 if (infoPtr->dwStyle & LVS_OWNERDATA)
5044 dispInfo.item.state = 0;
5046 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5047 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5049 /* NOTE: copy only fields which we _know_ are initialized, some apps
5050 * depend on the uninitialized fields being 0 */
5051 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5052 dispInfo.item.iItem = lpLVItem->iItem;
5053 dispInfo.item.iSubItem = isubitem;
5054 if (lpLVItem->mask & LVIF_TEXT)
5056 dispInfo.item.pszText = lpLVItem->pszText;
5057 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5059 if (lpLVItem->mask & LVIF_STATE)
5060 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5061 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5062 dispInfo.item.stateMask = lpLVItem->stateMask;
5063 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5065 /* full size structure expected - _WIN32IE >= 0x560 */
5066 *lpLVItem = dispInfo.item;
5068 else if (lpLVItem->mask & LVIF_INDENT)
5070 /* indent member expected - _WIN32IE >= 0x300 */
5071 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5073 else
5075 /* minimal structure expected */
5076 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5078 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5081 /* make sure lParam is zeroed out */
5082 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5084 /* we store only a little state, so if we're not asked, we're done */
5085 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5087 /* if focus is handled by us, report it */
5088 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5090 lpLVItem->state &= ~LVIS_FOCUSED;
5091 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5092 lpLVItem->state |= LVIS_FOCUSED;
5095 /* and do the same for selection, if we handle it */
5096 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5098 lpLVItem->state &= ~LVIS_SELECTED;
5099 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5100 lpLVItem->state |= LVIS_SELECTED;
5103 return TRUE;
5106 /* find the item and subitem structures before we proceed */
5107 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5108 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5109 assert (lpItem);
5111 if (isubitem)
5113 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5114 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5115 if (!lpSubItem)
5117 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5118 isubitem = 0;
5121 else
5122 pItemHdr = &lpItem->hdr;
5124 /* Do we need to query the state from the app? */
5125 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5127 dispInfo.item.mask |= LVIF_STATE;
5128 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5131 /* Do we need to enquire about the image? */
5132 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5133 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5135 dispInfo.item.mask |= LVIF_IMAGE;
5136 dispInfo.item.iImage = I_IMAGECALLBACK;
5139 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5140 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5142 dispInfo.item.mask |= LVIF_TEXT;
5143 dispInfo.item.pszText = lpLVItem->pszText;
5144 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5145 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5146 *dispInfo.item.pszText = '\0';
5149 /* If we don't have all the requested info, query the application */
5150 if (dispInfo.item.mask != 0)
5152 dispInfo.item.iItem = lpLVItem->iItem;
5153 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5154 dispInfo.item.lParam = lpItem->lParam;
5155 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5156 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5159 /* we should not store values for subitems */
5160 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5162 /* Now, handle the iImage field */
5163 if (dispInfo.item.mask & LVIF_IMAGE)
5165 lpLVItem->iImage = dispInfo.item.iImage;
5166 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5167 pItemHdr->iImage = dispInfo.item.iImage;
5169 else if (lpLVItem->mask & LVIF_IMAGE)
5171 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5172 lpLVItem->iImage = pItemHdr->iImage;
5173 else
5174 lpLVItem->iImage = 0;
5177 /* The pszText field */
5178 if (dispInfo.item.mask & LVIF_TEXT)
5180 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5181 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5183 lpLVItem->pszText = dispInfo.item.pszText;
5185 else if (lpLVItem->mask & LVIF_TEXT)
5187 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5188 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5191 /* if this is a subitem, we're done */
5192 if (isubitem) return TRUE;
5194 /* Next is the lParam field */
5195 if (dispInfo.item.mask & LVIF_PARAM)
5197 lpLVItem->lParam = dispInfo.item.lParam;
5198 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5199 lpItem->lParam = dispInfo.item.lParam;
5201 else if (lpLVItem->mask & LVIF_PARAM)
5202 lpLVItem->lParam = lpItem->lParam;
5204 /* ... the state field (this one is different due to uCallbackmask) */
5205 if (lpLVItem->mask & LVIF_STATE)
5207 lpLVItem->state = lpItem->state;
5208 if (dispInfo.item.mask & LVIF_STATE)
5210 lpLVItem->state &= ~dispInfo.item.stateMask;
5211 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5213 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5215 lpLVItem->state &= ~LVIS_FOCUSED;
5216 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5217 lpLVItem->state |= LVIS_FOCUSED;
5219 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5221 lpLVItem->state &= ~LVIS_SELECTED;
5222 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5223 lpLVItem->state |= LVIS_SELECTED;
5227 /* and last, but not least, the indent field */
5228 if (lpLVItem->mask & LVIF_INDENT)
5229 lpLVItem->iIndent = lpItem->iIndent;
5231 return TRUE;
5234 /***
5235 * DESCRIPTION:
5236 * Retrieves item attributes.
5238 * PARAMETER(S):
5239 * [I] hwnd : window handle
5240 * [IO] lpLVItem : item info
5241 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5242 * if FALSE, the lpLVItem is a LPLVITEMA.
5244 * NOTE:
5245 * This is the external 'GetItem' interface -- it properly copies
5246 * the text in the provided buffer.
5248 * RETURN:
5249 * SUCCESS : TRUE
5250 * FAILURE : FALSE
5252 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5254 LPWSTR pszText;
5255 BOOL bResult;
5257 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5258 return FALSE;
5260 pszText = lpLVItem->pszText;
5261 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5262 if (bResult && lpLVItem->pszText != pszText)
5263 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5264 lpLVItem->pszText = pszText;
5266 return bResult;
5270 /***
5271 * DESCRIPTION:
5272 * Retrieves the position (upper-left) of the listview control item.
5273 * Note that for LVS_ICON style, the upper-left is that of the icon
5274 * and not the bounding box.
5276 * PARAMETER(S):
5277 * [I] infoPtr : valid pointer to the listview structure
5278 * [I] nItem : item index
5279 * [O] lpptPosition : coordinate information
5281 * RETURN:
5282 * SUCCESS : TRUE
5283 * FAILURE : FALSE
5285 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5287 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5288 POINT Origin;
5290 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5292 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5294 LISTVIEW_GetOrigin(infoPtr, &Origin);
5295 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5297 if (uView == LVS_ICON)
5299 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5300 lpptPosition->y += ICON_TOP_PADDING;
5302 lpptPosition->x += Origin.x;
5303 lpptPosition->y += Origin.y;
5305 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5306 return TRUE;
5310 /***
5311 * DESCRIPTION:
5312 * Retrieves the bounding rectangle for a listview control item.
5314 * PARAMETER(S):
5315 * [I] infoPtr : valid pointer to the listview structure
5316 * [I] nItem : item index
5317 * [IO] lprc : bounding rectangle coordinates
5318 * lprc->left specifies the portion of the item for which the bounding
5319 * rectangle will be retrieved.
5321 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5322 * including the icon and label.
5324 * * For LVS_ICON
5325 * * Experiment shows that native control returns:
5326 * * width = min (48, length of text line)
5327 * * .left = position.x - (width - iconsize.cx)/2
5328 * * .right = .left + width
5329 * * height = #lines of text * ntmHeight + icon height + 8
5330 * * .top = position.y - 2
5331 * * .bottom = .top + height
5332 * * separation between items .y = itemSpacing.cy - height
5333 * * .x = itemSpacing.cx - width
5334 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5336 * * For LVS_ICON
5337 * * Experiment shows that native control returns:
5338 * * width = iconSize.cx + 16
5339 * * .left = position.x - (width - iconsize.cx)/2
5340 * * .right = .left + width
5341 * * height = iconSize.cy + 4
5342 * * .top = position.y - 2
5343 * * .bottom = .top + height
5344 * * separation between items .y = itemSpacing.cy - height
5345 * * .x = itemSpacing.cx - width
5346 * LVIR_LABEL Returns the bounding rectangle of the item text.
5348 * * For LVS_ICON
5349 * * Experiment shows that native control returns:
5350 * * width = text length
5351 * * .left = position.x - width/2
5352 * * .right = .left + width
5353 * * height = ntmH * linecount + 2
5354 * * .top = position.y + iconSize.cy + 6
5355 * * .bottom = .top + height
5356 * * separation between items .y = itemSpacing.cy - height
5357 * * .x = itemSpacing.cx - width
5358 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5359 * rectangles, but excludes columns in report view.
5361 * RETURN:
5362 * SUCCESS : TRUE
5363 * FAILURE : FALSE
5365 * NOTES
5366 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5367 * upon whether the window has the focus currently and on whether the item
5368 * is the one with the focus. Ensure that the control's record of which
5369 * item has the focus agrees with the items' records.
5371 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5373 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5374 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5375 BOOL doLabel = TRUE, oversizedBox = FALSE;
5376 POINT Position, Origin;
5377 LVITEMW lvItem;
5378 RECT label_rect;
5380 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5382 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5384 LISTVIEW_GetOrigin(infoPtr, &Origin);
5385 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5387 /* Be smart and try to figure out the minimum we have to do */
5388 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5389 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5390 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5391 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5392 oversizedBox = TRUE;
5394 /* get what we need from the item before hand, so we make
5395 * only one request. This can speed up things, if data
5396 * is stored on the app side */
5397 lvItem.mask = 0;
5398 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5399 if (doLabel) lvItem.mask |= LVIF_TEXT;
5400 lvItem.iItem = nItem;
5401 lvItem.iSubItem = 0;
5402 lvItem.pszText = szDispText;
5403 lvItem.cchTextMax = DISP_TEXT_SIZE;
5404 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5405 /* we got the state already up, simulate it here, to avoid a reget */
5406 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5408 lvItem.mask |= LVIF_STATE;
5409 lvItem.stateMask = LVIS_FOCUSED;
5410 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5413 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5414 lprc->left = LVIR_BOUNDS;
5415 switch(lprc->left)
5417 case LVIR_ICON:
5418 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5419 break;
5421 case LVIR_LABEL:
5422 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5423 break;
5425 case LVIR_BOUNDS:
5426 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5427 break;
5429 case LVIR_SELECTBOUNDS:
5430 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5431 UnionRect(lprc, lprc, &label_rect);
5432 break;
5434 default:
5435 WARN("Unknown value: %ld\n", lprc->left);
5436 return FALSE;
5439 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5441 TRACE(" rect=%s\n", debugrect(lprc));
5443 return TRUE;
5446 /***
5447 * DESCRIPTION:
5448 * Retrieves the spacing between listview control items.
5450 * PARAMETER(S):
5451 * [I] infoPtr : valid pointer to the listview structure
5452 * [IO] lprc : rectangle to receive the output
5453 * on input, lprc->top = nSubItem
5454 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5456 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5457 * not only those of the first column.
5458 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5460 * RETURN:
5461 * TRUE: success
5462 * FALSE: failure
5464 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5466 POINT Position;
5467 LVITEMW lvItem;
5469 if (!lprc) return FALSE;
5471 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5472 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5473 if (lprc->top == 0)
5474 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5476 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5478 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5480 lvItem.mask = 0;
5481 lvItem.iItem = nItem;
5482 lvItem.iSubItem = lprc->top;
5484 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5485 switch(lprc->left)
5487 case LVIR_ICON:
5488 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5489 break;
5491 case LVIR_LABEL:
5492 case LVIR_BOUNDS:
5493 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5494 break;
5496 default:
5497 ERR("Unknown bounds=%ld\n", lprc->left);
5498 return FALSE;
5501 OffsetRect(lprc, Position.x, Position.y);
5502 return TRUE;
5506 /***
5507 * DESCRIPTION:
5508 * Retrieves the width of a label.
5510 * PARAMETER(S):
5511 * [I] infoPtr : valid pointer to the listview structure
5513 * RETURN:
5514 * SUCCESS : string width (in pixels)
5515 * FAILURE : zero
5517 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5519 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5520 LVITEMW lvItem;
5522 TRACE("(nItem=%d)\n", nItem);
5524 lvItem.mask = LVIF_TEXT;
5525 lvItem.iItem = nItem;
5526 lvItem.iSubItem = 0;
5527 lvItem.pszText = szDispText;
5528 lvItem.cchTextMax = DISP_TEXT_SIZE;
5529 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5531 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5534 /***
5535 * DESCRIPTION:
5536 * Retrieves the spacing between listview control items.
5538 * PARAMETER(S):
5539 * [I] infoPtr : valid pointer to the listview structure
5540 * [I] bSmall : flag for small or large icon
5542 * RETURN:
5543 * Horizontal + vertical spacing
5545 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5547 LONG lResult;
5549 if (!bSmall)
5551 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5553 else
5555 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5556 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5557 else
5558 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5560 return lResult;
5563 /***
5564 * DESCRIPTION:
5565 * Retrieves the state of a listview control item.
5567 * PARAMETER(S):
5568 * [I] infoPtr : valid pointer to the listview structure
5569 * [I] nItem : item index
5570 * [I] uMask : state mask
5572 * RETURN:
5573 * State specified by the mask.
5575 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5577 LVITEMW lvItem;
5579 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5581 lvItem.iItem = nItem;
5582 lvItem.iSubItem = 0;
5583 lvItem.mask = LVIF_STATE;
5584 lvItem.stateMask = uMask;
5585 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5587 return lvItem.state & uMask;
5590 /***
5591 * DESCRIPTION:
5592 * Retrieves the text of a listview control item or subitem.
5594 * PARAMETER(S):
5595 * [I] hwnd : window handle
5596 * [I] nItem : item index
5597 * [IO] lpLVItem : item information
5598 * [I] isW : TRUE if lpLVItem is Unicode
5600 * RETURN:
5601 * SUCCESS : string length
5602 * FAILURE : 0
5604 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5606 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5608 lpLVItem->mask = LVIF_TEXT;
5609 lpLVItem->iItem = nItem;
5610 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5612 return textlenT(lpLVItem->pszText, isW);
5615 /***
5616 * DESCRIPTION:
5617 * Searches for an item based on properties + relationships.
5619 * PARAMETER(S):
5620 * [I] infoPtr : valid pointer to the listview structure
5621 * [I] nItem : item index
5622 * [I] uFlags : relationship flag
5624 * RETURN:
5625 * SUCCESS : item index
5626 * FAILURE : -1
5628 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5630 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5631 UINT uMask = 0;
5632 LVFINDINFOW lvFindInfo;
5633 INT nCountPerColumn;
5634 INT nCountPerRow;
5635 INT i;
5637 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5638 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5640 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5642 if (uFlags & LVNI_CUT)
5643 uMask |= LVIS_CUT;
5645 if (uFlags & LVNI_DROPHILITED)
5646 uMask |= LVIS_DROPHILITED;
5648 if (uFlags & LVNI_FOCUSED)
5649 uMask |= LVIS_FOCUSED;
5651 if (uFlags & LVNI_SELECTED)
5652 uMask |= LVIS_SELECTED;
5654 /* if we're asked for the focused item, that's only one,
5655 * so it's worth optimizing */
5656 if (uFlags & LVNI_FOCUSED)
5658 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5659 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5662 if (uFlags & LVNI_ABOVE)
5664 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5666 while (nItem >= 0)
5668 nItem--;
5669 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5670 return nItem;
5673 else
5675 /* Special case for autoarrange - move 'til the top of a list */
5676 if (is_autoarrange(infoPtr))
5678 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5679 while (nItem - nCountPerRow >= 0)
5681 nItem -= nCountPerRow;
5682 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5683 return nItem;
5685 return -1;
5687 lvFindInfo.flags = LVFI_NEARESTXY;
5688 lvFindInfo.vkDirection = VK_UP;
5689 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5690 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5692 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5693 return nItem;
5697 else if (uFlags & LVNI_BELOW)
5699 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5701 while (nItem < infoPtr->nItemCount)
5703 nItem++;
5704 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5705 return nItem;
5708 else
5710 /* Special case for autoarrange - move 'til the bottom of a list */
5711 if (is_autoarrange(infoPtr))
5713 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5714 while (nItem + nCountPerRow < infoPtr->nItemCount )
5716 nItem += nCountPerRow;
5717 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5718 return nItem;
5720 return -1;
5722 lvFindInfo.flags = LVFI_NEARESTXY;
5723 lvFindInfo.vkDirection = VK_DOWN;
5724 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5725 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5727 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5728 return nItem;
5732 else if (uFlags & LVNI_TOLEFT)
5734 if (uView == LVS_LIST)
5736 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5737 while (nItem - nCountPerColumn >= 0)
5739 nItem -= nCountPerColumn;
5740 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5741 return nItem;
5744 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5746 /* Special case for autoarrange - move 'ti the beginning of a row */
5747 if (is_autoarrange(infoPtr))
5749 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5750 while (nItem % nCountPerRow > 0)
5752 nItem --;
5753 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5754 return nItem;
5756 return -1;
5758 lvFindInfo.flags = LVFI_NEARESTXY;
5759 lvFindInfo.vkDirection = VK_LEFT;
5760 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5761 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5763 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5764 return nItem;
5768 else if (uFlags & LVNI_TORIGHT)
5770 if (uView == LVS_LIST)
5772 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5773 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5775 nItem += nCountPerColumn;
5776 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5777 return nItem;
5780 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5782 /* Special case for autoarrange - move 'til the end of a row */
5783 if (is_autoarrange(infoPtr))
5785 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5786 while (nItem % nCountPerRow < nCountPerRow - 1 )
5788 nItem ++;
5789 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5790 return nItem;
5792 return -1;
5794 lvFindInfo.flags = LVFI_NEARESTXY;
5795 lvFindInfo.vkDirection = VK_RIGHT;
5796 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5797 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5799 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5800 return nItem;
5804 else
5806 nItem++;
5808 /* search by index */
5809 for (i = nItem; i < infoPtr->nItemCount; i++)
5811 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5812 return i;
5816 return -1;
5819 /* LISTVIEW_GetNumberOfWorkAreas */
5821 /***
5822 * DESCRIPTION:
5823 * Retrieves the origin coordinates when in icon or small icon display mode.
5825 * PARAMETER(S):
5826 * [I] infoPtr : valid pointer to the listview structure
5827 * [O] lpptOrigin : coordinate information
5829 * RETURN:
5830 * None.
5832 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5834 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5835 INT nHorzPos = 0, nVertPos = 0;
5836 SCROLLINFO scrollInfo;
5838 scrollInfo.cbSize = sizeof(SCROLLINFO);
5839 scrollInfo.fMask = SIF_POS;
5841 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5842 nHorzPos = scrollInfo.nPos;
5843 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5844 nVertPos = scrollInfo.nPos;
5846 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5848 lpptOrigin->x = infoPtr->rcList.left;
5849 lpptOrigin->y = infoPtr->rcList.top;
5850 if (uView == LVS_LIST)
5851 nHorzPos *= infoPtr->nItemWidth;
5852 else if (uView == LVS_REPORT)
5853 nVertPos *= infoPtr->nItemHeight;
5855 lpptOrigin->x -= nHorzPos;
5856 lpptOrigin->y -= nVertPos;
5858 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5861 /***
5862 * DESCRIPTION:
5863 * Retrieves the width of a string.
5865 * PARAMETER(S):
5866 * [I] hwnd : window handle
5867 * [I] lpszText : text string to process
5868 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5870 * RETURN:
5871 * SUCCESS : string width (in pixels)
5872 * FAILURE : zero
5874 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5876 SIZE stringSize;
5878 stringSize.cx = 0;
5879 if (is_textT(lpszText, isW))
5881 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5882 HDC hdc = GetDC(infoPtr->hwndSelf);
5883 HFONT hOldFont = SelectObject(hdc, hFont);
5885 if (isW)
5886 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5887 else
5888 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5889 SelectObject(hdc, hOldFont);
5890 ReleaseDC(infoPtr->hwndSelf, hdc);
5892 return stringSize.cx;
5895 /***
5896 * DESCRIPTION:
5897 * Determines which listview item is located at the specified position.
5899 * PARAMETER(S):
5900 * [I] infoPtr : valid pointer to the listview structure
5901 * [IO] lpht : hit test information
5902 * [I] subitem : fill out iSubItem.
5903 * [I] select : return the index only if the hit selects the item
5905 * NOTE:
5906 * (mm 20001022): We must not allow iSubItem to be touched, for
5907 * an app might pass only a structure with space up to iItem!
5908 * (MS Office 97 does that for instance in the file open dialog)
5910 * RETURN:
5911 * SUCCESS : item index
5912 * FAILURE : -1
5914 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5916 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5917 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5918 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5919 POINT Origin, Position, opt;
5920 LVITEMW lvItem;
5921 ITERATOR i;
5922 INT iItem;
5924 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5926 lpht->flags = 0;
5927 lpht->iItem = -1;
5928 if (subitem) lpht->iSubItem = 0;
5930 if (infoPtr->rcList.left > lpht->pt.x)
5931 lpht->flags |= LVHT_TOLEFT;
5932 else if (infoPtr->rcList.right < lpht->pt.x)
5933 lpht->flags |= LVHT_TORIGHT;
5935 if (infoPtr->rcList.top > lpht->pt.y)
5936 lpht->flags |= LVHT_ABOVE;
5937 else if (infoPtr->rcList.bottom < lpht->pt.y)
5938 lpht->flags |= LVHT_BELOW;
5940 TRACE("lpht->flags=0x%x\n", lpht->flags);
5941 if (lpht->flags) return -1;
5943 lpht->flags |= LVHT_NOWHERE;
5945 LISTVIEW_GetOrigin(infoPtr, &Origin);
5947 /* first deal with the large items */
5948 rcSearch.left = lpht->pt.x;
5949 rcSearch.top = lpht->pt.y;
5950 rcSearch.right = rcSearch.left + 1;
5951 rcSearch.bottom = rcSearch.top + 1;
5953 iterator_frameditems(&i, infoPtr, &rcSearch);
5954 iterator_next(&i); /* go to first item in the sequence */
5955 iItem = i.nItem;
5956 iterator_destroy(&i);
5958 TRACE("lpht->iItem=%d\n", iItem);
5959 if (iItem == -1) return -1;
5961 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5962 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5963 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5964 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5965 lvItem.iItem = iItem;
5966 lvItem.iSubItem = 0;
5967 lvItem.pszText = szDispText;
5968 lvItem.cchTextMax = DISP_TEXT_SIZE;
5969 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5970 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5972 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5973 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5974 opt.x = lpht->pt.x - Position.x - Origin.x;
5975 opt.y = lpht->pt.y - Position.y - Origin.y;
5977 if (uView == LVS_REPORT)
5978 rcBounds = rcBox;
5979 else
5980 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5981 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5982 if (!PtInRect(&rcBounds, opt)) return -1;
5984 if (PtInRect(&rcIcon, opt))
5985 lpht->flags |= LVHT_ONITEMICON;
5986 else if (PtInRect(&rcLabel, opt))
5987 lpht->flags |= LVHT_ONITEMLABEL;
5988 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5989 lpht->flags |= LVHT_ONITEMSTATEICON;
5990 if (lpht->flags & LVHT_ONITEM)
5991 lpht->flags &= ~LVHT_NOWHERE;
5993 TRACE("lpht->flags=0x%x\n", lpht->flags);
5994 if (uView == LVS_REPORT && subitem)
5996 INT j;
5998 rcBounds.right = rcBounds.left;
5999 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6001 rcBounds.left = rcBounds.right;
6002 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6003 if (PtInRect(&rcBounds, opt))
6005 lpht->iSubItem = j;
6006 break;
6011 if (select && !(uView == LVS_REPORT &&
6012 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6013 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6015 if (uView == LVS_REPORT)
6017 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6018 UnionRect(&rcBounds, &rcBounds, &rcState);
6020 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6022 return lpht->iItem = iItem;
6026 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6027 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6028 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6029 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6030 their own sort proc. when sending LVM_SORTITEMS.
6032 /* Platform SDK:
6033 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6035 LVS_SORTXXX must be specified,
6036 LVS_OWNERDRAW is not set,
6037 <item>.pszText is not LPSTR_TEXTCALLBACK.
6039 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6040 are sorted based on item text..."
6042 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6044 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6045 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6046 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6048 /* if we're sorting descending, negate the return value */
6049 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6052 /***
6053 * DESCRIPTION:
6054 * Inserts a new item in the listview control.
6056 * PARAMETER(S):
6057 * [I] infoPtr : valid pointer to the listview structure
6058 * [I] lpLVItem : item information
6059 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6061 * RETURN:
6062 * SUCCESS : new item index
6063 * FAILURE : -1
6065 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6067 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6068 INT nItem;
6069 HDPA hdpaSubItems;
6070 NMLISTVIEW nmlv;
6071 ITEM_INFO *lpItem;
6072 BOOL is_sorted, has_changed;
6073 LVITEMW item;
6075 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6077 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6079 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6080 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6082 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6084 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6086 /* insert item in listview control data structure */
6087 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6088 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6090 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6091 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6093 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6094 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6095 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6096 if (nItem == -1) goto fail;
6097 infoPtr->nItemCount++;
6099 /* shift indices first so they don't get tangled */
6100 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6102 /* set the item attributes */
6103 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6105 /* full size structure expected - _WIN32IE >= 0x560 */
6106 item = *lpLVItem;
6108 else if (lpLVItem->mask & LVIF_INDENT)
6110 /* indent member expected - _WIN32IE >= 0x300 */
6111 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6113 else
6115 /* minimal structure expected */
6116 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6118 item.iItem = nItem;
6119 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6120 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6122 /* if we're sorted, sort the list, and update the index */
6123 if (is_sorted)
6125 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6126 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6127 assert(nItem != -1);
6130 /* make room for the position, if we are in the right mode */
6131 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6133 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6134 goto undo;
6135 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6137 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6138 goto undo;
6142 /* send LVN_INSERTITEM notification */
6143 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6144 nmlv.iItem = nItem;
6145 nmlv.lParam = lpItem->lParam;
6146 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6148 /* align items (set position of each item) */
6149 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6151 POINT pt;
6153 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6154 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6155 else
6156 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6158 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6161 /* now is the invalidation fun */
6162 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6163 return nItem;
6165 undo:
6166 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6167 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6168 infoPtr->nItemCount--;
6169 fail:
6170 DPA_DeletePtr(hdpaSubItems, 0);
6171 DPA_Destroy (hdpaSubItems);
6172 Free (lpItem);
6173 return -1;
6176 /***
6177 * DESCRIPTION:
6178 * Redraws a range of items.
6180 * PARAMETER(S):
6181 * [I] infoPtr : valid pointer to the listview structure
6182 * [I] nFirst : first item
6183 * [I] nLast : last item
6185 * RETURN:
6186 * SUCCESS : TRUE
6187 * FAILURE : FALSE
6189 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6191 INT i;
6193 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6194 max(nFirst, nLast) >= infoPtr->nItemCount)
6195 return FALSE;
6197 for (i = nFirst; i <= nLast; i++)
6198 LISTVIEW_InvalidateItem(infoPtr, i);
6200 return TRUE;
6203 /***
6204 * DESCRIPTION:
6205 * Scroll the content of a listview.
6207 * PARAMETER(S):
6208 * [I] infoPtr : valid pointer to the listview structure
6209 * [I] dx : horizontal scroll amount in pixels
6210 * [I] dy : vertical scroll amount in pixels
6212 * RETURN:
6213 * SUCCESS : TRUE
6214 * FAILURE : FALSE
6216 * COMMENTS:
6217 * If the control is in report mode (LVS_REPORT) the control can
6218 * be scrolled only in line increments. "dy" will be rounded to the
6219 * nearest number of pixels that are a whole line. Ex: if line height
6220 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6221 * is passed the the scroll will be 0. (per MSDN 7/2002)
6223 * For: (per experimentaion with native control and CSpy ListView)
6224 * LVS_ICON dy=1 = 1 pixel (vertical only)
6225 * dx ignored
6226 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6227 * dx ignored
6228 * LVS_LIST dx=1 = 1 column (horizontal only)
6229 * but will only scroll 1 column per message
6230 * no matter what the value.
6231 * dy must be 0 or FALSE returned.
6232 * LVS_REPORT dx=1 = 1 pixel
6233 * dy= see above
6236 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6238 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6239 case LVS_REPORT:
6240 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6241 dy /= infoPtr->nItemHeight;
6242 break;
6243 case LVS_LIST:
6244 if (dy != 0) return FALSE;
6245 break;
6246 default: /* icon */
6247 dx = 0;
6248 break;
6251 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6252 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6254 return TRUE;
6257 /***
6258 * DESCRIPTION:
6259 * Sets the background color.
6261 * PARAMETER(S):
6262 * [I] infoPtr : valid pointer to the listview structure
6263 * [I] clrBk : background color
6265 * RETURN:
6266 * SUCCESS : TRUE
6267 * FAILURE : FALSE
6269 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6271 TRACE("(clrBk=%lx)\n", clrBk);
6273 if(infoPtr->clrBk != clrBk) {
6274 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6275 infoPtr->clrBk = clrBk;
6276 if (clrBk == CLR_NONE)
6277 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6278 else
6279 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6280 LISTVIEW_InvalidateList(infoPtr);
6283 return TRUE;
6286 /* LISTVIEW_SetBkImage */
6288 /*** Helper for {Insert,Set}ColumnT *only* */
6289 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6291 if (lpColumn->mask & LVCF_FMT)
6293 /* format member is valid */
6294 lphdi->mask |= HDI_FORMAT;
6296 /* set text alignment (leftmost column must be left-aligned) */
6297 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6298 lphdi->fmt |= HDF_LEFT;
6299 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6300 lphdi->fmt |= HDF_RIGHT;
6301 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6302 lphdi->fmt |= HDF_CENTER;
6304 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6305 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6307 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6309 lphdi->fmt |= HDF_IMAGE;
6310 lphdi->iImage = I_IMAGECALLBACK;
6314 if (lpColumn->mask & LVCF_WIDTH)
6316 lphdi->mask |= HDI_WIDTH;
6317 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6319 /* make it fill the remainder of the controls width */
6320 RECT rcHeader;
6321 INT item_index;
6323 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6325 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6326 lphdi->cxy += rcHeader.right - rcHeader.left;
6329 /* retrieve the layout of the header */
6330 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6331 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6333 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6335 else
6336 lphdi->cxy = lpColumn->cx;
6339 if (lpColumn->mask & LVCF_TEXT)
6341 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6342 lphdi->fmt |= HDF_STRING;
6343 lphdi->pszText = lpColumn->pszText;
6344 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6347 if (lpColumn->mask & LVCF_IMAGE)
6349 lphdi->mask |= HDI_IMAGE;
6350 lphdi->iImage = lpColumn->iImage;
6353 if (lpColumn->mask & LVCF_ORDER)
6355 lphdi->mask |= HDI_ORDER;
6356 lphdi->iOrder = lpColumn->iOrder;
6361 /***
6362 * DESCRIPTION:
6363 * Inserts a new column.
6365 * PARAMETER(S):
6366 * [I] infoPtr : valid pointer to the listview structure
6367 * [I] nColumn : column index
6368 * [I] lpColumn : column information
6369 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6371 * RETURN:
6372 * SUCCESS : new column index
6373 * FAILURE : -1
6375 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6376 const LVCOLUMNW *lpColumn, BOOL isW)
6378 COLUMN_INFO *lpColumnInfo;
6379 INT nNewColumn;
6380 HDITEMW hdi;
6382 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6384 if (!lpColumn || nColumn < 0) return -1;
6385 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6387 ZeroMemory(&hdi, sizeof(HDITEMW));
6388 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6390 /* insert item in header control */
6391 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6392 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6393 (WPARAM)nColumn, (LPARAM)&hdi);
6394 if (nNewColumn == -1) return -1;
6395 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6397 /* create our own column info */
6398 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6399 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6401 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6402 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6404 /* now we have to actually adjust the data */
6405 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6407 SUBITEM_INFO *lpSubItem;
6408 HDPA hdpaSubItems;
6409 INT nItem, i;
6411 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6413 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6414 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6416 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6417 if (lpSubItem->iSubItem >= nNewColumn)
6418 lpSubItem->iSubItem++;
6423 /* make space for the new column */
6424 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6426 return nNewColumn;
6428 fail:
6429 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6430 if (lpColumnInfo)
6432 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6433 Free(lpColumnInfo);
6435 return -1;
6438 /***
6439 * DESCRIPTION:
6440 * Sets the attributes of a header item.
6442 * PARAMETER(S):
6443 * [I] infoPtr : valid pointer to the listview structure
6444 * [I] nColumn : column index
6445 * [I] lpColumn : column attributes
6446 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6448 * RETURN:
6449 * SUCCESS : TRUE
6450 * FAILURE : FALSE
6452 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6453 const LVCOLUMNW *lpColumn, BOOL isW)
6455 HDITEMW hdi, hdiget;
6456 BOOL bResult;
6458 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6460 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6462 ZeroMemory(&hdi, sizeof(HDITEMW));
6463 if (lpColumn->mask & LVCF_FMT)
6465 hdi.mask |= HDI_FORMAT;
6466 hdiget.mask = HDI_FORMAT;
6467 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6468 hdi.fmt = hdiget.fmt & HDF_STRING;
6470 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6472 /* set header item attributes */
6473 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6474 if (!bResult) return FALSE;
6476 if (lpColumn->mask & LVCF_FMT)
6478 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6479 int oldFmt = lpColumnInfo->fmt;
6481 lpColumnInfo->fmt = lpColumn->fmt;
6482 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6484 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6485 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6489 return TRUE;
6492 /***
6493 * DESCRIPTION:
6494 * Sets the column order array
6496 * PARAMETERS:
6497 * [I] infoPtr : valid pointer to the listview structure
6498 * [I] iCount : number of elements in column order array
6499 * [I] lpiArray : pointer to column order array
6501 * RETURN:
6502 * SUCCESS : TRUE
6503 * FAILURE : FALSE
6505 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6507 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6509 if (!lpiArray)
6510 return FALSE;
6512 return TRUE;
6516 /***
6517 * DESCRIPTION:
6518 * Sets the width of a column
6520 * PARAMETERS:
6521 * [I] infoPtr : valid pointer to the listview structure
6522 * [I] nColumn : column index
6523 * [I] cx : column width
6525 * RETURN:
6526 * SUCCESS : TRUE
6527 * FAILURE : FALSE
6529 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6531 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6532 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6533 INT max_cx = 0;
6534 HDITEMW hdi;
6536 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6538 /* set column width only if in report or list mode */
6539 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6541 /* take care of invalid cx values */
6542 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6543 else if (uView == LVS_LIST && cx < 1) return FALSE;
6545 /* resize all columns if in LVS_LIST mode */
6546 if(uView == LVS_LIST)
6548 infoPtr->nItemWidth = cx;
6549 LISTVIEW_InvalidateList(infoPtr);
6550 return TRUE;
6553 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6555 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6557 INT nLabelWidth;
6558 LVITEMW lvItem;
6560 lvItem.mask = LVIF_TEXT;
6561 lvItem.iItem = 0;
6562 lvItem.iSubItem = nColumn;
6563 lvItem.pszText = szDispText;
6564 lvItem.cchTextMax = DISP_TEXT_SIZE;
6565 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6567 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6568 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6569 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6571 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6572 max_cx += infoPtr->iconSize.cx;
6573 max_cx += TRAILING_LABEL_PADDING;
6576 /* autosize based on listview items width */
6577 if(cx == LVSCW_AUTOSIZE)
6578 cx = max_cx;
6579 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6581 /* if iCol is the last column make it fill the remainder of the controls width */
6582 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6584 RECT rcHeader;
6585 POINT Origin;
6587 LISTVIEW_GetOrigin(infoPtr, &Origin);
6588 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6590 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6592 else
6594 /* Despite what the MS docs say, if this is not the last
6595 column, then MS resizes the column to the width of the
6596 largest text string in the column, including headers
6597 and items. This is different from LVSCW_AUTOSIZE in that
6598 LVSCW_AUTOSIZE ignores the header string length. */
6599 cx = 0;
6601 /* retrieve header text */
6602 hdi.mask = HDI_TEXT;
6603 hdi.cchTextMax = DISP_TEXT_SIZE;
6604 hdi.pszText = szDispText;
6605 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6607 HDC hdc = GetDC(infoPtr->hwndSelf);
6608 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6609 SIZE size;
6611 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6612 cx = size.cx + TRAILING_HEADER_PADDING;
6613 /* FIXME: Take into account the header image, if one is present */
6614 SelectObject(hdc, old_font);
6615 ReleaseDC(infoPtr->hwndSelf, hdc);
6617 cx = max (cx, max_cx);
6621 if (cx < 0) return FALSE;
6623 /* call header to update the column change */
6624 hdi.mask = HDI_WIDTH;
6625 hdi.cxy = cx;
6626 TRACE("hdi.cxy=%d\n", hdi.cxy);
6627 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6630 /***
6631 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6634 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6636 HDC hdc_wnd, hdc;
6637 HBITMAP hbm_im, hbm_mask, hbm_orig;
6638 RECT rc;
6639 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6640 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6641 HIMAGELIST himl;
6643 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6644 ILC_COLOR | ILC_MASK, 2, 2);
6645 hdc_wnd = GetDC(infoPtr->hwndSelf);
6646 hdc = CreateCompatibleDC(hdc_wnd);
6647 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6648 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6649 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6651 rc.left = rc.top = 0;
6652 rc.right = GetSystemMetrics(SM_CXSMICON);
6653 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6655 hbm_orig = SelectObject(hdc, hbm_mask);
6656 FillRect(hdc, &rc, hbr_white);
6657 InflateRect(&rc, -3, -3);
6658 FillRect(hdc, &rc, hbr_black);
6660 SelectObject(hdc, hbm_im);
6661 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6662 SelectObject(hdc, hbm_orig);
6663 ImageList_Add(himl, hbm_im, hbm_mask);
6665 SelectObject(hdc, hbm_im);
6666 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6667 SelectObject(hdc, hbm_orig);
6668 ImageList_Add(himl, hbm_im, hbm_mask);
6670 DeleteObject(hbm_mask);
6671 DeleteObject(hbm_im);
6672 DeleteDC(hdc);
6674 return himl;
6677 /***
6678 * DESCRIPTION:
6679 * Sets the extended listview style.
6681 * PARAMETERS:
6682 * [I] infoPtr : valid pointer to the listview structure
6683 * [I] dwMask : mask
6684 * [I] dwStyle : style
6686 * RETURN:
6687 * SUCCESS : previous style
6688 * FAILURE : 0
6690 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6692 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6694 /* set new style */
6695 if (dwMask)
6696 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6697 else
6698 infoPtr->dwLvExStyle = dwStyle;
6700 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6702 HIMAGELIST himl = 0;
6703 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6704 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6705 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6708 return dwOldStyle;
6711 /***
6712 * DESCRIPTION:
6713 * Sets the new hot cursor used during hot tracking and hover selection.
6715 * PARAMETER(S):
6716 * [I] infoPtr : valid pointer to the listview structure
6717 * [I} hCurosr : the new hot cursor handle
6719 * RETURN:
6720 * Returns the previous hot cursor
6722 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6724 HCURSOR oldCursor = infoPtr->hHotCursor;
6726 infoPtr->hHotCursor = hCursor;
6728 return oldCursor;
6732 /***
6733 * DESCRIPTION:
6734 * Sets the hot item index.
6736 * PARAMETERS:
6737 * [I] infoPtr : valid pointer to the listview structure
6738 * [I] iIndex : index
6740 * RETURN:
6741 * SUCCESS : previous hot item index
6742 * FAILURE : -1 (no hot item)
6744 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6746 INT iOldIndex = infoPtr->nHotItem;
6748 infoPtr->nHotItem = iIndex;
6750 return iOldIndex;
6754 /***
6755 * DESCRIPTION:
6756 * Sets the amount of time the cursor must hover over an item before it is selected.
6758 * PARAMETER(S):
6759 * [I] infoPtr : valid pointer to the listview structure
6760 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6762 * RETURN:
6763 * Returns the previous hover time
6765 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6767 DWORD oldHoverTime = infoPtr->dwHoverTime;
6769 infoPtr->dwHoverTime = dwHoverTime;
6771 return oldHoverTime;
6774 /***
6775 * DESCRIPTION:
6776 * Sets spacing for icons of LVS_ICON style.
6778 * PARAMETER(S):
6779 * [I] infoPtr : valid pointer to the listview structure
6780 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6781 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6783 * RETURN:
6784 * MAKELONG(oldcx, oldcy)
6786 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6788 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6789 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6791 TRACE("requested=(%d,%d)\n", cx, cy);
6793 /* this is supported only for LVS_ICON style */
6794 if (uView != LVS_ICON) return oldspacing;
6796 /* set to defaults, if instructed to */
6797 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6798 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6800 /* if 0 then compute width
6801 * FIXME: Should scan each item and determine max width of
6802 * icon or label, then make that the width */
6803 if (cx == 0)
6804 cx = infoPtr->iconSpacing.cx;
6806 /* if 0 then compute height */
6807 if (cy == 0)
6808 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6809 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6812 infoPtr->iconSpacing.cx = cx;
6813 infoPtr->iconSpacing.cy = cy;
6815 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6816 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6817 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6818 infoPtr->ntmHeight);
6820 /* these depend on the iconSpacing */
6821 LISTVIEW_UpdateItemSize(infoPtr);
6823 return oldspacing;
6826 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6828 INT cx, cy;
6830 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6832 size->cx = cx;
6833 size->cy = cy;
6835 else
6837 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6838 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6842 /***
6843 * DESCRIPTION:
6844 * Sets image lists.
6846 * PARAMETER(S):
6847 * [I] infoPtr : valid pointer to the listview structure
6848 * [I] nType : image list type
6849 * [I] himl : image list handle
6851 * RETURN:
6852 * SUCCESS : old image list
6853 * FAILURE : NULL
6855 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6857 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6858 INT oldHeight = infoPtr->nItemHeight;
6859 HIMAGELIST himlOld = 0;
6861 TRACE("(nType=%d, himl=%p\n", nType, himl);
6863 switch (nType)
6865 case LVSIL_NORMAL:
6866 himlOld = infoPtr->himlNormal;
6867 infoPtr->himlNormal = himl;
6868 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6869 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6870 break;
6872 case LVSIL_SMALL:
6873 himlOld = infoPtr->himlSmall;
6874 infoPtr->himlSmall = himl;
6875 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6876 break;
6878 case LVSIL_STATE:
6879 himlOld = infoPtr->himlState;
6880 infoPtr->himlState = himl;
6881 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6882 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6883 break;
6885 default:
6886 ERR("Unknown icon type=%d\n", nType);
6887 return NULL;
6890 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6891 if (infoPtr->nItemHeight != oldHeight)
6892 LISTVIEW_UpdateScroll(infoPtr);
6894 return himlOld;
6897 /***
6898 * DESCRIPTION:
6899 * Preallocates memory (does *not* set the actual count of items !)
6901 * PARAMETER(S):
6902 * [I] infoPtr : valid pointer to the listview structure
6903 * [I] nItems : item count (projected number of items to allocate)
6904 * [I] dwFlags : update flags
6906 * RETURN:
6907 * SUCCESS : TRUE
6908 * FAILURE : FALSE
6910 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6912 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6914 if (infoPtr->dwStyle & LVS_OWNERDATA)
6916 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6917 INT nOldCount = infoPtr->nItemCount;
6919 if (nItems < nOldCount)
6921 RANGE range = { nItems, nOldCount };
6922 ranges_del(infoPtr->selectionRanges, range);
6923 if (infoPtr->nFocusedItem >= nItems)
6925 infoPtr->nFocusedItem = -1;
6926 SetRectEmpty(&infoPtr->rcFocus);
6930 infoPtr->nItemCount = nItems;
6931 LISTVIEW_UpdateScroll(infoPtr);
6933 /* the flags are valid only in ownerdata report and list modes */
6934 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6936 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6937 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6939 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6940 LISTVIEW_InvalidateList(infoPtr);
6941 else
6943 INT nFrom, nTo;
6944 POINT Origin;
6945 RECT rcErase;
6947 LISTVIEW_GetOrigin(infoPtr, &Origin);
6948 nFrom = min(nOldCount, nItems);
6949 nTo = max(nOldCount, nItems);
6951 if (uView == LVS_REPORT)
6953 rcErase.left = 0;
6954 rcErase.top = nFrom * infoPtr->nItemHeight;
6955 rcErase.right = infoPtr->nItemWidth;
6956 rcErase.bottom = nTo * infoPtr->nItemHeight;
6957 OffsetRect(&rcErase, Origin.x, Origin.y);
6958 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6959 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6961 else /* LVS_LIST */
6963 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6965 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6966 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6967 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6968 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6969 OffsetRect(&rcErase, Origin.x, Origin.y);
6970 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6971 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6973 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6974 rcErase.top = 0;
6975 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6976 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6977 OffsetRect(&rcErase, Origin.x, Origin.y);
6978 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6979 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6983 else
6985 /* According to MSDN for non-LVS_OWNERDATA this is just
6986 * a performance issue. The control allocates its internal
6987 * data structures for the number of items specified. It
6988 * cuts down on the number of memory allocations. Therefore
6989 * we will just issue a WARN here
6991 WARN("for non-ownerdata performance option not implemented.\n");
6994 return TRUE;
6997 /***
6998 * DESCRIPTION:
6999 * Sets the position of an item.
7001 * PARAMETER(S):
7002 * [I] infoPtr : valid pointer to the listview structure
7003 * [I] nItem : item index
7004 * [I] pt : coordinate
7006 * RETURN:
7007 * SUCCESS : TRUE
7008 * FAILURE : FALSE
7010 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7012 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7013 POINT Origin;
7015 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7017 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7018 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7020 LISTVIEW_GetOrigin(infoPtr, &Origin);
7022 /* This point value seems to be an undocumented feature.
7023 * The best guess is that it means either at the origin,
7024 * or at true beginning of the list. I will assume the origin. */
7025 if ((pt.x == -1) && (pt.y == -1))
7026 pt = Origin;
7028 if (uView == LVS_ICON)
7030 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7031 pt.y -= ICON_TOP_PADDING;
7033 pt.x -= Origin.x;
7034 pt.y -= Origin.y;
7036 infoPtr->bAutoarrange = FALSE;
7038 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7041 /***
7042 * DESCRIPTION:
7043 * Sets the state of one or many items.
7045 * PARAMETER(S):
7046 * [I] infoPtr : valid pointer to the listview structure
7047 * [I] nItem : item index
7048 * [I] lpLVItem : item or subitem info
7050 * RETURN:
7051 * SUCCESS : TRUE
7052 * FAILURE : FALSE
7054 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7056 BOOL bResult = TRUE;
7057 LVITEMW lvItem;
7059 lvItem.iItem = nItem;
7060 lvItem.iSubItem = 0;
7061 lvItem.mask = LVIF_STATE;
7062 lvItem.state = lpLVItem->state;
7063 lvItem.stateMask = lpLVItem->stateMask;
7064 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7066 if (nItem == -1)
7068 /* apply to all items */
7069 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7070 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7072 else
7073 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7076 *update selection mark
7078 * Investigation on windows 2k showed that selection mark was updated
7079 * whenever a new selection was made, but if the selected item was
7080 * unselected it was not updated.
7082 * we are probably still not 100% accurate, but this at least sets the
7083 * proper selection mark when it is needed
7086 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7087 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7089 int i;
7090 infoPtr->nSelectionMark = -1;
7091 for (i = 0; i < infoPtr->nItemCount; i++)
7093 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7095 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7097 infoPtr->nSelectionMark = i;
7098 break;
7101 else if (ranges_contain(infoPtr->selectionRanges, i))
7103 infoPtr->nSelectionMark = i;
7104 break;
7109 return bResult;
7112 /***
7113 * DESCRIPTION:
7114 * Sets the text of an item or subitem.
7116 * PARAMETER(S):
7117 * [I] hwnd : window handle
7118 * [I] nItem : item index
7119 * [I] lpLVItem : item or subitem info
7120 * [I] isW : TRUE if input is Unicode
7122 * RETURN:
7123 * SUCCESS : TRUE
7124 * FAILURE : FALSE
7126 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7128 LVITEMW lvItem;
7130 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7132 lvItem.iItem = nItem;
7133 lvItem.iSubItem = lpLVItem->iSubItem;
7134 lvItem.mask = LVIF_TEXT;
7135 lvItem.pszText = lpLVItem->pszText;
7136 lvItem.cchTextMax = lpLVItem->cchTextMax;
7138 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7140 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7143 /***
7144 * DESCRIPTION:
7145 * Set item index that marks the start of a multiple selection.
7147 * PARAMETER(S):
7148 * [I] infoPtr : valid pointer to the listview structure
7149 * [I] nIndex : index
7151 * RETURN:
7152 * Index number or -1 if there is no selection mark.
7154 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7156 INT nOldIndex = infoPtr->nSelectionMark;
7158 TRACE("(nIndex=%d)\n", nIndex);
7160 infoPtr->nSelectionMark = nIndex;
7162 return nOldIndex;
7165 /***
7166 * DESCRIPTION:
7167 * Sets the text background color.
7169 * PARAMETER(S):
7170 * [I] infoPtr : valid pointer to the listview structure
7171 * [I] clrTextBk : text background color
7173 * RETURN:
7174 * SUCCESS : TRUE
7175 * FAILURE : FALSE
7177 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7179 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7181 if (infoPtr->clrTextBk != clrTextBk)
7183 infoPtr->clrTextBk = clrTextBk;
7184 LISTVIEW_InvalidateList(infoPtr);
7187 return TRUE;
7190 /***
7191 * DESCRIPTION:
7192 * Sets the text foreground color.
7194 * PARAMETER(S):
7195 * [I] infoPtr : valid pointer to the listview structure
7196 * [I] clrText : text color
7198 * RETURN:
7199 * SUCCESS : TRUE
7200 * FAILURE : FALSE
7202 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7204 TRACE("(clrText=%lx)\n", clrText);
7206 if (infoPtr->clrText != clrText)
7208 infoPtr->clrText = clrText;
7209 LISTVIEW_InvalidateList(infoPtr);
7212 return TRUE;
7215 /***
7216 * DESCRIPTION:
7217 * Determines which listview item is located at the specified position.
7219 * PARAMETER(S):
7220 * [I] infoPtr : valid pointer to the listview structure
7221 * [I] hwndNewToolTip : handle to new ToolTip
7223 * RETURN:
7224 * old tool tip
7226 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7228 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7229 infoPtr->hwndToolTip = hwndNewToolTip;
7230 return hwndOldToolTip;
7233 /* LISTVIEW_SetUnicodeFormat */
7234 /* LISTVIEW_SetWorkAreas */
7236 /***
7237 * DESCRIPTION:
7238 * Callback internally used by LISTVIEW_SortItems()
7240 * PARAMETER(S):
7241 * [I] first : pointer to first ITEM_INFO to compare
7242 * [I] second : pointer to second ITEM_INFO to compare
7243 * [I] lParam : HWND of control
7245 * RETURN:
7246 * if first comes before second : negative
7247 * if first comes after second : positive
7248 * if first and second are equivalent : zero
7250 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7252 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7253 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7254 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7256 /* Forward the call to the client defined callback */
7257 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7260 /***
7261 * DESCRIPTION:
7262 * Sorts the listview items.
7264 * PARAMETER(S):
7265 * [I] infoPtr : valid pointer to the listview structure
7266 * [I] pfnCompare : application-defined value
7267 * [I] lParamSort : pointer to comparision callback
7269 * RETURN:
7270 * SUCCESS : TRUE
7271 * FAILURE : FALSE
7273 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7275 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7276 HDPA hdpaSubItems;
7277 ITEM_INFO *lpItem;
7278 LPVOID selectionMarkItem;
7279 LVITEMW item;
7280 int i;
7282 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7284 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7286 if (!pfnCompare) return FALSE;
7287 if (!infoPtr->hdpaItems) return FALSE;
7289 /* if there are 0 or 1 items, there is no need to sort */
7290 if (infoPtr->nItemCount < 2) return TRUE;
7292 if (infoPtr->nFocusedItem >= 0)
7294 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7295 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7296 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7298 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7299 /* clear the lpItem->state for non-selected ones */
7300 /* remove the selection ranges */
7302 infoPtr->pfnCompare = pfnCompare;
7303 infoPtr->lParamSort = lParamSort;
7304 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7306 /* Adjust selections and indices so that they are the way they should
7307 * be after the sort (otherwise, the list items move around, but
7308 * whatever is at the item's previous original position will be
7309 * selected instead)
7311 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7312 for (i=0; i < infoPtr->nItemCount; i++)
7314 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7315 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7317 if (lpItem->state & LVIS_SELECTED)
7319 item.state = LVIS_SELECTED;
7320 item.stateMask = LVIS_SELECTED;
7321 LISTVIEW_SetItemState(infoPtr, i, &item);
7323 if (lpItem->state & LVIS_FOCUSED)
7325 infoPtr->nFocusedItem = i;
7326 lpItem->state &= ~LVIS_FOCUSED;
7329 if (selectionMarkItem != NULL)
7330 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7331 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7333 /* refresh the display */
7334 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7335 LISTVIEW_InvalidateList(infoPtr);
7337 return TRUE;
7340 /***
7341 * DESCRIPTION:
7342 * Updates an items or rearranges the listview control.
7344 * PARAMETER(S):
7345 * [I] infoPtr : valid pointer to the listview structure
7346 * [I] nItem : item index
7348 * RETURN:
7349 * SUCCESS : TRUE
7350 * FAILURE : FALSE
7352 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7354 TRACE("(nItem=%d)\n", nItem);
7356 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7358 /* rearrange with default alignment style */
7359 if (is_autoarrange(infoPtr))
7360 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7361 else
7362 LISTVIEW_InvalidateItem(infoPtr, nItem);
7364 return TRUE;
7368 /***
7369 * DESCRIPTION:
7370 * Creates the listview control.
7372 * PARAMETER(S):
7373 * [I] hwnd : window handle
7374 * [I] lpcs : the create parameters
7376 * RETURN:
7377 * Success: 0
7378 * Failure: -1
7380 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7382 LISTVIEW_INFO *infoPtr;
7383 UINT uView = lpcs->style & LVS_TYPEMASK;
7384 LOGFONTW logFont;
7386 TRACE("(lpcs=%p)\n", lpcs);
7388 /* initialize info pointer */
7389 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7390 if (!infoPtr) return -1;
7392 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7394 infoPtr->hwndSelf = hwnd;
7395 infoPtr->dwStyle = lpcs->style;
7396 /* determine the type of structures to use */
7397 infoPtr->hwndNotify = lpcs->hwndParent;
7398 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7399 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7401 /* initialize color information */
7402 infoPtr->clrBk = CLR_NONE;
7403 infoPtr->clrText = comctl32_color.clrWindowText;
7404 infoPtr->clrTextBk = CLR_DEFAULT;
7405 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7407 /* set default values */
7408 infoPtr->nFocusedItem = -1;
7409 infoPtr->nSelectionMark = -1;
7410 infoPtr->nHotItem = -1;
7411 infoPtr->bRedraw = TRUE;
7412 infoPtr->bNoItemMetrics = TRUE;
7413 infoPtr->bDoChangeNotify = TRUE;
7414 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7415 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7416 infoPtr->nEditLabelItem = -1;
7417 infoPtr->dwHoverTime = -1; /* default system hover time */
7419 /* get default font (icon title) */
7420 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7421 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7422 infoPtr->hFont = infoPtr->hDefaultFont;
7423 LISTVIEW_SaveTextMetrics(infoPtr);
7425 /* create header */
7426 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7427 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7428 0, 0, 0, 0, hwnd, NULL,
7429 lpcs->hInstance, NULL);
7430 if (!infoPtr->hwndHeader) goto fail;
7432 /* set header unicode format */
7433 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7435 /* set header font */
7436 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7438 /* allocate memory for the data structure */
7439 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7440 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7441 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7442 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7443 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7445 /* initialize the icon sizes */
7446 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7447 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7449 /* init item size to avoid division by 0 */
7450 LISTVIEW_UpdateItemSize (infoPtr);
7452 if (uView == LVS_REPORT)
7454 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7456 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7458 else
7460 /* set HDS_HIDDEN flag to hide the header bar */
7461 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7462 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7466 return 0;
7468 fail:
7469 DestroyWindow(infoPtr->hwndHeader);
7470 ranges_destroy(infoPtr->selectionRanges);
7471 DPA_Destroy(infoPtr->hdpaItems);
7472 DPA_Destroy(infoPtr->hdpaPosX);
7473 DPA_Destroy(infoPtr->hdpaPosY);
7474 DPA_Destroy(infoPtr->hdpaColumns);
7475 Free(infoPtr);
7476 return -1;
7479 /***
7480 * DESCRIPTION:
7481 * Erases the background of the listview control.
7483 * PARAMETER(S):
7484 * [I] infoPtr : valid pointer to the listview structure
7485 * [I] hdc : device context handle
7487 * RETURN:
7488 * SUCCESS : TRUE
7489 * FAILURE : FALSE
7491 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7493 RECT rc;
7495 TRACE("(hdc=%p)\n", hdc);
7497 if (!GetClipBox(hdc, &rc)) return FALSE;
7499 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7503 /***
7504 * DESCRIPTION:
7505 * Helper function for LISTVIEW_[HV]Scroll *only*.
7506 * Performs vertical/horizontal scrolling by a give amount.
7508 * PARAMETER(S):
7509 * [I] infoPtr : valid pointer to the listview structure
7510 * [I] dx : amount of horizontal scroll
7511 * [I] dy : amount of vertical scroll
7513 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7515 /* now we can scroll the list */
7516 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7517 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7518 /* if we have focus, adjust rect */
7519 OffsetRect(&infoPtr->rcFocus, dx, dy);
7520 UpdateWindow(infoPtr->hwndSelf);
7523 /***
7524 * DESCRIPTION:
7525 * Performs vertical scrolling.
7527 * PARAMETER(S):
7528 * [I] infoPtr : valid pointer to the listview structure
7529 * [I] nScrollCode : scroll code
7530 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7531 * [I] hScrollWnd : scrollbar control window handle
7533 * RETURN:
7534 * Zero
7536 * NOTES:
7537 * SB_LINEUP/SB_LINEDOWN:
7538 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7539 * for LVS_REPORT is 1 line
7540 * for LVS_LIST cannot occur
7543 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7544 INT nScrollDiff, HWND hScrollWnd)
7546 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7547 INT nOldScrollPos, nNewScrollPos;
7548 SCROLLINFO scrollInfo;
7549 BOOL is_an_icon;
7551 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7552 debugscrollcode(nScrollCode), nScrollDiff);
7554 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7556 scrollInfo.cbSize = sizeof(SCROLLINFO);
7557 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7559 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7561 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7563 nOldScrollPos = scrollInfo.nPos;
7564 switch (nScrollCode)
7566 case SB_INTERNAL:
7567 break;
7569 case SB_LINEUP:
7570 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7571 break;
7573 case SB_LINEDOWN:
7574 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7575 break;
7577 case SB_PAGEUP:
7578 nScrollDiff = -scrollInfo.nPage;
7579 break;
7581 case SB_PAGEDOWN:
7582 nScrollDiff = scrollInfo.nPage;
7583 break;
7585 case SB_THUMBPOSITION:
7586 case SB_THUMBTRACK:
7587 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7588 break;
7590 default:
7591 nScrollDiff = 0;
7594 /* quit right away if pos isn't changing */
7595 if (nScrollDiff == 0) return 0;
7597 /* calculate new position, and handle overflows */
7598 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7599 if (nScrollDiff > 0) {
7600 if (nNewScrollPos < nOldScrollPos ||
7601 nNewScrollPos > scrollInfo.nMax)
7602 nNewScrollPos = scrollInfo.nMax;
7603 } else {
7604 if (nNewScrollPos > nOldScrollPos ||
7605 nNewScrollPos < scrollInfo.nMin)
7606 nNewScrollPos = scrollInfo.nMin;
7609 /* set the new position, and reread in case it changed */
7610 scrollInfo.fMask = SIF_POS;
7611 scrollInfo.nPos = nNewScrollPos;
7612 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7614 /* carry on only if it really changed */
7615 if (nNewScrollPos == nOldScrollPos) return 0;
7617 /* now adjust to client coordinates */
7618 nScrollDiff = nOldScrollPos - nNewScrollPos;
7619 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7621 /* and scroll the window */
7622 scroll_list(infoPtr, 0, nScrollDiff);
7624 return 0;
7627 /***
7628 * DESCRIPTION:
7629 * Performs horizontal scrolling.
7631 * PARAMETER(S):
7632 * [I] infoPtr : valid pointer to the listview structure
7633 * [I] nScrollCode : scroll code
7634 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7635 * [I] hScrollWnd : scrollbar control window handle
7637 * RETURN:
7638 * Zero
7640 * NOTES:
7641 * SB_LINELEFT/SB_LINERIGHT:
7642 * for LVS_ICON, LVS_SMALLICON 1 pixel
7643 * for LVS_REPORT is 1 pixel
7644 * for LVS_LIST is 1 column --> which is a 1 because the
7645 * scroll is based on columns not pixels
7648 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7649 INT nScrollDiff, HWND hScrollWnd)
7651 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7652 INT nOldScrollPos, nNewScrollPos;
7653 SCROLLINFO scrollInfo;
7655 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7656 debugscrollcode(nScrollCode), nScrollDiff);
7658 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7660 scrollInfo.cbSize = sizeof(SCROLLINFO);
7661 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7663 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7665 nOldScrollPos = scrollInfo.nPos;
7667 switch (nScrollCode)
7669 case SB_INTERNAL:
7670 break;
7672 case SB_LINELEFT:
7673 nScrollDiff = -1;
7674 break;
7676 case SB_LINERIGHT:
7677 nScrollDiff = 1;
7678 break;
7680 case SB_PAGELEFT:
7681 nScrollDiff = -scrollInfo.nPage;
7682 break;
7684 case SB_PAGERIGHT:
7685 nScrollDiff = scrollInfo.nPage;
7686 break;
7688 case SB_THUMBPOSITION:
7689 case SB_THUMBTRACK:
7690 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7691 break;
7693 default:
7694 nScrollDiff = 0;
7697 /* quit right away if pos isn't changing */
7698 if (nScrollDiff == 0) return 0;
7700 /* calculate new position, and handle overflows */
7701 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7702 if (nScrollDiff > 0) {
7703 if (nNewScrollPos < nOldScrollPos ||
7704 nNewScrollPos > scrollInfo.nMax)
7705 nNewScrollPos = scrollInfo.nMax;
7706 } else {
7707 if (nNewScrollPos > nOldScrollPos ||
7708 nNewScrollPos < scrollInfo.nMin)
7709 nNewScrollPos = scrollInfo.nMin;
7712 /* set the new position, and reread in case it changed */
7713 scrollInfo.fMask = SIF_POS;
7714 scrollInfo.nPos = nNewScrollPos;
7715 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7717 /* carry on only if it really changed */
7718 if (nNewScrollPos == nOldScrollPos) return 0;
7720 if(uView == LVS_REPORT)
7721 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7723 /* now adjust to client coordinates */
7724 nScrollDiff = nOldScrollPos - nNewScrollPos;
7725 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7727 /* and scroll the window */
7728 scroll_list(infoPtr, nScrollDiff, 0);
7730 return 0;
7733 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7735 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7736 INT gcWheelDelta = 0;
7737 INT pulScrollLines = 3;
7738 SCROLLINFO scrollInfo;
7740 TRACE("(wheelDelta=%d)\n", wheelDelta);
7742 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7743 gcWheelDelta -= wheelDelta;
7745 scrollInfo.cbSize = sizeof(SCROLLINFO);
7746 scrollInfo.fMask = SIF_POS;
7748 switch(uView)
7750 case LVS_ICON:
7751 case LVS_SMALLICON:
7753 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7754 * should be fixed in the future.
7756 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7757 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7758 break;
7760 case LVS_REPORT:
7761 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7763 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7764 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7765 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7767 break;
7769 case LVS_LIST:
7770 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7771 break;
7773 return 0;
7776 /***
7777 * DESCRIPTION:
7778 * ???
7780 * PARAMETER(S):
7781 * [I] infoPtr : valid pointer to the listview structure
7782 * [I] nVirtualKey : virtual key
7783 * [I] lKeyData : key data
7785 * RETURN:
7786 * Zero
7788 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7790 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7791 INT nItem = -1;
7792 NMLVKEYDOWN nmKeyDown;
7794 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7796 /* send LVN_KEYDOWN notification */
7797 nmKeyDown.wVKey = nVirtualKey;
7798 nmKeyDown.flags = 0;
7799 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7801 switch (nVirtualKey)
7803 case VK_RETURN:
7804 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7806 notify(infoPtr, NM_RETURN);
7807 notify(infoPtr, LVN_ITEMACTIVATE);
7809 break;
7811 case VK_HOME:
7812 if (infoPtr->nItemCount > 0)
7813 nItem = 0;
7814 break;
7816 case VK_END:
7817 if (infoPtr->nItemCount > 0)
7818 nItem = infoPtr->nItemCount - 1;
7819 break;
7821 case VK_LEFT:
7822 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7823 break;
7825 case VK_UP:
7826 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7827 break;
7829 case VK_RIGHT:
7830 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7831 break;
7833 case VK_DOWN:
7834 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7835 break;
7837 case VK_PRIOR:
7838 if (uView == LVS_REPORT)
7839 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7840 else
7841 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7842 * LISTVIEW_GetCountPerRow(infoPtr);
7843 if(nItem < 0) nItem = 0;
7844 break;
7846 case VK_NEXT:
7847 if (uView == LVS_REPORT)
7848 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7849 else
7850 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7851 * LISTVIEW_GetCountPerRow(infoPtr);
7852 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7853 break;
7856 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7857 LISTVIEW_KeySelection(infoPtr, nItem);
7859 return 0;
7862 /***
7863 * DESCRIPTION:
7864 * Kills the focus.
7866 * PARAMETER(S):
7867 * [I] infoPtr : valid pointer to the listview structure
7869 * RETURN:
7870 * Zero
7872 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7874 TRACE("()\n");
7876 /* if we did not have the focus, there's nothing to do */
7877 if (!infoPtr->bFocus) return 0;
7879 /* send NM_KILLFOCUS notification */
7880 notify(infoPtr, NM_KILLFOCUS);
7882 /* if we have a focus rectagle, get rid of it */
7883 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7885 /* set window focus flag */
7886 infoPtr->bFocus = FALSE;
7888 /* invalidate the selected items before reseting focus flag */
7889 LISTVIEW_InvalidateSelectedItems(infoPtr);
7891 return 0;
7895 /***
7896 * DESCRIPTION:
7897 * Track mouse/dragging
7899 * PARAMETER(S):
7900 * [I] infoPtr : valid pointer to the listview structure
7901 * [I] pt : mouse coordinate
7903 * RETURN:
7904 * Zero
7906 static LRESULT LISTVIEW_TrackMouse(LISTVIEW_INFO *infoPtr, POINT pt)
7908 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
7909 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
7910 RECT r;
7911 MSG msg;
7913 TRACE("\n");
7915 r.top = pt.y - cyDrag;
7916 r.left = pt.x - cxDrag;
7917 r.bottom = pt.y + cyDrag;
7918 r.right = pt.x + cxDrag;
7920 SetCapture(infoPtr->hwndSelf);
7922 while (1)
7924 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
7926 if (msg.message == WM_MOUSEMOVE)
7928 pt.x = (short)LOWORD(msg.lParam);
7929 pt.y = (short)HIWORD(msg.lParam);
7930 if (PtInRect(&r, pt))
7931 continue;
7932 else
7934 ReleaseCapture();
7935 return 1;
7938 else if (msg.message >= WM_LBUTTONDOWN &&
7939 msg.message <= WM_RBUTTONDBLCLK)
7941 break;
7944 DispatchMessageW(&msg);
7947 if (GetCapture() != infoPtr->hwndSelf)
7948 return 0;
7951 ReleaseCapture();
7952 return 0;
7956 /***
7957 * DESCRIPTION:
7958 * Processes double click messages (left mouse button).
7960 * PARAMETER(S):
7961 * [I] infoPtr : valid pointer to the listview structure
7962 * [I] wKey : key flag
7963 * [I] x,y : mouse coordinate
7965 * RETURN:
7966 * Zero
7968 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
7970 LVHITTESTINFO htInfo;
7972 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
7974 /* send NM_RELEASEDCAPTURE notification */
7975 notify(infoPtr, NM_RELEASEDCAPTURE);
7977 htInfo.pt.x = x;
7978 htInfo.pt.y = y;
7980 /* send NM_DBLCLK notification */
7981 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7982 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7984 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7985 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
7987 return 0;
7990 /***
7991 * DESCRIPTION:
7992 * Processes mouse down messages (left mouse button).
7994 * PARAMETER(S):
7995 * [I] infoPtr : valid pointer to the listview structure
7996 * [I] wKey : key flag
7997 * [I] x,y : mouse coordinate
7999 * RETURN:
8000 * Zero
8002 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8004 LVHITTESTINFO lvHitTestInfo;
8005 static BOOL bGroupSelect = TRUE;
8006 POINT pt = { x, y };
8007 INT nItem;
8009 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8011 /* send NM_RELEASEDCAPTURE notification */
8012 notify(infoPtr, NM_RELEASEDCAPTURE);
8014 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8016 /* set left button down flag */
8017 infoPtr->bLButtonDown = TRUE;
8019 lvHitTestInfo.pt.x = x;
8020 lvHitTestInfo.pt.y = y;
8022 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8023 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8024 infoPtr->nEditLabelItem = -1;
8025 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8027 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8029 DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
8030 if(state == 1 || state == 2)
8032 LVITEMW lvitem;
8033 state ^= 3;
8034 lvitem.state = state << 12;
8035 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8036 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8038 return 0;
8040 if (LISTVIEW_TrackMouse(infoPtr, lvHitTestInfo.pt))
8042 NMLISTVIEW nmlv;
8044 ZeroMemory(&nmlv, sizeof(nmlv));
8045 nmlv.iItem = nItem;
8046 nmlv.ptAction.x = lvHitTestInfo.pt.x;
8047 nmlv.ptAction.y = lvHitTestInfo.pt.y;
8049 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
8051 return 0;
8054 if (infoPtr->dwStyle & LVS_SINGLESEL)
8056 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8057 infoPtr->nEditLabelItem = nItem;
8058 else
8059 LISTVIEW_SetSelection(infoPtr, nItem);
8061 else
8063 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8065 if (bGroupSelect)
8067 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8068 LISTVIEW_SetItemFocus(infoPtr, nItem);
8069 infoPtr->nSelectionMark = nItem;
8071 else
8073 LVITEMW item;
8075 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8076 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8078 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8079 infoPtr->nSelectionMark = nItem;
8082 else if (wKey & MK_CONTROL)
8084 LVITEMW item;
8086 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8088 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8089 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8090 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8091 infoPtr->nSelectionMark = nItem;
8093 else if (wKey & MK_SHIFT)
8095 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8097 else
8099 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8100 infoPtr->nEditLabelItem = nItem;
8102 /* set selection (clears other pre-existing selections) */
8103 LISTVIEW_SetSelection(infoPtr, nItem);
8107 else
8109 /* remove all selections */
8110 LISTVIEW_DeselectAll(infoPtr);
8111 ReleaseCapture();
8114 return 0;
8117 /***
8118 * DESCRIPTION:
8119 * Processes mouse up messages (left mouse button).
8121 * PARAMETER(S):
8122 * [I] infoPtr : valid pointer to the listview structure
8123 * [I] wKey : key flag
8124 * [I] x,y : mouse coordinate
8126 * RETURN:
8127 * Zero
8129 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8131 LVHITTESTINFO lvHitTestInfo;
8133 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8135 if (!infoPtr->bLButtonDown) return 0;
8137 lvHitTestInfo.pt.x = x;
8138 lvHitTestInfo.pt.y = y;
8140 /* send NM_CLICK notification */
8141 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8142 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8144 /* set left button flag */
8145 infoPtr->bLButtonDown = FALSE;
8147 /* if we clicked on a selected item, edit the label */
8148 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8149 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8151 return 0;
8154 /***
8155 * DESCRIPTION:
8156 * Destroys the listview control (called after WM_DESTROY).
8158 * PARAMETER(S):
8159 * [I] infoPtr : valid pointer to the listview structure
8161 * RETURN:
8162 * Zero
8164 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8166 TRACE("()\n");
8168 /* delete all items */
8169 LISTVIEW_DeleteAllItems(infoPtr);
8171 /* destroy data structure */
8172 DPA_Destroy(infoPtr->hdpaItems);
8173 DPA_Destroy(infoPtr->hdpaPosX);
8174 DPA_Destroy(infoPtr->hdpaPosY);
8175 DPA_Destroy(infoPtr->hdpaColumns);
8176 ranges_destroy(infoPtr->selectionRanges);
8178 /* destroy image lists */
8179 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8181 if (infoPtr->himlNormal)
8182 ImageList_Destroy(infoPtr->himlNormal);
8183 if (infoPtr->himlSmall)
8184 ImageList_Destroy(infoPtr->himlSmall);
8185 if (infoPtr->himlState)
8186 ImageList_Destroy(infoPtr->himlState);
8189 /* destroy font, bkgnd brush */
8190 infoPtr->hFont = 0;
8191 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8192 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8194 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8196 /* free listview info pointer*/
8197 Free(infoPtr);
8199 return 0;
8202 /***
8203 * DESCRIPTION:
8204 * Handles notifications from header.
8206 * PARAMETER(S):
8207 * [I] infoPtr : valid pointer to the listview structure
8208 * [I] nCtrlId : control identifier
8209 * [I] lpnmh : notification information
8211 * RETURN:
8212 * Zero
8214 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8216 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8218 TRACE("(lpnmh=%p)\n", lpnmh);
8220 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8222 switch (lpnmh->hdr.code)
8224 case HDN_TRACKW:
8225 case HDN_TRACKA:
8226 case HDN_ITEMCHANGEDW:
8227 case HDN_ITEMCHANGEDA:
8229 COLUMN_INFO *lpColumnInfo;
8230 INT dx, cxy;
8232 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8234 HDITEMW hdi;
8236 hdi.mask = HDI_WIDTH;
8237 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8238 cxy = hdi.cxy;
8240 else
8241 cxy = lpnmh->pitem->cxy;
8243 /* determine how much we change since the last know position */
8244 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8245 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8246 if (dx != 0)
8248 RECT rcCol = lpColumnInfo->rcHeader;
8250 lpColumnInfo->rcHeader.right += dx;
8251 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8252 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8254 /* this trick works for left aligned columns only */
8255 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8257 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8258 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8260 rcCol.top = infoPtr->rcList.top;
8261 rcCol.bottom = infoPtr->rcList.bottom;
8262 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8266 break;
8268 case HDN_ITEMCLICKW:
8269 case HDN_ITEMCLICKA:
8271 /* Handle sorting by Header Column */
8272 NMLISTVIEW nmlv;
8274 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8275 nmlv.iItem = -1;
8276 nmlv.iSubItem = lpnmh->iItem;
8277 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8279 break;
8282 return 0;
8285 /***
8286 * DESCRIPTION:
8287 * Determines the type of structure to use.
8289 * PARAMETER(S):
8290 * [I] infoPtr : valid pointer to the listview structureof the sender
8291 * [I] hwndFrom : listview window handle
8292 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8294 * RETURN:
8295 * Zero
8297 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8299 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8301 if (nCommand != NF_REQUERY) return 0;
8303 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8305 return 0;
8308 /***
8309 * DESCRIPTION:
8310 * Paints/Repaints the listview control.
8312 * PARAMETER(S):
8313 * [I] infoPtr : valid pointer to the listview structure
8314 * [I] hdc : device context handle
8316 * RETURN:
8317 * Zero
8319 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8321 TRACE("(hdc=%p)\n", hdc);
8323 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8325 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8327 infoPtr->bNoItemMetrics = FALSE;
8328 LISTVIEW_UpdateItemSize(infoPtr);
8329 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8330 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8331 LISTVIEW_UpdateScroll(infoPtr);
8333 if (hdc)
8334 LISTVIEW_Refresh(infoPtr, hdc);
8335 else
8337 PAINTSTRUCT ps;
8339 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8340 if (!hdc) return 1;
8341 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8342 LISTVIEW_Refresh(infoPtr, hdc);
8343 EndPaint(infoPtr->hwndSelf, &ps);
8346 return 0;
8350 /***
8351 * DESCRIPTION:
8352 * Paints/Repaints the listview control.
8354 * PARAMETER(S):
8355 * [I] infoPtr : valid pointer to the listview structure
8356 * [I] hdc : device context handle
8357 * [I] options : drawing options
8359 * RETURN:
8360 * Zero
8362 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8364 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8366 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8367 return 0;
8369 if (options & PRF_ERASEBKGND)
8370 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8372 if (options & PRF_CLIENT)
8373 LISTVIEW_Paint(infoPtr, hdc);
8375 return 0;
8379 /***
8380 * DESCRIPTION:
8381 * Processes double click messages (right mouse button).
8383 * PARAMETER(S):
8384 * [I] infoPtr : valid pointer to the listview structure
8385 * [I] wKey : key flag
8386 * [I] x,y : mouse coordinate
8388 * RETURN:
8389 * Zero
8391 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8393 LVHITTESTINFO lvHitTestInfo;
8395 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8397 /* send NM_RELEASEDCAPTURE notification */
8398 notify(infoPtr, NM_RELEASEDCAPTURE);
8400 /* send NM_RDBLCLK notification */
8401 lvHitTestInfo.pt.x = x;
8402 lvHitTestInfo.pt.y = y;
8403 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8404 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8406 return 0;
8409 /***
8410 * DESCRIPTION:
8411 * Processes mouse down messages (right mouse button).
8413 * PARAMETER(S):
8414 * [I] infoPtr : valid pointer to the listview structure
8415 * [I] wKey : key flag
8416 * [I] x,y : mouse coordinate
8418 * RETURN:
8419 * Zero
8421 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8423 LVHITTESTINFO lvHitTestInfo;
8424 INT nItem;
8426 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8428 /* send NM_RELEASEDCAPTURE notification */
8429 notify(infoPtr, NM_RELEASEDCAPTURE);
8431 /* make sure the listview control window has the focus */
8432 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8434 /* set right button down flag */
8435 infoPtr->bRButtonDown = TRUE;
8437 /* determine the index of the selected item */
8438 lvHitTestInfo.pt.x = x;
8439 lvHitTestInfo.pt.y = y;
8440 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8442 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8444 LISTVIEW_SetItemFocus(infoPtr, nItem);
8445 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8446 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8447 LISTVIEW_SetSelection(infoPtr, nItem);
8449 else
8451 LISTVIEW_DeselectAll(infoPtr);
8454 return 0;
8457 /***
8458 * DESCRIPTION:
8459 * Processes mouse up messages (right mouse button).
8461 * PARAMETER(S):
8462 * [I] infoPtr : valid pointer to the listview structure
8463 * [I] wKey : key flag
8464 * [I] x,y : mouse coordinate
8466 * RETURN:
8467 * Zero
8469 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8471 LVHITTESTINFO lvHitTestInfo;
8472 POINT pt;
8474 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8476 if (!infoPtr->bRButtonDown) return 0;
8478 /* set button flag */
8479 infoPtr->bRButtonDown = FALSE;
8481 /* Send NM_RClICK notification */
8482 lvHitTestInfo.pt.x = x;
8483 lvHitTestInfo.pt.y = y;
8484 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8485 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8487 /* Change to screen coordinate for WM_CONTEXTMENU */
8488 pt = lvHitTestInfo.pt;
8489 ClientToScreen(infoPtr->hwndSelf, &pt);
8491 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8492 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8493 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8495 return 0;
8499 /***
8500 * DESCRIPTION:
8501 * Sets the cursor.
8503 * PARAMETER(S):
8504 * [I] infoPtr : valid pointer to the listview structure
8505 * [I] hwnd : window handle of window containing the cursor
8506 * [I] nHittest : hit-test code
8507 * [I] wMouseMsg : ideintifier of the mouse message
8509 * RETURN:
8510 * TRUE if cursor is set
8511 * FALSE otherwise
8513 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8515 LVHITTESTINFO lvHitTestInfo;
8517 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8519 if(!infoPtr->hHotCursor) return FALSE;
8521 GetCursorPos(&lvHitTestInfo.pt);
8522 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8524 SetCursor(infoPtr->hHotCursor);
8526 return TRUE;
8529 /***
8530 * DESCRIPTION:
8531 * Sets the focus.
8533 * PARAMETER(S):
8534 * [I] infoPtr : valid pointer to the listview structure
8535 * [I] hwndLoseFocus : handle of previously focused window
8537 * RETURN:
8538 * Zero
8540 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8542 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8544 /* if we have the focus already, there's nothing to do */
8545 if (infoPtr->bFocus) return 0;
8547 /* send NM_SETFOCUS notification */
8548 notify(infoPtr, NM_SETFOCUS);
8550 /* set window focus flag */
8551 infoPtr->bFocus = TRUE;
8553 /* put the focus rect back on */
8554 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8556 /* redraw all visible selected items */
8557 LISTVIEW_InvalidateSelectedItems(infoPtr);
8559 return 0;
8562 /***
8563 * DESCRIPTION:
8564 * Sets the font.
8566 * PARAMETER(S):
8567 * [I] infoPtr : valid pointer to the listview structure
8568 * [I] fRedraw : font handle
8569 * [I] fRedraw : redraw flag
8571 * RETURN:
8572 * Zero
8574 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8576 HFONT oldFont = infoPtr->hFont;
8578 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8580 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8581 if (infoPtr->hFont == oldFont) return 0;
8583 LISTVIEW_SaveTextMetrics(infoPtr);
8585 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8586 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8588 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8590 return 0;
8593 /***
8594 * DESCRIPTION:
8595 * Message handling for WM_SETREDRAW.
8596 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8598 * PARAMETER(S):
8599 * [I] infoPtr : valid pointer to the listview structure
8600 * [I] bRedraw: state of redraw flag
8602 * RETURN:
8603 * DefWinProc return value
8605 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8607 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8609 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8610 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8612 infoPtr->bRedraw = bRedraw;
8614 if(!bRedraw) return 0;
8616 if (is_autoarrange(infoPtr))
8617 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8618 LISTVIEW_UpdateScroll(infoPtr);
8620 /* despite what the WM_SETREDRAW docs says, apps expect us
8621 * to invalidate the listview here... stupid! */
8622 LISTVIEW_InvalidateList(infoPtr);
8624 return 0;
8627 /***
8628 * DESCRIPTION:
8629 * Resizes the listview control. This function processes WM_SIZE
8630 * messages. At this time, the width and height are not used.
8632 * PARAMETER(S):
8633 * [I] infoPtr : valid pointer to the listview structure
8634 * [I] Width : new width
8635 * [I] Height : new height
8637 * RETURN:
8638 * Zero
8640 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8642 RECT rcOld = infoPtr->rcList;
8644 TRACE("(width=%d, height=%d)\n", Width, Height);
8646 LISTVIEW_UpdateSize(infoPtr);
8647 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8649 /* do not bother with display related stuff if we're not redrawing */
8650 if (!is_redrawing(infoPtr)) return 0;
8652 if (is_autoarrange(infoPtr))
8653 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8655 LISTVIEW_UpdateScroll(infoPtr);
8657 /* refresh all only for lists whose height changed significantly */
8658 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8659 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8660 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8661 LISTVIEW_InvalidateList(infoPtr);
8663 return 0;
8666 /***
8667 * DESCRIPTION:
8668 * Sets the size information.
8670 * PARAMETER(S):
8671 * [I] infoPtr : valid pointer to the listview structure
8673 * RETURN:
8674 * None
8676 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8678 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8680 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8682 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8684 if (uView == LVS_LIST)
8686 /* Apparently the "LIST" style is supposed to have the same
8687 * number of items in a column even if there is no scroll bar.
8688 * Since if a scroll bar already exists then the bottom is already
8689 * reduced, only reduce if the scroll bar does not currently exist.
8690 * The "2" is there to mimic the native control. I think it may be
8691 * related to either padding or edges. (GLA 7/2002)
8693 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8694 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8695 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8697 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8699 HDLAYOUT hl;
8700 WINDOWPOS wp;
8702 hl.prc = &infoPtr->rcList;
8703 hl.pwpos = &wp;
8704 Header_Layout(infoPtr->hwndHeader, &hl);
8706 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8708 infoPtr->rcList.top = max(wp.cy, 0);
8711 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8714 /***
8715 * DESCRIPTION:
8716 * Processes WM_STYLECHANGED messages.
8718 * PARAMETER(S):
8719 * [I] infoPtr : valid pointer to the listview structure
8720 * [I] wStyleType : window style type (normal or extended)
8721 * [I] lpss : window style information
8723 * RETURN:
8724 * Zero
8726 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8727 const STYLESTRUCT *lpss)
8729 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8730 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8732 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8733 wStyleType, lpss->styleOld, lpss->styleNew);
8735 if (wStyleType != GWL_STYLE) return 0;
8737 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8738 /* what if LVS_OWNERDATA changed? */
8739 /* or LVS_SINGLESEL */
8740 /* or LVS_SORT{AS,DES}CENDING */
8742 infoPtr->dwStyle = lpss->styleNew;
8744 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8745 ((lpss->styleNew & WS_HSCROLL) == 0))
8746 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8748 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8749 ((lpss->styleNew & WS_VSCROLL) == 0))
8750 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8752 if (uNewView != uOldView)
8754 SIZE oldIconSize = infoPtr->iconSize;
8755 HIMAGELIST himl;
8757 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8758 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8760 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8761 SetRectEmpty(&infoPtr->rcFocus);
8763 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8764 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8766 if (uNewView == LVS_ICON)
8768 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8770 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8771 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8772 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8775 else if (uNewView == LVS_REPORT)
8777 HDLAYOUT hl;
8778 WINDOWPOS wp;
8780 hl.prc = &infoPtr->rcList;
8781 hl.pwpos = &wp;
8782 Header_Layout(infoPtr->hwndHeader, &hl);
8783 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8786 LISTVIEW_UpdateItemSize(infoPtr);
8789 if (uNewView == LVS_REPORT)
8790 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8792 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8793 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8794 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8796 /* update the size of the client area */
8797 LISTVIEW_UpdateSize(infoPtr);
8799 /* add scrollbars if needed */
8800 LISTVIEW_UpdateScroll(infoPtr);
8802 /* invalidate client area + erase background */
8803 LISTVIEW_InvalidateList(infoPtr);
8805 return 0;
8808 /***
8809 * DESCRIPTION:
8810 * Window procedure of the listview control.
8813 static LRESULT WINAPI
8814 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8816 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8818 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8820 if (!infoPtr && (uMsg != WM_CREATE))
8821 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8823 switch (uMsg)
8825 case LVM_APPROXIMATEVIEWRECT:
8826 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8827 LOWORD(lParam), HIWORD(lParam));
8828 case LVM_ARRANGE:
8829 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8831 /* case LVM_CANCELEDITLABEL: */
8833 case LVM_CREATEDRAGIMAGE:
8834 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8836 case LVM_DELETEALLITEMS:
8837 return LISTVIEW_DeleteAllItems(infoPtr);
8839 case LVM_DELETECOLUMN:
8840 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8842 case LVM_DELETEITEM:
8843 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8845 case LVM_EDITLABELW:
8846 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8848 case LVM_EDITLABELA:
8849 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8851 /* case LVM_ENABLEGROUPVIEW: */
8853 case LVM_ENSUREVISIBLE:
8854 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8856 case LVM_FINDITEMW:
8857 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8859 case LVM_FINDITEMA:
8860 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8862 case LVM_GETBKCOLOR:
8863 return infoPtr->clrBk;
8865 /* case LVM_GETBKIMAGE: */
8867 case LVM_GETCALLBACKMASK:
8868 return infoPtr->uCallbackMask;
8870 case LVM_GETCOLUMNA:
8871 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8873 case LVM_GETCOLUMNW:
8874 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8876 case LVM_GETCOLUMNORDERARRAY:
8877 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8879 case LVM_GETCOLUMNWIDTH:
8880 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8882 case LVM_GETCOUNTPERPAGE:
8883 return LISTVIEW_GetCountPerPage(infoPtr);
8885 case LVM_GETEDITCONTROL:
8886 return (LRESULT)infoPtr->hwndEdit;
8888 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8889 return infoPtr->dwLvExStyle;
8891 /* case LVM_GETGROUPINFO: */
8893 /* case LVM_GETGROUPMETRICS: */
8895 case LVM_GETHEADER:
8896 return (LRESULT)infoPtr->hwndHeader;
8898 case LVM_GETHOTCURSOR:
8899 return (LRESULT)infoPtr->hHotCursor;
8901 case LVM_GETHOTITEM:
8902 return infoPtr->nHotItem;
8904 case LVM_GETHOVERTIME:
8905 return infoPtr->dwHoverTime;
8907 case LVM_GETIMAGELIST:
8908 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8910 /* case LVM_GETINSERTMARK: */
8912 /* case LVM_GETINSERTMARKCOLOR: */
8914 /* case LVM_GETINSERTMARKRECT: */
8916 case LVM_GETISEARCHSTRINGA:
8917 case LVM_GETISEARCHSTRINGW:
8918 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8919 return FALSE;
8921 case LVM_GETITEMA:
8922 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8924 case LVM_GETITEMW:
8925 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8927 case LVM_GETITEMCOUNT:
8928 return infoPtr->nItemCount;
8930 case LVM_GETITEMPOSITION:
8931 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8933 case LVM_GETITEMRECT:
8934 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8936 case LVM_GETITEMSPACING:
8937 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8939 case LVM_GETITEMSTATE:
8940 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8942 case LVM_GETITEMTEXTA:
8943 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8945 case LVM_GETITEMTEXTW:
8946 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8948 case LVM_GETNEXTITEM:
8949 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8951 case LVM_GETNUMBEROFWORKAREAS:
8952 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8953 return 1;
8955 case LVM_GETORIGIN:
8956 if (!lParam) return FALSE;
8957 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8958 return TRUE;
8960 /* case LVM_GETOUTLINECOLOR: */
8962 /* case LVM_GETSELECTEDCOLUMN: */
8964 case LVM_GETSELECTEDCOUNT:
8965 return LISTVIEW_GetSelectedCount(infoPtr);
8967 case LVM_GETSELECTIONMARK:
8968 return infoPtr->nSelectionMark;
8970 case LVM_GETSTRINGWIDTHA:
8971 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8973 case LVM_GETSTRINGWIDTHW:
8974 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8976 case LVM_GETSUBITEMRECT:
8977 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8979 case LVM_GETTEXTBKCOLOR:
8980 return infoPtr->clrTextBk;
8982 case LVM_GETTEXTCOLOR:
8983 return infoPtr->clrText;
8985 /* case LVM_GETTILEINFO: */
8987 /* case LVM_GETTILEVIEWINFO: */
8989 case LVM_GETTOOLTIPS:
8990 if( !infoPtr->hwndToolTip )
8991 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
8992 return (LRESULT)infoPtr->hwndToolTip;
8994 case LVM_GETTOPINDEX:
8995 return LISTVIEW_GetTopIndex(infoPtr);
8997 /*case LVM_GETUNICODEFORMAT:
8998 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8999 return FALSE;*/
9001 /* case LVM_GETVIEW: */
9003 case LVM_GETVIEWRECT:
9004 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9006 case LVM_GETWORKAREAS:
9007 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9008 return FALSE;
9010 /* case LVM_HASGROUP: */
9012 case LVM_HITTEST:
9013 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9015 case LVM_INSERTCOLUMNA:
9016 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9018 case LVM_INSERTCOLUMNW:
9019 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9021 /* case LVM_INSERTGROUP: */
9023 /* case LVM_INSERTGROUPSORTED: */
9025 case LVM_INSERTITEMA:
9026 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9028 case LVM_INSERTITEMW:
9029 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9031 /* case LVM_INSERTMARKHITTEST: */
9033 /* case LVM_ISGROUPVIEWENABLED: */
9035 /* case LVM_MAPIDTOINDEX: */
9037 /* case LVM_MAPINDEXTOID: */
9039 /* case LVM_MOVEGROUP: */
9041 /* case LVM_MOVEITEMTOGROUP: */
9043 case LVM_REDRAWITEMS:
9044 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9046 /* case LVM_REMOVEALLGROUPS: */
9048 /* case LVM_REMOVEGROUP: */
9050 case LVM_SCROLL:
9051 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9053 case LVM_SETBKCOLOR:
9054 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9056 /* case LVM_SETBKIMAGE: */
9058 case LVM_SETCALLBACKMASK:
9059 infoPtr->uCallbackMask = (UINT)wParam;
9060 return TRUE;
9062 case LVM_SETCOLUMNA:
9063 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9065 case LVM_SETCOLUMNW:
9066 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9068 case LVM_SETCOLUMNORDERARRAY:
9069 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9071 case LVM_SETCOLUMNWIDTH:
9072 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9074 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9075 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9077 /* case LVM_SETGROUPINFO: */
9079 /* case LVM_SETGROUPMETRICS: */
9081 case LVM_SETHOTCURSOR:
9082 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9084 case LVM_SETHOTITEM:
9085 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9087 case LVM_SETHOVERTIME:
9088 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9090 case LVM_SETICONSPACING:
9091 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9093 case LVM_SETIMAGELIST:
9094 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9096 /* case LVM_SETINFOTIP: */
9098 /* case LVM_SETINSERTMARK: */
9100 /* case LVM_SETINSERTMARKCOLOR: */
9102 case LVM_SETITEMA:
9103 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9105 case LVM_SETITEMW:
9106 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9108 case LVM_SETITEMCOUNT:
9109 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9111 case LVM_SETITEMPOSITION:
9113 POINT pt;
9114 pt.x = (short)LOWORD(lParam);
9115 pt.y = (short)HIWORD(lParam);
9116 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9119 case LVM_SETITEMPOSITION32:
9120 if (lParam == 0) return FALSE;
9121 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9123 case LVM_SETITEMSTATE:
9124 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9126 case LVM_SETITEMTEXTA:
9127 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9129 case LVM_SETITEMTEXTW:
9130 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9132 /* case LVM_SETOUTLINECOLOR: */
9134 /* case LVM_SETSELECTEDCOLUMN: */
9136 case LVM_SETSELECTIONMARK:
9137 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9139 case LVM_SETTEXTBKCOLOR:
9140 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9142 case LVM_SETTEXTCOLOR:
9143 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9145 /* case LVM_SETTILEINFO: */
9147 /* case LVM_SETTILEVIEWINFO: */
9149 /* case LVM_SETTILEWIDTH: */
9151 case LVM_SETTOOLTIPS:
9152 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9154 /* case LVM_SETUNICODEFORMAT: */
9156 /* case LVM_SETVIEW: */
9158 /* case LVM_SETWORKAREAS: */
9160 /* case LVM_SORTGROUPS: */
9162 case LVM_SORTITEMS:
9163 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9165 /* LVM_SORTITEMSEX: */
9167 case LVM_SUBITEMHITTEST:
9168 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9170 case LVM_UPDATE:
9171 return LISTVIEW_Update(infoPtr, (INT)wParam);
9173 case WM_CHAR:
9174 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9176 case WM_COMMAND:
9177 return LISTVIEW_Command(infoPtr, wParam, lParam);
9179 case WM_CREATE:
9180 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9182 case WM_ERASEBKGND:
9183 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9185 case WM_GETDLGCODE:
9186 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9188 case WM_GETFONT:
9189 return (LRESULT)infoPtr->hFont;
9191 case WM_HSCROLL:
9192 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9194 case WM_KEYDOWN:
9195 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9197 case WM_KILLFOCUS:
9198 return LISTVIEW_KillFocus(infoPtr);
9200 case WM_LBUTTONDBLCLK:
9201 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9203 case WM_LBUTTONDOWN:
9204 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9206 case WM_LBUTTONUP:
9207 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9209 case WM_MOUSEMOVE:
9210 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9212 case WM_MOUSEHOVER:
9213 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9215 case WM_NCDESTROY:
9216 return LISTVIEW_NCDestroy(infoPtr);
9218 case WM_NOTIFY:
9219 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9220 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9221 else return 0;
9223 case WM_NOTIFYFORMAT:
9224 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9226 case WM_PRINTCLIENT:
9227 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9229 case WM_PAINT:
9230 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9232 case WM_RBUTTONDBLCLK:
9233 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9235 case WM_RBUTTONDOWN:
9236 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9238 case WM_RBUTTONUP:
9239 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9241 case WM_SETCURSOR:
9242 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9243 return TRUE;
9244 goto fwd_msg;
9246 case WM_SETFOCUS:
9247 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9249 case WM_SETFONT:
9250 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9252 case WM_SETREDRAW:
9253 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9255 case WM_SIZE:
9256 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9258 case WM_STYLECHANGED:
9259 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9261 case WM_SYSCOLORCHANGE:
9262 COMCTL32_RefreshSysColors();
9263 return 0;
9265 /* case WM_TIMER: */
9267 case WM_VSCROLL:
9268 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9270 case WM_MOUSEWHEEL:
9271 if (wParam & (MK_SHIFT | MK_CONTROL))
9272 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9273 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9275 case WM_WINDOWPOSCHANGED:
9276 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9278 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9279 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9280 LISTVIEW_UpdateSize(infoPtr);
9281 LISTVIEW_UpdateScroll(infoPtr);
9283 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9285 /* case WM_WININICHANGE: */
9287 default:
9288 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9289 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9291 fwd_msg:
9292 /* call default window procedure */
9293 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9298 /***
9299 * DESCRIPTION:
9300 * Registers the window class.
9302 * PARAMETER(S):
9303 * None
9305 * RETURN:
9306 * None
9308 void LISTVIEW_Register(void)
9310 WNDCLASSW wndClass;
9312 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9313 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9314 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9315 wndClass.cbClsExtra = 0;
9316 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9317 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9318 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9319 wndClass.lpszClassName = WC_LISTVIEWW;
9320 RegisterClassW(&wndClass);
9323 /***
9324 * DESCRIPTION:
9325 * Unregisters the window class.
9327 * PARAMETER(S):
9328 * None
9330 * RETURN:
9331 * None
9333 void LISTVIEW_Unregister(void)
9335 UnregisterClassW(WC_LISTVIEWW, NULL);
9338 /***
9339 * DESCRIPTION:
9340 * Handle any WM_COMMAND messages
9342 * PARAMETER(S):
9343 * [I] infoPtr : valid pointer to the listview structure
9344 * [I] wParam : the first message parameter
9345 * [I] lParam : the second message parameter
9347 * RETURN:
9348 * Zero.
9350 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9352 switch (HIWORD(wParam))
9354 case EN_UPDATE:
9357 * Adjust the edit window size
9359 WCHAR buffer[1024];
9360 HDC hdc = GetDC(infoPtr->hwndEdit);
9361 HFONT hFont, hOldFont = 0;
9362 RECT rect;
9363 SIZE sz;
9364 int len;
9366 if (!infoPtr->hwndEdit || !hdc) return 0;
9367 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9368 GetWindowRect(infoPtr->hwndEdit, &rect);
9370 /* Select font to get the right dimension of the string */
9371 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9372 if(hFont != 0)
9374 hOldFont = SelectObject(hdc, hFont);
9377 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9379 TEXTMETRICW textMetric;
9381 /* Add Extra spacing for the next character */
9382 GetTextMetricsW(hdc, &textMetric);
9383 sz.cx += (textMetric.tmMaxCharWidth * 2);
9385 SetWindowPos (
9386 infoPtr->hwndEdit,
9387 HWND_TOP,
9390 sz.cx,
9391 rect.bottom - rect.top,
9392 SWP_DRAWFRAME|SWP_NOMOVE);
9394 if(hFont != 0)
9395 SelectObject(hdc, hOldFont);
9397 ReleaseDC(infoPtr->hwndSelf, hdc);
9399 break;
9402 default:
9403 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9406 return 0;
9410 /***
9411 * DESCRIPTION:
9412 * Subclassed edit control windproc function
9414 * PARAMETER(S):
9415 * [I] hwnd : the edit window handle
9416 * [I] uMsg : the message that is to be processed
9417 * [I] wParam : first message parameter
9418 * [I] lParam : second message parameter
9419 * [I] isW : TRUE if input is Unicode
9421 * RETURN:
9422 * Zero.
9424 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9426 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9427 BOOL cancel = FALSE;
9429 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9430 hwnd, uMsg, wParam, lParam, isW);
9432 switch (uMsg)
9434 case WM_GETDLGCODE:
9435 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9437 case WM_KILLFOCUS:
9438 break;
9440 case WM_DESTROY:
9442 WNDPROC editProc = infoPtr->EditWndProc;
9443 infoPtr->EditWndProc = 0;
9444 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9445 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9448 case WM_KEYDOWN:
9449 if (VK_ESCAPE == (INT)wParam)
9451 cancel = TRUE;
9452 break;
9454 else if (VK_RETURN == (INT)wParam)
9455 break;
9457 default:
9458 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9461 /* kill the edit */
9462 if (infoPtr->hwndEdit)
9464 LPWSTR buffer = NULL;
9466 infoPtr->hwndEdit = 0;
9467 if (!cancel)
9469 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9471 if (len)
9473 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9475 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9476 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9480 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9482 if (buffer) Free(buffer);
9486 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9487 return 0;
9490 /***
9491 * DESCRIPTION:
9492 * Subclassed edit control Unicode windproc function
9494 * PARAMETER(S):
9495 * [I] hwnd : the edit window handle
9496 * [I] uMsg : the message that is to be processed
9497 * [I] wParam : first message parameter
9498 * [I] lParam : second message parameter
9500 * RETURN:
9502 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9504 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9507 /***
9508 * DESCRIPTION:
9509 * Subclassed edit control ANSI windproc function
9511 * PARAMETER(S):
9512 * [I] hwnd : the edit window handle
9513 * [I] uMsg : the message that is to be processed
9514 * [I] wParam : first message parameter
9515 * [I] lParam : second message parameter
9517 * RETURN:
9519 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9521 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9524 /***
9525 * DESCRIPTION:
9526 * Creates a subclassed edit cotrol
9528 * PARAMETER(S):
9529 * [I] infoPtr : valid pointer to the listview structure
9530 * [I] text : initial text for the edit
9531 * [I] style : the window style
9532 * [I] isW : TRUE if input is Unicode
9534 * RETURN:
9536 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9537 INT x, INT y, INT width, INT height, BOOL isW)
9539 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9540 HWND hedit;
9541 SIZE sz;
9542 HDC hdc;
9543 HDC hOldFont=0;
9544 TEXTMETRICW textMetric;
9545 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9547 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9549 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9550 hdc = GetDC(infoPtr->hwndSelf);
9552 /* Select the font to get appropriate metric dimensions */
9553 if(infoPtr->hFont != 0)
9554 hOldFont = SelectObject(hdc, infoPtr->hFont);
9556 /*Get String Length in pixels */
9557 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9559 /*Add Extra spacing for the next character */
9560 GetTextMetricsW(hdc, &textMetric);
9561 sz.cx += (textMetric.tmMaxCharWidth * 2);
9563 if(infoPtr->hFont != 0)
9564 SelectObject(hdc, hOldFont);
9566 ReleaseDC(infoPtr->hwndSelf, hdc);
9567 if (isW)
9568 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9569 else
9570 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9572 if (!hedit) return 0;
9574 infoPtr->EditWndProc = (WNDPROC)
9575 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9576 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9578 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9580 return hedit;