We should callback for inexistent subitems.
[wine/multimedia.git] / dlls / comctl32 / listview.c
blob7da53465f987cccfae1beec4e2d416b9a3d8ef51
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_GetNextItem is very inefficient
49 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
50 * -- LISTVIEW_SetIconSpacing is incomplete
51 * -- LVSICF_NOINVALIDATEALL, LVSICF_NOSCROLL not implemented
52 * -- LISTVIEW_SortItems is broken
53 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
55 * Speedups
56 * -- LISTVIEW_SetItemCount is too invalidation happy
57 * -- LISTVIEW_Size invalidates too much
58 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
59 * instead of inserting in the right spot
60 * -- we should keep an ordered array of coordinates in iconic mode
61 * this would allow to frame items (iterator_frameditems),
62 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
64 * Flags
65 * -- LVIF_COLUMNS
66 * -- LVIF_GROUPID
67 * -- LVIF_NORECOMPUTE
69 * States
70 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
71 * -- LVIS_CUT
72 * -- LVIS_DROPHILITED
73 * -- LVIS_OVERLAYMASK
75 * Styles
76 * -- LVS_NOLABELWRAP
77 * -- LVS_NOSCROLL (see Q137520)
78 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
80 * Extended Styles
81 * -- LVS_EX_BORDERSELECT
82 * -- LVS_EX_CHECKBOXES
83 * -- LVS_EX_FLATSB
84 * -- LVS_EX_GRIDLINES
85 * -- LVS_EX_HEADERDRAGDROP
86 * -- LVS_EX_INFOTIP
87 * -- LVS_EX_LABELTIP
88 * -- LVS_EX_MULTIWORKAREAS
89 * -- LVS_EX_ONECLICKACTIVATE
90 * -- LVS_EX_REGIONAL
91 * -- LVS_EX_SIMPLESELECT
92 * -- LVS_EX_SUBITEMIMAGES
93 * -- LVS_EX_TRACKSELECT
94 * -- LVS_EX_TWOCLICKACTIVATE
95 * -- LVS_EX_UNDERLINECOLD
96 * -- LVS_EX_UNDERLINEHOT
98 * Notifications:
99 * -- LVN_BEGINDRAG, LVN_BEGINRDRAG
100 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
101 * -- LVN_GETINFOTIP
102 * -- LVN_HOTTRACK
103 * -- LVN_MARQUEEBEGIN
104 * -- LVN_ODFINDITEM
105 * -- LVN_ODSTATECHANGED
106 * -- LVN_SETDISPINFO
107 * -- NM_HOVER
109 * Messages:
110 * -- LVM_CANCELEDITLABEL
111 * -- LVM_CREATEDRAGIMAGE
112 * -- LVM_ENABLEGROUPVIEW
113 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
114 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
115 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
116 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
117 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
118 * -- LVM_GETINSERTMARKRECT
119 * -- LVM_GETNUMBEROFWORKAREAS
120 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
121 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
122 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
123 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
124 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
125 * -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
126 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
127 * -- LVM_GETVIEW, LVM_SETVIEW
128 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
129 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
130 * -- LVM_INSERTGROUPSORTED
131 * -- LVM_INSERTMARKHITTEST
132 * -- LVM_ISGROUPVIEWENABLED
133 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
134 * -- LVM_MOVEGROUP
135 * -- LVM_MOVEITEMTOGROUP
136 * -- LVM_SETINFOTIP
137 * -- LVM_SETTILEWIDTH
138 * -- LVM_SORTGROUPS
139 * -- LVM_SORTITEMSEX
141 * Known differences in message stream from native control (not known if
142 * these differences cause problems):
143 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
144 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
145 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
146 * processing for "USEDOUBLECLICKTIME".
149 #include "config.h"
150 #include "wine/port.h"
152 #include <assert.h>
153 #include <ctype.h>
154 #include <string.h>
155 #include <stdlib.h>
156 #include <stdio.h>
158 #include "winbase.h"
159 #include "winnt.h"
160 #include "heap.h"
161 #include "commctrl.h"
162 #include "comctl32.h"
164 #include "wine/debug.h"
165 #include "wine/unicode.h"
167 WINE_DEFAULT_DEBUG_CHANNEL(listview);
169 /* make sure you set this to 0 for production use! */
170 #define DEBUG_RANGES 1
172 typedef struct tagCOLUMN_INFO
174 RECT rcHeader; /* tracks the header's rectangle */
175 int fmt; /* same as LVCOLUMN.fmt */
176 } COLUMN_INFO;
178 typedef struct tagITEMHDR
180 LPWSTR pszText;
181 INT iImage;
182 } ITEMHDR, *LPITEMHDR;
184 typedef struct tagSUBITEM_INFO
186 ITEMHDR hdr;
187 INT iSubItem;
188 } SUBITEM_INFO;
190 typedef struct tagITEM_INFO
192 ITEMHDR hdr;
193 UINT state;
194 LPARAM lParam;
195 INT iIndent;
196 } ITEM_INFO;
198 typedef struct tagRANGE
200 INT lower;
201 INT upper;
202 } RANGE;
204 typedef struct tagRANGES
206 HDPA hdpa;
207 } *RANGES;
209 typedef struct tagITERATOR
211 INT nItem;
212 INT nSpecial;
213 RANGE range;
214 RANGES ranges;
215 INT index;
216 } ITERATOR;
218 typedef struct tagLISTVIEW_INFO
220 HWND hwndSelf;
221 HBRUSH hBkBrush;
222 COLORREF clrBk;
223 COLORREF clrText;
224 COLORREF clrTextBk;
225 COLORREF clrTextBkDefault;
226 HIMAGELIST himlNormal;
227 HIMAGELIST himlSmall;
228 HIMAGELIST himlState;
229 BOOL bLButtonDown;
230 BOOL bRButtonDown;
231 INT nItemHeight;
232 INT nItemWidth;
233 RANGES selectionRanges;
234 INT nSelectionMark;
235 INT nHotItem;
236 SHORT notifyFormat;
237 RECT rcList; /* This rectangle is really the window
238 * client rectangle possibly reduced by the
239 * horizontal scroll bar and/or header - see
240 * LISTVIEW_UpdateSize. This rectangle offset
241 * by the LISTVIEW_GetOrigin value is in
242 * client coordinates */
243 SIZE iconSize;
244 SIZE iconSpacing;
245 SIZE iconStateSize;
246 UINT uCallbackMask;
247 HWND hwndHeader;
248 HCURSOR hHotCursor;
249 HFONT hDefaultFont;
250 HFONT hFont;
251 INT ntmHeight; /* From GetTextMetrics from above font */
252 BOOL bRedraw; /* Turns on/off repaints & invalidations */
253 BOOL bFirstPaint; /* Flags if the control has never painted before */
254 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
255 BOOL bFocus;
256 INT nFocusedItem;
257 RECT rcFocus;
258 DWORD dwStyle; /* the cached window GWL_STYLE */
259 DWORD dwLvExStyle; /* extended listview style */
260 INT nItemCount; /* the number of items in the list */
261 HDPA hdpaItems; /* array ITEM_INFO pointers */
262 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
263 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
264 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
265 POINT currIconPos; /* this is the position next icon will be placed */
266 PFNLVCOMPARE pfnCompare;
267 LPARAM lParamSort;
268 HWND hwndEdit;
269 WNDPROC EditWndProc;
270 INT nEditLabelItem;
271 DWORD dwHoverTime;
273 DWORD lastKeyPressTimestamp;
274 WPARAM charCode;
275 INT nSearchParamLength;
276 WCHAR szSearchParam[ MAX_PATH ];
277 BOOL bIsDrawing;
278 } LISTVIEW_INFO;
281 * constants
283 /* How many we debug buffer to allocate */
284 #define DEBUG_BUFFERS 20
285 /* The size of a single debug bbuffer */
286 #define DEBUG_BUFFER_SIZE 256
288 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
289 #define SB_INTERNAL -1
291 /* maximum size of a label */
292 #define DISP_TEXT_SIZE 512
294 /* padding for items in list and small icon display modes */
295 #define WIDTH_PADDING 12
297 /* padding for items in list, report and small icon display modes */
298 #define HEIGHT_PADDING 1
300 /* offset of items in report display mode */
301 #define REPORT_MARGINX 2
303 /* padding for icon in large icon display mode
304 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
305 * that HITTEST will see.
306 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
307 * ICON_TOP_PADDING - sum of the two above.
308 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
309 * LABEL_VERT_PADDING - between bottom of text and end of box
311 * ICON_LR_PADDING - additional width above icon size.
312 * ICON_LR_HALF - half of the above value
314 #define ICON_TOP_PADDING_NOTHITABLE 2
315 #define ICON_TOP_PADDING_HITABLE 2
316 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
317 #define ICON_BOTTOM_PADDING 4
318 #define LABEL_VERT_PADDING 7
319 #define ICON_LR_PADDING 16
320 #define ICON_LR_HALF (ICON_LR_PADDING/2)
322 /* default label width for items in list and small icon display modes */
323 #define DEFAULT_LABEL_WIDTH 40
325 /* default column width for items in list display mode */
326 #define DEFAULT_COLUMN_WIDTH 128
328 /* Size of "line" scroll for V & H scrolls */
329 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
331 /* Padding betwen image and label */
332 #define IMAGE_PADDING 2
334 /* Padding behind the label */
335 #define TRAILING_PADDING 5
337 /* Border for the icon caption */
338 #define CAPTION_BORDER 2
340 /* Standard DrawText flags */
341 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
342 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
343 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
345 /* The time in milisecods to reset the search in the list */
346 #define KEY_DELAY 450
348 /* Dump the LISTVIEW_INFO structure to the debug channel */
349 #define LISTVIEW_DUMP(iP) do { \
350 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
351 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
352 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
353 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
354 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
355 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
356 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
357 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
358 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
359 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
360 } while(0)
364 * forward declarations
366 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
367 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
368 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
369 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
370 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
371 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
372 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
373 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
374 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
375 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
376 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
377 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
378 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
379 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
380 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
381 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
382 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
383 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
384 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
385 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
386 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
387 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
388 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
389 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
390 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
392 /******** Text handling functions *************************************/
394 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
395 * text string. The string may be ANSI or Unicode, in which case
396 * the boolean isW tells us the type of the string.
398 * The name of the function tell what type of strings it expects:
399 * W: Unicode, T: ANSI/Unicode - function of isW
402 static inline BOOL is_textW(LPCWSTR text)
404 return text != NULL && text != LPSTR_TEXTCALLBACKW;
407 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
409 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
410 return is_textW(text);
413 static inline int textlenT(LPCWSTR text, BOOL isW)
415 return !is_textT(text, isW) ? 0 :
416 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
419 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
421 if (isDestW)
422 if (isSrcW) lstrcpynW(dest, src, max);
423 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
424 else
425 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
426 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
429 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
431 LPWSTR wstr = (LPWSTR)text;
433 if (!isW && is_textT(text, isW))
435 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
436 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
437 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
439 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
440 return wstr;
443 static inline void textfreeT(LPWSTR wstr, BOOL isW)
445 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
449 * dest is a pointer to a Unicode string
450 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
452 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
454 BOOL bResult = TRUE;
456 if (src == LPSTR_TEXTCALLBACKW)
458 if (is_textW(*dest)) COMCTL32_Free(*dest);
459 *dest = LPSTR_TEXTCALLBACKW;
461 else
463 LPWSTR pszText = textdupTtoW(src, isW);
464 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
465 bResult = Str_SetPtrW(dest, pszText);
466 textfreeT(pszText, isW);
468 return bResult;
472 * compares a Unicode to a Unicode/ANSI text string
474 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
476 if (!aw) return bt ? -1 : 0;
477 if (!bt) return aw ? 1 : 0;
478 if (aw == LPSTR_TEXTCALLBACKW)
479 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
480 if (bt != LPSTR_TEXTCALLBACKW)
482 LPWSTR bw = textdupTtoW(bt, isW);
483 int r = bw ? lstrcmpW(aw, bw) : 1;
484 textfreeT(bw, isW);
485 return r;
488 return 1;
491 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
493 int res;
495 n = min(min(n, strlenW(s1)), strlenW(s2));
496 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
497 return res ? res - sizeof(WCHAR) : res;
500 /******** Debugging functions *****************************************/
502 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
504 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
505 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
508 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
510 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
511 n = min(textlenT(text, isW), n);
512 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
515 static char* debug_getbuf()
517 static int index = 0;
518 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
519 return buffers[index++ % DEBUG_BUFFERS];
522 static inline char* debugrange(const RANGE* lprng)
524 if (lprng)
526 char* buf = debug_getbuf();
527 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
528 return buf;
529 } else return "(null)";
532 static inline char* debugpoint(const POINT* lppt)
534 if (lppt)
536 char* buf = debug_getbuf();
537 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
538 return buf;
539 } else return "(null)";
542 static inline char* debugrect(const RECT* rect)
544 if (rect)
546 char* buf = debug_getbuf();
547 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
548 rect->left, rect->top, rect->right, rect->bottom);
549 return buf;
550 } else return "(null)";
553 static char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
555 char* buf = debug_getbuf(), *text = buf;
556 int len, size = DEBUG_BUFFER_SIZE;
558 if (pScrollInfo == NULL) return "(null)";
559 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
560 if (len == -1) goto end; buf += len; size -= len;
561 if (pScrollInfo->fMask & SIF_RANGE)
562 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
563 else len = 0;
564 if (len == -1) goto end; buf += len; size -= len;
565 if (pScrollInfo->fMask & SIF_PAGE)
566 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
567 else len = 0;
568 if (len == -1) goto end; buf += len; size -= len;
569 if (pScrollInfo->fMask & SIF_POS)
570 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
571 else len = 0;
572 if (len == -1) goto end; buf += len; size -= len;
573 if (pScrollInfo->fMask & SIF_TRACKPOS)
574 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
575 else len = 0;
576 if (len == -1) goto end; buf += len; size -= len;
577 goto undo;
578 end:
579 buf = text + strlen(text);
580 undo:
581 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
582 return text;
585 static char* debugnmlistview(LPNMLISTVIEW plvnm)
587 if (plvnm)
589 char* buf = debug_getbuf();
590 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
591 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
592 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
593 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
594 return buf;
595 } else return "(null)";
598 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
600 char* buf = debug_getbuf(), *text = buf;
601 int len, size = DEBUG_BUFFER_SIZE;
603 if (lpLVItem == NULL) return "(null)";
604 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
605 if (len == -1) goto end; buf += len; size -= len;
606 if (lpLVItem->mask & LVIF_STATE)
607 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
608 else len = 0;
609 if (len == -1) goto end; buf += len; size -= len;
610 if (lpLVItem->mask & LVIF_TEXT)
611 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
612 else len = 0;
613 if (len == -1) goto end; buf += len; size -= len;
614 if (lpLVItem->mask & LVIF_IMAGE)
615 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
616 else len = 0;
617 if (len == -1) goto end; buf += len; size -= len;
618 if (lpLVItem->mask & LVIF_PARAM)
619 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
620 else len = 0;
621 if (len == -1) goto end; buf += len; size -= len;
622 if (lpLVItem->mask & LVIF_INDENT)
623 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
624 else len = 0;
625 if (len == -1) goto end; buf += len; size -= len;
626 goto undo;
627 end:
628 buf = text + strlen(text);
629 undo:
630 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
631 return text;
634 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
636 char* buf = debug_getbuf(), *text = buf;
637 int len, size = DEBUG_BUFFER_SIZE;
639 if (lpColumn == NULL) return "(null)";
640 len = snprintf(buf, size, "{");
641 if (len == -1) goto end; buf += len; size -= len;
642 if (lpColumn->mask & LVCF_SUBITEM)
643 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
644 else len = 0;
645 if (len == -1) goto end; buf += len; size -= len;
646 if (lpColumn->mask & LVCF_FMT)
647 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
648 else len = 0;
649 if (len == -1) goto end; buf += len; size -= len;
650 if (lpColumn->mask & LVCF_WIDTH)
651 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
652 else len = 0;
653 if (len == -1) goto end; buf += len; size -= len;
654 if (lpColumn->mask & LVCF_TEXT)
655 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
656 else len = 0;
657 if (len == -1) goto end; buf += len; size -= len;
658 if (lpColumn->mask & LVCF_IMAGE)
659 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
660 else len = 0;
661 if (len == -1) goto end; buf += len; size -= len;
662 if (lpColumn->mask & LVCF_ORDER)
663 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
664 else len = 0;
665 if (len == -1) goto end; buf += len; size -= len;
666 goto undo;
667 end:
668 buf = text + strlen(text);
669 undo:
670 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
671 return text;
675 /******** Notification functions i************************************/
677 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
679 LRESULT result;
681 TRACE("(code=%d)\n", code);
683 pnmh->hwndFrom = infoPtr->hwndSelf;
684 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
685 pnmh->code = code;
686 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
687 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
689 TRACE(" <= %ld\n", result);
691 return result;
694 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
696 NMHDR nmh;
697 return notify_hdr(infoPtr, code, &nmh);
700 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
702 notify(infoPtr, LVN_ITEMACTIVATE);
705 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
707 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
708 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
711 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
713 NMLISTVIEW nmlv;
715 ZeroMemory(&nmlv, sizeof(nmlv));
716 nmlv.iItem = lvht->iItem;
717 nmlv.iSubItem = lvht->iSubItem;
718 nmlv.ptAction = lvht->pt;
719 return notify_listview(infoPtr, code, &nmlv);
722 static int get_ansi_notification(INT unicodeNotificationCode)
724 switch (unicodeNotificationCode)
726 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
727 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
728 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
729 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
730 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
731 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
733 ERR("unknown notification %x\n", unicodeNotificationCode);
734 assert(FALSE);
738 Send notification. depends on dispinfoW having same
739 structure as dispinfoA.
740 infoPtr : listview struct
741 notificationCode : *Unicode* notification code
742 pdi : dispinfo structure (can be unicode or ansi)
743 isW : TRUE if dispinfo is Unicode
745 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
747 BOOL bResult = FALSE;
748 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
749 INT realNotifCode;
750 INT cchTempBufMax = 0, savCchTextMax = 0;
751 LPWSTR pszTempBuf = NULL, savPszText = NULL;
753 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
755 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
756 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
759 if (convertToAnsi || convertToUnicode)
761 if (notificationCode != LVN_GETDISPINFOW)
763 cchTempBufMax = convertToUnicode ?
764 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
765 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
767 else
769 cchTempBufMax = pdi->item.cchTextMax;
770 *pdi->item.pszText = 0; /* make sure we don't process garbage */
773 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
774 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
775 if (!pszTempBuf) return FALSE;
776 if (convertToUnicode)
777 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
778 pszTempBuf, cchTempBufMax);
779 else
780 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
781 cchTempBufMax, NULL, NULL);
782 savCchTextMax = pdi->item.cchTextMax;
783 savPszText = pdi->item.pszText;
784 pdi->item.pszText = pszTempBuf;
785 pdi->item.cchTextMax = cchTempBufMax;
788 if (infoPtr->notifyFormat == NFR_ANSI)
789 realNotifCode = get_ansi_notification(notificationCode);
790 else
791 realNotifCode = notificationCode;
792 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
793 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
795 if (convertToUnicode || convertToAnsi)
797 if (convertToUnicode) /* note : pointer can be changed by app ! */
798 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
799 savCchTextMax, NULL, NULL);
800 else
801 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
802 savPszText, savCchTextMax);
803 pdi->item.pszText = savPszText; /* restores our buffer */
804 pdi->item.cchTextMax = savCchTextMax;
805 HeapFree(GetProcessHeap(), 0, pszTempBuf);
807 return bResult;
810 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds)
812 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
813 lpnmlvcd->nmcd.hdc = hdc;
814 lpnmlvcd->nmcd.rc = *rcBounds;
815 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
816 lpnmlvcd->clrText = infoPtr->clrText;
819 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
821 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
822 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
825 /******** Item iterator functions **********************************/
827 static RANGES ranges_create(int count);
828 static void ranges_destroy(RANGES ranges);
829 static BOOL ranges_add(RANGES ranges, RANGE range);
830 static BOOL ranges_del(RANGES ranges, RANGE range);
831 static void ranges_dump(RANGES ranges);
833 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
835 RANGE range = { nItem, nItem + 1 };
837 return ranges_add(ranges, range);
840 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
842 RANGE range = { nItem, nItem + 1 };
844 return ranges_del(ranges, range);
847 /***
848 * ITERATOR DOCUMENTATION
850 * The iterator functions allow for easy, and convenient iteration
851 * over items of iterest in the list. Typically, you create a
852 * iterator, use it, and destroy it, as such:
853 * ITERATOR i;
855 * iterator_xxxitems(&i, ...);
856 * while (iterator_{prev,next}(&i)
858 * //code which uses i.nItem
860 * iterator_destroy(&i);
862 * where xxx is either: framed, or visible.
863 * Note that it is important that the code destroys the iterator
864 * after it's done with it, as the creation of the iterator may
865 * allocate memory, which thus needs to be freed.
867 * You can iterate both forwards, and backwards through the list,
868 * by using iterator_next or iterator_prev respectively.
870 * Lower numbered items are draw on top of higher number items in
871 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
872 * items may overlap). So, to test items, you should use
873 * iterator_next
874 * which lists the items top to bottom (in Z-order).
875 * For drawing items, you should use
876 * iterator_prev
877 * which lists the items bottom to top (in Z-order).
878 * If you keep iterating over the items after the end-of-items
879 * marker (-1) is returned, the iterator will start from the
880 * beginning. Typically, you don't need to test for -1,
881 * because iterator_{next,prev} will return TRUE if more items
882 * are to be iterated over, or FALSE otherwise.
884 * Note: the iterator is defined to be bidirectional. That is,
885 * any number of prev followed by any number of next, or
886 * five versa, should leave the iterator at the same item:
887 * prev * n, next * n = next * n, prev * n
889 * The iterator has a notion of a out-of-order, special item,
890 * which sits at the start of the list. This is used in
891 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
892 * which needs to be first, as it may overlap other items.
894 * The code is a bit messy because we have:
895 * - a special item to deal with
896 * - simple range, or composite range
897 * - empty range.
898 * If find bugs, or want to add features, please make sure you
899 * always check/modify *both* iterator_prev, and iterator_next.
902 /****
903 * This function iterates through the items in increasing order,
904 * but prefixed by the special item, then -1. That is:
905 * special, 1, 2, 3, ..., n, -1.
906 * Each item is listed only once.
908 static inline BOOL iterator_next(ITERATOR* i)
910 if (i->nItem == -1)
912 i->nItem = i->nSpecial;
913 if (i->nItem != -1) return TRUE;
915 if (i->nItem == i->nSpecial)
917 if (i->ranges) i->index = 0;
918 goto pickarange;
921 i->nItem++;
922 testitem:
923 if (i->nItem == i->nSpecial) i->nItem++;
924 if (i->nItem < i->range.upper) return TRUE;
926 pickarange:
927 if (i->ranges)
929 if (i->index < i->ranges->hdpa->nItemCount)
930 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
931 else goto end;
933 else if (i->nItem >= i->range.upper) goto end;
935 i->nItem = i->range.lower;
936 if (i->nItem >= 0) goto testitem;
937 end:
938 i->nItem = -1;
939 return FALSE;
942 /****
943 * This function iterates through the items in decreasing order,
944 * followed by the special item, then -1. That is:
945 * n, n-1, ..., 3, 2, 1, special, -1.
946 * Each item is listed only once.
948 static inline BOOL iterator_prev(ITERATOR* i)
950 BOOL start = FALSE;
952 if (i->nItem == -1)
954 start = TRUE;
955 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
956 goto pickarange;
958 if (i->nItem == i->nSpecial)
960 i->nItem = -1;
961 return FALSE;
964 testitem:
965 i->nItem--;
966 if (i->nItem == i->nSpecial) i->nItem--;
967 if (i->nItem >= i->range.lower) return TRUE;
969 pickarange:
970 if (i->ranges)
972 if (i->index > 0)
973 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
974 else goto end;
976 else if (!start && i->nItem < i->range.lower) goto end;
978 i->nItem = i->range.upper;
979 if (i->nItem > 0) goto testitem;
980 end:
981 return (i->nItem = i->nSpecial) != -1;
984 static RANGE iterator_range(ITERATOR* i)
986 RANGE range;
988 if (!i->ranges) return i->range;
990 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
991 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
992 return range;
995 /***
996 * Releases resources associated with this ierator.
998 static inline void iterator_destroy(ITERATOR* i)
1000 if (i->ranges) ranges_destroy(i->ranges);
1003 /***
1004 * Create an empty iterator.
1006 static inline BOOL iterator_empty(ITERATOR* i)
1008 ZeroMemory(i, sizeof(*i));
1009 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1010 return TRUE;
1014 /***
1015 * Create an iterator over a bunch of ranges.
1016 * Please note that the iterator will take ownership of the ranges,
1017 * and will free them upon destruction.
1019 static inline BOOL iterator_ranges(ITERATOR* i, RANGES ranges)
1021 iterator_empty(i);
1022 i->ranges = ranges;
1023 return TRUE;
1026 /***
1027 * Creates an iterator over the items which intersect lprc.
1029 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
1031 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1032 RECT frame = *lprc, rcItem, rcTemp;
1033 POINT Origin;
1035 /* in case we fail, we want to return an empty iterator */
1036 if (!iterator_empty(i)) return FALSE;
1038 LISTVIEW_GetOrigin(infoPtr, &Origin);
1040 TRACE("(lprc=%s)\n", debugrect(lprc));
1041 OffsetRect(&frame, -Origin.x, -Origin.y);
1043 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1045 INT nItem;
1047 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1049 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1050 if (IntersectRect(&rcTemp, &rcItem, lprc))
1051 i->nSpecial = infoPtr->nFocusedItem;
1053 if (!(i->ranges = ranges_create(50))) return FALSE;
1054 /* to do better here, we need to have PosX, and PosY sorted */
1055 TRACE("building icon ranges:\n");
1056 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1058 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1059 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1060 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1061 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1062 if (IntersectRect(&rcTemp, &rcItem, &frame))
1063 ranges_additem(i->ranges, nItem);
1065 return TRUE;
1067 else if (uView == LVS_REPORT)
1069 INT lower, upper;
1071 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1072 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1074 lower = max(frame.top / infoPtr->nItemHeight, 0);
1075 upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1);
1076 if (upper < lower) return TRUE;
1077 i->range.lower = lower;
1078 i->range.upper = upper + 1;
1079 TRACE(" report=%s\n", debugrange(&i->range));
1081 else
1083 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1084 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1085 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1086 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1087 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1088 INT lower = nFirstCol * nPerCol + nFirstRow;
1089 RANGE item_range;
1090 INT nCol;
1092 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1093 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1095 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1097 if (!(i->ranges = ranges_create(nLastCol - nFirstCol + 1))) return FALSE;
1098 TRACE("building list ranges:\n");
1099 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1101 item_range.lower = nCol * nPerCol + nFirstRow;
1102 if(item_range.lower >= infoPtr->nItemCount) break;
1103 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1104 TRACE(" list=%s\n", debugrange(&item_range));
1105 ranges_add(i->ranges, item_range);
1109 return TRUE;
1112 /***
1113 * Creates an iterator over the items which intersect the visible region of hdc.
1115 static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
1117 POINT Origin, Position;
1118 RECT rcItem, rcClip;
1119 INT rgntype;
1121 rgntype = GetClipBox(hdc, &rcClip);
1122 if (rgntype == NULLREGION) return iterator_empty(i);
1123 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1124 if (rgntype == SIMPLEREGION) return TRUE;
1126 /* first deal with the special item */
1127 if (i->nSpecial != -1)
1129 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1130 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1133 /* if we can't deal with the region, we'll just go with the simple range */
1134 LISTVIEW_GetOrigin(infoPtr, &Origin);
1135 TRACE("building visible range:\n");
1136 if (!i->ranges && i->range.lower < i->range.upper)
1138 if (!(i->ranges = ranges_create(50))) return TRUE;
1139 if (!ranges_add(i->ranges, i->range))
1141 ranges_destroy(i->ranges);
1142 i->ranges = 0;
1143 return TRUE;
1147 /* now delete the invisible items from the list */
1148 while(iterator_next(i))
1150 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1151 rcItem.left = Position.x + Origin.x;
1152 rcItem.top = Position.y + Origin.y;
1153 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1154 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1155 if (!RectVisible(hdc, &rcItem))
1156 ranges_delitem(i->ranges, i->nItem);
1158 /* the iterator should restart on the next iterator_next */
1159 TRACE("done\n");
1161 return TRUE;
1164 /******** Misc helper functions ************************************/
1166 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1167 WPARAM wParam, LPARAM lParam, BOOL isW)
1169 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1170 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1173 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1175 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1177 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1178 (uView == LVS_ICON || uView == LVS_SMALLICON);
1181 /******** Internal API functions ************************************/
1183 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1185 assert (nSubItem >= 0 && nSubItem < infoPtr->hdpaColumns->nItemCount);
1186 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1189 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1191 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1194 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1196 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1199 /* Listview invlaidation functions: use _only_ these function to invalidate */
1201 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1203 return infoPtr->bRedraw && !infoPtr->bFirstPaint;
1206 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT*rect)
1208 if(!is_redrawing(infoPtr)) return;
1209 TRACE(" invalidating rect=%s\n", debugrect(rect));
1210 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1213 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1215 RECT rcBox;
1217 if(!is_redrawing(infoPtr)) return;
1218 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1219 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1222 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1224 POINT Origin, Position;
1225 RECT rcBox;
1227 if(!is_redrawing(infoPtr)) return;
1228 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1229 LISTVIEW_GetOrigin(infoPtr, &Origin);
1230 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1231 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1232 rcBox.top = 0;
1233 rcBox.bottom = infoPtr->nItemHeight;
1234 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1235 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1238 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1240 LISTVIEW_InvalidateRect(infoPtr, NULL);
1243 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1245 RECT rcCol;
1247 if(!infoPtr->bRedraw) return;
1248 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1249 rcCol.top = infoPtr->rcList.top;
1250 rcCol.bottom = infoPtr->rcList.bottom;
1251 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1254 /***
1255 * DESCRIPTION:
1256 * Retrieves the number of items that can fit vertically in the client area.
1258 * PARAMETER(S):
1259 * [I] infoPtr : valid pointer to the listview structure
1261 * RETURN:
1262 * Number of items per row.
1264 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1266 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1268 return max(nListWidth/infoPtr->nItemWidth, 1);
1271 /***
1272 * DESCRIPTION:
1273 * Retrieves the number of items that can fit horizontally in the client
1274 * area.
1276 * PARAMETER(S):
1277 * [I] infoPtr : valid pointer to the listview structure
1279 * RETURN:
1280 * Number of items per column.
1282 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1284 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1286 return max(nListHeight / infoPtr->nItemHeight, 1);
1290 /*************************************************************************
1291 * LISTVIEW_ProcessLetterKeys
1293 * Processes keyboard messages generated by pressing the letter keys
1294 * on the keyboard.
1295 * What this does is perform a case insensitive search from the
1296 * current position with the following quirks:
1297 * - If two chars or more are pressed in quick succession we search
1298 * for the corresponding string (e.g. 'abc').
1299 * - If there is a delay we wipe away the current search string and
1300 * restart with just that char.
1301 * - If the user keeps pressing the same character, whether slowly or
1302 * fast, so that the search string is entirely composed of this
1303 * character ('aaaaa' for instance), then we search for first item
1304 * that starting with that character.
1305 * - If the user types the above character in quick succession, then
1306 * we must also search for the corresponding string ('aaaaa'), and
1307 * go to that string if there is a match.
1309 * PARAMETERS
1310 * [I] hwnd : handle to the window
1311 * [I] charCode : the character code, the actual character
1312 * [I] keyData : key data
1314 * RETURNS
1316 * Zero.
1318 * BUGS
1320 * - The current implementation has a list of characters it will
1321 * accept and it ignores averything else. In particular it will
1322 * ignore accentuated characters which seems to match what
1323 * Windows does. But I'm not sure it makes sense to follow
1324 * Windows there.
1325 * - We don't sound a beep when the search fails.
1327 * SEE ALSO
1329 * TREEVIEW_ProcessLetterKeys
1331 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1333 INT nItem;
1334 INT endidx,idx;
1335 LVITEMW item;
1336 WCHAR buffer[MAX_PATH];
1337 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1339 /* simple parameter checking */
1340 if (!charCode || !keyData) return 0;
1342 /* only allow the valid WM_CHARs through */
1343 if (!isalnum(charCode) &&
1344 charCode != '.' && charCode != '`' && charCode != '!' &&
1345 charCode != '@' && charCode != '#' && charCode != '$' &&
1346 charCode != '%' && charCode != '^' && charCode != '&' &&
1347 charCode != '*' && charCode != '(' && charCode != ')' &&
1348 charCode != '-' && charCode != '_' && charCode != '+' &&
1349 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1350 charCode != '}' && charCode != '[' && charCode != '{' &&
1351 charCode != '/' && charCode != '?' && charCode != '>' &&
1352 charCode != '<' && charCode != ',' && charCode != '~')
1353 return 0;
1355 /* if there's one item or less, there is no where to go */
1356 if (infoPtr->nItemCount <= 1) return 0;
1358 /* update the search parameters */
1359 infoPtr->lastKeyPressTimestamp = GetTickCount();
1360 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1361 if (infoPtr->nSearchParamLength < MAX_PATH)
1362 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1363 if (infoPtr->charCode != charCode)
1364 infoPtr->charCode = charCode = 0;
1365 } else {
1366 infoPtr->charCode=charCode;
1367 infoPtr->szSearchParam[0]=charCode;
1368 infoPtr->nSearchParamLength=1;
1369 /* Redundant with the 1 char string */
1370 charCode=0;
1373 /* and search from the current position */
1374 nItem=-1;
1375 if (infoPtr->nFocusedItem >= 0) {
1376 endidx=infoPtr->nFocusedItem;
1377 idx=endidx;
1378 /* if looking for single character match,
1379 * then we must always move forward
1381 if (infoPtr->nSearchParamLength == 1)
1382 idx++;
1383 } else {
1384 endidx=infoPtr->nItemCount;
1385 idx=0;
1387 do {
1388 if (idx == infoPtr->nItemCount) {
1389 if (endidx == infoPtr->nItemCount || endidx == 0)
1390 break;
1391 idx=0;
1394 /* get item */
1395 item.mask = LVIF_TEXT;
1396 item.iItem = idx;
1397 item.iSubItem = 0;
1398 item.pszText = buffer;
1399 item.cchTextMax = MAX_PATH;
1400 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1402 /* check for a match */
1403 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1404 nItem=idx;
1405 break;
1406 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1407 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1408 /* This would work but we must keep looking for a longer match */
1409 nItem=idx;
1411 idx++;
1412 } while (idx != endidx);
1414 if (nItem != -1)
1415 LISTVIEW_KeySelection(infoPtr, nItem);
1417 return 0;
1420 /*************************************************************************
1421 * LISTVIEW_UpdateHeaderSize [Internal]
1423 * Function to resize the header control
1425 * PARAMS
1426 * [I] hwnd : handle to a window
1427 * [I] nNewScrollPos : scroll pos to set
1429 * RETURNS
1430 * None.
1432 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1434 RECT winRect;
1435 POINT point[2];
1437 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1439 GetWindowRect(infoPtr->hwndHeader, &winRect);
1440 point[0].x = winRect.left;
1441 point[0].y = winRect.top;
1442 point[1].x = winRect.right;
1443 point[1].y = winRect.bottom;
1445 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1446 point[0].x = -nNewScrollPos;
1447 point[1].x += nNewScrollPos;
1449 SetWindowPos(infoPtr->hwndHeader,0,
1450 point[0].x,point[0].y,point[1].x,point[1].y,
1451 SWP_NOZORDER | SWP_NOACTIVATE);
1454 /***
1455 * DESCRIPTION:
1456 * Update the scrollbars. This functions should be called whenever
1457 * the content, size or view changes.
1459 * PARAMETER(S):
1460 * [I] infoPtr : valid pointer to the listview structure
1462 * RETURN:
1463 * None
1465 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1467 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1468 SCROLLINFO horzInfo, vertInfo;
1470 if (infoPtr->dwStyle & LVS_NOSCROLL) return;
1471 if (!is_redrawing(infoPtr)) return;
1473 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1474 horzInfo.cbSize = sizeof(SCROLLINFO);
1475 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1477 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1478 vertInfo.cbSize = sizeof(SCROLLINFO);
1479 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1481 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1482 if (uView == LVS_LIST)
1484 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1485 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1486 horzInfo.nPage /= infoPtr->nItemWidth;
1488 else if (uView == LVS_REPORT)
1490 horzInfo.nMax = infoPtr->nItemWidth;
1491 vertInfo.nMax = infoPtr->nItemCount;
1492 vertInfo.nPage /= infoPtr->nItemHeight;
1494 else /* LVS_ICON, or LVS_SMALLICON */
1496 RECT rcView;
1498 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1500 horzInfo.nMax = rcView.right - rcView.left;
1501 vertInfo.nMax = rcView.bottom - rcView.top;
1505 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1506 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1507 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1508 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1510 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1511 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1512 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1513 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1515 /* Update the Header Control */
1516 if (uView == LVS_REPORT)
1518 horzInfo.fMask = SIF_POS;
1519 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1520 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1525 /***
1526 * DESCRIPTION:
1527 * Shows/hides the focus rectangle.
1529 * PARAMETER(S):
1530 * [I] infoPtr : valid pointer to the listview structure
1531 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1533 * RETURN:
1534 * None
1536 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1538 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1539 HDC hdc;
1541 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1543 if (infoPtr->nFocusedItem < 0) return;
1545 /* we need some gymnastics in ICON mode to handle large items */
1546 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1548 RECT rcBox;
1550 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1551 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1553 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1554 return;
1558 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1560 /* for some reason, owner draw should work only in report mode */
1561 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1563 DRAWITEMSTRUCT dis;
1564 LVITEMW item;
1566 item.iItem = infoPtr->nFocusedItem;
1567 item.iSubItem = 0;
1568 item.mask = LVIF_PARAM;
1569 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1571 ZeroMemory(&dis, sizeof(dis));
1572 dis.CtlType = ODT_LISTVIEW;
1573 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1574 dis.itemID = item.iItem;
1575 dis.itemAction = ODA_FOCUS;
1576 if (fShow) dis.itemState |= ODS_FOCUS;
1577 dis.hwndItem = infoPtr->hwndSelf;
1578 dis.hDC = hdc;
1579 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1580 dis.itemData = item.lParam;
1582 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1584 else
1586 DrawFocusRect(hdc, &infoPtr->rcFocus);
1588 done:
1589 ReleaseDC(infoPtr->hwndSelf, hdc);
1592 /***
1593 * Invalidates all visible selected items.
1595 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1597 ITERATOR i;
1599 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1600 while(iterator_next(&i))
1602 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1603 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1605 iterator_destroy(&i);
1609 /***
1610 * DESCRIPTION: [INTERNAL]
1611 * Computes an item's (left,top) corner, relative to rcView.
1612 * That is, the position has NOT been made relative to the Origin.
1613 * This is deliberate, to avoid computing the Origin over, and
1614 * over again, when this function is call in a loop. Instead,
1615 * one ca factor the computation of the Origin before the loop,
1616 * and offset the value retured by this function, on every iteration.
1618 * PARAMETER(S):
1619 * [I] infoPtr : valid pointer to the listview structure
1620 * [I] nItem : item number
1621 * [O] lpptOrig : item top, left corner
1623 * RETURN:
1624 * None.
1626 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1628 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1630 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1632 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1634 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1635 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1637 else if (uView == LVS_LIST)
1639 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1640 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1641 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1643 else /* LVS_REPORT */
1645 lpptPosition->x = REPORT_MARGINX;
1646 lpptPosition->y = nItem * infoPtr->nItemHeight;
1650 /***
1651 * DESCRIPTION: [INTERNAL]
1652 * Compute the rectangles of an item. This is to localize all
1653 * the computations in one place. If you are not interested in some
1654 * of these values, simply pass in a NULL -- the fucntion is smart
1655 * enough to compute only what's necessary. The function computes
1656 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1657 * one, the BOX rectangle. This rectangle is very cheap to compute,
1658 * and is guaranteed to contain all the other rectangles. Computing
1659 * the ICON rect is also cheap, but all the others are potentaily
1660 * expensive. This gives an easy and effective optimization when
1661 * searching (like point inclusion, or rectangle intersection):
1662 * first test against the BOX, and if TRUE, test agains the desired
1663 * rectangle.
1664 * If the function does not have all the necessary information
1665 * to computed the requested rectangles, will crash with a
1666 * failed assertion. This is done so we catch all programming
1667 * errors, given that the function is called only from our code.
1669 * We have the following 'special' meanings for a few fields:
1670 * * If LVIS_FOCUSED is set, we assume the item has the focus
1671 * This is important in ICON mode, where it might get a larger
1672 * then usual rectange
1674 * Please note that subitem support works only in REPORT mode.
1676 * PARAMETER(S):
1677 * [I] infoPtr : valid pointer to the listview structure
1678 * [I] lpLVItem : item to compute the measures for
1679 * [O] lprcBox : ptr to Box rectangle
1680 * The internal LVIR_BOX rectangle
1681 * [0] lprcState : ptr to State icon rectangle
1682 * The internal LVIR_STATE rectangle
1683 * [O] lprcIcon : ptr to Icon rectangle
1684 * Same as LVM_GETITEMRECT with LVIR_ICON
1685 * [O] lprcLabel : ptr to Label rectangle
1686 * Same as LVM_GETITEMRECT with LVIR_LABEL
1688 * RETURN:
1689 * None.
1691 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1692 LPRECT lprcBox, LPRECT lprcState,
1693 LPRECT lprcIcon, LPRECT lprcLabel)
1695 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1696 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1697 RECT Box, State, Icon, Label;
1698 COLUMN_INFO *lpColumnInfo = NULL;
1700 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1702 /* Be smart and try to figure out the minimum we have to do */
1703 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1704 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1706 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1707 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1709 if (lprcLabel) doLabel = TRUE;
1710 if (doLabel || lprcIcon) doIcon = TRUE;
1711 if (doIcon || lprcState) doState = TRUE;
1713 /************************************************************/
1714 /* compute the box rectangle (it should be cheap to do) */
1715 /************************************************************/
1716 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1717 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1719 if (lpLVItem->iSubItem)
1721 Box = lpColumnInfo->rcHeader;
1723 else
1725 Box.left = 0;
1726 Box.right = infoPtr->nItemWidth;
1728 Box.top = 0;
1729 Box.bottom = infoPtr->nItemHeight;
1731 /************************************************************/
1732 /* compute STATEICON bounding box */
1733 /************************************************************/
1734 if (doState)
1736 if (uView == LVS_ICON)
1738 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1739 if (infoPtr->himlNormal)
1740 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1741 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1743 else
1745 /* we need the ident in report mode, if we don't have it, we fail */
1746 State.left = Box.left;
1747 if (uView == LVS_REPORT)
1749 State.left += REPORT_MARGINX;
1750 if (lpLVItem->iSubItem == 0)
1752 assert(lpLVItem->mask & LVIF_INDENT);
1753 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1756 State.top = Box.top;
1758 State.right = State.left;
1759 State.bottom = State.top;
1760 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1762 State.right += infoPtr->iconStateSize.cx;
1763 State.bottom += infoPtr->iconStateSize.cy;
1765 if (lprcState) *lprcState = State;
1766 TRACE(" - state=%s\n", debugrect(&State));
1769 /************************************************************/
1770 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1771 /************************************************************/
1772 if (doIcon)
1774 if (uView == LVS_ICON)
1776 Icon.left = Box.left;
1777 if (infoPtr->himlNormal)
1778 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1779 Icon.top = Box.top + ICON_TOP_PADDING;
1780 Icon.right = Icon.left;
1781 Icon.bottom = Icon.top;
1782 if (infoPtr->himlNormal)
1784 Icon.right += infoPtr->iconSize.cx;
1785 Icon.bottom += infoPtr->iconSize.cy;
1788 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1790 Icon.left = State.right;
1791 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1792 Icon.top = Box.top;
1793 Icon.right = Icon.left;
1794 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1795 Icon.right += infoPtr->iconSize.cx;
1796 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1798 if(lprcIcon) *lprcIcon = Icon;
1799 TRACE(" - icon=%s\n", debugrect(&Icon));
1802 /************************************************************/
1803 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1804 /************************************************************/
1805 if (doLabel)
1807 SIZE labelSize = { 0, 0 };
1809 /* calculate how far to the right can the label strech */
1810 Label.right = Box.right;
1811 if (uView == LVS_REPORT)
1813 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1814 Label.right -= REPORT_MARGINX;
1817 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1819 labelSize.cx = infoPtr->nItemWidth;
1820 labelSize.cy = infoPtr->nItemHeight;
1821 goto calc_label;
1824 /* we need the text in non owner draw mode */
1825 assert(lpLVItem->mask & LVIF_TEXT);
1826 if (is_textT(lpLVItem->pszText, TRUE))
1828 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1829 HDC hdc = GetDC(infoPtr->hwndSelf);
1830 HFONT hOldFont = SelectObject(hdc, hFont);
1831 UINT uFormat;
1832 RECT rcText;
1834 /* compute rough rectangle where the label will go */
1835 SetRectEmpty(&rcText);
1836 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1837 rcText.bottom = infoPtr->nItemHeight;
1838 if (uView == LVS_ICON)
1839 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1841 /* now figure out the flags */
1842 if (uView == LVS_ICON)
1843 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1844 else
1845 uFormat = LV_SL_DT_FLAGS;
1847 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1849 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1850 labelSize.cy = rcText.bottom - rcText.top;
1852 SelectObject(hdc, hOldFont);
1853 ReleaseDC(infoPtr->hwndSelf, hdc);
1856 calc_label:
1857 if (uView == LVS_ICON)
1859 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1860 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1861 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1862 Label.right = Label.left + labelSize.cx;
1863 Label.bottom = Label.top + infoPtr->nItemHeight;
1864 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1866 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1867 labelSize.cy /= infoPtr->ntmHeight;
1868 labelSize.cy = max(labelSize.cy, 1);
1869 labelSize.cy *= infoPtr->ntmHeight;
1871 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1873 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1875 Label.left = Icon.right;
1876 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1877 Label.top = Box.top;
1878 Label.right = min(Label.left + labelSize.cx, Label.right);
1879 Label.bottom = Label.top + infoPtr->nItemHeight;
1882 if (lprcLabel) *lprcLabel = Label;
1883 TRACE(" - label=%s\n", debugrect(&Label));
1886 /* Fix the Box if necessary */
1887 if (lprcBox)
1889 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1890 else *lprcBox = Box;
1892 TRACE(" - box=%s\n", debugrect(&Box));
1895 /***
1896 * DESCRIPTION: [INTERNAL]
1898 * PARAMETER(S):
1899 * [I] infoPtr : valid pointer to the listview structure
1900 * [I] nItem : item number
1901 * [O] lprcBox : ptr to Box rectangle
1903 * RETURN:
1904 * None.
1906 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1908 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1909 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1910 POINT Position, Origin;
1911 LVITEMW lvItem;
1913 LISTVIEW_GetOrigin(infoPtr, &Origin);
1914 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1916 /* Be smart and try to figure out the minimum we have to do */
1917 lvItem.mask = 0;
1918 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1919 lvItem.mask |= LVIF_TEXT;
1920 lvItem.iItem = nItem;
1921 lvItem.iSubItem = 0;
1922 lvItem.pszText = szDispText;
1923 lvItem.cchTextMax = DISP_TEXT_SIZE;
1924 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
1925 if (uView == LVS_ICON)
1927 lvItem.mask |= LVIF_STATE;
1928 lvItem.stateMask = LVIS_FOCUSED;
1929 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1931 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
1933 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1937 /***
1938 * DESCRIPTION:
1939 * Returns the current icon position, and advances it along the top.
1940 * The returned position is not offset by Origin.
1942 * PARAMETER(S):
1943 * [I] infoPtr : valid pointer to the listview structure
1944 * [O] lpPos : will get the current icon position
1946 * RETURN:
1947 * None
1949 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
1951 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1953 *lpPos = infoPtr->currIconPos;
1955 infoPtr->currIconPos.x += infoPtr->nItemWidth;
1956 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
1958 infoPtr->currIconPos.x = 0;
1959 infoPtr->currIconPos.y += infoPtr->nItemHeight;
1963 /***
1964 * DESCRIPTION:
1965 * Returns the current icon position, and advances it down the left edge.
1966 * The returned position is not offset by Origin.
1968 * PARAMETER(S):
1969 * [I] infoPtr : valid pointer to the listview structure
1970 * [O] lpPos : will get the current icon position
1972 * RETURN:
1973 * None
1975 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
1977 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1979 *lpPos = infoPtr->currIconPos;
1981 infoPtr->currIconPos.y += infoPtr->nItemHeight;
1982 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
1984 infoPtr->currIconPos.x += infoPtr->nItemWidth;
1985 infoPtr->currIconPos.y = 0;
1989 /***
1990 * DESCRIPTION:
1991 * Moves an icon to the specified position.
1992 * It takes care of invalidating the item, etc.
1994 * PARAMETER(S):
1995 * [I] infoPtr : valid pointer to the listview structure
1996 * [I] nItem : the item to move
1997 * [I] lpPos : the new icon position
1998 * [I] isNew : flags the item as being new
2000 * RETURN:
2001 * Success: TRUE
2002 * Failure: FALSE
2004 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lppt, BOOL isNew)
2006 POINT old;
2008 if (!isNew)
2010 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2011 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2013 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2014 LISTVIEW_InvalidateItem(infoPtr, nItem);
2017 /* Allocating a POINTER for every item is too resource intensive,
2018 * so we'll keep the (x,y) in different arrays */
2019 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2020 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2022 LISTVIEW_InvalidateItem(infoPtr, nItem);
2024 return TRUE;
2027 /***
2028 * DESCRIPTION:
2029 * Arranges listview items in icon display mode.
2031 * PARAMETER(S):
2032 * [I] infoPtr : valid pointer to the listview structure
2033 * [I] nAlignCode : alignment code
2035 * RETURN:
2036 * SUCCESS : TRUE
2037 * FAILURE : FALSE
2039 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2041 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2042 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2043 POINT pos;
2044 INT i;
2046 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2048 if (nAlignCode == LVA_DEFAULT)
2050 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2051 else nAlignCode = LVA_ALIGNTOP;
2054 switch (nAlignCode)
2056 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2057 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2058 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2059 default: return FALSE;
2062 infoPtr->bAutoarrange = TRUE;
2063 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2064 for (i = 0; i < infoPtr->nItemCount; i++)
2066 next_pos(infoPtr, &pos);
2067 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2070 return TRUE;
2073 /***
2074 * DESCRIPTION:
2075 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2077 * PARAMETER(S):
2078 * [I] infoPtr : valid pointer to the listview structure
2079 * [O] lprcView : bounding rectangle
2081 * RETURN:
2082 * SUCCESS : TRUE
2083 * FAILURE : FALSE
2085 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2087 INT i, x, y;
2089 SetRectEmpty(lprcView);
2091 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2093 case LVS_ICON:
2094 case LVS_SMALLICON:
2095 for (i = 0; i < infoPtr->nItemCount; i++)
2097 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2098 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2099 lprcView->right = max(lprcView->right, x);
2100 lprcView->bottom = max(lprcView->bottom, y);
2102 if (infoPtr->nItemCount > 0)
2104 lprcView->right += infoPtr->nItemWidth;
2105 lprcView->bottom += infoPtr->nItemHeight;
2107 break;
2109 case LVS_LIST:
2110 y = LISTVIEW_GetCountPerColumn(infoPtr);
2111 x = infoPtr->nItemCount / y;
2112 if (infoPtr->nItemCount % y) x++;
2113 lprcView->right = x * infoPtr->nItemWidth;
2114 lprcView->bottom = y * infoPtr->nItemHeight;
2115 break;
2117 case LVS_REPORT:
2118 lprcView->right = infoPtr->nItemWidth;
2119 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2120 break;
2124 /***
2125 * DESCRIPTION:
2126 * Retrieves the bounding rectangle of all the items.
2128 * PARAMETER(S):
2129 * [I] infoPtr : valid pointer to the listview structure
2130 * [O] lprcView : bounding rectangle
2132 * RETURN:
2133 * SUCCESS : TRUE
2134 * FAILURE : FALSE
2136 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2138 POINT ptOrigin;
2140 TRACE("(lprcView=%p)\n", lprcView);
2142 if (!lprcView) return FALSE;
2144 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2145 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2146 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2148 TRACE("lprcView=%s\n", debugrect(lprcView));
2150 return TRUE;
2153 /***
2154 * DESCRIPTION:
2155 * Retrieves the subitem pointer associated with the subitem index.
2157 * PARAMETER(S):
2158 * [I] hdpaSubItems : DPA handle for a specific item
2159 * [I] nSubItem : index of subitem
2161 * RETURN:
2162 * SUCCESS : subitem pointer
2163 * FAILURE : NULL
2165 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2167 SUBITEM_INFO *lpSubItem;
2168 INT i;
2170 /* we should binary search here if need be */
2171 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2173 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2174 if (lpSubItem->iSubItem == nSubItem)
2175 return lpSubItem;
2178 return NULL;
2182 /***
2183 * DESCRIPTION:
2184 * Caclulates the desired item width.
2186 * PARAMETER(S):
2187 * [I] infoPtr : valid pointer to the listview structure
2189 * RETURN:
2190 * The desired item width.
2192 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2194 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2195 INT nItemWidth;
2197 if (uView == LVS_ICON)
2198 nItemWidth = infoPtr->iconSpacing.cx;
2199 else if (uView == LVS_REPORT)
2201 RECT rcHeader;
2203 if (infoPtr->hdpaColumns->nItemCount > 0)
2205 LISTVIEW_GetHeaderRect(infoPtr, infoPtr->hdpaColumns->nItemCount - 1, &rcHeader);
2206 nItemWidth = rcHeader.right;
2208 else nItemWidth = 0;
2210 else /* LVS_SMALLICON, or LVS_LIST */
2212 INT i;
2214 for (nItemWidth = i = 0; i < infoPtr->nItemCount; i++)
2215 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2217 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
2218 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx + IMAGE_PADDING;
2220 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2223 return max(nItemWidth, 1);
2226 /***
2227 * DESCRIPTION:
2228 * Caclulates the desired item height.
2230 * PARAMETER(S):
2231 * [I] infoPtr : valid pointer to the listview structure
2233 * RETURN:
2234 * The desired item height.
2236 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2238 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2239 INT nItemHeight;
2241 if (uView == LVS_ICON)
2242 nItemHeight = infoPtr->iconSpacing.cy;
2243 else
2245 nItemHeight = infoPtr->ntmHeight;
2246 if (infoPtr->himlState)
2247 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2248 if (infoPtr->himlSmall)
2249 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2250 if (infoPtr->himlState || infoPtr->himlSmall)
2251 nItemHeight += HEIGHT_PADDING;
2254 return max(nItemHeight, 1);
2257 /***
2258 * DESCRIPTION:
2259 * Updates the width, and height of an item.
2261 * PARAMETER(S):
2262 * [I] infoPtr : valid pointer to the listview structure
2264 * RETURN:
2265 * None.
2267 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2269 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2270 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2274 /***
2275 * DESCRIPTION:
2276 * Retrieves and saves important text metrics info for the current
2277 * Listview font.
2279 * PARAMETER(S):
2280 * [I] infoPtr : valid pointer to the listview structure
2283 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2285 HDC hdc = GetDC(infoPtr->hwndSelf);
2286 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2287 HFONT hOldFont = SelectObject(hdc, hFont);
2288 TEXTMETRICW tm;
2290 if (GetTextMetricsW(hdc, &tm))
2291 infoPtr->ntmHeight = tm.tmHeight;
2292 SelectObject(hdc, hOldFont);
2293 ReleaseDC(infoPtr->hwndSelf, hdc);
2295 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2298 /***
2299 * DESCRIPTION:
2300 * A compare function for ranges
2302 * PARAMETER(S)
2303 * [I] range1 : pointer to range 1;
2304 * [I] range2 : pointer to range 2;
2305 * [I] flags : flags
2307 * RETURNS:
2308 * > 0 : if range 1 > range 2
2309 * < 0 : if range 2 > range 1
2310 * = 0 : if range intersects range 2
2312 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2314 INT cmp;
2316 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2317 cmp = -1;
2318 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2319 cmp = 1;
2320 else
2321 cmp = 0;
2323 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2325 return cmp;
2328 #if DEBUG_RANGES
2329 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2330 #else
2331 #define ranges_check(ranges, desc) do { } while(0)
2332 #endif
2334 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2336 INT i;
2337 RANGE *prev, *curr;
2339 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2340 assert (ranges);
2341 assert (ranges->hdpa->nItemCount >= 0);
2342 ranges_dump(ranges);
2343 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2344 if (ranges->hdpa->nItemCount > 0)
2345 assert (prev->lower >= 0 && prev->lower < prev->upper);
2346 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2348 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2349 assert (prev->upper <= curr->lower);
2350 assert (curr->lower < curr->upper);
2351 prev = curr;
2353 TRACE("--- Done checking---\n");
2356 static RANGES ranges_create(int count)
2358 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2359 if (!ranges) return NULL;
2360 ranges->hdpa = DPA_Create(count);
2361 if (ranges->hdpa) return ranges;
2362 COMCTL32_Free(ranges);
2363 return NULL;
2366 static void ranges_clear(RANGES ranges)
2368 INT i;
2370 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2371 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2372 DPA_DeleteAllPtrs(ranges->hdpa);
2376 static void ranges_destroy(RANGES ranges)
2378 ranges_clear(ranges);
2379 DPA_Destroy(ranges->hdpa);
2380 COMCTL32_Free(ranges);
2383 static RANGES ranges_clone(RANGES ranges)
2385 RANGES clone;
2386 INT i;
2388 if (!(clone = ranges_create(ranges->hdpa->nItemCount))) goto fail;
2390 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2392 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2393 if (!newrng) goto fail;
2394 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2395 DPA_SetPtr(clone->hdpa, i, newrng);
2397 return clone;
2399 fail:
2400 TRACE ("clone failed\n");
2401 if (clone) ranges_destroy(clone);
2402 return NULL;
2405 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2407 INT i;
2409 for (i = 0; i < sub->hdpa->nItemCount; i++)
2410 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2412 return ranges;
2415 static void ranges_dump(RANGES ranges)
2417 INT i;
2419 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2420 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2423 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2425 RANGE srchrng = { nItem, nItem + 1 };
2427 TRACE("(nItem=%d)\n", nItem);
2428 ranges_check(ranges, "before contain");
2429 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2432 static INT ranges_itemcount(RANGES ranges)
2434 INT i, count = 0;
2436 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2438 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2439 count += sel->upper - sel->lower;
2442 return count;
2445 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2447 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2448 INT index;
2450 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2451 if (index == -1) return TRUE;
2453 for (; index < ranges->hdpa->nItemCount; index++)
2455 chkrng = DPA_GetPtr(ranges->hdpa, index);
2456 if (chkrng->lower >= nItem)
2457 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2458 if (chkrng->upper > nItem)
2459 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2461 return TRUE;
2464 static BOOL ranges_add(RANGES ranges, RANGE range)
2466 RANGE srchrgn;
2467 INT index;
2469 TRACE("(%s)\n", debugrange(&range));
2470 ranges_check(ranges, "before add");
2472 /* try find overlapping regions first */
2473 srchrgn.lower = range.lower - 1;
2474 srchrgn.upper = range.upper + 1;
2475 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2477 if (index == -1)
2479 RANGE *newrgn;
2481 TRACE("Adding new range\n");
2483 /* create the brand new range to insert */
2484 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2485 if(!newrgn) goto fail;
2486 *newrgn = range;
2488 /* figure out where to insert it */
2489 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2490 TRACE("index=%d\n", index);
2491 if (index == -1) index = 0;
2493 /* and get it over with */
2494 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2496 COMCTL32_Free(newrgn);
2497 goto fail;
2500 else
2502 RANGE *chkrgn, *mrgrgn;
2503 INT fromindex, mergeindex;
2505 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2506 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2508 chkrgn->lower = min(range.lower, chkrgn->lower);
2509 chkrgn->upper = max(range.upper, chkrgn->upper);
2511 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2513 /* merge now common anges */
2514 fromindex = 0;
2515 srchrgn.lower = chkrgn->lower - 1;
2516 srchrgn.upper = chkrgn->upper + 1;
2520 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2521 if (mergeindex == -1) break;
2522 if (mergeindex == index)
2524 fromindex = index + 1;
2525 continue;
2528 TRACE("Merge with index %i\n", mergeindex);
2530 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2531 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2532 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2533 COMCTL32_Free(mrgrgn);
2534 DPA_DeletePtr(ranges->hdpa, mergeindex);
2535 if (mergeindex < index) index --;
2536 } while(1);
2539 ranges_check(ranges, "after add");
2540 return TRUE;
2542 fail:
2543 ranges_check(ranges, "failed add");
2544 return FALSE;
2547 static BOOL ranges_del(RANGES ranges, RANGE range)
2549 RANGE *chkrgn;
2550 INT index;
2552 TRACE("(%s)\n", debugrange(&range));
2553 ranges_check(ranges, "before del");
2555 /* we don't use DPAS_SORTED here, since we need *
2556 * to find the first overlapping range */
2557 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2558 while(index != -1)
2560 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2562 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2564 /* case 1: Same range */
2565 if ( (chkrgn->upper == range.upper) &&
2566 (chkrgn->lower == range.lower) )
2568 DPA_DeletePtr(ranges->hdpa, index);
2569 break;
2571 /* case 2: engulf */
2572 else if ( (chkrgn->upper <= range.upper) &&
2573 (chkrgn->lower >= range.lower) )
2575 DPA_DeletePtr(ranges->hdpa, index);
2577 /* case 3: overlap upper */
2578 else if ( (chkrgn->upper <= range.upper) &&
2579 (chkrgn->lower < range.lower) )
2581 chkrgn->upper = range.lower;
2583 /* case 4: overlap lower */
2584 else if ( (chkrgn->upper > range.upper) &&
2585 (chkrgn->lower >= range.lower) )
2587 chkrgn->lower = range.upper;
2588 break;
2590 /* case 5: fully internal */
2591 else
2593 RANGE tmprgn = *chkrgn, *newrgn;
2595 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2596 newrgn->lower = chkrgn->lower;
2597 newrgn->upper = range.lower;
2598 chkrgn->lower = range.upper;
2599 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2601 COMCTL32_Free(newrgn);
2602 goto fail;
2604 chkrgn = &tmprgn;
2605 break;
2608 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2611 ranges_check(ranges, "after del");
2612 return TRUE;
2614 fail:
2615 ranges_check(ranges, "failed del");
2616 return FALSE;
2619 /***
2620 * DESCRIPTION:
2621 * Removes all selection ranges
2623 * Parameters(s):
2624 * [I] infoPtr : valid pointer to the listview structure
2625 * [I] toSkip : item range to skip removing the selection
2627 * RETURNS:
2628 * SUCCESS : TRUE
2629 * FAILURE : TRUE
2631 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2633 LVITEMW lvItem;
2634 ITERATOR i;
2635 RANGES clone;
2637 TRACE("()\n");
2639 lvItem.state = 0;
2640 lvItem.stateMask = LVIS_SELECTED;
2642 /* need to clone the DPA because callbacks can change it */
2643 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2644 iterator_ranges(&i, ranges_diff(clone, toSkip));
2645 while(iterator_next(&i))
2646 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2647 /* note that the iterator destructor will free the cloned range */
2648 iterator_destroy(&i);
2650 return TRUE;
2653 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2655 RANGES toSkip;
2657 if (!(toSkip = ranges_create(1))) return FALSE;
2658 if (nItem != -1) ranges_additem(toSkip, nItem);
2659 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2660 ranges_destroy(toSkip);
2661 return TRUE;
2664 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2666 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2669 /***
2670 * DESCRIPTION:
2671 * Retrieves the number of items that are marked as selected.
2673 * PARAMETER(S):
2674 * [I] infoPtr : valid pointer to the listview structure
2676 * RETURN:
2677 * Number of items selected.
2679 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2681 INT nSelectedCount = 0;
2683 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2685 INT i;
2686 for (i = 0; i < infoPtr->nItemCount; i++)
2688 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2689 nSelectedCount++;
2692 else
2693 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2695 TRACE("nSelectedCount=%d\n", nSelectedCount);
2696 return nSelectedCount;
2699 /***
2700 * DESCRIPTION:
2701 * Manages the item focus.
2703 * PARAMETER(S):
2704 * [I] infoPtr : valid pointer to the listview structure
2705 * [I] nItem : item index
2707 * RETURN:
2708 * TRUE : focused item changed
2709 * FALSE : focused item has NOT changed
2711 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2713 INT oldFocus = infoPtr->nFocusedItem;
2714 LVITEMW lvItem;
2716 if (nItem == infoPtr->nFocusedItem) return FALSE;
2718 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2719 lvItem.stateMask = LVIS_FOCUSED;
2720 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2722 return oldFocus != infoPtr->nFocusedItem;
2725 /* Helper function for LISTVIEW_ShiftIndices *only* */
2726 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2728 if (nShiftItem < nItem) return nShiftItem;
2730 if (nShiftItem > nItem) return nShiftItem + direction;
2732 if (direction > 0) return nShiftItem + direction;
2734 return min(nShiftItem, infoPtr->nItemCount - 1);
2738 * DESCRIPTION:
2739 * Updates the various indices after an item has been inserted or deleted.
2741 * PARAMETER(S):
2742 * [I] infoPtr : valid pointer to the listview structure
2743 * [I] nItem : item index
2744 * [I] direction : Direction of shift, +1 or -1.
2746 * RETURN:
2747 * None
2749 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2751 INT nNewFocus;
2753 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2755 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2757 assert(abs(direction) == 1);
2759 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2761 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2762 if (nNewFocus != infoPtr->nFocusedItem)
2763 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2765 /* But we are not supposed to modify nHotItem! */
2770 * DESCRIPTION:
2771 * Adds a block of selections.
2773 * PARAMETER(S):
2774 * [I] infoPtr : valid pointer to the listview structure
2775 * [I] nItem : item index
2777 * RETURN:
2778 * None
2780 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2782 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2783 INT nLast = max(infoPtr->nSelectionMark, nItem);
2784 INT i;
2785 LVITEMW item;
2787 if (nFirst == -1) nFirst = nItem;
2789 item.state = LVIS_SELECTED;
2790 item.stateMask = LVIS_SELECTED;
2792 /* FIXME: this is not correct LVS_OWNERDATA
2793 * setting the item states individually will generate
2794 * a LVN_ITEMCHANGED notification for each one. Instead,
2795 * we have to send a LVN_ODSTATECHANGED notification.
2796 * See MSDN documentation for LVN_ITEMCHANGED.
2798 for (i = nFirst; i <= nLast; i++)
2799 LISTVIEW_SetItemState(infoPtr,i,&item);
2803 /***
2804 * DESCRIPTION:
2805 * Sets a single group selection.
2807 * PARAMETER(S):
2808 * [I] infoPtr : valid pointer to the listview structure
2809 * [I] nItem : item index
2811 * RETURN:
2812 * None
2814 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2816 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2817 RANGES selection;
2818 LVITEMW item;
2819 ITERATOR i;
2821 if (!(selection = ranges_create(100))) return;
2823 item.state = LVIS_SELECTED;
2824 item.stateMask = LVIS_SELECTED;
2826 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2828 if (infoPtr->nSelectionMark == -1)
2830 infoPtr->nSelectionMark = nItem;
2831 ranges_additem(selection, nItem);
2833 else
2835 RANGE sel;
2837 sel.lower = min(infoPtr->nSelectionMark, nItem);
2838 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2839 ranges_add(selection, sel);
2842 else
2844 RECT rcItem, rcSel, rcSelMark;
2845 POINT ptItem;
2847 rcItem.left = LVIR_BOUNDS;
2848 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2849 rcSelMark.left = LVIR_BOUNDS;
2850 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2851 UnionRect(&rcSel, &rcItem, &rcSelMark);
2852 iterator_frameditems(&i, infoPtr, &rcSel);
2853 while(iterator_next(&i))
2855 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2856 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
2858 iterator_destroy(&i);
2861 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
2862 iterator_ranges(&i, selection);
2863 while(iterator_next(&i))
2864 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2865 /* this will also destroy the selection */
2866 iterator_destroy(&i);
2868 LISTVIEW_SetItemFocus(infoPtr, nItem);
2871 /***
2872 * DESCRIPTION:
2873 * Sets a single selection.
2875 * PARAMETER(S):
2876 * [I] infoPtr : valid pointer to the listview structure
2877 * [I] nItem : item index
2879 * RETURN:
2880 * None
2882 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2884 LVITEMW lvItem;
2886 TRACE("nItem=%d\n", nItem);
2888 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
2890 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2891 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2892 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2894 infoPtr->nSelectionMark = nItem;
2897 /***
2898 * DESCRIPTION:
2899 * Set selection(s) with keyboard.
2901 * PARAMETER(S):
2902 * [I] infoPtr : valid pointer to the listview structure
2903 * [I] nItem : item index
2905 * RETURN:
2906 * SUCCESS : TRUE (needs to be repainted)
2907 * FAILURE : FALSE (nothing has changed)
2909 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2911 /* FIXME: pass in the state */
2912 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2913 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2914 BOOL bResult = FALSE;
2916 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2918 if (infoPtr->dwStyle & LVS_SINGLESEL)
2920 bResult = TRUE;
2921 LISTVIEW_SetSelection(infoPtr, nItem);
2923 else
2925 if (wShift)
2927 bResult = TRUE;
2928 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2930 else if (wCtrl)
2932 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2934 else
2936 bResult = TRUE;
2937 LISTVIEW_SetSelection(infoPtr, nItem);
2940 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2943 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2944 return bResult;
2948 /***
2949 * DESCRIPTION:
2950 * Called when the mouse is being actively tracked and has hovered for a specified
2951 * amount of time
2953 * PARAMETER(S):
2954 * [I] infoPtr : valid pointer to the listview structure
2955 * [I] fwKeys : key indicator
2956 * [I] pts : mouse position
2958 * RETURN:
2959 * 0 if the message was processed, non-zero if there was an error
2961 * INFO:
2962 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2963 * over the item for a certain period of time.
2966 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2968 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2969 /* FIXME: select the item!!! */
2970 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2972 return 0;
2975 /***
2976 * DESCRIPTION:
2977 * Called whenever WM_MOUSEMOVE is received.
2979 * PARAMETER(S):
2980 * [I] infoPtr : valid pointer to the listview structure
2981 * [I] fwKeys : key indicator
2982 * [I] pts : mouse position
2984 * RETURN:
2985 * 0 if the message is processed, non-zero if there was an error
2987 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2989 TRACKMOUSEEVENT trackinfo;
2991 /* see if we are supposed to be tracking mouse hovering */
2992 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2993 /* fill in the trackinfo struct */
2994 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2995 trackinfo.dwFlags = TME_QUERY;
2996 trackinfo.hwndTrack = infoPtr->hwndSelf;
2997 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2999 /* see if we are already tracking this hwnd */
3000 _TrackMouseEvent(&trackinfo);
3002 if(!(trackinfo.dwFlags & TME_HOVER)) {
3003 trackinfo.dwFlags = TME_HOVER;
3005 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3006 _TrackMouseEvent(&trackinfo);
3010 return 0;
3014 /***
3015 * Tests wheather the item is assignable to a list with style lStyle
3017 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
3019 if ( (lpLVItem->mask & LVIF_TEXT) &&
3020 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3021 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3023 return TRUE;
3027 /***
3028 * DESCRIPTION:
3029 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3031 * PARAMETER(S):
3032 * [I] infoPtr : valid pointer to the listview structure
3033 * [I] lpLVItem : valid pointer to new item atttributes
3034 * [I] isNew : the item being set is being inserted
3035 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3036 * [O] bChanged : will be set to TRUE if the item really changed
3038 * RETURN:
3039 * SUCCESS : TRUE
3040 * FAILURE : FALSE
3042 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3044 ITEM_INFO *lpItem;
3045 NMLISTVIEW nmlv;
3046 UINT uChanged = 0;
3047 LVITEMW item;
3049 TRACE("()\n");
3051 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3053 if (lpLVItem->mask == 0) return TRUE;
3055 if (infoPtr->dwStyle & LVS_OWNERDATA)
3057 /* a virtual listview we stores only selection and focus */
3058 if ((lpLVItem->mask & ~LVIF_STATE) || (lpLVItem->stateMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3059 return FALSE;
3060 lpItem = NULL;
3062 else
3064 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3065 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3066 assert (lpItem);
3069 /* we need to get the lParam and state of the item */
3070 item.iItem = lpLVItem->iItem;
3071 item.iSubItem = lpLVItem->iSubItem;
3072 item.mask = LVIF_STATE | LVIF_PARAM;
3073 item.stateMask = ~0;
3074 item.state = 0;
3075 item.lParam = 0;
3076 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3078 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3079 /* determine what fields will change */
3080 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3081 uChanged |= LVIF_STATE;
3083 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3084 uChanged |= LVIF_IMAGE;
3086 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3087 uChanged |= LVIF_PARAM;
3089 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3090 uChanged |= LVIF_INDENT;
3092 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3093 uChanged |= LVIF_TEXT;
3095 TRACE("uChanged=0x%x\n", uChanged);
3096 if (!uChanged) return TRUE;
3097 *bChanged = TRUE;
3099 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3100 nmlv.iItem = lpLVItem->iItem;
3101 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3102 nmlv.uOldState = item.state;
3103 nmlv.uChanged = uChanged;
3104 nmlv.lParam = item.lParam;
3106 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3107 /* and we are _NOT_ virtual (LVS_OWERNDATA) */
3108 if(lpItem && !isNew && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3109 return FALSE;
3111 /* copy information */
3112 if (lpLVItem->mask & LVIF_TEXT)
3113 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3115 if (lpLVItem->mask & LVIF_IMAGE)
3116 lpItem->hdr.iImage = lpLVItem->iImage;
3118 if (lpLVItem->mask & LVIF_PARAM)
3119 lpItem->lParam = lpLVItem->lParam;
3121 if (lpLVItem->mask & LVIF_INDENT)
3122 lpItem->iIndent = lpLVItem->iIndent;
3124 if (uChanged & LVIF_STATE)
3126 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED))
3128 lpItem->state &= ~lpLVItem->stateMask;
3129 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3131 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3133 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3134 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3136 else if (lpLVItem->stateMask & LVIS_SELECTED)
3137 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3139 /* if we are asked to change focus, and we manage it, do it */
3140 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3142 if (lpLVItem->state & LVIS_FOCUSED)
3144 LISTVIEW_SetItemFocus(infoPtr, -1);
3145 infoPtr->nFocusedItem = lpLVItem->iItem;
3146 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
3148 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3149 infoPtr->nFocusedItem = -1;
3153 /* if we're inserting the item, we're done */
3154 if (isNew) return TRUE;
3156 /* send LVN_ITEMCHANGED notification */
3157 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3158 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3160 return TRUE;
3163 /***
3164 * DESCRIPTION:
3165 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3167 * PARAMETER(S):
3168 * [I] infoPtr : valid pointer to the listview structure
3169 * [I] lpLVItem : valid pointer to new subitem atttributes
3170 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3171 * [O] bChanged : will be set to TRUE if the item really changed
3173 * RETURN:
3174 * SUCCESS : TRUE
3175 * FAILURE : FALSE
3177 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
3179 HDPA hdpaSubItems;
3180 SUBITEM_INFO *lpSubItem;
3182 /* we do not support subitems for virtual listviews */
3183 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3185 /* set subitem only if column is present */
3186 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3188 /* First do some sanity checks */
3189 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3190 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3192 /* get the subitem structure, and create it if not there */
3193 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3194 assert (hdpaSubItems);
3196 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3197 if (!lpSubItem)
3199 SUBITEM_INFO *tmpSubItem;
3200 INT i;
3202 lpSubItem = (SUBITEM_INFO *)COMCTL32_Alloc(sizeof(SUBITEM_INFO));
3203 if (!lpSubItem) return FALSE;
3204 /* we could binary search here, if need be...*/
3205 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3207 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3208 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3210 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3212 COMCTL32_Free(lpSubItem);
3213 return FALSE;
3215 lpSubItem->iSubItem = lpLVItem->iSubItem;
3216 *bChanged = TRUE;
3219 if (lpLVItem->mask & LVIF_IMAGE)
3220 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3222 lpSubItem->hdr.iImage = lpLVItem->iImage;
3223 *bChanged = TRUE;
3226 if (lpLVItem->mask & LVIF_TEXT)
3227 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3229 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3230 *bChanged = TRUE;
3233 return TRUE;
3236 /***
3237 * DESCRIPTION:
3238 * Sets item attributes.
3240 * PARAMETER(S):
3241 * [I] infoPtr : valid pointer to the listview structure
3242 * [I] lpLVItem : new item atttributes
3243 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3245 * RETURN:
3246 * SUCCESS : TRUE
3247 * FAILURE : FALSE
3249 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3251 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3252 LPWSTR pszText = NULL;
3253 BOOL bResult, bChanged = FALSE;
3255 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3257 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3258 return FALSE;
3260 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3261 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3263 pszText = lpLVItem->pszText;
3264 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3267 /* actually set the fields */
3268 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3270 if (lpLVItem->iSubItem)
3271 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3272 else
3273 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3275 /* redraw item, if necessary */
3276 if (bChanged && !infoPtr->bIsDrawing)
3278 /* this little optimization eliminates some nasty flicker */
3279 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3280 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3281 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3282 else
3283 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3285 /* restore text */
3286 if (pszText)
3288 textfreeT(lpLVItem->pszText, isW);
3289 lpLVItem->pszText = pszText;
3292 return bResult;
3295 /***
3296 * DESCRIPTION:
3297 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3299 * PARAMETER(S):
3300 * [I] infoPtr : valid pointer to the listview structure
3302 * RETURN:
3303 * item index
3305 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3307 LONG lStyle = infoPtr->dwStyle;
3308 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3309 INT nItem = 0;
3310 SCROLLINFO scrollInfo;
3312 scrollInfo.cbSize = sizeof(SCROLLINFO);
3313 scrollInfo.fMask = SIF_POS;
3315 if (uView == LVS_LIST)
3317 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3318 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3320 else if (uView == LVS_REPORT)
3322 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3323 nItem = scrollInfo.nPos;
3325 else
3327 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3328 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3331 TRACE("nItem=%d\n", nItem);
3333 return nItem;
3337 /***
3338 * DESCRIPTION:
3339 * Erases the background of the given rectangle
3341 * PARAMETER(S):
3342 * [I] infoPtr : valid pointer to the listview structure
3343 * [I] hdc : device context handle
3344 * [I] lprcBox : clipping rectangle
3346 * RETURN:
3347 * Success: TRUE
3348 * Failure: FALSE
3350 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3352 if (!infoPtr->hBkBrush) return FALSE;
3354 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3356 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3359 /***
3360 * DESCRIPTION:
3361 * Draws an item.
3363 * PARAMETER(S):
3364 * [I] infoPtr : valid pointer to the listview structure
3365 * [I] hdc : device context handle
3366 * [I] nItem : item index
3367 * [I] nSubItem : subitem index
3368 * [I] pos : item position in client coordinates
3369 * [I] cdmode : custom draw mode
3371 * RETURN:
3372 * Success: TRUE
3373 * Failure: FALSE
3375 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3377 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3378 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3379 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3380 DWORD cditemmode = CDRF_DODEFAULT;
3381 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3382 NMLVCUSTOMDRAW nmlvcd;
3383 HIMAGELIST himl;
3384 LVITEMW lvItem;
3386 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3388 /* get information needed for drawing the item */
3389 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3390 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3391 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3392 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3393 lvItem.iItem = nItem;
3394 lvItem.iSubItem = nSubItem;
3395 lvItem.state = 0;
3396 lvItem.lParam = 0;
3397 lvItem.cchTextMax = DISP_TEXT_SIZE;
3398 lvItem.pszText = szDispText;
3399 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3400 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3401 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3402 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3403 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3405 /* now check if we need to update the focus rectangle */
3406 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3408 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3409 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3410 OffsetRect(&rcBox, pos.x, pos.y);
3411 OffsetRect(&rcState, pos.x, pos.y);
3412 OffsetRect(&rcIcon, pos.x, pos.y);
3413 OffsetRect(&rcLabel, pos.x, pos.y);
3414 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3415 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3417 /* fill in the custom draw structure */
3418 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox);
3419 nmlvcd.nmcd.dwItemSpec = lvItem.iItem;
3420 nmlvcd.iSubItem = lvItem.iSubItem;
3421 if (lvItem.state & LVIS_SELECTED) nmlvcd.nmcd.uItemState |= CDIS_SELECTED;
3422 if (lvItem.state & LVIS_FOCUSED) nmlvcd.nmcd.uItemState |= CDIS_FOCUS;
3423 if (lvItem.iItem == infoPtr->nHotItem) nmlvcd.nmcd.uItemState |= CDIS_HOT;
3424 nmlvcd.nmcd.lItemlParam = lvItem.lParam;
3426 if (cdmode & CDRF_NOTIFYITEMDRAW)
3427 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3428 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3430 /* apprently, for selected items, we have to override the returned values */
3431 if (lvItem.state & LVIS_SELECTED)
3433 if (infoPtr->bFocus)
3435 nmlvcd.clrTextBk = comctl32_color.clrHighlight;
3436 nmlvcd.clrText = comctl32_color.clrHighlightText;
3438 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
3440 nmlvcd.clrTextBk = comctl32_color.clr3dFace;
3441 nmlvcd.clrText = comctl32_color.clrBtnText;
3445 /* state icons */
3446 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3448 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3449 if (uStateImage)
3451 TRACE("uStateImage=%d\n", uStateImage);
3452 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3456 /* small icons */
3457 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3458 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3460 TRACE("iImage=%d\n", lvItem.iImage);
3461 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3462 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3465 /* Don't bother painting item being edited */
3466 if (infoPtr->hwndEdit && lprcFocus && nSubItem == 0) goto postpaint;
3468 /* Set the text attributes */
3469 if (nmlvcd.clrTextBk != CLR_NONE)
3471 SetBkMode(hdc, OPAQUE);
3472 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3474 else
3475 SetBkMode(hdc, TRANSPARENT);
3476 SetTextColor(hdc, nmlvcd.clrText);
3478 /* draw the selection background, if we're drawing the main item */
3479 if (nSubItem == 0)
3481 rcSelect = rcLabel;
3482 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3483 rcSelect.right = rcBox.right;
3485 if (nmlvcd.clrTextBk != CLR_NONE)
3486 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3487 if(lprcFocus) *lprcFocus = rcSelect;
3490 /* figure out the text drawing flags */
3491 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3492 if (uView == LVS_ICON)
3493 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3494 else if (nSubItem)
3496 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3498 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3499 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3500 default: uFormat |= DT_LEFT;
3503 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3504 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3506 postpaint:
3507 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3508 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3509 return TRUE;
3512 /***
3513 * DESCRIPTION:
3514 * Draws listview items when in owner draw mode.
3516 * PARAMETER(S):
3517 * [I] infoPtr : valid pointer to the listview structure
3518 * [I] hdc : device context handle
3520 * RETURN:
3521 * None
3523 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3525 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3526 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3527 POINT Origin, Position;
3528 DRAWITEMSTRUCT dis;
3529 LVITEMW item;
3530 ITERATOR i;
3532 TRACE("()\n");
3534 ZeroMemory(&dis, sizeof(dis));
3536 /* Get scroll info once before loop */
3537 LISTVIEW_GetOrigin(infoPtr, &Origin);
3539 /* figure out what we need to draw */
3540 iterator_visibleitems(&i, infoPtr, hdc);
3542 /* send cache hint notification */
3543 if (infoPtr->dwStyle & LVS_OWNERDATA)
3545 RANGE range = iterator_range(&i);
3546 NMLVCACHEHINT nmlv;
3548 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3549 nmlv.iFrom = range.lower;
3550 nmlv.iTo = range.upper - 1;
3551 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3554 /* iterate through the invalidated rows */
3555 while(iterator_next(&i))
3557 item.iItem = i.nItem;
3558 item.iSubItem = 0;
3559 item.mask = LVIF_PARAM | LVIF_STATE;
3560 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3561 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3563 dis.CtlType = ODT_LISTVIEW;
3564 dis.CtlID = uID;
3565 dis.itemID = item.iItem;
3566 dis.itemAction = ODA_DRAWENTIRE;
3567 dis.itemState = 0;
3568 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3569 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3570 dis.hwndItem = infoPtr->hwndSelf;
3571 dis.hDC = hdc;
3572 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3573 dis.rcItem.left = Position.x + Origin.x;
3574 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3575 dis.rcItem.top = Position.y + Origin.y;
3576 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3577 dis.itemData = item.lParam;
3579 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3580 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3582 iterator_destroy(&i);
3585 /***
3586 * DESCRIPTION:
3587 * Draws listview items when in report display mode.
3589 * PARAMETER(S):
3590 * [I] infoPtr : valid pointer to the listview structure
3591 * [I] hdc : device context handle
3592 * [I] cdmode : custom draw mode
3594 * RETURN:
3595 * None
3597 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3599 INT rgntype, nFirstCol, nLastCol, nCol;
3600 RECT rcClip, rcItem;
3601 POINT Origin, Position;
3602 ITERATOR i;
3604 TRACE("()\n");
3606 /* figure out what to draw */
3607 rgntype = GetClipBox(hdc, &rcClip);
3608 if (rgntype == NULLREGION) return;
3610 /* Get scroll info once before loop */
3611 LISTVIEW_GetOrigin(infoPtr, &Origin);
3613 /* narrow down the columns we need to paint */
3614 for(nFirstCol = 0; nFirstCol < infoPtr->hdpaColumns->nItemCount; nFirstCol++)
3616 LISTVIEW_GetHeaderRect(infoPtr, nFirstCol, &rcItem);
3617 if (rcItem.right + Origin.x >= rcClip.left) break;
3619 for(nLastCol = infoPtr->hdpaColumns->nItemCount - 1; nLastCol >= 0; nLastCol--)
3621 LISTVIEW_GetHeaderRect(infoPtr, nLastCol, &rcItem);
3622 if (rcItem.left + Origin.x < rcClip.right) break;
3625 /* figure out what we need to draw */
3626 iterator_visibleitems(&i, infoPtr, hdc);
3628 /* a last few bits before we start drawing */
3629 TRACE("Colums=(%d - %d)\n", nFirstCol, nLastCol);
3631 /* iterate through the invalidated rows */
3632 while(iterator_next(&i))
3634 /* iterate through the invalidated columns */
3635 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
3637 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3638 Position.x += Origin.x;
3639 Position.y += Origin.y;
3641 if (rgntype == COMPLEXREGION)
3643 LISTVIEW_GetHeaderRect(infoPtr, nCol, &rcItem);
3644 rcItem.top = 0;
3645 rcItem.bottom = infoPtr->nItemHeight;
3646 OffsetRect(&rcItem, Position.x, Position.y);
3647 if (!RectVisible(hdc, &rcItem)) continue;
3650 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, nCol, Position, cdmode);
3653 iterator_destroy(&i);
3656 /***
3657 * DESCRIPTION:
3658 * Draws listview items when in list display mode.
3660 * PARAMETER(S):
3661 * [I] infoPtr : valid pointer to the listview structure
3662 * [I] hdc : device context handle
3663 * [I] cdmode : custom draw mode
3665 * RETURN:
3666 * None
3668 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3670 POINT Origin, Position;
3671 ITERATOR i;
3673 /* Get scroll info once before loop */
3674 LISTVIEW_GetOrigin(infoPtr, &Origin);
3676 /* figure out what we need to draw */
3677 iterator_visibleitems(&i, infoPtr, hdc);
3679 while(iterator_prev(&i))
3681 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3682 Position.x += Origin.x;
3683 Position.y += Origin.y;
3685 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3687 iterator_destroy(&i);
3691 /***
3692 * DESCRIPTION:
3693 * Draws listview items.
3695 * PARAMETER(S):
3696 * [I] infoPtr : valid pointer to the listview structure
3697 * [I] hdc : device context handle
3699 * RETURN:
3700 * NoneX
3702 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3704 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3705 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3706 NMLVCUSTOMDRAW nmlvcd;
3707 HFONT hOldFont;
3708 DWORD cdmode;
3709 INT oldBkMode;
3710 RECT rcClient;
3712 LISTVIEW_DUMP(infoPtr);
3714 infoPtr->bIsDrawing = TRUE;
3716 /* save dc values we're gonna trash while drawing */
3717 hOldFont = SelectObject(hdc, infoPtr->hFont);
3718 oldBkMode = GetBkMode(hdc);
3719 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3720 oldTextColor = GetTextColor(hdc);
3722 oldClrTextBk = infoPtr->clrTextBk;
3723 oldClrText = infoPtr->clrText;
3725 GetClientRect(infoPtr->hwndSelf, &rcClient);
3726 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3727 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3728 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3730 /* Use these colors to draw the items */
3731 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3732 infoPtr->clrText = nmlvcd.clrText;
3734 /* nothing to draw */
3735 if(infoPtr->nItemCount == 0) goto enddraw;
3737 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3738 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3739 else
3741 if (uView == LVS_REPORT)
3742 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3743 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3744 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3746 /* if we have a focus rect, draw it */
3747 if (infoPtr->bFocus)
3748 DrawFocusRect(hdc, &infoPtr->rcFocus);
3751 enddraw:
3752 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3753 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3755 infoPtr->clrTextBk = oldClrTextBk;
3756 infoPtr->clrText = oldClrText;
3758 SelectObject(hdc, hOldFont);
3759 SetBkMode(hdc, oldBkMode);
3760 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3761 SetTextColor(hdc, oldTextColor);
3762 infoPtr->bIsDrawing = FALSE;
3766 /***
3767 * DESCRIPTION:
3768 * Calculates the approximate width and height of a given number of items.
3770 * PARAMETER(S):
3771 * [I] infoPtr : valid pointer to the listview structure
3772 * [I] nItemCount : number of items
3773 * [I] wWidth : width
3774 * [I] wHeight : height
3776 * RETURN:
3777 * Returns a DWORD. The width in the low word and the height in high word.
3779 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3780 WORD wWidth, WORD wHeight)
3782 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3783 INT nItemCountPerColumn = 1;
3784 INT nColumnCount = 0;
3785 DWORD dwViewRect = 0;
3787 if (nItemCount == -1)
3788 nItemCount = infoPtr->nItemCount;
3790 if (uView == LVS_LIST)
3792 if (wHeight == 0xFFFF)
3794 /* use current height */
3795 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3798 if (wHeight < infoPtr->nItemHeight)
3799 wHeight = infoPtr->nItemHeight;
3801 if (nItemCount > 0)
3803 if (infoPtr->nItemHeight > 0)
3805 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3806 if (nItemCountPerColumn == 0)
3807 nItemCountPerColumn = 1;
3809 if (nItemCount % nItemCountPerColumn != 0)
3810 nColumnCount = nItemCount / nItemCountPerColumn;
3811 else
3812 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3816 /* Microsoft padding magic */
3817 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3818 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3820 dwViewRect = MAKELONG(wWidth, wHeight);
3822 else if (uView == LVS_REPORT)
3823 FIXME("uView == LVS_REPORT: not implemented\n");
3824 else if (uView == LVS_SMALLICON)
3825 FIXME("uView == LVS_SMALLICON: not implemented\n");
3826 else if (uView == LVS_ICON)
3827 FIXME("uView == LVS_ICON: not implemented\n");
3829 return dwViewRect;
3832 /* << LISTVIEW_CreateDragImage >> */
3835 /***
3836 * DESCRIPTION:
3837 * Removes all listview items and subitems.
3839 * PARAMETER(S):
3840 * [I] infoPtr : valid pointer to the listview structure
3842 * RETURN:
3843 * SUCCESS : TRUE
3844 * FAILURE : FALSE
3846 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3848 NMLISTVIEW nmlv;
3849 HDPA hdpaSubItems = NULL;
3850 BOOL bSuppress;
3851 ITEMHDR *hdrItem;
3852 INT i, j;
3854 TRACE("()\n");
3856 /* we do it directly, to avoid notifications */
3857 ranges_clear(infoPtr->selectionRanges);
3858 infoPtr->nSelectionMark = -1;
3859 infoPtr->nFocusedItem = -1;
3860 SetRectEmpty(&infoPtr->rcFocus);
3861 /* But we are supposed to leave nHotItem as is! */
3864 /* send LVN_DELETEALLITEMS notification */
3865 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3866 nmlv.iItem = -1;
3867 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3869 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
3871 /* send LVN_DELETEITEM notification, if not supressed */
3872 if (!bSuppress)
3874 nmlv.iItem = i;
3875 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3877 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3879 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3880 for (j = 0; j < hdpaSubItems->nItemCount; j++)
3882 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
3883 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
3884 COMCTL32_Free(hdrItem);
3886 DPA_Destroy(hdpaSubItems);
3887 DPA_DeletePtr(infoPtr->hdpaItems, i);
3889 DPA_DeletePtr(infoPtr->hdpaPosX, i);
3890 DPA_DeletePtr(infoPtr->hdpaPosY, i);
3891 infoPtr->nItemCount --;
3894 LISTVIEW_UpdateScroll(infoPtr);
3896 LISTVIEW_InvalidateList(infoPtr);
3898 return TRUE;
3901 /***
3902 * DESCRIPTION:
3903 * Scrolls, and updates the columns, when a column is changing width.
3905 * PARAMETER(S):
3906 * [I] infoPtr : valid pointer to the listview structure
3907 * [I] nColumn : column to scroll
3908 * [I] dx : amount of scroll, in pixels
3910 * RETURN:
3911 * None.
3913 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
3915 COLUMN_INFO *lpColumnInfo;
3916 RECT rcOld, rcCol;
3917 INT nCol;
3919 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, infoPtr->hdpaColumns->nItemCount - 1));
3920 rcCol = lpColumnInfo->rcHeader;
3921 if (nColumn >= infoPtr->hdpaColumns->nItemCount)
3922 rcCol.left = rcCol.right;
3924 /* ajust the other columns */
3925 for (nCol = nColumn; nCol < infoPtr->hdpaColumns->nItemCount; nCol++)
3927 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
3928 lpColumnInfo->rcHeader.left += dx;
3929 lpColumnInfo->rcHeader.right += dx;
3932 /* do not update screen if not in report mode */
3933 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
3935 /* if we have a focus, must first erase the focus rect */
3936 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3938 /* Need to reset the item width when inserting a new column */
3939 infoPtr->nItemWidth += dx;
3941 LISTVIEW_UpdateScroll(infoPtr);
3943 /* scroll to cover the deleted column, and invalidate for redraw */
3944 rcOld = infoPtr->rcList;
3945 rcOld.left = rcCol.left;
3946 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3948 /* we can restore focus now */
3949 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3952 /***
3953 * DESCRIPTION:
3954 * Removes a column from the listview control.
3956 * PARAMETER(S):
3957 * [I] infoPtr : valid pointer to the listview structure
3958 * [I] nColumn : column index
3960 * RETURN:
3961 * SUCCESS : TRUE
3962 * FAILURE : FALSE
3964 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3966 RECT rcCol;
3968 TRACE("nColumn=%d\n", nColumn);
3970 if (nColumn <= 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3972 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
3974 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3975 return FALSE;
3977 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
3978 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
3980 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3982 SUBITEM_INFO *lpSubItem, *lpDelItem;
3983 HDPA hdpaSubItems;
3984 INT nItem, nSubItem, i;
3986 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3988 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3989 nSubItem = 0;
3990 lpDelItem = 0;
3991 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3993 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3994 if (lpSubItem->iSubItem == nColumn)
3996 nSubItem = i;
3997 lpDelItem = lpSubItem;
3999 else if (lpSubItem->iSubItem > nColumn)
4001 lpSubItem->iSubItem--;
4005 /* if we found our subitem, zapp it */
4006 if (nSubItem > 0)
4008 /* free string */
4009 if (is_textW(lpDelItem->hdr.pszText))
4010 COMCTL32_Free(lpDelItem->hdr.pszText);
4012 /* free item */
4013 COMCTL32_Free(lpDelItem);
4015 /* free dpa memory */
4016 DPA_DeletePtr(hdpaSubItems, nSubItem);
4021 /* update the other column info */
4022 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4024 return TRUE;
4027 /***
4028 * DESCRIPTION:
4029 * Invalidates the listview after an item's insertion or deletion.
4031 * PARAMETER(S):
4032 * [I] infoPtr : valid pointer to the listview structure
4033 * [I] nItem : item index
4034 * [I] dir : -1 if deleting, 1 if inserting
4036 * RETURN:
4037 * None
4039 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4041 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4042 INT nPerCol, nItemCol, nItemRow;
4043 RECT rcScroll;
4044 POINT Origin;
4046 /* if we don't refresh, what's the point of scrolling? */
4047 if (!is_redrawing(infoPtr)) return;
4049 assert (abs(dir) == 1);
4051 /* arrange icons if autoarrange is on */
4052 if (is_autoarrange(infoPtr))
4054 BOOL arrange = TRUE;
4055 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4056 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4057 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4060 /* scrollbars need updating */
4061 LISTVIEW_UpdateScroll(infoPtr);
4063 /* figure out the item's position */
4064 if (uView == LVS_REPORT)
4065 nPerCol = infoPtr->nItemCount + 1;
4066 else if (uView == LVS_LIST)
4067 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4068 else /* LVS_ICON, or LVS_SMALLICON */
4069 return;
4071 nItemCol = nItem / nPerCol;
4072 nItemRow = nItem % nPerCol;
4073 LISTVIEW_GetOrigin(infoPtr, &Origin);
4075 /* move the items below up a slot */
4076 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4077 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4078 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4079 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4080 OffsetRect(&rcScroll, Origin.x, Origin.y);
4081 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4082 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4083 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4085 /* report has only that column, so we're done */
4086 if (uView == LVS_REPORT) return;
4088 /* now for LISTs, we have to deal with the columns to the right */
4089 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4090 rcScroll.top = 0;
4091 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4092 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4093 OffsetRect(&rcScroll, Origin.x, Origin.y);
4094 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4095 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4096 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4099 /***
4100 * DESCRIPTION:
4101 * Removes an item from the listview control.
4103 * PARAMETER(S):
4104 * [I] infoPtr : valid pointer to the listview structure
4105 * [I] nItem : item index
4107 * RETURN:
4108 * SUCCESS : TRUE
4109 * FAILURE : FALSE
4111 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4113 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4114 NMLISTVIEW nmlv;
4115 LVITEMW item;
4117 TRACE("(nItem=%d)\n", nItem);
4119 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4121 /* remove selection, and focus */
4122 item.state = 0;
4123 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4124 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4126 /* send LVN_DELETEITEM notification. */
4127 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
4128 nmlv.iItem = nItem;
4129 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
4131 /* we need to do this here, because we'll be deleting stuff */
4132 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4133 LISTVIEW_InvalidateItem(infoPtr, nItem);
4135 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4137 HDPA hdpaSubItems;
4138 ITEMHDR *hdrItem;
4139 INT i;
4141 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4142 for (i = 0; i < hdpaSubItems->nItemCount; i++)
4144 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4145 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4146 COMCTL32_Free(hdrItem);
4148 DPA_Destroy(hdpaSubItems);
4151 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4153 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4154 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4157 infoPtr->nItemCount--;
4158 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4160 /* now is the invalidation fun */
4161 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4162 return TRUE;
4166 /***
4167 * DESCRIPTION:
4168 * Callback implementation for editlabel control
4170 * PARAMETER(S):
4171 * [I] infoPtr : valid pointer to the listview structure
4172 * [I] pszText : modified text
4173 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4175 * RETURN:
4176 * SUCCESS : TRUE
4177 * FAILURE : FALSE
4179 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4181 NMLVDISPINFOW dispInfo;
4183 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4185 ZeroMemory(&dispInfo, sizeof(dispInfo));
4186 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4187 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4188 dispInfo.item.iSubItem = 0;
4189 dispInfo.item.stateMask = ~0;
4190 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4191 /* add the text from the edit in */
4192 dispInfo.item.mask |= LVIF_TEXT;
4193 dispInfo.item.pszText = pszText;
4194 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4196 /* Do we need to update the Item Text */
4197 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4198 if (!pszText) return TRUE;
4200 ZeroMemory(&dispInfo, sizeof(dispInfo));
4201 dispInfo.item.mask = LVIF_TEXT;
4202 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4203 dispInfo.item.iSubItem = 0;
4204 dispInfo.item.pszText = pszText;
4205 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4206 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4209 /***
4210 * DESCRIPTION:
4211 * Begin in place editing of specified list view item
4213 * PARAMETER(S):
4214 * [I] infoPtr : valid pointer to the listview structure
4215 * [I] nItem : item index
4216 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4218 * RETURN:
4219 * SUCCESS : TRUE
4220 * FAILURE : FALSE
4222 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4224 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4225 NMLVDISPINFOW dispInfo;
4226 RECT rect;
4228 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4230 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4231 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4233 infoPtr->nEditLabelItem = nItem;
4235 /* Is the EditBox still there, if so remove it */
4236 if(infoPtr->hwndEdit != 0)
4238 SetFocus(infoPtr->hwndSelf);
4239 infoPtr->hwndEdit = 0;
4242 LISTVIEW_SetSelection(infoPtr, nItem);
4243 LISTVIEW_SetItemFocus(infoPtr, nItem);
4245 rect.left = LVIR_LABEL;
4246 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4248 ZeroMemory(&dispInfo, sizeof(dispInfo));
4249 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4250 dispInfo.item.iItem = nItem;
4251 dispInfo.item.iSubItem = 0;
4252 dispInfo.item.stateMask = ~0;
4253 dispInfo.item.pszText = szDispText;
4254 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4255 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4257 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4258 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4259 if (!infoPtr->hwndEdit) return 0;
4261 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4263 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4264 infoPtr->hwndEdit = 0;
4265 return 0;
4268 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4269 SetFocus(infoPtr->hwndEdit);
4270 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4271 return infoPtr->hwndEdit;
4275 /***
4276 * DESCRIPTION:
4277 * Ensures the specified item is visible, scrolling into view if necessary.
4279 * PARAMETER(S):
4280 * [I] infoPtr : valid pointer to the listview structure
4281 * [I] nItem : item index
4282 * [I] bPartial : partially or entirely visible
4284 * RETURN:
4285 * SUCCESS : TRUE
4286 * FAILURE : FALSE
4288 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4290 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4291 INT nScrollPosHeight = 0;
4292 INT nScrollPosWidth = 0;
4293 INT nHorzAdjust = 0;
4294 INT nVertAdjust = 0;
4295 INT nHorzDiff = 0;
4296 INT nVertDiff = 0;
4297 RECT rcItem, rcTemp;
4299 rcItem.left = LVIR_BOUNDS;
4300 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4302 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4304 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4306 /* scroll left/right, but in LVS_REPORT mode */
4307 if (uView == LVS_LIST)
4308 nScrollPosWidth = infoPtr->nItemWidth;
4309 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4310 nScrollPosWidth = 1;
4312 if (rcItem.left < infoPtr->rcList.left)
4314 nHorzAdjust = -1;
4315 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4317 else
4319 nHorzAdjust = 1;
4320 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4324 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4326 /* scroll up/down, but not in LVS_LIST mode */
4327 if (uView == LVS_REPORT)
4328 nScrollPosHeight = infoPtr->nItemHeight;
4329 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4330 nScrollPosHeight = 1;
4332 if (rcItem.top < infoPtr->rcList.top)
4334 nVertAdjust = -1;
4335 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4337 else
4339 nVertAdjust = 1;
4340 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4344 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4346 if (nScrollPosWidth)
4348 INT diff = nHorzDiff / nScrollPosWidth;
4349 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4350 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4353 if (nScrollPosHeight)
4355 INT diff = nVertDiff / nScrollPosHeight;
4356 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4357 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4360 return TRUE;
4363 /***
4364 * DESCRIPTION:
4365 * Searches for an item with specific characteristics.
4367 * PARAMETER(S):
4368 * [I] hwnd : window handle
4369 * [I] nStart : base item index
4370 * [I] lpFindInfo : item information to look for
4372 * RETURN:
4373 * SUCCESS : index of item
4374 * FAILURE : -1
4376 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4377 LPLVFINDINFOW lpFindInfo)
4379 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4380 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4381 BOOL bWrap = FALSE, bNearest = FALSE;
4382 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4383 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4384 POINT Position, Destination;
4385 LVITEMW lvItem;
4387 if (!lpFindInfo || nItem < 0) return -1;
4389 lvItem.mask = 0;
4390 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4392 lvItem.mask |= LVIF_TEXT;
4393 lvItem.pszText = szDispText;
4394 lvItem.cchTextMax = DISP_TEXT_SIZE;
4397 if (lpFindInfo->flags & LVFI_WRAP)
4398 bWrap = TRUE;
4400 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4401 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4403 POINT Origin;
4404 RECT rcArea;
4406 LISTVIEW_GetOrigin(infoPtr, &Origin);
4407 Destination.x = lpFindInfo->pt.x - Origin.x;
4408 Destination.y = lpFindInfo->pt.y - Origin.y;
4409 switch(lpFindInfo->vkDirection)
4411 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4412 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4413 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4414 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4415 case VK_HOME: Destination.x = Destination.y = 0; break;
4416 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4417 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4418 case VK_END:
4419 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4420 Destination.x = rcArea.right;
4421 Destination.y = rcArea.bottom;
4422 break;
4423 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4425 bNearest = TRUE;
4428 /* if LVFI_PARAM is specified, all other flags are ignored */
4429 if (lpFindInfo->flags & LVFI_PARAM)
4431 lvItem.mask |= LVIF_PARAM;
4432 bNearest = FALSE;
4433 lvItem.mask &= ~LVIF_TEXT;
4436 again:
4437 for (; nItem < nLast; nItem++)
4439 lvItem.iItem = nItem;
4440 lvItem.iSubItem = 0;
4441 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4443 if (lvItem.mask & LVIF_PARAM)
4445 if (lpFindInfo->lParam == lvItem.lParam)
4446 return nItem;
4447 else
4448 continue;
4451 if (lvItem.mask & LVIF_TEXT)
4453 if (lpFindInfo->flags & LVFI_PARTIAL)
4455 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4457 else
4459 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4463 if (!bNearest) return nItem;
4465 /* This is very inefficient. To do a good job here,
4466 * we need a sorted array of (x,y) item positions */
4467 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4469 /* compute the distance^2 to the destination */
4470 xdist = Destination.x - Position.x;
4471 ydist = Destination.y - Position.y;
4472 dist = xdist * xdist + ydist * ydist;
4474 /* remember the distance, and item if it's closer */
4475 if (dist < mindist)
4477 mindist = dist;
4478 nNearestItem = nItem;
4482 if (bWrap)
4484 nItem = 0;
4485 nLast = min(nStart + 1, infoPtr->nItemCount);
4486 bWrap = FALSE;
4487 goto again;
4490 return nNearestItem;
4493 /***
4494 * DESCRIPTION:
4495 * Searches for an item with specific characteristics.
4497 * PARAMETER(S):
4498 * [I] hwnd : window handle
4499 * [I] nStart : base item index
4500 * [I] lpFindInfo : item information to look for
4502 * RETURN:
4503 * SUCCESS : index of item
4504 * FAILURE : -1
4506 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4507 LPLVFINDINFOA lpFindInfo)
4509 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4510 LVFINDINFOW fiw;
4511 INT res;
4513 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4514 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4515 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4516 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4517 return res;
4520 /***
4521 * DESCRIPTION:
4522 * Retrieves the background image of the listview control.
4524 * PARAMETER(S):
4525 * [I] infoPtr : valid pointer to the listview structure
4526 * [O] lpBkImage : background image attributes
4528 * RETURN:
4529 * SUCCESS : TRUE
4530 * FAILURE : FALSE
4532 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4533 /* { */
4534 /* FIXME (listview, "empty stub!\n"); */
4535 /* return FALSE; */
4536 /* } */
4538 /***
4539 * DESCRIPTION:
4540 * Retrieves column attributes.
4542 * PARAMETER(S):
4543 * [I] infoPtr : valid pointer to the listview structure
4544 * [I] nColumn : column index
4545 * [IO] lpColumn : column information
4546 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4547 * otherwise it is in fact a LPLVCOLUMNA
4549 * RETURN:
4550 * SUCCESS : TRUE
4551 * FAILURE : FALSE
4553 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4555 COLUMN_INFO *lpColumnInfo;
4556 HDITEMW hdi;
4558 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4559 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4561 /* initialize memory */
4562 ZeroMemory(&hdi, sizeof(hdi));
4564 if (lpColumn->mask & LVCF_TEXT)
4566 hdi.mask |= HDI_TEXT;
4567 hdi.pszText = lpColumn->pszText;
4568 hdi.cchTextMax = lpColumn->cchTextMax;
4571 if (lpColumn->mask & LVCF_IMAGE)
4572 hdi.mask |= HDI_IMAGE;
4574 if (lpColumn->mask & LVCF_ORDER)
4575 hdi.mask |= HDI_ORDER;
4577 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4579 if (lpColumn->mask & LVCF_FMT)
4580 lpColumn->fmt = lpColumnInfo->fmt;
4582 if (lpColumn->mask & LVCF_WIDTH)
4583 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4585 if (lpColumn->mask & LVCF_IMAGE)
4586 lpColumn->iImage = hdi.iImage;
4588 if (lpColumn->mask & LVCF_ORDER)
4589 lpColumn->iOrder = hdi.iOrder;
4591 return TRUE;
4595 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4597 INT i;
4599 if (!lpiArray)
4600 return FALSE;
4602 /* FIXME: little hack */
4603 for (i = 0; i < iCount; i++)
4604 lpiArray[i] = i;
4606 return TRUE;
4609 /***
4610 * DESCRIPTION:
4611 * Retrieves the column width.
4613 * PARAMETER(S):
4614 * [I] infoPtr : valid pointer to the listview structure
4615 * [I] int : column index
4617 * RETURN:
4618 * SUCCESS : column width
4619 * FAILURE : zero
4621 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4623 INT nColumnWidth = 0;
4624 RECT rcHeader;
4626 TRACE("nColumn=%d\n", nColumn);
4628 /* we have a 'column' in LIST and REPORT mode only */
4629 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4631 case LVS_LIST:
4632 nColumnWidth = infoPtr->nItemWidth;
4633 break;
4634 case LVS_REPORT:
4635 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return 0;
4636 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4637 nColumnWidth = rcHeader.right - rcHeader.left;
4638 break;
4641 TRACE("nColumnWidth=%d\n", nColumnWidth);
4642 return nColumnWidth;
4645 /***
4646 * DESCRIPTION:
4647 * In list or report display mode, retrieves the number of items that can fit
4648 * vertically in the visible area. In icon or small icon display mode,
4649 * retrieves the total number of visible items.
4651 * PARAMETER(S):
4652 * [I] infoPtr : valid pointer to the listview structure
4654 * RETURN:
4655 * Number of fully visible items.
4657 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4659 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4661 case LVS_ICON:
4662 case LVS_SMALLICON:
4663 return infoPtr->nItemCount;
4664 case LVS_REPORT:
4665 return LISTVIEW_GetCountPerColumn(infoPtr);
4666 case LVS_LIST:
4667 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4669 assert(FALSE);
4672 /***
4673 * DESCRIPTION:
4674 * Retrieves an image list handle.
4676 * PARAMETER(S):
4677 * [I] infoPtr : valid pointer to the listview structure
4678 * [I] nImageList : image list identifier
4680 * RETURN:
4681 * SUCCESS : image list handle
4682 * FAILURE : NULL
4684 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4686 switch (nImageList)
4688 case LVSIL_NORMAL: return infoPtr->himlNormal;
4689 case LVSIL_SMALL: return infoPtr->himlSmall;
4690 case LVSIL_STATE: return infoPtr->himlState;
4692 return NULL;
4695 /* LISTVIEW_GetISearchString */
4697 /***
4698 * DESCRIPTION:
4699 * Retrieves item attributes.
4701 * PARAMETER(S):
4702 * [I] hwnd : window handle
4703 * [IO] lpLVItem : item info
4704 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4705 * if FALSE, the lpLVItem is a LPLVITEMA.
4707 * NOTE:
4708 * This is the internal 'GetItem' interface -- it tries to
4709 * be smart, and avoids text copies, if possible, by modifing
4710 * lpLVItem->pszText to point to the text string. Please note
4711 * that this is not always possible (e.g. OWNERDATA), so on
4712 * entry you *must* supply valid values for pszText, and cchTextMax.
4713 * The only difference to the documented interface is that upon
4714 * return, you should use *only* the lpLVItem->pszText, rather than
4715 * the buffer pointer you provided on input. Most code already does
4716 * that, so it's not a problem.
4717 * For the two cases when the text must be copied (that is,
4718 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4720 * RETURN:
4721 * SUCCESS : TRUE
4722 * FAILURE : FALSE
4724 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4726 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
4727 NMLVDISPINFOW dispInfo;
4728 ITEM_INFO *lpItem;
4729 ITEMHDR* pItemHdr;
4730 HDPA hdpaSubItems;
4732 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4734 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4735 return FALSE;
4737 if (lpLVItem->mask == 0) return TRUE;
4739 /* a quick optimization if all we're asked is the focus state
4740 * these queries are worth optimising since they are common,
4741 * and can be answered in constant time, without the heavy accesses */
4742 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4743 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4745 lpLVItem->state = 0;
4746 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4747 lpLVItem->state |= LVIS_FOCUSED;
4748 return TRUE;
4751 ZeroMemory(&dispInfo, sizeof(dispInfo));
4753 /* if the app stores all the data, handle it separately */
4754 if (infoPtr->dwStyle & LVS_OWNERDATA)
4756 dispInfo.item.state = 0;
4758 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
4759 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
4761 /* NOTE: copy only fields which we _know_ are initialized, some apps
4762 * depend on the uninitialized fields being 0 */
4763 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
4764 dispInfo.item.iItem = lpLVItem->iItem;
4765 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4766 if (lpLVItem->mask & LVIF_TEXT)
4768 dispInfo.item.pszText = lpLVItem->pszText;
4769 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4771 if (lpLVItem->mask & LVIF_STATE)
4772 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4773 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4774 dispInfo.item.stateMask = lpLVItem->stateMask;
4775 *lpLVItem = dispInfo.item;
4776 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4779 /* make sure lParam is zeroed out */
4780 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
4782 /* we store only a little state, so if we're not asked, we're done */
4783 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4785 /* if focus is handled by us, report it */
4786 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4788 lpLVItem->state &= ~LVIS_FOCUSED;
4789 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4790 lpLVItem->state |= LVIS_FOCUSED;
4793 /* and do the same for selection, if we handle it */
4794 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4796 lpLVItem->state &= ~LVIS_SELECTED;
4797 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4798 lpLVItem->state |= LVIS_SELECTED;
4801 return TRUE;
4804 /* find the item and subitem structures before we proceed */
4805 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4806 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4807 assert (lpItem);
4809 if (lpLVItem->iSubItem)
4811 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4812 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
4814 else
4815 pItemHdr = &lpItem->hdr;
4817 /* Do we need to query the state from the app? */
4818 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4820 dispInfo.item.mask |= LVIF_STATE;
4821 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4824 /* Do we need to enquire about the image? */
4825 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4826 dispInfo.item.mask |= LVIF_IMAGE;
4828 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4829 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4831 dispInfo.item.mask |= LVIF_TEXT;
4832 dispInfo.item.pszText = lpLVItem->pszText;
4833 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4834 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4835 *dispInfo.item.pszText = '\0';
4838 /* If we don't have all the requested info, query the application */
4839 if (dispInfo.item.mask != 0)
4841 dispInfo.item.iItem = lpLVItem->iItem;
4842 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4843 dispInfo.item.lParam = lpItem->lParam;
4844 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4845 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4848 /* we should not store values for subitems */
4849 if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
4851 /* Now, handle the iImage field */
4852 if (dispInfo.item.mask & LVIF_IMAGE)
4854 lpLVItem->iImage = dispInfo.item.iImage;
4855 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
4856 pItemHdr->iImage = dispInfo.item.iImage;
4858 else if (lpLVItem->mask & LVIF_IMAGE)
4859 lpLVItem->iImage = pItemHdr->iImage;
4861 /* The pszText field */
4862 if (dispInfo.item.mask & LVIF_TEXT)
4864 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4865 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4867 lpLVItem->pszText = dispInfo.item.pszText;
4869 else if (lpLVItem->mask & LVIF_TEXT)
4871 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4872 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4875 /* if this is a subitem, we're done */
4876 if (lpLVItem->iSubItem) return TRUE;
4878 /* Next is the lParam field */
4879 if (dispInfo.item.mask & LVIF_PARAM)
4881 lpLVItem->lParam = dispInfo.item.lParam;
4882 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4883 lpItem->lParam = dispInfo.item.lParam;
4885 else if (lpLVItem->mask & LVIF_PARAM)
4886 lpLVItem->lParam = lpItem->lParam;
4888 /* ... the state field (this one is different due to uCallbackmask) */
4889 if (lpLVItem->mask & LVIF_STATE)
4891 lpLVItem->state = lpItem->state;
4892 if (dispInfo.item.mask & LVIF_STATE)
4894 lpLVItem->state &= ~dispInfo.item.stateMask;
4895 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4897 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4899 lpLVItem->state &= ~LVIS_FOCUSED;
4900 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4901 lpLVItem->state |= LVIS_FOCUSED;
4903 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4905 lpLVItem->state &= ~LVIS_SELECTED;
4906 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4907 lpLVItem->state |= LVIS_SELECTED;
4911 /* and last, but not least, the indent field */
4912 if (lpLVItem->mask & LVIF_INDENT)
4913 lpLVItem->iIndent = lpItem->iIndent;
4915 return TRUE;
4918 /***
4919 * DESCRIPTION:
4920 * Retrieves item attributes.
4922 * PARAMETER(S):
4923 * [I] hwnd : window handle
4924 * [IO] lpLVItem : item info
4925 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4926 * if FALSE, the lpLVItem is a LPLVITEMA.
4928 * NOTE:
4929 * This is the external 'GetItem' interface -- it properly copies
4930 * the text in the provided buffer.
4932 * RETURN:
4933 * SUCCESS : TRUE
4934 * FAILURE : FALSE
4936 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4938 LPWSTR pszText;
4939 BOOL bResult;
4941 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4942 return FALSE;
4944 pszText = lpLVItem->pszText;
4945 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4946 if (bResult && lpLVItem->pszText != pszText)
4947 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4948 lpLVItem->pszText = pszText;
4950 return bResult;
4954 /***
4955 * DESCRIPTION:
4956 * Retrieves the position (upper-left) of the listview control item.
4957 * Note that for LVS_ICON style, the upper-left is that of the icon
4958 * and not the bounding box.
4960 * PARAMETER(S):
4961 * [I] infoPtr : valid pointer to the listview structure
4962 * [I] nItem : item index
4963 * [O] lpptPosition : coordinate information
4965 * RETURN:
4966 * SUCCESS : TRUE
4967 * FAILURE : FALSE
4969 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4971 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4972 POINT Origin;
4974 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4976 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4978 LISTVIEW_GetOrigin(infoPtr, &Origin);
4979 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
4981 if (uView == LVS_ICON)
4983 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4984 lpptPosition->y += ICON_TOP_PADDING;
4986 lpptPosition->x += Origin.x;
4987 lpptPosition->y += Origin.y;
4989 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4990 return TRUE;
4994 /***
4995 * DESCRIPTION:
4996 * Retrieves the bounding rectangle for a listview control item.
4998 * PARAMETER(S):
4999 * [I] infoPtr : valid pointer to the listview structure
5000 * [I] nItem : item index
5001 * [IO] lprc : bounding rectangle coordinates
5002 * lprc->left specifies the portion of the item for which the bounding
5003 * rectangle will be retrieved.
5005 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5006 * including the icon and label.
5008 * * For LVS_ICON
5009 * * Experiment shows that native control returns:
5010 * * width = min (48, length of text line)
5011 * * .left = position.x - (width - iconsize.cx)/2
5012 * * .right = .left + width
5013 * * height = #lines of text * ntmHeight + icon height + 8
5014 * * .top = position.y - 2
5015 * * .bottom = .top + height
5016 * * separation between items .y = itemSpacing.cy - height
5017 * * .x = itemSpacing.cx - width
5018 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5020 * * For LVS_ICON
5021 * * Experiment shows that native control returns:
5022 * * width = iconSize.cx + 16
5023 * * .left = position.x - (width - iconsize.cx)/2
5024 * * .right = .left + width
5025 * * height = iconSize.cy + 4
5026 * * .top = position.y - 2
5027 * * .bottom = .top + height
5028 * * separation between items .y = itemSpacing.cy - height
5029 * * .x = itemSpacing.cx - width
5030 * LVIR_LABEL Returns the bounding rectangle of the item text.
5032 * * For LVS_ICON
5033 * * Experiment shows that native control returns:
5034 * * width = text length
5035 * * .left = position.x - width/2
5036 * * .right = .left + width
5037 * * height = ntmH * linecount + 2
5038 * * .top = position.y + iconSize.cy + 6
5039 * * .bottom = .top + height
5040 * * separation between items .y = itemSpacing.cy - height
5041 * * .x = itemSpacing.cx - width
5042 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5043 * rectangles, but excludes columns in report view.
5045 * RETURN:
5046 * SUCCESS : TRUE
5047 * FAILURE : FALSE
5049 * NOTES
5050 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5051 * upon whether the window has the focus currently and on whether the item
5052 * is the one with the focus. Ensure that the control's record of which
5053 * item has the focus agrees with the items' records.
5055 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5057 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5058 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5059 BOOL doLabel = TRUE, oversizedBox = FALSE;
5060 POINT Position, Origin;
5061 LVITEMW lvItem;
5062 RECT label_rect;
5064 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5066 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5068 LISTVIEW_GetOrigin(infoPtr, &Origin);
5069 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5071 /* Be smart and try to figure out the minimum we have to do */
5072 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5073 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5074 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5075 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5076 oversizedBox = TRUE;
5078 /* get what we need from the item before hand, so we make
5079 * only one request. This can speed up things, if data
5080 * is stored on the app side */
5081 lvItem.mask = 0;
5082 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5083 if (doLabel) lvItem.mask |= LVIF_TEXT;
5084 lvItem.iItem = nItem;
5085 lvItem.iSubItem = 0;
5086 lvItem.pszText = szDispText;
5087 lvItem.cchTextMax = DISP_TEXT_SIZE;
5088 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5089 /* we got the state already up, simulate it here, to avoid a reget */
5090 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5092 lvItem.mask |= LVIF_STATE;
5093 lvItem.stateMask = LVIS_FOCUSED;
5094 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5097 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5098 lprc->left = LVIR_BOUNDS;
5099 switch(lprc->left)
5101 case LVIR_ICON:
5102 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5103 break;
5105 case LVIR_LABEL:
5106 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5107 break;
5109 case LVIR_BOUNDS:
5110 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5111 break;
5113 case LVIR_SELECTBOUNDS:
5114 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5115 UnionRect(lprc, lprc, &label_rect);
5116 break;
5118 default:
5119 WARN("Unknown value: %d\n", lprc->left);
5120 return FALSE;
5123 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5125 TRACE(" rect=%s\n", debugrect(lprc));
5127 return TRUE;
5130 /***
5131 * DESCRIPTION:
5132 * Retrieves the spacing between listview control items.
5134 * PARAMETER(S):
5135 * [I] infoPtr : valid pointer to the listview structure
5136 * [IO] lprc : rectangle to receive the output
5137 * on input, lprc->top = nSubItem
5138 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5140 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5141 * not only those of the first column.
5142 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5144 * RETURN:
5145 * TRUE: success
5146 * FALSE: failure
5148 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5150 POINT Position, Origin;
5151 LVITEMW lvItem;
5153 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5155 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5157 LISTVIEW_GetOrigin(infoPtr, &Origin);
5158 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5160 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5161 lvItem.iItem = nItem;
5162 lvItem.iSubItem = lprc->top;
5164 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5165 switch(lprc->left)
5167 case LVIR_ICON:
5168 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5169 break;
5171 case LVIR_LABEL:
5172 case LVIR_BOUNDS:
5173 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5174 break;
5176 default:
5177 ERR("Unknown bounds=%d\n", lprc->left);
5178 return FALSE;
5181 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5182 return TRUE;
5186 /***
5187 * DESCRIPTION:
5188 * Retrieves the width of a label.
5190 * PARAMETER(S):
5191 * [I] infoPtr : valid pointer to the listview structure
5193 * RETURN:
5194 * SUCCESS : string width (in pixels)
5195 * FAILURE : zero
5197 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5199 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5200 LVITEMW lvItem;
5202 TRACE("(nItem=%d)\n", nItem);
5204 lvItem.mask = LVIF_TEXT;
5205 lvItem.iItem = nItem;
5206 lvItem.iSubItem = 0;
5207 lvItem.pszText = szDispText;
5208 lvItem.cchTextMax = DISP_TEXT_SIZE;
5209 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5211 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5214 /***
5215 * DESCRIPTION:
5216 * Retrieves the spacing between listview control items.
5218 * PARAMETER(S):
5219 * [I] infoPtr : valid pointer to the listview structure
5220 * [I] bSmall : flag for small or large icon
5222 * RETURN:
5223 * Horizontal + vertical spacing
5225 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5227 LONG lResult;
5229 if (!bSmall)
5231 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5233 else
5235 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5236 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5237 else
5238 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5240 return lResult;
5243 /***
5244 * DESCRIPTION:
5245 * Retrieves the state of a listview control item.
5247 * PARAMETER(S):
5248 * [I] infoPtr : valid pointer to the listview structure
5249 * [I] nItem : item index
5250 * [I] uMask : state mask
5252 * RETURN:
5253 * State specified by the mask.
5255 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5257 LVITEMW lvItem;
5259 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5261 lvItem.iItem = nItem;
5262 lvItem.iSubItem = 0;
5263 lvItem.mask = LVIF_STATE;
5264 lvItem.stateMask = uMask;
5265 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5267 return lvItem.state & uMask;
5270 /***
5271 * DESCRIPTION:
5272 * Retrieves the text of a listview control item or subitem.
5274 * PARAMETER(S):
5275 * [I] hwnd : window handle
5276 * [I] nItem : item index
5277 * [IO] lpLVItem : item information
5278 * [I] isW : TRUE if lpLVItem is Unicode
5280 * RETURN:
5281 * SUCCESS : string length
5282 * FAILURE : 0
5284 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5286 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5288 lpLVItem->mask = LVIF_TEXT;
5289 lpLVItem->iItem = nItem;
5290 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5292 return textlenT(lpLVItem->pszText, isW);
5295 /***
5296 * DESCRIPTION:
5297 * Searches for an item based on properties + relationships.
5299 * PARAMETER(S):
5300 * [I] infoPtr : valid pointer to the listview structure
5301 * [I] nItem : item index
5302 * [I] uFlags : relationship flag
5304 * RETURN:
5305 * SUCCESS : item index
5306 * FAILURE : -1
5308 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5310 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5311 UINT uMask = 0;
5312 LVFINDINFOW lvFindInfo;
5313 INT nCountPerColumn;
5314 INT i;
5316 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5317 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5319 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5321 if (uFlags & LVNI_CUT)
5322 uMask |= LVIS_CUT;
5324 if (uFlags & LVNI_DROPHILITED)
5325 uMask |= LVIS_DROPHILITED;
5327 if (uFlags & LVNI_FOCUSED)
5328 uMask |= LVIS_FOCUSED;
5330 if (uFlags & LVNI_SELECTED)
5331 uMask |= LVIS_SELECTED;
5333 /* if we're asked for the focused item, that's only one,
5334 * so it's worth optimizing */
5335 if (uFlags & LVNI_FOCUSED)
5337 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5338 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5341 if (uFlags & LVNI_ABOVE)
5343 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5345 while (nItem >= 0)
5347 nItem--;
5348 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5349 return nItem;
5352 else
5354 lvFindInfo.flags = LVFI_NEARESTXY;
5355 lvFindInfo.vkDirection = VK_UP;
5356 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5357 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5359 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5360 return nItem;
5364 else if (uFlags & LVNI_BELOW)
5366 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5368 while (nItem < infoPtr->nItemCount)
5370 nItem++;
5371 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5372 return nItem;
5375 else
5377 lvFindInfo.flags = LVFI_NEARESTXY;
5378 lvFindInfo.vkDirection = VK_DOWN;
5379 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5380 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5382 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5383 return nItem;
5387 else if (uFlags & LVNI_TOLEFT)
5389 if (uView == LVS_LIST)
5391 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5392 while (nItem - nCountPerColumn >= 0)
5394 nItem -= nCountPerColumn;
5395 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5396 return nItem;
5399 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5401 lvFindInfo.flags = LVFI_NEARESTXY;
5402 lvFindInfo.vkDirection = VK_LEFT;
5403 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5404 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5406 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5407 return nItem;
5411 else if (uFlags & LVNI_TORIGHT)
5413 if (uView == LVS_LIST)
5415 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5416 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5418 nItem += nCountPerColumn;
5419 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5420 return nItem;
5423 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5425 lvFindInfo.flags = LVFI_NEARESTXY;
5426 lvFindInfo.vkDirection = VK_RIGHT;
5427 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5428 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5430 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5431 return nItem;
5435 else
5437 nItem++;
5439 /* search by index */
5440 for (i = nItem; i < infoPtr->nItemCount; i++)
5442 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5443 return i;
5447 return -1;
5450 /* LISTVIEW_GetNumberOfWorkAreas */
5452 /***
5453 * DESCRIPTION:
5454 * Retrieves the origin coordinates when in icon or small icon display mode.
5456 * PARAMETER(S):
5457 * [I] infoPtr : valid pointer to the listview structure
5458 * [O] lpptOrigin : coordinate information
5460 * RETURN:
5461 * None.
5463 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5465 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5466 INT nHorzPos = 0, nVertPos = 0;
5467 SCROLLINFO scrollInfo;
5469 scrollInfo.cbSize = sizeof(SCROLLINFO);
5470 scrollInfo.fMask = SIF_POS;
5472 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5473 nHorzPos = scrollInfo.nPos;
5474 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5475 nVertPos = scrollInfo.nPos;
5477 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5479 lpptOrigin->x = infoPtr->rcList.left;
5480 lpptOrigin->y = infoPtr->rcList.top;
5481 if (uView == LVS_LIST)
5482 nHorzPos *= infoPtr->nItemWidth;
5483 else if (uView == LVS_REPORT)
5484 nVertPos *= infoPtr->nItemHeight;
5486 lpptOrigin->x -= nHorzPos;
5487 lpptOrigin->y -= nVertPos;
5489 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5492 /***
5493 * DESCRIPTION:
5494 * Retrieves the width of a string.
5496 * PARAMETER(S):
5497 * [I] hwnd : window handle
5498 * [I] lpszText : text string to process
5499 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5501 * RETURN:
5502 * SUCCESS : string width (in pixels)
5503 * FAILURE : zero
5505 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5507 SIZE stringSize;
5509 stringSize.cx = 0;
5510 if (is_textT(lpszText, isW))
5512 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5513 HDC hdc = GetDC(infoPtr->hwndSelf);
5514 HFONT hOldFont = SelectObject(hdc, hFont);
5516 if (isW)
5517 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5518 else
5519 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5520 SelectObject(hdc, hOldFont);
5521 ReleaseDC(infoPtr->hwndSelf, hdc);
5523 return stringSize.cx;
5526 /***
5527 * DESCRIPTION:
5528 * Determines which listview item is located at the specified position.
5530 * PARAMETER(S):
5531 * [I] infoPtr : valid pointer to the listview structure
5532 * [IO] lpht : hit test information
5533 * [I] subitem : fill out iSubItem.
5534 * [I] select : return the index only if the hit selects the item
5536 * NOTE:
5537 * (mm 20001022): We must not allow iSubItem to be touched, for
5538 * an app might pass only a structure with space up to iItem!
5539 * (MS Office 97 does that for instance in the file open dialog)
5541 * RETURN:
5542 * SUCCESS : item index
5543 * FAILURE : -1
5545 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5547 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5548 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5549 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5550 POINT Origin, Position, opt;
5551 LVITEMW lvItem;
5552 ITERATOR i;
5554 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5556 lpht->flags = 0;
5557 lpht->iItem = -1;
5558 if (subitem) lpht->iSubItem = 0;
5560 if (infoPtr->rcList.left > lpht->pt.x)
5561 lpht->flags |= LVHT_TOLEFT;
5562 else if (infoPtr->rcList.right < lpht->pt.x)
5563 lpht->flags |= LVHT_TORIGHT;
5565 if (infoPtr->rcList.top > lpht->pt.y)
5566 lpht->flags |= LVHT_ABOVE;
5567 else if (infoPtr->rcList.bottom < lpht->pt.y)
5568 lpht->flags |= LVHT_BELOW;
5570 TRACE("lpht->flags=0x%x\n", lpht->flags);
5571 if (lpht->flags) return -1;
5573 lpht->flags |= LVHT_NOWHERE;
5575 LISTVIEW_GetOrigin(infoPtr, &Origin);
5577 /* first deal with the large items */
5578 rcSearch.left = lpht->pt.x;
5579 rcSearch.top = lpht->pt.y;
5580 rcSearch.right = rcSearch.left + 1;
5581 rcSearch.bottom = rcSearch.top + 1;
5583 iterator_frameditems(&i, infoPtr, &rcSearch);
5584 iterator_next(&i); /* go to first item in the sequence */
5585 lpht->iItem = i.nItem;
5586 iterator_destroy(&i);
5588 TRACE("lpht->iItem=%d\n", lpht->iItem);
5589 if (lpht->iItem == -1) return -1;
5591 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5592 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5593 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5594 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5595 lvItem.iItem = lpht->iItem;
5596 lvItem.iSubItem = 0;
5597 lvItem.pszText = szDispText;
5598 lvItem.cchTextMax = DISP_TEXT_SIZE;
5599 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5600 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5602 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5603 LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position);
5604 opt.x = lpht->pt.x - Position.x - Origin.x;
5605 opt.y = lpht->pt.y - Position.y - Origin.y;
5607 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5608 rcBounds = rcBox;
5609 else
5610 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5611 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5612 if (!PtInRect(&rcBounds, opt)) return -1;
5614 if (PtInRect(&rcIcon, opt))
5615 lpht->flags |= LVHT_ONITEMICON;
5616 else if (PtInRect(&rcLabel, opt))
5617 lpht->flags |= LVHT_ONITEMLABEL;
5618 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5619 lpht->flags |= LVHT_ONITEMSTATEICON;
5620 if (lpht->flags & LVHT_ONITEM)
5621 lpht->flags &= ~LVHT_NOWHERE;
5623 TRACE("lpht->flags=0x%x\n", lpht->flags);
5624 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5626 INT j;
5628 rcBounds.right = rcBounds.left;
5629 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5631 rcBounds.left = rcBounds.right;
5632 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5633 if (PtInRect(&rcBounds, opt))
5635 lpht->iSubItem = j;
5636 break;
5641 if (!select || lpht->iItem == -1) return lpht->iItem;
5643 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5645 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5646 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5650 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5651 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5652 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5653 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5654 their own sort proc. when sending LVM_SORTITEMS.
5656 /* Platform SDK:
5657 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5659 LVS_SORTXXX must be specified,
5660 LVS_OWNERDRAW is not set,
5661 <item>.pszText is not LPSTR_TEXTCALLBACK.
5663 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5664 are sorted based on item text..."
5666 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5668 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5669 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5670 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5672 /* if we're sorting descending, negate the return value */
5673 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5676 /***
5677 * nESCRIPTION:
5678 * Inserts a new item in the listview control.
5680 * PARAMETER(S):
5681 * [I] infoPtr : valid pointer to the listview structure
5682 * [I] lpLVItem : item information
5683 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5685 * RETURN:
5686 * SUCCESS : new item index
5687 * FAILURE : -1
5689 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5691 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5692 INT nItem;
5693 HDPA hdpaSubItems;
5694 NMLISTVIEW nmlv;
5695 ITEM_INFO *lpItem;
5696 BOOL is_sorted, has_changed;
5697 LVITEMW item;
5699 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5701 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5703 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5704 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5706 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5708 if ( !(lpItem = (ITEM_INFO *)COMCTL32_Alloc(sizeof(ITEM_INFO))) )
5709 return -1;
5711 /* insert item in listview control data structure */
5712 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5713 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5715 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5716 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5718 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5719 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5720 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5721 if (nItem == -1) goto fail;
5722 infoPtr->nItemCount++;
5724 /* set the item attributes */
5725 item = *lpLVItem;
5726 item.iItem = nItem;
5727 item.state &= ~LVIS_STATEIMAGEMASK;
5728 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5730 /* if we're sorted, sort the list, and update the index */
5731 if (is_sorted)
5733 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5734 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5735 assert(nItem != -1);
5738 /* make room for the position, if we are in the right mode */
5739 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5741 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5742 goto undo;
5743 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5745 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5746 goto undo;
5750 /* Add the subitem list to the items array. Do this last in case we go to
5751 * fail during the above.
5753 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5755 /* send LVN_INSERTITEM notification */
5756 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5757 nmlv.iItem = nItem;
5758 nmlv.lParam = lpItem->lParam;
5759 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5761 /* align items (set position of each item) */
5762 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
5764 POINT pt;
5766 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
5767 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
5768 else
5769 LISTVIEW_NextIconPosTop(infoPtr, &pt);
5771 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
5774 /* now is the invalidation fun */
5775 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
5776 return nItem;
5778 undo:
5779 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5780 infoPtr->nItemCount--;
5781 fail:
5782 DPA_DeletePtr(hdpaSubItems, 0);
5783 DPA_Destroy (hdpaSubItems);
5784 COMCTL32_Free (lpItem);
5785 return -1;
5788 /***
5789 * DESCRIPTION:
5790 * Redraws a range of items.
5792 * PARAMETER(S):
5793 * [I] infoPtr : valid pointer to the listview structure
5794 * [I] nFirst : first item
5795 * [I] nLast : last item
5797 * RETURN:
5798 * SUCCESS : TRUE
5799 * FAILURE : FALSE
5801 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5803 INT i;
5805 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5806 max(nFirst, nLast) >= infoPtr->nItemCount)
5807 return FALSE;
5809 for (i = nFirst; i <= nLast; i++)
5810 LISTVIEW_InvalidateItem(infoPtr, i);
5812 return TRUE;
5815 /***
5816 * DESCRIPTION:
5817 * Scroll the content of a listview.
5819 * PARAMETER(S):
5820 * [I] infoPtr : valid pointer to the listview structure
5821 * [I] dx : horizontal scroll amount in pixels
5822 * [I] dy : vertical scroll amount in pixels
5824 * RETURN:
5825 * SUCCESS : TRUE
5826 * FAILURE : FALSE
5828 * COMMENTS:
5829 * If the control is in report mode (LVS_REPORT) the control can
5830 * be scrolled only in line increments. "dy" will be rounded to the
5831 * nearest number of pixels that are a whole line. Ex: if line height
5832 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5833 * is passed the the scroll will be 0. (per MSDN 7/2002)
5835 * For: (per experimentaion with native control and CSpy ListView)
5836 * LVS_ICON dy=1 = 1 pixel (vertical only)
5837 * dx ignored
5838 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5839 * dx ignored
5840 * LVS_LIST dx=1 = 1 column (horizontal only)
5841 * but will only scroll 1 column per message
5842 * no matter what the value.
5843 * dy must be 0 or FALSE returned.
5844 * LVS_REPORT dx=1 = 1 pixel
5845 * dy= see above
5848 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5850 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5851 case LVS_REPORT:
5852 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5853 dy /= infoPtr->nItemHeight;
5854 break;
5855 case LVS_LIST:
5856 if (dy != 0) return FALSE;
5857 break;
5858 default: /* icon */
5859 dx = 0;
5860 break;
5863 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5864 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5866 return TRUE;
5869 /***
5870 * DESCRIPTION:
5871 * Sets the background color.
5873 * PARAMETER(S):
5874 * [I] infoPtr : valid pointer to the listview structure
5875 * [I] clrBk : background color
5877 * RETURN:
5878 * SUCCESS : TRUE
5879 * FAILURE : FALSE
5881 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5883 TRACE("(clrBk=%lx)\n", clrBk);
5885 if(infoPtr->clrBk != clrBk) {
5886 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5887 infoPtr->clrBk = clrBk;
5888 if (clrBk == CLR_NONE)
5889 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5890 else
5891 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5892 LISTVIEW_InvalidateList(infoPtr);
5895 return TRUE;
5898 /* LISTVIEW_SetBkImage */
5900 /*** Helper for {Insert,Set}ColumnT *only* */
5901 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5903 if (lpColumn->mask & LVCF_FMT)
5905 /* format member is valid */
5906 lphdi->mask |= HDI_FORMAT;
5908 /* set text alignment (leftmost column must be left-aligned) */
5909 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5910 lphdi->fmt |= HDF_LEFT;
5911 else if (lpColumn->fmt & LVCFMT_RIGHT)
5912 lphdi->fmt |= HDF_RIGHT;
5913 else if (lpColumn->fmt & LVCFMT_CENTER)
5914 lphdi->fmt |= HDF_CENTER;
5916 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5917 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
5919 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5921 lphdi->fmt |= HDF_IMAGE;
5922 lphdi->iImage = I_IMAGECALLBACK;
5926 if (lpColumn->mask & LVCF_WIDTH)
5928 lphdi->mask |= HDI_WIDTH;
5929 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5931 /* make it fill the remainder of the controls width */
5932 RECT rcHeader;
5933 INT item_index;
5935 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5937 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
5938 lphdi->cxy += rcHeader.right - rcHeader.left;
5941 /* retrieve the layout of the header */
5942 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5943 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
5945 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
5947 else
5948 lphdi->cxy = lpColumn->cx;
5951 if (lpColumn->mask & LVCF_TEXT)
5953 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
5954 lphdi->fmt |= HDF_STRING;
5955 lphdi->pszText = lpColumn->pszText;
5956 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
5959 if (lpColumn->mask & LVCF_IMAGE)
5961 lphdi->mask |= HDI_IMAGE;
5962 lphdi->iImage = lpColumn->iImage;
5965 if (lpColumn->mask & LVCF_ORDER)
5967 lphdi->mask |= HDI_ORDER;
5968 lphdi->iOrder = lpColumn->iOrder;
5973 /***
5974 * DESCRIPTION:
5975 * Inserts a new column.
5977 * PARAMETER(S):
5978 * [I] infoPtr : valid pointer to the listview structure
5979 * [I] nColumn : column index
5980 * [I] lpColumn : column information
5981 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
5983 * RETURN:
5984 * SUCCESS : new column index
5985 * FAILURE : -1
5987 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5988 LPLVCOLUMNW lpColumn, BOOL isW)
5990 COLUMN_INFO *lpColumnInfo;
5991 INT nNewColumn;
5992 HDITEMW hdi;
5994 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5996 if (!lpColumn || nColumn < 0 || nColumn > infoPtr->hdpaColumns->nItemCount) return -1;
5998 ZeroMemory(&hdi, sizeof(HDITEMW));
5999 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6001 /* insert item in header control */
6002 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6003 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6004 (WPARAM)nColumn, (LPARAM)&hdi);
6005 if (nNewColumn == -1) return -1;
6006 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6008 /* create our own column info */
6009 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
6010 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6012 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6013 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6015 /* now we have to actually adjust the data */
6016 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6018 SUBITEM_INFO *lpSubItem, *lpMainItem, **lpNewItems = 0;
6019 HDPA hdpaSubItems;
6020 INT nItem, i;
6022 /* preallocate memory, so we can fail gracefully */
6023 if (nNewColumn == 0)
6025 lpNewItems = COMCTL32_Alloc(sizeof(SUBITEM_INFO *) * infoPtr->nItemCount);
6026 if (!lpNewItems) goto fail;
6027 for (i = 0; i < infoPtr->nItemCount; i++)
6028 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(SUBITEM_INFO)))) break;
6029 if (i != infoPtr->nItemCount)
6031 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
6032 COMCTL32_Free(lpNewItems);
6033 goto fail;
6037 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6039 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6040 for (i = 1; i < hdpaSubItems->nItemCount; i++)
6042 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6043 if (lpSubItem->iSubItem >= nNewColumn)
6044 lpSubItem->iSubItem++;
6047 /* for inserting column 0, we have to special-case the main item */
6048 if (nNewColumn == 0)
6050 lpMainItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6051 lpSubItem = lpNewItems[nItem];
6052 lpSubItem->hdr = lpMainItem->hdr;
6053 lpSubItem->iSubItem = 1;
6054 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
6055 lpMainItem->iSubItem = 0;
6056 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
6060 COMCTL32_Free(lpNewItems);
6063 /* make space for the new column */
6064 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6066 return nNewColumn;
6068 fail:
6069 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6070 if (lpColumnInfo)
6072 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6073 COMCTL32_Free(lpColumnInfo);
6075 return -1;
6078 /***
6079 * DESCRIPTION:
6080 * Sets the attributes of a header item.
6082 * PARAMETER(S):
6083 * [I] infoPtr : valid pointer to the listview structure
6084 * [I] nColumn : column index
6085 * [I] lpColumn : column attributes
6086 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6088 * RETURN:
6089 * SUCCESS : TRUE
6090 * FAILURE : FALSE
6092 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6093 LPLVCOLUMNW lpColumn, BOOL isW)
6095 HDITEMW hdi, hdiget;
6096 BOOL bResult;
6098 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6100 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6102 ZeroMemory(&hdi, sizeof(HDITEMW));
6103 if (lpColumn->mask & LVCF_FMT)
6105 hdi.mask |= HDI_FORMAT;
6106 hdiget.mask = HDI_FORMAT;
6107 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6108 hdi.fmt = hdiget.fmt & HDF_STRING;
6110 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6112 /* set header item attributes */
6113 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6114 if (!bResult) return FALSE;
6116 if (lpColumn->mask & LVCF_FMT)
6118 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6119 int oldFmt = lpColumnInfo->fmt;
6121 lpColumnInfo->fmt = lpColumn->fmt;
6122 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6124 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6125 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6129 return TRUE;
6132 /***
6133 * DESCRIPTION:
6134 * Sets the column order array
6136 * PARAMETERS:
6137 * [I] infoPtr : valid pointer to the listview structure
6138 * [I] iCount : number of elements in column order array
6139 * [I] lpiArray : pointer to column order array
6141 * RETURN:
6142 * SUCCESS : TRUE
6143 * FAILURE : FALSE
6145 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6147 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6149 if (!lpiArray)
6150 return FALSE;
6152 return TRUE;
6156 /***
6157 * DESCRIPTION:
6158 * Sets the width of a column
6160 * PARAMETERS:
6161 * [I] infoPtr : valid pointer to the listview structure
6162 * [I] nColumn : column index
6163 * [I] cx : column width
6165 * RETURN:
6166 * SUCCESS : TRUE
6167 * FAILURE : FALSE
6169 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6171 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6172 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6173 INT max_cx = 0;
6174 HDITEMW hdi;
6176 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6178 /* set column width only if in report or list mode */
6179 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6181 /* take care of invalid cx values */
6182 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6183 else if (uView == LVS_LIST && cx < 1) return FALSE;
6185 /* resize all columns if in LVS_LIST mode */
6186 if(uView == LVS_LIST)
6188 infoPtr->nItemWidth = cx;
6189 LISTVIEW_InvalidateList(infoPtr);
6190 return TRUE;
6193 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6195 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6197 INT nLabelWidth;
6198 LVITEMW lvItem;
6200 lvItem.mask = LVIF_TEXT;
6201 lvItem.iItem = 0;
6202 lvItem.iSubItem = nColumn;
6203 lvItem.pszText = szDispText;
6204 lvItem.cchTextMax = DISP_TEXT_SIZE;
6205 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6207 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6208 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6209 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6211 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6212 max_cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6213 max_cx += REPORT_MARGINX + TRAILING_PADDING;
6216 /* autosize based on listview items width */
6217 if(cx == LVSCW_AUTOSIZE)
6218 cx = max_cx;
6219 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6221 /* if iCol is the last column make it fill the remainder of the controls width */
6222 if(nColumn == infoPtr->hdpaColumns->nItemCount - 1)
6224 RECT rcHeader;
6225 POINT Origin;
6227 LISTVIEW_GetOrigin(infoPtr, &Origin);
6228 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6230 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6232 else
6234 /* Despite what the MS docs say, if this is not the last
6235 column, then MS resizes the column to the width of the
6236 largest text string in the column, including headers
6237 and items. This is different from LVSCW_AUTOSIZE in that
6238 LVSCW_AUTOSIZE ignores the header string length. */
6239 cx = 0;
6241 /* retrieve header text */
6242 hdi.mask = HDI_TEXT;
6243 hdi.cchTextMax = DISP_TEXT_SIZE;
6244 hdi.pszText = szDispText;
6245 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6247 HDC hdc = GetDC(infoPtr->hwndSelf);
6248 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6249 SIZE size;
6251 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6252 cx = size.cx;
6253 /* FIXME: Take into account the header image, if one is present */
6254 SelectObject(hdc, old_font);
6255 ReleaseDC(infoPtr->hwndSelf, hdc);
6257 cx = max (cx, max_cx);
6261 if (cx < 0) return FALSE;
6263 /* call header to update the column change */
6264 hdi.mask = HDI_WIDTH;
6265 hdi.cxy = cx;
6266 TRACE("hdi.cxy=%d\n", hdi.cxy);
6267 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6270 /***
6271 * DESCRIPTION:
6272 * Sets the extended listview style.
6274 * PARAMETERS:
6275 * [I] infoPtr : valid pointer to the listview structure
6276 * [I] dwMask : mask
6277 * [I] dwStyle : style
6279 * RETURN:
6280 * SUCCESS : previous style
6281 * FAILURE : 0
6283 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6285 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6287 /* set new style */
6288 if (dwMask)
6289 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6290 else
6291 infoPtr->dwLvExStyle = dwStyle;
6293 return dwOldStyle;
6296 /***
6297 * DESCRIPTION:
6298 * Sets the new hot cursor used during hot tracking and hover selection.
6300 * PARAMETER(S):
6301 * [I] infoPtr : valid pointer to the listview structure
6302 * [I} hCurosr : the new hot cursor handle
6304 * RETURN:
6305 * Returns the previous hot cursor
6307 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6309 HCURSOR oldCursor = infoPtr->hHotCursor;
6311 infoPtr->hHotCursor = hCursor;
6313 return oldCursor;
6317 /***
6318 * DESCRIPTION:
6319 * Sets the hot item index.
6321 * PARAMETERS:
6322 * [I] infoPtr : valid pointer to the listview structure
6323 * [I] iIndex : index
6325 * RETURN:
6326 * SUCCESS : previous hot item index
6327 * FAILURE : -1 (no hot item)
6329 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6331 INT iOldIndex = infoPtr->nHotItem;
6333 infoPtr->nHotItem = iIndex;
6335 return iOldIndex;
6339 /***
6340 * DESCRIPTION:
6341 * Sets the amount of time the cursor must hover over an item before it is selected.
6343 * PARAMETER(S):
6344 * [I] infoPtr : valid pointer to the listview structure
6345 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6347 * RETURN:
6348 * Returns the previous hover time
6350 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6352 DWORD oldHoverTime = infoPtr->dwHoverTime;
6354 infoPtr->dwHoverTime = dwHoverTime;
6356 return oldHoverTime;
6359 /***
6360 * DESCRIPTION:
6361 * Sets spacing for icons of LVS_ICON style.
6363 * PARAMETER(S):
6364 * [I] infoPtr : valid pointer to the listview structure
6365 * [I] spacing : MAKELONG(cx, cy)
6367 * RETURN:
6368 * MAKELONG(oldcx, oldcy)
6370 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6372 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6373 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6374 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6376 TRACE("requested=(%d,%d)\n", cx, cy);
6378 /* this is supported only for LVS_ICON style */
6379 if (uView != LVS_ICON) return oldspacing;
6381 /* set to defaults, if instructed to */
6382 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6383 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6385 /* if 0 then compute width
6386 * FIXME: Should scan each item and determine max width of
6387 * icon or label, then make that the width */
6388 if (cx == 0)
6389 cx = infoPtr->iconSpacing.cx;
6391 /* if 0 then compute height */
6392 if (cy == 0)
6393 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6394 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6397 infoPtr->iconSpacing.cx = cx;
6398 infoPtr->iconSpacing.cy = cy;
6400 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6401 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6402 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6403 infoPtr->ntmHeight);
6405 /* these depend on the iconSpacing */
6406 LISTVIEW_UpdateItemSize(infoPtr);
6408 return oldspacing;
6411 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6413 INT cx, cy;
6415 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6417 size->cx = cx;
6418 size->cy = cy;
6420 else
6422 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6423 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6427 /***
6428 * DESCRIPTION:
6429 * Sets image lists.
6431 * PARAMETER(S):
6432 * [I] infoPtr : valid pointer to the listview structure
6433 * [I] nType : image list type
6434 * [I] himl : image list handle
6436 * RETURN:
6437 * SUCCESS : old image list
6438 * FAILURE : NULL
6440 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6442 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6443 INT oldHeight = infoPtr->nItemHeight;
6444 HIMAGELIST himlOld = 0;
6446 switch (nType)
6448 case LVSIL_NORMAL:
6449 himlOld = infoPtr->himlNormal;
6450 infoPtr->himlNormal = himl;
6451 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6452 LISTVIEW_SetIconSpacing(infoPtr, 0);
6453 break;
6455 case LVSIL_SMALL:
6456 himlOld = infoPtr->himlSmall;
6457 infoPtr->himlSmall = himl;
6458 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6459 break;
6461 case LVSIL_STATE:
6462 himlOld = infoPtr->himlState;
6463 infoPtr->himlState = himl;
6464 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6465 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6466 break;
6468 default:
6469 ERR("Unknown icon type=%d\n", nType);
6470 return NULL;
6473 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6474 if (infoPtr->nItemHeight != oldHeight)
6475 LISTVIEW_UpdateScroll(infoPtr);
6477 return himlOld;
6480 /***
6481 * DESCRIPTION:
6482 * Preallocates memory (does *not* set the actual count of items !)
6484 * PARAMETER(S):
6485 * [I] infoPtr : valid pointer to the listview structure
6486 * [I] nItems : item count (projected number of items to allocate)
6487 * [I] dwFlags : update flags
6489 * RETURN:
6490 * SUCCESS : TRUE
6491 * FAILURE : FALSE
6493 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6495 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6497 if (infoPtr->dwStyle & LVS_OWNERDATA)
6499 int precount,topvisible;
6501 TRACE("LVS_OWNERDATA is set!\n");
6502 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6503 FIXME("flags %s %s not implemented\n",
6504 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6505 : "",
6506 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6508 LISTVIEW_DeselectAll(infoPtr);
6510 precount = infoPtr->nItemCount;
6511 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6512 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6514 infoPtr->nItemCount = nItems;
6515 LISTVIEW_UpdateItemSize(infoPtr);
6517 LISTVIEW_UpdateSize(infoPtr);
6518 LISTVIEW_UpdateScroll(infoPtr);
6520 if (min(precount,infoPtr->nItemCount) < topvisible)
6521 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6523 else
6525 /* According to MSDN for non-LVS_OWNERDATA this is just
6526 * a performance issue. The control allocates its internal
6527 * data structures for the number of items specified. It
6528 * cuts down on the number of memory allocations. Therefore
6529 * we will just issue a WARN here
6531 WARN("for non-ownerdata performance option not implemented.\n");
6534 return TRUE;
6537 /***
6538 * DESCRIPTION:
6539 * Sets the position of an item.
6541 * PARAMETER(S):
6542 * [I] infoPtr : valid pointer to the listview structure
6543 * [I] nItem : item index
6544 * [I] pt : coordinate
6546 * RETURN:
6547 * SUCCESS : TRUE
6548 * FAILURE : FALSE
6550 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6552 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6553 POINT Origin;
6555 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6557 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6558 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6560 LISTVIEW_GetOrigin(infoPtr, &Origin);
6562 /* This point value seems to be an undocumented feature.
6563 * The best guess is that it means either at the origin,
6564 * or at true beginning of the list. I will assume the origin. */
6565 if ((pt.x == -1) && (pt.y == -1))
6566 pt = Origin;
6568 if (uView == LVS_ICON)
6570 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6571 pt.y -= ICON_TOP_PADDING;
6573 pt.x -= Origin.x;
6574 pt.y -= Origin.y;
6576 infoPtr->bAutoarrange = FALSE;
6578 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
6581 /***
6582 * DESCRIPTION:
6583 * Sets the state of one or many items.
6585 * PARAMETER(S):
6586 * [I] infoPtr : valid pointer to the listview structure
6587 * [I] nItem : item index
6588 * [I] lpLVItem : item or subitem info
6590 * RETURN:
6591 * SUCCESS : TRUE
6592 * FAILURE : FALSE
6594 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6596 BOOL bResult = TRUE;
6597 LVITEMW lvItem;
6599 lvItem.iItem = nItem;
6600 lvItem.iSubItem = 0;
6601 lvItem.mask = LVIF_STATE;
6602 lvItem.state = lpLVItem->state;
6603 lvItem.stateMask = lpLVItem->stateMask;
6604 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6606 if (nItem == -1)
6608 /* apply to all items */
6609 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6610 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6612 else
6613 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6615 return bResult;
6618 /***
6619 * DESCRIPTION:
6620 * Sets the text of an item or subitem.
6622 * PARAMETER(S):
6623 * [I] hwnd : window handle
6624 * [I] nItem : item index
6625 * [I] lpLVItem : item or subitem info
6626 * [I] isW : TRUE if input is Unicode
6628 * RETURN:
6629 * SUCCESS : TRUE
6630 * FAILURE : FALSE
6632 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6634 LVITEMW lvItem;
6636 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6638 lvItem.iItem = nItem;
6639 lvItem.iSubItem = lpLVItem->iSubItem;
6640 lvItem.mask = LVIF_TEXT;
6641 lvItem.pszText = lpLVItem->pszText;
6642 lvItem.cchTextMax = lpLVItem->cchTextMax;
6644 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6646 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6649 /***
6650 * DESCRIPTION:
6651 * Set item index that marks the start of a multiple selection.
6653 * PARAMETER(S):
6654 * [I] infoPtr : valid pointer to the listview structure
6655 * [I] nIndex : index
6657 * RETURN:
6658 * Index number or -1 if there is no selection mark.
6660 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6662 INT nOldIndex = infoPtr->nSelectionMark;
6664 TRACE("(nIndex=%d)\n", nIndex);
6666 infoPtr->nSelectionMark = nIndex;
6668 return nOldIndex;
6671 /***
6672 * DESCRIPTION:
6673 * Sets the text background color.
6675 * PARAMETER(S):
6676 * [I] infoPtr : valid pointer to the listview structure
6677 * [I] clrTextBk : text background color
6679 * RETURN:
6680 * SUCCESS : TRUE
6681 * FAILURE : FALSE
6683 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6685 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6687 if (infoPtr->clrTextBk != clrTextBk)
6689 infoPtr->clrTextBk = clrTextBk;
6690 LISTVIEW_InvalidateList(infoPtr);
6693 return TRUE;
6696 /***
6697 * DESCRIPTION:
6698 * Sets the text foreground color.
6700 * PARAMETER(S):
6701 * [I] infoPtr : valid pointer to the listview structure
6702 * [I] clrText : text color
6704 * RETURN:
6705 * SUCCESS : TRUE
6706 * FAILURE : FALSE
6708 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6710 TRACE("(clrText=%lx)\n", clrText);
6712 if (infoPtr->clrText != clrText)
6714 infoPtr->clrText = clrText;
6715 LISTVIEW_InvalidateList(infoPtr);
6718 return TRUE;
6721 /* LISTVIEW_SetToolTips */
6722 /* LISTVIEW_SetUnicodeFormat */
6723 /* LISTVIEW_SetWorkAreas */
6725 /***
6726 * DESCRIPTION:
6727 * Callback internally used by LISTVIEW_SortItems()
6729 * PARAMETER(S):
6730 * [I] first : pointer to first ITEM_INFO to compare
6731 * [I] second : pointer to second ITEM_INFO to compare
6732 * [I] lParam : HWND of control
6734 * RETURN:
6735 * if first comes before second : negative
6736 * if first comes after second : positive
6737 * if first and second are equivalent : zero
6739 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6741 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6742 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6743 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6745 /* Forward the call to the client defined callback */
6746 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6749 /***
6750 * DESCRIPTION:
6751 * Sorts the listview items.
6753 * PARAMETER(S):
6754 * [I] infoPtr : valid pointer to the listview structure
6755 * [I] pfnCompare : application-defined value
6756 * [I] lParamSort : pointer to comparision callback
6758 * RETURN:
6759 * SUCCESS : TRUE
6760 * FAILURE : FALSE
6762 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6764 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6765 HDPA hdpaSubItems;
6766 ITEM_INFO *lpItem;
6767 LPVOID selectionMarkItem;
6768 LVITEMW item;
6769 int i;
6771 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6773 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
6775 if (!infoPtr->hdpaItems) return FALSE;
6777 /* if there are 0 or 1 items, there is no need to sort */
6778 if (infoPtr->nItemCount < 2) return TRUE;
6780 if (infoPtr->nFocusedItem >= 0)
6782 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6783 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6784 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6786 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6787 /* clear the lpItem->state for non-selected ones */
6788 /* remove the selection ranges */
6790 infoPtr->pfnCompare = pfnCompare;
6791 infoPtr->lParamSort = lParamSort;
6792 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6794 /* Adjust selections and indices so that they are the way they should
6795 * be after the sort (otherwise, the list items move around, but
6796 * whatever is at the item's previous original position will be
6797 * selected instead)
6799 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6800 for (i=0; i < infoPtr->nItemCount; i++)
6802 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6803 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6805 if (lpItem->state & LVIS_SELECTED)
6807 item.state = LVIS_SELECTED;
6808 item.stateMask = LVIS_SELECTED;
6809 LISTVIEW_SetItemState(infoPtr, i, &item);
6811 if (lpItem->state & LVIS_FOCUSED)
6813 infoPtr->nFocusedItem = i;
6814 lpItem->state &= ~LVIS_FOCUSED;
6817 if (selectionMarkItem != NULL)
6818 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6819 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6821 /* refresh the display */
6822 if (uView != LVS_ICON && uView != LVS_SMALLICON)
6823 LISTVIEW_InvalidateList(infoPtr);
6825 return TRUE;
6828 /***
6829 * DESCRIPTION:
6830 * Updates an items or rearranges the listview control.
6832 * PARAMETER(S):
6833 * [I] infoPtr : valid pointer to the listview structure
6834 * [I] nItem : item index
6836 * RETURN:
6837 * SUCCESS : TRUE
6838 * FAILURE : FALSE
6840 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6842 TRACE("(nItem=%d)\n", nItem);
6844 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6846 /* rearrange with default alignment style */
6847 if (is_autoarrange(infoPtr))
6848 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
6849 else
6850 LISTVIEW_InvalidateItem(infoPtr, nItem);
6852 return TRUE;
6856 /***
6857 * DESCRIPTION:
6858 * Creates the listview control.
6860 * PARAMETER(S):
6861 * [I] hwnd : window handle
6862 * [I] lpcs : the create parameters
6864 * RETURN:
6865 * Success: 0
6866 * Failure: -1
6868 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6870 LISTVIEW_INFO *infoPtr;
6871 UINT uView = lpcs->style & LVS_TYPEMASK;
6872 LOGFONTW logFont;
6874 TRACE("(lpcs=%p)\n", lpcs);
6876 /* initialize info pointer */
6877 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6878 if (!infoPtr) return -1;
6880 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6882 infoPtr->hwndSelf = hwnd;
6883 infoPtr->dwStyle = lpcs->style;
6884 /* determine the type of structures to use */
6885 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6886 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6888 /* initialize color information */
6889 infoPtr->clrBk = CLR_NONE;
6890 infoPtr->clrText = comctl32_color.clrWindowText;
6891 infoPtr->clrTextBk = CLR_DEFAULT;
6892 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6894 /* set default values */
6895 infoPtr->nFocusedItem = -1;
6896 infoPtr->nSelectionMark = -1;
6897 infoPtr->nHotItem = -1;
6898 infoPtr->bRedraw = TRUE;
6899 infoPtr->bFirstPaint = TRUE;
6900 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6901 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6902 infoPtr->nEditLabelItem = -1;
6903 infoPtr->dwHoverTime = -1; /* default system hover time */
6905 /* get default font (icon title) */
6906 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6907 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6908 infoPtr->hFont = infoPtr->hDefaultFont;
6909 LISTVIEW_SaveTextMetrics(infoPtr);
6911 /* create header */
6912 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6913 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6914 0, 0, 0, 0, hwnd, (HMENU)0,
6915 lpcs->hInstance, NULL);
6917 /* set header unicode format */
6918 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
6920 /* set header font */
6921 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
6923 /* allocate memory for the selection ranges */
6924 if (!(infoPtr->selectionRanges = ranges_create(10))) return -1;
6926 /* allocate memory for the data structure */
6927 /* FIXME: what if we fail? */
6928 infoPtr->hdpaItems = DPA_Create(10);
6929 infoPtr->hdpaPosX = DPA_Create(10);
6930 infoPtr->hdpaPosY = DPA_Create(10);
6931 infoPtr->hdpaColumns = DPA_Create(10);
6933 /* initialize the icon sizes */
6934 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
6935 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
6937 /* init item size to avoid division by 0 */
6938 LISTVIEW_UpdateItemSize (infoPtr);
6940 if (uView == LVS_REPORT)
6942 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6944 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6946 else
6948 /* set HDS_HIDDEN flag to hide the header bar */
6949 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6950 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6954 return 0;
6957 /***
6958 * DESCRIPTION:
6959 * Erases the background of the listview control.
6961 * PARAMETER(S):
6962 * [I] infoPtr : valid pointer to the listview structure
6963 * [I] hdc : device context handle
6965 * RETURN:
6966 * SUCCESS : TRUE
6967 * FAILURE : FALSE
6969 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6971 RECT rc;
6973 TRACE("(hdc=%p)\n", hdc);
6975 if (!GetClipBox(hdc, &rc)) return FALSE;
6977 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
6981 /***
6982 * DESCRIPTION:
6983 * Helper function for LISTVIEW_[HV]Scroll *only*.
6984 * Performs vertical/horizontal scrolling by a give amount.
6986 * PARAMETER(S):
6987 * [I] infoPtr : valid pointer to the listview structure
6988 * [I] dx : amount of horizontal scroll
6989 * [I] dy : amount of vertical scroll
6991 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6993 /* now we can scroll the list */
6994 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
6995 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
6996 /* if we have focus, adjust rect */
6997 OffsetRect(&infoPtr->rcFocus, dx, dy);
6998 UpdateWindow(infoPtr->hwndSelf);
7001 /***
7002 * DESCRIPTION:
7003 * Performs vertical scrolling.
7005 * PARAMETER(S):
7006 * [I] infoPtr : valid pointer to the listview structure
7007 * [I] nScrollCode : scroll code
7008 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7009 * [I] hScrollWnd : scrollbar control window handle
7011 * RETURN:
7012 * Zero
7014 * NOTES:
7015 * SB_LINEUP/SB_LINEDOWN:
7016 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7017 * for LVS_REPORT is 1 line
7018 * for LVS_LIST cannot occur
7021 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7022 INT nScrollDiff, HWND hScrollWnd)
7024 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7025 INT nOldScrollPos, nNewScrollPos;
7026 SCROLLINFO scrollInfo;
7027 BOOL is_an_icon;
7029 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7031 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7033 scrollInfo.cbSize = sizeof(SCROLLINFO);
7034 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7036 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7038 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7040 nOldScrollPos = scrollInfo.nPos;
7041 switch (nScrollCode)
7043 case SB_INTERNAL:
7044 break;
7046 case SB_LINEUP:
7047 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7048 break;
7050 case SB_LINEDOWN:
7051 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7052 break;
7054 case SB_PAGEUP:
7055 nScrollDiff = -scrollInfo.nPage;
7056 break;
7058 case SB_PAGEDOWN:
7059 nScrollDiff = scrollInfo.nPage;
7060 break;
7062 case SB_THUMBPOSITION:
7063 case SB_THUMBTRACK:
7064 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7065 break;
7067 default:
7068 nScrollDiff = 0;
7071 /* quit right away if pos isn't changing */
7072 if (nScrollDiff == 0) return 0;
7074 /* calculate new position, and handle overflows */
7075 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7076 if (nScrollDiff > 0) {
7077 if (nNewScrollPos < nOldScrollPos ||
7078 nNewScrollPos > scrollInfo.nMax)
7079 nNewScrollPos = scrollInfo.nMax;
7080 } else {
7081 if (nNewScrollPos > nOldScrollPos ||
7082 nNewScrollPos < scrollInfo.nMin)
7083 nNewScrollPos = scrollInfo.nMin;
7086 /* set the new position, and reread in case it changed */
7087 scrollInfo.fMask = SIF_POS;
7088 scrollInfo.nPos = nNewScrollPos;
7089 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7091 /* carry on only if it really changed */
7092 if (nNewScrollPos == nOldScrollPos) return 0;
7094 /* now adjust to client coordinates */
7095 nScrollDiff = nOldScrollPos - nNewScrollPos;
7096 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7098 /* and scroll the window */
7099 scroll_list(infoPtr, 0, nScrollDiff);
7101 return 0;
7104 /***
7105 * DESCRIPTION:
7106 * Performs horizontal scrolling.
7108 * PARAMETER(S):
7109 * [I] infoPtr : valid pointer to the listview structure
7110 * [I] nScrollCode : scroll code
7111 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7112 * [I] hScrollWnd : scrollbar control window handle
7114 * RETURN:
7115 * Zero
7117 * NOTES:
7118 * SB_LINELEFT/SB_LINERIGHT:
7119 * for LVS_ICON, LVS_SMALLICON 1 pixel
7120 * for LVS_REPORT is 1 pixel
7121 * for LVS_LIST is 1 column --> which is a 1 because the
7122 * scroll is based on columns not pixels
7125 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7126 INT nScrollDiff, HWND hScrollWnd)
7128 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7129 INT nOldScrollPos, nNewScrollPos;
7130 SCROLLINFO scrollInfo;
7132 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7134 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7136 scrollInfo.cbSize = sizeof(SCROLLINFO);
7137 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7139 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7141 nOldScrollPos = scrollInfo.nPos;
7143 switch (nScrollCode)
7145 case SB_INTERNAL:
7146 break;
7148 case SB_LINELEFT:
7149 nScrollDiff = -1;
7150 break;
7152 case SB_LINERIGHT:
7153 nScrollDiff = 1;
7154 break;
7156 case SB_PAGELEFT:
7157 nScrollDiff = -scrollInfo.nPage;
7158 break;
7160 case SB_PAGERIGHT:
7161 nScrollDiff = scrollInfo.nPage;
7162 break;
7164 case SB_THUMBPOSITION:
7165 case SB_THUMBTRACK:
7166 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7167 break;
7169 default:
7170 nScrollDiff = 0;
7173 /* quit right away if pos isn't changing */
7174 if (nScrollDiff == 0) return 0;
7176 /* calculate new position, and handle overflows */
7177 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7178 if (nScrollDiff > 0) {
7179 if (nNewScrollPos < nOldScrollPos ||
7180 nNewScrollPos > scrollInfo.nMax)
7181 nNewScrollPos = scrollInfo.nMax;
7182 } else {
7183 if (nNewScrollPos > nOldScrollPos ||
7184 nNewScrollPos < scrollInfo.nMin)
7185 nNewScrollPos = scrollInfo.nMin;
7188 /* set the new position, and reread in case it changed */
7189 scrollInfo.fMask = SIF_POS;
7190 scrollInfo.nPos = nNewScrollPos;
7191 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7193 /* carry on only if it really changed */
7194 if (nNewScrollPos == nOldScrollPos) return 0;
7196 if(uView == LVS_REPORT)
7197 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7199 /* now adjust to client coordinates */
7200 nScrollDiff = nOldScrollPos - nNewScrollPos;
7201 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7203 /* and scroll the window */
7204 scroll_list(infoPtr, nScrollDiff, 0);
7206 return 0;
7209 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7211 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7212 INT gcWheelDelta = 0;
7213 UINT pulScrollLines = 3;
7214 SCROLLINFO scrollInfo;
7216 TRACE("(wheelDelta=%d)\n", wheelDelta);
7218 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7219 gcWheelDelta -= wheelDelta;
7221 scrollInfo.cbSize = sizeof(SCROLLINFO);
7222 scrollInfo.fMask = SIF_POS;
7224 switch(uView)
7226 case LVS_ICON:
7227 case LVS_SMALLICON:
7229 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7230 * should be fixed in the future.
7232 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7233 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7234 scrollInfo.nPos + (gcWheelDelta < 0) ?
7235 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7236 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7237 break;
7239 case LVS_REPORT:
7240 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7242 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7244 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7245 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7246 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7249 break;
7251 case LVS_LIST:
7252 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7253 break;
7255 return 0;
7258 /***
7259 * DESCRIPTION:
7260 * ???
7262 * PARAMETER(S):
7263 * [I] infoPtr : valid pointer to the listview structure
7264 * [I] nVirtualKey : virtual key
7265 * [I] lKeyData : key data
7267 * RETURN:
7268 * Zero
7270 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7272 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7273 INT nItem = -1;
7274 NMLVKEYDOWN nmKeyDown;
7276 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7278 /* send LVN_KEYDOWN notification */
7279 nmKeyDown.wVKey = nVirtualKey;
7280 nmKeyDown.flags = 0;
7281 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7283 switch (nVirtualKey)
7285 case VK_RETURN:
7286 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7288 notify(infoPtr, NM_RETURN);
7289 notify(infoPtr, LVN_ITEMACTIVATE);
7291 break;
7293 case VK_HOME:
7294 if (infoPtr->nItemCount > 0)
7295 nItem = 0;
7296 break;
7298 case VK_END:
7299 if (infoPtr->nItemCount > 0)
7300 nItem = infoPtr->nItemCount - 1;
7301 break;
7303 case VK_LEFT:
7304 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7305 break;
7307 case VK_UP:
7308 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7309 break;
7311 case VK_RIGHT:
7312 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7313 break;
7315 case VK_DOWN:
7316 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7317 break;
7319 case VK_PRIOR:
7320 if (uView == LVS_REPORT)
7321 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7322 else
7323 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7324 * LISTVIEW_GetCountPerRow(infoPtr);
7325 if(nItem < 0) nItem = 0;
7326 break;
7328 case VK_NEXT:
7329 if (uView == LVS_REPORT)
7330 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7331 else
7332 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7333 * LISTVIEW_GetCountPerRow(infoPtr);
7334 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7335 break;
7338 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7339 LISTVIEW_KeySelection(infoPtr, nItem);
7341 return 0;
7344 /***
7345 * DESCRIPTION:
7346 * Kills the focus.
7348 * PARAMETER(S):
7349 * [I] infoPtr : valid pointer to the listview structure
7351 * RETURN:
7352 * Zero
7354 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7356 TRACE("()\n");
7358 /* if we did not have the focus, there's nothing to do */
7359 if (!infoPtr->bFocus) return 0;
7361 /* send NM_KILLFOCUS notification */
7362 notify(infoPtr, NM_KILLFOCUS);
7364 /* if we have a focus rectagle, get rid of it */
7365 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7367 /* set window focus flag */
7368 infoPtr->bFocus = FALSE;
7370 /* invalidate the selected items before reseting focus flag */
7371 LISTVIEW_InvalidateSelectedItems(infoPtr);
7373 return 0;
7376 /***
7377 * DESCRIPTION:
7378 * Processes double click messages (left mouse button).
7380 * PARAMETER(S):
7381 * [I] infoPtr : valid pointer to the listview structure
7382 * [I] wKey : key flag
7383 * [I] pts : mouse coordinate
7385 * RETURN:
7386 * Zero
7388 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7390 LVHITTESTINFO htInfo;
7392 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7394 /* send NM_RELEASEDCAPTURE notification */
7395 notify(infoPtr, NM_RELEASEDCAPTURE);
7397 htInfo.pt.x = pts.x;
7398 htInfo.pt.y = pts.y;
7400 /* send NM_DBLCLK notification */
7401 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7402 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7404 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7405 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7407 return 0;
7410 /***
7411 * DESCRIPTION:
7412 * Processes mouse down messages (left mouse button).
7414 * PARAMETER(S):
7415 * [I] infoPtr : valid pointer to the listview structure
7416 * [I] wKey : key flag
7417 * [I] pts : mouse coordinate
7419 * RETURN:
7420 * Zero
7422 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7424 LVHITTESTINFO lvHitTestInfo;
7425 static BOOL bGroupSelect = TRUE;
7426 POINT pt = { pts.x, pts.y };
7427 INT nItem;
7429 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7431 /* send NM_RELEASEDCAPTURE notification */
7432 notify(infoPtr, NM_RELEASEDCAPTURE);
7434 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7436 /* set left button down flag */
7437 infoPtr->bLButtonDown = TRUE;
7439 lvHitTestInfo.pt.x = pts.x;
7440 lvHitTestInfo.pt.y = pts.y;
7442 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7443 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7444 infoPtr->nEditLabelItem = -1;
7445 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7447 if (infoPtr->dwStyle & LVS_SINGLESEL)
7449 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7450 infoPtr->nEditLabelItem = nItem;
7451 else
7452 LISTVIEW_SetSelection(infoPtr, nItem);
7454 else
7456 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7458 if (bGroupSelect)
7460 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7461 LISTVIEW_SetItemFocus(infoPtr, nItem);
7462 infoPtr->nSelectionMark = nItem;
7464 else
7466 LVITEMW item;
7468 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7469 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7471 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7472 infoPtr->nSelectionMark = nItem;
7475 else if (wKey & MK_CONTROL)
7477 LVITEMW item;
7479 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7481 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7482 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7483 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7484 infoPtr->nSelectionMark = nItem;
7486 else if (wKey & MK_SHIFT)
7488 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7490 else
7492 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7493 infoPtr->nEditLabelItem = nItem;
7495 /* set selection (clears other pre-existing selections) */
7496 LISTVIEW_SetSelection(infoPtr, nItem);
7500 else
7502 /* remove all selections */
7503 LISTVIEW_DeselectAll(infoPtr);
7506 return 0;
7509 /***
7510 * DESCRIPTION:
7511 * Processes mouse up messages (left mouse button).
7513 * PARAMETER(S):
7514 * [I] infoPtr : valid pointer to the listview structure
7515 * [I] wKey : key flag
7516 * [I] pts : mouse coordinate
7518 * RETURN:
7519 * Zero
7521 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7523 LVHITTESTINFO lvHitTestInfo;
7525 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7527 if (!infoPtr->bLButtonDown) return 0;
7529 lvHitTestInfo.pt.x = pts.x;
7530 lvHitTestInfo.pt.y = pts.y;
7532 /* send NM_CLICK notification */
7533 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7534 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7536 /* set left button flag */
7537 infoPtr->bLButtonDown = FALSE;
7539 /* if we clicked on a selected item, edit the label */
7540 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7541 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7543 return 0;
7546 /***
7547 * DESCRIPTION:
7548 * Destroys the listview control (called after WM_DESTROY).
7550 * PARAMETER(S):
7551 * [I] infoPtr : valid pointer to the listview structure
7553 * RETURN:
7554 * Zero
7556 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7558 TRACE("()\n");
7560 /* delete all items */
7561 LISTVIEW_DeleteAllItems(infoPtr);
7563 /* destroy data structure */
7564 DPA_Destroy(infoPtr->hdpaItems);
7565 if (infoPtr->selectionRanges) ranges_destroy(infoPtr->selectionRanges);
7567 /* destroy image lists */
7568 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7570 if (infoPtr->himlNormal)
7571 ImageList_Destroy(infoPtr->himlNormal);
7572 if (infoPtr->himlSmall)
7573 ImageList_Destroy(infoPtr->himlSmall);
7574 if (infoPtr->himlState)
7575 ImageList_Destroy(infoPtr->himlState);
7578 /* destroy font, bkgnd brush */
7579 infoPtr->hFont = 0;
7580 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7581 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7583 /* free listview info pointer*/
7584 COMCTL32_Free(infoPtr);
7586 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7587 return 0;
7590 /***
7591 * DESCRIPTION:
7592 * Handles notifications from header.
7594 * PARAMETER(S):
7595 * [I] infoPtr : valid pointer to the listview structure
7596 * [I] nCtrlId : control identifier
7597 * [I] lpnmh : notification information
7599 * RETURN:
7600 * Zero
7602 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, LPNMHEADERW lpnmh)
7604 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7606 TRACE("(lpnmh=%p)\n", lpnmh);
7608 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7610 switch (lpnmh->hdr.code)
7612 case HDN_TRACKW:
7613 case HDN_TRACKA:
7614 case HDN_ITEMCHANGEDW:
7615 case HDN_ITEMCHANGEDA:
7617 COLUMN_INFO *lpColumnInfo;
7618 INT dx, cxy;
7620 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7622 HDITEMW hdi;
7624 hdi.mask = HDI_WIDTH;
7625 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7626 cxy = hdi.cxy;
7628 else
7629 cxy = lpnmh->pitem->cxy;
7631 /* determine how much we change since the last know position */
7632 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7633 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7634 if (dx != 0)
7636 lpColumnInfo->rcHeader.right += dx;
7637 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7638 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, lpnmh->iItem);
7641 break;
7643 case HDN_ITEMCLICKW:
7644 case HDN_ITEMCLICKA:
7646 /* Handle sorting by Header Column */
7647 NMLISTVIEW nmlv;
7649 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7650 nmlv.iItem = -1;
7651 nmlv.iSubItem = lpnmh->iItem;
7652 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7654 break;
7657 return 0;
7660 /***
7661 * DESCRIPTION:
7662 * Determines the type of structure to use.
7664 * PARAMETER(S):
7665 * [I] infoPtr : valid pointer to the listview structureof the sender
7666 * [I] hwndFrom : listview window handle
7667 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7669 * RETURN:
7670 * Zero
7672 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7674 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
7676 if (nCommand != NF_REQUERY) return 0;
7678 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
7680 return 0;
7683 /***
7684 * DESCRIPTION:
7685 * Paints/Repaints the listview control.
7687 * PARAMETER(S):
7688 * [I] infoPtr : valid pointer to the listview structure
7689 * [I] hdc : device context handle
7691 * RETURN:
7692 * Zero
7694 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7696 TRACE("(hdc=%p)\n", hdc);
7698 if (infoPtr->bFirstPaint)
7700 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7702 infoPtr->bFirstPaint = FALSE;
7703 LISTVIEW_UpdateSize(infoPtr);
7704 LISTVIEW_UpdateItemSize(infoPtr);
7705 if (uView == LVS_ICON || uView == LVS_SMALLICON)
7706 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7707 LISTVIEW_UpdateScroll(infoPtr);
7709 if (hdc)
7710 LISTVIEW_Refresh(infoPtr, hdc);
7711 else
7713 PAINTSTRUCT ps;
7715 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7716 if (!hdc) return 1;
7717 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7718 LISTVIEW_Refresh(infoPtr, hdc);
7719 EndPaint(infoPtr->hwndSelf, &ps);
7722 return 0;
7725 /***
7726 * DESCRIPTION:
7727 * Processes double click messages (right mouse button).
7729 * PARAMETER(S):
7730 * [I] infoPtr : valid pointer to the listview structure
7731 * [I] wKey : key flag
7732 * [I] pts : mouse coordinate
7734 * RETURN:
7735 * Zero
7737 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7739 LVHITTESTINFO lvHitTestInfo;
7741 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7743 /* send NM_RELEASEDCAPTURE notification */
7744 notify(infoPtr, NM_RELEASEDCAPTURE);
7746 /* send NM_RDBLCLK notification */
7747 lvHitTestInfo.pt.x = pts.x;
7748 lvHitTestInfo.pt.y = pts.y;
7749 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7750 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7752 return 0;
7755 /***
7756 * DESCRIPTION:
7757 * Processes mouse down messages (right mouse button).
7759 * PARAMETER(S):
7760 * [I] infoPtr : valid pointer to the listview structure
7761 * [I] wKey : key flag
7762 * [I] pts : mouse coordinate
7764 * RETURN:
7765 * Zero
7767 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7769 LVHITTESTINFO lvHitTestInfo;
7770 INT nItem;
7772 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7774 /* send NM_RELEASEDCAPTURE notification */
7775 notify(infoPtr, NM_RELEASEDCAPTURE);
7777 /* make sure the listview control window has the focus */
7778 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7780 /* set right button down flag */
7781 infoPtr->bRButtonDown = TRUE;
7783 /* determine the index of the selected item */
7784 lvHitTestInfo.pt.x = pts.x;
7785 lvHitTestInfo.pt.y = pts.y;
7786 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7788 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7790 LISTVIEW_SetItemFocus(infoPtr, nItem);
7791 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7792 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7793 LISTVIEW_SetSelection(infoPtr, nItem);
7795 else
7797 LISTVIEW_DeselectAll(infoPtr);
7800 return 0;
7803 /***
7804 * DESCRIPTION:
7805 * Processes mouse up messages (right mouse button).
7807 * PARAMETER(S):
7808 * [I] infoPtr : valid pointer to the listview structure
7809 * [I] wKey : key flag
7810 * [I] pts : mouse coordinate
7812 * RETURN:
7813 * Zero
7815 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7817 LVHITTESTINFO lvHitTestInfo;
7818 POINT pt;
7820 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7822 if (!infoPtr->bRButtonDown) return 0;
7824 /* set button flag */
7825 infoPtr->bRButtonDown = FALSE;
7827 /* Send NM_RClICK notification */
7828 lvHitTestInfo.pt.x = pts.x;
7829 lvHitTestInfo.pt.y = pts.y;
7830 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7831 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7833 /* Change to screen coordinate for WM_CONTEXTMENU */
7834 pt = lvHitTestInfo.pt;
7835 ClientToScreen(infoPtr->hwndSelf, &pt);
7837 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7838 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7839 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7841 return 0;
7845 /***
7846 * DESCRIPTION:
7847 * Sets the cursor.
7849 * PARAMETER(S):
7850 * [I] infoPtr : valid pointer to the listview structure
7851 * [I] hwnd : window handle of window containing the cursor
7852 * [I] nHittest : hit-test code
7853 * [I] wMouseMsg : ideintifier of the mouse message
7855 * RETURN:
7856 * TRUE if cursor is set
7857 * FALSE otherwise
7859 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7861 LVHITTESTINFO lvHitTestInfo;
7863 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7865 if(!infoPtr->hHotCursor) return FALSE;
7867 GetCursorPos(&lvHitTestInfo.pt);
7868 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7870 SetCursor(infoPtr->hHotCursor);
7872 return TRUE;
7875 /***
7876 * DESCRIPTION:
7877 * Sets the focus.
7879 * PARAMETER(S):
7880 * [I] infoPtr : valid pointer to the listview structure
7881 * [I] hwndLoseFocus : handle of previously focused window
7883 * RETURN:
7884 * Zero
7886 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7888 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
7890 /* if we have the focus already, there's nothing to do */
7891 if (infoPtr->bFocus) return 0;
7893 /* send NM_SETFOCUS notification */
7894 notify(infoPtr, NM_SETFOCUS);
7896 /* set window focus flag */
7897 infoPtr->bFocus = TRUE;
7899 /* put the focus rect back on */
7900 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7902 /* redraw all visible selected items */
7903 LISTVIEW_InvalidateSelectedItems(infoPtr);
7905 return 0;
7908 /***
7909 * DESCRIPTION:
7910 * Sets the font.
7912 * PARAMETER(S):
7913 * [I] infoPtr : valid pointer to the listview structure
7914 * [I] fRedraw : font handle
7915 * [I] fRedraw : redraw flag
7917 * RETURN:
7918 * Zero
7920 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7922 HFONT oldFont = infoPtr->hFont;
7924 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
7926 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7927 if (infoPtr->hFont == oldFont) return 0;
7929 LISTVIEW_SaveTextMetrics(infoPtr);
7931 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7932 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7934 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7936 return 0;
7939 /***
7940 * DESCRIPTION:
7941 * Message handling for WM_SETREDRAW.
7942 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7944 * PARAMETER(S):
7945 * [I] infoPtr : valid pointer to the listview structure
7946 * [I] bRedraw: state of redraw flag
7948 * RETURN:
7949 * DefWinProc return value
7951 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7953 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7955 infoPtr->bRedraw = bRedraw;
7957 if(!bRedraw) return 0;
7959 LISTVIEW_UpdateSize(infoPtr);
7960 if (uView == LVS_ICON || uView == LVS_SMALLICON)
7961 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7962 LISTVIEW_UpdateScroll(infoPtr);
7963 LISTVIEW_InvalidateList(infoPtr);
7965 return 0;
7968 /***
7969 * DESCRIPTION:
7970 * Resizes the listview control. This function processes WM_SIZE
7971 * messages. At this time, the width and height are not used.
7973 * PARAMETER(S):
7974 * [I] infoPtr : valid pointer to the listview structure
7975 * [I] Width : new width
7976 * [I] Height : new height
7978 * RETURN:
7979 * Zero
7981 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
7983 TRACE("(width=%d, height=%d)\n", Width, Height);
7985 if (!is_redrawing(infoPtr)) return 0;
7987 if (!LISTVIEW_UpdateSize(infoPtr)) return 0;
7989 if (is_autoarrange(infoPtr))
7990 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7992 LISTVIEW_UpdateScroll(infoPtr);
7994 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7996 return 0;
7999 /***
8000 * DESCRIPTION:
8001 * Sets the size information.
8003 * PARAMETER(S):
8004 * [I] infoPtr : valid pointer to the listview structure
8006 * RETURN:
8007 * Zero if no size change
8008 * 1 of size changed
8010 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8012 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8013 RECT rcList;
8014 RECT rcOld;
8016 GetClientRect(infoPtr->hwndSelf, &rcList);
8017 CopyRect(&rcOld,&(infoPtr->rcList));
8018 infoPtr->rcList.left = 0;
8019 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8020 infoPtr->rcList.top = 0;
8021 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8023 if (uView == LVS_LIST)
8025 /* Apparently the "LIST" style is supposed to have the same
8026 * number of items in a column even if there is no scroll bar.
8027 * Since if a scroll bar already exists then the bottom is already
8028 * reduced, only reduce if the scroll bar does not currently exist.
8029 * The "2" is there to mimic the native control. I think it may be
8030 * related to either padding or edges. (GLA 7/2002)
8032 if (!(infoPtr->dwStyle & WS_HSCROLL))
8034 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8035 if (infoPtr->rcList.bottom > nHScrollHeight)
8036 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8038 else
8040 if (infoPtr->rcList.bottom > 2)
8041 infoPtr->rcList.bottom -= 2;
8044 else if (uView == LVS_REPORT)
8046 HDLAYOUT hl;
8047 WINDOWPOS wp;
8049 hl.prc = &rcList;
8050 hl.pwpos = &wp;
8051 Header_Layout(infoPtr->hwndHeader, &hl);
8053 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8055 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
8056 infoPtr->rcList.top = max(wp.cy, 0);
8058 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8061 /***
8062 * DESCRIPTION:
8063 * Processes WM_STYLECHANGED messages.
8065 * PARAMETER(S):
8066 * [I] infoPtr : valid pointer to the listview structure
8067 * [I] wStyleType : window style type (normal or extended)
8068 * [I] lpss : window style information
8070 * RETURN:
8071 * Zero
8073 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8074 LPSTYLESTRUCT lpss)
8076 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8077 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8079 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8080 wStyleType, lpss->styleOld, lpss->styleNew);
8082 if (wStyleType != GWL_STYLE) return 0;
8084 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8085 /* what if LVS_OWNERDATA changed? */
8086 /* or LVS_SINGLESEL */
8087 /* or LVS_SORT{AS,DES}CENDING */
8089 infoPtr->dwStyle = lpss->styleNew;
8091 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8092 ((lpss->styleNew & WS_HSCROLL) == 0))
8093 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8095 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8096 ((lpss->styleNew & WS_VSCROLL) == 0))
8097 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8099 if (uNewView != uOldView)
8101 SIZE oldIconSize = infoPtr->iconSize;
8102 HIMAGELIST himl;
8104 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8105 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8107 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8108 SetRectEmpty(&infoPtr->rcFocus);
8110 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8111 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8113 if (uNewView == LVS_ICON)
8115 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8117 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8118 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8119 LISTVIEW_SetIconSpacing(infoPtr, 0);
8122 else if (uNewView == LVS_REPORT)
8124 HDLAYOUT hl;
8125 WINDOWPOS wp;
8127 hl.prc = &infoPtr->rcList;
8128 hl.pwpos = &wp;
8129 Header_Layout(infoPtr->hwndHeader, &hl);
8130 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8133 LISTVIEW_UpdateItemSize(infoPtr);
8136 if (uNewView == LVS_REPORT)
8137 ShowWindow(infoPtr->hwndHeader, (LVS_NOCOLUMNHEADER & lpss->styleNew) ? SW_HIDE : SW_SHOWNORMAL);
8139 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8140 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8141 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8143 /* update the size of the client area */
8144 LISTVIEW_UpdateSize(infoPtr);
8146 /* add scrollbars if needed */
8147 LISTVIEW_UpdateScroll(infoPtr);
8149 /* invalidate client area + erase background */
8150 LISTVIEW_InvalidateList(infoPtr);
8152 return 0;
8155 /***
8156 * DESCRIPTION:
8157 * Window procedure of the listview control.
8160 static LRESULT WINAPI
8161 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8163 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8165 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8167 if (!infoPtr && (uMsg != WM_CREATE))
8168 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8170 if (infoPtr)
8172 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8175 switch (uMsg)
8177 case LVM_APPROXIMATEVIEWRECT:
8178 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8179 LOWORD(lParam), HIWORD(lParam));
8180 case LVM_ARRANGE:
8181 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8183 /* case LVM_CANCELEDITLABEL: */
8185 /* case LVM_CREATEDRAGIMAGE: */
8187 case LVM_DELETEALLITEMS:
8188 return LISTVIEW_DeleteAllItems(infoPtr);
8190 case LVM_DELETECOLUMN:
8191 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8193 case LVM_DELETEITEM:
8194 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8196 case LVM_EDITLABELW:
8197 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8199 case LVM_EDITLABELA:
8200 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8202 /* case LVM_ENABLEGROUPVIEW: */
8204 case LVM_ENSUREVISIBLE:
8205 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8207 case LVM_FINDITEMW:
8208 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8210 case LVM_FINDITEMA:
8211 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8213 case LVM_GETBKCOLOR:
8214 return infoPtr->clrBk;
8216 /* case LVM_GETBKIMAGE: */
8218 case LVM_GETCALLBACKMASK:
8219 return infoPtr->uCallbackMask;
8221 case LVM_GETCOLUMNA:
8222 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8224 case LVM_GETCOLUMNW:
8225 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8227 case LVM_GETCOLUMNORDERARRAY:
8228 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8230 case LVM_GETCOLUMNWIDTH:
8231 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8233 case LVM_GETCOUNTPERPAGE:
8234 return LISTVIEW_GetCountPerPage(infoPtr);
8236 case LVM_GETEDITCONTROL:
8237 return (LRESULT)infoPtr->hwndEdit;
8239 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8240 return infoPtr->dwLvExStyle;
8242 /* case LVM_GETGROUPINFO: */
8244 /* case LVM_GETGROUPMETRICS: */
8246 case LVM_GETHEADER:
8247 return (LRESULT)infoPtr->hwndHeader;
8249 case LVM_GETHOTCURSOR:
8250 return (LRESULT)infoPtr->hHotCursor;
8252 case LVM_GETHOTITEM:
8253 return infoPtr->nHotItem;
8255 case LVM_GETHOVERTIME:
8256 return infoPtr->dwHoverTime;
8258 case LVM_GETIMAGELIST:
8259 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8261 /* case LVM_GETINSERTMARK: */
8263 /* case LVM_GETINSERTMARKCOLOR: */
8265 /* case LVM_GETINSERTMARKRECT: */
8267 case LVM_GETISEARCHSTRINGA:
8268 case LVM_GETISEARCHSTRINGW:
8269 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8270 return FALSE;
8272 case LVM_GETITEMA:
8273 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8275 case LVM_GETITEMW:
8276 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8278 case LVM_GETITEMCOUNT:
8279 return infoPtr->nItemCount;
8281 case LVM_GETITEMPOSITION:
8282 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8284 case LVM_GETITEMRECT:
8285 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8287 case LVM_GETITEMSPACING:
8288 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8290 case LVM_GETITEMSTATE:
8291 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8293 case LVM_GETITEMTEXTA:
8294 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8296 case LVM_GETITEMTEXTW:
8297 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8299 case LVM_GETNEXTITEM:
8300 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8302 case LVM_GETNUMBEROFWORKAREAS:
8303 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8304 return 1;
8306 case LVM_GETORIGIN:
8307 if (!lParam) return FALSE;
8308 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8309 return TRUE;
8311 /* case LVM_GETOUTLINECOLOR: */
8313 /* case LVM_GETSELECTEDCOLUMN: */
8315 case LVM_GETSELECTEDCOUNT:
8316 return LISTVIEW_GetSelectedCount(infoPtr);
8318 case LVM_GETSELECTIONMARK:
8319 return infoPtr->nSelectionMark;
8321 case LVM_GETSTRINGWIDTHA:
8322 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8324 case LVM_GETSTRINGWIDTHW:
8325 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8327 case LVM_GETSUBITEMRECT:
8328 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8330 case LVM_GETTEXTBKCOLOR:
8331 return infoPtr->clrTextBk;
8333 case LVM_GETTEXTCOLOR:
8334 return infoPtr->clrText;
8336 /* case LVM_GETTILEINFO: */
8338 /* case LVM_GETTILEVIEWINFO: */
8340 case LVM_GETTOOLTIPS:
8341 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8342 return FALSE;
8344 case LVM_GETTOPINDEX:
8345 return LISTVIEW_GetTopIndex(infoPtr);
8347 /*case LVM_GETUNICODEFORMAT:
8348 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8349 return FALSE;*/
8351 /* case LVM_GETVIEW: */
8353 case LVM_GETVIEWRECT:
8354 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8356 case LVM_GETWORKAREAS:
8357 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8358 return FALSE;
8360 /* case LVM_HASGROUP: */
8362 case LVM_HITTEST:
8363 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8365 case LVM_INSERTCOLUMNA:
8366 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8368 case LVM_INSERTCOLUMNW:
8369 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8371 /* case LVM_INSERTGROUP: */
8373 /* case LVM_INSERTGROUPSORTED: */
8375 case LVM_INSERTITEMA:
8376 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8378 case LVM_INSERTITEMW:
8379 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8381 /* case LVM_INSERTMARKHITTEST: */
8383 /* case LVM_ISGROUPVIEWENABLED: */
8385 /* case LVM_MAPIDTOINDEX: */
8387 /* case LVM_MAPINDEXTOID: */
8389 /* case LVM_MOVEGROUP: */
8391 /* case LVM_MOVEITEMTOGROUP: */
8393 case LVM_REDRAWITEMS:
8394 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8396 /* case LVM_REMOVEALLGROUPS: */
8398 /* case LVM_REMOVEGROUP: */
8400 case LVM_SCROLL:
8401 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8403 case LVM_SETBKCOLOR:
8404 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8406 /* case LVM_SETBKIMAGE: */
8408 case LVM_SETCALLBACKMASK:
8409 infoPtr->uCallbackMask = (UINT)wParam;
8410 return TRUE;
8412 case LVM_SETCOLUMNA:
8413 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8415 case LVM_SETCOLUMNW:
8416 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8418 case LVM_SETCOLUMNORDERARRAY:
8419 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8421 case LVM_SETCOLUMNWIDTH:
8422 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8424 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8425 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8427 /* case LVM_SETGROUPINFO: */
8429 /* case LVM_SETGROUPMETRICS: */
8431 case LVM_SETHOTCURSOR:
8432 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8434 case LVM_SETHOTITEM:
8435 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8437 case LVM_SETHOVERTIME:
8438 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8440 case LVM_SETICONSPACING:
8441 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8443 case LVM_SETIMAGELIST:
8444 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8446 /* case LVM_SETINFOTIP: */
8448 /* case LVM_SETINSERTMARK: */
8450 /* case LVM_SETINSERTMARKCOLOR: */
8452 case LVM_SETITEMA:
8453 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8455 case LVM_SETITEMW:
8456 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8458 case LVM_SETITEMCOUNT:
8459 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8461 case LVM_SETITEMPOSITION:
8463 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8464 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8467 case LVM_SETITEMPOSITION32:
8468 if (lParam == 0) return FALSE;
8469 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8471 case LVM_SETITEMSTATE:
8472 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8474 case LVM_SETITEMTEXTA:
8475 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8477 case LVM_SETITEMTEXTW:
8478 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8480 /* case LVM_SETOUTLINECOLOR: */
8482 /* case LVM_SETSELECTEDCOLUMN: */
8484 case LVM_SETSELECTIONMARK:
8485 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8487 case LVM_SETTEXTBKCOLOR:
8488 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8490 case LVM_SETTEXTCOLOR:
8491 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8493 /* case LVM_SETTILEINFO: */
8495 /* case LVM_SETTILEVIEWINFO: */
8497 /* case LVM_SETTILEWIDTH: */
8499 /* case LVM_SETTOOLTIPS: */
8501 /* case LVM_SETUNICODEFORMAT: */
8503 /* case LVM_SETVIEW: */
8505 /* case LVM_SETWORKAREAS: */
8507 /* case LVM_SORTGROUPS: */
8509 case LVM_SORTITEMS:
8510 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8512 /* LVM_SORTITEMSEX: */
8514 case LVM_SUBITEMHITTEST:
8515 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8517 case LVM_UPDATE:
8518 return LISTVIEW_Update(infoPtr, (INT)wParam);
8520 case WM_CHAR:
8521 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8523 case WM_COMMAND:
8524 return LISTVIEW_Command(infoPtr, wParam, lParam);
8526 case WM_CREATE:
8527 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8529 case WM_ERASEBKGND:
8530 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8532 case WM_GETDLGCODE:
8533 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8535 case WM_GETFONT:
8536 return (LRESULT)infoPtr->hFont;
8538 case WM_HSCROLL:
8539 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8541 case WM_KEYDOWN:
8542 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8544 case WM_KILLFOCUS:
8545 return LISTVIEW_KillFocus(infoPtr);
8547 case WM_LBUTTONDBLCLK:
8548 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8550 case WM_LBUTTONDOWN:
8551 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8553 case WM_LBUTTONUP:
8554 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8556 case WM_MOUSEMOVE:
8557 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8559 case WM_MOUSEHOVER:
8560 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8562 case WM_NCDESTROY:
8563 return LISTVIEW_NCDestroy(infoPtr);
8565 case WM_NOTIFY:
8566 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8567 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8568 else return 0;
8570 case WM_NOTIFYFORMAT:
8571 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8573 case WM_PAINT:
8574 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8576 case WM_RBUTTONDBLCLK:
8577 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8579 case WM_RBUTTONDOWN:
8580 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8582 case WM_RBUTTONUP:
8583 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8585 case WM_SETCURSOR:
8586 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8587 return TRUE;
8588 goto fwd_msg;
8590 case WM_SETFOCUS:
8591 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8593 case WM_SETFONT:
8594 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8596 case WM_SETREDRAW:
8597 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8599 case WM_SIZE:
8600 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8602 case WM_STYLECHANGED:
8603 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8605 case WM_SYSCOLORCHANGE:
8606 COMCTL32_RefreshSysColors();
8607 return 0;
8609 /* case WM_TIMER: */
8611 case WM_VSCROLL:
8612 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8614 case WM_MOUSEWHEEL:
8615 if (wParam & (MK_SHIFT | MK_CONTROL))
8616 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8617 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8619 case WM_WINDOWPOSCHANGED:
8620 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
8622 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8623 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8624 /* FIXME: why do we need this here? */
8625 LISTVIEW_UpdateSize(infoPtr);
8626 LISTVIEW_UpdateScroll(infoPtr);
8628 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8630 /* case WM_WININICHANGE: */
8632 default:
8633 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8634 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8636 fwd_msg:
8637 /* call default window procedure */
8638 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8641 return 0;
8644 /***
8645 * DESCRIPTION:
8646 * Registers the window class.
8648 * PARAMETER(S):
8649 * None
8651 * RETURN:
8652 * None
8654 void LISTVIEW_Register(void)
8656 WNDCLASSW wndClass;
8658 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8659 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8660 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8661 wndClass.cbClsExtra = 0;
8662 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8663 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8664 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8665 wndClass.lpszClassName = WC_LISTVIEWW;
8666 RegisterClassW(&wndClass);
8669 /***
8670 * DESCRIPTION:
8671 * Unregisters the window class.
8673 * PARAMETER(S):
8674 * None
8676 * RETURN:
8677 * None
8679 void LISTVIEW_Unregister(void)
8681 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8684 /***
8685 * DESCRIPTION:
8686 * Handle any WM_COMMAND messages
8688 * PARAMETER(S):
8689 * [I] infoPtr : valid pointer to the listview structure
8690 * [I] wParam : the first message parameter
8691 * [I] lParam : the second message parameter
8693 * RETURN:
8694 * Zero.
8696 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8698 switch (HIWORD(wParam))
8700 case EN_UPDATE:
8703 * Adjust the edit window size
8705 WCHAR buffer[1024];
8706 HDC hdc = GetDC(infoPtr->hwndEdit);
8707 HFONT hFont, hOldFont = 0;
8708 RECT rect;
8709 SIZE sz;
8710 int len;
8712 if (!infoPtr->hwndEdit || !hdc) return 0;
8713 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8714 GetWindowRect(infoPtr->hwndEdit, &rect);
8716 /* Select font to get the right dimension of the string */
8717 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8718 if(hFont != 0)
8720 hOldFont = SelectObject(hdc, hFont);
8723 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8725 TEXTMETRICW textMetric;
8727 /* Add Extra spacing for the next character */
8728 GetTextMetricsW(hdc, &textMetric);
8729 sz.cx += (textMetric.tmMaxCharWidth * 2);
8731 SetWindowPos (
8732 infoPtr->hwndEdit,
8733 HWND_TOP,
8736 sz.cx,
8737 rect.bottom - rect.top,
8738 SWP_DRAWFRAME|SWP_NOMOVE);
8740 if(hFont != 0)
8741 SelectObject(hdc, hOldFont);
8743 ReleaseDC(infoPtr->hwndSelf, hdc);
8745 break;
8748 default:
8749 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8752 return 0;
8756 /***
8757 * DESCRIPTION:
8758 * Subclassed edit control windproc function
8760 * PARAMETER(S):
8761 * [I] hwnd : the edit window handle
8762 * [I] uMsg : the message that is to be processed
8763 * [I] wParam : first message parameter
8764 * [I] lParam : second message parameter
8765 * [I] isW : TRUE if input is Unicode
8767 * RETURN:
8768 * Zero.
8770 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
8772 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8773 BOOL cancel = FALSE;
8775 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8776 hwnd, uMsg, wParam, lParam, isW);
8778 switch (uMsg)
8780 case WM_GETDLGCODE:
8781 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8783 case WM_KILLFOCUS:
8784 break;
8786 case WM_DESTROY:
8788 WNDPROC editProc = infoPtr->EditWndProc;
8789 infoPtr->EditWndProc = 0;
8790 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8791 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8794 case WM_KEYDOWN:
8795 if (VK_ESCAPE == (INT)wParam)
8797 cancel = TRUE;
8798 break;
8800 else if (VK_RETURN == (INT)wParam)
8801 break;
8803 default:
8804 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8807 /* kill the edit */
8808 if (infoPtr->hwndEdit)
8810 LPWSTR buffer = NULL;
8812 infoPtr->hwndEdit = 0;
8813 if (!cancel)
8815 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8817 if (len)
8819 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8821 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8822 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8826 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8828 if (buffer) COMCTL32_Free(buffer);
8832 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8833 return 0;
8836 /***
8837 * DESCRIPTION:
8838 * Subclassed edit control Unicode windproc function
8840 * PARAMETER(S):
8841 * [I] hwnd : the edit window handle
8842 * [I] uMsg : the message that is to be processed
8843 * [I] wParam : first message parameter
8844 * [I] lParam : second message parameter
8846 * RETURN:
8848 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8850 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8853 /***
8854 * DESCRIPTION:
8855 * Subclassed edit control ANSI windproc function
8857 * PARAMETER(S):
8858 * [I] hwnd : the edit window handle
8859 * [I] uMsg : the message that is to be processed
8860 * [I] wParam : first message parameter
8861 * [I] lParam : second message parameter
8863 * RETURN:
8865 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8867 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8870 /***
8871 * DESCRIPTION:
8872 * Creates a subclassed edit cotrol
8874 * PARAMETER(S):
8875 * [I] infoPtr : valid pointer to the listview structure
8876 * [I] text : initial text for the edit
8877 * [I] style : the window style
8878 * [I] isW : TRUE if input is Unicode
8880 * RETURN:
8882 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8883 INT x, INT y, INT width, INT height, BOOL isW)
8885 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8886 HWND hedit;
8887 SIZE sz;
8888 HDC hdc;
8889 HDC hOldFont=0;
8890 TEXTMETRICW textMetric;
8891 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8893 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8895 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8896 hdc = GetDC(infoPtr->hwndSelf);
8898 /* Select the font to get appropriate metric dimensions */
8899 if(infoPtr->hFont != 0)
8900 hOldFont = SelectObject(hdc, infoPtr->hFont);
8902 /*Get String Lenght in pixels */
8903 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8905 /*Add Extra spacing for the next character */
8906 GetTextMetricsW(hdc, &textMetric);
8907 sz.cx += (textMetric.tmMaxCharWidth * 2);
8909 if(infoPtr->hFont != 0)
8910 SelectObject(hdc, hOldFont);
8912 ReleaseDC(infoPtr->hwndSelf, hdc);
8913 if (isW)
8914 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8915 else
8916 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8918 if (!hedit) return 0;
8920 infoPtr->EditWndProc = (WNDPROC)
8921 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8922 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8924 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
8926 return hedit;