Rename LISTVIEW_ITEM to ITEM_INFO for consistency.
[wine/multimedia.git] / dlls / comctl32 / listview.c
blob3a652151f59bb56b9ab70be7a78535af1319ecf5
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:
34 * -- Hot item handling.
35 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
36 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs)
37 * -- work areas
38 * -- tilemode
39 * -- groups
40 * -- FIXMEs (search for them)
42 * States
43 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
44 * -- LVIS_CUT
45 * -- LVIS_DROPHILITED
46 * -- LVIS_OVERLAYMASK
48 * Styles
49 * -- LVS_NOLABELWRAP
50 * -- LVS_NOSCROLL (see Q137520)
51 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
53 * Extended Styles
54 * -- LVS_EX_BORDERSELECT
55 * -- LVS_EX_CHECKBOXES
56 * -- LVS_EX_FLATSB
57 * -- LVS_EX_GRIDLINES
58 * -- LVS_EX_HEADERDRAGDROP
59 * -- LVS_EX_INFOTIP
60 * -- LVS_EX_LABELTIP
61 * -- LVS_EX_MULTIWORKAREAS
62 * -- LVS_EX_ONECLICKACTIVATE
63 * -- LVS_EX_REGIONAL
64 * -- LVS_EX_SIMPLESELECT
65 * -- LVS_EX_SUBITEMIMAGES
66 * -- LVS_EX_TRACKSELECT
67 * -- LVS_EX_TWOCLICKACTIVATE
68 * -- LVS_EX_UNDERLINECOLD
69 * -- LVS_EX_UNDERLINEHOT
71 * Notifications:
72 * -- LVN_BEGINDRAG, LVN_BEGINRDRAG
73 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
74 * -- LVN_GETINFOTIP
75 * -- LVN_HOTTRACK
76 * -- LVN_MARQUEEBEGIN
77 * -- LVN_ODFINDITEM
78 * -- LVN_ODSTATECHANGED
79 * -- LVN_SETDISPINFO
80 * -- NM_HOVER
82 * Messages:
83 * -- LVM_CANCELEDITLABEL
84 * -- LVM_CREATEDRAGIMAGE
85 * -- LVM_ENABLEGROUPVIEW
86 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
87 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
88 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
89 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
90 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
91 * -- LVM_GETINSERTMARKRECT
92 * -- LVM_GETNUMBEROFWORKAREAS
93 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
94 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
95 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
96 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
97 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
98 * -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
99 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
100 * -- LVM_GETVIEW, LVM_SETVIEW
101 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
102 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
103 * -- LVM_INSERTGROUPSORTED
104 * -- LVM_INSERTMARKHITTEST
105 * -- LVM_ISGROUPVIEWENABLED
106 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
107 * -- LVM_MOVEGROUP
108 * -- LVM_MOVEITEMTOGROUP
109 * -- LVM_SETINFOTIP
110 * -- LVM_SETTILEWIDTH
111 * -- LVM_SORTGROUPS
112 * -- LVM_SORTITEMSEX
114 * Known differences in message stream from native control (not known if
115 * these differences cause problems):
116 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
117 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
118 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
119 * processing for "USEDOUBLECLICKTIME".
122 #include "config.h"
123 #include "wine/port.h"
125 #include <assert.h>
126 #include <ctype.h>
127 #include <string.h>
128 #include <stdlib.h>
129 #include <stdio.h>
131 #include "winbase.h"
132 #include "winnt.h"
133 #include "heap.h"
134 #include "commctrl.h"
135 #include "comctl32.h"
137 #include "wine/debug.h"
138 #include "wine/unicode.h"
140 WINE_DEFAULT_DEBUG_CHANNEL(listview);
142 /* make sure you set this to 0 for production use! */
143 #define DEBUG_RANGES 1
145 typedef struct tagCOLUMN_INFO
147 RECT rcHeader; /* tracks the header's rectangle */
148 int fmt; /* same as LVCOLUMN.fmt */
149 } COLUMN_INFO;
151 typedef struct tagITEMHDR
153 LPWSTR pszText;
154 INT iImage;
155 } ITEMHDR, *LPITEMHDR;
157 typedef struct tagSUBITEM_INFO
159 ITEMHDR hdr;
160 INT iSubItem;
161 } SUBITEM_INFO;
163 typedef struct tagITEM_INFO
165 ITEMHDR hdr;
166 UINT state;
167 LPARAM lParam;
168 INT iIndent;
169 } ITEM_INFO;
171 typedef struct tagRANGE
173 INT lower;
174 INT upper;
175 } RANGE;
177 typedef struct tagRANGES
179 HDPA hdpa;
180 } *RANGES;
182 typedef struct tagITERATOR
184 INT nItem;
185 INT nSpecial;
186 RANGE range;
187 RANGES ranges;
188 INT index;
189 } ITERATOR;
191 typedef struct tagLISTVIEW_INFO
193 HWND hwndSelf;
194 HBRUSH hBkBrush;
195 COLORREF clrBk;
196 COLORREF clrText;
197 COLORREF clrTextBk;
198 COLORREF clrTextBkDefault;
199 HIMAGELIST himlNormal;
200 HIMAGELIST himlSmall;
201 HIMAGELIST himlState;
202 BOOL bLButtonDown;
203 BOOL bRButtonDown;
204 INT nItemHeight;
205 INT nItemWidth;
206 RANGES selectionRanges;
207 INT nSelectionMark;
208 INT nHotItem;
209 SHORT notifyFormat;
210 RECT rcList; /* This rectangle is really the window
211 * client rectangle possibly reduced by the
212 * horizontal scroll bar and/or header - see
213 * LISTVIEW_UpdateSize. This rectangle offset
214 * by the LISTVIEW_GetOrigin value is in
215 * client coordinates */
216 SIZE iconSize;
217 SIZE iconSpacing;
218 SIZE iconStateSize;
219 UINT uCallbackMask;
220 HWND hwndHeader;
221 HFONT hDefaultFont;
222 HCURSOR hHotCursor;
223 HFONT hFont;
224 INT ntmHeight; /* from GetTextMetrics from above font */
225 BOOL bRedraw;
226 BOOL bFocus;
227 INT nFocusedItem;
228 RECT rcFocus;
229 DWORD dwStyle; /* the cached window GWL_STYLE */
230 DWORD dwLvExStyle; /* extended listview style */
231 INT nItemCount; /* the number of items in the list */
232 HDPA hdpaItems; /* array ITEM_INFO pointers */
233 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
234 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
235 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
236 PFNLVCOMPARE pfnCompare;
237 LPARAM lParamSort;
238 HWND hwndEdit;
239 WNDPROC EditWndProc;
240 INT nEditLabelItem;
241 DWORD dwHoverTime;
243 DWORD lastKeyPressTimestamp;
244 WPARAM charCode;
245 INT nSearchParamLength;
246 WCHAR szSearchParam[ MAX_PATH ];
247 BOOL bIsDrawing;
248 } LISTVIEW_INFO;
251 * constants
253 /* How many we debug buffer to allocate */
254 #define DEBUG_BUFFERS 20
255 /* The size of a single debug bbuffer */
256 #define DEBUG_BUFFER_SIZE 256
258 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
259 #define SB_INTERNAL -1
261 /* maximum size of a label */
262 #define DISP_TEXT_SIZE 512
264 /* padding for items in list and small icon display modes */
265 #define WIDTH_PADDING 12
267 /* padding for items in list, report and small icon display modes */
268 #define HEIGHT_PADDING 1
270 /* offset of items in report display mode */
271 #define REPORT_MARGINX 2
273 /* padding for icon in large icon display mode
274 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
275 * that HITTEST will see.
276 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
277 * ICON_TOP_PADDING - sum of the two above.
278 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
279 * LABEL_VERT_PADDING - between bottom of text and end of box
281 * ICON_LR_PADDING - additional width above icon size.
282 * ICON_LR_HALF - half of the above value
284 #define ICON_TOP_PADDING_NOTHITABLE 2
285 #define ICON_TOP_PADDING_HITABLE 2
286 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
287 #define ICON_BOTTOM_PADDING 4
288 #define LABEL_VERT_PADDING 7
289 #define ICON_LR_PADDING 16
290 #define ICON_LR_HALF (ICON_LR_PADDING/2)
292 /* default label width for items in list and small icon display modes */
293 #define DEFAULT_LABEL_WIDTH 40
295 /* default column width for items in list display mode */
296 #define DEFAULT_COLUMN_WIDTH 128
298 /* Size of "line" scroll for V & H scrolls */
299 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
301 /* Padding betwen image and label */
302 #define IMAGE_PADDING 2
304 /* Padding behind the label */
305 #define TRAILING_PADDING 5
307 /* Border for the icon caption */
308 #define CAPTION_BORDER 2
310 /* Standard DrawText flags */
311 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
312 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
313 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
315 /* The time in milisecods to reset the search in the list */
316 #define KEY_DELAY 450
318 /* Dump the LISTVIEW_INFO structure to the debug channel */
319 #define LISTVIEW_DUMP(iP) do { \
320 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
321 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
322 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
323 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
324 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
325 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
326 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
327 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
328 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
329 TRACE("hwndSelf=%08x, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
330 } while(0)
334 * forward declarations
336 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
337 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
338 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
339 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
340 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
341 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
342 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
343 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
344 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
345 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
346 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
347 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
348 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
349 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
350 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
351 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
352 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
353 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
354 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
355 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
356 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
357 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
358 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
359 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
360 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
361 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
363 /******** Text handling functions *************************************/
365 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
366 * text string. The string may be ANSI or Unicode, in which case
367 * the boolean isW tells us the type of the string.
369 * The name of the function tell what type of strings it expects:
370 * W: Unicode, T: ANSI/Unicode - function of isW
373 static inline BOOL is_textW(LPCWSTR text)
375 return text != NULL && text != LPSTR_TEXTCALLBACKW;
378 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
380 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
381 return is_textW(text);
384 static inline int textlenT(LPCWSTR text, BOOL isW)
386 return !is_textT(text, isW) ? 0 :
387 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
390 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
392 if (isDestW)
393 if (isSrcW) lstrcpynW(dest, src, max);
394 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
395 else
396 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
397 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
400 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
402 LPWSTR wstr = (LPWSTR)text;
404 if (!isW && is_textT(text, isW))
406 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
407 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
408 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
410 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
411 return wstr;
414 static inline void textfreeT(LPWSTR wstr, BOOL isW)
416 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
420 * dest is a pointer to a Unicode string
421 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
423 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
425 BOOL bResult = TRUE;
427 if (src == LPSTR_TEXTCALLBACKW)
429 if (is_textW(*dest)) COMCTL32_Free(*dest);
430 *dest = LPSTR_TEXTCALLBACKW;
432 else
434 LPWSTR pszText = textdupTtoW(src, isW);
435 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
436 bResult = Str_SetPtrW(dest, pszText);
437 textfreeT(pszText, isW);
439 return bResult;
443 * compares a Unicode to a Unicode/ANSI text string
445 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
447 if (!aw) return bt ? -1 : 0;
448 if (!bt) return aw ? 1 : 0;
449 if (aw == LPSTR_TEXTCALLBACKW)
450 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
451 if (bt != LPSTR_TEXTCALLBACKW)
453 LPWSTR bw = textdupTtoW(bt, isW);
454 int r = bw ? lstrcmpW(aw, bw) : 1;
455 textfreeT(bw, isW);
456 return r;
459 return 1;
462 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
464 int res;
466 n = min(min(n, strlenW(s1)), strlenW(s2));
467 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
468 return res ? res - sizeof(WCHAR) : res;
471 /******** Debugging functions *****************************************/
473 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
475 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
476 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
479 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
481 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
482 n = min(textlenT(text, isW), n);
483 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
486 static char* debug_getbuf()
488 static int index = 0;
489 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
490 return buffers[index++ % DEBUG_BUFFERS];
493 static inline char* debugrange(const RANGE* lprng)
495 if (lprng)
497 char* buf = debug_getbuf();
498 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
499 return buf;
500 } else return "(null)";
503 static inline char* debugpoint(const POINT* lppt)
505 if (lppt)
507 char* buf = debug_getbuf();
508 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
509 return buf;
510 } else return "(null)";
513 static inline char* debugrect(const RECT* rect)
515 if (rect)
517 char* buf = debug_getbuf();
518 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
519 rect->left, rect->top, rect->right, rect->bottom);
520 return buf;
521 } else return "(null)";
524 static char* debugnmlistview(LPNMLISTVIEW plvnm)
526 if (plvnm)
528 char* buf = debug_getbuf();
529 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
530 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
531 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
532 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
533 return buf;
534 } else return "(null)";
537 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
539 char* buf = debug_getbuf(), *text = buf;
540 int len, size = DEBUG_BUFFER_SIZE;
542 if (lpLVItem == NULL) return "(null)";
543 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
544 if (len == -1) goto end; buf += len; size -= len;
545 if (lpLVItem->mask & LVIF_STATE)
546 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
547 else len = 0;
548 if (len == -1) goto end; buf += len; size -= len;
549 if (lpLVItem->mask & LVIF_TEXT)
550 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
551 else len = 0;
552 if (len == -1) goto end; buf += len; size -= len;
553 if (lpLVItem->mask & LVIF_IMAGE)
554 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
555 else len = 0;
556 if (len == -1) goto end; buf += len; size -= len;
557 if (lpLVItem->mask & LVIF_PARAM)
558 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
559 else len = 0;
560 if (len == -1) goto end; buf += len; size -= len;
561 if (lpLVItem->mask & LVIF_INDENT)
562 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
563 else len = 0;
564 if (len == -1) goto end; buf += len; size -= len;
565 goto undo;
566 end:
567 buf = text + strlen(text);
568 undo:
569 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
570 return text;
573 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
575 char* buf = debug_getbuf(), *text = buf;
576 int len, size = DEBUG_BUFFER_SIZE;
578 if (lpColumn == NULL) return "(null)";
579 len = snprintf(buf, size, "{");
580 if (len == -1) goto end; buf += len; size -= len;
581 if (lpColumn->mask & LVCF_SUBITEM)
582 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
583 else len = 0;
584 if (len == -1) goto end; buf += len; size -= len;
585 if (lpColumn->mask & LVCF_FMT)
586 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
587 else len = 0;
588 if (len == -1) goto end; buf += len; size -= len;
589 if (lpColumn->mask & LVCF_WIDTH)
590 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
591 else len = 0;
592 if (len == -1) goto end; buf += len; size -= len;
593 if (lpColumn->mask & LVCF_TEXT)
594 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
595 else len = 0;
596 if (len == -1) goto end; buf += len; size -= len;
597 if (lpColumn->mask & LVCF_IMAGE)
598 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
599 else len = 0;
600 if (len == -1) goto end; buf += len; size -= len;
601 if (lpColumn->mask & LVCF_ORDER)
602 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
603 else len = 0;
604 if (len == -1) goto end; buf += len; size -= len;
605 goto undo;
606 end:
607 buf = text + strlen(text);
608 undo:
609 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
610 return text;
614 /******** Notification functions i************************************/
616 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
618 LRESULT result;
620 TRACE("(code=%d)\n", code);
622 pnmh->hwndFrom = infoPtr->hwndSelf;
623 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
624 pnmh->code = code;
625 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
626 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
628 TRACE(" <= %ld\n", result);
630 return result;
633 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
635 NMHDR nmh;
636 return notify_hdr(infoPtr, code, &nmh);
639 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
641 notify(infoPtr, LVN_ITEMACTIVATE);
644 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
646 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
647 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
650 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
652 NMLISTVIEW nmlv;
654 ZeroMemory(&nmlv, sizeof(nmlv));
655 nmlv.iItem = lvht->iItem;
656 nmlv.iSubItem = lvht->iSubItem;
657 nmlv.ptAction = lvht->pt;
658 return notify_listview(infoPtr, code, &nmlv);
661 static int get_ansi_notification(INT unicodeNotificationCode)
663 switch (unicodeNotificationCode)
665 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
666 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
667 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
668 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
669 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
670 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
672 ERR("unknown notification %x\n", unicodeNotificationCode);
673 assert(FALSE);
677 Send notification. depends on dispinfoW having same
678 structure as dispinfoA.
679 infoPtr : listview struct
680 notificationCode : *Unicode* notification code
681 pdi : dispinfo structure (can be unicode or ansi)
682 isW : TRUE if dispinfo is Unicode
684 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
686 BOOL bResult = FALSE;
687 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
688 INT realNotifCode;
689 INT cchTempBufMax = 0, savCchTextMax = 0;
690 LPWSTR pszTempBuf = NULL, savPszText = NULL;
692 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
694 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
695 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
698 if (convertToAnsi || convertToUnicode)
700 if (notificationCode != LVN_GETDISPINFOW)
702 cchTempBufMax = convertToUnicode ?
703 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
704 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
706 else
708 cchTempBufMax = pdi->item.cchTextMax;
709 *pdi->item.pszText = 0; /* make sure we don't process garbage */
712 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
713 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
714 if (!pszTempBuf) return FALSE;
715 if (convertToUnicode)
716 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
717 pszTempBuf, cchTempBufMax);
718 else
719 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
720 cchTempBufMax, NULL, NULL);
721 savCchTextMax = pdi->item.cchTextMax;
722 savPszText = pdi->item.pszText;
723 pdi->item.pszText = pszTempBuf;
724 pdi->item.cchTextMax = cchTempBufMax;
727 if (infoPtr->notifyFormat == NFR_ANSI)
728 realNotifCode = get_ansi_notification(notificationCode);
729 else
730 realNotifCode = notificationCode;
731 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
732 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
734 if (convertToUnicode || convertToAnsi)
736 if (convertToUnicode) /* note : pointer can be changed by app ! */
737 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
738 savCchTextMax, NULL, NULL);
739 else
740 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
741 savPszText, savCchTextMax);
742 pdi->item.pszText = savPszText; /* restores our buffer */
743 pdi->item.cchTextMax = savCchTextMax;
744 HeapFree(GetProcessHeap(), 0, pszTempBuf);
746 return bResult;
749 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds)
751 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
752 lpnmlvcd->nmcd.hdc = hdc;
753 lpnmlvcd->nmcd.rc = *rcBounds;
754 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
755 lpnmlvcd->clrText = infoPtr->clrText;
758 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
760 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
761 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
764 /******** Item iterator functions **********************************/
766 static RANGES ranges_create(int count);
767 static void ranges_destroy(RANGES ranges);
768 static BOOL ranges_add(RANGES ranges, RANGE range);
769 static BOOL ranges_del(RANGES ranges, RANGE range);
770 static void ranges_dump(RANGES ranges);
772 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
774 RANGE range = { nItem, nItem + 1 };
776 return ranges_add(ranges, range);
779 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
781 RANGE range = { nItem, nItem + 1 };
783 return ranges_del(ranges, range);
786 /***
787 * ITERATOR DOCUMENTATION
789 * The iterator functions allow for easy, and convenient iteration
790 * over items of iterest in the list. Typically, you create a
791 * iterator, use it, and destroy it, as such:
792 * ITERATOR i;
794 * iterator_xxxitems(&i, ...);
795 * while (iterator_{prev,next}(&i)
797 * //code which uses i.nItem
799 * iterator_destroy(&i);
801 * where xxx is either: framed, or visible.
802 * Note that it is important that the code destroys the iterator
803 * after it's done with it, as the creation of the iterator may
804 * allocate memory, which thus needs to be freed.
806 * You can iterate both forwards, and backwards through the list,
807 * by using iterator_next or iterator_prev respectively.
809 * Lower numbered items are draw on top of higher number items in
810 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
811 * items may overlap). So, to test items, you should use
812 * iterator_next
813 * which lists the items top to bottom (in Z-order).
814 * For drawing items, you should use
815 * iterator_prev
816 * which lists the items bottom to top (in Z-order).
817 * If you keep iterating over the items after the end-of-items
818 * marker (-1) is returned, the iterator will start from the
819 * beginning. Typically, you don't need to test for -1,
820 * because iterator_{next,prev} will return TRUE if more items
821 * are to be iterated over, or FALSE otherwise.
823 * Note: the iterator is defined to be bidirectional. That is,
824 * any number of prev followed by any number of next, or
825 * five versa, should leave the iterator at the same item:
826 * prev * n, next * n = next * n, prev * n
828 * The iterator has a notion of a out-of-order, special item,
829 * which sits at the start of the list. This is used in
830 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
831 * which needs to be first, as it may overlap other items.
833 * The code is a bit messy because we have:
834 * - a special item to deal with
835 * - simple range, or composite range
836 * - empty range.
837 * If find bugs, or want to add features, please make sure you
838 * always check/modify *both* iterator_prev, and iterator_next.
841 /****
842 * This function iterates through the items in increasing order,
843 * but prefixed by the special item, then -1. That is:
844 * special, 1, 2, 3, ..., n, -1.
845 * Each item is listed only once.
847 static inline BOOL iterator_next(ITERATOR* i)
849 if (i->nItem == -1)
851 i->nItem = i->nSpecial;
852 if (i->nItem != -1) return TRUE;
854 if (i->nItem == i->nSpecial)
856 if (i->ranges) i->index = 0;
857 goto pickarange;
860 i->nItem++;
861 testitem:
862 if (i->nItem == i->nSpecial) i->nItem++;
863 if (i->nItem < i->range.upper) return TRUE;
865 pickarange:
866 if (i->ranges)
868 if (i->index < i->ranges->hdpa->nItemCount)
869 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
870 else goto end;
872 else if (i->nItem >= i->range.upper) goto end;
874 i->nItem = i->range.lower;
875 if (i->nItem >= 0) goto testitem;
876 end:
877 i->nItem = -1;
878 return FALSE;
881 /****
882 * This function iterates through the items in decreasing order,
883 * followed by the special item, then -1. That is:
884 * n, n-1, ..., 3, 2, 1, special, -1.
885 * Each item is listed only once.
887 static inline BOOL iterator_prev(ITERATOR* i)
889 BOOL start = FALSE;
891 if (i->nItem == -1)
893 start = TRUE;
894 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
895 goto pickarange;
897 if (i->nItem == i->nSpecial)
899 i->nItem = -1;
900 return FALSE;
903 testitem:
904 i->nItem--;
905 if (i->nItem == i->nSpecial) i->nItem--;
906 if (i->nItem >= i->range.lower) return TRUE;
908 pickarange:
909 if (i->ranges)
911 if (i->index > 0)
912 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
913 else goto end;
915 else if (!start && i->nItem < i->range.lower) goto end;
917 i->nItem = i->range.upper;
918 if (i->nItem > 0) goto testitem;
919 end:
920 return (i->nItem = i->nSpecial) != -1;
923 static RANGE iterator_range(ITERATOR* i)
925 RANGE range;
927 if (!i->ranges) return i->range;
929 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
930 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
931 return range;
934 /***
935 * Releases resources associated with this ierator.
937 static inline void iterator_destroy(ITERATOR* i)
939 if (i->ranges) ranges_destroy(i->ranges);
942 /***
943 * Create an empty iterator.
945 static inline BOOL iterator_empty(ITERATOR* i)
947 ZeroMemory(i, sizeof(*i));
948 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
949 return TRUE;
953 /***
954 * Create an iterator over a bunch of ranges.
955 * Please note that the iterator will take ownership of the ranges,
956 * and will free them upon destruction.
958 static inline BOOL iterator_ranges(ITERATOR* i, RANGES ranges)
960 iterator_empty(i);
961 i->ranges = ranges;
962 return TRUE;
965 /***
966 * Creates an iterator over the items which intersect lprc.
968 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
970 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
971 RECT frame = *lprc, rcItem, rcTemp;
972 POINT Origin;
974 /* in case we fail, we want to return an empty iterator */
975 if (!iterator_empty(i)) return FALSE;
977 LISTVIEW_GetOrigin(infoPtr, &Origin);
979 TRACE("(lprc=%s)\n", debugrect(lprc));
980 OffsetRect(&frame, -Origin.x, -Origin.y);
982 if (uView == LVS_ICON || uView == LVS_SMALLICON)
984 INT nItem;
986 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
988 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
989 if (IntersectRect(&rcTemp, &rcItem, lprc))
990 i->nSpecial = infoPtr->nFocusedItem;
992 if (!(i->ranges = ranges_create(50))) return FALSE;
993 /* to do better here, we need to have PosX, and PosY sorted */
994 TRACE("building icon ranges:\n");
995 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
997 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
998 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
999 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1000 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1001 if (IntersectRect(&rcTemp, &rcItem, &frame))
1002 ranges_additem(i->ranges, nItem);
1004 return TRUE;
1006 else if (uView == LVS_REPORT)
1008 INT lower, upper;
1010 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1011 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1013 lower = max(frame.top / infoPtr->nItemHeight, 0);
1014 upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1);
1015 if (upper < lower) return TRUE;
1016 i->range.lower = lower;
1017 i->range.upper = upper + 1;
1018 TRACE(" report=%s\n", debugrange(&i->range));
1020 else
1022 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1023 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1024 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1025 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1026 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1027 INT lower = nFirstCol * nPerCol + nFirstRow;
1028 RANGE item_range;
1029 INT nCol;
1031 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1032 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1034 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1036 if (!(i->ranges = ranges_create(nLastCol - nFirstCol + 1))) return FALSE;
1037 TRACE("building list ranges:\n");
1038 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1040 item_range.lower = nCol * nPerCol + nFirstRow;
1041 if(item_range.lower >= infoPtr->nItemCount) break;
1042 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1043 TRACE(" list=%s\n", debugrange(&item_range));
1044 ranges_add(i->ranges, item_range);
1048 return TRUE;
1051 /***
1052 * Creates an iterator over the items which intersect the visible region of hdc.
1054 static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
1056 POINT Origin, Position;
1057 RECT rcItem, rcClip;
1058 INT rgntype;
1060 rgntype = GetClipBox(hdc, &rcClip);
1061 if (rgntype == NULLREGION) return iterator_empty(i);
1062 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1063 if (rgntype == SIMPLEREGION) return TRUE;
1065 /* first deal with the special item */
1066 if (i->nSpecial != -1)
1068 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1069 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1072 /* if we can't deal with the region, we'll just go with the simple range */
1073 LISTVIEW_GetOrigin(infoPtr, &Origin);
1074 TRACE("building visible range:\n");
1075 if (!i->ranges && i->range.lower < i->range.upper)
1077 if (!(i->ranges = ranges_create(50))) return TRUE;
1078 if (!ranges_add(i->ranges, i->range))
1080 ranges_destroy(i->ranges);
1081 i->ranges = 0;
1082 return TRUE;
1086 /* now delete the invisible items from the list */
1087 while(iterator_next(i))
1089 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1090 rcItem.left = Position.x + Origin.x;
1091 rcItem.top = Position.y + Origin.y;
1092 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1093 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1094 if (!RectVisible(hdc, &rcItem))
1095 ranges_delitem(i->ranges, i->nItem);
1097 /* the iterator should restart on the next iterator_next */
1098 TRACE("done\n");
1100 return TRUE;
1103 /******** Misc helper functions ************************************/
1105 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1106 WPARAM wParam, LPARAM lParam, BOOL isW)
1108 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1109 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1112 /******** Internal API functions ************************************/
1114 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1116 assert (nSubItem >= 0 && nSubItem < infoPtr->hdpaColumns->nItemCount);
1117 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1120 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1122 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1125 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1127 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1130 /* The Invalidate* are macros, so we preserve the caller location */
1131 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
1132 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
1133 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
1134 break;\
1137 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
1138 RECT rcBox; \
1139 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox); \
1140 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1141 } while (0)
1143 #define LISTVIEW_InvalidateSubItem(infoPtr, nItem, nSubItem) do { \
1144 POINT Origin, Position; \
1145 RECT rcBox; \
1146 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT); \
1147 LISTVIEW_GetOrigin(infoPtr, &Origin); \
1148 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); \
1149 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox); \
1150 rcBox.top = 0; \
1151 rcBox.bottom = infoPtr->nItemHeight; \
1152 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y); \
1153 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1154 } while (0)
1156 #define LISTVIEW_InvalidateList(infoPtr)\
1157 LISTVIEW_InvalidateRect(infoPtr, NULL)
1160 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1162 RECT rcCol;
1164 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1165 rcCol.top = infoPtr->rcList.top;
1166 rcCol.bottom = infoPtr->rcList.bottom;
1167 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1170 /***
1171 * DESCRIPTION:
1172 * Retrieves the number of items that can fit vertically in the client area.
1174 * PARAMETER(S):
1175 * [I] infoPtr : valid pointer to the listview structure
1177 * RETURN:
1178 * Number of items per row.
1180 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1182 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1184 return max(nListWidth/infoPtr->nItemWidth, 1);
1187 /***
1188 * DESCRIPTION:
1189 * Retrieves the number of items that can fit horizontally in the client
1190 * area.
1192 * PARAMETER(S):
1193 * [I] infoPtr : valid pointer to the listview structure
1195 * RETURN:
1196 * Number of items per column.
1198 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1200 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1202 return max(nListHeight / infoPtr->nItemHeight, 1);
1206 /*************************************************************************
1207 * LISTVIEW_ProcessLetterKeys
1209 * Processes keyboard messages generated by pressing the letter keys
1210 * on the keyboard.
1211 * What this does is perform a case insensitive search from the
1212 * current position with the following quirks:
1213 * - If two chars or more are pressed in quick succession we search
1214 * for the corresponding string (e.g. 'abc').
1215 * - If there is a delay we wipe away the current search string and
1216 * restart with just that char.
1217 * - If the user keeps pressing the same character, whether slowly or
1218 * fast, so that the search string is entirely composed of this
1219 * character ('aaaaa' for instance), then we search for first item
1220 * that starting with that character.
1221 * - If the user types the above character in quick succession, then
1222 * we must also search for the corresponding string ('aaaaa'), and
1223 * go to that string if there is a match.
1225 * PARAMETERS
1226 * [I] hwnd : handle to the window
1227 * [I] charCode : the character code, the actual character
1228 * [I] keyData : key data
1230 * RETURNS
1232 * Zero.
1234 * BUGS
1236 * - The current implementation has a list of characters it will
1237 * accept and it ignores averything else. In particular it will
1238 * ignore accentuated characters which seems to match what
1239 * Windows does. But I'm not sure it makes sense to follow
1240 * Windows there.
1241 * - We don't sound a beep when the search fails.
1243 * SEE ALSO
1245 * TREEVIEW_ProcessLetterKeys
1247 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1249 INT nItem;
1250 INT endidx,idx;
1251 LVITEMW item;
1252 WCHAR buffer[MAX_PATH];
1253 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1255 /* simple parameter checking */
1256 if (!charCode || !keyData) return 0;
1258 /* only allow the valid WM_CHARs through */
1259 if (!isalnum(charCode) &&
1260 charCode != '.' && charCode != '`' && charCode != '!' &&
1261 charCode != '@' && charCode != '#' && charCode != '$' &&
1262 charCode != '%' && charCode != '^' && charCode != '&' &&
1263 charCode != '*' && charCode != '(' && charCode != ')' &&
1264 charCode != '-' && charCode != '_' && charCode != '+' &&
1265 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1266 charCode != '}' && charCode != '[' && charCode != '{' &&
1267 charCode != '/' && charCode != '?' && charCode != '>' &&
1268 charCode != '<' && charCode != ',' && charCode != '~')
1269 return 0;
1271 /* if there's one item or less, there is no where to go */
1272 if (infoPtr->nItemCount <= 1) return 0;
1274 /* update the search parameters */
1275 infoPtr->lastKeyPressTimestamp = GetTickCount();
1276 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1277 if (infoPtr->nSearchParamLength < MAX_PATH)
1278 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1279 if (infoPtr->charCode != charCode)
1280 infoPtr->charCode = charCode = 0;
1281 } else {
1282 infoPtr->charCode=charCode;
1283 infoPtr->szSearchParam[0]=charCode;
1284 infoPtr->nSearchParamLength=1;
1285 /* Redundant with the 1 char string */
1286 charCode=0;
1289 /* and search from the current position */
1290 nItem=-1;
1291 if (infoPtr->nFocusedItem >= 0) {
1292 endidx=infoPtr->nFocusedItem;
1293 idx=endidx;
1294 /* if looking for single character match,
1295 * then we must always move forward
1297 if (infoPtr->nSearchParamLength == 1)
1298 idx++;
1299 } else {
1300 endidx=infoPtr->nItemCount;
1301 idx=0;
1303 do {
1304 if (idx == infoPtr->nItemCount) {
1305 if (endidx == infoPtr->nItemCount || endidx == 0)
1306 break;
1307 idx=0;
1310 /* get item */
1311 item.mask = LVIF_TEXT;
1312 item.iItem = idx;
1313 item.iSubItem = 0;
1314 item.pszText = buffer;
1315 item.cchTextMax = MAX_PATH;
1316 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1318 /* check for a match */
1319 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1320 nItem=idx;
1321 break;
1322 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1323 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1324 /* This would work but we must keep looking for a longer match */
1325 nItem=idx;
1327 idx++;
1328 } while (idx != endidx);
1330 if (nItem != -1)
1331 LISTVIEW_KeySelection(infoPtr, nItem);
1333 return 0;
1336 /*************************************************************************
1337 * LISTVIEW_UpdateHeaderSize [Internal]
1339 * Function to resize the header control
1341 * PARAMS
1342 * [I] hwnd : handle to a window
1343 * [I] nNewScrollPos : scroll pos to set
1345 * RETURNS
1346 * None.
1348 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1350 RECT winRect;
1351 POINT point[2];
1353 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1355 GetWindowRect(infoPtr->hwndHeader, &winRect);
1356 point[0].x = winRect.left;
1357 point[0].y = winRect.top;
1358 point[1].x = winRect.right;
1359 point[1].y = winRect.bottom;
1361 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1362 point[0].x = -nNewScrollPos;
1363 point[1].x += nNewScrollPos;
1365 SetWindowPos(infoPtr->hwndHeader,0,
1366 point[0].x,point[0].y,point[1].x,point[1].y,
1367 SWP_NOZORDER | SWP_NOACTIVATE);
1370 /***
1371 * DESCRIPTION:
1372 * Update the scrollbars. This functions should be called whenever
1373 * the content, size or view changes.
1375 * PARAMETER(S):
1376 * [I] infoPtr : valid pointer to the listview structure
1378 * RETURN:
1379 * None
1381 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1383 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1384 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1385 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1386 SCROLLINFO scrollInfo;
1388 if (infoPtr->dwStyle & LVS_NOSCROLL) return;
1390 scrollInfo.cbSize = sizeof(SCROLLINFO);
1392 if (uView == LVS_LIST)
1394 /* update horizontal scrollbar */
1395 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1396 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1398 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1399 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1401 scrollInfo.nMin = 0;
1402 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1403 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1404 scrollInfo.nMax--;
1405 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1406 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1407 scrollInfo.nPage = nCountPerRow;
1408 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1409 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1410 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1412 else if (uView == LVS_REPORT)
1414 BOOL test;
1416 /* update vertical scrollbar */
1417 scrollInfo.nMin = 0;
1418 scrollInfo.nMax = infoPtr->nItemCount - 1;
1419 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1420 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1421 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1422 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1423 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1424 scrollInfo.nMax, scrollInfo.nPage, test);
1425 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1426 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1428 /* update horizontal scrollbar */
1429 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1430 scrollInfo.fMask = SIF_POS;
1431 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1432 || infoPtr->nItemCount == 0)
1434 scrollInfo.nPos = 0;
1436 scrollInfo.nMin = 0;
1437 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1438 scrollInfo.nPage = nListWidth;
1439 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1440 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1441 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1442 scrollInfo.nMax, scrollInfo.nPage, test);
1443 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1444 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1446 /* Update the Header Control */
1447 scrollInfo.fMask = SIF_POS;
1448 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1449 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1452 else
1454 RECT rcView;
1456 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1458 INT nViewWidth = rcView.right - rcView.left;
1459 INT nViewHeight = rcView.bottom - rcView.top;
1461 /* Update Horizontal Scrollbar */
1462 scrollInfo.fMask = SIF_POS;
1463 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1464 || infoPtr->nItemCount == 0)
1466 scrollInfo.nPos = 0;
1468 scrollInfo.nMin = 0;
1469 scrollInfo.nMax = max(nViewWidth, 0)-1;
1470 scrollInfo.nPage = nListWidth;
1471 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1472 TRACE("LVS_ICON/SMALLICON Horz.\n");
1473 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1475 /* Update Vertical Scrollbar */
1476 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1477 scrollInfo.fMask = SIF_POS;
1478 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1479 || infoPtr->nItemCount == 0)
1481 scrollInfo.nPos = 0;
1483 scrollInfo.nMin = 0;
1484 scrollInfo.nMax = max(nViewHeight,0)-1;
1485 scrollInfo.nPage = nListHeight;
1486 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1487 TRACE("LVS_ICON/SMALLICON Vert.\n");
1488 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1494 /***
1495 * DESCRIPTION:
1496 * Shows/hides the focus rectangle.
1498 * PARAMETER(S):
1499 * [I] infoPtr : valid pointer to the listview structure
1500 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1502 * RETURN:
1503 * None
1505 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1507 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1508 HDC hdc;
1510 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1512 if (infoPtr->nFocusedItem < 0) return;
1514 /* we need some gymnastics in ICON mode to handle large items */
1515 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1517 RECT rcBox;
1519 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1520 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1522 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1523 return;
1527 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1529 /* for some reason, owner draw should work only in report mode */
1530 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1532 DRAWITEMSTRUCT dis;
1533 LVITEMW item;
1535 item.iItem = infoPtr->nFocusedItem;
1536 item.iSubItem = 0;
1537 item.mask = LVIF_PARAM;
1538 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1540 ZeroMemory(&dis, sizeof(dis));
1541 dis.CtlType = ODT_LISTVIEW;
1542 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1543 dis.itemID = item.iItem;
1544 dis.itemAction = ODA_FOCUS;
1545 if (fShow) dis.itemState |= ODS_FOCUS;
1546 dis.hwndItem = infoPtr->hwndSelf;
1547 dis.hDC = hdc;
1548 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1549 dis.itemData = item.lParam;
1551 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1553 else
1555 DrawFocusRect(hdc, &infoPtr->rcFocus);
1557 done:
1558 ReleaseDC(infoPtr->hwndSelf, hdc);
1561 /***
1562 * Invalidates all visible selected items.
1564 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1566 ITERATOR i;
1568 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1569 while(iterator_next(&i))
1571 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1572 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1574 iterator_destroy(&i);
1578 /***
1579 * DESCRIPTION: [INTERNAL]
1580 * Computes an item's (left,top) corner, relative to rcView.
1581 * That is, the position has NOT been made relative to the Origin.
1582 * This is deliberate, to avoid computing the Origin over, and
1583 * over again, when this function is call in a loop. Instead,
1584 * one ca factor the computation of the Origin before the loop,
1585 * and offset the value retured by this function, on every iteration.
1587 * PARAMETER(S):
1588 * [I] infoPtr : valid pointer to the listview structure
1589 * [I] nItem : item number
1590 * [O] lpptOrig : item top, left corner
1592 * RETURN:
1593 * None.
1595 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1597 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1599 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1601 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1603 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1604 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1606 else if (uView == LVS_LIST)
1608 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1609 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1610 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1612 else /* LVS_REPORT */
1614 lpptPosition->x = REPORT_MARGINX;
1615 lpptPosition->y = nItem * infoPtr->nItemHeight;
1619 /***
1620 * DESCRIPTION: [INTERNAL]
1621 * Compute the rectangles of an item. This is to localize all
1622 * the computations in one place. If you are not interested in some
1623 * of these values, simply pass in a NULL -- the fucntion is smart
1624 * enough to compute only what's necessary. The function computes
1625 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1626 * one, the BOX rectangle. This rectangle is very cheap to compute,
1627 * and is guaranteed to contain all the other rectangles. Computing
1628 * the ICON rect is also cheap, but all the others are potentaily
1629 * expensive. This gives an easy and effective optimization when
1630 * searching (like point inclusion, or rectangle intersection):
1631 * first test against the BOX, and if TRUE, test agains the desired
1632 * rectangle.
1633 * If the function does not have all the necessary information
1634 * to computed the requested rectangles, will crash with a
1635 * failed assertion. This is done so we catch all programming
1636 * errors, given that the function is called only from our code.
1638 * We have the following 'special' meanings for a few fields:
1639 * * If LVIS_FOCUSED is set, we assume the item has the focus
1640 * This is important in ICON mode, where it might get a larger
1641 * then usual rectange
1643 * Please note that subitem support works only in REPORT mode.
1645 * PARAMETER(S):
1646 * [I] infoPtr : valid pointer to the listview structure
1647 * [I] lpLVItem : item to compute the measures for
1648 * [O] lprcBox : ptr to Box rectangle
1649 * The internal LVIR_BOX rectangle
1650 * [0] lprcState : ptr to State icon rectangle
1651 * The internal LVIR_STATE rectangle
1652 * [O] lprcIcon : ptr to Icon rectangle
1653 * Same as LVM_GETITEMRECT with LVIR_ICON
1654 * [O] lprcLabel : ptr to Label rectangle
1655 * Same as LVM_GETITEMRECT with LVIR_LABEL
1657 * RETURN:
1658 * None.
1660 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1661 LPRECT lprcBox, LPRECT lprcState,
1662 LPRECT lprcIcon, LPRECT lprcLabel)
1664 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1665 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1666 RECT Box, State, Icon, Label;
1667 COLUMN_INFO *lpColumnInfo = NULL;
1669 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1671 /* Be smart and try to figure out the minimum we have to do */
1672 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1673 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1675 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1676 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1678 if (lprcLabel) doLabel = TRUE;
1679 if (doLabel || lprcIcon) doIcon = TRUE;
1680 if (doIcon || lprcState) doState = TRUE;
1682 /************************************************************/
1683 /* compute the box rectangle (it should be cheap to do) */
1684 /************************************************************/
1685 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1686 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1688 if (lpLVItem->iSubItem)
1690 Box = lpColumnInfo->rcHeader;
1692 else
1694 Box.left = 0;
1695 Box.right = infoPtr->nItemWidth;
1697 Box.top = 0;
1698 Box.bottom = infoPtr->nItemHeight;
1700 /************************************************************/
1701 /* compute STATEICON bounding box */
1702 /************************************************************/
1703 if (doState)
1705 if (uView == LVS_ICON)
1707 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1708 if (infoPtr->himlNormal)
1709 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1710 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1712 else
1714 /* we need the ident in report mode, if we don't have it, we fail */
1715 State.left = Box.left;
1716 if (uView == LVS_REPORT)
1718 State.left += REPORT_MARGINX;
1719 if (lpLVItem->iSubItem == 0)
1721 assert(lpLVItem->mask & LVIF_INDENT);
1722 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1725 State.top = Box.top;
1727 State.right = State.left;
1728 State.bottom = State.top;
1729 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1731 State.right += infoPtr->iconStateSize.cx;
1732 State.bottom += infoPtr->iconStateSize.cy;
1734 if (lprcState) *lprcState = State;
1735 TRACE(" - state=%s\n", debugrect(&State));
1738 /************************************************************/
1739 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1740 /************************************************************/
1741 if (doIcon)
1743 if (uView == LVS_ICON)
1745 Icon.left = Box.left;
1746 if (infoPtr->himlNormal)
1747 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1748 Icon.top = Box.top + ICON_TOP_PADDING;
1749 Icon.right = Icon.left;
1750 Icon.bottom = Icon.top;
1751 if (infoPtr->himlNormal)
1753 Icon.right += infoPtr->iconSize.cx;
1754 Icon.bottom += infoPtr->iconSize.cy;
1757 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1759 Icon.left = State.right;
1760 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1761 Icon.top = Box.top;
1762 Icon.right = Icon.left;
1763 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1764 Icon.right += infoPtr->iconSize.cx;
1765 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1767 if(lprcIcon) *lprcIcon = Icon;
1768 TRACE(" - icon=%s\n", debugrect(&Icon));
1771 /************************************************************/
1772 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1773 /************************************************************/
1774 if (doLabel)
1776 SIZE labelSize = { 0, 0 };
1778 /* calculate how far to the right can the label strech */
1779 Label.right = Box.right;
1780 if (uView == LVS_REPORT)
1782 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1783 Label.right -= REPORT_MARGINX;
1786 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1788 labelSize.cx = infoPtr->nItemWidth;
1789 labelSize.cy = infoPtr->nItemHeight;
1790 goto calc_label;
1793 /* we need the text in non owner draw mode */
1794 assert(lpLVItem->mask & LVIF_TEXT);
1795 if (is_textT(lpLVItem->pszText, TRUE))
1797 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1798 HDC hdc = GetDC(infoPtr->hwndSelf);
1799 HFONT hOldFont = SelectObject(hdc, hFont);
1800 UINT uFormat;
1801 RECT rcText;
1803 /* compute rough rectangle where the label will go */
1804 SetRectEmpty(&rcText);
1805 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1806 rcText.bottom = infoPtr->nItemHeight;
1807 if (uView == LVS_ICON)
1808 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1810 /* now figure out the flags */
1811 if (uView == LVS_ICON)
1812 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1813 else
1814 uFormat = LV_SL_DT_FLAGS;
1816 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1818 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1819 labelSize.cy = rcText.bottom - rcText.top;
1821 SelectObject(hdc, hOldFont);
1822 ReleaseDC(infoPtr->hwndSelf, hdc);
1825 calc_label:
1826 if (uView == LVS_ICON)
1828 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1829 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1830 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1831 Label.right = Label.left + labelSize.cx;
1832 Label.bottom = Label.top + infoPtr->nItemHeight;
1833 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1835 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1836 labelSize.cy /= infoPtr->ntmHeight;
1837 labelSize.cy = max(labelSize.cy, 1);
1838 labelSize.cy *= infoPtr->ntmHeight;
1840 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1842 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1844 Label.left = Icon.right;
1845 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1846 Label.top = Box.top;
1847 Label.right = min(Label.left + labelSize.cx, Label.right);
1848 Label.bottom = Label.top + infoPtr->nItemHeight;
1851 if (lprcLabel) *lprcLabel = Label;
1852 TRACE(" - label=%s\n", debugrect(&Label));
1855 /* Fix the Box if necessary */
1856 if (lprcBox)
1858 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1859 else *lprcBox = Box;
1861 TRACE(" - box=%s\n", debugrect(&Box));
1864 /***
1865 * DESCRIPTION: [INTERNAL]
1867 * PARAMETER(S):
1868 * [I] infoPtr : valid pointer to the listview structure
1869 * [I] nItem : item number
1870 * [O] lprcBox : ptr to Box rectangle
1872 * RETURN:
1873 * None.
1875 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1877 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1878 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1879 POINT Position, Origin;
1880 LVITEMW lvItem;
1882 LISTVIEW_GetOrigin(infoPtr, &Origin);
1883 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1885 /* Be smart and try to figure out the minimum we have to do */
1886 lvItem.mask = 0;
1887 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1888 lvItem.mask |= LVIF_TEXT;
1889 lvItem.iItem = nItem;
1890 lvItem.iSubItem = 0;
1891 lvItem.pszText = szDispText;
1892 lvItem.cchTextMax = DISP_TEXT_SIZE;
1893 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
1894 if (uView == LVS_ICON)
1896 lvItem.mask |= LVIF_STATE;
1897 lvItem.stateMask = LVIS_FOCUSED;
1898 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1900 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
1902 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1905 /***
1906 * DESCRIPTION:
1907 * Aligns the items with the top edge of the window.
1909 * PARAMETER(S):
1910 * [I] infoPtr : valid pointer to the listview structure
1912 * RETURN:
1913 * None
1915 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1917 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1918 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1919 POINT ptItem;
1920 INT i, off_x=0, off_y=0;
1922 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1924 /* Since SetItemPosition uses upper-left of icon, and for
1925 style=LVS_ICON the icon is not left adjusted, get the offset */
1926 if (uView == LVS_ICON)
1928 off_y = ICON_TOP_PADDING;
1929 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1931 ptItem.x = off_x;
1932 ptItem.y = off_y;
1933 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1934 off_x, off_y,
1935 infoPtr->rcList.left, infoPtr->rcList.right);
1937 if (nListWidth > infoPtr->nItemWidth)
1939 for (i = 0; i < infoPtr->nItemCount; i++)
1941 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1943 ptItem.x = off_x;
1944 ptItem.y += infoPtr->nItemHeight;
1947 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1948 ptItem.x += infoPtr->nItemWidth;
1951 else
1953 for (i = 0; i < infoPtr->nItemCount; i++)
1955 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1956 ptItem.y += infoPtr->nItemHeight;
1962 /***
1963 * DESCRIPTION:
1964 * Aligns the items with the left edge of the window.
1966 * PARAMETER(S):
1967 * [I] infoPtr : valid pointer to the listview structure
1969 * RETURN:
1970 * None
1972 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1974 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1975 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1976 POINT ptItem;
1977 INT i, off_x=0, off_y=0;
1979 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1981 /* Since SetItemPosition uses upper-left of icon, and for
1982 style=LVS_ICON the icon is not left adjusted, get the offset */
1983 if (uView == LVS_ICON)
1985 off_y = ICON_TOP_PADDING;
1986 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1988 ptItem.x = off_x;
1989 ptItem.y = off_y;
1990 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1992 if (nListHeight > infoPtr->nItemHeight)
1994 for (i = 0; i < infoPtr->nItemCount; i++)
1996 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1998 ptItem.y = off_y;
1999 ptItem.x += infoPtr->nItemWidth;
2002 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
2003 ptItem.y += infoPtr->nItemHeight;
2006 else
2008 for (i = 0; i < infoPtr->nItemCount; i++)
2010 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
2011 ptItem.x += infoPtr->nItemWidth;
2018 /***
2019 * DESCRIPTION:
2020 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2022 * PARAMETER(S):
2023 * [I] infoPtr : valid pointer to the listview structure
2024 * [O] lprcView : bounding rectangle
2026 * RETURN:
2027 * SUCCESS : TRUE
2028 * FAILURE : FALSE
2030 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2032 INT i, x, y;
2034 SetRectEmpty(lprcView);
2036 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2038 case LVS_ICON:
2039 case LVS_SMALLICON:
2040 for (i = 0; i < infoPtr->nItemCount; i++)
2042 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2043 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2044 lprcView->right = max(lprcView->right, x);
2045 lprcView->bottom = max(lprcView->bottom, y);
2047 if (infoPtr->nItemCount > 0)
2049 lprcView->right += infoPtr->nItemWidth;
2050 lprcView->bottom += infoPtr->nItemHeight;
2052 break;
2054 case LVS_LIST:
2055 y = LISTVIEW_GetCountPerColumn(infoPtr);
2056 x = infoPtr->nItemCount / y;
2057 if (infoPtr->nItemCount % y) x++;
2058 lprcView->right = x * infoPtr->nItemWidth;
2059 lprcView->bottom = y * infoPtr->nItemHeight;
2060 break;
2062 case LVS_REPORT:
2063 lprcView->right = infoPtr->nItemWidth;
2064 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2065 break;
2069 /***
2070 * DESCRIPTION:
2071 * Retrieves the bounding rectangle of all the items.
2073 * PARAMETER(S):
2074 * [I] infoPtr : valid pointer to the listview structure
2075 * [O] lprcView : bounding rectangle
2077 * RETURN:
2078 * SUCCESS : TRUE
2079 * FAILURE : FALSE
2081 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2083 POINT ptOrigin;
2085 TRACE("(lprcView=%p)\n", lprcView);
2087 if (!lprcView) return FALSE;
2089 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2090 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2091 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2093 TRACE("lprcView=%s\n", debugrect(lprcView));
2095 return TRUE;
2098 /***
2099 * DESCRIPTION:
2100 * Retrieves the subitem pointer associated with the subitem index.
2102 * PARAMETER(S):
2103 * [I] hdpaSubItems : DPA handle for a specific item
2104 * [I] nSubItem : index of subitem
2106 * RETURN:
2107 * SUCCESS : subitem pointer
2108 * FAILURE : NULL
2110 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2112 SUBITEM_INFO *lpSubItem;
2113 INT i;
2115 /* we should binary search here if need be */
2116 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2118 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2119 if (lpSubItem->iSubItem == nSubItem)
2120 return lpSubItem;
2123 return NULL;
2127 /***
2128 * DESCRIPTION:
2129 * Calculates the width of a specific item.
2131 * PARAMETER(S):
2132 * [I] infoPtr : valid pointer to the listview structure
2133 * [I] nItem : item to calculate width, or -1 for max of all
2135 * RETURN:
2136 * Returns the width of an item width an item.
2138 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
2140 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2141 INT nItemWidth = 0, i;
2143 if (uView == LVS_ICON)
2144 nItemWidth = infoPtr->iconSpacing.cx;
2145 else if (uView == LVS_REPORT)
2147 RECT rcHeaderItem;
2149 /* calculate width of header */
2150 for (i = 0; i < infoPtr->hdpaColumns->nItemCount; i++)
2152 LISTVIEW_GetHeaderRect(infoPtr, i, &rcHeaderItem);
2153 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
2156 else
2158 INT nLabelWidth;
2160 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
2162 /* get width of string */
2163 if (nItem == -1)
2165 for (i = 0; i < infoPtr->nItemCount; i++)
2167 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
2168 nItemWidth = max(nItemWidth, nLabelWidth);
2171 else
2172 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
2173 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
2174 nItemWidth += WIDTH_PADDING;
2175 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2176 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2177 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
2180 return max(nItemWidth, 1);
2183 /***
2184 * DESCRIPTION:
2185 * Calculates the max width of any item in the list.
2187 * PARAMETER(S):
2188 * [I] infoPtr : valid pointer to the listview structure
2190 * RETURN:
2191 * Returns item width.
2193 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
2195 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
2198 /***
2199 * DESCRIPTION:
2200 * Retrieves and saves important text metrics info for the current
2201 * Listview font.
2203 * PARAMETER(S):
2204 * [I] infoPtr : valid pointer to the listview structure
2207 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2209 HDC hdc = GetDC(infoPtr->hwndSelf);
2210 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2211 HFONT hOldFont = SelectObject(hdc, hFont);
2212 TEXTMETRICW tm;
2214 if (GetTextMetricsW(hdc, &tm))
2215 infoPtr->ntmHeight = tm.tmHeight;
2216 SelectObject(hdc, hOldFont);
2217 ReleaseDC(infoPtr->hwndSelf, hdc);
2219 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2223 /***
2224 * DESCRIPTION:
2225 * Calculates the height of an item.
2227 * PARAMETER(S):
2228 * [I] infoPtr : valid pointer to the listview structure
2230 * RETURN:
2231 * Returns item height.
2233 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *infoPtr)
2235 INT nItemHeight;
2237 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
2238 nItemHeight = infoPtr->iconSpacing.cy;
2239 else
2241 nItemHeight = infoPtr->ntmHeight;
2242 if (infoPtr->himlState)
2243 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2244 if (infoPtr->himlSmall)
2245 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2246 if (infoPtr->himlState || infoPtr->himlSmall)
2247 nItemHeight += HEIGHT_PADDING;
2249 return nItemHeight;
2252 /***
2253 * DESCRIPTION:
2254 * A compare function for ranges
2256 * PARAMETER(S)
2257 * [I] range1 : pointer to range 1;
2258 * [I] range2 : pointer to range 2;
2259 * [I] flags : flags
2261 * RETURNS:
2262 * > 0 : if range 1 > range 2
2263 * < 0 : if range 2 > range 1
2264 * = 0 : if range intersects range 2
2266 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2268 INT cmp;
2270 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2271 cmp = -1;
2272 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2273 cmp = 1;
2274 else
2275 cmp = 0;
2277 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2279 return cmp;
2282 #if DEBUG_RANGES
2283 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2284 #else
2285 #define ranges_check(ranges, desc) do { } while(0)
2286 #endif
2288 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2290 INT i;
2291 RANGE *prev, *curr;
2293 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2294 assert (ranges);
2295 assert (ranges->hdpa->nItemCount >= 0);
2296 ranges_dump(ranges);
2297 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2298 if (ranges->hdpa->nItemCount > 0)
2299 assert (prev->lower >= 0 && prev->lower < prev->upper);
2300 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2302 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2303 assert (prev->upper <= curr->lower);
2304 assert (curr->lower < curr->upper);
2305 prev = curr;
2307 TRACE("--- Done checking---\n");
2310 static RANGES ranges_create(int count)
2312 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2313 if (!ranges) return NULL;
2314 ranges->hdpa = DPA_Create(count);
2315 if (ranges->hdpa) return ranges;
2316 COMCTL32_Free(ranges);
2317 return NULL;
2320 static void ranges_clear(RANGES ranges)
2322 INT i;
2324 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2325 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2326 DPA_DeleteAllPtrs(ranges->hdpa);
2330 static void ranges_destroy(RANGES ranges)
2332 ranges_clear(ranges);
2333 DPA_Destroy(ranges->hdpa);
2334 COMCTL32_Free(ranges);
2337 static RANGES ranges_clone(RANGES ranges)
2339 RANGES clone;
2340 INT i;
2342 if (!(clone = ranges_create(ranges->hdpa->nItemCount))) goto fail;
2344 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2346 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2347 if (!newrng) goto fail;
2348 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2349 DPA_SetPtr(clone->hdpa, i, newrng);
2351 return clone;
2353 fail:
2354 TRACE ("clone failed\n");
2355 if (clone) ranges_destroy(clone);
2356 return NULL;
2359 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2361 INT i;
2363 for (i = 0; i < sub->hdpa->nItemCount; i++)
2364 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2366 return ranges;
2369 static void ranges_dump(RANGES ranges)
2371 INT i;
2373 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2374 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2377 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2379 RANGE srchrng = { nItem, nItem + 1 };
2381 TRACE("(nItem=%d)\n", nItem);
2382 ranges_check(ranges, "before contain");
2383 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2386 static INT ranges_itemcount(RANGES ranges)
2388 INT i, count = 0;
2390 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2392 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2393 count += sel->upper - sel->lower;
2396 return count;
2399 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2401 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2402 INT index;
2404 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2405 if (index == -1) return TRUE;
2407 for (; index < ranges->hdpa->nItemCount; index++)
2409 chkrng = DPA_GetPtr(ranges->hdpa, index);
2410 if (chkrng->lower >= nItem)
2411 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2412 if (chkrng->upper > nItem)
2413 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2415 return TRUE;
2418 static BOOL ranges_add(RANGES ranges, RANGE range)
2420 RANGE srchrgn;
2421 INT index;
2423 TRACE("(%s)\n", debugrange(&range));
2424 ranges_check(ranges, "before add");
2426 /* try find overlapping regions first */
2427 srchrgn.lower = range.lower - 1;
2428 srchrgn.upper = range.upper + 1;
2429 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2431 if (index == -1)
2433 RANGE *newrgn;
2435 TRACE("Adding new range\n");
2437 /* create the brand new range to insert */
2438 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2439 if(!newrgn) goto fail;
2440 *newrgn = range;
2442 /* figure out where to insert it */
2443 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2444 TRACE("index=%d\n", index);
2445 if (index == -1) index = 0;
2447 /* and get it over with */
2448 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2450 COMCTL32_Free(newrgn);
2451 goto fail;
2454 else
2456 RANGE *chkrgn, *mrgrgn;
2457 INT fromindex, mergeindex;
2459 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2460 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2462 chkrgn->lower = min(range.lower, chkrgn->lower);
2463 chkrgn->upper = max(range.upper, chkrgn->upper);
2465 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2467 /* merge now common anges */
2468 fromindex = 0;
2469 srchrgn.lower = chkrgn->lower - 1;
2470 srchrgn.upper = chkrgn->upper + 1;
2474 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2475 if (mergeindex == -1) break;
2476 if (mergeindex == index)
2478 fromindex = index + 1;
2479 continue;
2482 TRACE("Merge with index %i\n", mergeindex);
2484 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2485 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2486 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2487 COMCTL32_Free(mrgrgn);
2488 DPA_DeletePtr(ranges->hdpa, mergeindex);
2489 if (mergeindex < index) index --;
2490 } while(1);
2493 ranges_check(ranges, "after add");
2494 return TRUE;
2496 fail:
2497 ranges_check(ranges, "failed add");
2498 return FALSE;
2501 static BOOL ranges_del(RANGES ranges, RANGE range)
2503 RANGE *chkrgn;
2504 INT index;
2506 TRACE("(%s)\n", debugrange(&range));
2507 ranges_check(ranges, "before del");
2509 /* we don't use DPAS_SORTED here, since we need *
2510 * to find the first overlapping range */
2511 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2512 while(index != -1)
2514 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2516 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2518 /* case 1: Same range */
2519 if ( (chkrgn->upper == range.upper) &&
2520 (chkrgn->lower == range.lower) )
2522 DPA_DeletePtr(ranges->hdpa, index);
2523 break;
2525 /* case 2: engulf */
2526 else if ( (chkrgn->upper <= range.upper) &&
2527 (chkrgn->lower >= range.lower) )
2529 DPA_DeletePtr(ranges->hdpa, index);
2531 /* case 3: overlap upper */
2532 else if ( (chkrgn->upper <= range.upper) &&
2533 (chkrgn->lower < range.lower) )
2535 chkrgn->upper = range.lower;
2537 /* case 4: overlap lower */
2538 else if ( (chkrgn->upper > range.upper) &&
2539 (chkrgn->lower >= range.lower) )
2541 chkrgn->lower = range.upper;
2542 break;
2544 /* case 5: fully internal */
2545 else
2547 RANGE tmprgn = *chkrgn, *newrgn;
2549 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2550 newrgn->lower = chkrgn->lower;
2551 newrgn->upper = range.lower;
2552 chkrgn->lower = range.upper;
2553 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2555 COMCTL32_Free(newrgn);
2556 goto fail;
2558 chkrgn = &tmprgn;
2559 break;
2562 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2565 ranges_check(ranges, "after del");
2566 return TRUE;
2568 fail:
2569 ranges_check(ranges, "failed del");
2570 return FALSE;
2573 /***
2574 * DESCRIPTION:
2575 * Removes all selection ranges
2577 * Parameters(s):
2578 * [I] infoPtr : valid pointer to the listview structure
2579 * [I] toSkip : item range to skip removing the selection
2581 * RETURNS:
2582 * SUCCESS : TRUE
2583 * FAILURE : TRUE
2585 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2587 LVITEMW lvItem;
2588 ITERATOR i;
2589 RANGES clone;
2591 TRACE("()\n");
2593 lvItem.state = 0;
2594 lvItem.stateMask = LVIS_SELECTED;
2596 /* need to clone the DPA because callbacks can change it */
2597 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2598 iterator_ranges(&i, ranges_diff(clone, toSkip));
2599 while(iterator_next(&i))
2600 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2601 /* note that the iterator destructor will free the cloned range */
2602 iterator_destroy(&i);
2604 return TRUE;
2607 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2609 RANGES toSkip;
2611 if (!(toSkip = ranges_create(1))) return FALSE;
2612 if (nItem != -1) ranges_additem(toSkip, nItem);
2613 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2614 ranges_destroy(toSkip);
2615 return TRUE;
2618 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2620 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2623 /***
2624 * DESCRIPTION:
2625 * Retrieves the number of items that are marked as selected.
2627 * PARAMETER(S):
2628 * [I] infoPtr : valid pointer to the listview structure
2630 * RETURN:
2631 * Number of items selected.
2633 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2635 INT nSelectedCount = 0;
2637 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2639 INT i;
2640 for (i = 0; i < infoPtr->nItemCount; i++)
2642 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2643 nSelectedCount++;
2646 else
2647 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2649 TRACE("nSelectedCount=%d\n", nSelectedCount);
2650 return nSelectedCount;
2653 /***
2654 * DESCRIPTION:
2655 * Manages the item focus.
2657 * PARAMETER(S):
2658 * [I] infoPtr : valid pointer to the listview structure
2659 * [I] nItem : item index
2661 * RETURN:
2662 * TRUE : focused item changed
2663 * FALSE : focused item has NOT changed
2665 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2667 INT oldFocus = infoPtr->nFocusedItem;
2668 LVITEMW lvItem;
2670 if (nItem == infoPtr->nFocusedItem) return FALSE;
2672 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2673 lvItem.stateMask = LVIS_FOCUSED;
2674 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2676 return oldFocus != infoPtr->nFocusedItem;
2679 /* Helper function for LISTVIEW_ShiftIndices *only* */
2680 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2682 if (nShiftItem < nItem) return nShiftItem;
2684 if (nShiftItem > nItem) return nShiftItem + direction;
2686 if (direction > 0) return nShiftItem + direction;
2688 return min(nShiftItem, infoPtr->nItemCount - 1);
2692 * DESCRIPTION:
2693 * Updates the various indices after an item has been inserted or deleted.
2695 * PARAMETER(S):
2696 * [I] infoPtr : valid pointer to the listview structure
2697 * [I] nItem : item index
2698 * [I] direction : Direction of shift, +1 or -1.
2700 * RETURN:
2701 * None
2703 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2705 INT nNewFocus;
2707 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2709 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2711 assert(abs(direction) == 1);
2713 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2715 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2716 if (nNewFocus != infoPtr->nFocusedItem)
2717 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2719 /* But we are not supposed to modify nHotItem! */
2724 * DESCRIPTION:
2725 * Adds a block of selections.
2727 * PARAMETER(S):
2728 * [I] infoPtr : valid pointer to the listview structure
2729 * [I] nItem : item index
2731 * RETURN:
2732 * None
2734 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2736 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2737 INT nLast = max(infoPtr->nSelectionMark, nItem);
2738 INT i;
2739 LVITEMW item;
2741 if (nFirst == -1) nFirst = nItem;
2743 item.state = LVIS_SELECTED;
2744 item.stateMask = LVIS_SELECTED;
2746 /* FIXME: this is not correct LVS_OWNERDATA
2747 * setting the item states individually will generate
2748 * a LVN_ITEMCHANGED notification for each one. Instead,
2749 * we have to send a LVN_ODSTATECHANGED notification.
2750 * See MSDN documentation for LVN_ITEMCHANGED.
2752 for (i = nFirst; i <= nLast; i++)
2753 LISTVIEW_SetItemState(infoPtr,i,&item);
2757 /***
2758 * DESCRIPTION:
2759 * Sets a single group selection.
2761 * PARAMETER(S):
2762 * [I] infoPtr : valid pointer to the listview structure
2763 * [I] nItem : item index
2765 * RETURN:
2766 * None
2768 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2770 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2771 RANGES selection;
2772 LVITEMW item;
2773 ITERATOR i;
2775 if (!(selection = ranges_create(100))) return;
2777 item.state = LVIS_SELECTED;
2778 item.stateMask = LVIS_SELECTED;
2780 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2782 if (infoPtr->nSelectionMark == -1)
2784 infoPtr->nSelectionMark = nItem;
2785 ranges_additem(selection, nItem);
2787 else
2789 RANGE sel;
2791 sel.lower = min(infoPtr->nSelectionMark, nItem);
2792 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2793 ranges_add(selection, sel);
2796 else
2798 RECT rcItem, rcSel, rcSelMark;
2799 POINT ptItem;
2801 rcItem.left = LVIR_BOUNDS;
2802 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2803 rcSelMark.left = LVIR_BOUNDS;
2804 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2805 UnionRect(&rcSel, &rcItem, &rcSelMark);
2806 iterator_frameditems(&i, infoPtr, &rcSel);
2807 while(iterator_next(&i))
2809 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2810 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
2812 iterator_destroy(&i);
2815 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
2816 iterator_ranges(&i, selection);
2817 while(iterator_next(&i))
2818 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2819 /* this will also destroy the selection */
2820 iterator_destroy(&i);
2822 LISTVIEW_SetItemFocus(infoPtr, nItem);
2825 /***
2826 * DESCRIPTION:
2827 * Sets a single selection.
2829 * PARAMETER(S):
2830 * [I] infoPtr : valid pointer to the listview structure
2831 * [I] nItem : item index
2833 * RETURN:
2834 * None
2836 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2838 LVITEMW lvItem;
2840 TRACE("nItem=%d\n", nItem);
2842 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
2844 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2845 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2846 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2848 infoPtr->nSelectionMark = nItem;
2851 /***
2852 * DESCRIPTION:
2853 * Set selection(s) with keyboard.
2855 * PARAMETER(S):
2856 * [I] infoPtr : valid pointer to the listview structure
2857 * [I] INT : item index
2859 * RETURN:
2860 * SUCCESS : TRUE (needs to be repainted)
2861 * FAILURE : FALSE (nothing has changed)
2863 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2865 /* FIXME: pass in the state */
2866 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2867 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2868 BOOL bResult = FALSE;
2870 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2872 if (infoPtr->dwStyle & LVS_SINGLESEL)
2874 bResult = TRUE;
2875 LISTVIEW_SetSelection(infoPtr, nItem);
2877 else
2879 if (wShift)
2881 bResult = TRUE;
2882 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2884 else if (wCtrl)
2886 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2888 else
2890 bResult = TRUE;
2891 LISTVIEW_SetSelection(infoPtr, nItem);
2894 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2897 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2898 return bResult;
2902 /***
2903 * DESCRIPTION:
2904 * Called when the mouse is being actively tracked and has hovered for a specified
2905 * amount of time
2907 * PARAMETER(S):
2908 * [I] infoPtr : valid pointer to the listview structure
2909 * [I] fwKeys : key indicator
2910 * [I] pts : mouse position
2912 * RETURN:
2913 * 0 if the message was processed, non-zero if there was an error
2915 * INFO:
2916 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2917 * over the item for a certain period of time.
2920 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2922 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2923 /* FIXME: select the item!!! */
2924 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2926 return 0;
2929 /***
2930 * DESCRIPTION:
2931 * Called whenever WM_MOUSEMOVE is received.
2933 * PARAMETER(S):
2934 * [I] infoPtr : valid pointer to the listview structure
2935 * [I] fwKeys : key indicator
2936 * [I] pts : mouse position
2938 * RETURN:
2939 * 0 if the message is processed, non-zero if there was an error
2941 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2943 TRACKMOUSEEVENT trackinfo;
2945 /* see if we are supposed to be tracking mouse hovering */
2946 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2947 /* fill in the trackinfo struct */
2948 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2949 trackinfo.dwFlags = TME_QUERY;
2950 trackinfo.hwndTrack = infoPtr->hwndSelf;
2951 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2953 /* see if we are already tracking this hwnd */
2954 _TrackMouseEvent(&trackinfo);
2956 if(!(trackinfo.dwFlags & TME_HOVER)) {
2957 trackinfo.dwFlags = TME_HOVER;
2959 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2960 _TrackMouseEvent(&trackinfo);
2964 return 0;
2968 /***
2969 * Tests wheather the item is assignable to a list with style lStyle
2971 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2973 if ( (lpLVItem->mask & LVIF_TEXT) &&
2974 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2975 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2977 return TRUE;
2981 /***
2982 * DESCRIPTION:
2983 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2985 * PARAMETER(S):
2986 * [I] infoPtr : valid pointer to the listview structure
2987 * [I] lpLVItem : valid pointer to new item atttributes
2988 * [I] isNew : the item being set is being inserted
2989 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2990 * [O] bChanged : will be set to TRUE if the item really changed
2992 * RETURN:
2993 * SUCCESS : TRUE
2994 * FAILURE : FALSE
2996 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
2998 ITEM_INFO *lpItem;
2999 NMLISTVIEW nmlv;
3000 UINT uChanged = 0;
3001 LVITEMW item;
3003 TRACE("()\n");
3005 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3007 if (lpLVItem->mask == 0) return TRUE;
3009 if (infoPtr->dwStyle & LVS_OWNERDATA)
3011 /* a virtual listview we stores only selection and focus */
3012 if ((lpLVItem->mask & ~LVIF_STATE) || (lpLVItem->stateMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3013 return FALSE;
3014 lpItem = NULL;
3016 else
3018 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3019 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3020 assert (lpItem);
3023 /* we need to get the lParam and state of the item */
3024 item.iItem = lpLVItem->iItem;
3025 item.iSubItem = lpLVItem->iSubItem;
3026 item.mask = LVIF_STATE | LVIF_PARAM;
3027 item.stateMask = ~0;
3028 item.state = 0;
3029 item.lParam = 0;
3030 if (!isNew && !LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
3032 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3033 /* determine what fields will change */
3034 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3035 uChanged |= LVIF_STATE;
3037 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3038 uChanged |= LVIF_IMAGE;
3040 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3041 uChanged |= LVIF_PARAM;
3043 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3044 uChanged |= LVIF_INDENT;
3046 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3047 uChanged |= LVIF_TEXT;
3049 TRACE("uChanged=0x%x\n", uChanged);
3050 if (!uChanged) return TRUE;
3051 *bChanged = TRUE;
3053 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3054 nmlv.iItem = lpLVItem->iItem;
3055 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3056 nmlv.uOldState = item.state;
3057 nmlv.uChanged = uChanged;
3058 nmlv.lParam = item.lParam;
3060 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3061 /* and we are _NOT_ virtual (LVS_OWERNDATA) */
3062 if(lpItem && !isNew && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3063 return FALSE;
3065 /* copy information */
3066 if (lpLVItem->mask & LVIF_TEXT)
3067 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3069 if (lpLVItem->mask & LVIF_IMAGE)
3070 lpItem->hdr.iImage = lpLVItem->iImage;
3072 if (lpLVItem->mask & LVIF_PARAM)
3073 lpItem->lParam = lpLVItem->lParam;
3075 if (lpLVItem->mask & LVIF_INDENT)
3076 lpItem->iIndent = lpLVItem->iIndent;
3078 if (uChanged & LVIF_STATE)
3080 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED))
3082 lpItem->state &= ~lpLVItem->stateMask;
3083 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3085 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3087 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3088 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3090 else if (lpLVItem->stateMask & LVIS_SELECTED)
3091 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3093 /* if we are asked to change focus, and we manage it, do it */
3094 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3096 if (lpLVItem->state & LVIS_FOCUSED)
3098 LISTVIEW_SetItemFocus(infoPtr, -1);
3099 infoPtr->nFocusedItem = lpLVItem->iItem;
3100 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
3102 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3103 infoPtr->nFocusedItem = -1;
3107 /* if we're inserting the item, we're done */
3108 if (isNew) return TRUE;
3110 /* send LVN_ITEMCHANGED notification */
3111 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3112 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3114 return TRUE;
3117 /***
3118 * DESCRIPTION:
3119 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3121 * PARAMETER(S):
3122 * [I] infoPtr : valid pointer to the listview structure
3123 * [I] lpLVItem : valid pointer to new subitem atttributes
3124 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3125 * [O] bChanged : will be set to TRUE if the item really changed
3127 * RETURN:
3128 * SUCCESS : TRUE
3129 * FAILURE : FALSE
3131 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
3133 HDPA hdpaSubItems;
3134 SUBITEM_INFO *lpSubItem;
3136 /* we do not support subitems for virtual listviews */
3137 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3139 /* set subitem only if column is present */
3140 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3142 /* First do some sanity checks */
3143 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3144 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3146 /* get the subitem structure, and create it if not there */
3147 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3148 assert (hdpaSubItems);
3150 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3151 if (!lpSubItem)
3153 SUBITEM_INFO *tmpSubItem;
3154 INT i;
3156 lpSubItem = (SUBITEM_INFO *)COMCTL32_Alloc(sizeof(SUBITEM_INFO));
3157 if (!lpSubItem) return FALSE;
3158 /* we could binary search here, if need be...*/
3159 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3161 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3162 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3164 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3166 COMCTL32_Free(lpSubItem);
3167 return FALSE;
3169 lpSubItem->iSubItem = lpLVItem->iSubItem;
3170 *bChanged = TRUE;
3173 if (lpLVItem->mask & LVIF_IMAGE)
3174 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3176 lpSubItem->hdr.iImage = lpLVItem->iImage;
3177 *bChanged = TRUE;
3180 if (lpLVItem->mask & LVIF_TEXT)
3181 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3183 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3184 *bChanged = TRUE;
3187 return TRUE;
3190 /***
3191 * DESCRIPTION:
3192 * Sets item attributes.
3194 * PARAMETER(S):
3195 * [I] infoPtr : valid pointer to the listview structure
3196 * [I] lpLVItem : new item atttributes
3197 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3199 * RETURN:
3200 * SUCCESS : TRUE
3201 * FAILURE : FALSE
3203 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3205 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3206 LPWSTR pszText = NULL;
3207 BOOL bResult, bChanged = FALSE;
3209 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3211 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3212 return FALSE;
3214 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3215 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3217 pszText = lpLVItem->pszText;
3218 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3221 /* actually set the fields */
3222 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3224 if (lpLVItem->iSubItem)
3225 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3226 else
3227 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3229 /* redraw item, if necessary */
3230 if (bChanged && !infoPtr->bIsDrawing)
3232 /* this little optimization eliminates some nasty flicker */
3233 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3234 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3235 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3236 else
3237 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3239 /* restore text */
3240 if (pszText)
3242 textfreeT(lpLVItem->pszText, isW);
3243 lpLVItem->pszText = pszText;
3246 return bResult;
3249 /***
3250 * DESCRIPTION:
3251 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3253 * PARAMETER(S):
3254 * [I] infoPtr : valid pointer to the listview structure
3256 * RETURN:
3257 * item index
3259 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3261 LONG lStyle = infoPtr->dwStyle;
3262 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3263 INT nItem = 0;
3264 SCROLLINFO scrollInfo;
3266 scrollInfo.cbSize = sizeof(SCROLLINFO);
3267 scrollInfo.fMask = SIF_POS;
3269 if (uView == LVS_LIST)
3271 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3272 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3274 else if (uView == LVS_REPORT)
3276 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3277 nItem = scrollInfo.nPos;
3279 else
3281 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3282 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3285 TRACE("nItem=%d\n", nItem);
3287 return nItem;
3291 /***
3292 * DESCRIPTION:
3293 * Erases the background of the given rectangle
3295 * PARAMETER(S):
3296 * [I] infoPtr : valid pointer to the listview structure
3297 * [I] hdc : device context handle
3298 * [I] lprcBox : clipping rectangle
3300 * RETURN:
3301 * Success: TRUE
3302 * Failure: FALSE
3304 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3306 if (!infoPtr->hBkBrush) return FALSE;
3308 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3310 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3313 /***
3314 * DESCRIPTION:
3315 * Draws an item.
3317 * PARAMETER(S):
3318 * [I] infoPtr : valid pointer to the listview structure
3319 * [I] hdc : device context handle
3320 * [I] nItem : item index
3321 * [I] nSubItem : subitem index
3322 * [I] pos : item position in client coordinates
3323 * [I] cdmode : custom draw mode
3325 * RETURN:
3326 * Success: TRUE
3327 * Failure: FALSE
3329 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3331 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3332 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3333 DWORD cditemmode = CDRF_DODEFAULT;
3334 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3335 NMLVCUSTOMDRAW nmlvcd;
3336 HIMAGELIST himl;
3337 LVITEMW lvItem;
3339 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3341 /* get information needed for drawing the item */
3342 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3343 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3344 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3345 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3346 lvItem.iItem = nItem;
3347 lvItem.iSubItem = nSubItem;
3348 lvItem.state = 0;
3349 lvItem.lParam = 0;
3350 lvItem.cchTextMax = DISP_TEXT_SIZE;
3351 lvItem.pszText = szDispText;
3352 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3353 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3354 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3355 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3357 /* now check if we need to update the focus rectangle */
3358 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3360 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3361 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3362 OffsetRect(&rcBox, pos.x, pos.y);
3363 OffsetRect(&rcState, pos.x, pos.y);
3364 OffsetRect(&rcIcon, pos.x, pos.y);
3365 OffsetRect(&rcLabel, pos.x, pos.y);
3366 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3367 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3369 /* fill in the custom draw structure */
3370 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox);
3371 nmlvcd.nmcd.dwItemSpec = lvItem.iItem;
3372 nmlvcd.iSubItem = lvItem.iSubItem;
3373 if (lvItem.state & LVIS_SELECTED) nmlvcd.nmcd.uItemState |= CDIS_SELECTED;
3374 if (lvItem.state & LVIS_FOCUSED) nmlvcd.nmcd.uItemState |= CDIS_FOCUS;
3375 if (lvItem.iItem == infoPtr->nHotItem) nmlvcd.nmcd.uItemState |= CDIS_HOT;
3376 nmlvcd.nmcd.lItemlParam = lvItem.lParam;
3378 if (lvItem.state & LVIS_SELECTED)
3380 if (infoPtr->bFocus)
3382 nmlvcd.clrTextBk = comctl32_color.clrHighlight;
3383 nmlvcd.clrText = comctl32_color.clrHighlightText;
3385 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
3387 nmlvcd.clrTextBk = comctl32_color.clr3dFace;
3388 nmlvcd.clrText = comctl32_color.clrBtnText;
3392 if (cdmode & CDRF_NOTIFYITEMDRAW)
3393 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3394 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3396 /* state icons */
3397 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3399 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3400 if (uStateImage)
3401 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3404 /* small icons */
3405 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3406 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3407 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3408 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3410 /* Don't bother painting item being edited */
3411 if (infoPtr->hwndEdit && lprcFocus && nSubItem == 0) goto postpaint;
3413 /* Set the text attributes */
3414 if (nmlvcd.clrTextBk != CLR_NONE)
3416 SetBkMode(hdc, OPAQUE);
3417 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3419 else
3420 SetBkMode(hdc, TRANSPARENT);
3421 SetTextColor(hdc, nmlvcd.clrText);
3423 /* draw the selection background, if we're drawing the main item */
3424 if (nSubItem == 0)
3426 rcSelect = rcLabel;
3427 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3428 rcSelect.right = rcBox.right;
3430 if (lvItem.state & LVIS_SELECTED)
3431 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3432 if(lprcFocus) *lprcFocus = rcSelect;
3435 /* figure out the text drawing flags */
3436 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3437 if (uView == LVS_ICON)
3438 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3439 else if (nSubItem)
3441 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3443 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3444 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3445 default: uFormat |= DT_LEFT;
3448 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3449 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3451 postpaint:
3452 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3453 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3454 return TRUE;
3457 /***
3458 * DESCRIPTION:
3459 * Draws listview items when in owner draw mode.
3461 * PARAMETER(S):
3462 * [I] infoPtr : valid pointer to the listview structure
3463 * [I] hdc : device context handle
3465 * RETURN:
3466 * None
3468 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3470 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3471 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3472 POINT Origin, Position;
3473 DRAWITEMSTRUCT dis;
3474 LVITEMW item;
3475 ITERATOR i;
3477 TRACE("()\n");
3479 ZeroMemory(&dis, sizeof(dis));
3481 /* Get scroll info once before loop */
3482 LISTVIEW_GetOrigin(infoPtr, &Origin);
3484 /* figure out what we need to draw */
3485 iterator_visibleitems(&i, infoPtr, hdc);
3487 /* send cache hint notification */
3488 if (infoPtr->dwStyle & LVS_OWNERDATA)
3490 RANGE range = iterator_range(&i);
3491 NMLVCACHEHINT nmlv;
3493 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3494 nmlv.iFrom = range.lower;
3495 nmlv.iTo = range.upper - 1;
3496 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3499 /* iterate through the invalidated rows */
3500 while(iterator_prev(&i))
3502 item.iItem = i.nItem;
3503 item.iSubItem = 0;
3504 item.mask = LVIF_PARAM | LVIF_STATE;
3505 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3506 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3508 dis.CtlType = ODT_LISTVIEW;
3509 dis.CtlID = uID;
3510 dis.itemID = item.iItem;
3511 dis.itemAction = ODA_DRAWENTIRE;
3512 dis.itemState = 0;
3513 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3514 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3515 dis.hwndItem = infoPtr->hwndSelf;
3516 dis.hDC = hdc;
3517 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3518 dis.rcItem.left = Position.x + Origin.x;
3519 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3520 dis.rcItem.top = Position.y + Origin.y;
3521 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3522 dis.itemData = item.lParam;
3524 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3525 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3527 iterator_destroy(&i);
3530 /***
3531 * DESCRIPTION:
3532 * Draws listview items when in report display mode.
3534 * PARAMETER(S):
3535 * [I] infoPtr : valid pointer to the listview structure
3536 * [I] hdc : device context handle
3537 * [I] cdmode : custom draw mode
3539 * RETURN:
3540 * None
3542 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3544 INT rgntype, nFirstCol, nLastCol, nCol;
3545 RECT rcClip, rcItem;
3546 POINT Origin, Position;
3547 ITERATOR i;
3549 TRACE("()\n");
3551 /* figure out what to draw */
3552 rgntype = GetClipBox(hdc, &rcClip);
3553 if (rgntype == NULLREGION) return;
3555 /* Get scroll info once before loop */
3556 LISTVIEW_GetOrigin(infoPtr, &Origin);
3558 /* narrow down the columns we need to paint */
3559 for(nFirstCol = 0; nFirstCol < infoPtr->hdpaColumns->nItemCount; nFirstCol++)
3561 LISTVIEW_GetHeaderRect(infoPtr, nFirstCol, &rcItem);
3562 if (rcItem.right + Origin.x >= rcClip.left) break;
3564 for(nLastCol = infoPtr->hdpaColumns->nItemCount - 1; nLastCol >= 0; nLastCol--)
3566 LISTVIEW_GetHeaderRect(infoPtr, nLastCol, &rcItem);
3567 if (rcItem.left + Origin.x < rcClip.right) break;
3570 /* figure out what we need to draw */
3571 iterator_visibleitems(&i, infoPtr, hdc);
3573 /* a last few bits before we start drawing */
3574 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3576 /* iterate through the invalidated rows */
3577 while(iterator_prev(&i))
3579 /* iterate through the invalidated columns */
3580 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
3582 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3583 Position.x += Origin.x;
3584 Position.y += Origin.y;
3586 if (rgntype == COMPLEXREGION)
3588 LISTVIEW_GetHeaderRect(infoPtr, nCol, &rcItem);
3589 rcItem.top = 0;
3590 rcItem.bottom = infoPtr->nItemHeight;
3591 OffsetRect(&rcItem, Position.x, Position.y);
3592 if (!RectVisible(hdc, &rcItem)) continue;
3595 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, nCol, Position, cdmode);
3598 iterator_destroy(&i);
3601 /***
3602 * DESCRIPTION:
3603 * Draws listview items when in list display mode.
3605 * PARAMETER(S):
3606 * [I] infoPtr : valid pointer to the listview structure
3607 * [I] hdc : device context handle
3608 * [I] cdmode : custom draw mode
3610 * RETURN:
3611 * None
3613 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3615 POINT Origin, Position;
3616 ITERATOR i;
3618 /* Get scroll info once before loop */
3619 LISTVIEW_GetOrigin(infoPtr, &Origin);
3621 /* figure out what we need to draw */
3622 iterator_visibleitems(&i, infoPtr, hdc);
3624 while(iterator_prev(&i))
3626 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3627 Position.x += Origin.x;
3628 Position.y += Origin.y;
3630 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3632 iterator_destroy(&i);
3636 /***
3637 * DESCRIPTION:
3638 * Draws listview items.
3640 * PARAMETER(S):
3641 * [I] infoPtr : valid pointer to the listview structure
3642 * [I] hdc : device context handle
3644 * RETURN:
3645 * NoneX
3647 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3649 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3650 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3651 NMLVCUSTOMDRAW nmlvcd;
3652 HFONT hOldFont;
3653 DWORD cdmode;
3654 INT oldBkMode;
3655 RECT rcClient;
3657 LISTVIEW_DUMP(infoPtr);
3659 infoPtr->bIsDrawing = TRUE;
3661 /* save dc values we're gonna trash while drawing */
3662 hOldFont = SelectObject(hdc, infoPtr->hFont);
3663 oldBkMode = GetBkMode(hdc);
3664 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3665 oldTextColor = GetTextColor(hdc);
3667 oldClrTextBk = infoPtr->clrTextBk;
3668 oldClrText = infoPtr->clrText;
3670 GetClientRect(infoPtr->hwndSelf, &rcClient);
3671 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3672 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3673 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3675 /* Use these colors to draw the items */
3676 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3677 infoPtr->clrText = nmlvcd.clrText;
3679 /* nothing to draw */
3680 if(infoPtr->nItemCount == 0) goto enddraw;
3682 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3683 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3684 else
3686 if (uView == LVS_REPORT)
3687 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3688 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3689 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3691 /* if we have a focus rect, draw it */
3692 if (infoPtr->bFocus)
3693 DrawFocusRect(hdc, &infoPtr->rcFocus);
3696 enddraw:
3697 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3698 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3700 infoPtr->clrTextBk = oldClrTextBk;
3701 infoPtr->clrText = oldClrText;
3703 SelectObject(hdc, hOldFont);
3704 SetBkMode(hdc, oldBkMode);
3705 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3706 SetTextColor(hdc, oldTextColor);
3707 infoPtr->bIsDrawing = FALSE;
3711 /***
3712 * DESCRIPTION:
3713 * Calculates the approximate width and height of a given number of items.
3715 * PARAMETER(S):
3716 * [I] infoPtr : valid pointer to the listview structure
3717 * [I] nItemCount : number of items
3718 * [I] wWidth : width
3719 * [I] wHeight : height
3721 * RETURN:
3722 * Returns a DWORD. The width in the low word and the height in high word.
3724 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3725 WORD wWidth, WORD wHeight)
3727 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3728 INT nItemCountPerColumn = 1;
3729 INT nColumnCount = 0;
3730 DWORD dwViewRect = 0;
3732 if (nItemCount == -1)
3733 nItemCount = infoPtr->nItemCount;
3735 if (uView == LVS_LIST)
3737 if (wHeight == 0xFFFF)
3739 /* use current height */
3740 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3743 if (wHeight < infoPtr->nItemHeight)
3744 wHeight = infoPtr->nItemHeight;
3746 if (nItemCount > 0)
3748 if (infoPtr->nItemHeight > 0)
3750 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3751 if (nItemCountPerColumn == 0)
3752 nItemCountPerColumn = 1;
3754 if (nItemCount % nItemCountPerColumn != 0)
3755 nColumnCount = nItemCount / nItemCountPerColumn;
3756 else
3757 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3761 /* Microsoft padding magic */
3762 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3763 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3765 dwViewRect = MAKELONG(wWidth, wHeight);
3767 else if (uView == LVS_REPORT)
3768 FIXME("uView == LVS_REPORT: not implemented\n");
3769 else if (uView == LVS_SMALLICON)
3770 FIXME("uView == LVS_SMALLICON: not implemented\n");
3771 else if (uView == LVS_ICON)
3772 FIXME("uView == LVS_ICON: not implemented\n");
3774 return dwViewRect;
3777 /***
3778 * DESCRIPTION:
3779 * Arranges listview items in icon display mode.
3781 * PARAMETER(S):
3782 * [I] infoPtr : valid pointer to the listview structure
3783 * [I] nAlignCode : alignment code
3785 * RETURN:
3786 * SUCCESS : TRUE
3787 * FAILURE : FALSE
3789 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3791 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3793 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
3795 if (nAlignCode == LVA_DEFAULT)
3797 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
3798 else nAlignCode = LVA_ALIGNTOP;
3801 switch (nAlignCode)
3803 case LVA_ALIGNLEFT: LISTVIEW_AlignLeft(infoPtr); return TRUE;
3804 case LVA_ALIGNTOP: LISTVIEW_AlignTop(infoPtr); return TRUE;
3805 case LVA_SNAPTOGRID: FIXME("LVA_SNAPTOGRID: not implemented\n"); break;
3808 return FALSE;
3811 /* << LISTVIEW_CreateDragImage >> */
3814 /***
3815 * DESCRIPTION:
3816 * Removes all listview items and subitems.
3818 * PARAMETER(S):
3819 * [I] infoPtr : valid pointer to the listview structure
3821 * RETURN:
3822 * SUCCESS : TRUE
3823 * FAILURE : FALSE
3825 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3827 NMLISTVIEW nmlv;
3828 HDPA hdpaSubItems = NULL;
3829 BOOL bSuppress;
3830 ITEMHDR *hdrItem;
3831 INT i, j;
3833 TRACE("()\n");
3835 /* we do it directly, to avoid notifications */
3836 ranges_clear(infoPtr->selectionRanges);
3837 infoPtr->nSelectionMark = -1;
3838 infoPtr->nFocusedItem = -1;
3839 SetRectEmpty(&infoPtr->rcFocus);
3840 /* But we are supposed to leave nHotItem as is! */
3843 /* send LVN_DELETEALLITEMS notification */
3844 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3845 nmlv.iItem = -1;
3846 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3848 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
3850 /* send LVN_DELETEITEM notification, if not supressed */
3851 if (!bSuppress)
3853 nmlv.iItem = i;
3854 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3856 if (infoPtr->dwStyle & LVS_OWNERDATA)
3858 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3859 for (j = 0; j < hdpaSubItems->nItemCount; j++)
3861 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
3862 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
3863 COMCTL32_Free(hdrItem);
3865 DPA_Destroy(hdpaSubItems);
3866 DPA_DeletePtr(infoPtr->hdpaItems, i);
3868 DPA_DeletePtr(infoPtr->hdpaPosX, i);
3869 DPA_DeletePtr(infoPtr->hdpaPosY, i);
3870 infoPtr->nItemCount --;
3873 LISTVIEW_UpdateScroll(infoPtr);
3875 LISTVIEW_InvalidateList(infoPtr);
3877 return TRUE;
3880 /***
3881 * DESCRIPTION:
3882 * Scrolls, and updates the columns, when a column is changing width.
3884 * PARAMETER(S):
3885 * [I] infoPtr : valid pointer to the listview structure
3886 * [I] nColumn : column to scroll
3887 * [I] dx : amount of scroll, in pixels
3889 * RETURN:
3890 * None.
3892 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
3894 COLUMN_INFO *lpColumnInfo;
3895 RECT rcOld, rcCol;
3896 INT nCol;
3898 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, infoPtr->hdpaColumns->nItemCount - 1));
3899 rcCol = lpColumnInfo->rcHeader;
3900 if (nColumn >= infoPtr->hdpaColumns->nItemCount)
3901 rcCol.left = rcCol.right;
3903 /* ajust the other columns */
3904 for (nCol = nColumn; nCol < infoPtr->hdpaColumns->nItemCount; nCol++)
3906 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
3907 lpColumnInfo->rcHeader.left += dx;
3908 lpColumnInfo->rcHeader.right += dx;
3911 /* do not update screen if not in report mode */
3912 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
3914 /* if we have a focus, must first erase the focus rect */
3915 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3917 /* Need to reset the item width when inserting a new column */
3918 infoPtr->nItemWidth += dx;
3920 LISTVIEW_UpdateScroll(infoPtr);
3922 /* scroll to cover the deleted column, and invalidate for redraw */
3923 rcOld = infoPtr->rcList;
3924 rcOld.left = rcCol.left;
3925 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3927 /* we can restore focus now */
3928 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3931 /***
3932 * DESCRIPTION:
3933 * Removes a column from the listview control.
3935 * PARAMETER(S):
3936 * [I] infoPtr : valid pointer to the listview structure
3937 * [I] nColumn : column index
3939 * RETURN:
3940 * SUCCESS : TRUE
3941 * FAILURE : FALSE
3943 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3945 RECT rcCol;
3947 TRACE("nColumn=%d\n", nColumn);
3949 if (nColumn <= 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3951 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
3953 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3954 return FALSE;
3956 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
3957 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
3959 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3961 SUBITEM_INFO *lpSubItem, *lpDelItem;
3962 HDPA hdpaSubItems;
3963 INT nItem, nSubItem, i;
3965 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3967 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3968 nSubItem = 0;
3969 lpDelItem = 0;
3970 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3972 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3973 if (lpSubItem->iSubItem == nColumn)
3975 nSubItem = i;
3976 lpDelItem = lpSubItem;
3978 else if (lpSubItem->iSubItem > nColumn)
3980 lpSubItem->iSubItem--;
3984 /* if we found our subitem, zapp it */
3985 if (nSubItem > 0)
3987 /* free string */
3988 if (is_textW(lpDelItem->hdr.pszText))
3989 COMCTL32_Free(lpDelItem->hdr.pszText);
3991 /* free item */
3992 COMCTL32_Free(lpDelItem);
3994 /* free dpa memory */
3995 DPA_DeletePtr(hdpaSubItems, nSubItem);
4000 /* update the other column info */
4001 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4003 return TRUE;
4006 /***
4007 * DESCRIPTION:
4008 * Invalidates the listview after an item's insertion or deletion.
4010 * PARAMETER(S):
4011 * [I] infoPtr : valid pointer to the listview structure
4012 * [I] nItem : item index
4013 * [I] dir : -1 if deleting, 1 if inserting
4015 * RETURN:
4016 * None
4018 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4020 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4021 INT nPerCol, nItemCol, nItemRow;
4022 RECT rcScroll;
4023 POINT Origin;
4025 assert (abs(dir) == 1);
4027 /* arrange icons if autoarrange is on */
4028 if (infoPtr->dwStyle & LVS_AUTOARRANGE)
4029 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4031 /* scrollbars need updating */
4032 LISTVIEW_UpdateScroll(infoPtr);
4034 /* figure out the item's position */
4035 if (uView == LVS_REPORT)
4036 nPerCol = infoPtr->nItemCount + 1;
4037 else if (uView == LVS_LIST)
4038 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4039 else /* LVS_ICON, or LVS_SMALLICON */
4040 return;
4042 nItemCol = nItem / nPerCol;
4043 nItemRow = nItem % nPerCol;
4044 LISTVIEW_GetOrigin(infoPtr, &Origin);
4046 /* move the items below up a slot */
4047 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4048 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4049 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4050 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4051 OffsetRect(&rcScroll, Origin.x, Origin.y);
4052 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4053 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4054 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4056 /* report has only that column, so we're done */
4057 if (uView == LVS_REPORT) return;
4059 /* now for LISTs, we have to deal with the columns to the right */
4060 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4061 rcScroll.top = 0;
4062 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4063 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4064 OffsetRect(&rcScroll, Origin.x, Origin.y);
4065 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4066 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4067 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4070 /***
4071 * DESCRIPTION:
4072 * Removes an item from the listview control.
4074 * PARAMETER(S):
4075 * [I] infoPtr : valid pointer to the listview structure
4076 * [I] nItem : item index
4078 * RETURN:
4079 * SUCCESS : TRUE
4080 * FAILURE : FALSE
4082 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4084 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4085 NMLISTVIEW nmlv;
4086 LVITEMW item;
4087 RECT rcBox;
4089 TRACE("(nItem=%d)\n", nItem);
4091 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4093 /* remove selection, and focus */
4094 item.state = 0;
4095 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4096 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4098 /* we need to do this here, because we'll be deleting stuff */
4099 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4100 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
4102 /* send LVN_DELETEITEM notification. */
4103 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
4104 nmlv.iItem = nItem;
4105 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
4107 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4109 HDPA hdpaSubItems;
4110 ITEMHDR *hdrItem;
4111 INT i;
4113 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4114 for (i = 0; i < hdpaSubItems->nItemCount; i++)
4116 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4117 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4118 COMCTL32_Free(hdrItem);
4120 DPA_Destroy(hdpaSubItems);
4123 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4125 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4126 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4127 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
4130 infoPtr->nItemCount--;
4131 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4133 /* now is the invalidation fun */
4134 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4135 return TRUE;
4139 /***
4140 * DESCRIPTION:
4141 * Callback implementation for editlabel control
4143 * PARAMETER(S):
4144 * [I] infoPtr : valid pointer to the listview structure
4145 * [I] pszText : modified text
4146 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4148 * RETURN:
4149 * SUCCESS : TRUE
4150 * FAILURE : FALSE
4152 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4154 NMLVDISPINFOW dispInfo;
4156 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4158 ZeroMemory(&dispInfo, sizeof(dispInfo));
4159 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4160 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4161 dispInfo.item.iSubItem = 0;
4162 dispInfo.item.stateMask = ~0;
4163 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4164 /* add the text from the edit in */
4165 dispInfo.item.mask |= LVIF_TEXT;
4166 dispInfo.item.pszText = pszText;
4167 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4169 /* Do we need to update the Item Text */
4170 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4171 if (!pszText) return TRUE;
4173 ZeroMemory(&dispInfo, sizeof(dispInfo));
4174 dispInfo.item.mask = LVIF_TEXT;
4175 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4176 dispInfo.item.iSubItem = 0;
4177 dispInfo.item.pszText = pszText;
4178 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4179 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4182 /***
4183 * DESCRIPTION:
4184 * Begin in place editing of specified list view item
4186 * PARAMETER(S):
4187 * [I] infoPtr : valid pointer to the listview structure
4188 * [I] nItem : item index
4189 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4191 * RETURN:
4192 * SUCCESS : TRUE
4193 * FAILURE : FALSE
4195 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4197 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4198 NMLVDISPINFOW dispInfo;
4199 RECT rect;
4201 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4203 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4204 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4206 infoPtr->nEditLabelItem = nItem;
4208 /* Is the EditBox still there, if so remove it */
4209 if(infoPtr->hwndEdit != 0)
4211 SetFocus(infoPtr->hwndSelf);
4212 infoPtr->hwndEdit = 0;
4215 LISTVIEW_SetSelection(infoPtr, nItem);
4216 LISTVIEW_SetItemFocus(infoPtr, nItem);
4218 rect.left = LVIR_LABEL;
4219 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4221 ZeroMemory(&dispInfo, sizeof(dispInfo));
4222 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4223 dispInfo.item.iItem = nItem;
4224 dispInfo.item.iSubItem = 0;
4225 dispInfo.item.stateMask = ~0;
4226 dispInfo.item.pszText = szDispText;
4227 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4228 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4230 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4231 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4232 if (!infoPtr->hwndEdit) return 0;
4234 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4236 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4237 infoPtr->hwndEdit = 0;
4238 return 0;
4241 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4242 SetFocus(infoPtr->hwndEdit);
4243 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4244 return infoPtr->hwndEdit;
4248 /***
4249 * DESCRIPTION:
4250 * Ensures the specified item is visible, scrolling into view if necessary.
4252 * PARAMETER(S):
4253 * [I] infoPtr : valid pointer to the listview structure
4254 * [I] nItem : item index
4255 * [I] bPartial : partially or entirely visible
4257 * RETURN:
4258 * SUCCESS : TRUE
4259 * FAILURE : FALSE
4261 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4263 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4264 INT nScrollPosHeight = 0;
4265 INT nScrollPosWidth = 0;
4266 INT nHorzAdjust = 0;
4267 INT nVertAdjust = 0;
4268 INT nHorzDiff = 0;
4269 INT nVertDiff = 0;
4270 RECT rcItem, rcTemp;
4272 rcItem.left = LVIR_BOUNDS;
4273 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4275 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4277 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4279 /* scroll left/right, but in LVS_REPORT mode */
4280 if (uView == LVS_LIST)
4281 nScrollPosWidth = infoPtr->nItemWidth;
4282 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4283 nScrollPosWidth = 1;
4285 if (rcItem.left < infoPtr->rcList.left)
4287 nHorzAdjust = -1;
4288 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4290 else
4292 nHorzAdjust = 1;
4293 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4297 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4299 /* scroll up/down, but not in LVS_LIST mode */
4300 if (uView == LVS_REPORT)
4301 nScrollPosHeight = infoPtr->nItemHeight;
4302 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4303 nScrollPosHeight = 1;
4305 if (rcItem.top < infoPtr->rcList.top)
4307 nVertAdjust = -1;
4308 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4310 else
4312 nVertAdjust = 1;
4313 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4317 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4319 if (nScrollPosWidth)
4321 INT diff = nHorzDiff / nScrollPosWidth;
4322 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4323 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4326 if (nScrollPosHeight)
4328 INT diff = nVertDiff / nScrollPosHeight;
4329 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4330 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4333 return TRUE;
4336 /***
4337 * DESCRIPTION:
4338 * Searches for an item with specific characteristics.
4340 * PARAMETER(S):
4341 * [I] hwnd : window handle
4342 * [I] nStart : base item index
4343 * [I] lpFindInfo : item information to look for
4345 * RETURN:
4346 * SUCCESS : index of item
4347 * FAILURE : -1
4349 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4350 LPLVFINDINFOW lpFindInfo)
4352 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4353 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4354 BOOL bWrap = FALSE, bNearest = FALSE;
4355 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4356 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4357 POINT Position, Destination;
4358 LVITEMW lvItem;
4360 if (!lpFindInfo || nItem < 0) return -1;
4362 lvItem.mask = 0;
4363 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4365 lvItem.mask |= LVIF_TEXT;
4366 lvItem.pszText = szDispText;
4367 lvItem.cchTextMax = DISP_TEXT_SIZE;
4370 if (lpFindInfo->flags & LVFI_WRAP)
4371 bWrap = TRUE;
4373 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4374 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4376 POINT Origin;
4377 RECT rcArea;
4379 FIXME("LVFI_NEARESTXY is slow.\n");
4380 LISTVIEW_GetOrigin(infoPtr, &Origin);
4381 Destination.x = lpFindInfo->pt.x - Origin.x;
4382 Destination.y = lpFindInfo->pt.y - Origin.y;
4383 switch(lpFindInfo->vkDirection)
4385 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4386 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4387 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4388 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4389 case VK_HOME: Destination.x = Destination.y = 0; break;
4390 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4391 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4392 case VK_END:
4393 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4394 Destination.x = rcArea.right;
4395 Destination.y = rcArea.bottom;
4396 break;
4397 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4399 bNearest = TRUE;
4402 /* if LVFI_PARAM is specified, all other flags are ignored */
4403 if (lpFindInfo->flags & LVFI_PARAM)
4405 lvItem.mask |= LVIF_PARAM;
4406 bNearest = FALSE;
4407 lvItem.mask &= ~LVIF_TEXT;
4410 again:
4411 for (; nItem < nLast; nItem++)
4413 lvItem.iItem = nItem;
4414 lvItem.iSubItem = 0;
4415 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4417 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4418 return nItem;
4420 if (lvItem.mask & LVIF_TEXT)
4422 if (lpFindInfo->flags & LVFI_PARTIAL)
4424 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4426 else
4428 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4432 if (!bNearest) return nItem;
4434 /* This is very inefficient. To do a good job here,
4435 * we need a sorted array of (x,y) item positions */
4436 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4438 /* compute the distance^2 to the destination */
4439 xdist = Destination.x - Position.x;
4440 ydist = Destination.y - Position.y;
4441 dist = xdist * xdist + ydist * ydist;
4443 /* remember the distance, and item if it's closer */
4444 if (dist < mindist)
4446 mindist = dist;
4447 nNearestItem = nItem;
4451 if (bWrap)
4453 nItem = 0;
4454 nLast = min(nStart + 1, infoPtr->nItemCount);
4455 bWrap = FALSE;
4456 goto again;
4459 return nNearestItem;
4462 /***
4463 * DESCRIPTION:
4464 * Searches for an item with specific characteristics.
4466 * PARAMETER(S):
4467 * [I] hwnd : window handle
4468 * [I] nStart : base item index
4469 * [I] lpFindInfo : item information to look for
4471 * RETURN:
4472 * SUCCESS : index of item
4473 * FAILURE : -1
4475 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4476 LPLVFINDINFOA lpFindInfo)
4478 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4479 LVFINDINFOW fiw;
4480 INT res;
4482 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4483 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4484 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4485 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4486 return res;
4489 /***
4490 * DESCRIPTION:
4491 * Retrieves the background image of the listview control.
4493 * PARAMETER(S):
4494 * [I] infoPtr : valid pointer to the listview structure
4495 * [O] lpBkImage : background image attributes
4497 * RETURN:
4498 * SUCCESS : TRUE
4499 * FAILURE : FALSE
4501 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4502 /* { */
4503 /* FIXME (listview, "empty stub!\n"); */
4504 /* return FALSE; */
4505 /* } */
4507 /***
4508 * DESCRIPTION:
4509 * Retrieves column attributes.
4511 * PARAMETER(S):
4512 * [I] infoPtr : valid pointer to the listview structure
4513 * [I] nColumn : column index
4514 * [IO] lpColumn : column information
4515 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4516 * otherwise it is in fact a LPLVCOLUMNA
4518 * RETURN:
4519 * SUCCESS : TRUE
4520 * FAILURE : FALSE
4522 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4524 COLUMN_INFO *lpColumnInfo;
4525 HDITEMW hdi;
4527 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4528 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4530 /* initialize memory */
4531 ZeroMemory(&hdi, sizeof(hdi));
4533 if (lpColumn->mask & LVCF_TEXT)
4535 hdi.mask |= HDI_TEXT;
4536 hdi.pszText = lpColumn->pszText;
4537 hdi.cchTextMax = lpColumn->cchTextMax;
4540 if (lpColumn->mask & LVCF_IMAGE)
4541 hdi.mask |= HDI_IMAGE;
4543 if (lpColumn->mask & LVCF_ORDER)
4544 hdi.mask |= HDI_ORDER;
4546 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4548 if (lpColumn->mask & LVCF_FMT)
4549 lpColumn->fmt = lpColumnInfo->fmt;
4551 if (lpColumn->mask & LVCF_WIDTH)
4552 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4554 if (lpColumn->mask & LVCF_IMAGE)
4555 lpColumn->iImage = hdi.iImage;
4557 if (lpColumn->mask & LVCF_ORDER)
4558 lpColumn->iOrder = hdi.iOrder;
4560 return TRUE;
4564 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4566 INT i;
4568 if (!lpiArray)
4569 return FALSE;
4571 /* FIXME: little hack */
4572 for (i = 0; i < iCount; i++)
4573 lpiArray[i] = i;
4575 return TRUE;
4578 /***
4579 * DESCRIPTION:
4580 * Retrieves the column width.
4582 * PARAMETER(S):
4583 * [I] infoPtr : valid pointer to the listview structure
4584 * [I] int : column index
4586 * RETURN:
4587 * SUCCESS : column width
4588 * FAILURE : zero
4590 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4592 INT nColumnWidth = 0;
4593 RECT rcHeader;
4595 TRACE("nColumn=%d\n", nColumn);
4597 /* we have a 'column' in LIST and REPORT mode only */
4598 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4600 case LVS_LIST:
4601 nColumnWidth = infoPtr->nItemWidth;
4602 break;
4603 case LVS_REPORT:
4604 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return 0;
4605 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4606 nColumnWidth = rcHeader.right - rcHeader.left;
4607 break;
4610 TRACE("nColumnWidth=%d\n", nColumnWidth);
4611 return nColumnWidth;
4614 /***
4615 * DESCRIPTION:
4616 * In list or report display mode, retrieves the number of items that can fit
4617 * vertically in the visible area. In icon or small icon display mode,
4618 * retrieves the total number of visible items.
4620 * PARAMETER(S):
4621 * [I] infoPtr : valid pointer to the listview structure
4623 * RETURN:
4624 * Number of fully visible items.
4626 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4628 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4630 case LVS_ICON:
4631 case LVS_SMALLICON:
4632 return infoPtr->nItemCount;
4633 case LVS_REPORT:
4634 return LISTVIEW_GetCountPerColumn(infoPtr);
4635 case LVS_LIST:
4636 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4638 assert(FALSE);
4641 /***
4642 * DESCRIPTION:
4643 * Retrieves an image list handle.
4645 * PARAMETER(S):
4646 * [I] infoPtr : valid pointer to the listview structure
4647 * [I] nImageList : image list identifier
4649 * RETURN:
4650 * SUCCESS : image list handle
4651 * FAILURE : NULL
4653 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4655 switch (nImageList)
4657 case LVSIL_NORMAL: return infoPtr->himlNormal;
4658 case LVSIL_SMALL: return infoPtr->himlSmall;
4659 case LVSIL_STATE: return infoPtr->himlState;
4661 return NULL;
4664 /* LISTVIEW_GetISearchString */
4666 /***
4667 * DESCRIPTION:
4668 * Retrieves item attributes.
4670 * PARAMETER(S):
4671 * [I] hwnd : window handle
4672 * [IO] lpLVItem : item info
4673 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4674 * if FALSE, the lpLVItem is a LPLVITEMA.
4676 * NOTE:
4677 * This is the internal 'GetItem' interface -- it tries to
4678 * be smart, and avoids text copies, if possible, by modifing
4679 * lpLVItem->pszText to point to the text string. Please note
4680 * that this is not always possible (e.g. OWNERDATA), so on
4681 * entry you *must* supply valid values for pszText, and cchTextMax.
4682 * The only difference to the documented interface is that upon
4683 * return, you should use *only* the lpLVItem->pszText, rather than
4684 * the buffer pointer you provided on input. Most code already does
4685 * that, so it's not a problem.
4686 * For the two cases when the text must be copied (that is,
4687 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4689 * RETURN:
4690 * SUCCESS : TRUE
4691 * FAILURE : FALSE
4693 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4695 NMLVDISPINFOW dispInfo;
4696 ITEM_INFO *lpItem;
4697 ITEMHDR* pItemHdr;
4698 HDPA hdpaSubItems;
4700 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4702 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4703 return FALSE;
4705 if (lpLVItem->mask == 0) return TRUE;
4707 /* a quick optimization if all we're asked is the focus state
4708 * these queries are worth optimising since they are common,
4709 * and can be answered in constant time, without the heavy accesses */
4710 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4711 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4713 lpLVItem->state = 0;
4714 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4715 lpLVItem->state |= LVIS_FOCUSED;
4716 return TRUE;
4719 ZeroMemory(&dispInfo, sizeof(dispInfo));
4721 /* if the app stores all the data, handle it separately */
4722 if (infoPtr->dwStyle & LVS_OWNERDATA)
4724 dispInfo.item.state = 0;
4726 /* if we need to callback, do it now */
4727 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4729 /* NOTE: copy only fields which we _know_ are initialized, some apps
4730 * depend on the uninitialized fields being 0 */
4731 dispInfo.item.mask = lpLVItem->mask;
4732 dispInfo.item.iItem = lpLVItem->iItem;
4733 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4734 if (lpLVItem->mask & LVIF_TEXT)
4736 dispInfo.item.pszText = lpLVItem->pszText;
4737 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4739 if (lpLVItem->mask & LVIF_STATE)
4740 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4741 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4742 dispInfo.item.stateMask = lpLVItem->stateMask;
4743 *lpLVItem = dispInfo.item;
4744 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4747 /* we store only a little state, so if we're not asked, we're done */
4748 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4750 /* if focus is handled by us, report it */
4751 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4753 lpLVItem->state &= ~LVIS_FOCUSED;
4754 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4755 lpLVItem->state |= LVIS_FOCUSED;
4758 /* and do the same for selection, if we handle it */
4759 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4761 lpLVItem->state &= ~LVIS_SELECTED;
4762 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4763 lpLVItem->state |= LVIS_SELECTED;
4766 return TRUE;
4769 /* find the item and subitem structures before we proceed */
4770 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4771 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4772 assert (lpItem);
4774 if (lpLVItem->iSubItem)
4776 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4777 if(!lpSubItem) return FALSE;
4778 pItemHdr = &lpSubItem->hdr;
4780 else
4781 pItemHdr = &lpItem->hdr;
4783 /* Do we need to query the state from the app? */
4784 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4786 dispInfo.item.mask |= LVIF_STATE;
4787 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4790 /* Do we need to enquire about the image? */
4791 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4792 dispInfo.item.mask |= LVIF_IMAGE;
4794 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4795 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4797 dispInfo.item.mask |= LVIF_TEXT;
4798 dispInfo.item.pszText = lpLVItem->pszText;
4799 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4800 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4801 *dispInfo.item.pszText = '\0';
4804 /* If we don't have all the requested info, query the application */
4805 if (dispInfo.item.mask != 0)
4807 dispInfo.item.iItem = lpLVItem->iItem;
4808 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4809 dispInfo.item.lParam = lpItem->lParam;
4810 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4811 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4814 /* Now, handle the iImage field */
4815 if (dispInfo.item.mask & LVIF_IMAGE)
4817 lpLVItem->iImage = dispInfo.item.iImage;
4818 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
4819 pItemHdr->iImage = dispInfo.item.iImage;
4821 else if (lpLVItem->mask & LVIF_IMAGE)
4822 lpLVItem->iImage = pItemHdr->iImage;
4824 /* The pszText field */
4825 if (dispInfo.item.mask & LVIF_TEXT)
4827 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4828 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4830 lpLVItem->pszText = dispInfo.item.pszText;
4832 else if (lpLVItem->mask & LVIF_TEXT)
4834 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4835 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4838 /* if this is a subitem, we're done */
4839 if (lpLVItem->iSubItem) return TRUE;
4841 /* Next is the lParam field */
4842 if (dispInfo.item.mask & LVIF_PARAM)
4844 lpLVItem->lParam = dispInfo.item.lParam;
4845 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4846 lpItem->lParam = dispInfo.item.lParam;
4848 else if (lpLVItem->mask & LVIF_PARAM)
4849 lpLVItem->lParam = lpItem->lParam;
4851 /* ... the state field (this one is different due to uCallbackmask) */
4852 if (lpLVItem->mask & LVIF_STATE)
4854 lpLVItem->state = lpItem->state;
4855 if (dispInfo.item.mask & LVIF_STATE)
4857 lpLVItem->state &= ~dispInfo.item.stateMask;
4858 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4860 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4862 lpLVItem->state &= ~LVIS_FOCUSED;
4863 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4864 lpLVItem->state |= LVIS_FOCUSED;
4866 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4868 lpLVItem->state &= ~LVIS_SELECTED;
4869 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4870 lpLVItem->state |= LVIS_SELECTED;
4874 /* and last, but not least, the indent field */
4875 if (lpLVItem->mask & LVIF_INDENT)
4876 lpLVItem->iIndent = lpItem->iIndent;
4878 return TRUE;
4881 /***
4882 * DESCRIPTION:
4883 * Retrieves item attributes.
4885 * PARAMETER(S):
4886 * [I] hwnd : window handle
4887 * [IO] lpLVItem : item info
4888 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4889 * if FALSE, the lpLVItem is a LPLVITEMA.
4891 * NOTE:
4892 * This is the external 'GetItem' interface -- it properly copies
4893 * the text in the provided buffer.
4895 * RETURN:
4896 * SUCCESS : TRUE
4897 * FAILURE : FALSE
4899 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4901 LPWSTR pszText;
4902 BOOL bResult;
4904 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4905 return FALSE;
4907 pszText = lpLVItem->pszText;
4908 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4909 if (bResult && lpLVItem->pszText != pszText)
4910 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4911 lpLVItem->pszText = pszText;
4913 return bResult;
4917 /***
4918 * DESCRIPTION:
4919 * Retrieves the position (upper-left) of the listview control item.
4920 * Note that for LVS_ICON style, the upper-left is that of the icon
4921 * and not the bounding box.
4923 * PARAMETER(S):
4924 * [I] infoPtr : valid pointer to the listview structure
4925 * [I] nItem : item index
4926 * [O] lpptPosition : coordinate information
4928 * RETURN:
4929 * SUCCESS : TRUE
4930 * FAILURE : FALSE
4932 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4934 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4935 POINT Origin;
4937 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4939 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4941 LISTVIEW_GetOrigin(infoPtr, &Origin);
4942 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
4944 if (uView == LVS_ICON)
4946 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4947 lpptPosition->y += ICON_TOP_PADDING;
4949 lpptPosition->x += Origin.x;
4950 lpptPosition->y += Origin.y;
4952 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4953 return TRUE;
4957 /***
4958 * DESCRIPTION:
4959 * Retrieves the bounding rectangle for a listview control item.
4961 * PARAMETER(S):
4962 * [I] infoPtr : valid pointer to the listview structure
4963 * [I] nItem : item index
4964 * [IO] lprc : bounding rectangle coordinates
4965 * lprc->left specifies the portion of the item for which the bounding
4966 * rectangle will be retrieved.
4968 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
4969 * including the icon and label.
4971 * * For LVS_ICON
4972 * * Experiment shows that native control returns:
4973 * * width = min (48, length of text line)
4974 * * .left = position.x - (width - iconsize.cx)/2
4975 * * .right = .left + width
4976 * * height = #lines of text * ntmHeight + icon height + 8
4977 * * .top = position.y - 2
4978 * * .bottom = .top + height
4979 * * separation between items .y = itemSpacing.cy - height
4980 * * .x = itemSpacing.cx - width
4981 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
4983 * * For LVS_ICON
4984 * * Experiment shows that native control returns:
4985 * * width = iconSize.cx + 16
4986 * * .left = position.x - (width - iconsize.cx)/2
4987 * * .right = .left + width
4988 * * height = iconSize.cy + 4
4989 * * .top = position.y - 2
4990 * * .bottom = .top + height
4991 * * separation between items .y = itemSpacing.cy - height
4992 * * .x = itemSpacing.cx - width
4993 * LVIR_LABEL Returns the bounding rectangle of the item text.
4995 * * For LVS_ICON
4996 * * Experiment shows that native control returns:
4997 * * width = text length
4998 * * .left = position.x - width/2
4999 * * .right = .left + width
5000 * * height = ntmH * linecount + 2
5001 * * .top = position.y + iconSize.cy + 6
5002 * * .bottom = .top + height
5003 * * separation between items .y = itemSpacing.cy - height
5004 * * .x = itemSpacing.cx - width
5005 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5006 * rectangles, but excludes columns in report view.
5008 * RETURN:
5009 * SUCCESS : TRUE
5010 * FAILURE : FALSE
5012 * NOTES
5013 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5014 * upon whether the window has the focus currently and on whether the item
5015 * is the one with the focus. Ensure that the control's record of which
5016 * item has the focus agrees with the items' records.
5018 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5020 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5021 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5022 BOOL doLabel = TRUE, oversizedBox = FALSE;
5023 POINT Position, Origin;
5024 LVITEMW lvItem;
5025 RECT label_rect;
5027 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5029 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5031 LISTVIEW_GetOrigin(infoPtr, &Origin);
5032 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5034 /* Be smart and try to figure out the minimum we have to do */
5035 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5036 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5037 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5038 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5039 oversizedBox = TRUE;
5041 /* get what we need from the item before hand, so we make
5042 * only one request. This can speed up things, if data
5043 * is stored on the app side */
5044 lvItem.mask = 0;
5045 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5046 if (doLabel) lvItem.mask |= LVIF_TEXT;
5047 lvItem.iItem = nItem;
5048 lvItem.iSubItem = 0;
5049 lvItem.pszText = szDispText;
5050 lvItem.cchTextMax = DISP_TEXT_SIZE;
5051 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5052 /* we got the state already up, simulate it here, to avoid a reget */
5053 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5055 lvItem.mask |= LVIF_STATE;
5056 lvItem.stateMask = LVIS_FOCUSED;
5057 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5060 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5061 lprc->left = LVIR_BOUNDS;
5062 switch(lprc->left)
5064 case LVIR_ICON:
5065 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5066 break;
5068 case LVIR_LABEL:
5069 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5070 break;
5072 case LVIR_BOUNDS:
5073 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5074 break;
5076 case LVIR_SELECTBOUNDS:
5077 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5078 UnionRect(lprc, lprc, &label_rect);
5079 break;
5081 default:
5082 WARN("Unknown value: %d\n", lprc->left);
5083 return FALSE;
5086 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5088 TRACE(" rect=%s\n", debugrect(lprc));
5090 return TRUE;
5093 /***
5094 * DESCRIPTION:
5095 * Retrieves the spacing between listview control items.
5097 * PARAMETER(S):
5098 * [I] infoPtr : valid pointer to the listview structure
5099 * [IO] lprc : rectangle to receive the output
5100 * on input, lprc->top = nSubItem
5101 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5103 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5104 * not only those of the first column.
5105 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5107 * RETURN:
5108 * TRUE: success
5109 * FALSE: failure
5111 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5113 POINT Position, Origin;
5114 LVITEMW lvItem;
5116 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5118 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5120 LISTVIEW_GetOrigin(infoPtr, &Origin);
5121 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5123 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5124 lvItem.iItem = nItem;
5125 lvItem.iSubItem = lprc->top;
5127 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5128 switch(lprc->left)
5130 case LVIR_ICON:
5131 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5132 break;
5134 case LVIR_LABEL:
5135 case LVIR_BOUNDS:
5136 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5137 break;
5139 default:
5140 ERR("Unknown bounds=%d\n", lprc->left);
5141 return FALSE;
5144 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5145 return TRUE;
5149 /***
5150 * DESCRIPTION:
5151 * Retrieves the width of a label.
5153 * PARAMETER(S):
5154 * [I] infoPtr : valid pointer to the listview structure
5156 * RETURN:
5157 * SUCCESS : string width (in pixels)
5158 * FAILURE : zero
5160 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5162 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5163 LVITEMW lvItem;
5165 TRACE("(nItem=%d)\n", nItem);
5167 lvItem.mask = LVIF_TEXT;
5168 lvItem.iItem = nItem;
5169 lvItem.iSubItem = 0;
5170 lvItem.pszText = szDispText;
5171 lvItem.cchTextMax = DISP_TEXT_SIZE;
5172 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5174 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5177 /***
5178 * DESCRIPTION:
5179 * Retrieves the spacing between listview control items.
5181 * PARAMETER(S):
5182 * [I] infoPtr : valid pointer to the listview structure
5183 * [I] bSmall : flag for small or large icon
5185 * RETURN:
5186 * Horizontal + vertical spacing
5188 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5190 LONG lResult;
5192 if (!bSmall)
5194 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5196 else
5198 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5199 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5200 else
5201 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5203 return lResult;
5206 /***
5207 * DESCRIPTION:
5208 * Retrieves the state of a listview control item.
5210 * PARAMETER(S):
5211 * [I] infoPtr : valid pointer to the listview structure
5212 * [I] nItem : item index
5213 * [I] uMask : state mask
5215 * RETURN:
5216 * State specified by the mask.
5218 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5220 LVITEMW lvItem;
5222 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5224 lvItem.iItem = nItem;
5225 lvItem.iSubItem = 0;
5226 lvItem.mask = LVIF_STATE;
5227 lvItem.stateMask = uMask;
5228 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5230 return lvItem.state & uMask;
5233 /***
5234 * DESCRIPTION:
5235 * Retrieves the text of a listview control item or subitem.
5237 * PARAMETER(S):
5238 * [I] hwnd : window handle
5239 * [I] nItem : item index
5240 * [IO] lpLVItem : item information
5241 * [I] isW : TRUE if lpLVItem is Unicode
5243 * RETURN:
5244 * SUCCESS : string length
5245 * FAILURE : 0
5247 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5249 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5251 lpLVItem->mask = LVIF_TEXT;
5252 lpLVItem->iItem = nItem;
5253 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5255 return textlenT(lpLVItem->pszText, isW);
5258 /***
5259 * DESCRIPTION:
5260 * Searches for an item based on properties + relationships.
5262 * PARAMETER(S):
5263 * [I] infoPtr : valid pointer to the listview structure
5264 * [I] nItem : item index
5265 * [I] uFlags : relationship flag
5267 * FIXME:
5268 * This function is very, very inefficient! Needs work.
5270 * RETURN:
5271 * SUCCESS : item index
5272 * FAILURE : -1
5274 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5276 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5277 UINT uMask = 0;
5278 LVFINDINFOW lvFindInfo;
5279 INT nCountPerColumn;
5280 INT i;
5282 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5283 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5285 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5287 if (uFlags & LVNI_CUT)
5288 uMask |= LVIS_CUT;
5290 if (uFlags & LVNI_DROPHILITED)
5291 uMask |= LVIS_DROPHILITED;
5293 if (uFlags & LVNI_FOCUSED)
5294 uMask |= LVIS_FOCUSED;
5296 if (uFlags & LVNI_SELECTED)
5297 uMask |= LVIS_SELECTED;
5299 /* if we're asked for the focused item, that's only one,
5300 * so it's worth optimizing */
5301 if (uFlags & LVNI_FOCUSED)
5303 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5304 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5307 if (uFlags & LVNI_ABOVE)
5309 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5311 while (nItem >= 0)
5313 nItem--;
5314 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5315 return nItem;
5318 else
5320 lvFindInfo.flags = LVFI_NEARESTXY;
5321 lvFindInfo.vkDirection = VK_UP;
5322 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5323 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5325 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5326 return nItem;
5330 else if (uFlags & LVNI_BELOW)
5332 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5334 while (nItem < infoPtr->nItemCount)
5336 nItem++;
5337 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5338 return nItem;
5341 else
5343 lvFindInfo.flags = LVFI_NEARESTXY;
5344 lvFindInfo.vkDirection = VK_DOWN;
5345 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5346 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5348 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5349 return nItem;
5353 else if (uFlags & LVNI_TOLEFT)
5355 if (uView == LVS_LIST)
5357 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5358 while (nItem - nCountPerColumn >= 0)
5360 nItem -= nCountPerColumn;
5361 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5362 return nItem;
5365 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5367 lvFindInfo.flags = LVFI_NEARESTXY;
5368 lvFindInfo.vkDirection = VK_LEFT;
5369 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5370 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5372 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5373 return nItem;
5377 else if (uFlags & LVNI_TORIGHT)
5379 if (uView == LVS_LIST)
5381 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5382 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5384 nItem += nCountPerColumn;
5385 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5386 return nItem;
5389 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5391 lvFindInfo.flags = LVFI_NEARESTXY;
5392 lvFindInfo.vkDirection = VK_RIGHT;
5393 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5394 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5396 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5397 return nItem;
5401 else
5403 nItem++;
5405 /* search by index */
5406 for (i = nItem; i < infoPtr->nItemCount; i++)
5408 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5409 return i;
5413 return -1;
5416 /* LISTVIEW_GetNumberOfWorkAreas */
5418 /***
5419 * DESCRIPTION:
5420 * Retrieves the origin coordinates when in icon or small icon display mode.
5422 * PARAMETER(S):
5423 * [I] infoPtr : valid pointer to the listview structure
5424 * [O] lpptOrigin : coordinate information
5426 * RETURN:
5427 * None.
5429 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5431 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5432 INT nHorzPos = 0, nVertPos = 0;
5433 SCROLLINFO scrollInfo;
5435 scrollInfo.cbSize = sizeof(SCROLLINFO);
5436 scrollInfo.fMask = SIF_POS;
5438 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5439 nHorzPos = scrollInfo.nPos;
5440 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5441 nVertPos = scrollInfo.nPos;
5443 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5445 lpptOrigin->x = infoPtr->rcList.left;
5446 lpptOrigin->y = infoPtr->rcList.top;
5447 if (uView == LVS_LIST)
5448 nHorzPos *= infoPtr->nItemWidth;
5449 else if (uView == LVS_REPORT)
5450 nVertPos *= infoPtr->nItemHeight;
5452 lpptOrigin->x -= nHorzPos;
5453 lpptOrigin->y -= nVertPos;
5455 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5458 /***
5459 * DESCRIPTION:
5460 * Retrieves the width of a string.
5462 * PARAMETER(S):
5463 * [I] hwnd : window handle
5464 * [I] lpszText : text string to process
5465 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5467 * RETURN:
5468 * SUCCESS : string width (in pixels)
5469 * FAILURE : zero
5471 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5473 SIZE stringSize;
5475 stringSize.cx = 0;
5476 if (is_textT(lpszText, isW))
5478 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5479 HDC hdc = GetDC(infoPtr->hwndSelf);
5480 HFONT hOldFont = SelectObject(hdc, hFont);
5482 if (isW)
5483 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5484 else
5485 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5486 SelectObject(hdc, hOldFont);
5487 ReleaseDC(infoPtr->hwndSelf, hdc);
5489 return stringSize.cx;
5492 /***
5493 * DESCRIPTION:
5494 * Determines which listview item is located at the specified position.
5496 * PARAMETER(S):
5497 * [I] infoPtr : valid pointer to the listview structure
5498 * [IO] lpht : hit test information
5499 * [I] subitem : fill out iSubItem.
5500 * [I] select : return the index only if the hit selects the item
5502 * NOTE:
5503 * (mm 20001022): We must not allow iSubItem to be touched, for
5504 * an app might pass only a structure with space up to iItem!
5505 * (MS Office 97 does that for instance in the file open dialog)
5507 * RETURN:
5508 * SUCCESS : item index
5509 * FAILURE : -1
5511 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5513 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5514 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5515 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5516 POINT Origin, Position, opt;
5517 LVITEMW lvItem;
5518 ITERATOR i;
5520 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5522 lpht->flags = 0;
5523 lpht->iItem = -1;
5524 if (subitem) lpht->iSubItem = 0;
5526 if (infoPtr->rcList.left > lpht->pt.x)
5527 lpht->flags |= LVHT_TOLEFT;
5528 else if (infoPtr->rcList.right < lpht->pt.x)
5529 lpht->flags |= LVHT_TORIGHT;
5531 if (infoPtr->rcList.top > lpht->pt.y)
5532 lpht->flags |= LVHT_ABOVE;
5533 else if (infoPtr->rcList.bottom < lpht->pt.y)
5534 lpht->flags |= LVHT_BELOW;
5536 TRACE("lpht->flags=0x%x\n", lpht->flags);
5537 if (lpht->flags) return -1;
5539 lpht->flags |= LVHT_NOWHERE;
5541 LISTVIEW_GetOrigin(infoPtr, &Origin);
5543 /* first deal with the large items */
5544 rcSearch.left = lpht->pt.x;
5545 rcSearch.top = lpht->pt.y;
5546 rcSearch.right = rcSearch.left + 1;
5547 rcSearch.bottom = rcSearch.top + 1;
5549 iterator_frameditems(&i, infoPtr, &rcSearch);
5550 iterator_next(&i); /* go to first item in the sequence */
5551 lpht->iItem = i.nItem;
5552 iterator_destroy(&i);
5554 TRACE("lpht->iItem=%d\n", lpht->iItem);
5555 if (lpht->iItem == -1) return -1;
5557 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5558 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5559 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5560 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5561 lvItem.iItem = lpht->iItem;
5562 lvItem.iSubItem = 0;
5563 lvItem.pszText = szDispText;
5564 lvItem.cchTextMax = DISP_TEXT_SIZE;
5565 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5566 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5568 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5569 LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position);
5570 opt.x = lpht->pt.x - Position.x - Origin.x;
5571 opt.y = lpht->pt.y - Position.y - Origin.y;
5573 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5574 rcBounds = rcBox;
5575 else
5576 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5577 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5578 if (!PtInRect(&rcBounds, opt)) return -1;
5580 if (PtInRect(&rcIcon, opt))
5581 lpht->flags |= LVHT_ONITEMICON;
5582 else if (PtInRect(&rcLabel, opt))
5583 lpht->flags |= LVHT_ONITEMLABEL;
5584 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5585 lpht->flags |= LVHT_ONITEMSTATEICON;
5586 if (lpht->flags & LVHT_ONITEM)
5587 lpht->flags &= ~LVHT_NOWHERE;
5589 TRACE("lpht->flags=0x%x\n", lpht->flags);
5590 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5592 INT j;
5594 rcBounds.right = rcBounds.left;
5595 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5597 rcBounds.left = rcBounds.right;
5598 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5599 if (PtInRect(&rcBounds, opt))
5601 lpht->iSubItem = j;
5602 break;
5607 if (!select || lpht->iItem == -1) return lpht->iItem;
5609 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5611 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5612 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5616 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5617 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5618 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5619 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5620 their own sort proc. when sending LVM_SORTITEMS.
5622 /* Platform SDK:
5623 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5625 LVS_SORTXXX must be specified,
5626 LVS_OWNERDRAW is not set,
5627 <item>.pszText is not LPSTR_TEXTCALLBACK.
5629 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5630 are sorted based on item text..."
5632 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5634 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5635 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5636 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5638 /* if we're sorting descending, negate the return value */
5639 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5642 /***
5643 * nESCRIPTION:
5644 * Inserts a new item in the listview control.
5646 * PARAMETER(S):
5647 * [I] infoPtr : valid pointer to the listview structure
5648 * [I] lpLVItem : item information
5649 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5651 * RETURN:
5652 * SUCCESS : new item index
5653 * FAILURE : -1
5655 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5657 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5658 INT nItem;
5659 HDPA hdpaSubItems;
5660 NMLISTVIEW nmlv;
5661 ITEM_INFO *lpItem;
5662 BOOL is_sorted, has_changed;
5663 LVITEMW item;
5665 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5667 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5669 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5670 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5672 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5674 if ( !(lpItem = (ITEM_INFO *)COMCTL32_Alloc(sizeof(ITEM_INFO))) )
5675 return -1;
5677 /* insert item in listview control data structure */
5678 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5679 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5681 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5682 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5684 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5685 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5686 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5687 if (nItem == -1) goto fail;
5688 infoPtr->nItemCount++;
5690 /* set the item attributes */
5691 item = *lpLVItem;
5692 item.iItem = nItem;
5693 item.state &= ~LVIS_STATEIMAGEMASK;
5694 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5696 /* if we're sorted, sort the list, and update the index */
5697 if (is_sorted)
5699 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5700 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5701 assert(nItem != -1);
5704 /* make room for the position, if we are in the right mode */
5705 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5707 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5708 goto undo;
5709 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5711 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5712 goto undo;
5716 /* Add the subitem list to the items array. Do this last in case we go to
5717 * fail during the above.
5719 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5721 /* send LVN_INSERTITEM notification */
5722 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5723 nmlv.iItem = nItem;
5724 nmlv.lParam = lpItem->lParam;
5725 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5727 /* align items (set position of each item) */
5728 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
5730 RECT rcBox;
5731 /* FIXME: compute X, Y here, inline */
5732 /*if (is_icon && (infoPtr->dwStyle & LVS_AUTOARRANGE))*/
5733 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5734 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
5735 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
5738 /* now is the invalidation fun */
5739 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
5740 return nItem;
5742 undo:
5743 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5744 infoPtr->nItemCount--;
5745 fail:
5746 DPA_DeletePtr(hdpaSubItems, 0);
5747 DPA_Destroy (hdpaSubItems);
5748 COMCTL32_Free (lpItem);
5749 return -1;
5752 /***
5753 * DESCRIPTION:
5754 * Redraws a range of items.
5756 * PARAMETER(S):
5757 * [I] infoPtr : valid pointer to the listview structure
5758 * [I] nFirst : first item
5759 * [I] nLast : last item
5761 * RETURN:
5762 * SUCCESS : TRUE
5763 * FAILURE : FALSE
5765 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5767 INT i;
5769 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5770 max(nFirst, nLast) >= infoPtr->nItemCount)
5771 return FALSE;
5773 for (i = nFirst; i <= nLast; i++)
5774 LISTVIEW_InvalidateItem(infoPtr, i);
5776 return TRUE;
5779 /***
5780 * DESCRIPTION:
5781 * Scroll the content of a listview.
5783 * PARAMETER(S):
5784 * [I] infoPtr : valid pointer to the listview structure
5785 * [I] dx : horizontal scroll amount in pixels
5786 * [I] dy : vertical scroll amount in pixels
5788 * RETURN:
5789 * SUCCESS : TRUE
5790 * FAILURE : FALSE
5792 * COMMENTS:
5793 * If the control is in report mode (LVS_REPORT) the control can
5794 * be scrolled only in line increments. "dy" will be rounded to the
5795 * nearest number of pixels that are a whole line. Ex: if line height
5796 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5797 * is passed the the scroll will be 0. (per MSDN 7/2002)
5799 * For: (per experimentaion with native control and CSpy ListView)
5800 * LVS_ICON dy=1 = 1 pixel (vertical only)
5801 * dx ignored
5802 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5803 * dx ignored
5804 * LVS_LIST dx=1 = 1 column (horizontal only)
5805 * but will only scroll 1 column per message
5806 * no matter what the value.
5807 * dy must be 0 or FALSE returned.
5808 * LVS_REPORT dx=1 = 1 pixel
5809 * dy= see above
5812 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5814 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5815 case LVS_REPORT:
5816 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5817 dy /= infoPtr->nItemHeight;
5818 break;
5819 case LVS_LIST:
5820 if (dy != 0) return FALSE;
5821 break;
5822 default: /* icon */
5823 dx = 0;
5824 break;
5827 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5828 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5830 return TRUE;
5833 /***
5834 * DESCRIPTION:
5835 * Sets the background color.
5837 * PARAMETER(S):
5838 * [I] infoPtr : valid pointer to the listview structure
5839 * [I] clrBk : background color
5841 * RETURN:
5842 * SUCCESS : TRUE
5843 * FAILURE : FALSE
5845 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5847 TRACE("(clrBk=%lx)\n", clrBk);
5849 if(infoPtr->clrBk != clrBk) {
5850 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5851 infoPtr->clrBk = clrBk;
5852 if (clrBk == CLR_NONE)
5853 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5854 else
5855 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5856 LISTVIEW_InvalidateList(infoPtr);
5859 return TRUE;
5862 /* LISTVIEW_SetBkImage */
5864 /*** Helper for {Insert,Set}ColumnT *only* */
5865 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5867 if (lpColumn->mask & LVCF_FMT)
5869 /* format member is valid */
5870 lphdi->mask |= HDI_FORMAT;
5872 /* set text alignment (leftmost column must be left-aligned) */
5873 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5874 lphdi->fmt |= HDF_LEFT;
5875 else if (lpColumn->fmt & LVCFMT_RIGHT)
5876 lphdi->fmt |= HDF_RIGHT;
5877 else if (lpColumn->fmt & LVCFMT_CENTER)
5878 lphdi->fmt |= HDF_CENTER;
5880 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5881 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
5883 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5885 lphdi->fmt |= HDF_IMAGE;
5886 lphdi->iImage = I_IMAGECALLBACK;
5890 if (lpColumn->mask & LVCF_WIDTH)
5892 lphdi->mask |= HDI_WIDTH;
5893 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5895 /* make it fill the remainder of the controls width */
5896 RECT rcHeader;
5897 INT item_index;
5899 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5901 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
5902 lphdi->cxy += rcHeader.right - rcHeader.left;
5905 /* retrieve the layout of the header */
5906 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5907 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
5909 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
5911 else
5912 lphdi->cxy = lpColumn->cx;
5915 if (lpColumn->mask & LVCF_TEXT)
5917 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
5918 lphdi->fmt |= HDF_STRING;
5919 lphdi->pszText = lpColumn->pszText;
5920 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
5923 if (lpColumn->mask & LVCF_IMAGE)
5925 lphdi->mask |= HDI_IMAGE;
5926 lphdi->iImage = lpColumn->iImage;
5929 if (lpColumn->mask & LVCF_ORDER)
5931 lphdi->mask |= HDI_ORDER;
5932 lphdi->iOrder = lpColumn->iOrder;
5937 /***
5938 * DESCRIPTION:
5939 * Inserts a new column.
5941 * PARAMETER(S):
5942 * [I] infoPtr : valid pointer to the listview structure
5943 * [I] nColumn : column index
5944 * [I] lpColumn : column information
5945 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
5947 * RETURN:
5948 * SUCCESS : new column index
5949 * FAILURE : -1
5951 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5952 LPLVCOLUMNW lpColumn, BOOL isW)
5954 COLUMN_INFO *lpColumnInfo;
5955 INT nNewColumn;
5956 HDITEMW hdi;
5958 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5960 if (!lpColumn || nColumn < 0 || nColumn > infoPtr->hdpaColumns->nItemCount) return -1;
5962 ZeroMemory(&hdi, sizeof(HDITEMW));
5963 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
5965 /* insert item in header control */
5966 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5967 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5968 (WPARAM)nColumn, (LPARAM)&hdi);
5969 if (nNewColumn == -1) return -1;
5970 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
5972 /* create our own column info */
5973 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
5974 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
5976 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
5977 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
5979 /* now we have to actually adjust the data */
5980 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5982 SUBITEM_INFO *lpSubItem, *lpMainItem, **lpNewItems = 0;
5983 HDPA hdpaSubItems;
5984 INT nItem, i;
5986 /* preallocate memory, so we can fail gracefully */
5987 if (nNewColumn == 0)
5989 lpNewItems = COMCTL32_Alloc(sizeof(SUBITEM_INFO *) * infoPtr->nItemCount);
5990 if (!lpNewItems) goto fail;
5991 for (i = 0; i < infoPtr->nItemCount; i++)
5992 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(SUBITEM_INFO)))) break;
5993 if (i != infoPtr->nItemCount)
5995 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5996 COMCTL32_Free(lpNewItems);
5997 goto fail;
6001 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6003 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6004 for (i = 1; i < hdpaSubItems->nItemCount; i++)
6006 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6007 if (lpSubItem->iSubItem >= nNewColumn)
6008 lpSubItem->iSubItem++;
6011 /* for inserting column 0, we have to special-case the main item */
6012 if (nNewColumn == 0)
6014 lpMainItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6015 lpSubItem = lpNewItems[nItem];
6016 lpSubItem->hdr = lpMainItem->hdr;
6017 lpSubItem->iSubItem = 1;
6018 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
6019 lpMainItem->iSubItem = 0;
6020 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
6024 COMCTL32_Free(lpNewItems);
6027 /* make space for the new column */
6028 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6030 return nNewColumn;
6032 fail:
6033 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6034 if (lpColumnInfo)
6036 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6037 COMCTL32_Free(lpColumnInfo);
6039 return -1;
6042 /***
6043 * DESCRIPTION:
6044 * Sets the attributes of a header item.
6046 * PARAMETER(S):
6047 * [I] infoPtr : valid pointer to the listview structure
6048 * [I] nColumn : column index
6049 * [I] lpColumn : column attributes
6050 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6052 * RETURN:
6053 * SUCCESS : TRUE
6054 * FAILURE : FALSE
6056 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6057 LPLVCOLUMNW lpColumn, BOOL isW)
6059 HDITEMW hdi, hdiget;
6060 BOOL bResult;
6062 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6064 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6066 ZeroMemory(&hdi, sizeof(HDITEMW));
6067 if (lpColumn->mask & LVCF_FMT)
6069 hdi.mask |= HDI_FORMAT;
6070 hdiget.mask = HDI_FORMAT;
6071 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6072 hdi.fmt = hdiget.fmt & HDF_STRING;
6074 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6076 /* set header item attributes */
6077 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6078 if (!bResult) return FALSE;
6080 if (lpColumn->mask & LVCF_FMT)
6082 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6083 int oldFmt = lpColumnInfo->fmt;
6085 lpColumnInfo->fmt = lpColumn->fmt;
6086 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6088 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6089 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6093 return TRUE;
6096 /***
6097 * DESCRIPTION:
6098 * Sets the column order array
6100 * PARAMETERS:
6101 * [I] infoPtr : valid pointer to the listview structure
6102 * [I] iCount : number of elements in column order array
6103 * [I] lpiArray : pointer to column order array
6105 * RETURN:
6106 * SUCCESS : TRUE
6107 * FAILURE : FALSE
6109 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6111 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6113 if (!lpiArray)
6114 return FALSE;
6116 return TRUE;
6120 /***
6121 * DESCRIPTION:
6122 * Sets the width of a column
6124 * PARAMETERS:
6125 * [I] infoPtr : valid pointer to the listview structure
6126 * [I] nColumn : column index
6127 * [I] cx : column width
6129 * RETURN:
6130 * SUCCESS : TRUE
6131 * FAILURE : FALSE
6133 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6135 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6136 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6137 INT max_cx = 0;
6138 HDITEMW hdi;
6140 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6142 /* set column width only if in report or list mode */
6143 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6145 /* take care of invalid cx values */
6146 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6147 else if (uView == LVS_LIST && cx < 1) return FALSE;
6149 /* resize all columns if in LVS_LIST mode */
6150 if(uView == LVS_LIST)
6152 infoPtr->nItemWidth = cx;
6153 LISTVIEW_InvalidateList(infoPtr);
6154 return TRUE;
6157 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6159 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6161 INT nLabelWidth;
6162 LVITEMW lvItem;
6164 lvItem.mask = LVIF_TEXT;
6165 lvItem.iItem = 0;
6166 lvItem.iSubItem = nColumn;
6167 lvItem.pszText = szDispText;
6168 lvItem.cchTextMax = DISP_TEXT_SIZE;
6169 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6171 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6172 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6173 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6175 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6176 max_cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6177 max_cx += REPORT_MARGINX + TRAILING_PADDING;
6180 /* autosize based on listview items width */
6181 if(cx == LVSCW_AUTOSIZE)
6182 cx = max_cx;
6183 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6185 /* if iCol is the last column make it fill the remainder of the controls width */
6186 if(nColumn == infoPtr->hdpaColumns->nItemCount - 1)
6188 RECT rcHeader;
6189 POINT Origin;
6191 LISTVIEW_GetOrigin(infoPtr, &Origin);
6192 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6194 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6196 else
6198 /* Despite what the MS docs say, if this is not the last
6199 column, then MS resizes the column to the width of the
6200 largest text string in the column, including headers
6201 and items. This is different from LVSCW_AUTOSIZE in that
6202 LVSCW_AUTOSIZE ignores the header string length. */
6203 cx = 0;
6205 /* retrieve header text */
6206 hdi.mask = HDI_TEXT;
6207 hdi.cchTextMax = DISP_TEXT_SIZE;
6208 hdi.pszText = szDispText;
6209 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6211 HDC hdc = GetDC(infoPtr->hwndSelf);
6212 HFONT old_font = SelectObject(hdc, SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6213 SIZE size;
6215 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6216 cx = size.cx;
6217 /* FIXME: Take into account the header image, if one is present */
6218 SelectObject(hdc, old_font);
6219 ReleaseDC(infoPtr->hwndSelf, hdc);
6221 cx = max (cx, max_cx);
6225 if (cx < 0) return FALSE;
6227 /* call header to update the column change */
6228 hdi.mask = HDI_WIDTH;
6229 hdi.cxy = cx;
6230 TRACE("hdi.cxy=%d\n", hdi.cxy);
6231 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6234 /***
6235 * DESCRIPTION:
6236 * Sets the extended listview style.
6238 * PARAMETERS:
6239 * [I] infoPtr : valid pointer to the listview structure
6240 * [I] dwMask : mask
6241 * [I] dwStyle : style
6243 * RETURN:
6244 * SUCCESS : previous style
6245 * FAILURE : 0
6247 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6249 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6251 /* set new style */
6252 if (dwMask)
6253 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6254 else
6255 infoPtr->dwLvExStyle = dwStyle;
6257 return dwOldStyle;
6260 /***
6261 * DESCRIPTION:
6262 * Sets the new hot cursor used during hot tracking and hover selection.
6264 * PARAMETER(S):
6265 * [I] infoPtr : valid pointer to the listview structure
6266 * [I} hCurosr : the new hot cursor handle
6268 * RETURN:
6269 * Returns the previous hot cursor
6271 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6273 HCURSOR oldCursor = infoPtr->hHotCursor;
6275 infoPtr->hHotCursor = hCursor;
6277 return oldCursor;
6281 /***
6282 * DESCRIPTION:
6283 * Sets the hot item index.
6285 * PARAMETERS:
6286 * [I] infoPtr : valid pointer to the listview structure
6287 * [I] iIndex : index
6289 * RETURN:
6290 * SUCCESS : previous hot item index
6291 * FAILURE : -1 (no hot item)
6293 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6295 INT iOldIndex = infoPtr->nHotItem;
6297 infoPtr->nHotItem = iIndex;
6299 return iOldIndex;
6303 /***
6304 * DESCRIPTION:
6305 * Sets the amount of time the cursor must hover over an item before it is selected.
6307 * PARAMETER(S):
6308 * [I] infoPtr : valid pointer to the listview structure
6309 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6311 * RETURN:
6312 * Returns the previous hover time
6314 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6316 DWORD oldHoverTime = infoPtr->dwHoverTime;
6318 infoPtr->dwHoverTime = dwHoverTime;
6320 return oldHoverTime;
6323 /***
6324 * DESCRIPTION:
6325 * Sets spacing for icons of LVS_ICON style.
6327 * PARAMETER(S):
6328 * [I] infoPtr : valid pointer to the listview structure
6329 * [I] spacing : MAKELONG(cx, cy)
6331 * RETURN:
6332 * MAKELONG(oldcx, oldcy)
6334 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6336 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6337 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6338 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6340 TRACE("requested=(%d,%d)\n", cx, cy);
6342 /* this is supported only for LVS_ICON style */
6343 if (uView != LVS_ICON) return oldspacing;
6345 /* set to defaults, if instructed to */
6346 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6347 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6349 /* if 0 then compute width
6350 * FIXME: Should scan each item and determine max width of
6351 * icon or label, then make that the width */
6352 if (cx == 0)
6353 cx = infoPtr->iconSpacing.cx;
6355 /* if 0 then compute height */
6356 if (cy == 0)
6357 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6358 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6361 infoPtr->iconSpacing.cx = cx;
6362 infoPtr->iconSpacing.cy = cy;
6364 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6365 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6366 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6367 infoPtr->ntmHeight);
6369 /* these depend on the iconSpacing */
6370 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6371 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6373 return oldspacing;
6376 inline void update_icon_size(HIMAGELIST himl, BOOL small, SIZE *size)
6378 INT cx, cy;
6380 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6382 size->cx = cx;
6383 size->cy = cy;
6385 else
6387 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6388 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6392 /***
6393 * DESCRIPTION:
6394 * Sets image lists.
6396 * PARAMETER(S):
6397 * [I] infoPtr : valid pointer to the listview structure
6398 * [I] nType : image list type
6399 * [I] himl : image list handle
6401 * RETURN:
6402 * SUCCESS : old image list
6403 * FAILURE : NULL
6405 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6407 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6408 INT oldHeight = infoPtr->nItemHeight;
6409 HIMAGELIST himlOld = 0;
6411 switch (nType)
6413 case LVSIL_NORMAL:
6414 himlOld = infoPtr->himlNormal;
6415 infoPtr->himlNormal = himl;
6416 if (uView == LVS_ICON) update_icon_size(himl, FALSE, &infoPtr->iconSize);
6417 LISTVIEW_SetIconSpacing(infoPtr, 0);
6418 break;
6420 case LVSIL_SMALL:
6421 himlOld = infoPtr->himlSmall;
6422 infoPtr->himlSmall = himl;
6423 if (uView != LVS_ICON) update_icon_size(himl, TRUE, &infoPtr->iconSize);
6424 break;
6426 case LVSIL_STATE:
6427 himlOld = infoPtr->himlState;
6428 infoPtr->himlState = himl;
6429 update_icon_size(himl, TRUE, &infoPtr->iconStateSize);
6430 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6431 break;
6433 default:
6434 ERR("Unknown icon type=%d\n", nType);
6435 return NULL;
6438 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6439 if (infoPtr->nItemHeight != oldHeight)
6440 LISTVIEW_UpdateScroll(infoPtr);
6442 return himlOld;
6445 /***
6446 * DESCRIPTION:
6447 * Preallocates memory (does *not* set the actual count of items !)
6449 * PARAMETER(S):
6450 * [I] infoPtr : valid pointer to the listview structure
6451 * [I] nItems : item count (projected number of items to allocate)
6452 * [I] dwFlags : update flags
6454 * RETURN:
6455 * SUCCESS : TRUE
6456 * FAILURE : FALSE
6458 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6460 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6462 if (infoPtr->dwStyle & LVS_OWNERDATA)
6464 int precount,topvisible;
6466 TRACE("LVS_OWNERDATA is set!\n");
6467 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6468 FIXME("flags %s %s not implemented\n",
6469 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6470 : "",
6471 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6473 LISTVIEW_DeselectAll(infoPtr);
6475 precount = infoPtr->nItemCount;
6476 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6477 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6479 infoPtr->nItemCount = nItems;
6480 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6481 DEFAULT_COLUMN_WIDTH);
6483 LISTVIEW_UpdateSize(infoPtr);
6484 LISTVIEW_UpdateScroll(infoPtr);
6486 if (min(precount,infoPtr->nItemCount) < topvisible)
6487 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6489 else
6491 /* According to MSDN for non-LVS_OWNERDATA this is just
6492 * a performance issue. The control allocates its internal
6493 * data structures for the number of items specified. It
6494 * cuts down on the number of memory allocations. Therefore
6495 * we will just issue a WARN here
6497 WARN("for non-ownerdata performance option not implemented.\n");
6500 return TRUE;
6503 /***
6504 * DESCRIPTION:
6505 * Sets the position of an item.
6507 * PARAMETER(S):
6508 * [I] infoPtr : valid pointer to the listview structure
6509 * [I] nItem : item index
6510 * [I] pt : coordinate
6512 * RETURN:
6513 * SUCCESS : TRUE
6514 * FAILURE : FALSE
6516 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6518 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6519 POINT old;
6521 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6523 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6524 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6526 /* This point value seems to be an undocumented feature.
6527 * The best guess is that it means either at the origin,
6528 * or at true beginning of the list. I will assume the origin. */
6529 if ((pt.x == -1) && (pt.y == -1))
6530 LISTVIEW_GetOrigin(infoPtr, &pt);
6531 else if (uView == LVS_ICON)
6533 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6534 pt.y -= ICON_TOP_PADDING;
6537 /* save the old position */
6538 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6539 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6541 /* Is the position changing? */
6542 if (pt.x == old.x && pt.y == old.y) return TRUE;
6544 /* FIXME: shouldn't we invalidate, as the item moved? */
6546 /* Allocating a POINTER for every item is too resource intensive,
6547 * so we'll keep the (x,y) in different arrays */
6548 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6549 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6550 return TRUE;
6552 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6553 nItem, debugpoint(&pt));
6554 return FALSE;
6557 /***
6558 * DESCRIPTION:
6559 * Sets the state of one or many items.
6561 * PARAMETER(S):
6562 * [I] infoPtr : valid pointer to the listview structure
6563 * [I] nItem : item index
6564 * [I] lpLVItem : item or subitem info
6566 * RETURN:
6567 * SUCCESS : TRUE
6568 * FAILURE : FALSE
6570 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6572 BOOL bResult = TRUE;
6573 LVITEMW lvItem;
6575 lvItem.iItem = nItem;
6576 lvItem.iSubItem = 0;
6577 lvItem.mask = LVIF_STATE;
6578 lvItem.state = lpLVItem->state;
6579 lvItem.stateMask = lpLVItem->stateMask;
6580 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6582 if (nItem == -1)
6584 /* apply to all items */
6585 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6586 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6588 else
6589 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6591 return bResult;
6594 /***
6595 * DESCRIPTION:
6596 * Sets the text of an item or subitem.
6598 * PARAMETER(S):
6599 * [I] hwnd : window handle
6600 * [I] nItem : item index
6601 * [I] lpLVItem : item or subitem info
6602 * [I] isW : TRUE if input is Unicode
6604 * RETURN:
6605 * SUCCESS : TRUE
6606 * FAILURE : FALSE
6608 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6610 LVITEMW lvItem;
6612 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6614 lvItem.iItem = nItem;
6615 lvItem.iSubItem = lpLVItem->iSubItem;
6616 lvItem.mask = LVIF_TEXT;
6617 lvItem.pszText = lpLVItem->pszText;
6618 lvItem.cchTextMax = lpLVItem->cchTextMax;
6620 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6622 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6625 /***
6626 * DESCRIPTION:
6627 * Set item index that marks the start of a multiple selection.
6629 * PARAMETER(S):
6630 * [I] infoPtr : valid pointer to the listview structure
6631 * [I] nIndex : index
6633 * RETURN:
6634 * Index number or -1 if there is no selection mark.
6636 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6638 INT nOldIndex = infoPtr->nSelectionMark;
6640 TRACE("(nIndex=%d)\n", nIndex);
6642 infoPtr->nSelectionMark = nIndex;
6644 return nOldIndex;
6647 /***
6648 * DESCRIPTION:
6649 * Sets the text background color.
6651 * PARAMETER(S):
6652 * [I] infoPtr : valid pointer to the listview structure
6653 * [I] clrTextBk : text background color
6655 * RETURN:
6656 * SUCCESS : TRUE
6657 * FAILURE : FALSE
6659 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6661 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6663 if (infoPtr->clrTextBk != clrTextBk)
6665 infoPtr->clrTextBk = clrTextBk;
6666 LISTVIEW_InvalidateList(infoPtr);
6669 return TRUE;
6672 /***
6673 * DESCRIPTION:
6674 * Sets the text foreground color.
6676 * PARAMETER(S):
6677 * [I] infoPtr : valid pointer to the listview structure
6678 * [I] clrText : text color
6680 * RETURN:
6681 * SUCCESS : TRUE
6682 * FAILURE : FALSE
6684 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6686 TRACE("(clrText=%lx)\n", clrText);
6688 if (infoPtr->clrText != clrText)
6690 infoPtr->clrText = clrText;
6691 LISTVIEW_InvalidateList(infoPtr);
6694 return TRUE;
6697 /* LISTVIEW_SetToolTips */
6698 /* LISTVIEW_SetUnicodeFormat */
6699 /* LISTVIEW_SetWorkAreas */
6701 /***
6702 * DESCRIPTION:
6703 * Callback internally used by LISTVIEW_SortItems()
6705 * PARAMETER(S):
6706 * [I] first : pointer to first ITEM_INFO to compare
6707 * [I] second : pointer to second ITEM_INFO to compare
6708 * [I] lParam : HWND of control
6710 * RETURN:
6711 * if first comes before second : negative
6712 * if first comes after second : positive
6713 * if first and second are equivalent : zero
6715 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6717 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6718 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6719 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6721 /* Forward the call to the client defined callback */
6722 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6725 /***
6726 * DESCRIPTION:
6727 * Sorts the listview items.
6729 * PARAMETER(S):
6730 * [I] infoPtr : valid pointer to the listview structure
6731 * [I] pfnCompare : application-defined value
6732 * [I] lParamSort : pointer to comparision callback
6734 * RETURN:
6735 * SUCCESS : TRUE
6736 * FAILURE : FALSE
6738 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6740 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6741 HDPA hdpaSubItems;
6742 ITEM_INFO *lpItem;
6743 LPVOID selectionMarkItem;
6744 LVITEMW item;
6745 int i;
6747 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6749 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
6751 if (!infoPtr->hdpaItems) return FALSE;
6753 /* if there are 0 or 1 items, there is no need to sort */
6754 if (infoPtr->nItemCount < 2) return TRUE;
6756 if (infoPtr->nFocusedItem >= 0)
6758 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6759 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6760 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6762 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6763 /* clear the lpItem->state for non-selected ones */
6764 /* remove the selection ranges */
6766 infoPtr->pfnCompare = pfnCompare;
6767 infoPtr->lParamSort = lParamSort;
6768 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6770 /* Adjust selections and indices so that they are the way they should
6771 * be after the sort (otherwise, the list items move around, but
6772 * whatever is at the item's previous original position will be
6773 * selected instead)
6775 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6776 for (i=0; i < infoPtr->nItemCount; i++)
6778 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6779 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6781 if (lpItem->state & LVIS_SELECTED)
6783 item.state = LVIS_SELECTED;
6784 item.stateMask = LVIS_SELECTED;
6785 LISTVIEW_SetItemState(infoPtr, i, &item);
6787 if (lpItem->state & LVIS_FOCUSED)
6789 infoPtr->nFocusedItem = i;
6790 lpItem->state &= ~LVIS_FOCUSED;
6793 if (selectionMarkItem != NULL)
6794 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6795 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6797 /* align the items */
6798 LISTVIEW_AlignTop(infoPtr);
6800 /* refresh the display */
6801 if (uView != LVS_ICON && uView != LVS_SMALLICON)
6802 LISTVIEW_InvalidateList(infoPtr);
6804 return TRUE;
6807 /***
6808 * DESCRIPTION:
6809 * Updates an items or rearranges the listview control.
6811 * PARAMETER(S):
6812 * [I] infoPtr : valid pointer to the listview structure
6813 * [I] nItem : item index
6815 * RETURN:
6816 * SUCCESS : TRUE
6817 * FAILURE : FALSE
6819 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6821 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6823 TRACE("(nItem=%d)\n", nItem);
6825 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6827 /* rearrange with default alignment style */
6828 if ((infoPtr->dwStyle & LVS_AUTOARRANGE) && (uView == LVS_ICON || uView == LVS_SMALLICON))
6829 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
6830 else
6831 LISTVIEW_InvalidateItem(infoPtr, nItem);
6833 return TRUE;
6837 /***
6838 * DESCRIPTION:
6839 * Creates the listview control.
6841 * PARAMETER(S):
6842 * [I] hwnd : window handle
6843 * [I] lpcs : the create parameters
6845 * RETURN:
6846 * Success: 0
6847 * Failure: -1
6849 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6851 LISTVIEW_INFO *infoPtr;
6852 UINT uView = lpcs->style & LVS_TYPEMASK;
6853 LOGFONTW logFont;
6855 TRACE("(lpcs=%p)\n", lpcs);
6857 /* initialize info pointer */
6858 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6859 if (!infoPtr) return -1;
6861 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6863 infoPtr->hwndSelf = hwnd;
6864 infoPtr->dwStyle = lpcs->style;
6865 /* determine the type of structures to use */
6866 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6867 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6869 /* initialize color information */
6870 infoPtr->clrBk = CLR_NONE;
6871 infoPtr->clrText = comctl32_color.clrWindowText;
6872 infoPtr->clrTextBk = CLR_DEFAULT;
6873 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6875 /* set default values */
6876 infoPtr->nFocusedItem = -1;
6877 infoPtr->nSelectionMark = -1;
6878 infoPtr->nHotItem = -1;
6879 infoPtr->bRedraw = TRUE;
6880 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6881 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6882 infoPtr->nEditLabelItem = -1;
6884 /* get default font (icon title) */
6885 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6886 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6887 infoPtr->hFont = infoPtr->hDefaultFont;
6888 LISTVIEW_SaveTextMetrics(infoPtr);
6890 /* create header */
6891 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6892 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6893 0, 0, 0, 0, hwnd, (HMENU)0,
6894 lpcs->hInstance, NULL);
6896 /* set header unicode format */
6897 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
6899 /* set header font */
6900 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
6902 /* allocate memory for the selection ranges */
6903 if (!(infoPtr->selectionRanges = ranges_create(10))) return -1;
6905 infoPtr->hdpaColumns = DPA_Create(10);
6907 if (uView == LVS_ICON)
6909 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6910 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6912 else if (uView == LVS_REPORT)
6914 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6916 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6918 else
6920 /* set HDS_HIDDEN flag to hide the header bar */
6921 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6922 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6926 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6927 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6929 else
6931 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6932 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6935 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
6936 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
6938 /* allocate memory for the data structure */
6939 infoPtr->hdpaItems = DPA_Create(10);
6940 infoPtr->hdpaPosX = DPA_Create(10);
6941 infoPtr->hdpaPosY = DPA_Create(10);
6943 /* initialize size of items */
6944 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6945 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6947 /* initialize the hover time to -1(indicating the default system hover time) */
6948 infoPtr->dwHoverTime = -1;
6950 return 0;
6953 /***
6954 * DESCRIPTION:
6955 * Erases the background of the listview control.
6957 * PARAMETER(S):
6958 * [I] infoPtr : valid pointer to the listview structure
6959 * [I] hdc : device context handle
6961 * RETURN:
6962 * SUCCESS : TRUE
6963 * FAILURE : FALSE
6965 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6967 RECT rc;
6969 TRACE("(hdc=%x)\n", hdc);
6971 if (!GetClipBox(hdc, &rc)) return FALSE;
6973 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
6977 /***
6978 * DESCRIPTION:
6979 * Helper function for LISTVIEW_[HV]Scroll *only*.
6980 * Performs vertical/horizontal scrolling by a give amount.
6982 * PARAMETER(S):
6983 * [I] infoPtr : valid pointer to the listview structure
6984 * [I] dx : amount of horizontal scroll
6985 * [I] dy : amount of vertical scroll
6987 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6989 /* now we can scroll the list */
6990 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
6991 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
6992 /* if we have focus, adjust rect */
6993 OffsetRect(&infoPtr->rcFocus, dx, dy);
6994 UpdateWindow(infoPtr->hwndSelf);
6997 /***
6998 * DESCRIPTION:
6999 * Performs vertical scrolling.
7001 * PARAMETER(S):
7002 * [I] infoPtr : valid pointer to the listview structure
7003 * [I] nScrollCode : scroll code
7004 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7005 * [I] hScrollWnd : scrollbar control window handle
7007 * RETURN:
7008 * Zero
7010 * NOTES:
7011 * SB_LINEUP/SB_LINEDOWN:
7012 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7013 * for LVS_REPORT is 1 line
7014 * for LVS_LIST cannot occur
7017 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7018 INT nScrollDiff, HWND hScrollWnd)
7020 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7021 INT nOldScrollPos, nNewScrollPos;
7022 SCROLLINFO scrollInfo;
7023 BOOL is_an_icon;
7025 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7027 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7029 scrollInfo.cbSize = sizeof(SCROLLINFO);
7030 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7032 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7034 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7036 nOldScrollPos = scrollInfo.nPos;
7037 switch (nScrollCode)
7039 case SB_INTERNAL:
7040 break;
7042 case SB_LINEUP:
7043 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7044 break;
7046 case SB_LINEDOWN:
7047 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7048 break;
7050 case SB_PAGEUP:
7051 nScrollDiff = -scrollInfo.nPage;
7052 break;
7054 case SB_PAGEDOWN:
7055 nScrollDiff = scrollInfo.nPage;
7056 break;
7058 case SB_THUMBPOSITION:
7059 case SB_THUMBTRACK:
7060 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7061 break;
7063 default:
7064 nScrollDiff = 0;
7067 /* quit right away if pos isn't changing */
7068 if (nScrollDiff == 0) return 0;
7070 /* calculate new position, and handle overflows */
7071 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7072 if (nScrollDiff > 0) {
7073 if (nNewScrollPos < nOldScrollPos ||
7074 nNewScrollPos > scrollInfo.nMax)
7075 nNewScrollPos = scrollInfo.nMax;
7076 } else {
7077 if (nNewScrollPos > nOldScrollPos ||
7078 nNewScrollPos < scrollInfo.nMin)
7079 nNewScrollPos = scrollInfo.nMin;
7082 /* set the new position, and reread in case it changed */
7083 scrollInfo.fMask = SIF_POS;
7084 scrollInfo.nPos = nNewScrollPos;
7085 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7087 /* carry on only if it really changed */
7088 if (nNewScrollPos == nOldScrollPos) return 0;
7090 /* now adjust to client coordinates */
7091 nScrollDiff = nOldScrollPos - nNewScrollPos;
7092 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7094 /* and scroll the window */
7095 scroll_list(infoPtr, 0, nScrollDiff);
7097 return 0;
7100 /***
7101 * DESCRIPTION:
7102 * Performs horizontal scrolling.
7104 * PARAMETER(S):
7105 * [I] infoPtr : valid pointer to the listview structure
7106 * [I] nScrollCode : scroll code
7107 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7108 * [I] hScrollWnd : scrollbar control window handle
7110 * RETURN:
7111 * Zero
7113 * NOTES:
7114 * SB_LINELEFT/SB_LINERIGHT:
7115 * for LVS_ICON, LVS_SMALLICON 1 pixel
7116 * for LVS_REPORT is 1 pixel
7117 * for LVS_LIST is 1 column --> which is a 1 because the
7118 * scroll is based on columns not pixels
7121 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7122 INT nScrollDiff, HWND hScrollWnd)
7124 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7125 INT nOldScrollPos, nNewScrollPos;
7126 SCROLLINFO scrollInfo;
7128 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7130 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7132 scrollInfo.cbSize = sizeof(SCROLLINFO);
7133 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7135 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7137 nOldScrollPos = scrollInfo.nPos;
7139 switch (nScrollCode)
7141 case SB_INTERNAL:
7142 break;
7144 case SB_LINELEFT:
7145 nScrollDiff = -1;
7146 break;
7148 case SB_LINERIGHT:
7149 nScrollDiff = 1;
7150 break;
7152 case SB_PAGELEFT:
7153 nScrollDiff = -scrollInfo.nPage;
7154 break;
7156 case SB_PAGERIGHT:
7157 nScrollDiff = scrollInfo.nPage;
7158 break;
7160 case SB_THUMBPOSITION:
7161 case SB_THUMBTRACK:
7162 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7163 break;
7165 default:
7166 nScrollDiff = 0;
7169 /* quit right away if pos isn't changing */
7170 if (nScrollDiff == 0) return 0;
7172 /* calculate new position, and handle overflows */
7173 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7174 if (nScrollDiff > 0) {
7175 if (nNewScrollPos < nOldScrollPos ||
7176 nNewScrollPos > scrollInfo.nMax)
7177 nNewScrollPos = scrollInfo.nMax;
7178 } else {
7179 if (nNewScrollPos > nOldScrollPos ||
7180 nNewScrollPos < scrollInfo.nMin)
7181 nNewScrollPos = scrollInfo.nMin;
7184 /* set the new position, and reread in case it changed */
7185 scrollInfo.fMask = SIF_POS;
7186 scrollInfo.nPos = nNewScrollPos;
7187 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7189 /* carry on only if it really changed */
7190 if (nNewScrollPos == nOldScrollPos) return 0;
7192 if(uView == LVS_REPORT)
7193 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7195 /* now adjust to client coordinates */
7196 nScrollDiff = nOldScrollPos - nNewScrollPos;
7197 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7199 /* and scroll the window */
7200 scroll_list(infoPtr, nScrollDiff, 0);
7202 return 0;
7205 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7207 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7208 INT gcWheelDelta = 0;
7209 UINT pulScrollLines = 3;
7210 SCROLLINFO scrollInfo;
7212 TRACE("(wheelDelta=%d)\n", wheelDelta);
7214 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7215 gcWheelDelta -= wheelDelta;
7217 scrollInfo.cbSize = sizeof(SCROLLINFO);
7218 scrollInfo.fMask = SIF_POS;
7220 switch(uView)
7222 case LVS_ICON:
7223 case LVS_SMALLICON:
7225 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7226 * should be fixed in the future.
7228 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7229 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7230 scrollInfo.nPos + (gcWheelDelta < 0) ?
7231 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7232 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7233 break;
7235 case LVS_REPORT:
7236 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7238 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7240 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7241 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7242 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7245 break;
7247 case LVS_LIST:
7248 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7249 break;
7251 return 0;
7254 /***
7255 * DESCRIPTION:
7256 * ???
7258 * PARAMETER(S):
7259 * [I] infoPtr : valid pointer to the listview structure
7260 * [I] nVirtualKey : virtual key
7261 * [I] lKeyData : key data
7263 * RETURN:
7264 * Zero
7266 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7268 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7269 INT nItem = -1;
7270 NMLVKEYDOWN nmKeyDown;
7272 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7274 /* send LVN_KEYDOWN notification */
7275 nmKeyDown.wVKey = nVirtualKey;
7276 nmKeyDown.flags = 0;
7277 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7279 switch (nVirtualKey)
7281 case VK_RETURN:
7282 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7284 notify(infoPtr, NM_RETURN);
7285 notify(infoPtr, LVN_ITEMACTIVATE);
7287 break;
7289 case VK_HOME:
7290 if (infoPtr->nItemCount > 0)
7291 nItem = 0;
7292 break;
7294 case VK_END:
7295 if (infoPtr->nItemCount > 0)
7296 nItem = infoPtr->nItemCount - 1;
7297 break;
7299 case VK_LEFT:
7300 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7301 break;
7303 case VK_UP:
7304 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7305 break;
7307 case VK_RIGHT:
7308 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7309 break;
7311 case VK_DOWN:
7312 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7313 break;
7315 case VK_PRIOR:
7316 if (uView == LVS_REPORT)
7317 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7318 else
7319 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7320 * LISTVIEW_GetCountPerRow(infoPtr);
7321 if(nItem < 0) nItem = 0;
7322 break;
7324 case VK_NEXT:
7325 if (uView == LVS_REPORT)
7326 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7327 else
7328 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7329 * LISTVIEW_GetCountPerRow(infoPtr);
7330 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7331 break;
7334 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7335 LISTVIEW_KeySelection(infoPtr, nItem);
7337 return 0;
7340 /***
7341 * DESCRIPTION:
7342 * Kills the focus.
7344 * PARAMETER(S):
7345 * [I] infoPtr : valid pointer to the listview structure
7347 * RETURN:
7348 * Zero
7350 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7352 TRACE("()\n");
7354 /* if we did not have the focus, there's nothing to do */
7355 if (!infoPtr->bFocus) return 0;
7357 /* send NM_KILLFOCUS notification */
7358 notify(infoPtr, NM_KILLFOCUS);
7360 /* if we have a focus rectagle, get rid of it */
7361 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7363 /* set window focus flag */
7364 infoPtr->bFocus = FALSE;
7366 /* invalidate the selected items before reseting focus flag */
7367 LISTVIEW_InvalidateSelectedItems(infoPtr);
7369 return 0;
7372 /***
7373 * DESCRIPTION:
7374 * Processes double click messages (left mouse button).
7376 * PARAMETER(S):
7377 * [I] infoPtr : valid pointer to the listview structure
7378 * [I] wKey : key flag
7379 * [I] pts : mouse coordinate
7381 * RETURN:
7382 * Zero
7384 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7386 LVHITTESTINFO htInfo;
7388 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7390 /* send NM_RELEASEDCAPTURE notification */
7391 notify(infoPtr, NM_RELEASEDCAPTURE);
7393 htInfo.pt.x = pts.x;
7394 htInfo.pt.y = pts.y;
7396 /* send NM_DBLCLK notification */
7397 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7398 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7400 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7401 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7403 return 0;
7406 /***
7407 * DESCRIPTION:
7408 * Processes mouse down messages (left mouse button).
7410 * PARAMETER(S):
7411 * [I] infoPtr : valid pointer to the listview structure
7412 * [I] wKey : key flag
7413 * [I] pts : mouse coordinate
7415 * RETURN:
7416 * Zero
7418 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7420 LVHITTESTINFO lvHitTestInfo;
7421 static BOOL bGroupSelect = TRUE;
7422 POINT pt = { pts.x, pts.y };
7423 INT nItem;
7425 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7427 /* send NM_RELEASEDCAPTURE notification */
7428 notify(infoPtr, NM_RELEASEDCAPTURE);
7430 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7432 /* set left button down flag */
7433 infoPtr->bLButtonDown = TRUE;
7435 lvHitTestInfo.pt.x = pts.x;
7436 lvHitTestInfo.pt.y = pts.y;
7438 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7439 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7440 infoPtr->nEditLabelItem = -1;
7441 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7443 if (infoPtr->dwStyle & LVS_SINGLESEL)
7445 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7446 infoPtr->nEditLabelItem = nItem;
7447 else
7448 LISTVIEW_SetSelection(infoPtr, nItem);
7450 else
7452 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7454 if (bGroupSelect)
7456 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7457 LISTVIEW_SetItemFocus(infoPtr, nItem);
7458 infoPtr->nSelectionMark = nItem;
7460 else
7462 LVITEMW item;
7464 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7465 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7467 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7468 infoPtr->nSelectionMark = nItem;
7471 else if (wKey & MK_CONTROL)
7473 LVITEMW item;
7475 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7477 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7478 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7479 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7480 infoPtr->nSelectionMark = nItem;
7482 else if (wKey & MK_SHIFT)
7484 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7486 else
7488 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7489 infoPtr->nEditLabelItem = nItem;
7491 /* set selection (clears other pre-existing selections) */
7492 LISTVIEW_SetSelection(infoPtr, nItem);
7496 else
7498 /* remove all selections */
7499 LISTVIEW_DeselectAll(infoPtr);
7502 return 0;
7505 /***
7506 * DESCRIPTION:
7507 * Processes mouse up messages (left mouse button).
7509 * PARAMETER(S):
7510 * [I] infoPtr : valid pointer to the listview structure
7511 * [I] wKey : key flag
7512 * [I] pts : mouse coordinate
7514 * RETURN:
7515 * Zero
7517 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7519 LVHITTESTINFO lvHitTestInfo;
7521 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7523 if (!infoPtr->bLButtonDown) return 0;
7525 lvHitTestInfo.pt.x = pts.x;
7526 lvHitTestInfo.pt.y = pts.y;
7528 /* send NM_CLICK notification */
7529 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7530 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7532 /* set left button flag */
7533 infoPtr->bLButtonDown = FALSE;
7535 /* if we clicked on a selected item, edit the label */
7536 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7537 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7539 return 0;
7542 /***
7543 * DESCRIPTION:
7544 * Destroys the listview control (called after WM_DESTROY).
7546 * PARAMETER(S):
7547 * [I] infoPtr : valid pointer to the listview structure
7549 * RETURN:
7550 * Zero
7552 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7554 TRACE("()\n");
7556 /* delete all items */
7557 LISTVIEW_DeleteAllItems(infoPtr);
7559 /* destroy data structure */
7560 DPA_Destroy(infoPtr->hdpaItems);
7561 if (infoPtr->selectionRanges) ranges_destroy(infoPtr->selectionRanges);
7563 /* destroy image lists */
7564 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7566 if (infoPtr->himlNormal)
7567 ImageList_Destroy(infoPtr->himlNormal);
7568 if (infoPtr->himlSmall)
7569 ImageList_Destroy(infoPtr->himlSmall);
7570 if (infoPtr->himlState)
7571 ImageList_Destroy(infoPtr->himlState);
7574 /* destroy font, bkgnd brush */
7575 infoPtr->hFont = 0;
7576 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7577 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7579 /* free listview info pointer*/
7580 COMCTL32_Free(infoPtr);
7582 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7583 return 0;
7586 /***
7587 * DESCRIPTION:
7588 * Handles notifications from header.
7590 * PARAMETER(S):
7591 * [I] infoPtr : valid pointer to the listview structure
7592 * [I] nCtrlId : control identifier
7593 * [I] lpnmh : notification information
7595 * RETURN:
7596 * Zero
7598 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, LPNMHEADERW lpnmh)
7600 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7602 TRACE("(lpnmh=%p)\n", lpnmh);
7604 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7606 switch (lpnmh->hdr.code)
7608 case HDN_TRACKW:
7609 case HDN_TRACKA:
7610 case HDN_ITEMCHANGEDW:
7611 case HDN_ITEMCHANGEDA:
7613 COLUMN_INFO *lpColumnInfo;
7614 INT dx, cxy;
7616 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7618 HDITEMW hdi;
7620 hdi.mask = HDI_WIDTH;
7621 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7622 cxy = hdi.cxy;
7624 else
7625 cxy = lpnmh->pitem->cxy;
7627 /* determine how much we change since the last know position */
7628 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7629 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7630 if (dx != 0)
7632 lpColumnInfo->rcHeader.right += dx;
7633 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7634 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, lpnmh->iItem);
7637 break;
7639 case HDN_ITEMCLICKW:
7640 case HDN_ITEMCLICKA:
7642 /* Handle sorting by Header Column */
7643 NMLISTVIEW nmlv;
7645 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7646 nmlv.iItem = -1;
7647 nmlv.iSubItem = lpnmh->iItem;
7648 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7650 break;
7653 return 0;
7656 /***
7657 * DESCRIPTION:
7658 * Determines the type of structure to use.
7660 * PARAMETER(S):
7661 * [I] infoPtr : valid pointer to the listview structureof the sender
7662 * [I] hwndFrom : listview window handle
7663 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7665 * RETURN:
7666 * Zero
7668 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7670 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7672 if (nCommand == NF_REQUERY)
7673 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7674 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7675 return 0;
7678 /***
7679 * DESCRIPTION:
7680 * Paints/Repaints the listview control.
7682 * PARAMETER(S):
7683 * [I] infoPtr : valid pointer to the listview structure
7684 * [I] hdc : device context handle
7686 * RETURN:
7687 * Zero
7689 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7691 TRACE("(hdc=%x)\n", hdc);
7693 if (hdc)
7694 LISTVIEW_Refresh(infoPtr, hdc);
7695 else
7697 PAINTSTRUCT ps;
7699 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7700 if (!hdc) return 1;
7701 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7702 LISTVIEW_Refresh(infoPtr, hdc);
7703 EndPaint(infoPtr->hwndSelf, &ps);
7706 return 0;
7709 /***
7710 * DESCRIPTION:
7711 * Processes double click messages (right mouse button).
7713 * PARAMETER(S):
7714 * [I] infoPtr : valid pointer to the listview structure
7715 * [I] wKey : key flag
7716 * [I] pts : mouse coordinate
7718 * RETURN:
7719 * Zero
7721 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7723 LVHITTESTINFO lvHitTestInfo;
7725 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7727 /* send NM_RELEASEDCAPTURE notification */
7728 notify(infoPtr, NM_RELEASEDCAPTURE);
7730 /* send NM_RDBLCLK notification */
7731 lvHitTestInfo.pt.x = pts.x;
7732 lvHitTestInfo.pt.y = pts.y;
7733 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7734 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7736 return 0;
7739 /***
7740 * DESCRIPTION:
7741 * Processes mouse down messages (right mouse button).
7743 * PARAMETER(S):
7744 * [I] infoPtr : valid pointer to the listview structure
7745 * [I] wKey : key flag
7746 * [I] pts : mouse coordinate
7748 * RETURN:
7749 * Zero
7751 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7753 LVHITTESTINFO lvHitTestInfo;
7754 INT nItem;
7756 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7758 /* send NM_RELEASEDCAPTURE notification */
7759 notify(infoPtr, NM_RELEASEDCAPTURE);
7761 /* make sure the listview control window has the focus */
7762 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7764 /* set right button down flag */
7765 infoPtr->bRButtonDown = TRUE;
7767 /* determine the index of the selected item */
7768 lvHitTestInfo.pt.x = pts.x;
7769 lvHitTestInfo.pt.y = pts.y;
7770 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7772 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7774 LISTVIEW_SetItemFocus(infoPtr, nItem);
7775 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7776 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7777 LISTVIEW_SetSelection(infoPtr, nItem);
7779 else
7781 LISTVIEW_DeselectAll(infoPtr);
7784 return 0;
7787 /***
7788 * DESCRIPTION:
7789 * Processes mouse up messages (right mouse button).
7791 * PARAMETER(S):
7792 * [I] infoPtr : valid pointer to the listview structure
7793 * [I] wKey : key flag
7794 * [I] pts : mouse coordinate
7796 * RETURN:
7797 * Zero
7799 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7801 LVHITTESTINFO lvHitTestInfo;
7802 POINT pt;
7804 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7806 if (!infoPtr->bRButtonDown) return 0;
7808 /* set button flag */
7809 infoPtr->bRButtonDown = FALSE;
7811 /* Send NM_RClICK notification */
7812 lvHitTestInfo.pt.x = pts.x;
7813 lvHitTestInfo.pt.y = pts.y;
7814 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7815 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7817 /* Change to screen coordinate for WM_CONTEXTMENU */
7818 pt = lvHitTestInfo.pt;
7819 ClientToScreen(infoPtr->hwndSelf, &pt);
7821 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7822 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7823 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7825 return 0;
7829 /***
7830 * DESCRIPTION:
7831 * Sets the cursor.
7833 * PARAMETER(S):
7834 * [I] infoPtr : valid pointer to the listview structure
7835 * [I] hwnd : window handle of window containing the cursor
7836 * [I] nHittest : hit-test code
7837 * [I] wMouseMsg : ideintifier of the mouse message
7839 * RETURN:
7840 * TRUE if cursor is set
7841 * FALSE otherwise
7843 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7845 LVHITTESTINFO lvHitTestInfo;
7847 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7849 if(!infoPtr->hHotCursor) return FALSE;
7851 GetCursorPos(&lvHitTestInfo.pt);
7852 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7854 SetCursor(infoPtr->hHotCursor);
7856 return TRUE;
7859 /***
7860 * DESCRIPTION:
7861 * Sets the focus.
7863 * PARAMETER(S):
7864 * [I] infoPtr : valid pointer to the listview structure
7865 * [I] hwndLoseFocus : handle of previously focused window
7867 * RETURN:
7868 * Zero
7870 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7872 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
7874 /* if we have the focus already, there's nothing to do */
7875 if (infoPtr->bFocus) return 0;
7877 /* send NM_SETFOCUS notification */
7878 notify(infoPtr, NM_SETFOCUS);
7880 /* set window focus flag */
7881 infoPtr->bFocus = TRUE;
7883 /* put the focus rect back on */
7884 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7886 /* redraw all visible selected items */
7887 LISTVIEW_InvalidateSelectedItems(infoPtr);
7889 return 0;
7892 /***
7893 * DESCRIPTION:
7894 * Sets the font.
7896 * PARAMETER(S):
7897 * [I] infoPtr : valid pointer to the listview structure
7898 * [I] fRedraw : font handle
7899 * [I] fRedraw : redraw flag
7901 * RETURN:
7902 * Zero
7904 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7906 HFONT oldFont = infoPtr->hFont;
7908 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
7910 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7911 if (infoPtr->hFont == oldFont) return 0;
7913 LISTVIEW_SaveTextMetrics(infoPtr);
7915 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7916 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7918 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7920 return 0;
7923 /***
7924 * DESCRIPTION:
7925 * Message handling for WM_SETREDRAW.
7926 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7928 * PARAMETER(S):
7929 * [I] infoPtr : valid pointer to the listview structure
7930 * [I] bRedraw: state of redraw flag
7932 * RETURN:
7933 * DefWinProc return value
7935 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7937 infoPtr->bRedraw = bRedraw;
7938 if(bRedraw)
7939 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
7940 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
7941 return 0;
7944 /***
7945 * DESCRIPTION:
7946 * Resizes the listview control. This function processes WM_SIZE
7947 * messages. At this time, the width and height are not used.
7949 * PARAMETER(S):
7950 * [I] infoPtr : valid pointer to the listview structure
7951 * [I] Width : new width
7952 * [I] Height : new height
7954 * RETURN:
7955 * Zero
7957 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
7959 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7961 TRACE("(width=%d, height=%d)\n", Width, Height);
7963 if (!LISTVIEW_UpdateSize(infoPtr)) return 0;
7965 if ((infoPtr->dwStyle & LVS_AUTOARRANGE) && (uView == LVS_SMALLICON || uView == LVS_ICON))
7966 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7968 LISTVIEW_UpdateScroll(infoPtr);
7970 LISTVIEW_InvalidateList(infoPtr);
7972 return 0;
7975 /***
7976 * DESCRIPTION:
7977 * Sets the size information.
7979 * PARAMETER(S):
7980 * [I] infoPtr : valid pointer to the listview structure
7982 * RETURN:
7983 * Zero if no size change
7984 * 1 of size changed
7986 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
7988 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7989 RECT rcList;
7990 RECT rcOld;
7992 GetClientRect(infoPtr->hwndSelf, &rcList);
7993 CopyRect(&rcOld,&(infoPtr->rcList));
7994 infoPtr->rcList.left = 0;
7995 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
7996 infoPtr->rcList.top = 0;
7997 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
7999 if (uView == LVS_LIST)
8001 /* Apparently the "LIST" style is supposed to have the same
8002 * number of items in a column even if there is no scroll bar.
8003 * Since if a scroll bar already exists then the bottom is already
8004 * reduced, only reduce if the scroll bar does not currently exist.
8005 * The "2" is there to mimic the native control. I think it may be
8006 * related to either padding or edges. (GLA 7/2002)
8008 if (!(infoPtr->dwStyle & WS_HSCROLL))
8010 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8011 if (infoPtr->rcList.bottom > nHScrollHeight)
8012 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8014 else
8016 if (infoPtr->rcList.bottom > 2)
8017 infoPtr->rcList.bottom -= 2;
8020 else if (uView == LVS_REPORT)
8022 HDLAYOUT hl;
8023 WINDOWPOS wp;
8025 hl.prc = &rcList;
8026 hl.pwpos = &wp;
8027 Header_Layout(infoPtr->hwndHeader, &hl);
8029 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8031 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
8032 infoPtr->rcList.top = max(wp.cy, 0);
8034 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8037 /***
8038 * DESCRIPTION:
8039 * Processes WM_STYLECHANGED messages.
8041 * PARAMETER(S):
8042 * [I] infoPtr : valid pointer to the listview structure
8043 * [I] wStyleType : window style type (normal or extended)
8044 * [I] lpss : window style information
8046 * RETURN:
8047 * Zero
8049 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8050 LPSTYLESTRUCT lpss)
8052 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8053 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8055 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8056 wStyleType, lpss->styleOld, lpss->styleNew);
8058 if (wStyleType != GWL_STYLE) return 0;
8060 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8061 /* what if LVS_OWNERDATA changed? */
8062 /* or LVS_SINGLESEL */
8063 /* or LVS_SORT{AS,DES}CENDING */
8065 infoPtr->dwStyle = lpss->styleNew;
8067 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8068 ((lpss->styleNew & WS_HSCROLL) == 0))
8069 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8071 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8072 ((lpss->styleNew & WS_VSCROLL) == 0))
8073 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8075 if (uNewView != uOldView)
8077 SIZE oldIconSize = infoPtr->iconSize;
8078 HIMAGELIST himl;
8080 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8081 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8083 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8084 SetRectEmpty(&infoPtr->rcFocus);
8086 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8087 update_icon_size(himl, uNewView != LVS_ICON, &infoPtr->iconSize);
8089 if (uNewView == LVS_ICON)
8091 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8093 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8094 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8095 LISTVIEW_SetIconSpacing(infoPtr, 0);
8098 else if (uNewView == LVS_REPORT)
8100 HDLAYOUT hl;
8101 WINDOWPOS wp;
8103 hl.prc = &infoPtr->rcList;
8104 hl.pwpos = &wp;
8105 Header_Layout(infoPtr->hwndHeader, &hl);
8106 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8109 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8110 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8113 if (uNewView == LVS_REPORT)
8114 ShowWindow(infoPtr->hwndHeader, (LVS_NOCOLUMNHEADER & lpss->styleNew) ? SW_HIDE : SW_SHOWNORMAL);
8116 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8117 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8118 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8120 /* update the size of the client area */
8121 LISTVIEW_UpdateSize(infoPtr);
8123 /* add scrollbars if needed */
8124 LISTVIEW_UpdateScroll(infoPtr);
8126 /* invalidate client area + erase background */
8127 LISTVIEW_InvalidateList(infoPtr);
8129 return 0;
8132 /***
8133 * DESCRIPTION:
8134 * Window procedure of the listview control.
8137 static LRESULT WINAPI
8138 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8140 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8142 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8144 if (!infoPtr && (uMsg != WM_CREATE))
8145 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8147 if (infoPtr)
8149 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8152 switch (uMsg)
8154 case LVM_APPROXIMATEVIEWRECT:
8155 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8156 LOWORD(lParam), HIWORD(lParam));
8157 case LVM_ARRANGE:
8158 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8160 /* case LVM_CANCELEDITLABEL: */
8162 /* case LVM_CREATEDRAGIMAGE: */
8164 case LVM_DELETEALLITEMS:
8165 return LISTVIEW_DeleteAllItems(infoPtr);
8167 case LVM_DELETECOLUMN:
8168 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8170 case LVM_DELETEITEM:
8171 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8173 case LVM_EDITLABELW:
8174 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8176 case LVM_EDITLABELA:
8177 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8179 /* case LVM_ENABLEGROUPVIEW: */
8181 case LVM_ENSUREVISIBLE:
8182 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8184 case LVM_FINDITEMW:
8185 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8187 case LVM_FINDITEMA:
8188 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8190 case LVM_GETBKCOLOR:
8191 return infoPtr->clrBk;
8193 /* case LVM_GETBKIMAGE: */
8195 case LVM_GETCALLBACKMASK:
8196 return infoPtr->uCallbackMask;
8198 case LVM_GETCOLUMNA:
8199 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8201 case LVM_GETCOLUMNW:
8202 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8204 case LVM_GETCOLUMNORDERARRAY:
8205 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8207 case LVM_GETCOLUMNWIDTH:
8208 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8210 case LVM_GETCOUNTPERPAGE:
8211 return LISTVIEW_GetCountPerPage(infoPtr);
8213 case LVM_GETEDITCONTROL:
8214 return (LRESULT)infoPtr->hwndEdit;
8216 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8217 return infoPtr->dwLvExStyle;
8219 /* case LVM_GETGROUPINFO: */
8221 /* case LVM_GETGROUPMETRICS: */
8223 case LVM_GETHEADER:
8224 return (LRESULT)infoPtr->hwndHeader;
8226 case LVM_GETHOTCURSOR:
8227 return (LRESULT)infoPtr->hHotCursor;
8229 case LVM_GETHOTITEM:
8230 return infoPtr->nHotItem;
8232 case LVM_GETHOVERTIME:
8233 return infoPtr->dwHoverTime;
8235 case LVM_GETIMAGELIST:
8236 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8238 /* case LVM_GETINSERTMARK: */
8240 /* case LVM_GETINSERTMARKCOLOR: */
8242 /* case LVM_GETINSERTMARKRECT: */
8244 case LVM_GETISEARCHSTRINGA:
8245 case LVM_GETISEARCHSTRINGW:
8246 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8247 return FALSE;
8249 case LVM_GETITEMA:
8250 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8252 case LVM_GETITEMW:
8253 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8255 case LVM_GETITEMCOUNT:
8256 return infoPtr->nItemCount;
8258 case LVM_GETITEMPOSITION:
8259 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8261 case LVM_GETITEMRECT:
8262 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8264 case LVM_GETITEMSPACING:
8265 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8267 case LVM_GETITEMSTATE:
8268 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8270 case LVM_GETITEMTEXTA:
8271 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8273 case LVM_GETITEMTEXTW:
8274 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8276 case LVM_GETNEXTITEM:
8277 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8279 case LVM_GETNUMBEROFWORKAREAS:
8280 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8281 return 1;
8283 case LVM_GETORIGIN:
8284 if (!lParam) return FALSE;
8285 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8286 return TRUE;
8288 /* case LVM_GETOUTLINECOLOR: */
8290 /* case LVM_GETSELECTEDCOLUMN: */
8292 case LVM_GETSELECTEDCOUNT:
8293 return LISTVIEW_GetSelectedCount(infoPtr);
8295 case LVM_GETSELECTIONMARK:
8296 return infoPtr->nSelectionMark;
8298 case LVM_GETSTRINGWIDTHA:
8299 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8301 case LVM_GETSTRINGWIDTHW:
8302 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8304 case LVM_GETSUBITEMRECT:
8305 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8307 case LVM_GETTEXTBKCOLOR:
8308 return infoPtr->clrTextBk;
8310 case LVM_GETTEXTCOLOR:
8311 return infoPtr->clrText;
8313 /* case LVM_GETTILEINFO: */
8315 /* case LVM_GETTILEVIEWINFO: */
8317 case LVM_GETTOOLTIPS:
8318 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8319 return FALSE;
8321 case LVM_GETTOPINDEX:
8322 return LISTVIEW_GetTopIndex(infoPtr);
8324 /*case LVM_GETUNICODEFORMAT:
8325 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8326 return FALSE;*/
8328 /* case LVM_GETVIEW: */
8330 case LVM_GETVIEWRECT:
8331 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8333 case LVM_GETWORKAREAS:
8334 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8335 return FALSE;
8337 /* case LVM_HASGROUP: */
8339 case LVM_HITTEST:
8340 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8342 case LVM_INSERTCOLUMNA:
8343 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8345 case LVM_INSERTCOLUMNW:
8346 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8348 /* case LVM_INSERTGROUP: */
8350 /* case LVM_INSERTGROUPSORTED: */
8352 case LVM_INSERTITEMA:
8353 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8355 case LVM_INSERTITEMW:
8356 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8358 /* case LVM_INSERTMARKHITTEST: */
8360 /* case LVM_ISGROUPVIEWENABLED: */
8362 /* case LVM_MAPIDTOINDEX: */
8364 /* case LVM_MAPINDEXTOID: */
8366 /* case LVM_MOVEGROUP: */
8368 /* case LVM_MOVEITEMTOGROUP: */
8370 case LVM_REDRAWITEMS:
8371 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8373 /* case LVM_REMOVEALLGROUPS: */
8375 /* case LVM_REMOVEGROUP: */
8377 case LVM_SCROLL:
8378 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8380 case LVM_SETBKCOLOR:
8381 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8383 /* case LVM_SETBKIMAGE: */
8385 case LVM_SETCALLBACKMASK:
8386 infoPtr->uCallbackMask = (UINT)wParam;
8387 return TRUE;
8389 case LVM_SETCOLUMNA:
8390 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8392 case LVM_SETCOLUMNW:
8393 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8395 case LVM_SETCOLUMNORDERARRAY:
8396 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8398 case LVM_SETCOLUMNWIDTH:
8399 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8401 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8402 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8404 /* case LVM_SETGROUPINFO: */
8406 /* case LVM_SETGROUPMETRICS: */
8408 case LVM_SETHOTCURSOR:
8409 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8411 case LVM_SETHOTITEM:
8412 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8414 case LVM_SETHOVERTIME:
8415 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8417 case LVM_SETICONSPACING:
8418 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8420 case LVM_SETIMAGELIST:
8421 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8423 /* case LVM_SETINFOTIP: */
8425 /* case LVM_SETINSERTMARK: */
8427 /* case LVM_SETINSERTMARKCOLOR: */
8429 case LVM_SETITEMA:
8430 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8432 case LVM_SETITEMW:
8433 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8435 case LVM_SETITEMCOUNT:
8436 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8438 case LVM_SETITEMPOSITION:
8440 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8441 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8444 case LVM_SETITEMPOSITION32:
8445 if (lParam == 0) return FALSE;
8446 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8448 case LVM_SETITEMSTATE:
8449 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8451 case LVM_SETITEMTEXTA:
8452 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8454 case LVM_SETITEMTEXTW:
8455 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8457 /* case LVM_SETOUTLINECOLOR: */
8459 /* case LVM_SETSELECTEDCOLUMN: */
8461 case LVM_SETSELECTIONMARK:
8462 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8464 case LVM_SETTEXTBKCOLOR:
8465 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8467 case LVM_SETTEXTCOLOR:
8468 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8470 /* case LVM_SETTILEINFO: */
8472 /* case LVM_SETTILEVIEWINFO: */
8474 /* case LVM_SETTILEWIDTH: */
8476 /* case LVM_SETTOOLTIPS: */
8478 /* case LVM_SETUNICODEFORMAT: */
8480 /* case LVM_SETVIEW: */
8482 /* case LVM_SETWORKAREAS: */
8484 /* case LVM_SORTGROUPS: */
8486 case LVM_SORTITEMS:
8487 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8489 /* LVM_SORTITEMSEX: */
8491 case LVM_SUBITEMHITTEST:
8492 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8494 case LVM_UPDATE:
8495 return LISTVIEW_Update(infoPtr, (INT)wParam);
8497 case WM_CHAR:
8498 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8500 case WM_COMMAND:
8501 return LISTVIEW_Command(infoPtr, wParam, lParam);
8503 case WM_CREATE:
8504 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8506 case WM_ERASEBKGND:
8507 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8509 case WM_GETDLGCODE:
8510 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8512 case WM_GETFONT:
8513 return infoPtr->hFont;
8515 case WM_HSCROLL:
8516 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8518 case WM_KEYDOWN:
8519 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8521 case WM_KILLFOCUS:
8522 return LISTVIEW_KillFocus(infoPtr);
8524 case WM_LBUTTONDBLCLK:
8525 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8527 case WM_LBUTTONDOWN:
8528 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8530 case WM_LBUTTONUP:
8531 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8533 case WM_MOUSEMOVE:
8534 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8536 case WM_MOUSEHOVER:
8537 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8539 case WM_NCDESTROY:
8540 return LISTVIEW_NCDestroy(infoPtr);
8542 case WM_NOTIFY:
8543 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8544 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8545 else return 0;
8547 case WM_NOTIFYFORMAT:
8548 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8550 case WM_PAINT:
8551 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8553 case WM_RBUTTONDBLCLK:
8554 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8556 case WM_RBUTTONDOWN:
8557 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8559 case WM_RBUTTONUP:
8560 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8562 case WM_SETCURSOR:
8563 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8564 return TRUE;
8565 goto fwd_msg;
8567 case WM_SETFOCUS:
8568 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8570 case WM_SETFONT:
8571 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8573 case WM_SETREDRAW:
8574 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8576 case WM_SIZE:
8577 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8579 case WM_STYLECHANGED:
8580 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8582 case WM_SYSCOLORCHANGE:
8583 COMCTL32_RefreshSysColors();
8584 return 0;
8586 /* case WM_TIMER: */
8588 case WM_VSCROLL:
8589 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8591 case WM_MOUSEWHEEL:
8592 if (wParam & (MK_SHIFT | MK_CONTROL))
8593 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8594 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8596 case WM_WINDOWPOSCHANGED:
8597 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8598 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8599 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8600 LISTVIEW_UpdateSize(infoPtr);
8601 LISTVIEW_UpdateScroll(infoPtr);
8603 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8605 /* case WM_WININICHANGE: */
8607 default:
8608 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8609 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8611 fwd_msg:
8612 /* call default window procedure */
8613 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8616 return 0;
8619 /***
8620 * DESCRIPTION:
8621 * Registers the window class.
8623 * PARAMETER(S):
8624 * None
8626 * RETURN:
8627 * None
8629 void LISTVIEW_Register(void)
8631 WNDCLASSW wndClass;
8633 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8634 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8635 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8636 wndClass.cbClsExtra = 0;
8637 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8638 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8639 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8640 wndClass.lpszClassName = WC_LISTVIEWW;
8641 RegisterClassW(&wndClass);
8644 /***
8645 * DESCRIPTION:
8646 * Unregisters the window class.
8648 * PARAMETER(S):
8649 * None
8651 * RETURN:
8652 * None
8654 void LISTVIEW_Unregister(void)
8656 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8659 /***
8660 * DESCRIPTION:
8661 * Handle any WM_COMMAND messages
8663 * PARAMETER(S):
8664 * [I] infoPtr : valid pointer to the listview structure
8665 * [I] wParam : the first message parameter
8666 * [I] lParam : the second message parameter
8668 * RETURN:
8669 * Zero.
8671 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8673 switch (HIWORD(wParam))
8675 case EN_UPDATE:
8678 * Adjust the edit window size
8680 WCHAR buffer[1024];
8681 HDC hdc = GetDC(infoPtr->hwndEdit);
8682 HFONT hFont, hOldFont = 0;
8683 RECT rect;
8684 SIZE sz;
8685 int len;
8687 if (!infoPtr->hwndEdit || !hdc) return 0;
8688 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8689 GetWindowRect(infoPtr->hwndEdit, &rect);
8691 /* Select font to get the right dimension of the string */
8692 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8693 if(hFont != 0)
8695 hOldFont = SelectObject(hdc, hFont);
8698 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8700 TEXTMETRICW textMetric;
8702 /* Add Extra spacing for the next character */
8703 GetTextMetricsW(hdc, &textMetric);
8704 sz.cx += (textMetric.tmMaxCharWidth * 2);
8706 SetWindowPos (
8707 infoPtr->hwndEdit,
8708 HWND_TOP,
8711 sz.cx,
8712 rect.bottom - rect.top,
8713 SWP_DRAWFRAME|SWP_NOMOVE);
8715 if(hFont != 0)
8716 SelectObject(hdc, hOldFont);
8718 ReleaseDC(infoPtr->hwndSelf, hdc);
8720 break;
8723 default:
8724 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8727 return 0;
8731 /***
8732 * DESCRIPTION:
8733 * Subclassed edit control windproc function
8735 * PARAMETER(S):
8736 * [I] hwnd : the edit window handle
8737 * [I] uMsg : the message that is to be processed
8738 * [I] wParam : first message parameter
8739 * [I] lParam : second message parameter
8740 * [I] isW : TRUE if input is Unicode
8742 * RETURN:
8743 * Zero.
8745 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
8747 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8748 BOOL cancel = FALSE;
8750 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8751 hwnd, uMsg, wParam, lParam, isW);
8753 switch (uMsg)
8755 case WM_GETDLGCODE:
8756 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8758 case WM_KILLFOCUS:
8759 break;
8761 case WM_DESTROY:
8763 WNDPROC editProc = infoPtr->EditWndProc;
8764 infoPtr->EditWndProc = 0;
8765 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8766 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8769 case WM_KEYDOWN:
8770 if (VK_ESCAPE == (INT)wParam)
8772 cancel = TRUE;
8773 break;
8775 else if (VK_RETURN == (INT)wParam)
8776 break;
8778 default:
8779 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8782 /* kill the edit */
8783 if (infoPtr->hwndEdit)
8785 LPWSTR buffer = NULL;
8787 infoPtr->hwndEdit = 0;
8788 if (!cancel)
8790 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8792 if (len)
8794 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8796 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8797 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8801 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8803 if (buffer) COMCTL32_Free(buffer);
8807 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8808 return 0;
8811 /***
8812 * DESCRIPTION:
8813 * Subclassed edit control Unicode windproc function
8815 * PARAMETER(S):
8816 * [I] hwnd : the edit window handle
8817 * [I] uMsg : the message that is to be processed
8818 * [I] wParam : first message parameter
8819 * [I] lParam : second message parameter
8821 * RETURN:
8823 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8825 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8828 /***
8829 * DESCRIPTION:
8830 * Subclassed edit control ANSI windproc function
8832 * PARAMETER(S):
8833 * [I] hwnd : the edit window handle
8834 * [I] uMsg : the message that is to be processed
8835 * [I] wParam : first message parameter
8836 * [I] lParam : second message parameter
8838 * RETURN:
8840 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8842 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8845 /***
8846 * DESCRIPTION:
8847 * Creates a subclassed edit cotrol
8849 * PARAMETER(S):
8850 * [I] infoPtr : valid pointer to the listview structure
8851 * [I] text : initial text for the edit
8852 * [I] style : the window style
8853 * [I] isW : TRUE if input is Unicode
8855 * RETURN:
8857 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8858 INT x, INT y, INT width, INT height, BOOL isW)
8860 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8861 HWND hedit;
8862 SIZE sz;
8863 HDC hdc;
8864 HDC hOldFont=0;
8865 TEXTMETRICW textMetric;
8866 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8868 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8870 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8871 hdc = GetDC(infoPtr->hwndSelf);
8873 /* Select the font to get appropriate metric dimensions */
8874 if(infoPtr->hFont != 0)
8875 hOldFont = SelectObject(hdc, infoPtr->hFont);
8877 /*Get String Lenght in pixels */
8878 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8880 /*Add Extra spacing for the next character */
8881 GetTextMetricsW(hdc, &textMetric);
8882 sz.cx += (textMetric.tmMaxCharWidth * 2);
8884 if(infoPtr->hFont != 0)
8885 SelectObject(hdc, hOldFont);
8887 ReleaseDC(infoPtr->hwndSelf, hdc);
8888 if (isW)
8889 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8890 else
8891 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8893 if (!hedit) return 0;
8895 infoPtr->EditWndProc = (WNDPROC)
8896 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8897 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8899 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
8901 return hedit;