Fixed header dependencies to be fully compatible with the Windows
[wine/multimedia.git] / dlls / comctl32 / listview.c
blob0371e3fe1094ea884bc6b3ffd3f97a087f3732e3
1 /*
2 * Listview control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 Codeweavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * NOTES
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on Oct. 21, 2002, by Dimitrie O. Paun.
29 * Unless otherwise noted, we belive this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
33 * TODO:
35 * Features
36 * -- Hot item handling, mouse hovering
37 * -- Workareas support
38 * -- Tilemode support
39 * -- Groups support
41 * Bugs
42 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs.
44 * -- in LISTVIEW_AddGroupSelection, se whould send LVN_ODSTATECHANGED
45 * -- LVA_SNAPTOGRID not implemented
46 * -- LISTVIEW_ApproximateViewRect partially implemented
47 * -- LISTVIEW_[GS]etColumnOrderArray stubs
48 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
49 * -- LISTVIEW_SetIconSpacing is incomplete
50 * -- LISTVIEW_SortItems is broken
51 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
53 * Speedups
54 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
55 * linear in the number of items in the list, and this is
56 * unacceptable for large lists.
57 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
58 * instead of inserting in the right spot
59 * -- we should keep an ordered array of coordinates in iconic mode
60 * this would allow to frame items (iterator_frameditems),
61 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
63 * Flags
64 * -- LVIF_COLUMNS
65 * -- LVIF_GROUPID
66 * -- LVIF_NORECOMPUTE
68 * States
69 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
70 * -- LVIS_CUT
71 * -- LVIS_DROPHILITED
72 * -- LVIS_OVERLAYMASK
74 * Styles
75 * -- LVS_NOLABELWRAP
76 * -- LVS_NOSCROLL (see Q137520)
77 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
79 * Extended Styles
80 * -- LVS_EX_BORDERSELECT
81 * -- LVS_EX_CHECKBOXES
82 * -- LVS_EX_FLATSB
83 * -- LVS_EX_GRIDLINES
84 * -- LVS_EX_HEADERDRAGDROP
85 * -- LVS_EX_INFOTIP
86 * -- LVS_EX_LABELTIP
87 * -- LVS_EX_MULTIWORKAREAS
88 * -- LVS_EX_ONECLICKACTIVATE
89 * -- LVS_EX_REGIONAL
90 * -- LVS_EX_SIMPLESELECT
91 * -- LVS_EX_SUBITEMIMAGES
92 * -- LVS_EX_TRACKSELECT
93 * -- LVS_EX_TWOCLICKACTIVATE
94 * -- LVS_EX_UNDERLINECOLD
95 * -- LVS_EX_UNDERLINEHOT
97 * Notifications:
98 * -- LVN_BEGINDRAG, LVN_BEGINRDRAG
99 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
100 * -- LVN_GETINFOTIP
101 * -- LVN_HOTTRACK
102 * -- LVN_MARQUEEBEGIN
103 * -- LVN_ODFINDITEM
104 * -- LVN_ODSTATECHANGED
105 * -- LVN_SETDISPINFO
106 * -- NM_HOVER
108 * Messages:
109 * -- LVM_CANCELEDITLABEL
110 * -- LVM_CREATEDRAGIMAGE
111 * -- LVM_ENABLEGROUPVIEW
112 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
113 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
114 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
115 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
116 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
117 * -- LVM_GETINSERTMARKRECT
118 * -- LVM_GETNUMBEROFWORKAREAS
119 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
120 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
121 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
122 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
123 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
124 * -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
125 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
126 * -- LVM_GETVIEW, LVM_SETVIEW
127 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
128 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
129 * -- LVM_INSERTGROUPSORTED
130 * -- LVM_INSERTMARKHITTEST
131 * -- LVM_ISGROUPVIEWENABLED
132 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
133 * -- LVM_MOVEGROUP
134 * -- LVM_MOVEITEMTOGROUP
135 * -- LVM_SETINFOTIP
136 * -- LVM_SETTILEWIDTH
137 * -- LVM_SORTGROUPS
138 * -- LVM_SORTITEMSEX
140 * Known differences in message stream from native control (not known if
141 * these differences cause problems):
142 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
143 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
144 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
145 * processing for "USEDOUBLECLICKTIME".
148 #include "config.h"
149 #include "wine/port.h"
151 #include <assert.h>
152 #include <ctype.h>
153 #include <string.h>
154 #include <stdlib.h>
155 #include <stdarg.h>
156 #include <stdio.h>
158 #include "windef.h"
159 #include "winbase.h"
160 #include "winnt.h"
161 #include "wingdi.h"
162 #include "winuser.h"
163 #include "winnls.h"
164 #include "commctrl.h"
165 #include "comctl32.h"
167 #include "wine/debug.h"
168 #include "wine/unicode.h"
170 WINE_DEFAULT_DEBUG_CHANNEL(listview);
172 /* make sure you set this to 0 for production use! */
173 #define DEBUG_RANGES 1
175 typedef struct tagCOLUMN_INFO
177 RECT rcHeader; /* tracks the header's rectangle */
178 int fmt; /* same as LVCOLUMN.fmt */
179 } COLUMN_INFO;
181 typedef struct tagITEMHDR
183 LPWSTR pszText;
184 INT iImage;
185 } ITEMHDR, *LPITEMHDR;
187 typedef struct tagSUBITEM_INFO
189 ITEMHDR hdr;
190 INT iSubItem;
191 } SUBITEM_INFO;
193 typedef struct tagITEM_INFO
195 ITEMHDR hdr;
196 UINT state;
197 LPARAM lParam;
198 INT iIndent;
199 } ITEM_INFO;
201 typedef struct tagRANGE
203 INT lower;
204 INT upper;
205 } RANGE;
207 typedef struct tagRANGES
209 HDPA hdpa;
210 } *RANGES;
212 typedef struct tagITERATOR
214 INT nItem;
215 INT nSpecial;
216 RANGE range;
217 RANGES ranges;
218 INT index;
219 } ITERATOR;
221 typedef struct tagLISTVIEW_INFO
223 HWND hwndSelf;
224 HBRUSH hBkBrush;
225 COLORREF clrBk;
226 COLORREF clrText;
227 COLORREF clrTextBk;
228 COLORREF clrTextBkDefault;
229 HIMAGELIST himlNormal;
230 HIMAGELIST himlSmall;
231 HIMAGELIST himlState;
232 BOOL bLButtonDown;
233 BOOL bRButtonDown;
234 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
235 INT nItemHeight;
236 INT nItemWidth;
237 RANGES selectionRanges;
238 INT nSelectionMark;
239 INT nHotItem;
240 SHORT notifyFormat;
241 RECT rcList; /* This rectangle is really the window
242 * client rectangle possibly reduced by the
243 * horizontal scroll bar and/or header - see
244 * LISTVIEW_UpdateSize. This rectangle offset
245 * by the LISTVIEW_GetOrigin value is in
246 * client coordinates */
247 SIZE iconSize;
248 SIZE iconSpacing;
249 SIZE iconStateSize;
250 UINT uCallbackMask;
251 HWND hwndHeader;
252 HCURSOR hHotCursor;
253 HFONT hDefaultFont;
254 HFONT hFont;
255 INT ntmHeight; /* Some cached metrics of the font used */
256 INT ntmAveCharWidth; /* by the listview to draw items */
257 BOOL bRedraw; /* Turns on/off repaints & invalidations */
258 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
259 BOOL bFocus;
260 BOOL bDoChangeNotify; /* send change notification messages? */
261 INT nFocusedItem;
262 RECT rcFocus;
263 DWORD dwStyle; /* the cached window GWL_STYLE */
264 DWORD dwLvExStyle; /* extended listview style */
265 INT nItemCount; /* the number of items in the list */
266 HDPA hdpaItems; /* array ITEM_INFO pointers */
267 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
268 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
269 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
270 POINT currIconPos; /* this is the position next icon will be placed */
271 PFNLVCOMPARE pfnCompare;
272 LPARAM lParamSort;
273 HWND hwndEdit;
274 WNDPROC EditWndProc;
275 INT nEditLabelItem;
276 DWORD dwHoverTime;
277 HWND hwndToolTip;
279 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
281 DWORD lastKeyPressTimestamp;
282 WPARAM charCode;
283 INT nSearchParamLength;
284 WCHAR szSearchParam[ MAX_PATH ];
285 BOOL bIsDrawing;
286 } LISTVIEW_INFO;
289 * constants
291 /* How many we debug buffer to allocate */
292 #define DEBUG_BUFFERS 20
293 /* The size of a single debug bbuffer */
294 #define DEBUG_BUFFER_SIZE 256
296 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
297 #define SB_INTERNAL -1
299 /* maximum size of a label */
300 #define DISP_TEXT_SIZE 512
302 /* padding for items in list and small icon display modes */
303 #define WIDTH_PADDING 12
305 /* padding for items in list, report and small icon display modes */
306 #define HEIGHT_PADDING 1
308 /* offset of items in report display mode */
309 #define REPORT_MARGINX 2
311 /* padding for icon in large icon display mode
312 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
313 * that HITTEST will see.
314 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
315 * ICON_TOP_PADDING - sum of the two above.
316 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
317 * LABEL_HOR_PADDING - between text and sides of box
318 * LABEL_VERT_PADDING - between bottom of text and end of box
320 * ICON_LR_PADDING - additional width above icon size.
321 * ICON_LR_HALF - half of the above value
323 #define ICON_TOP_PADDING_NOTHITABLE 2
324 #define ICON_TOP_PADDING_HITABLE 2
325 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
326 #define ICON_BOTTOM_PADDING 4
327 #define LABEL_HOR_PADDING 5
328 #define LABEL_VERT_PADDING 7
329 #define ICON_LR_PADDING 16
330 #define ICON_LR_HALF (ICON_LR_PADDING/2)
332 /* default label width for items in list and small icon display modes */
333 #define DEFAULT_LABEL_WIDTH 40
335 /* default column width for items in list display mode */
336 #define DEFAULT_COLUMN_WIDTH 128
338 /* Size of "line" scroll for V & H scrolls */
339 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
341 /* Padding betwen image and label */
342 #define IMAGE_PADDING 2
344 /* Padding behind the label */
345 #define TRAILING_LABEL_PADDING 12
346 #define TRAILING_HEADER_PADDING 11
348 /* Border for the icon caption */
349 #define CAPTION_BORDER 2
351 /* Standard DrawText flags */
352 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
353 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
354 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
356 /* The time in milliseconds to reset the search in the list */
357 #define KEY_DELAY 450
359 /* Dump the LISTVIEW_INFO structure to the debug channel */
360 #define LISTVIEW_DUMP(iP) do { \
361 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
362 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
363 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
364 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
365 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
366 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
367 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
368 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
369 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
370 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
371 } while(0)
374 * forward declarations
376 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
377 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
378 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
379 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
380 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
381 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
382 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
383 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
384 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
385 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
386 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
387 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
388 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
389 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
390 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
391 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
392 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
393 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
394 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
395 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
396 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
397 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
398 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
399 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
400 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
402 /******** Text handling functions *************************************/
404 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
405 * text string. The string may be ANSI or Unicode, in which case
406 * the boolean isW tells us the type of the string.
408 * The name of the function tell what type of strings it expects:
409 * W: Unicode, T: ANSI/Unicode - function of isW
412 static inline BOOL is_textW(LPCWSTR text)
414 return text != NULL && text != LPSTR_TEXTCALLBACKW;
417 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
419 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
420 return is_textW(text);
423 static inline int textlenT(LPCWSTR text, BOOL isW)
425 return !is_textT(text, isW) ? 0 :
426 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
429 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
431 if (isDestW)
432 if (isSrcW) lstrcpynW(dest, src, max);
433 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
434 else
435 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
436 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
439 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
441 LPWSTR wstr = (LPWSTR)text;
443 if (!isW && is_textT(text, isW))
445 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
446 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
447 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
449 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
450 return wstr;
453 static inline void textfreeT(LPWSTR wstr, BOOL isW)
455 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
459 * dest is a pointer to a Unicode string
460 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
462 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
464 BOOL bResult = TRUE;
466 if (src == LPSTR_TEXTCALLBACKW)
468 if (is_textW(*dest)) COMCTL32_Free(*dest);
469 *dest = LPSTR_TEXTCALLBACKW;
471 else
473 LPWSTR pszText = textdupTtoW(src, isW);
474 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
475 bResult = Str_SetPtrW(dest, pszText);
476 textfreeT(pszText, isW);
478 return bResult;
482 * compares a Unicode to a Unicode/ANSI text string
484 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
486 if (!aw) return bt ? -1 : 0;
487 if (!bt) return aw ? 1 : 0;
488 if (aw == LPSTR_TEXTCALLBACKW)
489 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
490 if (bt != LPSTR_TEXTCALLBACKW)
492 LPWSTR bw = textdupTtoW(bt, isW);
493 int r = bw ? lstrcmpW(aw, bw) : 1;
494 textfreeT(bw, isW);
495 return r;
498 return 1;
501 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
503 int res;
505 n = min(min(n, strlenW(s1)), strlenW(s2));
506 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
507 return res ? res - sizeof(WCHAR) : res;
510 /******** Debugging functions *****************************************/
512 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
514 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
515 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
518 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
520 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
521 n = min(textlenT(text, isW), n);
522 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
525 static char* debug_getbuf()
527 static int index = 0;
528 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
529 return buffers[index++ % DEBUG_BUFFERS];
532 static inline char* debugrange(const RANGE *lprng)
534 if (lprng)
536 char* buf = debug_getbuf();
537 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
538 return buf;
539 } else return "(null)";
542 static inline char* debugpoint(const POINT *lppt)
544 if (lppt)
546 char* buf = debug_getbuf();
547 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
548 return buf;
549 } else return "(null)";
552 static inline char* debugrect(const RECT *rect)
554 if (rect)
556 char* buf = debug_getbuf();
557 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
558 rect->left, rect->top, rect->right, rect->bottom);
559 return buf;
560 } else return "(null)";
563 static char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
565 char* buf = debug_getbuf(), *text = buf;
566 int len, size = DEBUG_BUFFER_SIZE;
568 if (pScrollInfo == NULL) return "(null)";
569 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
570 if (len == -1) goto end; buf += len; size -= len;
571 if (pScrollInfo->fMask & SIF_RANGE)
572 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
573 else len = 0;
574 if (len == -1) goto end; buf += len; size -= len;
575 if (pScrollInfo->fMask & SIF_PAGE)
576 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
577 else len = 0;
578 if (len == -1) goto end; buf += len; size -= len;
579 if (pScrollInfo->fMask & SIF_POS)
580 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
581 else len = 0;
582 if (len == -1) goto end; buf += len; size -= len;
583 if (pScrollInfo->fMask & SIF_TRACKPOS)
584 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
585 else len = 0;
586 if (len == -1) goto end; buf += len; size -= len;
587 goto undo;
588 end:
589 buf = text + strlen(text);
590 undo:
591 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
592 return text;
595 static char* debugnmlistview(const NMLISTVIEW *plvnm)
597 if (plvnm)
599 char* buf = debug_getbuf();
600 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
601 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
602 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
603 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
604 return buf;
605 } else return "(null)";
608 static char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
610 char* buf = debug_getbuf(), *text = buf;
611 int len, size = DEBUG_BUFFER_SIZE;
613 if (lpLVItem == NULL) return "(null)";
614 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
615 if (len == -1) goto end; buf += len; size -= len;
616 if (lpLVItem->mask & LVIF_STATE)
617 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
618 else len = 0;
619 if (len == -1) goto end; buf += len; size -= len;
620 if (lpLVItem->mask & LVIF_TEXT)
621 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
622 else len = 0;
623 if (len == -1) goto end; buf += len; size -= len;
624 if (lpLVItem->mask & LVIF_IMAGE)
625 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
626 else len = 0;
627 if (len == -1) goto end; buf += len; size -= len;
628 if (lpLVItem->mask & LVIF_PARAM)
629 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
630 else len = 0;
631 if (len == -1) goto end; buf += len; size -= len;
632 if (lpLVItem->mask & LVIF_INDENT)
633 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
634 else len = 0;
635 if (len == -1) goto end; buf += len; size -= len;
636 goto undo;
637 end:
638 buf = text + strlen(text);
639 undo:
640 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
641 return text;
644 static char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
646 char* buf = debug_getbuf(), *text = buf;
647 int len, size = DEBUG_BUFFER_SIZE;
649 if (lpColumn == NULL) return "(null)";
650 len = snprintf(buf, size, "{");
651 if (len == -1) goto end; buf += len; size -= len;
652 if (lpColumn->mask & LVCF_SUBITEM)
653 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
654 else len = 0;
655 if (len == -1) goto end; buf += len; size -= len;
656 if (lpColumn->mask & LVCF_FMT)
657 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
658 else len = 0;
659 if (len == -1) goto end; buf += len; size -= len;
660 if (lpColumn->mask & LVCF_WIDTH)
661 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
662 else len = 0;
663 if (len == -1) goto end; buf += len; size -= len;
664 if (lpColumn->mask & LVCF_TEXT)
665 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
666 else len = 0;
667 if (len == -1) goto end; buf += len; size -= len;
668 if (lpColumn->mask & LVCF_IMAGE)
669 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
670 else len = 0;
671 if (len == -1) goto end; buf += len; size -= len;
672 if (lpColumn->mask & LVCF_ORDER)
673 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
674 else len = 0;
675 if (len == -1) goto end; buf += len; size -= len;
676 goto undo;
677 end:
678 buf = text + strlen(text);
679 undo:
680 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
681 return text;
684 static char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
686 if (lpht)
688 char* buf = debug_getbuf();
689 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
690 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
691 return buf;
692 } else return "(null)";
695 /* Return the corresponding text for a given scroll value */
696 static inline LPCSTR debugscrollcode(int nScrollCode)
698 switch(nScrollCode)
700 case SB_LINELEFT: return "SB_LINELEFT";
701 case SB_LINERIGHT: return "SB_LINERIGHT";
702 case SB_PAGELEFT: return "SB_PAGELEFT";
703 case SB_PAGERIGHT: return "SB_PAGERIGHT";
704 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
705 case SB_THUMBTRACK: return "SB_THUMBTRACK";
706 case SB_ENDSCROLL: return "SB_ENDSCROLL";
707 case SB_INTERNAL: return "SB_INTERNAL";
708 default: return "unknown";
713 /******** Notification functions i************************************/
715 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
717 LRESULT result;
719 TRACE("(code=%d)\n", code);
721 pnmh->hwndFrom = infoPtr->hwndSelf;
722 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
723 pnmh->code = code;
724 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
725 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
727 TRACE(" <= %ld\n", result);
729 return result;
732 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
734 NMHDR nmh;
735 return notify_hdr(infoPtr, code, &nmh);
738 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
740 notify(infoPtr, LVN_ITEMACTIVATE);
743 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
745 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
746 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
749 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
751 NMLISTVIEW nmlv;
752 LVITEMW item;
754 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
755 ZeroMemory(&nmlv, sizeof(nmlv));
756 nmlv.iItem = lvht->iItem;
757 nmlv.iSubItem = lvht->iSubItem;
758 nmlv.ptAction = lvht->pt;
759 item.mask = LVIF_PARAM;
760 item.iItem = lvht->iItem;
761 item.iSubItem = 0;
762 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
763 return notify_listview(infoPtr, code, &nmlv);
766 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
768 NMLISTVIEW nmlv;
769 LVITEMW item;
771 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
772 nmlv.iItem = nItem;
773 item.mask = LVIF_PARAM;
774 item.iItem = nItem;
775 item.iSubItem = 0;
776 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
777 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
780 static int get_ansi_notification(INT unicodeNotificationCode)
782 switch (unicodeNotificationCode)
784 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
785 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
786 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
787 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
788 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
789 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
791 ERR("unknown notification %x\n", unicodeNotificationCode);
792 assert(FALSE);
793 return 0;
797 With testing on Windows 2000 it looks like the notify format
798 has nothing to do with this message. It ALWAYS seems to be
799 in ansi format.
801 infoPtr : listview struct
802 notificationCode : *Unicode* notification code
803 pdi : dispinfo structure (can be unicode or ansi)
804 isW : TRUE if dispinfo is Unicode
806 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
808 BOOL bResult = FALSE;
809 BOOL convertToAnsi = FALSE;
810 INT cchTempBufMax = 0, savCchTextMax = 0;
811 LPWSTR pszTempBuf = NULL, savPszText = NULL;
813 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
814 convertToAnsi = isW;
816 if (convertToAnsi)
818 if (notificationCode != LVN_GETDISPINFOW)
820 cchTempBufMax = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText,
821 -1, NULL, 0, NULL, NULL);
823 else
825 cchTempBufMax = pdi->item.cchTextMax;
826 *pdi->item.pszText = 0; /* make sure we don't process garbage */
829 pszTempBuf = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) *
830 cchTempBufMax);
831 if (!pszTempBuf) return FALSE;
833 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR)
834 pszTempBuf, cchTempBufMax, NULL, NULL);
836 savCchTextMax = pdi->item.cchTextMax;
837 savPszText = pdi->item.pszText;
838 pdi->item.pszText = pszTempBuf;
839 pdi->item.cchTextMax = cchTempBufMax;
842 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat !=
843 NFR_ANSI));
845 bResult = notify_hdr(infoPtr, get_ansi_notification(notificationCode),
846 (LPNMHDR)pdi);
848 if (convertToAnsi)
850 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
851 savPszText, savCchTextMax);
852 pdi->item.pszText = savPszText; /* restores our buffer */
853 pdi->item.cchTextMax = savCchTextMax;
854 HeapFree(GetProcessHeap(), 0, pszTempBuf);
856 return bResult;
859 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
860 const RECT *rcBounds, const LVITEMW *lplvItem)
862 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
863 lpnmlvcd->nmcd.hdc = hdc;
864 lpnmlvcd->nmcd.rc = *rcBounds;
865 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
866 lpnmlvcd->clrText = infoPtr->clrText;
867 if (!lplvItem) return;
868 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
869 lpnmlvcd->iSubItem = lplvItem->iSubItem;
870 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
871 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
872 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
873 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
876 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
878 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
879 DWORD result;
881 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
882 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
883 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
884 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
885 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
886 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
887 return result;
890 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
892 /* apprently, for selected items, we have to override the returned values */
893 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
895 if (infoPtr->bFocus)
897 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
898 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
900 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
902 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
903 lpnmlvcd->clrText = comctl32_color.clrBtnText;
907 /* Set the text attributes */
908 if (lpnmlvcd->clrTextBk != CLR_NONE)
910 SetBkMode(hdc, OPAQUE);
911 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
912 SetBkColor(hdc, infoPtr->clrTextBkDefault);
913 else
914 SetBkColor(hdc,lpnmlvcd->clrTextBk);
916 else
917 SetBkMode(hdc, TRANSPARENT);
918 SetTextColor(hdc, lpnmlvcd->clrText);
921 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
923 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
926 /******** Item iterator functions **********************************/
928 static RANGES ranges_create(int count);
929 static void ranges_destroy(RANGES ranges);
930 static BOOL ranges_add(RANGES ranges, RANGE range);
931 static BOOL ranges_del(RANGES ranges, RANGE range);
932 static void ranges_dump(RANGES ranges);
934 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
936 RANGE range = { nItem, nItem + 1 };
938 return ranges_add(ranges, range);
941 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
943 RANGE range = { nItem, nItem + 1 };
945 return ranges_del(ranges, range);
948 /***
949 * ITERATOR DOCUMENTATION
951 * The iterator functions allow for easy, and convenient iteration
952 * over items of iterest in the list. Typically, you create a
953 * iterator, use it, and destroy it, as such:
954 * ITERATOR i;
956 * iterator_xxxitems(&i, ...);
957 * while (iterator_{prev,next}(&i)
959 * //code which uses i.nItem
961 * iterator_destroy(&i);
963 * where xxx is either: framed, or visible.
964 * Note that it is important that the code destroys the iterator
965 * after it's done with it, as the creation of the iterator may
966 * allocate memory, which thus needs to be freed.
968 * You can iterate both forwards, and backwards through the list,
969 * by using iterator_next or iterator_prev respectively.
971 * Lower numbered items are draw on top of higher number items in
972 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
973 * items may overlap). So, to test items, you should use
974 * iterator_next
975 * which lists the items top to bottom (in Z-order).
976 * For drawing items, you should use
977 * iterator_prev
978 * which lists the items bottom to top (in Z-order).
979 * If you keep iterating over the items after the end-of-items
980 * marker (-1) is returned, the iterator will start from the
981 * beginning. Typically, you don't need to test for -1,
982 * because iterator_{next,prev} will return TRUE if more items
983 * are to be iterated over, or FALSE otherwise.
985 * Note: the iterator is defined to be bidirectional. That is,
986 * any number of prev followed by any number of next, or
987 * five versa, should leave the iterator at the same item:
988 * prev * n, next * n = next * n, prev * n
990 * The iterator has a notion of a out-of-order, special item,
991 * which sits at the start of the list. This is used in
992 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
993 * which needs to be first, as it may overlap other items.
995 * The code is a bit messy because we have:
996 * - a special item to deal with
997 * - simple range, or composite range
998 * - empty range.
999 * If you find bugs, or want to add features, please make sure you
1000 * always check/modify *both* iterator_prev, and iterator_next.
1003 /****
1004 * This function iterates through the items in increasing order,
1005 * but prefixed by the special item, then -1. That is:
1006 * special, 1, 2, 3, ..., n, -1.
1007 * Each item is listed only once.
1009 static inline BOOL iterator_next(ITERATOR* i)
1011 if (i->nItem == -1)
1013 i->nItem = i->nSpecial;
1014 if (i->nItem != -1) return TRUE;
1016 if (i->nItem == i->nSpecial)
1018 if (i->ranges) i->index = 0;
1019 goto pickarange;
1022 i->nItem++;
1023 testitem:
1024 if (i->nItem == i->nSpecial) i->nItem++;
1025 if (i->nItem < i->range.upper) return TRUE;
1027 pickarange:
1028 if (i->ranges)
1030 if (i->index < i->ranges->hdpa->nItemCount)
1031 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1032 else goto end;
1034 else if (i->nItem >= i->range.upper) goto end;
1036 i->nItem = i->range.lower;
1037 if (i->nItem >= 0) goto testitem;
1038 end:
1039 i->nItem = -1;
1040 return FALSE;
1043 /****
1044 * This function iterates through the items in decreasing order,
1045 * followed by the special item, then -1. That is:
1046 * n, n-1, ..., 3, 2, 1, special, -1.
1047 * Each item is listed only once.
1049 static inline BOOL iterator_prev(ITERATOR* i)
1051 BOOL start = FALSE;
1053 if (i->nItem == -1)
1055 start = TRUE;
1056 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
1057 goto pickarange;
1059 if (i->nItem == i->nSpecial)
1061 i->nItem = -1;
1062 return FALSE;
1065 testitem:
1066 i->nItem--;
1067 if (i->nItem == i->nSpecial) i->nItem--;
1068 if (i->nItem >= i->range.lower) return TRUE;
1070 pickarange:
1071 if (i->ranges)
1073 if (i->index > 0)
1074 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1075 else goto end;
1077 else if (!start && i->nItem < i->range.lower) goto end;
1079 i->nItem = i->range.upper;
1080 if (i->nItem > 0) goto testitem;
1081 end:
1082 return (i->nItem = i->nSpecial) != -1;
1085 static RANGE iterator_range(ITERATOR* i)
1087 RANGE range;
1089 if (!i->ranges) return i->range;
1091 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1092 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
1093 return range;
1096 /***
1097 * Releases resources associated with this ierator.
1099 static inline void iterator_destroy(ITERATOR* i)
1101 ranges_destroy(i->ranges);
1104 /***
1105 * Create an empty iterator.
1107 static inline BOOL iterator_empty(ITERATOR* i)
1109 ZeroMemory(i, sizeof(*i));
1110 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1111 return TRUE;
1114 /***
1115 * Create an iterator over a range.
1117 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1119 iterator_empty(i);
1120 i->range = range;
1121 return TRUE;
1124 /***
1125 * Create an iterator over a bunch of ranges.
1126 * Please note that the iterator will take ownership of the ranges,
1127 * and will free them upon destruction.
1129 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1131 iterator_empty(i);
1132 i->ranges = ranges;
1133 return TRUE;
1136 /***
1137 * Creates an iterator over the items which intersect lprc.
1139 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1141 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1142 RECT frame = *lprc, rcItem, rcTemp;
1143 POINT Origin;
1145 /* in case we fail, we want to return an empty iterator */
1146 if (!iterator_empty(i)) return FALSE;
1148 LISTVIEW_GetOrigin(infoPtr, &Origin);
1150 TRACE("(lprc=%s)\n", debugrect(lprc));
1151 OffsetRect(&frame, -Origin.x, -Origin.y);
1153 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1155 INT nItem;
1157 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1159 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1160 if (IntersectRect(&rcTemp, &rcItem, lprc))
1161 i->nSpecial = infoPtr->nFocusedItem;
1163 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1164 /* to do better here, we need to have PosX, and PosY sorted */
1165 TRACE("building icon ranges:\n");
1166 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1168 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1169 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1170 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1171 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1172 if (IntersectRect(&rcTemp, &rcItem, &frame))
1173 ranges_additem(i->ranges, nItem);
1175 return TRUE;
1177 else if (uView == LVS_REPORT)
1179 RANGE range;
1181 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1182 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1184 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1185 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1186 if (range.upper <= range.lower) return TRUE;
1187 if (!iterator_rangeitems(i, range)) return FALSE;
1188 TRACE(" report=%s\n", debugrange(&i->range));
1190 else
1192 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1193 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1194 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1195 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1196 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1197 INT lower = nFirstCol * nPerCol + nFirstRow;
1198 RANGE item_range;
1199 INT nCol;
1201 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1202 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1204 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1206 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1207 TRACE("building list ranges:\n");
1208 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1210 item_range.lower = nCol * nPerCol + nFirstRow;
1211 if(item_range.lower >= infoPtr->nItemCount) break;
1212 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1213 TRACE(" list=%s\n", debugrange(&item_range));
1214 ranges_add(i->ranges, item_range);
1218 return TRUE;
1221 /***
1222 * Creates an iterator over the items which intersect the visible region of hdc.
1224 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1226 POINT Origin, Position;
1227 RECT rcItem, rcClip;
1228 INT rgntype;
1230 rgntype = GetClipBox(hdc, &rcClip);
1231 if (rgntype == NULLREGION) return iterator_empty(i);
1232 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1233 if (rgntype == SIMPLEREGION) return TRUE;
1235 /* first deal with the special item */
1236 if (i->nSpecial != -1)
1238 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1239 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1242 /* if we can't deal with the region, we'll just go with the simple range */
1243 LISTVIEW_GetOrigin(infoPtr, &Origin);
1244 TRACE("building visible range:\n");
1245 if (!i->ranges && i->range.lower < i->range.upper)
1247 if (!(i->ranges = ranges_create(50))) return TRUE;
1248 if (!ranges_add(i->ranges, i->range))
1250 ranges_destroy(i->ranges);
1251 i->ranges = 0;
1252 return TRUE;
1256 /* now delete the invisible items from the list */
1257 while(iterator_next(i))
1259 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1260 rcItem.left = Position.x + Origin.x;
1261 rcItem.top = Position.y + Origin.y;
1262 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1263 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1264 if (!RectVisible(hdc, &rcItem))
1265 ranges_delitem(i->ranges, i->nItem);
1267 /* the iterator should restart on the next iterator_next */
1268 TRACE("done\n");
1270 return TRUE;
1273 /******** Misc helper functions ************************************/
1275 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1276 WPARAM wParam, LPARAM lParam, BOOL isW)
1278 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1279 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1282 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1284 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1286 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1287 (uView == LVS_ICON || uView == LVS_SMALLICON);
1290 /******** Internal API functions ************************************/
1292 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1294 static COLUMN_INFO mainItem;
1296 if (nSubItem == 0 && infoPtr->hdpaColumns->nItemCount == 0) return &mainItem;
1297 assert (nSubItem >= 0 && nSubItem < infoPtr->hdpaColumns->nItemCount);
1298 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1301 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1303 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1306 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1308 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1311 /* Listview invalidation functions: use _only_ these functions to invalidate */
1313 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1315 return infoPtr->bRedraw;
1318 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1320 if(!is_redrawing(infoPtr)) return;
1321 TRACE(" invalidating rect=%s\n", debugrect(rect));
1322 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1325 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1327 RECT rcBox;
1329 if(!is_redrawing(infoPtr)) return;
1330 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1331 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1334 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1336 POINT Origin, Position;
1337 RECT rcBox;
1339 if(!is_redrawing(infoPtr)) return;
1340 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1341 LISTVIEW_GetOrigin(infoPtr, &Origin);
1342 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1343 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1344 rcBox.top = 0;
1345 rcBox.bottom = infoPtr->nItemHeight;
1346 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1347 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1350 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1352 LISTVIEW_InvalidateRect(infoPtr, NULL);
1355 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1357 RECT rcCol;
1359 if(!is_redrawing(infoPtr)) return;
1360 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1361 rcCol.top = infoPtr->rcList.top;
1362 rcCol.bottom = infoPtr->rcList.bottom;
1363 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1366 /***
1367 * DESCRIPTION:
1368 * Retrieves the number of items that can fit vertically in the client area.
1370 * PARAMETER(S):
1371 * [I] infoPtr : valid pointer to the listview structure
1373 * RETURN:
1374 * Number of items per row.
1376 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1378 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1380 return max(nListWidth/infoPtr->nItemWidth, 1);
1383 /***
1384 * DESCRIPTION:
1385 * Retrieves the number of items that can fit horizontally in the client
1386 * area.
1388 * PARAMETER(S):
1389 * [I] infoPtr : valid pointer to the listview structure
1391 * RETURN:
1392 * Number of items per column.
1394 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1396 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1398 return max(nListHeight / infoPtr->nItemHeight, 1);
1402 /*************************************************************************
1403 * LISTVIEW_ProcessLetterKeys
1405 * Processes keyboard messages generated by pressing the letter keys
1406 * on the keyboard.
1407 * What this does is perform a case insensitive search from the
1408 * current position with the following quirks:
1409 * - If two chars or more are pressed in quick succession we search
1410 * for the corresponding string (e.g. 'abc').
1411 * - If there is a delay we wipe away the current search string and
1412 * restart with just that char.
1413 * - If the user keeps pressing the same character, whether slowly or
1414 * fast, so that the search string is entirely composed of this
1415 * character ('aaaaa' for instance), then we search for first item
1416 * that starting with that character.
1417 * - If the user types the above character in quick succession, then
1418 * we must also search for the corresponding string ('aaaaa'), and
1419 * go to that string if there is a match.
1421 * PARAMETERS
1422 * [I] hwnd : handle to the window
1423 * [I] charCode : the character code, the actual character
1424 * [I] keyData : key data
1426 * RETURNS
1428 * Zero.
1430 * BUGS
1432 * - The current implementation has a list of characters it will
1433 * accept and it ignores averything else. In particular it will
1434 * ignore accentuated characters which seems to match what
1435 * Windows does. But I'm not sure it makes sense to follow
1436 * Windows there.
1437 * - We don't sound a beep when the search fails.
1439 * SEE ALSO
1441 * TREEVIEW_ProcessLetterKeys
1443 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1445 INT nItem;
1446 INT endidx,idx;
1447 LVITEMW item;
1448 WCHAR buffer[MAX_PATH];
1449 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1451 /* simple parameter checking */
1452 if (!charCode || !keyData) return 0;
1454 /* only allow the valid WM_CHARs through */
1455 if (!isalnum(charCode) &&
1456 charCode != '.' && charCode != '`' && charCode != '!' &&
1457 charCode != '@' && charCode != '#' && charCode != '$' &&
1458 charCode != '%' && charCode != '^' && charCode != '&' &&
1459 charCode != '*' && charCode != '(' && charCode != ')' &&
1460 charCode != '-' && charCode != '_' && charCode != '+' &&
1461 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1462 charCode != '}' && charCode != '[' && charCode != '{' &&
1463 charCode != '/' && charCode != '?' && charCode != '>' &&
1464 charCode != '<' && charCode != ',' && charCode != '~')
1465 return 0;
1467 /* if there's one item or less, there is no where to go */
1468 if (infoPtr->nItemCount <= 1) return 0;
1470 /* update the search parameters */
1471 infoPtr->lastKeyPressTimestamp = GetTickCount();
1472 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1473 if (infoPtr->nSearchParamLength < MAX_PATH)
1474 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1475 if (infoPtr->charCode != charCode)
1476 infoPtr->charCode = charCode = 0;
1477 } else {
1478 infoPtr->charCode=charCode;
1479 infoPtr->szSearchParam[0]=charCode;
1480 infoPtr->nSearchParamLength=1;
1481 /* Redundant with the 1 char string */
1482 charCode=0;
1485 /* and search from the current position */
1486 nItem=-1;
1487 if (infoPtr->nFocusedItem >= 0) {
1488 endidx=infoPtr->nFocusedItem;
1489 idx=endidx;
1490 /* if looking for single character match,
1491 * then we must always move forward
1493 if (infoPtr->nSearchParamLength == 1)
1494 idx++;
1495 } else {
1496 endidx=infoPtr->nItemCount;
1497 idx=0;
1499 do {
1500 if (idx == infoPtr->nItemCount) {
1501 if (endidx == infoPtr->nItemCount || endidx == 0)
1502 break;
1503 idx=0;
1506 /* get item */
1507 item.mask = LVIF_TEXT;
1508 item.iItem = idx;
1509 item.iSubItem = 0;
1510 item.pszText = buffer;
1511 item.cchTextMax = MAX_PATH;
1512 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1514 /* check for a match */
1515 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1516 nItem=idx;
1517 break;
1518 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1519 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1520 /* This would work but we must keep looking for a longer match */
1521 nItem=idx;
1523 idx++;
1524 } while (idx != endidx);
1526 if (nItem != -1)
1527 LISTVIEW_KeySelection(infoPtr, nItem);
1529 return 0;
1532 /*************************************************************************
1533 * LISTVIEW_UpdateHeaderSize [Internal]
1535 * Function to resize the header control
1537 * PARAMS
1538 * [I] hwnd : handle to a window
1539 * [I] nNewScrollPos : scroll pos to set
1541 * RETURNS
1542 * None.
1544 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1546 RECT winRect;
1547 POINT point[2];
1549 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1551 GetWindowRect(infoPtr->hwndHeader, &winRect);
1552 point[0].x = winRect.left;
1553 point[0].y = winRect.top;
1554 point[1].x = winRect.right;
1555 point[1].y = winRect.bottom;
1557 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1558 point[0].x = -nNewScrollPos;
1559 point[1].x += nNewScrollPos;
1561 SetWindowPos(infoPtr->hwndHeader,0,
1562 point[0].x,point[0].y,point[1].x,point[1].y,
1563 SWP_NOZORDER | SWP_NOACTIVATE);
1566 /***
1567 * DESCRIPTION:
1568 * Update the scrollbars. This functions should be called whenever
1569 * the content, size or view changes.
1571 * PARAMETER(S):
1572 * [I] infoPtr : valid pointer to the listview structure
1574 * RETURN:
1575 * None
1577 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1579 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1580 SCROLLINFO horzInfo, vertInfo;
1582 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1584 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1585 horzInfo.cbSize = sizeof(SCROLLINFO);
1586 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1588 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1589 if (uView == LVS_LIST)
1591 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1592 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1594 /* scroll by at least one column per page */
1595 if(horzInfo.nPage < infoPtr->nItemWidth)
1596 horzInfo.nPage = infoPtr->nItemWidth;
1598 horzInfo.nPage /= infoPtr->nItemWidth;
1600 else if (uView == LVS_REPORT)
1602 horzInfo.nMax = infoPtr->nItemWidth;
1604 else /* LVS_ICON, or LVS_SMALLICON */
1606 RECT rcView;
1608 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1611 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1612 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1613 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1614 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1616 /* Setting the horizontal scroll can change the listview size
1617 * (and potentially everything else) so we need to recompute
1618 * everything again for the vertical scroll
1621 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1622 vertInfo.cbSize = sizeof(SCROLLINFO);
1623 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1625 if (uView == LVS_REPORT)
1627 vertInfo.nMax = infoPtr->nItemCount;
1629 /* scroll by at least one page */
1630 if(vertInfo.nPage < infoPtr->nItemHeight)
1631 vertInfo.nPage = infoPtr->nItemHeight;
1633 vertInfo.nPage /= infoPtr->nItemHeight;
1635 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1637 RECT rcView;
1639 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1642 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1643 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1644 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1645 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1647 /* Update the Header Control */
1648 if (uView == LVS_REPORT)
1650 horzInfo.fMask = SIF_POS;
1651 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1652 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1657 /***
1658 * DESCRIPTION:
1659 * Shows/hides the focus rectangle.
1661 * PARAMETER(S):
1662 * [I] infoPtr : valid pointer to the listview structure
1663 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1665 * RETURN:
1666 * None
1668 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1670 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1671 HDC hdc;
1673 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1675 if (infoPtr->nFocusedItem < 0) return;
1677 /* we need some gymnastics in ICON mode to handle large items */
1678 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1680 RECT rcBox;
1682 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1683 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1685 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1686 return;
1690 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1692 /* for some reason, owner draw should work only in report mode */
1693 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1695 DRAWITEMSTRUCT dis;
1696 LVITEMW item;
1698 item.iItem = infoPtr->nFocusedItem;
1699 item.iSubItem = 0;
1700 item.mask = LVIF_PARAM;
1701 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1703 ZeroMemory(&dis, sizeof(dis));
1704 dis.CtlType = ODT_LISTVIEW;
1705 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1706 dis.itemID = item.iItem;
1707 dis.itemAction = ODA_FOCUS;
1708 if (fShow) dis.itemState |= ODS_FOCUS;
1709 dis.hwndItem = infoPtr->hwndSelf;
1710 dis.hDC = hdc;
1711 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1712 dis.itemData = item.lParam;
1714 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1716 else
1718 DrawFocusRect(hdc, &infoPtr->rcFocus);
1720 done:
1721 ReleaseDC(infoPtr->hwndSelf, hdc);
1724 /***
1725 * Invalidates all visible selected items.
1727 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1729 ITERATOR i;
1731 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1732 while(iterator_next(&i))
1734 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1735 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1737 iterator_destroy(&i);
1741 /***
1742 * DESCRIPTION: [INTERNAL]
1743 * Computes an item's (left,top) corner, relative to rcView.
1744 * That is, the position has NOT been made relative to the Origin.
1745 * This is deliberate, to avoid computing the Origin over, and
1746 * over again, when this function is call in a loop. Instead,
1747 * one ca factor the computation of the Origin before the loop,
1748 * and offset the value retured by this function, on every iteration.
1750 * PARAMETER(S):
1751 * [I] infoPtr : valid pointer to the listview structure
1752 * [I] nItem : item number
1753 * [O] lpptOrig : item top, left corner
1755 * RETURN:
1756 * None.
1758 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1760 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1762 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1764 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1766 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1767 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1769 else if (uView == LVS_LIST)
1771 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1772 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1773 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1775 else /* LVS_REPORT */
1777 lpptPosition->x = 0;
1778 lpptPosition->y = nItem * infoPtr->nItemHeight;
1782 /***
1783 * DESCRIPTION: [INTERNAL]
1784 * Compute the rectangles of an item. This is to localize all
1785 * the computations in one place. If you are not interested in some
1786 * of these values, simply pass in a NULL -- the fucntion is smart
1787 * enough to compute only what's necessary. The function computes
1788 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1789 * one, the BOX rectangle. This rectangle is very cheap to compute,
1790 * and is guaranteed to contain all the other rectangles. Computing
1791 * the ICON rect is also cheap, but all the others are potentaily
1792 * expensive. This gives an easy and effective optimization when
1793 * searching (like point inclusion, or rectangle intersection):
1794 * first test against the BOX, and if TRUE, test agains the desired
1795 * rectangle.
1796 * If the function does not have all the necessary information
1797 * to computed the requested rectangles, will crash with a
1798 * failed assertion. This is done so we catch all programming
1799 * errors, given that the function is called only from our code.
1801 * We have the following 'special' meanings for a few fields:
1802 * * If LVIS_FOCUSED is set, we assume the item has the focus
1803 * This is important in ICON mode, where it might get a larger
1804 * then usual rectange
1806 * Please note that subitem support works only in REPORT mode.
1808 * PARAMETER(S):
1809 * [I] infoPtr : valid pointer to the listview structure
1810 * [I] lpLVItem : item to compute the measures for
1811 * [O] lprcBox : ptr to Box rectangle
1812 * The internal LVIR_BOX rectangle
1813 * [0] lprcState : ptr to State icon rectangle
1814 * The internal LVIR_STATE rectangle
1815 * [O] lprcIcon : ptr to Icon rectangle
1816 * Same as LVM_GETITEMRECT with LVIR_ICON
1817 * [O] lprcLabel : ptr to Label rectangle
1818 * Same as LVM_GETITEMRECT with LVIR_LABEL
1820 * RETURN:
1821 * None.
1823 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1824 LPRECT lprcBox, LPRECT lprcState,
1825 LPRECT lprcIcon, LPRECT lprcLabel)
1827 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1828 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1829 RECT Box, State, Icon, Label;
1830 COLUMN_INFO *lpColumnInfo = NULL;
1832 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1834 /* Be smart and try to figure out the minimum we have to do */
1835 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1836 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1838 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1839 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1841 if (lprcLabel) doLabel = TRUE;
1842 if (doLabel || lprcIcon) doIcon = TRUE;
1843 if (doIcon || lprcState) doState = TRUE;
1845 /************************************************************/
1846 /* compute the box rectangle (it should be cheap to do) */
1847 /************************************************************/
1848 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1849 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1851 if (lpLVItem->iSubItem)
1853 Box = lpColumnInfo->rcHeader;
1855 else
1857 Box.left = 0;
1858 Box.right = infoPtr->nItemWidth;
1860 Box.top = 0;
1861 Box.bottom = infoPtr->nItemHeight;
1863 /************************************************************/
1864 /* compute STATEICON bounding box */
1865 /************************************************************/
1866 if (doState)
1868 if (uView == LVS_ICON)
1870 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1871 if (infoPtr->himlNormal)
1872 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1873 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1875 else
1877 /* we need the ident in report mode, if we don't have it, we fail */
1878 State.left = Box.left;
1879 if (uView == LVS_REPORT)
1881 if (lpLVItem->iSubItem == 0)
1883 State.left += REPORT_MARGINX;
1884 assert(lpLVItem->mask & LVIF_INDENT);
1885 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1888 State.top = Box.top;
1890 State.right = State.left;
1891 State.bottom = State.top;
1892 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1894 State.right += infoPtr->iconStateSize.cx;
1895 State.bottom += infoPtr->iconStateSize.cy;
1897 if (lprcState) *lprcState = State;
1898 TRACE(" - state=%s\n", debugrect(&State));
1901 /************************************************************/
1902 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1903 /************************************************************/
1904 if (doIcon)
1906 if (uView == LVS_ICON)
1908 Icon.left = Box.left;
1909 if (infoPtr->himlNormal)
1910 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1911 Icon.top = Box.top + ICON_TOP_PADDING;
1912 Icon.right = Icon.left;
1913 Icon.bottom = Icon.top;
1914 if (infoPtr->himlNormal)
1916 Icon.right += infoPtr->iconSize.cx;
1917 Icon.bottom += infoPtr->iconSize.cy;
1920 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1922 Icon.left = State.right;
1923 Icon.top = Box.top;
1924 Icon.right = Icon.left;
1925 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1926 Icon.right += infoPtr->iconSize.cx;
1927 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1929 if(lprcIcon) *lprcIcon = Icon;
1930 TRACE(" - icon=%s\n", debugrect(&Icon));
1933 /************************************************************/
1934 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1935 /************************************************************/
1936 if (doLabel)
1938 SIZE labelSize = { 0, 0 };
1940 /* calculate how far to the right can the label strech */
1941 Label.right = Box.right;
1942 if (uView == LVS_REPORT)
1944 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1947 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1949 labelSize.cx = infoPtr->nItemWidth;
1950 labelSize.cy = infoPtr->nItemHeight;
1951 goto calc_label;
1954 /* we need the text in non owner draw mode */
1955 assert(lpLVItem->mask & LVIF_TEXT);
1956 if (is_textT(lpLVItem->pszText, TRUE))
1958 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1959 HDC hdc = GetDC(infoPtr->hwndSelf);
1960 HFONT hOldFont = SelectObject(hdc, hFont);
1961 UINT uFormat;
1962 RECT rcText;
1964 /* compute rough rectangle where the label will go */
1965 SetRectEmpty(&rcText);
1966 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1967 rcText.bottom = infoPtr->nItemHeight;
1968 if (uView == LVS_ICON)
1969 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1971 /* now figure out the flags */
1972 if (uView == LVS_ICON)
1973 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1974 else
1975 uFormat = LV_SL_DT_FLAGS;
1977 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1979 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
1980 labelSize.cy = rcText.bottom - rcText.top;
1982 SelectObject(hdc, hOldFont);
1983 ReleaseDC(infoPtr->hwndSelf, hdc);
1986 calc_label:
1987 if (uView == LVS_ICON)
1989 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1990 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1991 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1992 Label.right = Label.left + labelSize.cx;
1993 Label.bottom = Label.top + infoPtr->nItemHeight;
1994 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1996 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1997 labelSize.cy /= infoPtr->ntmHeight;
1998 labelSize.cy = max(labelSize.cy, 1);
1999 labelSize.cy *= infoPtr->ntmHeight;
2001 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2003 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2005 Label.left = Icon.right;
2006 Label.top = Box.top;
2007 Label.right = min(Label.left + labelSize.cx, Label.right);
2008 Label.bottom = Label.top + infoPtr->nItemHeight;
2011 if (lprcLabel) *lprcLabel = Label;
2012 TRACE(" - label=%s\n", debugrect(&Label));
2015 /* Fix the Box if necessary */
2016 if (lprcBox)
2018 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2019 else *lprcBox = Box;
2021 TRACE(" - box=%s\n", debugrect(&Box));
2024 /***
2025 * DESCRIPTION: [INTERNAL]
2027 * PARAMETER(S):
2028 * [I] infoPtr : valid pointer to the listview structure
2029 * [I] nItem : item number
2030 * [O] lprcBox : ptr to Box rectangle
2032 * RETURN:
2033 * None.
2035 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2037 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2038 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2039 POINT Position, Origin;
2040 LVITEMW lvItem;
2042 LISTVIEW_GetOrigin(infoPtr, &Origin);
2043 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2045 /* Be smart and try to figure out the minimum we have to do */
2046 lvItem.mask = 0;
2047 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2048 lvItem.mask |= LVIF_TEXT;
2049 lvItem.iItem = nItem;
2050 lvItem.iSubItem = 0;
2051 lvItem.pszText = szDispText;
2052 lvItem.cchTextMax = DISP_TEXT_SIZE;
2053 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2054 if (uView == LVS_ICON)
2056 lvItem.mask |= LVIF_STATE;
2057 lvItem.stateMask = LVIS_FOCUSED;
2058 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2060 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2062 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2066 /***
2067 * DESCRIPTION:
2068 * Returns the current icon position, and advances it along the top.
2069 * The returned position is not offset by Origin.
2071 * PARAMETER(S):
2072 * [I] infoPtr : valid pointer to the listview structure
2073 * [O] lpPos : will get the current icon position
2075 * RETURN:
2076 * None
2078 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2080 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2082 *lpPos = infoPtr->currIconPos;
2084 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2085 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2087 infoPtr->currIconPos.x = 0;
2088 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2092 /***
2093 * DESCRIPTION:
2094 * Returns the current icon position, and advances it down the left edge.
2095 * The returned position is not offset by Origin.
2097 * PARAMETER(S):
2098 * [I] infoPtr : valid pointer to the listview structure
2099 * [O] lpPos : will get the current icon position
2101 * RETURN:
2102 * None
2104 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2106 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2108 *lpPos = infoPtr->currIconPos;
2110 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2111 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2113 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2114 infoPtr->currIconPos.y = 0;
2118 /***
2119 * DESCRIPTION:
2120 * Moves an icon to the specified position.
2121 * It takes care of invalidating the item, etc.
2123 * PARAMETER(S):
2124 * [I] infoPtr : valid pointer to the listview structure
2125 * [I] nItem : the item to move
2126 * [I] lpPos : the new icon position
2127 * [I] isNew : flags the item as being new
2129 * RETURN:
2130 * Success: TRUE
2131 * Failure: FALSE
2133 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2135 POINT old;
2137 if (!isNew)
2139 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2140 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2142 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2143 LISTVIEW_InvalidateItem(infoPtr, nItem);
2146 /* Allocating a POINTER for every item is too resource intensive,
2147 * so we'll keep the (x,y) in different arrays */
2148 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2149 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2151 LISTVIEW_InvalidateItem(infoPtr, nItem);
2153 return TRUE;
2156 /***
2157 * DESCRIPTION:
2158 * Arranges listview items in icon display mode.
2160 * PARAMETER(S):
2161 * [I] infoPtr : valid pointer to the listview structure
2162 * [I] nAlignCode : alignment code
2164 * RETURN:
2165 * SUCCESS : TRUE
2166 * FAILURE : FALSE
2168 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2170 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2171 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2172 POINT pos;
2173 INT i;
2175 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2177 TRACE("nAlignCode=%d\n", nAlignCode);
2179 if (nAlignCode == LVA_DEFAULT)
2181 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2182 else nAlignCode = LVA_ALIGNTOP;
2185 switch (nAlignCode)
2187 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2188 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2189 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2190 default: return FALSE;
2193 infoPtr->bAutoarrange = TRUE;
2194 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2195 for (i = 0; i < infoPtr->nItemCount; i++)
2197 next_pos(infoPtr, &pos);
2198 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2201 return TRUE;
2204 /***
2205 * DESCRIPTION:
2206 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2208 * PARAMETER(S):
2209 * [I] infoPtr : valid pointer to the listview structure
2210 * [O] lprcView : bounding rectangle
2212 * RETURN:
2213 * SUCCESS : TRUE
2214 * FAILURE : FALSE
2216 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2218 INT i, x, y;
2220 SetRectEmpty(lprcView);
2222 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2224 case LVS_ICON:
2225 case LVS_SMALLICON:
2226 for (i = 0; i < infoPtr->nItemCount; i++)
2228 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2229 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2230 lprcView->right = max(lprcView->right, x);
2231 lprcView->bottom = max(lprcView->bottom, y);
2233 if (infoPtr->nItemCount > 0)
2235 lprcView->right += infoPtr->nItemWidth;
2236 lprcView->bottom += infoPtr->nItemHeight;
2238 break;
2240 case LVS_LIST:
2241 y = LISTVIEW_GetCountPerColumn(infoPtr);
2242 x = infoPtr->nItemCount / y;
2243 if (infoPtr->nItemCount % y) x++;
2244 lprcView->right = x * infoPtr->nItemWidth;
2245 lprcView->bottom = y * infoPtr->nItemHeight;
2246 break;
2248 case LVS_REPORT:
2249 lprcView->right = infoPtr->nItemWidth;
2250 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2251 break;
2255 /***
2256 * DESCRIPTION:
2257 * Retrieves the bounding rectangle of all the items.
2259 * PARAMETER(S):
2260 * [I] infoPtr : valid pointer to the listview structure
2261 * [O] lprcView : bounding rectangle
2263 * RETURN:
2264 * SUCCESS : TRUE
2265 * FAILURE : FALSE
2267 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2269 POINT ptOrigin;
2271 TRACE("(lprcView=%p)\n", lprcView);
2273 if (!lprcView) return FALSE;
2275 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2276 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2277 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2279 TRACE("lprcView=%s\n", debugrect(lprcView));
2281 return TRUE;
2284 /***
2285 * DESCRIPTION:
2286 * Retrieves the subitem pointer associated with the subitem index.
2288 * PARAMETER(S):
2289 * [I] hdpaSubItems : DPA handle for a specific item
2290 * [I] nSubItem : index of subitem
2292 * RETURN:
2293 * SUCCESS : subitem pointer
2294 * FAILURE : NULL
2296 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2298 SUBITEM_INFO *lpSubItem;
2299 INT i;
2301 /* we should binary search here if need be */
2302 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2304 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2305 if (lpSubItem->iSubItem == nSubItem)
2306 return lpSubItem;
2309 return NULL;
2313 /***
2314 * DESCRIPTION:
2315 * Caclulates the desired item width.
2317 * PARAMETER(S):
2318 * [I] infoPtr : valid pointer to the listview structure
2320 * RETURN:
2321 * The desired item width.
2323 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2325 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2326 INT nItemWidth = 0;
2328 TRACE("uView=%d\n", uView);
2330 if (uView == LVS_ICON)
2331 nItemWidth = infoPtr->iconSpacing.cx;
2332 else if (uView == LVS_REPORT)
2334 RECT rcHeader;
2336 if (infoPtr->hdpaColumns->nItemCount > 0)
2338 LISTVIEW_GetHeaderRect(infoPtr, infoPtr->hdpaColumns->nItemCount - 1, &rcHeader);
2339 nItemWidth = rcHeader.right;
2342 else /* LVS_SMALLICON, or LVS_LIST */
2344 INT i;
2346 for (i = 0; i < infoPtr->nItemCount; i++)
2347 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2349 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2350 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2352 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2355 return max(nItemWidth, 1);
2358 /***
2359 * DESCRIPTION:
2360 * Caclulates the desired item height.
2362 * PARAMETER(S):
2363 * [I] infoPtr : valid pointer to the listview structure
2365 * RETURN:
2366 * The desired item height.
2368 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2370 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2371 INT nItemHeight;
2373 TRACE("uView=%d\n", uView);
2375 if (uView == LVS_ICON)
2376 nItemHeight = infoPtr->iconSpacing.cy;
2377 else
2379 nItemHeight = infoPtr->ntmHeight;
2380 if (infoPtr->himlState)
2381 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2382 if (infoPtr->himlSmall)
2383 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2384 if (infoPtr->himlState || infoPtr->himlSmall)
2385 nItemHeight += HEIGHT_PADDING;
2388 return max(nItemHeight, 1);
2391 /***
2392 * DESCRIPTION:
2393 * Updates the width, and height of an item.
2395 * PARAMETER(S):
2396 * [I] infoPtr : valid pointer to the listview structure
2398 * RETURN:
2399 * None.
2401 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2403 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2404 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2408 /***
2409 * DESCRIPTION:
2410 * Retrieves and saves important text metrics info for the current
2411 * Listview font.
2413 * PARAMETER(S):
2414 * [I] infoPtr : valid pointer to the listview structure
2417 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2419 HDC hdc = GetDC(infoPtr->hwndSelf);
2420 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2421 HFONT hOldFont = SelectObject(hdc, hFont);
2422 TEXTMETRICW tm;
2424 if (GetTextMetricsW(hdc, &tm))
2426 infoPtr->ntmHeight = tm.tmHeight;
2427 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2429 SelectObject(hdc, hOldFont);
2430 ReleaseDC(infoPtr->hwndSelf, hdc);
2432 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2435 /***
2436 * DESCRIPTION:
2437 * A compare function for ranges
2439 * PARAMETER(S)
2440 * [I] range1 : pointer to range 1;
2441 * [I] range2 : pointer to range 2;
2442 * [I] flags : flags
2444 * RETURNS:
2445 * > 0 : if range 1 > range 2
2446 * < 0 : if range 2 > range 1
2447 * = 0 : if range intersects range 2
2449 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2451 INT cmp;
2453 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2454 cmp = -1;
2455 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2456 cmp = 1;
2457 else
2458 cmp = 0;
2460 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2462 return cmp;
2465 #if DEBUG_RANGES
2466 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2467 #else
2468 #define ranges_check(ranges, desc) do { } while(0)
2469 #endif
2471 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2473 INT i;
2474 RANGE *prev, *curr;
2476 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2477 assert (ranges);
2478 assert (ranges->hdpa->nItemCount >= 0);
2479 ranges_dump(ranges);
2480 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2481 if (ranges->hdpa->nItemCount > 0)
2482 assert (prev->lower >= 0 && prev->lower < prev->upper);
2483 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2485 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2486 assert (prev->upper <= curr->lower);
2487 assert (curr->lower < curr->upper);
2488 prev = curr;
2490 TRACE("--- Done checking---\n");
2493 static RANGES ranges_create(int count)
2495 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2496 if (!ranges) return NULL;
2497 ranges->hdpa = DPA_Create(count);
2498 if (ranges->hdpa) return ranges;
2499 COMCTL32_Free(ranges);
2500 return NULL;
2503 static void ranges_clear(RANGES ranges)
2505 INT i;
2507 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2508 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2509 DPA_DeleteAllPtrs(ranges->hdpa);
2513 static void ranges_destroy(RANGES ranges)
2515 if (!ranges) return;
2516 ranges_clear(ranges);
2517 DPA_Destroy(ranges->hdpa);
2518 COMCTL32_Free(ranges);
2521 static RANGES ranges_clone(RANGES ranges)
2523 RANGES clone;
2524 INT i;
2526 if (!(clone = ranges_create(ranges->hdpa->nItemCount))) goto fail;
2528 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2530 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2531 if (!newrng) goto fail;
2532 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2533 DPA_SetPtr(clone->hdpa, i, newrng);
2535 return clone;
2537 fail:
2538 TRACE ("clone failed\n");
2539 ranges_destroy(clone);
2540 return NULL;
2543 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2545 INT i;
2547 for (i = 0; i < sub->hdpa->nItemCount; i++)
2548 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2550 return ranges;
2553 static void ranges_dump(RANGES ranges)
2555 INT i;
2557 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2558 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2561 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2563 RANGE srchrng = { nItem, nItem + 1 };
2565 TRACE("(nItem=%d)\n", nItem);
2566 ranges_check(ranges, "before contain");
2567 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2570 static INT ranges_itemcount(RANGES ranges)
2572 INT i, count = 0;
2574 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2576 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2577 count += sel->upper - sel->lower;
2580 return count;
2583 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2585 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2586 INT index;
2588 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2589 if (index == -1) return TRUE;
2591 for (; index < ranges->hdpa->nItemCount; index++)
2593 chkrng = DPA_GetPtr(ranges->hdpa, index);
2594 if (chkrng->lower >= nItem)
2595 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2596 if (chkrng->upper > nItem)
2597 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2599 return TRUE;
2602 static BOOL ranges_add(RANGES ranges, RANGE range)
2604 RANGE srchrgn;
2605 INT index;
2607 TRACE("(%s)\n", debugrange(&range));
2608 ranges_check(ranges, "before add");
2610 /* try find overlapping regions first */
2611 srchrgn.lower = range.lower - 1;
2612 srchrgn.upper = range.upper + 1;
2613 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2615 if (index == -1)
2617 RANGE *newrgn;
2619 TRACE("Adding new range\n");
2621 /* create the brand new range to insert */
2622 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2623 if(!newrgn) goto fail;
2624 *newrgn = range;
2626 /* figure out where to insert it */
2627 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2628 TRACE("index=%d\n", index);
2629 if (index == -1) index = 0;
2631 /* and get it over with */
2632 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2634 COMCTL32_Free(newrgn);
2635 goto fail;
2638 else
2640 RANGE *chkrgn, *mrgrgn;
2641 INT fromindex, mergeindex;
2643 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2644 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2646 chkrgn->lower = min(range.lower, chkrgn->lower);
2647 chkrgn->upper = max(range.upper, chkrgn->upper);
2649 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2651 /* merge now common anges */
2652 fromindex = 0;
2653 srchrgn.lower = chkrgn->lower - 1;
2654 srchrgn.upper = chkrgn->upper + 1;
2658 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2659 if (mergeindex == -1) break;
2660 if (mergeindex == index)
2662 fromindex = index + 1;
2663 continue;
2666 TRACE("Merge with index %i\n", mergeindex);
2668 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2669 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2670 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2671 COMCTL32_Free(mrgrgn);
2672 DPA_DeletePtr(ranges->hdpa, mergeindex);
2673 if (mergeindex < index) index --;
2674 } while(1);
2677 ranges_check(ranges, "after add");
2678 return TRUE;
2680 fail:
2681 ranges_check(ranges, "failed add");
2682 return FALSE;
2685 static BOOL ranges_del(RANGES ranges, RANGE range)
2687 RANGE *chkrgn;
2688 INT index;
2690 TRACE("(%s)\n", debugrange(&range));
2691 ranges_check(ranges, "before del");
2693 /* we don't use DPAS_SORTED here, since we need *
2694 * to find the first overlapping range */
2695 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2696 while(index != -1)
2698 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2700 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2702 /* case 1: Same range */
2703 if ( (chkrgn->upper == range.upper) &&
2704 (chkrgn->lower == range.lower) )
2706 DPA_DeletePtr(ranges->hdpa, index);
2707 break;
2709 /* case 2: engulf */
2710 else if ( (chkrgn->upper <= range.upper) &&
2711 (chkrgn->lower >= range.lower) )
2713 DPA_DeletePtr(ranges->hdpa, index);
2715 /* case 3: overlap upper */
2716 else if ( (chkrgn->upper <= range.upper) &&
2717 (chkrgn->lower < range.lower) )
2719 chkrgn->upper = range.lower;
2721 /* case 4: overlap lower */
2722 else if ( (chkrgn->upper > range.upper) &&
2723 (chkrgn->lower >= range.lower) )
2725 chkrgn->lower = range.upper;
2726 break;
2728 /* case 5: fully internal */
2729 else
2731 RANGE tmprgn = *chkrgn, *newrgn;
2733 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2734 newrgn->lower = chkrgn->lower;
2735 newrgn->upper = range.lower;
2736 chkrgn->lower = range.upper;
2737 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2739 COMCTL32_Free(newrgn);
2740 goto fail;
2742 chkrgn = &tmprgn;
2743 break;
2746 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2749 ranges_check(ranges, "after del");
2750 return TRUE;
2752 fail:
2753 ranges_check(ranges, "failed del");
2754 return FALSE;
2757 /***
2758 * DESCRIPTION:
2759 * Removes all selection ranges
2761 * Parameters(s):
2762 * [I] infoPtr : valid pointer to the listview structure
2763 * [I] toSkip : item range to skip removing the selection
2765 * RETURNS:
2766 * SUCCESS : TRUE
2767 * FAILURE : TRUE
2769 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2771 LVITEMW lvItem;
2772 ITERATOR i;
2773 RANGES clone;
2775 TRACE("()\n");
2777 lvItem.state = 0;
2778 lvItem.stateMask = LVIS_SELECTED;
2780 /* need to clone the DPA because callbacks can change it */
2781 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2782 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2783 while(iterator_next(&i))
2784 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2785 /* note that the iterator destructor will free the cloned range */
2786 iterator_destroy(&i);
2788 return TRUE;
2791 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2793 RANGES toSkip;
2795 if (!(toSkip = ranges_create(1))) return FALSE;
2796 if (nItem != -1) ranges_additem(toSkip, nItem);
2797 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2798 ranges_destroy(toSkip);
2799 return TRUE;
2802 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2804 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2807 /***
2808 * DESCRIPTION:
2809 * Retrieves the number of items that are marked as selected.
2811 * PARAMETER(S):
2812 * [I] infoPtr : valid pointer to the listview structure
2814 * RETURN:
2815 * Number of items selected.
2817 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2819 INT nSelectedCount = 0;
2821 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2823 INT i;
2824 for (i = 0; i < infoPtr->nItemCount; i++)
2826 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2827 nSelectedCount++;
2830 else
2831 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2833 TRACE("nSelectedCount=%d\n", nSelectedCount);
2834 return nSelectedCount;
2837 /***
2838 * DESCRIPTION:
2839 * Manages the item focus.
2841 * PARAMETER(S):
2842 * [I] infoPtr : valid pointer to the listview structure
2843 * [I] nItem : item index
2845 * RETURN:
2846 * TRUE : focused item changed
2847 * FALSE : focused item has NOT changed
2849 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2851 INT oldFocus = infoPtr->nFocusedItem;
2852 LVITEMW lvItem;
2854 if (nItem == infoPtr->nFocusedItem) return FALSE;
2856 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2857 lvItem.stateMask = LVIS_FOCUSED;
2858 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2860 return oldFocus != infoPtr->nFocusedItem;
2863 /* Helper function for LISTVIEW_ShiftIndices *only* */
2864 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2866 if (nShiftItem < nItem) return nShiftItem;
2868 if (nShiftItem > nItem) return nShiftItem + direction;
2870 if (direction > 0) return nShiftItem + direction;
2872 return min(nShiftItem, infoPtr->nItemCount - 1);
2876 * DESCRIPTION:
2877 * Updates the various indices after an item has been inserted or deleted.
2879 * PARAMETER(S):
2880 * [I] infoPtr : valid pointer to the listview structure
2881 * [I] nItem : item index
2882 * [I] direction : Direction of shift, +1 or -1.
2884 * RETURN:
2885 * None
2887 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2889 INT nNewFocus;
2890 BOOL bOldChange;
2892 /* temporarily disable change notification while shifting items */
2893 bOldChange = infoPtr->bDoChangeNotify;
2894 infoPtr->bDoChangeNotify = FALSE;
2896 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2898 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2900 assert(abs(direction) == 1);
2902 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2904 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2905 if (nNewFocus != infoPtr->nFocusedItem)
2906 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2908 /* But we are not supposed to modify nHotItem! */
2910 infoPtr->bDoChangeNotify = bOldChange;
2915 * DESCRIPTION:
2916 * Adds a block of selections.
2918 * PARAMETER(S):
2919 * [I] infoPtr : valid pointer to the listview structure
2920 * [I] nItem : item index
2922 * RETURN:
2923 * None
2925 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2927 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2928 INT nLast = max(infoPtr->nSelectionMark, nItem);
2929 INT i;
2930 LVITEMW item;
2932 if (nFirst == -1) nFirst = nItem;
2934 item.state = LVIS_SELECTED;
2935 item.stateMask = LVIS_SELECTED;
2937 /* FIXME: this is not correct LVS_OWNERDATA
2938 * setting the item states individually will generate
2939 * a LVN_ITEMCHANGED notification for each one. Instead,
2940 * we have to send a LVN_ODSTATECHANGED notification.
2941 * See MSDN documentation for LVN_ITEMCHANGED.
2943 for (i = nFirst; i <= nLast; i++)
2944 LISTVIEW_SetItemState(infoPtr,i,&item);
2948 /***
2949 * DESCRIPTION:
2950 * Sets a single group selection.
2952 * PARAMETER(S):
2953 * [I] infoPtr : valid pointer to the listview structure
2954 * [I] nItem : item index
2956 * RETURN:
2957 * None
2959 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2961 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2962 RANGES selection;
2963 LVITEMW item;
2964 ITERATOR i;
2966 if (!(selection = ranges_create(100))) return;
2968 item.state = LVIS_SELECTED;
2969 item.stateMask = LVIS_SELECTED;
2971 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2973 if (infoPtr->nSelectionMark == -1)
2975 infoPtr->nSelectionMark = nItem;
2976 ranges_additem(selection, nItem);
2978 else
2980 RANGE sel;
2982 sel.lower = min(infoPtr->nSelectionMark, nItem);
2983 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2984 ranges_add(selection, sel);
2987 else
2989 RECT rcItem, rcSel, rcSelMark;
2990 POINT ptItem;
2992 rcItem.left = LVIR_BOUNDS;
2993 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2994 rcSelMark.left = LVIR_BOUNDS;
2995 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2996 UnionRect(&rcSel, &rcItem, &rcSelMark);
2997 iterator_frameditems(&i, infoPtr, &rcSel);
2998 while(iterator_next(&i))
3000 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3001 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3003 iterator_destroy(&i);
3006 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3007 iterator_rangesitems(&i, selection);
3008 while(iterator_next(&i))
3009 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3010 /* this will also destroy the selection */
3011 iterator_destroy(&i);
3013 LISTVIEW_SetItemFocus(infoPtr, nItem);
3016 /***
3017 * DESCRIPTION:
3018 * Sets a single selection.
3020 * PARAMETER(S):
3021 * [I] infoPtr : valid pointer to the listview structure
3022 * [I] nItem : item index
3024 * RETURN:
3025 * None
3027 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3029 LVITEMW lvItem;
3031 TRACE("nItem=%d\n", nItem);
3033 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3035 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3036 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3037 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3039 infoPtr->nSelectionMark = nItem;
3042 /***
3043 * DESCRIPTION:
3044 * Set selection(s) with keyboard.
3046 * PARAMETER(S):
3047 * [I] infoPtr : valid pointer to the listview structure
3048 * [I] nItem : item index
3050 * RETURN:
3051 * SUCCESS : TRUE (needs to be repainted)
3052 * FAILURE : FALSE (nothing has changed)
3054 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3056 /* FIXME: pass in the state */
3057 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3058 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3059 BOOL bResult = FALSE;
3061 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3063 if (infoPtr->dwStyle & LVS_SINGLESEL)
3065 bResult = TRUE;
3066 LISTVIEW_SetSelection(infoPtr, nItem);
3068 else
3070 if (wShift)
3072 bResult = TRUE;
3073 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3075 else if (wCtrl)
3077 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3079 else
3081 bResult = TRUE;
3082 LISTVIEW_SetSelection(infoPtr, nItem);
3085 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3088 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3089 return bResult;
3093 /***
3094 * DESCRIPTION:
3095 * Called when the mouse is being actively tracked and has hovered for a specified
3096 * amount of time
3098 * PARAMETER(S):
3099 * [I] infoPtr : valid pointer to the listview structure
3100 * [I] fwKeys : key indicator
3101 * [I] pts : mouse position
3103 * RETURN:
3104 * 0 if the message was processed, non-zero if there was an error
3106 * INFO:
3107 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3108 * over the item for a certain period of time.
3111 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3113 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3114 /* FIXME: select the item!!! */
3115 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3117 return 0;
3120 /***
3121 * DESCRIPTION:
3122 * Called whenever WM_MOUSEMOVE is received.
3124 * PARAMETER(S):
3125 * [I] infoPtr : valid pointer to the listview structure
3126 * [I] fwKeys : key indicator
3127 * [I] pts : mouse position
3129 * RETURN:
3130 * 0 if the message is processed, non-zero if there was an error
3132 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3134 TRACKMOUSEEVENT trackinfo;
3136 /* see if we are supposed to be tracking mouse hovering */
3137 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3138 /* fill in the trackinfo struct */
3139 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3140 trackinfo.dwFlags = TME_QUERY;
3141 trackinfo.hwndTrack = infoPtr->hwndSelf;
3142 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3144 /* see if we are already tracking this hwnd */
3145 _TrackMouseEvent(&trackinfo);
3147 if(!(trackinfo.dwFlags & TME_HOVER)) {
3148 trackinfo.dwFlags = TME_HOVER;
3150 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3151 _TrackMouseEvent(&trackinfo);
3155 return 0;
3159 /***
3160 * Tests wheather the item is assignable to a list with style lStyle
3162 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3164 if ( (lpLVItem->mask & LVIF_TEXT) &&
3165 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3166 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3168 return TRUE;
3172 /***
3173 * DESCRIPTION:
3174 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3176 * PARAMETER(S):
3177 * [I] infoPtr : valid pointer to the listview structure
3178 * [I] lpLVItem : valid pointer to new item atttributes
3179 * [I] isNew : the item being set is being inserted
3180 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3181 * [O] bChanged : will be set to TRUE if the item really changed
3183 * RETURN:
3184 * SUCCESS : TRUE
3185 * FAILURE : FALSE
3187 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3189 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3190 ITEM_INFO *lpItem;
3191 NMLISTVIEW nmlv;
3192 UINT uChanged = 0;
3193 LVITEMW item;
3195 TRACE("()\n");
3197 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3199 if (lpLVItem->mask == 0) return TRUE;
3201 if (infoPtr->dwStyle & LVS_OWNERDATA)
3203 /* a virtual listview we stores only selection and focus */
3204 if (lpLVItem->mask & ~LVIF_STATE)
3205 return FALSE;
3206 lpItem = NULL;
3208 else
3210 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3211 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3212 assert (lpItem);
3215 /* we need to get the lParam and state of the item */
3216 item.iItem = lpLVItem->iItem;
3217 item.iSubItem = lpLVItem->iSubItem;
3218 item.mask = LVIF_STATE | LVIF_PARAM;
3219 item.stateMask = ~0;
3220 item.state = 0;
3221 item.lParam = 0;
3222 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3224 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3225 /* determine what fields will change */
3226 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3227 uChanged |= LVIF_STATE;
3229 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3230 uChanged |= LVIF_IMAGE;
3232 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3233 uChanged |= LVIF_PARAM;
3235 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3236 uChanged |= LVIF_INDENT;
3238 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3239 uChanged |= LVIF_TEXT;
3241 TRACE("uChanged=0x%x\n", uChanged);
3242 if (!uChanged) return TRUE;
3243 *bChanged = TRUE;
3245 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3246 nmlv.iItem = lpLVItem->iItem;
3247 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3248 nmlv.uOldState = item.state;
3249 nmlv.uChanged = uChanged;
3250 nmlv.lParam = item.lParam;
3252 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3253 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3254 /* are enabled */
3255 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3256 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3257 return FALSE;
3259 /* copy information */
3260 if (lpLVItem->mask & LVIF_TEXT)
3261 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3263 if (lpLVItem->mask & LVIF_IMAGE)
3264 lpItem->hdr.iImage = lpLVItem->iImage;
3266 if (lpLVItem->mask & LVIF_PARAM)
3267 lpItem->lParam = lpLVItem->lParam;
3269 if (lpLVItem->mask & LVIF_INDENT)
3270 lpItem->iIndent = lpLVItem->iIndent;
3272 if (uChanged & LVIF_STATE)
3274 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3276 lpItem->state &= ~lpLVItem->stateMask;
3277 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3279 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3281 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3282 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3284 else if (lpLVItem->stateMask & LVIS_SELECTED)
3285 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3287 /* if we are asked to change focus, and we manage it, do it */
3288 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3290 if (lpLVItem->state & LVIS_FOCUSED)
3292 LISTVIEW_SetItemFocus(infoPtr, -1);
3293 infoPtr->nFocusedItem = lpLVItem->iItem;
3294 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3296 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3297 infoPtr->nFocusedItem = -1;
3301 /* if we're inserting the item, we're done */
3302 if (isNew) return TRUE;
3304 /* send LVN_ITEMCHANGED notification */
3305 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3306 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3308 return TRUE;
3311 /***
3312 * DESCRIPTION:
3313 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3315 * PARAMETER(S):
3316 * [I] infoPtr : valid pointer to the listview structure
3317 * [I] lpLVItem : valid pointer to new subitem atttributes
3318 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3319 * [O] bChanged : will be set to TRUE if the item really changed
3321 * RETURN:
3322 * SUCCESS : TRUE
3323 * FAILURE : FALSE
3325 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3327 HDPA hdpaSubItems;
3328 SUBITEM_INFO *lpSubItem;
3330 /* we do not support subitems for virtual listviews */
3331 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3333 /* set subitem only if column is present */
3334 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3336 /* First do some sanity checks */
3337 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3338 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3340 /* get the subitem structure, and create it if not there */
3341 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3342 assert (hdpaSubItems);
3344 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3345 if (!lpSubItem)
3347 SUBITEM_INFO *tmpSubItem;
3348 INT i;
3350 lpSubItem = (SUBITEM_INFO *)COMCTL32_Alloc(sizeof(SUBITEM_INFO));
3351 if (!lpSubItem) return FALSE;
3352 /* we could binary search here, if need be...*/
3353 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3355 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3356 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3358 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3360 COMCTL32_Free(lpSubItem);
3361 return FALSE;
3363 lpSubItem->iSubItem = lpLVItem->iSubItem;
3364 *bChanged = TRUE;
3367 if (lpLVItem->mask & LVIF_IMAGE)
3368 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3370 lpSubItem->hdr.iImage = lpLVItem->iImage;
3371 *bChanged = TRUE;
3374 if (lpLVItem->mask & LVIF_TEXT)
3375 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3377 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3378 *bChanged = TRUE;
3381 return TRUE;
3384 /***
3385 * DESCRIPTION:
3386 * Sets item attributes.
3388 * PARAMETER(S):
3389 * [I] infoPtr : valid pointer to the listview structure
3390 * [I] lpLVItem : new item atttributes
3391 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3393 * RETURN:
3394 * SUCCESS : TRUE
3395 * FAILURE : FALSE
3397 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3399 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3400 LPWSTR pszText = NULL;
3401 BOOL bResult, bChanged = FALSE;
3403 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3405 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3406 return FALSE;
3408 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3409 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3411 pszText = lpLVItem->pszText;
3412 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3415 /* actually set the fields */
3416 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3418 if (lpLVItem->iSubItem)
3419 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3420 else
3421 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3423 /* redraw item, if necessary */
3424 if (bChanged && !infoPtr->bIsDrawing)
3426 /* this little optimization eliminates some nasty flicker */
3427 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3428 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3429 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3430 else
3431 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3433 /* restore text */
3434 if (pszText)
3436 textfreeT(lpLVItem->pszText, isW);
3437 ((LVITEMW *)lpLVItem)->pszText = pszText;
3440 return bResult;
3443 /***
3444 * DESCRIPTION:
3445 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3447 * PARAMETER(S):
3448 * [I] infoPtr : valid pointer to the listview structure
3450 * RETURN:
3451 * item index
3453 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3455 LONG lStyle = infoPtr->dwStyle;
3456 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3457 INT nItem = 0;
3458 SCROLLINFO scrollInfo;
3460 scrollInfo.cbSize = sizeof(SCROLLINFO);
3461 scrollInfo.fMask = SIF_POS;
3463 if (uView == LVS_LIST)
3465 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3466 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3468 else if (uView == LVS_REPORT)
3470 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3471 nItem = scrollInfo.nPos;
3473 else
3475 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3476 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3479 TRACE("nItem=%d\n", nItem);
3481 return nItem;
3485 /***
3486 * DESCRIPTION:
3487 * Erases the background of the given rectangle
3489 * PARAMETER(S):
3490 * [I] infoPtr : valid pointer to the listview structure
3491 * [I] hdc : device context handle
3492 * [I] lprcBox : clipping rectangle
3494 * RETURN:
3495 * Success: TRUE
3496 * Failure: FALSE
3498 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3500 if (!infoPtr->hBkBrush) return FALSE;
3502 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3504 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3507 /***
3508 * DESCRIPTION:
3509 * Draws an item.
3511 * PARAMETER(S):
3512 * [I] infoPtr : valid pointer to the listview structure
3513 * [I] hdc : device context handle
3514 * [I] nItem : item index
3515 * [I] nSubItem : subitem index
3516 * [I] pos : item position in client coordinates
3517 * [I] cdmode : custom draw mode
3519 * RETURN:
3520 * Success: TRUE
3521 * Failure: FALSE
3523 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3525 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3526 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3527 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3528 DWORD cdsubitemmode = CDRF_DODEFAULT;
3529 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3530 NMLVCUSTOMDRAW nmlvcd;
3531 HIMAGELIST himl;
3532 LVITEMW lvItem;
3534 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3536 /* get information needed for drawing the item */
3537 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3538 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3539 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3540 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3541 lvItem.iItem = nItem;
3542 lvItem.iSubItem = nSubItem;
3543 lvItem.state = 0;
3544 lvItem.lParam = 0;
3545 lvItem.cchTextMax = DISP_TEXT_SIZE;
3546 lvItem.pszText = szDispText;
3547 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3548 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3549 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3550 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3551 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3553 /* now check if we need to update the focus rectangle */
3554 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3556 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3557 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3558 OffsetRect(&rcBox, pos.x, pos.y);
3559 OffsetRect(&rcState, pos.x, pos.y);
3560 OffsetRect(&rcIcon, pos.x, pos.y);
3561 OffsetRect(&rcLabel, pos.x, pos.y);
3562 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3563 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3565 /* fill in the custom draw structure */
3566 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3568 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3569 if (cdmode & CDRF_NOTIFYITEMDRAW)
3570 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3571 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3572 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3573 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3574 prepaint_setup(infoPtr, hdc, &nmlvcd);
3576 /* in full row select, subitems, will just use main item's colors */
3577 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3578 nmlvcd.clrTextBk = CLR_NONE;
3580 /* state icons */
3581 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3583 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3584 if (uStateImage)
3586 TRACE("uStateImage=%d\n", uStateImage);
3587 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3591 /* small icons */
3592 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3593 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3595 TRACE("iImage=%d\n", lvItem.iImage);
3596 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3597 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3600 /* Don't bother painting item being edited */
3601 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3603 /* draw the selection background, if we're drawing the main item */
3604 if (nSubItem == 0)
3606 rcSelect = rcLabel;
3607 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3608 rcSelect.right = rcBox.right;
3610 if (nmlvcd.clrTextBk != CLR_NONE)
3611 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3612 if(lprcFocus) *lprcFocus = rcSelect;
3615 /* figure out the text drawing flags */
3616 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3617 if (uView == LVS_ICON)
3618 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3619 else if (nSubItem)
3621 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3623 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3624 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3625 default: uFormat |= DT_LEFT;
3628 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3630 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3631 else rcLabel.left += LABEL_HOR_PADDING;
3633 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3634 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3636 postpaint:
3637 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3638 notify_postpaint(infoPtr, &nmlvcd);
3639 return TRUE;
3642 /***
3643 * DESCRIPTION:
3644 * Draws listview items when in owner draw mode.
3646 * PARAMETER(S):
3647 * [I] infoPtr : valid pointer to the listview structure
3648 * [I] hdc : device context handle
3650 * RETURN:
3651 * None
3653 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3655 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3656 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3657 DWORD cditemmode = CDRF_DODEFAULT;
3658 NMLVCUSTOMDRAW nmlvcd;
3659 POINT Origin, Position;
3660 DRAWITEMSTRUCT dis;
3661 LVITEMW item;
3663 TRACE("()\n");
3665 ZeroMemory(&dis, sizeof(dis));
3667 /* Get scroll info once before loop */
3668 LISTVIEW_GetOrigin(infoPtr, &Origin);
3670 /* iterate through the invalidated rows */
3671 while(iterator_next(i))
3673 item.iItem = i->nItem;
3674 item.iSubItem = 0;
3675 item.mask = LVIF_PARAM | LVIF_STATE;
3676 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3677 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3679 dis.CtlType = ODT_LISTVIEW;
3680 dis.CtlID = uID;
3681 dis.itemID = item.iItem;
3682 dis.itemAction = ODA_DRAWENTIRE;
3683 dis.itemState = 0;
3684 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3685 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3686 dis.hwndItem = infoPtr->hwndSelf;
3687 dis.hDC = hdc;
3688 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3689 dis.rcItem.left = Position.x + Origin.x;
3690 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3691 dis.rcItem.top = Position.y + Origin.y;
3692 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3693 dis.itemData = item.lParam;
3695 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3697 if (cdmode & CDRF_NOTIFYITEMDRAW)
3699 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3700 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3703 if (!(cditemmode & CDRF_SKIPDEFAULT))
3705 prepaint_setup (infoPtr, hdc, &nmlvcd);
3706 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3709 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3710 notify_postpaint(infoPtr, &nmlvcd);
3714 /***
3715 * DESCRIPTION:
3716 * Draws listview items when in report display mode.
3718 * PARAMETER(S):
3719 * [I] infoPtr : valid pointer to the listview structure
3720 * [I] hdc : device context handle
3721 * [I] cdmode : custom draw mode
3723 * RETURN:
3724 * None
3726 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3728 INT rgntype;
3729 RECT rcClip, rcItem;
3730 POINT Origin, Position;
3731 RANGE colRange;
3732 ITERATOR j;
3734 TRACE("()\n");
3736 /* figure out what to draw */
3737 rgntype = GetClipBox(hdc, &rcClip);
3738 if (rgntype == NULLREGION) return;
3740 /* Get scroll info once before loop */
3741 LISTVIEW_GetOrigin(infoPtr, &Origin);
3743 /* narrow down the columns we need to paint */
3744 for(colRange.lower = 0; colRange.lower < infoPtr->hdpaColumns->nItemCount; colRange.lower++)
3746 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3747 if (rcItem.right + Origin.x >= rcClip.left) break;
3749 for(colRange.upper = infoPtr->hdpaColumns->nItemCount; colRange.upper > 0; colRange.upper--)
3751 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3752 if (rcItem.left + Origin.x < rcClip.right) break;
3754 iterator_rangeitems(&j, colRange);
3756 /* in full row select, we _have_ to draw the main item */
3757 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3758 j.nSpecial = 0;
3760 /* iterate through the invalidated rows */
3761 while(iterator_next(i))
3763 /* iterate through the invalidated columns */
3764 while(iterator_next(&j))
3766 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3767 Position.x += Origin.x;
3768 Position.y += Origin.y;
3770 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3772 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3773 rcItem.top = 0;
3774 rcItem.bottom = infoPtr->nItemHeight;
3775 OffsetRect(&rcItem, Position.x, Position.y);
3776 if (!RectVisible(hdc, &rcItem)) continue;
3779 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3782 iterator_destroy(&j);
3785 /***
3786 * DESCRIPTION:
3787 * Draws listview items when in list display mode.
3789 * PARAMETER(S):
3790 * [I] infoPtr : valid pointer to the listview structure
3791 * [I] hdc : device context handle
3792 * [I] cdmode : custom draw mode
3794 * RETURN:
3795 * None
3797 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3799 POINT Origin, Position;
3801 /* Get scroll info once before loop */
3802 LISTVIEW_GetOrigin(infoPtr, &Origin);
3804 while(iterator_prev(i))
3806 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3807 Position.x += Origin.x;
3808 Position.y += Origin.y;
3810 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3815 /***
3816 * DESCRIPTION:
3817 * Draws listview items.
3819 * PARAMETER(S):
3820 * [I] infoPtr : valid pointer to the listview structure
3821 * [I] hdc : device context handle
3823 * RETURN:
3824 * NoneX
3826 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3828 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3829 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3830 NMLVCUSTOMDRAW nmlvcd;
3831 HFONT hOldFont;
3832 DWORD cdmode;
3833 INT oldBkMode;
3834 RECT rcClient;
3835 ITERATOR i;
3837 LISTVIEW_DUMP(infoPtr);
3839 infoPtr->bIsDrawing = TRUE;
3841 /* save dc values we're gonna trash while drawing */
3842 hOldFont = SelectObject(hdc, infoPtr->hFont);
3843 oldBkMode = GetBkMode(hdc);
3844 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3845 oldTextColor = GetTextColor(hdc);
3847 oldClrTextBk = infoPtr->clrTextBk;
3848 oldClrText = infoPtr->clrText;
3850 infoPtr->cditemmode = CDRF_DODEFAULT;
3852 GetClientRect(infoPtr->hwndSelf, &rcClient);
3853 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3854 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3855 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3856 prepaint_setup(infoPtr, hdc, &nmlvcd);
3858 /* Use these colors to draw the items */
3859 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3860 infoPtr->clrText = nmlvcd.clrText;
3862 /* nothing to draw */
3863 if(infoPtr->nItemCount == 0) goto enddraw;
3865 /* figure out what we need to draw */
3866 iterator_visibleitems(&i, infoPtr, hdc);
3868 /* send cache hint notification */
3869 if (infoPtr->dwStyle & LVS_OWNERDATA)
3871 RANGE range = iterator_range(&i);
3872 NMLVCACHEHINT nmlv;
3874 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3875 nmlv.iFrom = range.lower;
3876 nmlv.iTo = range.upper - 1;
3877 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3880 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3881 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3882 else
3884 if (uView == LVS_REPORT)
3885 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3886 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3887 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3889 /* if we have a focus rect, draw it */
3890 if (infoPtr->bFocus)
3891 DrawFocusRect(hdc, &infoPtr->rcFocus);
3893 iterator_destroy(&i);
3895 enddraw:
3896 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3897 notify_postpaint(infoPtr, &nmlvcd);
3899 infoPtr->clrTextBk = oldClrTextBk;
3900 infoPtr->clrText = oldClrText;
3902 SelectObject(hdc, hOldFont);
3903 SetBkMode(hdc, oldBkMode);
3904 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3905 SetTextColor(hdc, oldTextColor);
3906 infoPtr->bIsDrawing = FALSE;
3910 /***
3911 * DESCRIPTION:
3912 * Calculates the approximate width and height of a given number of items.
3914 * PARAMETER(S):
3915 * [I] infoPtr : valid pointer to the listview structure
3916 * [I] nItemCount : number of items
3917 * [I] wWidth : width
3918 * [I] wHeight : height
3920 * RETURN:
3921 * Returns a DWORD. The width in the low word and the height in high word.
3923 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3924 WORD wWidth, WORD wHeight)
3926 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3927 INT nItemCountPerColumn = 1;
3928 INT nColumnCount = 0;
3929 DWORD dwViewRect = 0;
3931 if (nItemCount == -1)
3932 nItemCount = infoPtr->nItemCount;
3934 if (uView == LVS_LIST)
3936 if (wHeight == 0xFFFF)
3938 /* use current height */
3939 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3942 if (wHeight < infoPtr->nItemHeight)
3943 wHeight = infoPtr->nItemHeight;
3945 if (nItemCount > 0)
3947 if (infoPtr->nItemHeight > 0)
3949 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3950 if (nItemCountPerColumn == 0)
3951 nItemCountPerColumn = 1;
3953 if (nItemCount % nItemCountPerColumn != 0)
3954 nColumnCount = nItemCount / nItemCountPerColumn;
3955 else
3956 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3960 /* Microsoft padding magic */
3961 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3962 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3964 dwViewRect = MAKELONG(wWidth, wHeight);
3966 else if (uView == LVS_REPORT)
3967 FIXME("uView == LVS_REPORT: not implemented\n");
3968 else if (uView == LVS_SMALLICON)
3969 FIXME("uView == LVS_SMALLICON: not implemented\n");
3970 else if (uView == LVS_ICON)
3971 FIXME("uView == LVS_ICON: not implemented\n");
3973 return dwViewRect;
3976 /* << LISTVIEW_CreateDragImage >> */
3979 /***
3980 * DESCRIPTION:
3981 * Removes all listview items and subitems.
3983 * PARAMETER(S):
3984 * [I] infoPtr : valid pointer to the listview structure
3986 * RETURN:
3987 * SUCCESS : TRUE
3988 * FAILURE : FALSE
3990 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3992 NMLISTVIEW nmlv;
3993 HDPA hdpaSubItems = NULL;
3994 BOOL bSuppress;
3995 ITEMHDR *hdrItem;
3996 INT i, j;
3998 TRACE("()\n");
4000 /* we do it directly, to avoid notifications */
4001 ranges_clear(infoPtr->selectionRanges);
4002 infoPtr->nSelectionMark = -1;
4003 infoPtr->nFocusedItem = -1;
4004 SetRectEmpty(&infoPtr->rcFocus);
4005 /* But we are supposed to leave nHotItem as is! */
4008 /* send LVN_DELETEALLITEMS notification */
4009 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4010 nmlv.iItem = -1;
4011 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4013 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4015 /* send LVN_DELETEITEM notification, if not supressed */
4016 if (!bSuppress) notify_deleteitem(infoPtr, i);
4017 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4019 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4020 for (j = 0; j < hdpaSubItems->nItemCount; j++)
4022 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4023 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4024 COMCTL32_Free(hdrItem);
4026 DPA_Destroy(hdpaSubItems);
4027 DPA_DeletePtr(infoPtr->hdpaItems, i);
4029 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4030 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4031 infoPtr->nItemCount --;
4034 LISTVIEW_UpdateScroll(infoPtr);
4036 LISTVIEW_InvalidateList(infoPtr);
4038 return TRUE;
4041 /***
4042 * DESCRIPTION:
4043 * Scrolls, and updates the columns, when a column is changing width.
4045 * PARAMETER(S):
4046 * [I] infoPtr : valid pointer to the listview structure
4047 * [I] nColumn : column to scroll
4048 * [I] dx : amount of scroll, in pixels
4050 * RETURN:
4051 * None.
4053 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4055 COLUMN_INFO *lpColumnInfo;
4056 RECT rcOld, rcCol;
4057 INT nCol;
4059 if (nColumn < 0 || infoPtr->hdpaColumns->nItemCount < 1) return;
4060 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, infoPtr->hdpaColumns->nItemCount - 1));
4061 rcCol = lpColumnInfo->rcHeader;
4062 if (nColumn >= infoPtr->hdpaColumns->nItemCount)
4063 rcCol.left = rcCol.right;
4065 /* ajust the other columns */
4066 for (nCol = nColumn; nCol < infoPtr->hdpaColumns->nItemCount; nCol++)
4068 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4069 lpColumnInfo->rcHeader.left += dx;
4070 lpColumnInfo->rcHeader.right += dx;
4073 /* do not update screen if not in report mode */
4074 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4076 /* if we have a focus, must first erase the focus rect */
4077 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4079 /* Need to reset the item width when inserting a new column */
4080 infoPtr->nItemWidth += dx;
4082 LISTVIEW_UpdateScroll(infoPtr);
4084 /* scroll to cover the deleted column, and invalidate for redraw */
4085 rcOld = infoPtr->rcList;
4086 rcOld.left = rcCol.left;
4087 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4089 /* we can restore focus now */
4090 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4093 /***
4094 * DESCRIPTION:
4095 * Removes a column from the listview control.
4097 * PARAMETER(S):
4098 * [I] infoPtr : valid pointer to the listview structure
4099 * [I] nColumn : column index
4101 * RETURN:
4102 * SUCCESS : TRUE
4103 * FAILURE : FALSE
4105 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4107 RECT rcCol;
4109 TRACE("nColumn=%d\n", nColumn);
4111 if (nColumn < 0 || infoPtr->hdpaColumns->nItemCount == 0
4112 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4114 /* While the MSDN specifically says that column zero should not be deleted,
4115 it does in fact work on WinNT, and at least one app depends on it. On
4116 WinNT, deleting column zero deletes the last column of items but the
4117 first header. Since no app will ever depend on that bizarre behavior,
4118 we just delete the last column including the header.
4120 if (nColumn == 0)
4121 nColumn = infoPtr->hdpaColumns->nItemCount - 1;
4123 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4125 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4126 return FALSE;
4128 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4129 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4131 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4133 SUBITEM_INFO *lpSubItem, *lpDelItem;
4134 HDPA hdpaSubItems;
4135 INT nItem, nSubItem, i;
4137 if (nColumn == 0)
4138 return LISTVIEW_DeleteAllItems(infoPtr);
4140 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4142 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4143 nSubItem = 0;
4144 lpDelItem = 0;
4145 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4147 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4148 if (lpSubItem->iSubItem == nColumn)
4150 nSubItem = i;
4151 lpDelItem = lpSubItem;
4153 else if (lpSubItem->iSubItem > nColumn)
4155 lpSubItem->iSubItem--;
4159 /* if we found our subitem, zapp it */
4160 if (nSubItem > 0)
4162 /* free string */
4163 if (is_textW(lpDelItem->hdr.pszText))
4164 COMCTL32_Free(lpDelItem->hdr.pszText);
4166 /* free item */
4167 COMCTL32_Free(lpDelItem);
4169 /* free dpa memory */
4170 DPA_DeletePtr(hdpaSubItems, nSubItem);
4175 /* update the other column info */
4176 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4178 return TRUE;
4181 /***
4182 * DESCRIPTION:
4183 * Invalidates the listview after an item's insertion or deletion.
4185 * PARAMETER(S):
4186 * [I] infoPtr : valid pointer to the listview structure
4187 * [I] nItem : item index
4188 * [I] dir : -1 if deleting, 1 if inserting
4190 * RETURN:
4191 * None
4193 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4195 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4196 INT nPerCol, nItemCol, nItemRow;
4197 RECT rcScroll;
4198 POINT Origin;
4200 /* if we don't refresh, what's the point of scrolling? */
4201 if (!is_redrawing(infoPtr)) return;
4203 assert (abs(dir) == 1);
4205 /* arrange icons if autoarrange is on */
4206 if (is_autoarrange(infoPtr))
4208 BOOL arrange = TRUE;
4209 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4210 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4211 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4214 /* scrollbars need updating */
4215 LISTVIEW_UpdateScroll(infoPtr);
4217 /* figure out the item's position */
4218 if (uView == LVS_REPORT)
4219 nPerCol = infoPtr->nItemCount + 1;
4220 else if (uView == LVS_LIST)
4221 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4222 else /* LVS_ICON, or LVS_SMALLICON */
4223 return;
4225 nItemCol = nItem / nPerCol;
4226 nItemRow = nItem % nPerCol;
4227 LISTVIEW_GetOrigin(infoPtr, &Origin);
4229 /* move the items below up a slot */
4230 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4231 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4232 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4233 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4234 OffsetRect(&rcScroll, Origin.x, Origin.y);
4235 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4236 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4238 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4239 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4240 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4243 /* report has only that column, so we're done */
4244 if (uView == LVS_REPORT) return;
4246 /* now for LISTs, we have to deal with the columns to the right */
4247 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4248 rcScroll.top = 0;
4249 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4250 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4251 OffsetRect(&rcScroll, Origin.x, Origin.y);
4252 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4253 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4254 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4257 /***
4258 * DESCRIPTION:
4259 * Removes an item from the listview control.
4261 * PARAMETER(S):
4262 * [I] infoPtr : valid pointer to the listview structure
4263 * [I] nItem : item index
4265 * RETURN:
4266 * SUCCESS : TRUE
4267 * FAILURE : FALSE
4269 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4271 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4272 LVITEMW item;
4274 TRACE("(nItem=%d)\n", nItem);
4276 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4278 /* remove selection, and focus */
4279 item.state = 0;
4280 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4281 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4283 /* send LVN_DELETEITEM notification. */
4284 notify_deleteitem(infoPtr, nItem);
4286 /* we need to do this here, because we'll be deleting stuff */
4287 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4288 LISTVIEW_InvalidateItem(infoPtr, nItem);
4290 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4292 HDPA hdpaSubItems;
4293 ITEMHDR *hdrItem;
4294 INT i;
4296 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4297 for (i = 0; i < hdpaSubItems->nItemCount; i++)
4299 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4300 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4301 COMCTL32_Free(hdrItem);
4303 DPA_Destroy(hdpaSubItems);
4306 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4308 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4309 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4312 infoPtr->nItemCount--;
4313 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4315 /* now is the invalidation fun */
4316 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4317 return TRUE;
4321 /***
4322 * DESCRIPTION:
4323 * Callback implementation for editlabel control
4325 * PARAMETER(S):
4326 * [I] infoPtr : valid pointer to the listview structure
4327 * [I] pszText : modified text
4328 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4330 * RETURN:
4331 * SUCCESS : TRUE
4332 * FAILURE : FALSE
4334 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4336 NMLVDISPINFOW dispInfo;
4338 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4340 ZeroMemory(&dispInfo, sizeof(dispInfo));
4341 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4342 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4343 dispInfo.item.iSubItem = 0;
4344 dispInfo.item.stateMask = ~0;
4345 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4346 /* add the text from the edit in */
4347 dispInfo.item.mask |= LVIF_TEXT;
4348 dispInfo.item.pszText = pszText;
4349 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4351 /* Do we need to update the Item Text */
4352 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4353 if (!pszText) return TRUE;
4355 ZeroMemory(&dispInfo, sizeof(dispInfo));
4356 dispInfo.item.mask = LVIF_TEXT;
4357 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4358 dispInfo.item.iSubItem = 0;
4359 dispInfo.item.pszText = pszText;
4360 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4361 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4364 /***
4365 * DESCRIPTION:
4366 * Begin in place editing of specified list view item
4368 * PARAMETER(S):
4369 * [I] infoPtr : valid pointer to the listview structure
4370 * [I] nItem : item index
4371 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4373 * RETURN:
4374 * SUCCESS : TRUE
4375 * FAILURE : FALSE
4377 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4379 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4380 NMLVDISPINFOW dispInfo;
4381 RECT rect;
4383 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4385 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4386 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4388 infoPtr->nEditLabelItem = nItem;
4390 /* Is the EditBox still there, if so remove it */
4391 if(infoPtr->hwndEdit != 0)
4393 SetFocus(infoPtr->hwndSelf);
4394 infoPtr->hwndEdit = 0;
4397 LISTVIEW_SetSelection(infoPtr, nItem);
4398 LISTVIEW_SetItemFocus(infoPtr, nItem);
4399 LISTVIEW_InvalidateItem(infoPtr, nItem);
4401 rect.left = LVIR_LABEL;
4402 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4404 ZeroMemory(&dispInfo, sizeof(dispInfo));
4405 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4406 dispInfo.item.iItem = nItem;
4407 dispInfo.item.iSubItem = 0;
4408 dispInfo.item.stateMask = ~0;
4409 dispInfo.item.pszText = szDispText;
4410 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4411 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4413 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4414 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4415 if (!infoPtr->hwndEdit) return 0;
4417 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4419 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4420 infoPtr->hwndEdit = 0;
4421 return 0;
4424 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4425 SetFocus(infoPtr->hwndEdit);
4426 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4427 return infoPtr->hwndEdit;
4431 /***
4432 * DESCRIPTION:
4433 * Ensures the specified item is visible, scrolling into view if necessary.
4435 * PARAMETER(S):
4436 * [I] infoPtr : valid pointer to the listview structure
4437 * [I] nItem : item index
4438 * [I] bPartial : partially or entirely visible
4440 * RETURN:
4441 * SUCCESS : TRUE
4442 * FAILURE : FALSE
4444 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4446 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4447 INT nScrollPosHeight = 0;
4448 INT nScrollPosWidth = 0;
4449 INT nHorzAdjust = 0;
4450 INT nVertAdjust = 0;
4451 INT nHorzDiff = 0;
4452 INT nVertDiff = 0;
4453 RECT rcItem, rcTemp;
4455 rcItem.left = LVIR_BOUNDS;
4456 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4458 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4460 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4462 /* scroll left/right, but in LVS_REPORT mode */
4463 if (uView == LVS_LIST)
4464 nScrollPosWidth = infoPtr->nItemWidth;
4465 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4466 nScrollPosWidth = 1;
4468 if (rcItem.left < infoPtr->rcList.left)
4470 nHorzAdjust = -1;
4471 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4473 else
4475 nHorzAdjust = 1;
4476 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4480 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4482 /* scroll up/down, but not in LVS_LIST mode */
4483 if (uView == LVS_REPORT)
4484 nScrollPosHeight = infoPtr->nItemHeight;
4485 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4486 nScrollPosHeight = 1;
4488 if (rcItem.top < infoPtr->rcList.top)
4490 nVertAdjust = -1;
4491 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4493 else
4495 nVertAdjust = 1;
4496 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4500 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4502 if (nScrollPosWidth)
4504 INT diff = nHorzDiff / nScrollPosWidth;
4505 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4506 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4509 if (nScrollPosHeight)
4511 INT diff = nVertDiff / nScrollPosHeight;
4512 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4513 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4516 return TRUE;
4519 /***
4520 * DESCRIPTION:
4521 * Searches for an item with specific characteristics.
4523 * PARAMETER(S):
4524 * [I] hwnd : window handle
4525 * [I] nStart : base item index
4526 * [I] lpFindInfo : item information to look for
4528 * RETURN:
4529 * SUCCESS : index of item
4530 * FAILURE : -1
4532 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4533 const LVFINDINFOW *lpFindInfo)
4535 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4536 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4537 BOOL bWrap = FALSE, bNearest = FALSE;
4538 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4539 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4540 POINT Position, Destination;
4541 LVITEMW lvItem;
4543 if (!lpFindInfo || nItem < 0) return -1;
4545 lvItem.mask = 0;
4546 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4548 lvItem.mask |= LVIF_TEXT;
4549 lvItem.pszText = szDispText;
4550 lvItem.cchTextMax = DISP_TEXT_SIZE;
4553 if (lpFindInfo->flags & LVFI_WRAP)
4554 bWrap = TRUE;
4556 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4557 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4559 POINT Origin;
4560 RECT rcArea;
4562 LISTVIEW_GetOrigin(infoPtr, &Origin);
4563 Destination.x = lpFindInfo->pt.x - Origin.x;
4564 Destination.y = lpFindInfo->pt.y - Origin.y;
4565 switch(lpFindInfo->vkDirection)
4567 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4568 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4569 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4570 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4571 case VK_HOME: Destination.x = Destination.y = 0; break;
4572 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4573 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4574 case VK_END:
4575 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4576 Destination.x = rcArea.right;
4577 Destination.y = rcArea.bottom;
4578 break;
4579 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4581 bNearest = TRUE;
4584 /* if LVFI_PARAM is specified, all other flags are ignored */
4585 if (lpFindInfo->flags & LVFI_PARAM)
4587 lvItem.mask |= LVIF_PARAM;
4588 bNearest = FALSE;
4589 lvItem.mask &= ~LVIF_TEXT;
4592 again:
4593 for (; nItem < nLast; nItem++)
4595 lvItem.iItem = nItem;
4596 lvItem.iSubItem = 0;
4597 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4599 if (lvItem.mask & LVIF_PARAM)
4601 if (lpFindInfo->lParam == lvItem.lParam)
4602 return nItem;
4603 else
4604 continue;
4607 if (lvItem.mask & LVIF_TEXT)
4609 if (lpFindInfo->flags & LVFI_PARTIAL)
4611 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4613 else
4615 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4619 if (!bNearest) return nItem;
4621 /* This is very inefficient. To do a good job here,
4622 * we need a sorted array of (x,y) item positions */
4623 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4625 /* compute the distance^2 to the destination */
4626 xdist = Destination.x - Position.x;
4627 ydist = Destination.y - Position.y;
4628 dist = xdist * xdist + ydist * ydist;
4630 /* remember the distance, and item if it's closer */
4631 if (dist < mindist)
4633 mindist = dist;
4634 nNearestItem = nItem;
4638 if (bWrap)
4640 nItem = 0;
4641 nLast = min(nStart + 1, infoPtr->nItemCount);
4642 bWrap = FALSE;
4643 goto again;
4646 return nNearestItem;
4649 /***
4650 * DESCRIPTION:
4651 * Searches for an item with specific characteristics.
4653 * PARAMETER(S):
4654 * [I] hwnd : window handle
4655 * [I] nStart : base item index
4656 * [I] lpFindInfo : item information to look for
4658 * RETURN:
4659 * SUCCESS : index of item
4660 * FAILURE : -1
4662 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4663 const LVFINDINFOA *lpFindInfo)
4665 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4666 LVFINDINFOW fiw;
4667 INT res;
4669 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4670 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4671 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4672 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4673 return res;
4676 /***
4677 * DESCRIPTION:
4678 * Retrieves the background image of the listview control.
4680 * PARAMETER(S):
4681 * [I] infoPtr : valid pointer to the listview structure
4682 * [O] lpBkImage : background image attributes
4684 * RETURN:
4685 * SUCCESS : TRUE
4686 * FAILURE : FALSE
4688 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4689 /* { */
4690 /* FIXME (listview, "empty stub!\n"); */
4691 /* return FALSE; */
4692 /* } */
4694 /***
4695 * DESCRIPTION:
4696 * Retrieves column attributes.
4698 * PARAMETER(S):
4699 * [I] infoPtr : valid pointer to the listview structure
4700 * [I] nColumn : column index
4701 * [IO] lpColumn : column information
4702 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4703 * otherwise it is in fact a LPLVCOLUMNA
4705 * RETURN:
4706 * SUCCESS : TRUE
4707 * FAILURE : FALSE
4709 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4711 COLUMN_INFO *lpColumnInfo;
4712 HDITEMW hdi;
4714 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4715 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4717 /* initialize memory */
4718 ZeroMemory(&hdi, sizeof(hdi));
4720 if (lpColumn->mask & LVCF_TEXT)
4722 hdi.mask |= HDI_TEXT;
4723 hdi.pszText = lpColumn->pszText;
4724 hdi.cchTextMax = lpColumn->cchTextMax;
4727 if (lpColumn->mask & LVCF_IMAGE)
4728 hdi.mask |= HDI_IMAGE;
4730 if (lpColumn->mask & LVCF_ORDER)
4731 hdi.mask |= HDI_ORDER;
4733 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4735 if (lpColumn->mask & LVCF_FMT)
4736 lpColumn->fmt = lpColumnInfo->fmt;
4738 if (lpColumn->mask & LVCF_WIDTH)
4739 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4741 if (lpColumn->mask & LVCF_IMAGE)
4742 lpColumn->iImage = hdi.iImage;
4744 if (lpColumn->mask & LVCF_ORDER)
4745 lpColumn->iOrder = hdi.iOrder;
4747 return TRUE;
4751 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4753 INT i;
4755 if (!lpiArray)
4756 return FALSE;
4758 /* FIXME: little hack */
4759 for (i = 0; i < iCount; i++)
4760 lpiArray[i] = i;
4762 return TRUE;
4765 /***
4766 * DESCRIPTION:
4767 * Retrieves the column width.
4769 * PARAMETER(S):
4770 * [I] infoPtr : valid pointer to the listview structure
4771 * [I] int : column index
4773 * RETURN:
4774 * SUCCESS : column width
4775 * FAILURE : zero
4777 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4779 INT nColumnWidth = 0;
4780 RECT rcHeader;
4782 TRACE("nColumn=%d\n", nColumn);
4784 /* we have a 'column' in LIST and REPORT mode only */
4785 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4787 case LVS_LIST:
4788 nColumnWidth = infoPtr->nItemWidth;
4789 break;
4790 case LVS_REPORT:
4791 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return 0;
4792 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4793 nColumnWidth = rcHeader.right - rcHeader.left;
4794 break;
4797 TRACE("nColumnWidth=%d\n", nColumnWidth);
4798 return nColumnWidth;
4801 /***
4802 * DESCRIPTION:
4803 * In list or report display mode, retrieves the number of items that can fit
4804 * vertically in the visible area. In icon or small icon display mode,
4805 * retrieves the total number of visible items.
4807 * PARAMETER(S):
4808 * [I] infoPtr : valid pointer to the listview structure
4810 * RETURN:
4811 * Number of fully visible items.
4813 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4815 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4817 case LVS_ICON:
4818 case LVS_SMALLICON:
4819 return infoPtr->nItemCount;
4820 case LVS_REPORT:
4821 return LISTVIEW_GetCountPerColumn(infoPtr);
4822 case LVS_LIST:
4823 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4825 assert(FALSE);
4826 return 0;
4829 /***
4830 * DESCRIPTION:
4831 * Retrieves an image list handle.
4833 * PARAMETER(S):
4834 * [I] infoPtr : valid pointer to the listview structure
4835 * [I] nImageList : image list identifier
4837 * RETURN:
4838 * SUCCESS : image list handle
4839 * FAILURE : NULL
4841 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4843 switch (nImageList)
4845 case LVSIL_NORMAL: return infoPtr->himlNormal;
4846 case LVSIL_SMALL: return infoPtr->himlSmall;
4847 case LVSIL_STATE: return infoPtr->himlState;
4849 return NULL;
4852 /* LISTVIEW_GetISearchString */
4854 /***
4855 * DESCRIPTION:
4856 * Retrieves item attributes.
4858 * PARAMETER(S):
4859 * [I] hwnd : window handle
4860 * [IO] lpLVItem : item info
4861 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4862 * if FALSE, the lpLVItem is a LPLVITEMA.
4864 * NOTE:
4865 * This is the internal 'GetItem' interface -- it tries to
4866 * be smart, and avoids text copies, if possible, by modifing
4867 * lpLVItem->pszText to point to the text string. Please note
4868 * that this is not always possible (e.g. OWNERDATA), so on
4869 * entry you *must* supply valid values for pszText, and cchTextMax.
4870 * The only difference to the documented interface is that upon
4871 * return, you should use *only* the lpLVItem->pszText, rather than
4872 * the buffer pointer you provided on input. Most code already does
4873 * that, so it's not a problem.
4874 * For the two cases when the text must be copied (that is,
4875 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4877 * RETURN:
4878 * SUCCESS : TRUE
4879 * FAILURE : FALSE
4881 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4883 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
4884 NMLVDISPINFOW dispInfo;
4885 ITEM_INFO *lpItem;
4886 ITEMHDR* pItemHdr;
4887 HDPA hdpaSubItems;
4889 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4891 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4892 return FALSE;
4894 if (lpLVItem->mask == 0) return TRUE;
4896 /* a quick optimization if all we're asked is the focus state
4897 * these queries are worth optimising since they are common,
4898 * and can be answered in constant time, without the heavy accesses */
4899 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4900 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4902 lpLVItem->state = 0;
4903 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4904 lpLVItem->state |= LVIS_FOCUSED;
4905 return TRUE;
4908 ZeroMemory(&dispInfo, sizeof(dispInfo));
4910 /* if the app stores all the data, handle it separately */
4911 if (infoPtr->dwStyle & LVS_OWNERDATA)
4913 dispInfo.item.state = 0;
4915 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
4916 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
4918 /* NOTE: copy only fields which we _know_ are initialized, some apps
4919 * depend on the uninitialized fields being 0 */
4920 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
4921 dispInfo.item.iItem = lpLVItem->iItem;
4922 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4923 if (lpLVItem->mask & LVIF_TEXT)
4925 dispInfo.item.pszText = lpLVItem->pszText;
4926 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4928 if (lpLVItem->mask & LVIF_STATE)
4929 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4930 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4931 dispInfo.item.stateMask = lpLVItem->stateMask;
4932 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
4934 /* full size structure expected - _WIN32IE >= 0x560 */
4935 *lpLVItem = dispInfo.item;
4937 else if (lpLVItem->mask & LVIF_INDENT)
4939 /* indent member expected - _WIN32IE >= 0x300 */
4940 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
4942 else
4944 /* minimal structure expected */
4945 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
4947 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4950 /* make sure lParam is zeroed out */
4951 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
4953 /* we store only a little state, so if we're not asked, we're done */
4954 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4956 /* if focus is handled by us, report it */
4957 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4959 lpLVItem->state &= ~LVIS_FOCUSED;
4960 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4961 lpLVItem->state |= LVIS_FOCUSED;
4964 /* and do the same for selection, if we handle it */
4965 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4967 lpLVItem->state &= ~LVIS_SELECTED;
4968 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4969 lpLVItem->state |= LVIS_SELECTED;
4972 return TRUE;
4975 /* find the item and subitem structures before we proceed */
4976 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4977 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4978 assert (lpItem);
4980 if (lpLVItem->iSubItem)
4982 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4983 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
4985 else
4986 pItemHdr = &lpItem->hdr;
4988 /* Do we need to query the state from the app? */
4989 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4991 dispInfo.item.mask |= LVIF_STATE;
4992 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4995 /* Do we need to enquire about the image? */
4996 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4997 dispInfo.item.mask |= LVIF_IMAGE;
4999 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5000 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5002 dispInfo.item.mask |= LVIF_TEXT;
5003 dispInfo.item.pszText = lpLVItem->pszText;
5004 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5005 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5006 *dispInfo.item.pszText = '\0';
5009 /* If we don't have all the requested info, query the application */
5010 if (dispInfo.item.mask != 0)
5012 dispInfo.item.iItem = lpLVItem->iItem;
5013 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5014 dispInfo.item.lParam = lpItem->lParam;
5015 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5016 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5019 /* we should not store values for subitems */
5020 if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5022 /* Now, handle the iImage field */
5023 if (dispInfo.item.mask & LVIF_IMAGE)
5025 lpLVItem->iImage = dispInfo.item.iImage;
5026 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5027 pItemHdr->iImage = dispInfo.item.iImage;
5029 else if (lpLVItem->mask & LVIF_IMAGE)
5030 lpLVItem->iImage = pItemHdr->iImage;
5032 /* The pszText field */
5033 if (dispInfo.item.mask & LVIF_TEXT)
5035 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5036 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5038 lpLVItem->pszText = dispInfo.item.pszText;
5040 else if (lpLVItem->mask & LVIF_TEXT)
5042 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5043 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5046 /* if this is a subitem, we're done */
5047 if (lpLVItem->iSubItem) return TRUE;
5049 /* Next is the lParam field */
5050 if (dispInfo.item.mask & LVIF_PARAM)
5052 lpLVItem->lParam = dispInfo.item.lParam;
5053 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5054 lpItem->lParam = dispInfo.item.lParam;
5056 else if (lpLVItem->mask & LVIF_PARAM)
5057 lpLVItem->lParam = lpItem->lParam;
5059 /* ... the state field (this one is different due to uCallbackmask) */
5060 if (lpLVItem->mask & LVIF_STATE)
5062 lpLVItem->state = lpItem->state;
5063 if (dispInfo.item.mask & LVIF_STATE)
5065 lpLVItem->state &= ~dispInfo.item.stateMask;
5066 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5068 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5070 lpLVItem->state &= ~LVIS_FOCUSED;
5071 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5072 lpLVItem->state |= LVIS_FOCUSED;
5074 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5076 lpLVItem->state &= ~LVIS_SELECTED;
5077 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5078 lpLVItem->state |= LVIS_SELECTED;
5082 /* and last, but not least, the indent field */
5083 if (lpLVItem->mask & LVIF_INDENT)
5084 lpLVItem->iIndent = lpItem->iIndent;
5086 return TRUE;
5089 /***
5090 * DESCRIPTION:
5091 * Retrieves item attributes.
5093 * PARAMETER(S):
5094 * [I] hwnd : window handle
5095 * [IO] lpLVItem : item info
5096 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5097 * if FALSE, the lpLVItem is a LPLVITEMA.
5099 * NOTE:
5100 * This is the external 'GetItem' interface -- it properly copies
5101 * the text in the provided buffer.
5103 * RETURN:
5104 * SUCCESS : TRUE
5105 * FAILURE : FALSE
5107 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5109 LPWSTR pszText;
5110 BOOL bResult;
5112 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5113 return FALSE;
5115 pszText = lpLVItem->pszText;
5116 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5117 if (bResult && lpLVItem->pszText != pszText)
5118 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5119 lpLVItem->pszText = pszText;
5121 return bResult;
5125 /***
5126 * DESCRIPTION:
5127 * Retrieves the position (upper-left) of the listview control item.
5128 * Note that for LVS_ICON style, the upper-left is that of the icon
5129 * and not the bounding box.
5131 * PARAMETER(S):
5132 * [I] infoPtr : valid pointer to the listview structure
5133 * [I] nItem : item index
5134 * [O] lpptPosition : coordinate information
5136 * RETURN:
5137 * SUCCESS : TRUE
5138 * FAILURE : FALSE
5140 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5142 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5143 POINT Origin;
5145 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5147 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5149 LISTVIEW_GetOrigin(infoPtr, &Origin);
5150 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5152 if (uView == LVS_ICON)
5154 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5155 lpptPosition->y += ICON_TOP_PADDING;
5157 lpptPosition->x += Origin.x;
5158 lpptPosition->y += Origin.y;
5160 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5161 return TRUE;
5165 /***
5166 * DESCRIPTION:
5167 * Retrieves the bounding rectangle for a listview control item.
5169 * PARAMETER(S):
5170 * [I] infoPtr : valid pointer to the listview structure
5171 * [I] nItem : item index
5172 * [IO] lprc : bounding rectangle coordinates
5173 * lprc->left specifies the portion of the item for which the bounding
5174 * rectangle will be retrieved.
5176 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5177 * including the icon and label.
5179 * * For LVS_ICON
5180 * * Experiment shows that native control returns:
5181 * * width = min (48, length of text line)
5182 * * .left = position.x - (width - iconsize.cx)/2
5183 * * .right = .left + width
5184 * * height = #lines of text * ntmHeight + icon height + 8
5185 * * .top = position.y - 2
5186 * * .bottom = .top + height
5187 * * separation between items .y = itemSpacing.cy - height
5188 * * .x = itemSpacing.cx - width
5189 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5191 * * For LVS_ICON
5192 * * Experiment shows that native control returns:
5193 * * width = iconSize.cx + 16
5194 * * .left = position.x - (width - iconsize.cx)/2
5195 * * .right = .left + width
5196 * * height = iconSize.cy + 4
5197 * * .top = position.y - 2
5198 * * .bottom = .top + height
5199 * * separation between items .y = itemSpacing.cy - height
5200 * * .x = itemSpacing.cx - width
5201 * LVIR_LABEL Returns the bounding rectangle of the item text.
5203 * * For LVS_ICON
5204 * * Experiment shows that native control returns:
5205 * * width = text length
5206 * * .left = position.x - width/2
5207 * * .right = .left + width
5208 * * height = ntmH * linecount + 2
5209 * * .top = position.y + iconSize.cy + 6
5210 * * .bottom = .top + height
5211 * * separation between items .y = itemSpacing.cy - height
5212 * * .x = itemSpacing.cx - width
5213 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5214 * rectangles, but excludes columns in report view.
5216 * RETURN:
5217 * SUCCESS : TRUE
5218 * FAILURE : FALSE
5220 * NOTES
5221 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5222 * upon whether the window has the focus currently and on whether the item
5223 * is the one with the focus. Ensure that the control's record of which
5224 * item has the focus agrees with the items' records.
5226 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5228 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5229 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5230 BOOL doLabel = TRUE, oversizedBox = FALSE;
5231 POINT Position, Origin;
5232 LVITEMW lvItem;
5233 RECT label_rect;
5235 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5237 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5239 LISTVIEW_GetOrigin(infoPtr, &Origin);
5240 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5242 /* Be smart and try to figure out the minimum we have to do */
5243 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5244 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5245 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5246 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5247 oversizedBox = TRUE;
5249 /* get what we need from the item before hand, so we make
5250 * only one request. This can speed up things, if data
5251 * is stored on the app side */
5252 lvItem.mask = 0;
5253 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5254 if (doLabel) lvItem.mask |= LVIF_TEXT;
5255 lvItem.iItem = nItem;
5256 lvItem.iSubItem = 0;
5257 lvItem.pszText = szDispText;
5258 lvItem.cchTextMax = DISP_TEXT_SIZE;
5259 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5260 /* we got the state already up, simulate it here, to avoid a reget */
5261 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5263 lvItem.mask |= LVIF_STATE;
5264 lvItem.stateMask = LVIS_FOCUSED;
5265 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5268 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5269 lprc->left = LVIR_BOUNDS;
5270 switch(lprc->left)
5272 case LVIR_ICON:
5273 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5274 break;
5276 case LVIR_LABEL:
5277 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5278 break;
5280 case LVIR_BOUNDS:
5281 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5282 break;
5284 case LVIR_SELECTBOUNDS:
5285 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5286 UnionRect(lprc, lprc, &label_rect);
5287 break;
5289 default:
5290 WARN("Unknown value: %ld\n", lprc->left);
5291 return FALSE;
5294 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5296 TRACE(" rect=%s\n", debugrect(lprc));
5298 return TRUE;
5301 /***
5302 * DESCRIPTION:
5303 * Retrieves the spacing between listview control items.
5305 * PARAMETER(S):
5306 * [I] infoPtr : valid pointer to the listview structure
5307 * [IO] lprc : rectangle to receive the output
5308 * on input, lprc->top = nSubItem
5309 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5311 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5312 * not only those of the first column.
5313 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5315 * RETURN:
5316 * TRUE: success
5317 * FALSE: failure
5319 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5321 POINT Position;
5322 LVITEMW lvItem;
5324 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5326 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5327 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5328 if (lprc->top == 0)
5329 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5331 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5333 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5334 lvItem.iItem = nItem;
5335 lvItem.iSubItem = lprc->top;
5337 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5338 switch(lprc->left)
5340 case LVIR_ICON:
5341 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5342 break;
5344 case LVIR_LABEL:
5345 case LVIR_BOUNDS:
5346 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5347 break;
5349 default:
5350 ERR("Unknown bounds=%ld\n", lprc->left);
5351 return FALSE;
5354 OffsetRect(lprc, Position.x, Position.y);
5355 return TRUE;
5359 /***
5360 * DESCRIPTION:
5361 * Retrieves the width of a label.
5363 * PARAMETER(S):
5364 * [I] infoPtr : valid pointer to the listview structure
5366 * RETURN:
5367 * SUCCESS : string width (in pixels)
5368 * FAILURE : zero
5370 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5372 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5373 LVITEMW lvItem;
5375 TRACE("(nItem=%d)\n", nItem);
5377 lvItem.mask = LVIF_TEXT;
5378 lvItem.iItem = nItem;
5379 lvItem.iSubItem = 0;
5380 lvItem.pszText = szDispText;
5381 lvItem.cchTextMax = DISP_TEXT_SIZE;
5382 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5384 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5387 /***
5388 * DESCRIPTION:
5389 * Retrieves the spacing between listview control items.
5391 * PARAMETER(S):
5392 * [I] infoPtr : valid pointer to the listview structure
5393 * [I] bSmall : flag for small or large icon
5395 * RETURN:
5396 * Horizontal + vertical spacing
5398 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5400 LONG lResult;
5402 if (!bSmall)
5404 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5406 else
5408 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5409 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5410 else
5411 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5413 return lResult;
5416 /***
5417 * DESCRIPTION:
5418 * Retrieves the state of a listview control item.
5420 * PARAMETER(S):
5421 * [I] infoPtr : valid pointer to the listview structure
5422 * [I] nItem : item index
5423 * [I] uMask : state mask
5425 * RETURN:
5426 * State specified by the mask.
5428 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5430 LVITEMW lvItem;
5432 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5434 lvItem.iItem = nItem;
5435 lvItem.iSubItem = 0;
5436 lvItem.mask = LVIF_STATE;
5437 lvItem.stateMask = uMask;
5438 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5440 return lvItem.state & uMask;
5443 /***
5444 * DESCRIPTION:
5445 * Retrieves the text of a listview control item or subitem.
5447 * PARAMETER(S):
5448 * [I] hwnd : window handle
5449 * [I] nItem : item index
5450 * [IO] lpLVItem : item information
5451 * [I] isW : TRUE if lpLVItem is Unicode
5453 * RETURN:
5454 * SUCCESS : string length
5455 * FAILURE : 0
5457 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5459 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5461 lpLVItem->mask = LVIF_TEXT;
5462 lpLVItem->iItem = nItem;
5463 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5465 return textlenT(lpLVItem->pszText, isW);
5468 /***
5469 * DESCRIPTION:
5470 * Searches for an item based on properties + relationships.
5472 * PARAMETER(S):
5473 * [I] infoPtr : valid pointer to the listview structure
5474 * [I] nItem : item index
5475 * [I] uFlags : relationship flag
5477 * RETURN:
5478 * SUCCESS : item index
5479 * FAILURE : -1
5481 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5483 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5484 UINT uMask = 0;
5485 LVFINDINFOW lvFindInfo;
5486 INT nCountPerColumn;
5487 INT nCountPerRow;
5488 INT i;
5490 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5491 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5493 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5495 if (uFlags & LVNI_CUT)
5496 uMask |= LVIS_CUT;
5498 if (uFlags & LVNI_DROPHILITED)
5499 uMask |= LVIS_DROPHILITED;
5501 if (uFlags & LVNI_FOCUSED)
5502 uMask |= LVIS_FOCUSED;
5504 if (uFlags & LVNI_SELECTED)
5505 uMask |= LVIS_SELECTED;
5507 /* if we're asked for the focused item, that's only one,
5508 * so it's worth optimizing */
5509 if (uFlags & LVNI_FOCUSED)
5511 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5512 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5515 if (uFlags & LVNI_ABOVE)
5517 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5519 while (nItem >= 0)
5521 nItem--;
5522 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5523 return nItem;
5526 else
5528 /* Special case for autoarrange - move 'til the top of a list */
5529 if (is_autoarrange(infoPtr))
5531 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5532 while (nItem - nCountPerRow >= 0)
5534 nItem -= nCountPerRow;
5535 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5536 return nItem;
5538 return -1;
5540 lvFindInfo.flags = LVFI_NEARESTXY;
5541 lvFindInfo.vkDirection = VK_UP;
5542 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5543 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5545 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5546 return nItem;
5550 else if (uFlags & LVNI_BELOW)
5552 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5554 while (nItem < infoPtr->nItemCount)
5556 nItem++;
5557 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5558 return nItem;
5561 else
5563 /* Special case for autoarrange - move 'til the bottom of a list */
5564 if (is_autoarrange(infoPtr))
5566 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5567 while (nItem + nCountPerRow < infoPtr->nItemCount )
5569 nItem += nCountPerRow;
5570 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5571 return nItem;
5573 return -1;
5575 lvFindInfo.flags = LVFI_NEARESTXY;
5576 lvFindInfo.vkDirection = VK_DOWN;
5577 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5578 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5580 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5581 return nItem;
5585 else if (uFlags & LVNI_TOLEFT)
5587 if (uView == LVS_LIST)
5589 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5590 while (nItem - nCountPerColumn >= 0)
5592 nItem -= nCountPerColumn;
5593 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5594 return nItem;
5597 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5599 /* Special case for autoarrange - move 'ti the beginning of a row */
5600 if (is_autoarrange(infoPtr))
5602 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5603 while (nItem % nCountPerRow > 0)
5605 nItem --;
5606 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5607 return nItem;
5609 return -1;
5611 lvFindInfo.flags = LVFI_NEARESTXY;
5612 lvFindInfo.vkDirection = VK_LEFT;
5613 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5614 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5616 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5617 return nItem;
5621 else if (uFlags & LVNI_TORIGHT)
5623 if (uView == LVS_LIST)
5625 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5626 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5628 nItem += nCountPerColumn;
5629 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5630 return nItem;
5633 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5635 /* Special case for autoarrange - move 'til the end of a row */
5636 if (is_autoarrange(infoPtr))
5638 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5639 while (nItem % nCountPerRow < nCountPerRow - 1 )
5641 nItem ++;
5642 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5643 return nItem;
5645 return -1;
5647 lvFindInfo.flags = LVFI_NEARESTXY;
5648 lvFindInfo.vkDirection = VK_RIGHT;
5649 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5650 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5652 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5653 return nItem;
5657 else
5659 nItem++;
5661 /* search by index */
5662 for (i = nItem; i < infoPtr->nItemCount; i++)
5664 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5665 return i;
5669 return -1;
5672 /* LISTVIEW_GetNumberOfWorkAreas */
5674 /***
5675 * DESCRIPTION:
5676 * Retrieves the origin coordinates when in icon or small icon display mode.
5678 * PARAMETER(S):
5679 * [I] infoPtr : valid pointer to the listview structure
5680 * [O] lpptOrigin : coordinate information
5682 * RETURN:
5683 * None.
5685 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5687 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5688 INT nHorzPos = 0, nVertPos = 0;
5689 SCROLLINFO scrollInfo;
5691 scrollInfo.cbSize = sizeof(SCROLLINFO);
5692 scrollInfo.fMask = SIF_POS;
5694 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5695 nHorzPos = scrollInfo.nPos;
5696 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5697 nVertPos = scrollInfo.nPos;
5699 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5701 lpptOrigin->x = infoPtr->rcList.left;
5702 lpptOrigin->y = infoPtr->rcList.top;
5703 if (uView == LVS_LIST)
5704 nHorzPos *= infoPtr->nItemWidth;
5705 else if (uView == LVS_REPORT)
5706 nVertPos *= infoPtr->nItemHeight;
5708 lpptOrigin->x -= nHorzPos;
5709 lpptOrigin->y -= nVertPos;
5711 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5714 /***
5715 * DESCRIPTION:
5716 * Retrieves the width of a string.
5718 * PARAMETER(S):
5719 * [I] hwnd : window handle
5720 * [I] lpszText : text string to process
5721 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5723 * RETURN:
5724 * SUCCESS : string width (in pixels)
5725 * FAILURE : zero
5727 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5729 SIZE stringSize;
5731 stringSize.cx = 0;
5732 if (is_textT(lpszText, isW))
5734 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5735 HDC hdc = GetDC(infoPtr->hwndSelf);
5736 HFONT hOldFont = SelectObject(hdc, hFont);
5738 if (isW)
5739 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5740 else
5741 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5742 SelectObject(hdc, hOldFont);
5743 ReleaseDC(infoPtr->hwndSelf, hdc);
5745 return stringSize.cx;
5748 /***
5749 * DESCRIPTION:
5750 * Determines which listview item is located at the specified position.
5752 * PARAMETER(S):
5753 * [I] infoPtr : valid pointer to the listview structure
5754 * [IO] lpht : hit test information
5755 * [I] subitem : fill out iSubItem.
5756 * [I] select : return the index only if the hit selects the item
5758 * NOTE:
5759 * (mm 20001022): We must not allow iSubItem to be touched, for
5760 * an app might pass only a structure with space up to iItem!
5761 * (MS Office 97 does that for instance in the file open dialog)
5763 * RETURN:
5764 * SUCCESS : item index
5765 * FAILURE : -1
5767 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5769 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5770 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5771 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5772 POINT Origin, Position, opt;
5773 LVITEMW lvItem;
5774 ITERATOR i;
5775 INT iItem;
5777 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5779 lpht->flags = 0;
5780 lpht->iItem = -1;
5781 if (subitem) lpht->iSubItem = 0;
5783 if (infoPtr->rcList.left > lpht->pt.x)
5784 lpht->flags |= LVHT_TOLEFT;
5785 else if (infoPtr->rcList.right < lpht->pt.x)
5786 lpht->flags |= LVHT_TORIGHT;
5788 if (infoPtr->rcList.top > lpht->pt.y)
5789 lpht->flags |= LVHT_ABOVE;
5790 else if (infoPtr->rcList.bottom < lpht->pt.y)
5791 lpht->flags |= LVHT_BELOW;
5793 TRACE("lpht->flags=0x%x\n", lpht->flags);
5794 if (lpht->flags) return -1;
5796 lpht->flags |= LVHT_NOWHERE;
5798 LISTVIEW_GetOrigin(infoPtr, &Origin);
5800 /* first deal with the large items */
5801 rcSearch.left = lpht->pt.x;
5802 rcSearch.top = lpht->pt.y;
5803 rcSearch.right = rcSearch.left + 1;
5804 rcSearch.bottom = rcSearch.top + 1;
5806 iterator_frameditems(&i, infoPtr, &rcSearch);
5807 iterator_next(&i); /* go to first item in the sequence */
5808 iItem = i.nItem;
5809 iterator_destroy(&i);
5811 TRACE("lpht->iItem=%d\n", iItem);
5812 if (iItem == -1) return -1;
5814 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5815 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5816 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5817 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5818 lvItem.iItem = iItem;
5819 lvItem.iSubItem = 0;
5820 lvItem.pszText = szDispText;
5821 lvItem.cchTextMax = DISP_TEXT_SIZE;
5822 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5823 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5825 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5826 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5827 opt.x = lpht->pt.x - Position.x - Origin.x;
5828 opt.y = lpht->pt.y - Position.y - Origin.y;
5830 if (uView == LVS_REPORT)
5831 rcBounds = rcBox;
5832 else
5833 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5834 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5835 if (!PtInRect(&rcBounds, opt)) return -1;
5837 if (PtInRect(&rcIcon, opt))
5838 lpht->flags |= LVHT_ONITEMICON;
5839 else if (PtInRect(&rcLabel, opt))
5840 lpht->flags |= LVHT_ONITEMLABEL;
5841 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5842 lpht->flags |= LVHT_ONITEMSTATEICON;
5843 if (lpht->flags & LVHT_ONITEM)
5844 lpht->flags &= ~LVHT_NOWHERE;
5846 TRACE("lpht->flags=0x%x\n", lpht->flags);
5847 if (uView == LVS_REPORT && subitem)
5849 INT j;
5851 rcBounds.right = rcBounds.left;
5852 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5854 rcBounds.left = rcBounds.right;
5855 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5856 if (PtInRect(&rcBounds, opt))
5858 lpht->iSubItem = j;
5859 break;
5864 if (select && !(uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)))
5866 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5867 if (!PtInRect(&rcBounds, opt)) iItem = -1;
5869 return lpht->iItem = iItem;
5873 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5874 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5875 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5876 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5877 their own sort proc. when sending LVM_SORTITEMS.
5879 /* Platform SDK:
5880 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5882 LVS_SORTXXX must be specified,
5883 LVS_OWNERDRAW is not set,
5884 <item>.pszText is not LPSTR_TEXTCALLBACK.
5886 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5887 are sorted based on item text..."
5889 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5891 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5892 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5893 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5895 /* if we're sorting descending, negate the return value */
5896 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5899 /***
5900 * nESCRIPTION:
5901 * Inserts a new item in the listview control.
5903 * PARAMETER(S):
5904 * [I] infoPtr : valid pointer to the listview structure
5905 * [I] lpLVItem : item information
5906 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5908 * RETURN:
5909 * SUCCESS : new item index
5910 * FAILURE : -1
5912 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
5914 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5915 INT nItem;
5916 HDPA hdpaSubItems;
5917 NMLISTVIEW nmlv;
5918 ITEM_INFO *lpItem;
5919 BOOL is_sorted, has_changed;
5920 LVITEMW item;
5922 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5924 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5926 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5927 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5929 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5931 if ( !(lpItem = (ITEM_INFO *)COMCTL32_Alloc(sizeof(ITEM_INFO))) )
5932 return -1;
5934 /* insert item in listview control data structure */
5935 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5936 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5938 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5939 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5941 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5942 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5943 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5944 if (nItem == -1) goto fail;
5945 infoPtr->nItemCount++;
5947 /* shift indices first so they don't get tangled */
5948 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5950 /* set the item attributes */
5951 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5953 /* full size structure expected - _WIN32IE >= 0x560 */
5954 item = *lpLVItem;
5956 else if (lpLVItem->mask & LVIF_INDENT)
5958 /* indent member expected - _WIN32IE >= 0x300 */
5959 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
5961 else
5963 /* minimal structure expected */
5964 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
5966 item.iItem = nItem;
5967 item.state &= ~LVIS_STATEIMAGEMASK;
5968 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5970 /* if we're sorted, sort the list, and update the index */
5971 if (is_sorted)
5973 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5974 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5975 assert(nItem != -1);
5978 /* make room for the position, if we are in the right mode */
5979 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5981 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5982 goto undo;
5983 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5985 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5986 goto undo;
5990 /* send LVN_INSERTITEM notification */
5991 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5992 nmlv.iItem = nItem;
5993 nmlv.lParam = lpItem->lParam;
5994 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5996 /* align items (set position of each item) */
5997 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
5999 POINT pt;
6001 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6002 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6003 else
6004 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6006 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6009 /* now is the invalidation fun */
6010 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6011 return nItem;
6013 undo:
6014 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6015 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6016 infoPtr->nItemCount--;
6017 fail:
6018 DPA_DeletePtr(hdpaSubItems, 0);
6019 DPA_Destroy (hdpaSubItems);
6020 COMCTL32_Free (lpItem);
6021 return -1;
6024 /***
6025 * DESCRIPTION:
6026 * Redraws a range of items.
6028 * PARAMETER(S):
6029 * [I] infoPtr : valid pointer to the listview structure
6030 * [I] nFirst : first item
6031 * [I] nLast : last item
6033 * RETURN:
6034 * SUCCESS : TRUE
6035 * FAILURE : FALSE
6037 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6039 INT i;
6041 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6042 max(nFirst, nLast) >= infoPtr->nItemCount)
6043 return FALSE;
6045 for (i = nFirst; i <= nLast; i++)
6046 LISTVIEW_InvalidateItem(infoPtr, i);
6048 return TRUE;
6051 /***
6052 * DESCRIPTION:
6053 * Scroll the content of a listview.
6055 * PARAMETER(S):
6056 * [I] infoPtr : valid pointer to the listview structure
6057 * [I] dx : horizontal scroll amount in pixels
6058 * [I] dy : vertical scroll amount in pixels
6060 * RETURN:
6061 * SUCCESS : TRUE
6062 * FAILURE : FALSE
6064 * COMMENTS:
6065 * If the control is in report mode (LVS_REPORT) the control can
6066 * be scrolled only in line increments. "dy" will be rounded to the
6067 * nearest number of pixels that are a whole line. Ex: if line height
6068 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6069 * is passed the the scroll will be 0. (per MSDN 7/2002)
6071 * For: (per experimentaion with native control and CSpy ListView)
6072 * LVS_ICON dy=1 = 1 pixel (vertical only)
6073 * dx ignored
6074 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6075 * dx ignored
6076 * LVS_LIST dx=1 = 1 column (horizontal only)
6077 * but will only scroll 1 column per message
6078 * no matter what the value.
6079 * dy must be 0 or FALSE returned.
6080 * LVS_REPORT dx=1 = 1 pixel
6081 * dy= see above
6084 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6086 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6087 case LVS_REPORT:
6088 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6089 dy /= infoPtr->nItemHeight;
6090 break;
6091 case LVS_LIST:
6092 if (dy != 0) return FALSE;
6093 break;
6094 default: /* icon */
6095 dx = 0;
6096 break;
6099 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6100 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6102 return TRUE;
6105 /***
6106 * DESCRIPTION:
6107 * Sets the background color.
6109 * PARAMETER(S):
6110 * [I] infoPtr : valid pointer to the listview structure
6111 * [I] clrBk : background color
6113 * RETURN:
6114 * SUCCESS : TRUE
6115 * FAILURE : FALSE
6117 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6119 TRACE("(clrBk=%lx)\n", clrBk);
6121 if(infoPtr->clrBk != clrBk) {
6122 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6123 infoPtr->clrBk = clrBk;
6124 if (clrBk == CLR_NONE)
6125 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6126 else
6127 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6128 LISTVIEW_InvalidateList(infoPtr);
6131 return TRUE;
6134 /* LISTVIEW_SetBkImage */
6136 /*** Helper for {Insert,Set}ColumnT *only* */
6137 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6139 if (lpColumn->mask & LVCF_FMT)
6141 /* format member is valid */
6142 lphdi->mask |= HDI_FORMAT;
6144 /* set text alignment (leftmost column must be left-aligned) */
6145 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6146 lphdi->fmt |= HDF_LEFT;
6147 else if (lpColumn->fmt & LVCFMT_RIGHT)
6148 lphdi->fmt |= HDF_RIGHT;
6149 else if (lpColumn->fmt & LVCFMT_CENTER)
6150 lphdi->fmt |= HDF_CENTER;
6152 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6153 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6155 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6157 lphdi->fmt |= HDF_IMAGE;
6158 lphdi->iImage = I_IMAGECALLBACK;
6162 if (lpColumn->mask & LVCF_WIDTH)
6164 lphdi->mask |= HDI_WIDTH;
6165 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6167 /* make it fill the remainder of the controls width */
6168 RECT rcHeader;
6169 INT item_index;
6171 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6173 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6174 lphdi->cxy += rcHeader.right - rcHeader.left;
6177 /* retrieve the layout of the header */
6178 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6179 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6181 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6183 else
6184 lphdi->cxy = lpColumn->cx;
6187 if (lpColumn->mask & LVCF_TEXT)
6189 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6190 lphdi->fmt |= HDF_STRING;
6191 lphdi->pszText = lpColumn->pszText;
6192 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6195 if (lpColumn->mask & LVCF_IMAGE)
6197 lphdi->mask |= HDI_IMAGE;
6198 lphdi->iImage = lpColumn->iImage;
6201 if (lpColumn->mask & LVCF_ORDER)
6203 lphdi->mask |= HDI_ORDER;
6204 lphdi->iOrder = lpColumn->iOrder;
6209 /***
6210 * DESCRIPTION:
6211 * Inserts a new column.
6213 * PARAMETER(S):
6214 * [I] infoPtr : valid pointer to the listview structure
6215 * [I] nColumn : column index
6216 * [I] lpColumn : column information
6217 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6219 * RETURN:
6220 * SUCCESS : new column index
6221 * FAILURE : -1
6223 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6224 const LVCOLUMNW *lpColumn, BOOL isW)
6226 COLUMN_INFO *lpColumnInfo;
6227 INT nNewColumn;
6228 HDITEMW hdi;
6230 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6232 if (!lpColumn || nColumn < 0) return -1;
6233 nColumn = min(nColumn, infoPtr->hdpaColumns->nItemCount);
6235 ZeroMemory(&hdi, sizeof(HDITEMW));
6236 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6238 /* insert item in header control */
6239 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6240 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6241 (WPARAM)nColumn, (LPARAM)&hdi);
6242 if (nNewColumn == -1) return -1;
6243 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6245 /* create our own column info */
6246 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
6247 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6249 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6250 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6252 /* now we have to actually adjust the data */
6253 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6255 SUBITEM_INFO *lpSubItem;
6256 HDPA hdpaSubItems;
6257 INT nItem, i;
6259 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6261 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6262 for (i = 1; i < hdpaSubItems->nItemCount; i++)
6264 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6265 if (lpSubItem->iSubItem >= nNewColumn)
6266 lpSubItem->iSubItem++;
6271 /* make space for the new column */
6272 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6274 return nNewColumn;
6276 fail:
6277 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6278 if (lpColumnInfo)
6280 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6281 COMCTL32_Free(lpColumnInfo);
6283 return -1;
6286 /***
6287 * DESCRIPTION:
6288 * Sets the attributes of a header item.
6290 * PARAMETER(S):
6291 * [I] infoPtr : valid pointer to the listview structure
6292 * [I] nColumn : column index
6293 * [I] lpColumn : column attributes
6294 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6296 * RETURN:
6297 * SUCCESS : TRUE
6298 * FAILURE : FALSE
6300 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6301 const LVCOLUMNW *lpColumn, BOOL isW)
6303 HDITEMW hdi, hdiget;
6304 BOOL bResult;
6306 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6308 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6310 ZeroMemory(&hdi, sizeof(HDITEMW));
6311 if (lpColumn->mask & LVCF_FMT)
6313 hdi.mask |= HDI_FORMAT;
6314 hdiget.mask = HDI_FORMAT;
6315 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6316 hdi.fmt = hdiget.fmt & HDF_STRING;
6318 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6320 /* set header item attributes */
6321 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6322 if (!bResult) return FALSE;
6324 if (lpColumn->mask & LVCF_FMT)
6326 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6327 int oldFmt = lpColumnInfo->fmt;
6329 lpColumnInfo->fmt = lpColumn->fmt;
6330 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6332 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6333 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6337 return TRUE;
6340 /***
6341 * DESCRIPTION:
6342 * Sets the column order array
6344 * PARAMETERS:
6345 * [I] infoPtr : valid pointer to the listview structure
6346 * [I] iCount : number of elements in column order array
6347 * [I] lpiArray : pointer to column order array
6349 * RETURN:
6350 * SUCCESS : TRUE
6351 * FAILURE : FALSE
6353 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6355 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6357 if (!lpiArray)
6358 return FALSE;
6360 return TRUE;
6364 /***
6365 * DESCRIPTION:
6366 * Sets the width of a column
6368 * PARAMETERS:
6369 * [I] infoPtr : valid pointer to the listview structure
6370 * [I] nColumn : column index
6371 * [I] cx : column width
6373 * RETURN:
6374 * SUCCESS : TRUE
6375 * FAILURE : FALSE
6377 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6379 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6380 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6381 INT max_cx = 0;
6382 HDITEMW hdi;
6384 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6386 /* set column width only if in report or list mode */
6387 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6389 /* take care of invalid cx values */
6390 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6391 else if (uView == LVS_LIST && cx < 1) return FALSE;
6393 /* resize all columns if in LVS_LIST mode */
6394 if(uView == LVS_LIST)
6396 infoPtr->nItemWidth = cx;
6397 LISTVIEW_InvalidateList(infoPtr);
6398 return TRUE;
6401 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6403 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6405 INT nLabelWidth;
6406 LVITEMW lvItem;
6408 lvItem.mask = LVIF_TEXT;
6409 lvItem.iItem = 0;
6410 lvItem.iSubItem = nColumn;
6411 lvItem.pszText = szDispText;
6412 lvItem.cchTextMax = DISP_TEXT_SIZE;
6413 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6415 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6416 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6417 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6419 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6420 max_cx += infoPtr->iconSize.cx;
6421 max_cx += TRAILING_LABEL_PADDING;
6424 /* autosize based on listview items width */
6425 if(cx == LVSCW_AUTOSIZE)
6426 cx = max_cx;
6427 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6429 /* if iCol is the last column make it fill the remainder of the controls width */
6430 if(nColumn == infoPtr->hdpaColumns->nItemCount - 1)
6432 RECT rcHeader;
6433 POINT Origin;
6435 LISTVIEW_GetOrigin(infoPtr, &Origin);
6436 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6438 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6440 else
6442 /* Despite what the MS docs say, if this is not the last
6443 column, then MS resizes the column to the width of the
6444 largest text string in the column, including headers
6445 and items. This is different from LVSCW_AUTOSIZE in that
6446 LVSCW_AUTOSIZE ignores the header string length. */
6447 cx = 0;
6449 /* retrieve header text */
6450 hdi.mask = HDI_TEXT;
6451 hdi.cchTextMax = DISP_TEXT_SIZE;
6452 hdi.pszText = szDispText;
6453 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6455 HDC hdc = GetDC(infoPtr->hwndSelf);
6456 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6457 SIZE size;
6459 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6460 cx = size.cx + TRAILING_HEADER_PADDING;
6461 /* FIXME: Take into account the header image, if one is present */
6462 SelectObject(hdc, old_font);
6463 ReleaseDC(infoPtr->hwndSelf, hdc);
6465 cx = max (cx, max_cx);
6469 if (cx < 0) return FALSE;
6471 /* call header to update the column change */
6472 hdi.mask = HDI_WIDTH;
6473 hdi.cxy = cx;
6474 TRACE("hdi.cxy=%d\n", hdi.cxy);
6475 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6478 /***
6479 * DESCRIPTION:
6480 * Sets the extended listview style.
6482 * PARAMETERS:
6483 * [I] infoPtr : valid pointer to the listview structure
6484 * [I] dwMask : mask
6485 * [I] dwStyle : style
6487 * RETURN:
6488 * SUCCESS : previous style
6489 * FAILURE : 0
6491 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6493 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6495 /* set new style */
6496 if (dwMask)
6497 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6498 else
6499 infoPtr->dwLvExStyle = dwStyle;
6501 return dwOldStyle;
6504 /***
6505 * DESCRIPTION:
6506 * Sets the new hot cursor used during hot tracking and hover selection.
6508 * PARAMETER(S):
6509 * [I] infoPtr : valid pointer to the listview structure
6510 * [I} hCurosr : the new hot cursor handle
6512 * RETURN:
6513 * Returns the previous hot cursor
6515 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6517 HCURSOR oldCursor = infoPtr->hHotCursor;
6519 infoPtr->hHotCursor = hCursor;
6521 return oldCursor;
6525 /***
6526 * DESCRIPTION:
6527 * Sets the hot item index.
6529 * PARAMETERS:
6530 * [I] infoPtr : valid pointer to the listview structure
6531 * [I] iIndex : index
6533 * RETURN:
6534 * SUCCESS : previous hot item index
6535 * FAILURE : -1 (no hot item)
6537 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6539 INT iOldIndex = infoPtr->nHotItem;
6541 infoPtr->nHotItem = iIndex;
6543 return iOldIndex;
6547 /***
6548 * DESCRIPTION:
6549 * Sets the amount of time the cursor must hover over an item before it is selected.
6551 * PARAMETER(S):
6552 * [I] infoPtr : valid pointer to the listview structure
6553 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6555 * RETURN:
6556 * Returns the previous hover time
6558 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6560 DWORD oldHoverTime = infoPtr->dwHoverTime;
6562 infoPtr->dwHoverTime = dwHoverTime;
6564 return oldHoverTime;
6567 /***
6568 * DESCRIPTION:
6569 * Sets spacing for icons of LVS_ICON style.
6571 * PARAMETER(S):
6572 * [I] infoPtr : valid pointer to the listview structure
6573 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6574 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6576 * RETURN:
6577 * MAKELONG(oldcx, oldcy)
6579 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6581 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6582 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6584 TRACE("requested=(%d,%d)\n", cx, cy);
6586 /* this is supported only for LVS_ICON style */
6587 if (uView != LVS_ICON) return oldspacing;
6589 /* set to defaults, if instructed to */
6590 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6591 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6593 /* if 0 then compute width
6594 * FIXME: Should scan each item and determine max width of
6595 * icon or label, then make that the width */
6596 if (cx == 0)
6597 cx = infoPtr->iconSpacing.cx;
6599 /* if 0 then compute height */
6600 if (cy == 0)
6601 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6602 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6605 infoPtr->iconSpacing.cx = cx;
6606 infoPtr->iconSpacing.cy = cy;
6608 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6609 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6610 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6611 infoPtr->ntmHeight);
6613 /* these depend on the iconSpacing */
6614 LISTVIEW_UpdateItemSize(infoPtr);
6616 return oldspacing;
6619 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6621 INT cx, cy;
6623 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6625 size->cx = cx;
6626 size->cy = cy;
6628 else
6630 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6631 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6635 /***
6636 * DESCRIPTION:
6637 * Sets image lists.
6639 * PARAMETER(S):
6640 * [I] infoPtr : valid pointer to the listview structure
6641 * [I] nType : image list type
6642 * [I] himl : image list handle
6644 * RETURN:
6645 * SUCCESS : old image list
6646 * FAILURE : NULL
6648 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6650 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6651 INT oldHeight = infoPtr->nItemHeight;
6652 HIMAGELIST himlOld = 0;
6654 TRACE("(nType=%d, himl=%p\n", nType, himl);
6656 switch (nType)
6658 case LVSIL_NORMAL:
6659 himlOld = infoPtr->himlNormal;
6660 infoPtr->himlNormal = himl;
6661 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6662 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6663 break;
6665 case LVSIL_SMALL:
6666 himlOld = infoPtr->himlSmall;
6667 infoPtr->himlSmall = himl;
6668 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6669 break;
6671 case LVSIL_STATE:
6672 himlOld = infoPtr->himlState;
6673 infoPtr->himlState = himl;
6674 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6675 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6676 break;
6678 default:
6679 ERR("Unknown icon type=%d\n", nType);
6680 return NULL;
6683 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6684 if (infoPtr->nItemHeight != oldHeight)
6685 LISTVIEW_UpdateScroll(infoPtr);
6687 return himlOld;
6690 /***
6691 * DESCRIPTION:
6692 * Preallocates memory (does *not* set the actual count of items !)
6694 * PARAMETER(S):
6695 * [I] infoPtr : valid pointer to the listview structure
6696 * [I] nItems : item count (projected number of items to allocate)
6697 * [I] dwFlags : update flags
6699 * RETURN:
6700 * SUCCESS : TRUE
6701 * FAILURE : FALSE
6703 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6705 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6707 if (infoPtr->dwStyle & LVS_OWNERDATA)
6709 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6710 INT nOldCount = infoPtr->nItemCount;
6712 if (nItems < nOldCount)
6714 RANGE range = { nItems, nOldCount };
6715 ranges_del(infoPtr->selectionRanges, range);
6716 if (infoPtr->nFocusedItem >= nItems)
6718 infoPtr->nFocusedItem = -1;
6719 SetRectEmpty(&infoPtr->rcFocus);
6723 infoPtr->nItemCount = nItems;
6724 LISTVIEW_UpdateScroll(infoPtr);
6726 /* the flags are valid only in ownerdata report and list modes */
6727 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6729 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6730 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6732 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6733 LISTVIEW_InvalidateList(infoPtr);
6734 else
6736 INT nFrom, nTo;
6737 POINT Origin;
6738 RECT rcErase;
6740 LISTVIEW_GetOrigin(infoPtr, &Origin);
6741 nFrom = min(nOldCount, nItems);
6742 nTo = max(nOldCount, nItems);
6744 if (uView == LVS_REPORT)
6746 rcErase.left = 0;
6747 rcErase.top = nFrom * infoPtr->nItemHeight;
6748 rcErase.right = infoPtr->nItemWidth;
6749 rcErase.bottom = nTo * infoPtr->nItemHeight;
6750 OffsetRect(&rcErase, Origin.x, Origin.y);
6751 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6752 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6754 else /* LVS_LIST */
6756 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6758 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6759 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6760 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6761 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6762 OffsetRect(&rcErase, Origin.x, Origin.y);
6763 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6764 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6766 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6767 rcErase.top = 0;
6768 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6769 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6770 OffsetRect(&rcErase, Origin.x, Origin.y);
6771 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6772 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6776 else
6778 /* According to MSDN for non-LVS_OWNERDATA this is just
6779 * a performance issue. The control allocates its internal
6780 * data structures for the number of items specified. It
6781 * cuts down on the number of memory allocations. Therefore
6782 * we will just issue a WARN here
6784 WARN("for non-ownerdata performance option not implemented.\n");
6787 return TRUE;
6790 /***
6791 * DESCRIPTION:
6792 * Sets the position of an item.
6794 * PARAMETER(S):
6795 * [I] infoPtr : valid pointer to the listview structure
6796 * [I] nItem : item index
6797 * [I] pt : coordinate
6799 * RETURN:
6800 * SUCCESS : TRUE
6801 * FAILURE : FALSE
6803 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6805 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6806 POINT Origin;
6808 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6810 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6811 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6813 LISTVIEW_GetOrigin(infoPtr, &Origin);
6815 /* This point value seems to be an undocumented feature.
6816 * The best guess is that it means either at the origin,
6817 * or at true beginning of the list. I will assume the origin. */
6818 if ((pt.x == -1) && (pt.y == -1))
6819 pt = Origin;
6821 if (uView == LVS_ICON)
6823 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6824 pt.y -= ICON_TOP_PADDING;
6826 pt.x -= Origin.x;
6827 pt.y -= Origin.y;
6829 infoPtr->bAutoarrange = FALSE;
6831 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
6834 /***
6835 * DESCRIPTION:
6836 * Sets the state of one or many items.
6838 * PARAMETER(S):
6839 * [I] infoPtr : valid pointer to the listview structure
6840 * [I] nItem : item index
6841 * [I] lpLVItem : item or subitem info
6843 * RETURN:
6844 * SUCCESS : TRUE
6845 * FAILURE : FALSE
6847 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
6849 BOOL bResult = TRUE;
6850 LVITEMW lvItem;
6852 lvItem.iItem = nItem;
6853 lvItem.iSubItem = 0;
6854 lvItem.mask = LVIF_STATE;
6855 lvItem.state = lpLVItem->state;
6856 lvItem.stateMask = lpLVItem->stateMask;
6857 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6859 if (nItem == -1)
6861 /* apply to all items */
6862 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6863 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6865 else
6866 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6868 return bResult;
6871 /***
6872 * DESCRIPTION:
6873 * Sets the text of an item or subitem.
6875 * PARAMETER(S):
6876 * [I] hwnd : window handle
6877 * [I] nItem : item index
6878 * [I] lpLVItem : item or subitem info
6879 * [I] isW : TRUE if input is Unicode
6881 * RETURN:
6882 * SUCCESS : TRUE
6883 * FAILURE : FALSE
6885 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
6887 LVITEMW lvItem;
6889 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6891 lvItem.iItem = nItem;
6892 lvItem.iSubItem = lpLVItem->iSubItem;
6893 lvItem.mask = LVIF_TEXT;
6894 lvItem.pszText = lpLVItem->pszText;
6895 lvItem.cchTextMax = lpLVItem->cchTextMax;
6897 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6899 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6902 /***
6903 * DESCRIPTION:
6904 * Set item index that marks the start of a multiple selection.
6906 * PARAMETER(S):
6907 * [I] infoPtr : valid pointer to the listview structure
6908 * [I] nIndex : index
6910 * RETURN:
6911 * Index number or -1 if there is no selection mark.
6913 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6915 INT nOldIndex = infoPtr->nSelectionMark;
6917 TRACE("(nIndex=%d)\n", nIndex);
6919 infoPtr->nSelectionMark = nIndex;
6921 return nOldIndex;
6924 /***
6925 * DESCRIPTION:
6926 * Sets the text background color.
6928 * PARAMETER(S):
6929 * [I] infoPtr : valid pointer to the listview structure
6930 * [I] clrTextBk : text background color
6932 * RETURN:
6933 * SUCCESS : TRUE
6934 * FAILURE : FALSE
6936 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6938 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6940 if (infoPtr->clrTextBk != clrTextBk)
6942 infoPtr->clrTextBk = clrTextBk;
6943 LISTVIEW_InvalidateList(infoPtr);
6946 return TRUE;
6949 /***
6950 * DESCRIPTION:
6951 * Sets the text foreground color.
6953 * PARAMETER(S):
6954 * [I] infoPtr : valid pointer to the listview structure
6955 * [I] clrText : text color
6957 * RETURN:
6958 * SUCCESS : TRUE
6959 * FAILURE : FALSE
6961 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6963 TRACE("(clrText=%lx)\n", clrText);
6965 if (infoPtr->clrText != clrText)
6967 infoPtr->clrText = clrText;
6968 LISTVIEW_InvalidateList(infoPtr);
6971 return TRUE;
6974 /***
6975 * DESCRIPTION:
6976 * Determines which listview item is located at the specified position.
6978 * PARAMETER(S):
6979 * [I] infoPtr : valid pointer to the listview structure
6980 * [I] hwndNewToolTip : handle to new ToolTip
6982 * RETURN:
6983 * old tool tip
6985 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
6987 HWND hwndOldToolTip = infoPtr->hwndToolTip;
6988 infoPtr->hwndToolTip = hwndNewToolTip;
6989 return hwndOldToolTip;
6992 /* LISTVIEW_SetUnicodeFormat */
6993 /* LISTVIEW_SetWorkAreas */
6995 /***
6996 * DESCRIPTION:
6997 * Callback internally used by LISTVIEW_SortItems()
6999 * PARAMETER(S):
7000 * [I] first : pointer to first ITEM_INFO to compare
7001 * [I] second : pointer to second ITEM_INFO to compare
7002 * [I] lParam : HWND of control
7004 * RETURN:
7005 * if first comes before second : negative
7006 * if first comes after second : positive
7007 * if first and second are equivalent : zero
7009 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7011 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7012 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7013 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7015 /* Forward the call to the client defined callback */
7016 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7019 /***
7020 * DESCRIPTION:
7021 * Sorts the listview items.
7023 * PARAMETER(S):
7024 * [I] infoPtr : valid pointer to the listview structure
7025 * [I] pfnCompare : application-defined value
7026 * [I] lParamSort : pointer to comparision callback
7028 * RETURN:
7029 * SUCCESS : TRUE
7030 * FAILURE : FALSE
7032 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7034 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7035 HDPA hdpaSubItems;
7036 ITEM_INFO *lpItem;
7037 LPVOID selectionMarkItem;
7038 LVITEMW item;
7039 int i;
7041 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7043 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7045 if (!infoPtr->hdpaItems) return FALSE;
7047 /* if there are 0 or 1 items, there is no need to sort */
7048 if (infoPtr->nItemCount < 2) return TRUE;
7050 if (infoPtr->nFocusedItem >= 0)
7052 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7053 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7054 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7056 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7057 /* clear the lpItem->state for non-selected ones */
7058 /* remove the selection ranges */
7060 infoPtr->pfnCompare = pfnCompare;
7061 infoPtr->lParamSort = lParamSort;
7062 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7064 /* Adjust selections and indices so that they are the way they should
7065 * be after the sort (otherwise, the list items move around, but
7066 * whatever is at the item's previous original position will be
7067 * selected instead)
7069 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7070 for (i=0; i < infoPtr->nItemCount; i++)
7072 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7073 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7075 if (lpItem->state & LVIS_SELECTED)
7077 item.state = LVIS_SELECTED;
7078 item.stateMask = LVIS_SELECTED;
7079 LISTVIEW_SetItemState(infoPtr, i, &item);
7081 if (lpItem->state & LVIS_FOCUSED)
7083 infoPtr->nFocusedItem = i;
7084 lpItem->state &= ~LVIS_FOCUSED;
7087 if (selectionMarkItem != NULL)
7088 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7089 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7091 /* refresh the display */
7092 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7093 LISTVIEW_InvalidateList(infoPtr);
7095 return TRUE;
7098 /***
7099 * DESCRIPTION:
7100 * Updates an items or rearranges the listview control.
7102 * PARAMETER(S):
7103 * [I] infoPtr : valid pointer to the listview structure
7104 * [I] nItem : item index
7106 * RETURN:
7107 * SUCCESS : TRUE
7108 * FAILURE : FALSE
7110 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7112 TRACE("(nItem=%d)\n", nItem);
7114 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7116 /* rearrange with default alignment style */
7117 if (is_autoarrange(infoPtr))
7118 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7119 else
7120 LISTVIEW_InvalidateItem(infoPtr, nItem);
7122 return TRUE;
7126 /***
7127 * DESCRIPTION:
7128 * Creates the listview control.
7130 * PARAMETER(S):
7131 * [I] hwnd : window handle
7132 * [I] lpcs : the create parameters
7134 * RETURN:
7135 * Success: 0
7136 * Failure: -1
7138 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7140 LISTVIEW_INFO *infoPtr;
7141 UINT uView = lpcs->style & LVS_TYPEMASK;
7142 LOGFONTW logFont;
7144 TRACE("(lpcs=%p)\n", lpcs);
7146 /* initialize info pointer */
7147 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7148 if (!infoPtr) return -1;
7150 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7152 infoPtr->hwndSelf = hwnd;
7153 infoPtr->dwStyle = lpcs->style;
7154 /* determine the type of structures to use */
7155 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7156 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7158 /* initialize color information */
7159 infoPtr->clrBk = CLR_NONE;
7160 infoPtr->clrText = comctl32_color.clrWindowText;
7161 infoPtr->clrTextBk = CLR_DEFAULT;
7162 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7164 /* set default values */
7165 infoPtr->nFocusedItem = -1;
7166 infoPtr->nSelectionMark = -1;
7167 infoPtr->nHotItem = -1;
7168 infoPtr->bRedraw = TRUE;
7169 infoPtr->bNoItemMetrics = TRUE;
7170 infoPtr->bDoChangeNotify = TRUE;
7171 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7172 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7173 infoPtr->nEditLabelItem = -1;
7174 infoPtr->dwHoverTime = -1; /* default system hover time */
7176 /* get default font (icon title) */
7177 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7178 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7179 infoPtr->hFont = infoPtr->hDefaultFont;
7180 LISTVIEW_SaveTextMetrics(infoPtr);
7182 /* create header */
7183 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7184 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7185 0, 0, 0, 0, hwnd, NULL,
7186 lpcs->hInstance, NULL);
7187 if (!infoPtr->hwndHeader) goto fail;
7189 /* set header unicode format */
7190 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7192 /* set header font */
7193 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7195 /* allocate memory for the data structure */
7196 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7197 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7198 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7199 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7200 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7202 /* initialize the icon sizes */
7203 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7204 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7206 /* init item size to avoid division by 0 */
7207 LISTVIEW_UpdateItemSize (infoPtr);
7209 if (uView == LVS_REPORT)
7211 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7213 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7215 else
7217 /* set HDS_HIDDEN flag to hide the header bar */
7218 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7219 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7223 return 0;
7225 fail:
7226 DestroyWindow(infoPtr->hwndHeader);
7227 ranges_destroy(infoPtr->selectionRanges);
7228 DPA_Destroy(infoPtr->hdpaItems);
7229 DPA_Destroy(infoPtr->hdpaPosX);
7230 DPA_Destroy(infoPtr->hdpaPosY);
7231 DPA_Destroy(infoPtr->hdpaColumns);
7232 COMCTL32_Free(infoPtr);
7233 return -1;
7236 /***
7237 * DESCRIPTION:
7238 * Erases the background of the listview control.
7240 * PARAMETER(S):
7241 * [I] infoPtr : valid pointer to the listview structure
7242 * [I] hdc : device context handle
7244 * RETURN:
7245 * SUCCESS : TRUE
7246 * FAILURE : FALSE
7248 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7250 RECT rc;
7252 TRACE("(hdc=%p)\n", hdc);
7254 if (!GetClipBox(hdc, &rc)) return FALSE;
7256 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7260 /***
7261 * DESCRIPTION:
7262 * Helper function for LISTVIEW_[HV]Scroll *only*.
7263 * Performs vertical/horizontal scrolling by a give amount.
7265 * PARAMETER(S):
7266 * [I] infoPtr : valid pointer to the listview structure
7267 * [I] dx : amount of horizontal scroll
7268 * [I] dy : amount of vertical scroll
7270 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7272 /* now we can scroll the list */
7273 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7274 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7275 /* if we have focus, adjust rect */
7276 OffsetRect(&infoPtr->rcFocus, dx, dy);
7277 UpdateWindow(infoPtr->hwndSelf);
7280 /***
7281 * DESCRIPTION:
7282 * Performs vertical scrolling.
7284 * PARAMETER(S):
7285 * [I] infoPtr : valid pointer to the listview structure
7286 * [I] nScrollCode : scroll code
7287 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7288 * [I] hScrollWnd : scrollbar control window handle
7290 * RETURN:
7291 * Zero
7293 * NOTES:
7294 * SB_LINEUP/SB_LINEDOWN:
7295 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7296 * for LVS_REPORT is 1 line
7297 * for LVS_LIST cannot occur
7300 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7301 INT nScrollDiff, HWND hScrollWnd)
7303 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7304 INT nOldScrollPos, nNewScrollPos;
7305 SCROLLINFO scrollInfo;
7306 BOOL is_an_icon;
7308 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7309 debugscrollcode(nScrollCode), nScrollDiff);
7311 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7313 scrollInfo.cbSize = sizeof(SCROLLINFO);
7314 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7316 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7318 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7320 nOldScrollPos = scrollInfo.nPos;
7321 switch (nScrollCode)
7323 case SB_INTERNAL:
7324 break;
7326 case SB_LINEUP:
7327 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7328 break;
7330 case SB_LINEDOWN:
7331 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7332 break;
7334 case SB_PAGEUP:
7335 nScrollDiff = -scrollInfo.nPage;
7336 break;
7338 case SB_PAGEDOWN:
7339 nScrollDiff = scrollInfo.nPage;
7340 break;
7342 case SB_THUMBPOSITION:
7343 case SB_THUMBTRACK:
7344 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7345 break;
7347 default:
7348 nScrollDiff = 0;
7351 /* quit right away if pos isn't changing */
7352 if (nScrollDiff == 0) return 0;
7354 /* calculate new position, and handle overflows */
7355 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7356 if (nScrollDiff > 0) {
7357 if (nNewScrollPos < nOldScrollPos ||
7358 nNewScrollPos > scrollInfo.nMax)
7359 nNewScrollPos = scrollInfo.nMax;
7360 } else {
7361 if (nNewScrollPos > nOldScrollPos ||
7362 nNewScrollPos < scrollInfo.nMin)
7363 nNewScrollPos = scrollInfo.nMin;
7366 /* set the new position, and reread in case it changed */
7367 scrollInfo.fMask = SIF_POS;
7368 scrollInfo.nPos = nNewScrollPos;
7369 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7371 /* carry on only if it really changed */
7372 if (nNewScrollPos == nOldScrollPos) return 0;
7374 /* now adjust to client coordinates */
7375 nScrollDiff = nOldScrollPos - nNewScrollPos;
7376 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7378 /* and scroll the window */
7379 scroll_list(infoPtr, 0, nScrollDiff);
7381 return 0;
7384 /***
7385 * DESCRIPTION:
7386 * Performs horizontal scrolling.
7388 * PARAMETER(S):
7389 * [I] infoPtr : valid pointer to the listview structure
7390 * [I] nScrollCode : scroll code
7391 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7392 * [I] hScrollWnd : scrollbar control window handle
7394 * RETURN:
7395 * Zero
7397 * NOTES:
7398 * SB_LINELEFT/SB_LINERIGHT:
7399 * for LVS_ICON, LVS_SMALLICON 1 pixel
7400 * for LVS_REPORT is 1 pixel
7401 * for LVS_LIST is 1 column --> which is a 1 because the
7402 * scroll is based on columns not pixels
7405 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7406 INT nScrollDiff, HWND hScrollWnd)
7408 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7409 INT nOldScrollPos, nNewScrollPos;
7410 SCROLLINFO scrollInfo;
7412 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7413 debugscrollcode(nScrollCode), nScrollDiff);
7415 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7417 scrollInfo.cbSize = sizeof(SCROLLINFO);
7418 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7420 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7422 nOldScrollPos = scrollInfo.nPos;
7424 switch (nScrollCode)
7426 case SB_INTERNAL:
7427 break;
7429 case SB_LINELEFT:
7430 nScrollDiff = -1;
7431 break;
7433 case SB_LINERIGHT:
7434 nScrollDiff = 1;
7435 break;
7437 case SB_PAGELEFT:
7438 nScrollDiff = -scrollInfo.nPage;
7439 break;
7441 case SB_PAGERIGHT:
7442 nScrollDiff = scrollInfo.nPage;
7443 break;
7445 case SB_THUMBPOSITION:
7446 case SB_THUMBTRACK:
7447 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7448 break;
7450 default:
7451 nScrollDiff = 0;
7454 /* quit right away if pos isn't changing */
7455 if (nScrollDiff == 0) return 0;
7457 /* calculate new position, and handle overflows */
7458 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7459 if (nScrollDiff > 0) {
7460 if (nNewScrollPos < nOldScrollPos ||
7461 nNewScrollPos > scrollInfo.nMax)
7462 nNewScrollPos = scrollInfo.nMax;
7463 } else {
7464 if (nNewScrollPos > nOldScrollPos ||
7465 nNewScrollPos < scrollInfo.nMin)
7466 nNewScrollPos = scrollInfo.nMin;
7469 /* set the new position, and reread in case it changed */
7470 scrollInfo.fMask = SIF_POS;
7471 scrollInfo.nPos = nNewScrollPos;
7472 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7474 /* carry on only if it really changed */
7475 if (nNewScrollPos == nOldScrollPos) return 0;
7477 if(uView == LVS_REPORT)
7478 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7480 /* now adjust to client coordinates */
7481 nScrollDiff = nOldScrollPos - nNewScrollPos;
7482 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7484 /* and scroll the window */
7485 scroll_list(infoPtr, nScrollDiff, 0);
7487 return 0;
7490 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7492 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7493 INT gcWheelDelta = 0;
7494 UINT pulScrollLines = 3;
7495 SCROLLINFO scrollInfo;
7497 TRACE("(wheelDelta=%d)\n", wheelDelta);
7499 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7500 gcWheelDelta -= wheelDelta;
7502 scrollInfo.cbSize = sizeof(SCROLLINFO);
7503 scrollInfo.fMask = SIF_POS;
7505 switch(uView)
7507 case LVS_ICON:
7508 case LVS_SMALLICON:
7510 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7511 * should be fixed in the future.
7513 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7514 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7515 break;
7517 case LVS_REPORT:
7518 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7520 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7521 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7522 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7524 break;
7526 case LVS_LIST:
7527 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7528 break;
7530 return 0;
7533 /***
7534 * DESCRIPTION:
7535 * ???
7537 * PARAMETER(S):
7538 * [I] infoPtr : valid pointer to the listview structure
7539 * [I] nVirtualKey : virtual key
7540 * [I] lKeyData : key data
7542 * RETURN:
7543 * Zero
7545 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7547 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7548 INT nItem = -1;
7549 NMLVKEYDOWN nmKeyDown;
7551 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7553 /* send LVN_KEYDOWN notification */
7554 nmKeyDown.wVKey = nVirtualKey;
7555 nmKeyDown.flags = 0;
7556 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7558 switch (nVirtualKey)
7560 case VK_RETURN:
7561 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7563 notify(infoPtr, NM_RETURN);
7564 notify(infoPtr, LVN_ITEMACTIVATE);
7566 break;
7568 case VK_HOME:
7569 if (infoPtr->nItemCount > 0)
7570 nItem = 0;
7571 break;
7573 case VK_END:
7574 if (infoPtr->nItemCount > 0)
7575 nItem = infoPtr->nItemCount - 1;
7576 break;
7578 case VK_LEFT:
7579 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7580 break;
7582 case VK_UP:
7583 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7584 break;
7586 case VK_RIGHT:
7587 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7588 break;
7590 case VK_DOWN:
7591 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7592 break;
7594 case VK_PRIOR:
7595 if (uView == LVS_REPORT)
7596 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7597 else
7598 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7599 * LISTVIEW_GetCountPerRow(infoPtr);
7600 if(nItem < 0) nItem = 0;
7601 break;
7603 case VK_NEXT:
7604 if (uView == LVS_REPORT)
7605 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7606 else
7607 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7608 * LISTVIEW_GetCountPerRow(infoPtr);
7609 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7610 break;
7613 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7614 LISTVIEW_KeySelection(infoPtr, nItem);
7616 return 0;
7619 /***
7620 * DESCRIPTION:
7621 * Kills the focus.
7623 * PARAMETER(S):
7624 * [I] infoPtr : valid pointer to the listview structure
7626 * RETURN:
7627 * Zero
7629 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7631 TRACE("()\n");
7633 /* if we did not have the focus, there's nothing to do */
7634 if (!infoPtr->bFocus) return 0;
7636 /* send NM_KILLFOCUS notification */
7637 notify(infoPtr, NM_KILLFOCUS);
7639 /* if we have a focus rectagle, get rid of it */
7640 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7642 /* set window focus flag */
7643 infoPtr->bFocus = FALSE;
7645 /* invalidate the selected items before reseting focus flag */
7646 LISTVIEW_InvalidateSelectedItems(infoPtr);
7648 return 0;
7651 /***
7652 * DESCRIPTION:
7653 * Processes double click messages (left mouse button).
7655 * PARAMETER(S):
7656 * [I] infoPtr : valid pointer to the listview structure
7657 * [I] wKey : key flag
7658 * [I] pts : mouse coordinate
7660 * RETURN:
7661 * Zero
7663 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7665 LVHITTESTINFO htInfo;
7667 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7669 /* send NM_RELEASEDCAPTURE notification */
7670 notify(infoPtr, NM_RELEASEDCAPTURE);
7672 htInfo.pt.x = pts.x;
7673 htInfo.pt.y = pts.y;
7675 /* send NM_DBLCLK notification */
7676 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7677 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7679 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7680 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7682 return 0;
7685 /***
7686 * DESCRIPTION:
7687 * Processes mouse down messages (left mouse button).
7689 * PARAMETER(S):
7690 * [I] infoPtr : valid pointer to the listview structure
7691 * [I] wKey : key flag
7692 * [I] pts : mouse coordinate
7694 * RETURN:
7695 * Zero
7697 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7699 LVHITTESTINFO lvHitTestInfo;
7700 static BOOL bGroupSelect = TRUE;
7701 POINT pt = { pts.x, pts.y };
7702 INT nItem;
7704 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7706 /* send NM_RELEASEDCAPTURE notification */
7707 notify(infoPtr, NM_RELEASEDCAPTURE);
7709 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7711 /* set left button down flag */
7712 infoPtr->bLButtonDown = TRUE;
7714 lvHitTestInfo.pt.x = pts.x;
7715 lvHitTestInfo.pt.y = pts.y;
7717 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7718 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7719 infoPtr->nEditLabelItem = -1;
7720 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7722 if (infoPtr->dwStyle & LVS_SINGLESEL)
7724 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7725 infoPtr->nEditLabelItem = nItem;
7726 else
7727 LISTVIEW_SetSelection(infoPtr, nItem);
7729 else
7731 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7733 if (bGroupSelect)
7735 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7736 LISTVIEW_SetItemFocus(infoPtr, nItem);
7737 infoPtr->nSelectionMark = nItem;
7739 else
7741 LVITEMW item;
7743 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7744 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7746 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7747 infoPtr->nSelectionMark = nItem;
7750 else if (wKey & MK_CONTROL)
7752 LVITEMW item;
7754 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7756 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7757 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7758 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7759 infoPtr->nSelectionMark = nItem;
7761 else if (wKey & MK_SHIFT)
7763 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7765 else
7767 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7768 infoPtr->nEditLabelItem = nItem;
7770 /* set selection (clears other pre-existing selections) */
7771 LISTVIEW_SetSelection(infoPtr, nItem);
7775 else
7777 /* remove all selections */
7778 LISTVIEW_DeselectAll(infoPtr);
7781 return 0;
7784 /***
7785 * DESCRIPTION:
7786 * Processes mouse up messages (left mouse button).
7788 * PARAMETER(S):
7789 * [I] infoPtr : valid pointer to the listview structure
7790 * [I] wKey : key flag
7791 * [I] pts : mouse coordinate
7793 * RETURN:
7794 * Zero
7796 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7798 LVHITTESTINFO lvHitTestInfo;
7800 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7802 if (!infoPtr->bLButtonDown) return 0;
7804 lvHitTestInfo.pt.x = pts.x;
7805 lvHitTestInfo.pt.y = pts.y;
7807 /* send NM_CLICK notification */
7808 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7809 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7811 /* set left button flag */
7812 infoPtr->bLButtonDown = FALSE;
7814 /* if we clicked on a selected item, edit the label */
7815 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7816 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7818 return 0;
7821 /***
7822 * DESCRIPTION:
7823 * Destroys the listview control (called after WM_DESTROY).
7825 * PARAMETER(S):
7826 * [I] infoPtr : valid pointer to the listview structure
7828 * RETURN:
7829 * Zero
7831 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7833 TRACE("()\n");
7835 /* delete all items */
7836 LISTVIEW_DeleteAllItems(infoPtr);
7838 /* destroy data structure */
7839 DPA_Destroy(infoPtr->hdpaItems);
7840 DPA_Destroy(infoPtr->hdpaPosX);
7841 DPA_Destroy(infoPtr->hdpaPosY);
7842 DPA_Destroy(infoPtr->hdpaColumns);
7843 ranges_destroy(infoPtr->selectionRanges);
7845 /* destroy image lists */
7846 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7848 if (infoPtr->himlNormal)
7849 ImageList_Destroy(infoPtr->himlNormal);
7850 if (infoPtr->himlSmall)
7851 ImageList_Destroy(infoPtr->himlSmall);
7852 if (infoPtr->himlState)
7853 ImageList_Destroy(infoPtr->himlState);
7856 /* destroy font, bkgnd brush */
7857 infoPtr->hFont = 0;
7858 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7859 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7861 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7863 /* free listview info pointer*/
7864 COMCTL32_Free(infoPtr);
7866 return 0;
7869 /***
7870 * DESCRIPTION:
7871 * Handles notifications from header.
7873 * PARAMETER(S):
7874 * [I] infoPtr : valid pointer to the listview structure
7875 * [I] nCtrlId : control identifier
7876 * [I] lpnmh : notification information
7878 * RETURN:
7879 * Zero
7881 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
7883 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7885 TRACE("(lpnmh=%p)\n", lpnmh);
7887 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7889 switch (lpnmh->hdr.code)
7891 case HDN_TRACKW:
7892 case HDN_TRACKA:
7893 case HDN_ITEMCHANGEDW:
7894 case HDN_ITEMCHANGEDA:
7896 COLUMN_INFO *lpColumnInfo;
7897 INT dx, cxy;
7899 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7901 HDITEMW hdi;
7903 hdi.mask = HDI_WIDTH;
7904 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7905 cxy = hdi.cxy;
7907 else
7908 cxy = lpnmh->pitem->cxy;
7910 /* determine how much we change since the last know position */
7911 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7912 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7913 if (dx != 0)
7915 RECT rcCol = lpColumnInfo->rcHeader;
7917 lpColumnInfo->rcHeader.right += dx;
7918 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7919 if (uView == LVS_REPORT && is_redrawing(infoPtr))
7921 /* this trick works for left aligned columns only */
7922 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7924 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
7925 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
7927 rcCol.top = infoPtr->rcList.top;
7928 rcCol.bottom = infoPtr->rcList.bottom;
7929 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7933 break;
7935 case HDN_ITEMCLICKW:
7936 case HDN_ITEMCLICKA:
7938 /* Handle sorting by Header Column */
7939 NMLISTVIEW nmlv;
7941 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7942 nmlv.iItem = -1;
7943 nmlv.iSubItem = lpnmh->iItem;
7944 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7946 break;
7949 return 0;
7952 /***
7953 * DESCRIPTION:
7954 * Determines the type of structure to use.
7956 * PARAMETER(S):
7957 * [I] infoPtr : valid pointer to the listview structureof the sender
7958 * [I] hwndFrom : listview window handle
7959 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7961 * RETURN:
7962 * Zero
7964 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7966 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
7968 if (nCommand != NF_REQUERY) return 0;
7970 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
7972 return 0;
7975 /***
7976 * DESCRIPTION:
7977 * Paints/Repaints the listview control.
7979 * PARAMETER(S):
7980 * [I] infoPtr : valid pointer to the listview structure
7981 * [I] hdc : device context handle
7983 * RETURN:
7984 * Zero
7986 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7988 TRACE("(hdc=%p)\n", hdc);
7990 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
7992 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7994 infoPtr->bNoItemMetrics = FALSE;
7995 LISTVIEW_UpdateItemSize(infoPtr);
7996 if (uView == LVS_ICON || uView == LVS_SMALLICON)
7997 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7998 LISTVIEW_UpdateScroll(infoPtr);
8000 if (hdc)
8001 LISTVIEW_Refresh(infoPtr, hdc);
8002 else
8004 PAINTSTRUCT ps;
8006 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8007 if (!hdc) return 1;
8008 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8009 LISTVIEW_Refresh(infoPtr, hdc);
8010 EndPaint(infoPtr->hwndSelf, &ps);
8013 return 0;
8016 /***
8017 * DESCRIPTION:
8018 * Processes double click messages (right mouse button).
8020 * PARAMETER(S):
8021 * [I] infoPtr : valid pointer to the listview structure
8022 * [I] wKey : key flag
8023 * [I] pts : mouse coordinate
8025 * RETURN:
8026 * Zero
8028 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8030 LVHITTESTINFO lvHitTestInfo;
8032 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8034 /* send NM_RELEASEDCAPTURE notification */
8035 notify(infoPtr, NM_RELEASEDCAPTURE);
8037 /* send NM_RDBLCLK notification */
8038 lvHitTestInfo.pt.x = pts.x;
8039 lvHitTestInfo.pt.y = pts.y;
8040 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8041 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8043 return 0;
8046 /***
8047 * DESCRIPTION:
8048 * Processes mouse down messages (right mouse button).
8050 * PARAMETER(S):
8051 * [I] infoPtr : valid pointer to the listview structure
8052 * [I] wKey : key flag
8053 * [I] pts : mouse coordinate
8055 * RETURN:
8056 * Zero
8058 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8060 LVHITTESTINFO lvHitTestInfo;
8061 INT nItem;
8063 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8065 /* send NM_RELEASEDCAPTURE notification */
8066 notify(infoPtr, NM_RELEASEDCAPTURE);
8068 /* make sure the listview control window has the focus */
8069 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8071 /* set right button down flag */
8072 infoPtr->bRButtonDown = TRUE;
8074 /* determine the index of the selected item */
8075 lvHitTestInfo.pt.x = pts.x;
8076 lvHitTestInfo.pt.y = pts.y;
8077 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8079 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8081 LISTVIEW_SetItemFocus(infoPtr, nItem);
8082 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8083 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8084 LISTVIEW_SetSelection(infoPtr, nItem);
8086 else
8088 LISTVIEW_DeselectAll(infoPtr);
8091 return 0;
8094 /***
8095 * DESCRIPTION:
8096 * Processes mouse up messages (right mouse button).
8098 * PARAMETER(S):
8099 * [I] infoPtr : valid pointer to the listview structure
8100 * [I] wKey : key flag
8101 * [I] pts : mouse coordinate
8103 * RETURN:
8104 * Zero
8106 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8108 LVHITTESTINFO lvHitTestInfo;
8109 POINT pt;
8111 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8113 if (!infoPtr->bRButtonDown) return 0;
8115 /* set button flag */
8116 infoPtr->bRButtonDown = FALSE;
8118 /* Send NM_RClICK notification */
8119 lvHitTestInfo.pt.x = pts.x;
8120 lvHitTestInfo.pt.y = pts.y;
8121 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8122 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8124 /* Change to screen coordinate for WM_CONTEXTMENU */
8125 pt = lvHitTestInfo.pt;
8126 ClientToScreen(infoPtr->hwndSelf, &pt);
8128 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8129 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8130 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8132 return 0;
8136 /***
8137 * DESCRIPTION:
8138 * Sets the cursor.
8140 * PARAMETER(S):
8141 * [I] infoPtr : valid pointer to the listview structure
8142 * [I] hwnd : window handle of window containing the cursor
8143 * [I] nHittest : hit-test code
8144 * [I] wMouseMsg : ideintifier of the mouse message
8146 * RETURN:
8147 * TRUE if cursor is set
8148 * FALSE otherwise
8150 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8152 LVHITTESTINFO lvHitTestInfo;
8154 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8156 if(!infoPtr->hHotCursor) return FALSE;
8158 GetCursorPos(&lvHitTestInfo.pt);
8159 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8161 SetCursor(infoPtr->hHotCursor);
8163 return TRUE;
8166 /***
8167 * DESCRIPTION:
8168 * Sets the focus.
8170 * PARAMETER(S):
8171 * [I] infoPtr : valid pointer to the listview structure
8172 * [I] hwndLoseFocus : handle of previously focused window
8174 * RETURN:
8175 * Zero
8177 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8179 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8181 /* if we have the focus already, there's nothing to do */
8182 if (infoPtr->bFocus) return 0;
8184 /* send NM_SETFOCUS notification */
8185 notify(infoPtr, NM_SETFOCUS);
8187 /* set window focus flag */
8188 infoPtr->bFocus = TRUE;
8190 /* put the focus rect back on */
8191 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8193 /* redraw all visible selected items */
8194 LISTVIEW_InvalidateSelectedItems(infoPtr);
8196 return 0;
8199 /***
8200 * DESCRIPTION:
8201 * Sets the font.
8203 * PARAMETER(S):
8204 * [I] infoPtr : valid pointer to the listview structure
8205 * [I] fRedraw : font handle
8206 * [I] fRedraw : redraw flag
8208 * RETURN:
8209 * Zero
8211 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8213 HFONT oldFont = infoPtr->hFont;
8215 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8217 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8218 if (infoPtr->hFont == oldFont) return 0;
8220 LISTVIEW_SaveTextMetrics(infoPtr);
8222 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8223 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8225 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8227 return 0;
8230 /***
8231 * DESCRIPTION:
8232 * Message handling for WM_SETREDRAW.
8233 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8235 * PARAMETER(S):
8236 * [I] infoPtr : valid pointer to the listview structure
8237 * [I] bRedraw: state of redraw flag
8239 * RETURN:
8240 * DefWinProc return value
8242 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8244 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8246 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8247 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8249 infoPtr->bRedraw = bRedraw;
8251 if(!bRedraw) return 0;
8253 if (is_autoarrange(infoPtr))
8254 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8255 LISTVIEW_UpdateScroll(infoPtr);
8257 /* despite what the WM_SETREDRAW docs says, apps expect us
8258 * to invalidate the listview here... stupid! */
8259 LISTVIEW_InvalidateList(infoPtr);
8261 return 0;
8264 /***
8265 * DESCRIPTION:
8266 * Resizes the listview control. This function processes WM_SIZE
8267 * messages. At this time, the width and height are not used.
8269 * PARAMETER(S):
8270 * [I] infoPtr : valid pointer to the listview structure
8271 * [I] Width : new width
8272 * [I] Height : new height
8274 * RETURN:
8275 * Zero
8277 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8279 RECT rcOld = infoPtr->rcList;
8281 TRACE("(width=%d, height=%d)\n", Width, Height);
8283 LISTVIEW_UpdateSize(infoPtr);
8284 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8286 /* do not bother with display related stuff if we're not redrawing */
8287 if (!is_redrawing(infoPtr)) return 0;
8289 if (is_autoarrange(infoPtr))
8290 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8292 LISTVIEW_UpdateScroll(infoPtr);
8294 /* refresh all only for lists whose height changed significantly */
8295 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8296 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8297 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8298 LISTVIEW_InvalidateList(infoPtr);
8300 return 0;
8303 /***
8304 * DESCRIPTION:
8305 * Sets the size information.
8307 * PARAMETER(S):
8308 * [I] infoPtr : valid pointer to the listview structure
8310 * RETURN:
8311 * None
8313 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8315 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8317 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8319 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8321 if (uView == LVS_LIST)
8323 /* Apparently the "LIST" style is supposed to have the same
8324 * number of items in a column even if there is no scroll bar.
8325 * Since if a scroll bar already exists then the bottom is already
8326 * reduced, only reduce if the scroll bar does not currently exist.
8327 * The "2" is there to mimic the native control. I think it may be
8328 * related to either padding or edges. (GLA 7/2002)
8330 if (!(infoPtr->dwStyle & WS_HSCROLL))
8331 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8332 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8334 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8336 HDLAYOUT hl;
8337 WINDOWPOS wp;
8339 hl.prc = &infoPtr->rcList;
8340 hl.pwpos = &wp;
8341 Header_Layout(infoPtr->hwndHeader, &hl);
8343 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8345 infoPtr->rcList.top = max(wp.cy, 0);
8348 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8351 /***
8352 * DESCRIPTION:
8353 * Processes WM_STYLECHANGED messages.
8355 * PARAMETER(S):
8356 * [I] infoPtr : valid pointer to the listview structure
8357 * [I] wStyleType : window style type (normal or extended)
8358 * [I] lpss : window style information
8360 * RETURN:
8361 * Zero
8363 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8364 const STYLESTRUCT *lpss)
8366 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8367 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8369 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8370 wStyleType, lpss->styleOld, lpss->styleNew);
8372 if (wStyleType != GWL_STYLE) return 0;
8374 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8375 /* what if LVS_OWNERDATA changed? */
8376 /* or LVS_SINGLESEL */
8377 /* or LVS_SORT{AS,DES}CENDING */
8379 infoPtr->dwStyle = lpss->styleNew;
8381 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8382 ((lpss->styleNew & WS_HSCROLL) == 0))
8383 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8385 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8386 ((lpss->styleNew & WS_VSCROLL) == 0))
8387 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8389 if (uNewView != uOldView)
8391 SIZE oldIconSize = infoPtr->iconSize;
8392 HIMAGELIST himl;
8394 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8395 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8397 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8398 SetRectEmpty(&infoPtr->rcFocus);
8400 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8401 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8403 if (uNewView == LVS_ICON)
8405 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8407 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8408 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8409 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8412 else if (uNewView == LVS_REPORT)
8414 HDLAYOUT hl;
8415 WINDOWPOS wp;
8417 hl.prc = &infoPtr->rcList;
8418 hl.pwpos = &wp;
8419 Header_Layout(infoPtr->hwndHeader, &hl);
8420 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8423 LISTVIEW_UpdateItemSize(infoPtr);
8426 if (uNewView == LVS_REPORT)
8427 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8429 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8430 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8431 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8433 /* update the size of the client area */
8434 LISTVIEW_UpdateSize(infoPtr);
8436 /* add scrollbars if needed */
8437 LISTVIEW_UpdateScroll(infoPtr);
8439 /* invalidate client area + erase background */
8440 LISTVIEW_InvalidateList(infoPtr);
8442 return 0;
8445 /***
8446 * DESCRIPTION:
8447 * Window procedure of the listview control.
8450 static LRESULT WINAPI
8451 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8453 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8455 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8457 if (!infoPtr && (uMsg != WM_CREATE))
8458 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8460 if (infoPtr)
8462 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8465 switch (uMsg)
8467 case LVM_APPROXIMATEVIEWRECT:
8468 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8469 LOWORD(lParam), HIWORD(lParam));
8470 case LVM_ARRANGE:
8471 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8473 /* case LVM_CANCELEDITLABEL: */
8475 /* case LVM_CREATEDRAGIMAGE: */
8477 case LVM_DELETEALLITEMS:
8478 return LISTVIEW_DeleteAllItems(infoPtr);
8480 case LVM_DELETECOLUMN:
8481 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8483 case LVM_DELETEITEM:
8484 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8486 case LVM_EDITLABELW:
8487 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8489 case LVM_EDITLABELA:
8490 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8492 /* case LVM_ENABLEGROUPVIEW: */
8494 case LVM_ENSUREVISIBLE:
8495 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8497 case LVM_FINDITEMW:
8498 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8500 case LVM_FINDITEMA:
8501 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8503 case LVM_GETBKCOLOR:
8504 return infoPtr->clrBk;
8506 /* case LVM_GETBKIMAGE: */
8508 case LVM_GETCALLBACKMASK:
8509 return infoPtr->uCallbackMask;
8511 case LVM_GETCOLUMNA:
8512 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8514 case LVM_GETCOLUMNW:
8515 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8517 case LVM_GETCOLUMNORDERARRAY:
8518 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8520 case LVM_GETCOLUMNWIDTH:
8521 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8523 case LVM_GETCOUNTPERPAGE:
8524 return LISTVIEW_GetCountPerPage(infoPtr);
8526 case LVM_GETEDITCONTROL:
8527 return (LRESULT)infoPtr->hwndEdit;
8529 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8530 return infoPtr->dwLvExStyle;
8532 /* case LVM_GETGROUPINFO: */
8534 /* case LVM_GETGROUPMETRICS: */
8536 case LVM_GETHEADER:
8537 return (LRESULT)infoPtr->hwndHeader;
8539 case LVM_GETHOTCURSOR:
8540 return (LRESULT)infoPtr->hHotCursor;
8542 case LVM_GETHOTITEM:
8543 return infoPtr->nHotItem;
8545 case LVM_GETHOVERTIME:
8546 return infoPtr->dwHoverTime;
8548 case LVM_GETIMAGELIST:
8549 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8551 /* case LVM_GETINSERTMARK: */
8553 /* case LVM_GETINSERTMARKCOLOR: */
8555 /* case LVM_GETINSERTMARKRECT: */
8557 case LVM_GETISEARCHSTRINGA:
8558 case LVM_GETISEARCHSTRINGW:
8559 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8560 return FALSE;
8562 case LVM_GETITEMA:
8563 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8565 case LVM_GETITEMW:
8566 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8568 case LVM_GETITEMCOUNT:
8569 return infoPtr->nItemCount;
8571 case LVM_GETITEMPOSITION:
8572 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8574 case LVM_GETITEMRECT:
8575 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8577 case LVM_GETITEMSPACING:
8578 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8580 case LVM_GETITEMSTATE:
8581 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8583 case LVM_GETITEMTEXTA:
8584 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8586 case LVM_GETITEMTEXTW:
8587 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8589 case LVM_GETNEXTITEM:
8590 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8592 case LVM_GETNUMBEROFWORKAREAS:
8593 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8594 return 1;
8596 case LVM_GETORIGIN:
8597 if (!lParam) return FALSE;
8598 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8599 return TRUE;
8601 /* case LVM_GETOUTLINECOLOR: */
8603 /* case LVM_GETSELECTEDCOLUMN: */
8605 case LVM_GETSELECTEDCOUNT:
8606 return LISTVIEW_GetSelectedCount(infoPtr);
8608 case LVM_GETSELECTIONMARK:
8609 return infoPtr->nSelectionMark;
8611 case LVM_GETSTRINGWIDTHA:
8612 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8614 case LVM_GETSTRINGWIDTHW:
8615 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8617 case LVM_GETSUBITEMRECT:
8618 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8620 case LVM_GETTEXTBKCOLOR:
8621 return infoPtr->clrTextBk;
8623 case LVM_GETTEXTCOLOR:
8624 return infoPtr->clrText;
8626 /* case LVM_GETTILEINFO: */
8628 /* case LVM_GETTILEVIEWINFO: */
8630 case LVM_GETTOOLTIPS:
8631 return (LRESULT)infoPtr->hwndToolTip;
8633 case LVM_GETTOPINDEX:
8634 return LISTVIEW_GetTopIndex(infoPtr);
8636 /*case LVM_GETUNICODEFORMAT:
8637 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8638 return FALSE;*/
8640 /* case LVM_GETVIEW: */
8642 case LVM_GETVIEWRECT:
8643 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8645 case LVM_GETWORKAREAS:
8646 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8647 return FALSE;
8649 /* case LVM_HASGROUP: */
8651 case LVM_HITTEST:
8652 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8654 case LVM_INSERTCOLUMNA:
8655 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8657 case LVM_INSERTCOLUMNW:
8658 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8660 /* case LVM_INSERTGROUP: */
8662 /* case LVM_INSERTGROUPSORTED: */
8664 case LVM_INSERTITEMA:
8665 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8667 case LVM_INSERTITEMW:
8668 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8670 /* case LVM_INSERTMARKHITTEST: */
8672 /* case LVM_ISGROUPVIEWENABLED: */
8674 /* case LVM_MAPIDTOINDEX: */
8676 /* case LVM_MAPINDEXTOID: */
8678 /* case LVM_MOVEGROUP: */
8680 /* case LVM_MOVEITEMTOGROUP: */
8682 case LVM_REDRAWITEMS:
8683 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8685 /* case LVM_REMOVEALLGROUPS: */
8687 /* case LVM_REMOVEGROUP: */
8689 case LVM_SCROLL:
8690 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8692 case LVM_SETBKCOLOR:
8693 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8695 /* case LVM_SETBKIMAGE: */
8697 case LVM_SETCALLBACKMASK:
8698 infoPtr->uCallbackMask = (UINT)wParam;
8699 return TRUE;
8701 case LVM_SETCOLUMNA:
8702 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8704 case LVM_SETCOLUMNW:
8705 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8707 case LVM_SETCOLUMNORDERARRAY:
8708 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8710 case LVM_SETCOLUMNWIDTH:
8711 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8713 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8714 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8716 /* case LVM_SETGROUPINFO: */
8718 /* case LVM_SETGROUPMETRICS: */
8720 case LVM_SETHOTCURSOR:
8721 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8723 case LVM_SETHOTITEM:
8724 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8726 case LVM_SETHOVERTIME:
8727 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8729 case LVM_SETICONSPACING:
8730 return LISTVIEW_SetIconSpacing(infoPtr, SLOWORD(lParam), SHIWORD(lParam));
8732 case LVM_SETIMAGELIST:
8733 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8735 /* case LVM_SETINFOTIP: */
8737 /* case LVM_SETINSERTMARK: */
8739 /* case LVM_SETINSERTMARKCOLOR: */
8741 case LVM_SETITEMA:
8742 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8744 case LVM_SETITEMW:
8745 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8747 case LVM_SETITEMCOUNT:
8748 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8750 case LVM_SETITEMPOSITION:
8752 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8753 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8756 case LVM_SETITEMPOSITION32:
8757 if (lParam == 0) return FALSE;
8758 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8760 case LVM_SETITEMSTATE:
8761 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8763 case LVM_SETITEMTEXTA:
8764 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8766 case LVM_SETITEMTEXTW:
8767 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8769 /* case LVM_SETOUTLINECOLOR: */
8771 /* case LVM_SETSELECTEDCOLUMN: */
8773 case LVM_SETSELECTIONMARK:
8774 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8776 case LVM_SETTEXTBKCOLOR:
8777 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8779 case LVM_SETTEXTCOLOR:
8780 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8782 /* case LVM_SETTILEINFO: */
8784 /* case LVM_SETTILEVIEWINFO: */
8786 /* case LVM_SETTILEWIDTH: */
8788 case LVM_SETTOOLTIPS:
8789 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
8791 /* case LVM_SETUNICODEFORMAT: */
8793 /* case LVM_SETVIEW: */
8795 /* case LVM_SETWORKAREAS: */
8797 /* case LVM_SORTGROUPS: */
8799 case LVM_SORTITEMS:
8800 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8802 /* LVM_SORTITEMSEX: */
8804 case LVM_SUBITEMHITTEST:
8805 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8807 case LVM_UPDATE:
8808 return LISTVIEW_Update(infoPtr, (INT)wParam);
8810 case WM_CHAR:
8811 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8813 case WM_COMMAND:
8814 return LISTVIEW_Command(infoPtr, wParam, lParam);
8816 case WM_CREATE:
8817 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8819 case WM_ERASEBKGND:
8820 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8822 case WM_GETDLGCODE:
8823 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8825 case WM_GETFONT:
8826 return (LRESULT)infoPtr->hFont;
8828 case WM_HSCROLL:
8829 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8831 case WM_KEYDOWN:
8832 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8834 case WM_KILLFOCUS:
8835 return LISTVIEW_KillFocus(infoPtr);
8837 case WM_LBUTTONDBLCLK:
8838 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8840 case WM_LBUTTONDOWN:
8841 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8843 case WM_LBUTTONUP:
8844 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8846 case WM_MOUSEMOVE:
8847 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8849 case WM_MOUSEHOVER:
8850 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8852 case WM_NCDESTROY:
8853 return LISTVIEW_NCDestroy(infoPtr);
8855 case WM_NOTIFY:
8856 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8857 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8858 else return 0;
8860 case WM_NOTIFYFORMAT:
8861 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8863 case WM_PAINT:
8864 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8866 case WM_RBUTTONDBLCLK:
8867 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8869 case WM_RBUTTONDOWN:
8870 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8872 case WM_RBUTTONUP:
8873 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8875 case WM_SETCURSOR:
8876 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8877 return TRUE;
8878 goto fwd_msg;
8880 case WM_SETFOCUS:
8881 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8883 case WM_SETFONT:
8884 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8886 case WM_SETREDRAW:
8887 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8889 case WM_SIZE:
8890 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8892 case WM_STYLECHANGED:
8893 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8895 case WM_SYSCOLORCHANGE:
8896 COMCTL32_RefreshSysColors();
8897 return 0;
8899 /* case WM_TIMER: */
8901 case WM_VSCROLL:
8902 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8904 case WM_MOUSEWHEEL:
8905 if (wParam & (MK_SHIFT | MK_CONTROL))
8906 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8907 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8909 case WM_WINDOWPOSCHANGED:
8910 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
8912 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8913 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8914 LISTVIEW_UpdateSize(infoPtr);
8915 LISTVIEW_UpdateScroll(infoPtr);
8917 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8919 /* case WM_WININICHANGE: */
8921 default:
8922 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8923 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8925 fwd_msg:
8926 /* call default window procedure */
8927 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8930 return 0;
8933 /***
8934 * DESCRIPTION:
8935 * Registers the window class.
8937 * PARAMETER(S):
8938 * None
8940 * RETURN:
8941 * None
8943 void LISTVIEW_Register(void)
8945 WNDCLASSW wndClass;
8947 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8948 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8949 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8950 wndClass.cbClsExtra = 0;
8951 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8952 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8953 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8954 wndClass.lpszClassName = WC_LISTVIEWW;
8955 RegisterClassW(&wndClass);
8958 /***
8959 * DESCRIPTION:
8960 * Unregisters the window class.
8962 * PARAMETER(S):
8963 * None
8965 * RETURN:
8966 * None
8968 void LISTVIEW_Unregister(void)
8970 UnregisterClassW(WC_LISTVIEWW, NULL);
8973 /***
8974 * DESCRIPTION:
8975 * Handle any WM_COMMAND messages
8977 * PARAMETER(S):
8978 * [I] infoPtr : valid pointer to the listview structure
8979 * [I] wParam : the first message parameter
8980 * [I] lParam : the second message parameter
8982 * RETURN:
8983 * Zero.
8985 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8987 switch (HIWORD(wParam))
8989 case EN_UPDATE:
8992 * Adjust the edit window size
8994 WCHAR buffer[1024];
8995 HDC hdc = GetDC(infoPtr->hwndEdit);
8996 HFONT hFont, hOldFont = 0;
8997 RECT rect;
8998 SIZE sz;
8999 int len;
9001 if (!infoPtr->hwndEdit || !hdc) return 0;
9002 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9003 GetWindowRect(infoPtr->hwndEdit, &rect);
9005 /* Select font to get the right dimension of the string */
9006 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9007 if(hFont != 0)
9009 hOldFont = SelectObject(hdc, hFont);
9012 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9014 TEXTMETRICW textMetric;
9016 /* Add Extra spacing for the next character */
9017 GetTextMetricsW(hdc, &textMetric);
9018 sz.cx += (textMetric.tmMaxCharWidth * 2);
9020 SetWindowPos (
9021 infoPtr->hwndEdit,
9022 HWND_TOP,
9025 sz.cx,
9026 rect.bottom - rect.top,
9027 SWP_DRAWFRAME|SWP_NOMOVE);
9029 if(hFont != 0)
9030 SelectObject(hdc, hOldFont);
9032 ReleaseDC(infoPtr->hwndSelf, hdc);
9034 break;
9037 default:
9038 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
9041 return 0;
9045 /***
9046 * DESCRIPTION:
9047 * Subclassed edit control windproc function
9049 * PARAMETER(S):
9050 * [I] hwnd : the edit window handle
9051 * [I] uMsg : the message that is to be processed
9052 * [I] wParam : first message parameter
9053 * [I] lParam : second message parameter
9054 * [I] isW : TRUE if input is Unicode
9056 * RETURN:
9057 * Zero.
9059 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9061 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9062 BOOL cancel = FALSE;
9064 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9065 hwnd, uMsg, wParam, lParam, isW);
9067 switch (uMsg)
9069 case WM_GETDLGCODE:
9070 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9072 case WM_KILLFOCUS:
9073 break;
9075 case WM_DESTROY:
9077 WNDPROC editProc = infoPtr->EditWndProc;
9078 infoPtr->EditWndProc = 0;
9079 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9080 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9083 case WM_KEYDOWN:
9084 if (VK_ESCAPE == (INT)wParam)
9086 cancel = TRUE;
9087 break;
9089 else if (VK_RETURN == (INT)wParam)
9090 break;
9092 default:
9093 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9096 /* kill the edit */
9097 if (infoPtr->hwndEdit)
9099 LPWSTR buffer = NULL;
9101 infoPtr->hwndEdit = 0;
9102 if (!cancel)
9104 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9106 if (len)
9108 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9110 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9111 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9115 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9117 if (buffer) COMCTL32_Free(buffer);
9121 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9122 return 0;
9125 /***
9126 * DESCRIPTION:
9127 * Subclassed edit control Unicode windproc function
9129 * PARAMETER(S):
9130 * [I] hwnd : the edit window handle
9131 * [I] uMsg : the message that is to be processed
9132 * [I] wParam : first message parameter
9133 * [I] lParam : second message parameter
9135 * RETURN:
9137 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9139 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9142 /***
9143 * DESCRIPTION:
9144 * Subclassed edit control ANSI windproc function
9146 * PARAMETER(S):
9147 * [I] hwnd : the edit window handle
9148 * [I] uMsg : the message that is to be processed
9149 * [I] wParam : first message parameter
9150 * [I] lParam : second message parameter
9152 * RETURN:
9154 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9156 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9159 /***
9160 * DESCRIPTION:
9161 * Creates a subclassed edit cotrol
9163 * PARAMETER(S):
9164 * [I] infoPtr : valid pointer to the listview structure
9165 * [I] text : initial text for the edit
9166 * [I] style : the window style
9167 * [I] isW : TRUE if input is Unicode
9169 * RETURN:
9171 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9172 INT x, INT y, INT width, INT height, BOOL isW)
9174 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9175 HWND hedit;
9176 SIZE sz;
9177 HDC hdc;
9178 HDC hOldFont=0;
9179 TEXTMETRICW textMetric;
9180 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9182 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9184 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9185 hdc = GetDC(infoPtr->hwndSelf);
9187 /* Select the font to get appropriate metric dimensions */
9188 if(infoPtr->hFont != 0)
9189 hOldFont = SelectObject(hdc, infoPtr->hFont);
9191 /*Get String Length in pixels */
9192 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9194 /*Add Extra spacing for the next character */
9195 GetTextMetricsW(hdc, &textMetric);
9196 sz.cx += (textMetric.tmMaxCharWidth * 2);
9198 if(infoPtr->hFont != 0)
9199 SelectObject(hdc, hOldFont);
9201 ReleaseDC(infoPtr->hwndSelf, hdc);
9202 if (isW)
9203 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9204 else
9205 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9207 if (!hedit) return 0;
9209 infoPtr->EditWndProc = (WNDPROC)
9210 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9211 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9213 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9215 return hedit;