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
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.
36 * -- Hot item handling, mouse hovering
37 * -- Workareas support
42 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs.
44 * -- in LISTVIEW_AddGroupSelection, se whould send LVN_ODSTATECHANGED
45 * -- LVA_SNAPTOGRID not implemented
46 * -- LISTVIEW_ApproximateViewRect partially implemented
47 * -- LISTVIEW_[GS]etColumnOrderArray stubs
48 * -- LISTVIEW_GetNextItem is very inefficient
49 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
50 * -- LISTVIEW_SetIconSpacing is incomplete
51 * -- LVSICF_NOINVALIDATEALL, LVSICF_NOSCROLL not implemented
52 * -- LISTVIEW_SortItems is broken
53 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
56 * -- LISTVIEW_SetItemCount is too invalidation happy
57 * -- LISTVIEW_Size invalidates too much
58 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
59 * instead of inserting in the right spot
60 * -- we should keep an ordered array of coordinates in iconic mode
61 * this would allow to frame items (iterator_frameditems),
62 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
70 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
77 * -- LVS_NOSCROLL (see Q137520)
78 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
81 * -- LVS_EX_BORDERSELECT
82 * -- LVS_EX_CHECKBOXES
85 * -- LVS_EX_HEADERDRAGDROP
88 * -- LVS_EX_MULTIWORKAREAS
89 * -- LVS_EX_ONECLICKACTIVATE
91 * -- LVS_EX_SIMPLESELECT
92 * -- LVS_EX_SUBITEMIMAGES
93 * -- LVS_EX_TRACKSELECT
94 * -- LVS_EX_TWOCLICKACTIVATE
95 * -- LVS_EX_UNDERLINECOLD
96 * -- LVS_EX_UNDERLINEHOT
99 * -- LVN_BEGINDRAG, LVN_BEGINRDRAG
100 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
103 * -- LVN_MARQUEEBEGIN
105 * -- LVN_ODSTATECHANGED
110 * -- LVM_CANCELEDITLABEL
111 * -- LVM_CREATEDRAGIMAGE
112 * -- LVM_ENABLEGROUPVIEW
113 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
114 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
115 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
116 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
117 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
118 * -- LVM_GETINSERTMARKRECT
119 * -- LVM_GETNUMBEROFWORKAREAS
120 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
121 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
122 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
123 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
124 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
125 * -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
126 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
127 * -- LVM_GETVIEW, LVM_SETVIEW
128 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
129 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
130 * -- LVM_INSERTGROUPSORTED
131 * -- LVM_INSERTMARKHITTEST
132 * -- LVM_ISGROUPVIEWENABLED
133 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
135 * -- LVM_MOVEITEMTOGROUP
137 * -- LVM_SETTILEWIDTH
141 * Known differences in message stream from native control (not known if
142 * these differences cause problems):
143 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
144 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
145 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
146 * processing for "USEDOUBLECLICKTIME".
150 #include "wine/port.h"
161 #include "commctrl.h"
162 #include "comctl32.h"
164 #include "wine/debug.h"
165 #include "wine/unicode.h"
167 WINE_DEFAULT_DEBUG_CHANNEL(listview);
169 /* make sure you set this to 0 for production use! */
170 #define DEBUG_RANGES 1
172 typedef struct tagCOLUMN_INFO
174 RECT rcHeader; /* tracks the header's rectangle */
175 int fmt; /* same as LVCOLUMN.fmt */
178 typedef struct tagITEMHDR
182 } ITEMHDR, *LPITEMHDR;
184 typedef struct tagSUBITEM_INFO
190 typedef struct tagITEM_INFO
198 typedef struct tagRANGE
204 typedef struct tagRANGES
209 typedef struct tagITERATOR
218 typedef struct tagLISTVIEW_INFO
225 COLORREF clrTextBkDefault;
226 HIMAGELIST himlNormal;
227 HIMAGELIST himlSmall;
228 HIMAGELIST himlState;
233 RANGES selectionRanges;
237 RECT rcList; /* This rectangle is really the window
238 * client rectangle possibly reduced by the
239 * horizontal scroll bar and/or header - see
240 * LISTVIEW_UpdateSize. This rectangle offset
241 * by the LISTVIEW_GetOrigin value is in
242 * client coordinates */
251 INT ntmHeight; /* Some cached metrics of the font used */
252 INT ntmAveCharWidth; /* by the listview to draw items */
253 BOOL bRedraw; /* Turns on/off repaints & invalidations */
254 BOOL bFirstPaint; /* Flags if the control has never painted before */
255 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
259 DWORD dwStyle; /* the cached window GWL_STYLE */
260 DWORD dwLvExStyle; /* extended listview style */
261 INT nItemCount; /* the number of items in the list */
262 HDPA hdpaItems; /* array ITEM_INFO pointers */
263 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
264 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
265 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
266 POINT currIconPos; /* this is the position next icon will be placed */
267 PFNLVCOMPARE pfnCompare;
274 DWORD lastKeyPressTimestamp;
276 INT nSearchParamLength;
277 WCHAR szSearchParam[ MAX_PATH ];
284 /* How many we debug buffer to allocate */
285 #define DEBUG_BUFFERS 20
286 /* The size of a single debug bbuffer */
287 #define DEBUG_BUFFER_SIZE 256
289 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
290 #define SB_INTERNAL -1
292 /* maximum size of a label */
293 #define DISP_TEXT_SIZE 512
295 /* padding for items in list and small icon display modes */
296 #define WIDTH_PADDING 12
298 /* padding for items in list, report and small icon display modes */
299 #define HEIGHT_PADDING 1
301 /* offset of items in report display mode */
302 #define REPORT_MARGINX 2
304 /* padding for icon in large icon display mode
305 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
306 * that HITTEST will see.
307 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
308 * ICON_TOP_PADDING - sum of the two above.
309 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
310 * LABEL_VERT_PADDING - between bottom of text and end of box
312 * ICON_LR_PADDING - additional width above icon size.
313 * ICON_LR_HALF - half of the above value
315 #define ICON_TOP_PADDING_NOTHITABLE 2
316 #define ICON_TOP_PADDING_HITABLE 2
317 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
318 #define ICON_BOTTOM_PADDING 4
319 #define LABEL_VERT_PADDING 7
320 #define ICON_LR_PADDING 16
321 #define ICON_LR_HALF (ICON_LR_PADDING/2)
323 /* default label width for items in list and small icon display modes */
324 #define DEFAULT_LABEL_WIDTH 40
326 /* default column width for items in list display mode */
327 #define DEFAULT_COLUMN_WIDTH 128
329 /* Size of "line" scroll for V & H scrolls */
330 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
332 /* Padding betwen image and label */
333 #define IMAGE_PADDING 2
335 /* Padding behind the label */
336 #define TRAILING_PADDING 5
338 /* Border for the icon caption */
339 #define CAPTION_BORDER 2
341 /* Standard DrawText flags */
342 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
343 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
344 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
346 /* The time in milisecods to reset the search in the list */
347 #define KEY_DELAY 450
349 /* Dump the LISTVIEW_INFO structure to the debug channel */
350 #define LISTVIEW_DUMP(iP) do { \
351 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
352 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
353 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
354 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
355 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
356 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
357 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
358 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
359 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
360 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
365 * forward declarations
367 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
368 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
369 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
370 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
371 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
372 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
373 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
374 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
375 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
376 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
377 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
378 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
379 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
380 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
381 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
382 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
383 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
384 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
385 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
386 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
387 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
388 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
389 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
390 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
391 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
393 /******** Text handling functions *************************************/
395 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
396 * text string. The string may be ANSI or Unicode, in which case
397 * the boolean isW tells us the type of the string.
399 * The name of the function tell what type of strings it expects:
400 * W: Unicode, T: ANSI/Unicode - function of isW
403 static inline BOOL is_textW(LPCWSTR text)
405 return text != NULL && text != LPSTR_TEXTCALLBACKW;
408 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
410 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
411 return is_textW(text);
414 static inline int textlenT(LPCWSTR text, BOOL isW)
416 return !is_textT(text, isW) ? 0 :
417 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
420 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
423 if (isSrcW) lstrcpynW(dest, src, max);
424 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
426 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
427 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
430 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
432 LPWSTR wstr = (LPWSTR)text;
434 if (!isW && is_textT(text, isW))
436 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
437 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
438 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
440 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
444 static inline void textfreeT(LPWSTR wstr, BOOL isW)
446 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
450 * dest is a pointer to a Unicode string
451 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
453 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
457 if (src == LPSTR_TEXTCALLBACKW)
459 if (is_textW(*dest)) COMCTL32_Free(*dest);
460 *dest = LPSTR_TEXTCALLBACKW;
464 LPWSTR pszText = textdupTtoW(src, isW);
465 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
466 bResult = Str_SetPtrW(dest, pszText);
467 textfreeT(pszText, isW);
473 * compares a Unicode to a Unicode/ANSI text string
475 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
477 if (!aw) return bt ? -1 : 0;
478 if (!bt) return aw ? 1 : 0;
479 if (aw == LPSTR_TEXTCALLBACKW)
480 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
481 if (bt != LPSTR_TEXTCALLBACKW)
483 LPWSTR bw = textdupTtoW(bt, isW);
484 int r = bw ? lstrcmpW(aw, bw) : 1;
492 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
496 n = min(min(n, strlenW(s1)), strlenW(s2));
497 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
498 return res ? res - sizeof(WCHAR) : res;
501 /******** Debugging functions *****************************************/
503 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
505 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
506 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
509 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
511 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
512 n = min(textlenT(text, isW), n);
513 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
516 static char* debug_getbuf()
518 static int index = 0;
519 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
520 return buffers[index++ % DEBUG_BUFFERS];
523 static inline char* debugrange(const RANGE* lprng)
527 char* buf = debug_getbuf();
528 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
530 } else return "(null)";
533 static inline char* debugpoint(const POINT* lppt)
537 char* buf = debug_getbuf();
538 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
540 } else return "(null)";
543 static inline char* debugrect(const RECT* rect)
547 char* buf = debug_getbuf();
548 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
549 rect->left, rect->top, rect->right, rect->bottom);
551 } else return "(null)";
554 static char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
556 char* buf = debug_getbuf(), *text = buf;
557 int len, size = DEBUG_BUFFER_SIZE;
559 if (pScrollInfo == NULL) return "(null)";
560 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
561 if (len == -1) goto end; buf += len; size -= len;
562 if (pScrollInfo->fMask & SIF_RANGE)
563 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
565 if (len == -1) goto end; buf += len; size -= len;
566 if (pScrollInfo->fMask & SIF_PAGE)
567 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
569 if (len == -1) goto end; buf += len; size -= len;
570 if (pScrollInfo->fMask & SIF_POS)
571 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
573 if (len == -1) goto end; buf += len; size -= len;
574 if (pScrollInfo->fMask & SIF_TRACKPOS)
575 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
577 if (len == -1) goto end; buf += len; size -= len;
580 buf = text + strlen(text);
582 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
586 static char* debugnmlistview(LPNMLISTVIEW plvnm)
590 char* buf = debug_getbuf();
591 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
592 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
593 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
594 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
596 } else return "(null)";
599 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
601 char* buf = debug_getbuf(), *text = buf;
602 int len, size = DEBUG_BUFFER_SIZE;
604 if (lpLVItem == NULL) return "(null)";
605 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
606 if (len == -1) goto end; buf += len; size -= len;
607 if (lpLVItem->mask & LVIF_STATE)
608 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
610 if (len == -1) goto end; buf += len; size -= len;
611 if (lpLVItem->mask & LVIF_TEXT)
612 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
614 if (len == -1) goto end; buf += len; size -= len;
615 if (lpLVItem->mask & LVIF_IMAGE)
616 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
618 if (len == -1) goto end; buf += len; size -= len;
619 if (lpLVItem->mask & LVIF_PARAM)
620 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
622 if (len == -1) goto end; buf += len; size -= len;
623 if (lpLVItem->mask & LVIF_INDENT)
624 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
626 if (len == -1) goto end; buf += len; size -= len;
629 buf = text + strlen(text);
631 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
635 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
637 char* buf = debug_getbuf(), *text = buf;
638 int len, size = DEBUG_BUFFER_SIZE;
640 if (lpColumn == NULL) return "(null)";
641 len = snprintf(buf, size, "{");
642 if (len == -1) goto end; buf += len; size -= len;
643 if (lpColumn->mask & LVCF_SUBITEM)
644 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
646 if (len == -1) goto end; buf += len; size -= len;
647 if (lpColumn->mask & LVCF_FMT)
648 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
650 if (len == -1) goto end; buf += len; size -= len;
651 if (lpColumn->mask & LVCF_WIDTH)
652 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
654 if (len == -1) goto end; buf += len; size -= len;
655 if (lpColumn->mask & LVCF_TEXT)
656 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
658 if (len == -1) goto end; buf += len; size -= len;
659 if (lpColumn->mask & LVCF_IMAGE)
660 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpColumn->mask & LVCF_ORDER)
664 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
666 if (len == -1) goto end; buf += len; size -= len;
669 buf = text + strlen(text);
671 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
676 /******** Notification functions i************************************/
678 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
682 TRACE("(code=%d)\n", code);
684 pnmh->hwndFrom = infoPtr->hwndSelf;
685 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
687 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
688 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
690 TRACE(" <= %ld\n", result);
695 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
698 return notify_hdr(infoPtr, code, &nmh);
701 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
703 notify(infoPtr, LVN_ITEMACTIVATE);
706 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
708 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
709 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
712 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
716 ZeroMemory(&nmlv, sizeof(nmlv));
717 nmlv.iItem = lvht->iItem;
718 nmlv.iSubItem = lvht->iSubItem;
719 nmlv.ptAction = lvht->pt;
720 return notify_listview(infoPtr, code, &nmlv);
723 static int get_ansi_notification(INT unicodeNotificationCode)
725 switch (unicodeNotificationCode)
727 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
728 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
729 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
730 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
731 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
732 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
734 ERR("unknown notification %x\n", unicodeNotificationCode);
739 Send notification. depends on dispinfoW having same
740 structure as dispinfoA.
741 infoPtr : listview struct
742 notificationCode : *Unicode* notification code
743 pdi : dispinfo structure (can be unicode or ansi)
744 isW : TRUE if dispinfo is Unicode
746 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
748 BOOL bResult = FALSE;
749 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
751 INT cchTempBufMax = 0, savCchTextMax = 0;
752 LPWSTR pszTempBuf = NULL, savPszText = NULL;
754 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
756 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
757 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
760 if (convertToAnsi || convertToUnicode)
762 if (notificationCode != LVN_GETDISPINFOW)
764 cchTempBufMax = convertToUnicode ?
765 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
766 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
770 cchTempBufMax = pdi->item.cchTextMax;
771 *pdi->item.pszText = 0; /* make sure we don't process garbage */
774 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
775 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
776 if (!pszTempBuf) return FALSE;
777 if (convertToUnicode)
778 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
779 pszTempBuf, cchTempBufMax);
781 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
782 cchTempBufMax, NULL, NULL);
783 savCchTextMax = pdi->item.cchTextMax;
784 savPszText = pdi->item.pszText;
785 pdi->item.pszText = pszTempBuf;
786 pdi->item.cchTextMax = cchTempBufMax;
789 if (infoPtr->notifyFormat == NFR_ANSI)
790 realNotifCode = get_ansi_notification(notificationCode);
792 realNotifCode = notificationCode;
793 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
794 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
796 if (convertToUnicode || convertToAnsi)
798 if (convertToUnicode) /* note : pointer can be changed by app ! */
799 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
800 savCchTextMax, NULL, NULL);
802 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
803 savPszText, savCchTextMax);
804 pdi->item.pszText = savPszText; /* restores our buffer */
805 pdi->item.cchTextMax = savCchTextMax;
806 HeapFree(GetProcessHeap(), 0, pszTempBuf);
811 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds)
813 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
814 lpnmlvcd->nmcd.hdc = hdc;
815 lpnmlvcd->nmcd.rc = *rcBounds;
816 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
817 lpnmlvcd->clrText = infoPtr->clrText;
820 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
822 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
823 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
826 /******** Item iterator functions **********************************/
828 static RANGES ranges_create(int count);
829 static void ranges_destroy(RANGES ranges);
830 static BOOL ranges_add(RANGES ranges, RANGE range);
831 static BOOL ranges_del(RANGES ranges, RANGE range);
832 static void ranges_dump(RANGES ranges);
834 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
836 RANGE range = { nItem, nItem + 1 };
838 return ranges_add(ranges, range);
841 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
843 RANGE range = { nItem, nItem + 1 };
845 return ranges_del(ranges, range);
849 * ITERATOR DOCUMENTATION
851 * The iterator functions allow for easy, and convenient iteration
852 * over items of iterest in the list. Typically, you create a
853 * iterator, use it, and destroy it, as such:
856 * iterator_xxxitems(&i, ...);
857 * while (iterator_{prev,next}(&i)
859 * //code which uses i.nItem
861 * iterator_destroy(&i);
863 * where xxx is either: framed, or visible.
864 * Note that it is important that the code destroys the iterator
865 * after it's done with it, as the creation of the iterator may
866 * allocate memory, which thus needs to be freed.
868 * You can iterate both forwards, and backwards through the list,
869 * by using iterator_next or iterator_prev respectively.
871 * Lower numbered items are draw on top of higher number items in
872 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
873 * items may overlap). So, to test items, you should use
875 * which lists the items top to bottom (in Z-order).
876 * For drawing items, you should use
878 * which lists the items bottom to top (in Z-order).
879 * If you keep iterating over the items after the end-of-items
880 * marker (-1) is returned, the iterator will start from the
881 * beginning. Typically, you don't need to test for -1,
882 * because iterator_{next,prev} will return TRUE if more items
883 * are to be iterated over, or FALSE otherwise.
885 * Note: the iterator is defined to be bidirectional. That is,
886 * any number of prev followed by any number of next, or
887 * five versa, should leave the iterator at the same item:
888 * prev * n, next * n = next * n, prev * n
890 * The iterator has a notion of a out-of-order, special item,
891 * which sits at the start of the list. This is used in
892 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
893 * which needs to be first, as it may overlap other items.
895 * The code is a bit messy because we have:
896 * - a special item to deal with
897 * - simple range, or composite range
899 * If find bugs, or want to add features, please make sure you
900 * always check/modify *both* iterator_prev, and iterator_next.
904 * This function iterates through the items in increasing order,
905 * but prefixed by the special item, then -1. That is:
906 * special, 1, 2, 3, ..., n, -1.
907 * Each item is listed only once.
909 static inline BOOL iterator_next(ITERATOR* i)
913 i->nItem = i->nSpecial;
914 if (i->nItem != -1) return TRUE;
916 if (i->nItem == i->nSpecial)
918 if (i->ranges) i->index = 0;
924 if (i->nItem == i->nSpecial) i->nItem++;
925 if (i->nItem < i->range.upper) return TRUE;
930 if (i->index < i->ranges->hdpa->nItemCount)
931 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
934 else if (i->nItem >= i->range.upper) goto end;
936 i->nItem = i->range.lower;
937 if (i->nItem >= 0) goto testitem;
944 * This function iterates through the items in decreasing order,
945 * followed by the special item, then -1. That is:
946 * n, n-1, ..., 3, 2, 1, special, -1.
947 * Each item is listed only once.
949 static inline BOOL iterator_prev(ITERATOR* i)
956 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
959 if (i->nItem == i->nSpecial)
967 if (i->nItem == i->nSpecial) i->nItem--;
968 if (i->nItem >= i->range.lower) return TRUE;
974 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
977 else if (!start && i->nItem < i->range.lower) goto end;
979 i->nItem = i->range.upper;
980 if (i->nItem > 0) goto testitem;
982 return (i->nItem = i->nSpecial) != -1;
985 static RANGE iterator_range(ITERATOR* i)
989 if (!i->ranges) return i->range;
991 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
992 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
997 * Releases resources associated with this ierator.
999 static inline void iterator_destroy(ITERATOR* i)
1001 if (i->ranges) ranges_destroy(i->ranges);
1005 * Create an empty iterator.
1007 static inline BOOL iterator_empty(ITERATOR* i)
1009 ZeroMemory(i, sizeof(*i));
1010 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1015 * Create an iterator over a range.
1017 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1025 * Create an iterator over a bunch of ranges.
1026 * Please note that the iterator will take ownership of the ranges,
1027 * and will free them upon destruction.
1029 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1037 * Creates an iterator over the items which intersect lprc.
1039 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
1041 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1042 RECT frame = *lprc, rcItem, rcTemp;
1045 /* in case we fail, we want to return an empty iterator */
1046 if (!iterator_empty(i)) return FALSE;
1048 LISTVIEW_GetOrigin(infoPtr, &Origin);
1050 TRACE("(lprc=%s)\n", debugrect(lprc));
1051 OffsetRect(&frame, -Origin.x, -Origin.y);
1053 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1057 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1059 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1060 if (IntersectRect(&rcTemp, &rcItem, lprc))
1061 i->nSpecial = infoPtr->nFocusedItem;
1063 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1064 /* to do better here, we need to have PosX, and PosY sorted */
1065 TRACE("building icon ranges:\n");
1066 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1068 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1069 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1070 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1071 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1072 if (IntersectRect(&rcTemp, &rcItem, &frame))
1073 ranges_additem(i->ranges, nItem);
1077 else if (uView == LVS_REPORT)
1081 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1082 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1084 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1085 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1086 if (range.upper <= range.lower) return TRUE;
1087 if (!iterator_rangeitems(i, range)) return FALSE;
1088 TRACE(" report=%s\n", debugrange(&i->range));
1092 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1093 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1094 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1095 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1096 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1097 INT lower = nFirstCol * nPerCol + nFirstRow;
1101 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1102 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1104 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1106 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1107 TRACE("building list ranges:\n");
1108 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1110 item_range.lower = nCol * nPerCol + nFirstRow;
1111 if(item_range.lower >= infoPtr->nItemCount) break;
1112 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1113 TRACE(" list=%s\n", debugrange(&item_range));
1114 ranges_add(i->ranges, item_range);
1122 * Creates an iterator over the items which intersect the visible region of hdc.
1124 static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
1126 POINT Origin, Position;
1127 RECT rcItem, rcClip;
1130 rgntype = GetClipBox(hdc, &rcClip);
1131 if (rgntype == NULLREGION) return iterator_empty(i);
1132 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1133 if (rgntype == SIMPLEREGION) return TRUE;
1135 /* first deal with the special item */
1136 if (i->nSpecial != -1)
1138 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1139 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1142 /* if we can't deal with the region, we'll just go with the simple range */
1143 LISTVIEW_GetOrigin(infoPtr, &Origin);
1144 TRACE("building visible range:\n");
1145 if (!i->ranges && i->range.lower < i->range.upper)
1147 if (!(i->ranges = ranges_create(50))) return TRUE;
1148 if (!ranges_add(i->ranges, i->range))
1150 ranges_destroy(i->ranges);
1156 /* now delete the invisible items from the list */
1157 while(iterator_next(i))
1159 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1160 rcItem.left = Position.x + Origin.x;
1161 rcItem.top = Position.y + Origin.y;
1162 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1163 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1164 if (!RectVisible(hdc, &rcItem))
1165 ranges_delitem(i->ranges, i->nItem);
1167 /* the iterator should restart on the next iterator_next */
1173 /******** Misc helper functions ************************************/
1175 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1176 WPARAM wParam, LPARAM lParam, BOOL isW)
1178 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1179 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1182 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1184 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1186 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1187 (uView == LVS_ICON || uView == LVS_SMALLICON);
1190 /******** Internal API functions ************************************/
1192 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1194 assert (nSubItem >= 0 && nSubItem < infoPtr->hdpaColumns->nItemCount);
1195 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1198 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1200 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1203 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1205 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1208 /* Listview invlaidation functions: use _only_ these function to invalidate */
1210 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1212 return infoPtr->bRedraw && !infoPtr->bFirstPaint;
1215 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT*rect)
1217 if(!is_redrawing(infoPtr)) return;
1218 TRACE(" invalidating rect=%s\n", debugrect(rect));
1219 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1222 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1226 if(!is_redrawing(infoPtr)) return;
1227 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1228 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1231 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1233 POINT Origin, Position;
1236 if(!is_redrawing(infoPtr)) return;
1237 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1238 LISTVIEW_GetOrigin(infoPtr, &Origin);
1239 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1240 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1242 rcBox.bottom = infoPtr->nItemHeight;
1243 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1244 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1247 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1249 LISTVIEW_InvalidateRect(infoPtr, NULL);
1252 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1256 if(!is_redrawing(infoPtr)) return;
1257 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1258 rcCol.top = infoPtr->rcList.top;
1259 rcCol.bottom = infoPtr->rcList.bottom;
1260 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1265 * Retrieves the number of items that can fit vertically in the client area.
1268 * [I] infoPtr : valid pointer to the listview structure
1271 * Number of items per row.
1273 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1275 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1277 return max(nListWidth/infoPtr->nItemWidth, 1);
1282 * Retrieves the number of items that can fit horizontally in the client
1286 * [I] infoPtr : valid pointer to the listview structure
1289 * Number of items per column.
1291 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1293 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1295 return max(nListHeight / infoPtr->nItemHeight, 1);
1299 /*************************************************************************
1300 * LISTVIEW_ProcessLetterKeys
1302 * Processes keyboard messages generated by pressing the letter keys
1304 * What this does is perform a case insensitive search from the
1305 * current position with the following quirks:
1306 * - If two chars or more are pressed in quick succession we search
1307 * for the corresponding string (e.g. 'abc').
1308 * - If there is a delay we wipe away the current search string and
1309 * restart with just that char.
1310 * - If the user keeps pressing the same character, whether slowly or
1311 * fast, so that the search string is entirely composed of this
1312 * character ('aaaaa' for instance), then we search for first item
1313 * that starting with that character.
1314 * - If the user types the above character in quick succession, then
1315 * we must also search for the corresponding string ('aaaaa'), and
1316 * go to that string if there is a match.
1319 * [I] hwnd : handle to the window
1320 * [I] charCode : the character code, the actual character
1321 * [I] keyData : key data
1329 * - The current implementation has a list of characters it will
1330 * accept and it ignores averything else. In particular it will
1331 * ignore accentuated characters which seems to match what
1332 * Windows does. But I'm not sure it makes sense to follow
1334 * - We don't sound a beep when the search fails.
1338 * TREEVIEW_ProcessLetterKeys
1340 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1345 WCHAR buffer[MAX_PATH];
1346 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1348 /* simple parameter checking */
1349 if (!charCode || !keyData) return 0;
1351 /* only allow the valid WM_CHARs through */
1352 if (!isalnum(charCode) &&
1353 charCode != '.' && charCode != '`' && charCode != '!' &&
1354 charCode != '@' && charCode != '#' && charCode != '$' &&
1355 charCode != '%' && charCode != '^' && charCode != '&' &&
1356 charCode != '*' && charCode != '(' && charCode != ')' &&
1357 charCode != '-' && charCode != '_' && charCode != '+' &&
1358 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1359 charCode != '}' && charCode != '[' && charCode != '{' &&
1360 charCode != '/' && charCode != '?' && charCode != '>' &&
1361 charCode != '<' && charCode != ',' && charCode != '~')
1364 /* if there's one item or less, there is no where to go */
1365 if (infoPtr->nItemCount <= 1) return 0;
1367 /* update the search parameters */
1368 infoPtr->lastKeyPressTimestamp = GetTickCount();
1369 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1370 if (infoPtr->nSearchParamLength < MAX_PATH)
1371 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1372 if (infoPtr->charCode != charCode)
1373 infoPtr->charCode = charCode = 0;
1375 infoPtr->charCode=charCode;
1376 infoPtr->szSearchParam[0]=charCode;
1377 infoPtr->nSearchParamLength=1;
1378 /* Redundant with the 1 char string */
1382 /* and search from the current position */
1384 if (infoPtr->nFocusedItem >= 0) {
1385 endidx=infoPtr->nFocusedItem;
1387 /* if looking for single character match,
1388 * then we must always move forward
1390 if (infoPtr->nSearchParamLength == 1)
1393 endidx=infoPtr->nItemCount;
1397 if (idx == infoPtr->nItemCount) {
1398 if (endidx == infoPtr->nItemCount || endidx == 0)
1404 item.mask = LVIF_TEXT;
1407 item.pszText = buffer;
1408 item.cchTextMax = MAX_PATH;
1409 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1411 /* check for a match */
1412 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1415 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1416 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1417 /* This would work but we must keep looking for a longer match */
1421 } while (idx != endidx);
1424 LISTVIEW_KeySelection(infoPtr, nItem);
1429 /*************************************************************************
1430 * LISTVIEW_UpdateHeaderSize [Internal]
1432 * Function to resize the header control
1435 * [I] hwnd : handle to a window
1436 * [I] nNewScrollPos : scroll pos to set
1441 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1446 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1448 GetWindowRect(infoPtr->hwndHeader, &winRect);
1449 point[0].x = winRect.left;
1450 point[0].y = winRect.top;
1451 point[1].x = winRect.right;
1452 point[1].y = winRect.bottom;
1454 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1455 point[0].x = -nNewScrollPos;
1456 point[1].x += nNewScrollPos;
1458 SetWindowPos(infoPtr->hwndHeader,0,
1459 point[0].x,point[0].y,point[1].x,point[1].y,
1460 SWP_NOZORDER | SWP_NOACTIVATE);
1465 * Update the scrollbars. This functions should be called whenever
1466 * the content, size or view changes.
1469 * [I] infoPtr : valid pointer to the listview structure
1474 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1476 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1477 SCROLLINFO horzInfo, vertInfo;
1479 if (infoPtr->dwStyle & LVS_NOSCROLL) return;
1480 if (!is_redrawing(infoPtr)) return;
1482 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1483 horzInfo.cbSize = sizeof(SCROLLINFO);
1484 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1486 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1487 vertInfo.cbSize = sizeof(SCROLLINFO);
1488 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1490 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1491 if (uView == LVS_LIST)
1493 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1494 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1495 horzInfo.nPage /= infoPtr->nItemWidth;
1497 else if (uView == LVS_REPORT)
1499 horzInfo.nMax = infoPtr->nItemWidth;
1500 vertInfo.nMax = infoPtr->nItemCount;
1501 vertInfo.nPage /= infoPtr->nItemHeight;
1503 else /* LVS_ICON, or LVS_SMALLICON */
1507 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1509 horzInfo.nMax = rcView.right - rcView.left;
1510 vertInfo.nMax = rcView.bottom - rcView.top;
1514 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1515 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1516 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1517 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1519 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1520 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1521 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1522 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1524 /* Update the Header Control */
1525 if (uView == LVS_REPORT)
1527 horzInfo.fMask = SIF_POS;
1528 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1529 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1536 * Shows/hides the focus rectangle.
1539 * [I] infoPtr : valid pointer to the listview structure
1540 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1545 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1547 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1550 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1552 if (infoPtr->nFocusedItem < 0) return;
1554 /* we need some gymnastics in ICON mode to handle large items */
1555 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1559 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1560 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1562 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1567 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1569 /* for some reason, owner draw should work only in report mode */
1570 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1575 item.iItem = infoPtr->nFocusedItem;
1577 item.mask = LVIF_PARAM;
1578 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1580 ZeroMemory(&dis, sizeof(dis));
1581 dis.CtlType = ODT_LISTVIEW;
1582 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1583 dis.itemID = item.iItem;
1584 dis.itemAction = ODA_FOCUS;
1585 if (fShow) dis.itemState |= ODS_FOCUS;
1586 dis.hwndItem = infoPtr->hwndSelf;
1588 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1589 dis.itemData = item.lParam;
1591 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1595 DrawFocusRect(hdc, &infoPtr->rcFocus);
1598 ReleaseDC(infoPtr->hwndSelf, hdc);
1602 * Invalidates all visible selected items.
1604 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1608 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1609 while(iterator_next(&i))
1611 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1612 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1614 iterator_destroy(&i);
1619 * DESCRIPTION: [INTERNAL]
1620 * Computes an item's (left,top) corner, relative to rcView.
1621 * That is, the position has NOT been made relative to the Origin.
1622 * This is deliberate, to avoid computing the Origin over, and
1623 * over again, when this function is call in a loop. Instead,
1624 * one ca factor the computation of the Origin before the loop,
1625 * and offset the value retured by this function, on every iteration.
1628 * [I] infoPtr : valid pointer to the listview structure
1629 * [I] nItem : item number
1630 * [O] lpptOrig : item top, left corner
1635 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1637 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1639 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1641 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1643 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1644 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1646 else if (uView == LVS_LIST)
1648 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1649 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1650 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1652 else /* LVS_REPORT */
1654 lpptPosition->x = REPORT_MARGINX;
1655 lpptPosition->y = nItem * infoPtr->nItemHeight;
1660 * DESCRIPTION: [INTERNAL]
1661 * Compute the rectangles of an item. This is to localize all
1662 * the computations in one place. If you are not interested in some
1663 * of these values, simply pass in a NULL -- the fucntion is smart
1664 * enough to compute only what's necessary. The function computes
1665 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1666 * one, the BOX rectangle. This rectangle is very cheap to compute,
1667 * and is guaranteed to contain all the other rectangles. Computing
1668 * the ICON rect is also cheap, but all the others are potentaily
1669 * expensive. This gives an easy and effective optimization when
1670 * searching (like point inclusion, or rectangle intersection):
1671 * first test against the BOX, and if TRUE, test agains the desired
1673 * If the function does not have all the necessary information
1674 * to computed the requested rectangles, will crash with a
1675 * failed assertion. This is done so we catch all programming
1676 * errors, given that the function is called only from our code.
1678 * We have the following 'special' meanings for a few fields:
1679 * * If LVIS_FOCUSED is set, we assume the item has the focus
1680 * This is important in ICON mode, where it might get a larger
1681 * then usual rectange
1683 * Please note that subitem support works only in REPORT mode.
1686 * [I] infoPtr : valid pointer to the listview structure
1687 * [I] lpLVItem : item to compute the measures for
1688 * [O] lprcBox : ptr to Box rectangle
1689 * The internal LVIR_BOX rectangle
1690 * [0] lprcState : ptr to State icon rectangle
1691 * The internal LVIR_STATE rectangle
1692 * [O] lprcIcon : ptr to Icon rectangle
1693 * Same as LVM_GETITEMRECT with LVIR_ICON
1694 * [O] lprcLabel : ptr to Label rectangle
1695 * Same as LVM_GETITEMRECT with LVIR_LABEL
1700 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1701 LPRECT lprcBox, LPRECT lprcState,
1702 LPRECT lprcIcon, LPRECT lprcLabel)
1704 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1705 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1706 RECT Box, State, Icon, Label;
1707 COLUMN_INFO *lpColumnInfo = NULL;
1709 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1711 /* Be smart and try to figure out the minimum we have to do */
1712 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1713 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1715 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1716 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1718 if (lprcLabel) doLabel = TRUE;
1719 if (doLabel || lprcIcon) doIcon = TRUE;
1720 if (doIcon || lprcState) doState = TRUE;
1722 /************************************************************/
1723 /* compute the box rectangle (it should be cheap to do) */
1724 /************************************************************/
1725 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1726 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1728 if (lpLVItem->iSubItem)
1730 Box = lpColumnInfo->rcHeader;
1735 Box.right = infoPtr->nItemWidth;
1738 Box.bottom = infoPtr->nItemHeight;
1740 /************************************************************/
1741 /* compute STATEICON bounding box */
1742 /************************************************************/
1745 if (uView == LVS_ICON)
1747 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1748 if (infoPtr->himlNormal)
1749 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1750 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1754 /* we need the ident in report mode, if we don't have it, we fail */
1755 State.left = Box.left;
1756 if (uView == LVS_REPORT)
1758 State.left += REPORT_MARGINX;
1759 if (lpLVItem->iSubItem == 0)
1761 assert(lpLVItem->mask & LVIF_INDENT);
1762 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1765 State.top = Box.top;
1767 State.right = State.left;
1768 State.bottom = State.top;
1769 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1771 State.right += infoPtr->iconStateSize.cx;
1772 State.bottom += infoPtr->iconStateSize.cy;
1774 if (lprcState) *lprcState = State;
1775 TRACE(" - state=%s\n", debugrect(&State));
1778 /************************************************************/
1779 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1780 /************************************************************/
1783 if (uView == LVS_ICON)
1785 Icon.left = Box.left;
1786 if (infoPtr->himlNormal)
1787 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1788 Icon.top = Box.top + ICON_TOP_PADDING;
1789 Icon.right = Icon.left;
1790 Icon.bottom = Icon.top;
1791 if (infoPtr->himlNormal)
1793 Icon.right += infoPtr->iconSize.cx;
1794 Icon.bottom += infoPtr->iconSize.cy;
1797 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1799 Icon.left = State.right;
1800 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1802 Icon.right = Icon.left;
1803 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1804 Icon.right += infoPtr->iconSize.cx;
1805 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1807 if(lprcIcon) *lprcIcon = Icon;
1808 TRACE(" - icon=%s\n", debugrect(&Icon));
1811 /************************************************************/
1812 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1813 /************************************************************/
1816 SIZE labelSize = { 0, 0 };
1818 /* calculate how far to the right can the label strech */
1819 Label.right = Box.right;
1820 if (uView == LVS_REPORT)
1822 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1823 Label.right -= REPORT_MARGINX;
1826 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1828 labelSize.cx = infoPtr->nItemWidth;
1829 labelSize.cy = infoPtr->nItemHeight;
1833 /* we need the text in non owner draw mode */
1834 assert(lpLVItem->mask & LVIF_TEXT);
1835 if (is_textT(lpLVItem->pszText, TRUE))
1837 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1838 HDC hdc = GetDC(infoPtr->hwndSelf);
1839 HFONT hOldFont = SelectObject(hdc, hFont);
1843 /* compute rough rectangle where the label will go */
1844 SetRectEmpty(&rcText);
1845 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1846 rcText.bottom = infoPtr->nItemHeight;
1847 if (uView == LVS_ICON)
1848 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1850 /* now figure out the flags */
1851 if (uView == LVS_ICON)
1852 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1854 uFormat = LV_SL_DT_FLAGS;
1856 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1858 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1859 labelSize.cy = rcText.bottom - rcText.top;
1861 SelectObject(hdc, hOldFont);
1862 ReleaseDC(infoPtr->hwndSelf, hdc);
1866 if (uView == LVS_ICON)
1868 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1869 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1870 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1871 Label.right = Label.left + labelSize.cx;
1872 Label.bottom = Label.top + infoPtr->nItemHeight;
1873 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1875 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1876 labelSize.cy /= infoPtr->ntmHeight;
1877 labelSize.cy = max(labelSize.cy, 1);
1878 labelSize.cy *= infoPtr->ntmHeight;
1880 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1882 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1884 Label.left = Icon.right;
1885 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1886 Label.top = Box.top;
1887 Label.right = min(Label.left + labelSize.cx, Label.right);
1888 Label.bottom = Label.top + infoPtr->nItemHeight;
1891 if (lprcLabel) *lprcLabel = Label;
1892 TRACE(" - label=%s\n", debugrect(&Label));
1895 /* Fix the Box if necessary */
1898 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1899 else *lprcBox = Box;
1901 TRACE(" - box=%s\n", debugrect(&Box));
1905 * DESCRIPTION: [INTERNAL]
1908 * [I] infoPtr : valid pointer to the listview structure
1909 * [I] nItem : item number
1910 * [O] lprcBox : ptr to Box rectangle
1915 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1917 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1918 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1919 POINT Position, Origin;
1922 LISTVIEW_GetOrigin(infoPtr, &Origin);
1923 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1925 /* Be smart and try to figure out the minimum we have to do */
1927 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1928 lvItem.mask |= LVIF_TEXT;
1929 lvItem.iItem = nItem;
1930 lvItem.iSubItem = 0;
1931 lvItem.pszText = szDispText;
1932 lvItem.cchTextMax = DISP_TEXT_SIZE;
1933 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
1934 if (uView == LVS_ICON)
1936 lvItem.mask |= LVIF_STATE;
1937 lvItem.stateMask = LVIS_FOCUSED;
1938 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1940 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
1942 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1948 * Returns the current icon position, and advances it along the top.
1949 * The returned position is not offset by Origin.
1952 * [I] infoPtr : valid pointer to the listview structure
1953 * [O] lpPos : will get the current icon position
1958 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
1960 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1962 *lpPos = infoPtr->currIconPos;
1964 infoPtr->currIconPos.x += infoPtr->nItemWidth;
1965 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
1967 infoPtr->currIconPos.x = 0;
1968 infoPtr->currIconPos.y += infoPtr->nItemHeight;
1974 * Returns the current icon position, and advances it down the left edge.
1975 * The returned position is not offset by Origin.
1978 * [I] infoPtr : valid pointer to the listview structure
1979 * [O] lpPos : will get the current icon position
1984 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
1986 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1988 *lpPos = infoPtr->currIconPos;
1990 infoPtr->currIconPos.y += infoPtr->nItemHeight;
1991 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
1993 infoPtr->currIconPos.x += infoPtr->nItemWidth;
1994 infoPtr->currIconPos.y = 0;
2000 * Moves an icon to the specified position.
2001 * It takes care of invalidating the item, etc.
2004 * [I] infoPtr : valid pointer to the listview structure
2005 * [I] nItem : the item to move
2006 * [I] lpPos : the new icon position
2007 * [I] isNew : flags the item as being new
2013 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lppt, BOOL isNew)
2019 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2020 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2022 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2023 LISTVIEW_InvalidateItem(infoPtr, nItem);
2026 /* Allocating a POINTER for every item is too resource intensive,
2027 * so we'll keep the (x,y) in different arrays */
2028 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2029 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2031 LISTVIEW_InvalidateItem(infoPtr, nItem);
2038 * Arranges listview items in icon display mode.
2041 * [I] infoPtr : valid pointer to the listview structure
2042 * [I] nAlignCode : alignment code
2048 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2050 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2051 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2055 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2057 TRACE("nAlignCode=%d\n", nAlignCode);
2059 if (nAlignCode == LVA_DEFAULT)
2061 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2062 else nAlignCode = LVA_ALIGNTOP;
2067 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2068 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2069 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2070 default: return FALSE;
2073 infoPtr->bAutoarrange = TRUE;
2074 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2075 for (i = 0; i < infoPtr->nItemCount; i++)
2077 next_pos(infoPtr, &pos);
2078 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2086 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2089 * [I] infoPtr : valid pointer to the listview structure
2090 * [O] lprcView : bounding rectangle
2096 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2100 SetRectEmpty(lprcView);
2102 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2106 for (i = 0; i < infoPtr->nItemCount; i++)
2108 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2109 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2110 lprcView->right = max(lprcView->right, x);
2111 lprcView->bottom = max(lprcView->bottom, y);
2113 if (infoPtr->nItemCount > 0)
2115 lprcView->right += infoPtr->nItemWidth;
2116 lprcView->bottom += infoPtr->nItemHeight;
2121 y = LISTVIEW_GetCountPerColumn(infoPtr);
2122 x = infoPtr->nItemCount / y;
2123 if (infoPtr->nItemCount % y) x++;
2124 lprcView->right = x * infoPtr->nItemWidth;
2125 lprcView->bottom = y * infoPtr->nItemHeight;
2129 lprcView->right = infoPtr->nItemWidth;
2130 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2137 * Retrieves the bounding rectangle of all the items.
2140 * [I] infoPtr : valid pointer to the listview structure
2141 * [O] lprcView : bounding rectangle
2147 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2151 TRACE("(lprcView=%p)\n", lprcView);
2153 if (!lprcView) return FALSE;
2155 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2156 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2157 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2159 TRACE("lprcView=%s\n", debugrect(lprcView));
2166 * Retrieves the subitem pointer associated with the subitem index.
2169 * [I] hdpaSubItems : DPA handle for a specific item
2170 * [I] nSubItem : index of subitem
2173 * SUCCESS : subitem pointer
2176 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2178 SUBITEM_INFO *lpSubItem;
2181 /* we should binary search here if need be */
2182 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2184 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2185 if (lpSubItem->iSubItem == nSubItem)
2195 * Caclulates the desired item width.
2198 * [I] infoPtr : valid pointer to the listview structure
2201 * The desired item width.
2203 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2205 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2208 TRACE("uView=%d\n", uView);
2210 if (uView == LVS_ICON)
2211 nItemWidth = infoPtr->iconSpacing.cx;
2212 else if (uView == LVS_REPORT)
2216 if (infoPtr->hdpaColumns->nItemCount > 0)
2218 LISTVIEW_GetHeaderRect(infoPtr, infoPtr->hdpaColumns->nItemCount - 1, &rcHeader);
2219 nItemWidth = rcHeader.right;
2222 else /* LVS_SMALLICON, or LVS_LIST */
2226 for (i = 0; i < infoPtr->nItemCount; i++)
2227 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2229 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
2230 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx + IMAGE_PADDING;
2232 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2235 return max(nItemWidth, 1);
2240 * Caclulates the desired item height.
2243 * [I] infoPtr : valid pointer to the listview structure
2246 * The desired item height.
2248 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2250 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2253 TRACE("uView=%d\n", uView);
2255 if (uView == LVS_ICON)
2256 nItemHeight = infoPtr->iconSpacing.cy;
2259 nItemHeight = infoPtr->ntmHeight;
2260 if (infoPtr->himlState)
2261 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2262 if (infoPtr->himlSmall)
2263 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2264 if (infoPtr->himlState || infoPtr->himlSmall)
2265 nItemHeight += HEIGHT_PADDING;
2268 return max(nItemHeight, 1);
2273 * Updates the width, and height of an item.
2276 * [I] infoPtr : valid pointer to the listview structure
2281 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2283 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2284 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2290 * Retrieves and saves important text metrics info for the current
2294 * [I] infoPtr : valid pointer to the listview structure
2297 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2299 HDC hdc = GetDC(infoPtr->hwndSelf);
2300 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2301 HFONT hOldFont = SelectObject(hdc, hFont);
2304 if (GetTextMetricsW(hdc, &tm))
2306 infoPtr->ntmHeight = tm.tmHeight;
2307 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2309 SelectObject(hdc, hOldFont);
2310 ReleaseDC(infoPtr->hwndSelf, hdc);
2312 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2317 * A compare function for ranges
2320 * [I] range1 : pointer to range 1;
2321 * [I] range2 : pointer to range 2;
2325 * > 0 : if range 1 > range 2
2326 * < 0 : if range 2 > range 1
2327 * = 0 : if range intersects range 2
2329 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2333 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2335 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2340 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2346 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2348 #define ranges_check(ranges, desc) do { } while(0)
2351 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2356 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2358 assert (ranges->hdpa->nItemCount >= 0);
2359 ranges_dump(ranges);
2360 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2361 if (ranges->hdpa->nItemCount > 0)
2362 assert (prev->lower >= 0 && prev->lower < prev->upper);
2363 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2365 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2366 assert (prev->upper <= curr->lower);
2367 assert (curr->lower < curr->upper);
2370 TRACE("--- Done checking---\n");
2373 static RANGES ranges_create(int count)
2375 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2376 if (!ranges) return NULL;
2377 ranges->hdpa = DPA_Create(count);
2378 if (ranges->hdpa) return ranges;
2379 COMCTL32_Free(ranges);
2383 static void ranges_clear(RANGES ranges)
2387 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2388 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2389 DPA_DeleteAllPtrs(ranges->hdpa);
2393 static void ranges_destroy(RANGES ranges)
2395 ranges_clear(ranges);
2396 DPA_Destroy(ranges->hdpa);
2397 COMCTL32_Free(ranges);
2400 static RANGES ranges_clone(RANGES ranges)
2405 if (!(clone = ranges_create(ranges->hdpa->nItemCount))) goto fail;
2407 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2409 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2410 if (!newrng) goto fail;
2411 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2412 DPA_SetPtr(clone->hdpa, i, newrng);
2417 TRACE ("clone failed\n");
2418 if (clone) ranges_destroy(clone);
2422 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2426 for (i = 0; i < sub->hdpa->nItemCount; i++)
2427 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2432 static void ranges_dump(RANGES ranges)
2436 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2437 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2440 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2442 RANGE srchrng = { nItem, nItem + 1 };
2444 TRACE("(nItem=%d)\n", nItem);
2445 ranges_check(ranges, "before contain");
2446 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2449 static INT ranges_itemcount(RANGES ranges)
2453 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2455 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2456 count += sel->upper - sel->lower;
2462 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2464 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2467 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2468 if (index == -1) return TRUE;
2470 for (; index < ranges->hdpa->nItemCount; index++)
2472 chkrng = DPA_GetPtr(ranges->hdpa, index);
2473 if (chkrng->lower >= nItem)
2474 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2475 if (chkrng->upper > nItem)
2476 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2481 static BOOL ranges_add(RANGES ranges, RANGE range)
2486 TRACE("(%s)\n", debugrange(&range));
2487 ranges_check(ranges, "before add");
2489 /* try find overlapping regions first */
2490 srchrgn.lower = range.lower - 1;
2491 srchrgn.upper = range.upper + 1;
2492 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2498 TRACE("Adding new range\n");
2500 /* create the brand new range to insert */
2501 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2502 if(!newrgn) goto fail;
2505 /* figure out where to insert it */
2506 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2507 TRACE("index=%d\n", index);
2508 if (index == -1) index = 0;
2510 /* and get it over with */
2511 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2513 COMCTL32_Free(newrgn);
2519 RANGE *chkrgn, *mrgrgn;
2520 INT fromindex, mergeindex;
2522 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2523 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2525 chkrgn->lower = min(range.lower, chkrgn->lower);
2526 chkrgn->upper = max(range.upper, chkrgn->upper);
2528 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2530 /* merge now common anges */
2532 srchrgn.lower = chkrgn->lower - 1;
2533 srchrgn.upper = chkrgn->upper + 1;
2537 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2538 if (mergeindex == -1) break;
2539 if (mergeindex == index)
2541 fromindex = index + 1;
2545 TRACE("Merge with index %i\n", mergeindex);
2547 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2548 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2549 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2550 COMCTL32_Free(mrgrgn);
2551 DPA_DeletePtr(ranges->hdpa, mergeindex);
2552 if (mergeindex < index) index --;
2556 ranges_check(ranges, "after add");
2560 ranges_check(ranges, "failed add");
2564 static BOOL ranges_del(RANGES ranges, RANGE range)
2569 TRACE("(%s)\n", debugrange(&range));
2570 ranges_check(ranges, "before del");
2572 /* we don't use DPAS_SORTED here, since we need *
2573 * to find the first overlapping range */
2574 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2577 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2579 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2581 /* case 1: Same range */
2582 if ( (chkrgn->upper == range.upper) &&
2583 (chkrgn->lower == range.lower) )
2585 DPA_DeletePtr(ranges->hdpa, index);
2588 /* case 2: engulf */
2589 else if ( (chkrgn->upper <= range.upper) &&
2590 (chkrgn->lower >= range.lower) )
2592 DPA_DeletePtr(ranges->hdpa, index);
2594 /* case 3: overlap upper */
2595 else if ( (chkrgn->upper <= range.upper) &&
2596 (chkrgn->lower < range.lower) )
2598 chkrgn->upper = range.lower;
2600 /* case 4: overlap lower */
2601 else if ( (chkrgn->upper > range.upper) &&
2602 (chkrgn->lower >= range.lower) )
2604 chkrgn->lower = range.upper;
2607 /* case 5: fully internal */
2610 RANGE tmprgn = *chkrgn, *newrgn;
2612 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2613 newrgn->lower = chkrgn->lower;
2614 newrgn->upper = range.lower;
2615 chkrgn->lower = range.upper;
2616 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2618 COMCTL32_Free(newrgn);
2625 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2628 ranges_check(ranges, "after del");
2632 ranges_check(ranges, "failed del");
2638 * Removes all selection ranges
2641 * [I] infoPtr : valid pointer to the listview structure
2642 * [I] toSkip : item range to skip removing the selection
2648 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2657 lvItem.stateMask = LVIS_SELECTED;
2659 /* need to clone the DPA because callbacks can change it */
2660 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2661 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2662 while(iterator_next(&i))
2663 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2664 /* note that the iterator destructor will free the cloned range */
2665 iterator_destroy(&i);
2670 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2674 if (!(toSkip = ranges_create(1))) return FALSE;
2675 if (nItem != -1) ranges_additem(toSkip, nItem);
2676 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2677 ranges_destroy(toSkip);
2681 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2683 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2688 * Retrieves the number of items that are marked as selected.
2691 * [I] infoPtr : valid pointer to the listview structure
2694 * Number of items selected.
2696 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2698 INT nSelectedCount = 0;
2700 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2703 for (i = 0; i < infoPtr->nItemCount; i++)
2705 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2710 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2712 TRACE("nSelectedCount=%d\n", nSelectedCount);
2713 return nSelectedCount;
2718 * Manages the item focus.
2721 * [I] infoPtr : valid pointer to the listview structure
2722 * [I] nItem : item index
2725 * TRUE : focused item changed
2726 * FALSE : focused item has NOT changed
2728 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2730 INT oldFocus = infoPtr->nFocusedItem;
2733 if (nItem == infoPtr->nFocusedItem) return FALSE;
2735 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2736 lvItem.stateMask = LVIS_FOCUSED;
2737 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2739 return oldFocus != infoPtr->nFocusedItem;
2742 /* Helper function for LISTVIEW_ShiftIndices *only* */
2743 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2745 if (nShiftItem < nItem) return nShiftItem;
2747 if (nShiftItem > nItem) return nShiftItem + direction;
2749 if (direction > 0) return nShiftItem + direction;
2751 return min(nShiftItem, infoPtr->nItemCount - 1);
2756 * Updates the various indices after an item has been inserted or deleted.
2759 * [I] infoPtr : valid pointer to the listview structure
2760 * [I] nItem : item index
2761 * [I] direction : Direction of shift, +1 or -1.
2766 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2770 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2772 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2774 assert(abs(direction) == 1);
2776 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2778 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2779 if (nNewFocus != infoPtr->nFocusedItem)
2780 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2782 /* But we are not supposed to modify nHotItem! */
2788 * Adds a block of selections.
2791 * [I] infoPtr : valid pointer to the listview structure
2792 * [I] nItem : item index
2797 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2799 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2800 INT nLast = max(infoPtr->nSelectionMark, nItem);
2804 if (nFirst == -1) nFirst = nItem;
2806 item.state = LVIS_SELECTED;
2807 item.stateMask = LVIS_SELECTED;
2809 /* FIXME: this is not correct LVS_OWNERDATA
2810 * setting the item states individually will generate
2811 * a LVN_ITEMCHANGED notification for each one. Instead,
2812 * we have to send a LVN_ODSTATECHANGED notification.
2813 * See MSDN documentation for LVN_ITEMCHANGED.
2815 for (i = nFirst; i <= nLast; i++)
2816 LISTVIEW_SetItemState(infoPtr,i,&item);
2822 * Sets a single group selection.
2825 * [I] infoPtr : valid pointer to the listview structure
2826 * [I] nItem : item index
2831 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2833 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2838 if (!(selection = ranges_create(100))) return;
2840 item.state = LVIS_SELECTED;
2841 item.stateMask = LVIS_SELECTED;
2843 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2845 if (infoPtr->nSelectionMark == -1)
2847 infoPtr->nSelectionMark = nItem;
2848 ranges_additem(selection, nItem);
2854 sel.lower = min(infoPtr->nSelectionMark, nItem);
2855 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2856 ranges_add(selection, sel);
2861 RECT rcItem, rcSel, rcSelMark;
2864 rcItem.left = LVIR_BOUNDS;
2865 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2866 rcSelMark.left = LVIR_BOUNDS;
2867 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2868 UnionRect(&rcSel, &rcItem, &rcSelMark);
2869 iterator_frameditems(&i, infoPtr, &rcSel);
2870 while(iterator_next(&i))
2872 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2873 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
2875 iterator_destroy(&i);
2878 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
2879 iterator_rangesitems(&i, selection);
2880 while(iterator_next(&i))
2881 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2882 /* this will also destroy the selection */
2883 iterator_destroy(&i);
2885 LISTVIEW_SetItemFocus(infoPtr, nItem);
2890 * Sets a single selection.
2893 * [I] infoPtr : valid pointer to the listview structure
2894 * [I] nItem : item index
2899 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2903 TRACE("nItem=%d\n", nItem);
2905 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
2907 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2908 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2909 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2911 infoPtr->nSelectionMark = nItem;
2916 * Set selection(s) with keyboard.
2919 * [I] infoPtr : valid pointer to the listview structure
2920 * [I] nItem : item index
2923 * SUCCESS : TRUE (needs to be repainted)
2924 * FAILURE : FALSE (nothing has changed)
2926 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2928 /* FIXME: pass in the state */
2929 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2930 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2931 BOOL bResult = FALSE;
2933 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2935 if (infoPtr->dwStyle & LVS_SINGLESEL)
2938 LISTVIEW_SetSelection(infoPtr, nItem);
2945 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2949 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2954 LISTVIEW_SetSelection(infoPtr, nItem);
2957 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2960 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2967 * Called when the mouse is being actively tracked and has hovered for a specified
2971 * [I] infoPtr : valid pointer to the listview structure
2972 * [I] fwKeys : key indicator
2973 * [I] pts : mouse position
2976 * 0 if the message was processed, non-zero if there was an error
2979 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2980 * over the item for a certain period of time.
2983 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2985 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2986 /* FIXME: select the item!!! */
2987 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2994 * Called whenever WM_MOUSEMOVE is received.
2997 * [I] infoPtr : valid pointer to the listview structure
2998 * [I] fwKeys : key indicator
2999 * [I] pts : mouse position
3002 * 0 if the message is processed, non-zero if there was an error
3004 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3006 TRACKMOUSEEVENT trackinfo;
3008 /* see if we are supposed to be tracking mouse hovering */
3009 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3010 /* fill in the trackinfo struct */
3011 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3012 trackinfo.dwFlags = TME_QUERY;
3013 trackinfo.hwndTrack = infoPtr->hwndSelf;
3014 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3016 /* see if we are already tracking this hwnd */
3017 _TrackMouseEvent(&trackinfo);
3019 if(!(trackinfo.dwFlags & TME_HOVER)) {
3020 trackinfo.dwFlags = TME_HOVER;
3022 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3023 _TrackMouseEvent(&trackinfo);
3032 * Tests wheather the item is assignable to a list with style lStyle
3034 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
3036 if ( (lpLVItem->mask & LVIF_TEXT) &&
3037 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3038 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3046 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3049 * [I] infoPtr : valid pointer to the listview structure
3050 * [I] lpLVItem : valid pointer to new item atttributes
3051 * [I] isNew : the item being set is being inserted
3052 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3053 * [O] bChanged : will be set to TRUE if the item really changed
3059 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3068 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3070 if (lpLVItem->mask == 0) return TRUE;
3072 if (infoPtr->dwStyle & LVS_OWNERDATA)
3074 /* a virtual listview we stores only selection and focus */
3075 if ((lpLVItem->mask & ~LVIF_STATE) || (lpLVItem->stateMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3081 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3082 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3086 /* we need to get the lParam and state of the item */
3087 item.iItem = lpLVItem->iItem;
3088 item.iSubItem = lpLVItem->iSubItem;
3089 item.mask = LVIF_STATE | LVIF_PARAM;
3090 item.stateMask = ~0;
3093 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3095 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3096 /* determine what fields will change */
3097 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3098 uChanged |= LVIF_STATE;
3100 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3101 uChanged |= LVIF_IMAGE;
3103 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3104 uChanged |= LVIF_PARAM;
3106 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3107 uChanged |= LVIF_INDENT;
3109 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3110 uChanged |= LVIF_TEXT;
3112 TRACE("uChanged=0x%x\n", uChanged);
3113 if (!uChanged) return TRUE;
3116 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3117 nmlv.iItem = lpLVItem->iItem;
3118 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3119 nmlv.uOldState = item.state;
3120 nmlv.uChanged = uChanged;
3121 nmlv.lParam = item.lParam;
3123 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3124 /* and we are _NOT_ virtual (LVS_OWERNDATA) */
3125 if(lpItem && !isNew && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3128 /* copy information */
3129 if (lpLVItem->mask & LVIF_TEXT)
3130 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3132 if (lpLVItem->mask & LVIF_IMAGE)
3133 lpItem->hdr.iImage = lpLVItem->iImage;
3135 if (lpLVItem->mask & LVIF_PARAM)
3136 lpItem->lParam = lpLVItem->lParam;
3138 if (lpLVItem->mask & LVIF_INDENT)
3139 lpItem->iIndent = lpLVItem->iIndent;
3141 if (uChanged & LVIF_STATE)
3143 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED))
3145 lpItem->state &= ~lpLVItem->stateMask;
3146 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3148 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3150 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3151 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3153 else if (lpLVItem->stateMask & LVIS_SELECTED)
3154 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3156 /* if we are asked to change focus, and we manage it, do it */
3157 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3159 if (lpLVItem->state & LVIS_FOCUSED)
3161 LISTVIEW_SetItemFocus(infoPtr, -1);
3162 infoPtr->nFocusedItem = lpLVItem->iItem;
3163 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
3165 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3166 infoPtr->nFocusedItem = -1;
3170 /* if we're inserting the item, we're done */
3171 if (isNew) return TRUE;
3173 /* send LVN_ITEMCHANGED notification */
3174 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3175 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3182 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3185 * [I] infoPtr : valid pointer to the listview structure
3186 * [I] lpLVItem : valid pointer to new subitem atttributes
3187 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3188 * [O] bChanged : will be set to TRUE if the item really changed
3194 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
3197 SUBITEM_INFO *lpSubItem;
3199 /* we do not support subitems for virtual listviews */
3200 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3202 /* set subitem only if column is present */
3203 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3205 /* First do some sanity checks */
3206 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3207 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3209 /* get the subitem structure, and create it if not there */
3210 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3211 assert (hdpaSubItems);
3213 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3216 SUBITEM_INFO *tmpSubItem;
3219 lpSubItem = (SUBITEM_INFO *)COMCTL32_Alloc(sizeof(SUBITEM_INFO));
3220 if (!lpSubItem) return FALSE;
3221 /* we could binary search here, if need be...*/
3222 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3224 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3225 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3227 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3229 COMCTL32_Free(lpSubItem);
3232 lpSubItem->iSubItem = lpLVItem->iSubItem;
3236 if (lpLVItem->mask & LVIF_IMAGE)
3237 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3239 lpSubItem->hdr.iImage = lpLVItem->iImage;
3243 if (lpLVItem->mask & LVIF_TEXT)
3244 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3246 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3255 * Sets item attributes.
3258 * [I] infoPtr : valid pointer to the listview structure
3259 * [I] lpLVItem : new item atttributes
3260 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3266 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3268 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3269 LPWSTR pszText = NULL;
3270 BOOL bResult, bChanged = FALSE;
3272 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3274 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3277 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3278 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3280 pszText = lpLVItem->pszText;
3281 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3284 /* actually set the fields */
3285 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3287 if (lpLVItem->iSubItem)
3288 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3290 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3292 /* redraw item, if necessary */
3293 if (bChanged && !infoPtr->bIsDrawing)
3295 /* this little optimization eliminates some nasty flicker */
3296 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3297 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3298 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3300 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3305 textfreeT(lpLVItem->pszText, isW);
3306 lpLVItem->pszText = pszText;
3314 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3317 * [I] infoPtr : valid pointer to the listview structure
3322 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3324 LONG lStyle = infoPtr->dwStyle;
3325 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3327 SCROLLINFO scrollInfo;
3329 scrollInfo.cbSize = sizeof(SCROLLINFO);
3330 scrollInfo.fMask = SIF_POS;
3332 if (uView == LVS_LIST)
3334 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3335 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3337 else if (uView == LVS_REPORT)
3339 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3340 nItem = scrollInfo.nPos;
3344 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3345 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3348 TRACE("nItem=%d\n", nItem);
3356 * Erases the background of the given rectangle
3359 * [I] infoPtr : valid pointer to the listview structure
3360 * [I] hdc : device context handle
3361 * [I] lprcBox : clipping rectangle
3367 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3369 if (!infoPtr->hBkBrush) return FALSE;
3371 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3373 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3381 * [I] infoPtr : valid pointer to the listview structure
3382 * [I] hdc : device context handle
3383 * [I] nItem : item index
3384 * [I] nSubItem : subitem index
3385 * [I] pos : item position in client coordinates
3386 * [I] cdmode : custom draw mode
3392 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3394 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3395 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3396 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3397 DWORD cditemmode = CDRF_DODEFAULT;
3398 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3399 NMLVCUSTOMDRAW nmlvcd;
3403 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3405 /* get information needed for drawing the item */
3406 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3407 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3408 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3409 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3410 lvItem.iItem = nItem;
3411 lvItem.iSubItem = nSubItem;
3414 lvItem.cchTextMax = DISP_TEXT_SIZE;
3415 lvItem.pszText = szDispText;
3416 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3417 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3418 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3419 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3420 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3422 /* now check if we need to update the focus rectangle */
3423 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3425 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3426 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3427 OffsetRect(&rcBox, pos.x, pos.y);
3428 OffsetRect(&rcState, pos.x, pos.y);
3429 OffsetRect(&rcIcon, pos.x, pos.y);
3430 OffsetRect(&rcLabel, pos.x, pos.y);
3431 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3432 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3434 /* fill in the custom draw structure */
3435 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox);
3436 nmlvcd.nmcd.dwItemSpec = lvItem.iItem;
3437 nmlvcd.iSubItem = lvItem.iSubItem;
3438 if (lvItem.state & LVIS_SELECTED) nmlvcd.nmcd.uItemState |= CDIS_SELECTED;
3439 if (lvItem.state & LVIS_FOCUSED) nmlvcd.nmcd.uItemState |= CDIS_FOCUS;
3440 if (lvItem.iItem == infoPtr->nHotItem) nmlvcd.nmcd.uItemState |= CDIS_HOT;
3441 nmlvcd.nmcd.lItemlParam = lvItem.lParam;
3443 if (cdmode & CDRF_NOTIFYITEMDRAW)
3444 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3445 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3447 /* apprently, for selected items, we have to override the returned values */
3448 if (lvItem.state & LVIS_SELECTED)
3450 if (infoPtr->bFocus)
3452 nmlvcd.clrTextBk = comctl32_color.clrHighlight;
3453 nmlvcd.clrText = comctl32_color.clrHighlightText;
3455 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
3457 nmlvcd.clrTextBk = comctl32_color.clr3dFace;
3458 nmlvcd.clrText = comctl32_color.clrBtnText;
3462 /* in full row select, subitems, will just use main item's colors */
3463 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3464 nmlvcd.clrTextBk = CLR_NONE;
3467 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3469 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3472 TRACE("uStateImage=%d\n", uStateImage);
3473 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3478 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3479 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3481 TRACE("iImage=%d\n", lvItem.iImage);
3482 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3483 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3486 /* Don't bother painting item being edited */
3487 if (infoPtr->hwndEdit && lprcFocus && nSubItem == 0) goto postpaint;
3489 /* Set the text attributes */
3490 if (nmlvcd.clrTextBk != CLR_NONE)
3492 SetBkMode(hdc, OPAQUE);
3493 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3496 SetBkMode(hdc, TRANSPARENT);
3497 SetTextColor(hdc, nmlvcd.clrText);
3499 /* draw the selection background, if we're drawing the main item */
3503 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3504 rcSelect.right = rcBox.right;
3506 if (nmlvcd.clrTextBk != CLR_NONE)
3507 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3508 if(lprcFocus) *lprcFocus = rcSelect;
3511 /* figure out the text drawing flags */
3512 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3513 if (uView == LVS_ICON)
3514 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3517 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3519 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3520 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3521 default: uFormat |= DT_LEFT;
3524 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3525 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3528 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3529 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3535 * Draws listview items when in owner draw mode.
3538 * [I] infoPtr : valid pointer to the listview structure
3539 * [I] hdc : device context handle
3544 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3546 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3547 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3548 POINT Origin, Position;
3555 ZeroMemory(&dis, sizeof(dis));
3557 /* Get scroll info once before loop */
3558 LISTVIEW_GetOrigin(infoPtr, &Origin);
3560 /* figure out what we need to draw */
3561 iterator_visibleitems(&i, infoPtr, hdc);
3563 /* send cache hint notification */
3564 if (infoPtr->dwStyle & LVS_OWNERDATA)
3566 RANGE range = iterator_range(&i);
3569 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3570 nmlv.iFrom = range.lower;
3571 nmlv.iTo = range.upper - 1;
3572 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3575 /* iterate through the invalidated rows */
3576 while(iterator_next(&i))
3578 item.iItem = i.nItem;
3580 item.mask = LVIF_PARAM | LVIF_STATE;
3581 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3582 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3584 dis.CtlType = ODT_LISTVIEW;
3586 dis.itemID = item.iItem;
3587 dis.itemAction = ODA_DRAWENTIRE;
3589 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3590 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3591 dis.hwndItem = infoPtr->hwndSelf;
3593 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3594 dis.rcItem.left = Position.x + Origin.x;
3595 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3596 dis.rcItem.top = Position.y + Origin.y;
3597 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3598 dis.itemData = item.lParam;
3600 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3601 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3603 iterator_destroy(&i);
3608 * Draws listview items when in report display mode.
3611 * [I] infoPtr : valid pointer to the listview structure
3612 * [I] hdc : device context handle
3613 * [I] cdmode : custom draw mode
3618 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3621 RECT rcClip, rcItem;
3622 POINT Origin, Position;
3628 /* figure out what to draw */
3629 rgntype = GetClipBox(hdc, &rcClip);
3630 if (rgntype == NULLREGION) return;
3632 /* Get scroll info once before loop */
3633 LISTVIEW_GetOrigin(infoPtr, &Origin);
3635 /* narrow down the columns we need to paint */
3636 for(colRange.lower = 0; colRange.lower < infoPtr->hdpaColumns->nItemCount; colRange.lower++)
3638 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3639 if (rcItem.right + Origin.x >= rcClip.left) break;
3641 for(colRange.upper = infoPtr->hdpaColumns->nItemCount; colRange.upper > 0; colRange.upper--)
3643 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3644 if (rcItem.left + Origin.x < rcClip.right) break;
3646 iterator_rangeitems(&j, colRange);
3648 /* in full row select, we _have_ to draw the main item */
3649 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3652 /* figure out what we need to draw */
3653 iterator_visibleitems(&i, infoPtr, hdc);
3655 /* iterate through the invalidated rows */
3656 while(iterator_next(&i))
3658 /* iterate through the invalidated columns */
3659 while(iterator_next(&j))
3661 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3662 Position.x += Origin.x;
3663 Position.y += Origin.y;
3665 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3667 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3669 rcItem.bottom = infoPtr->nItemHeight;
3670 OffsetRect(&rcItem, Position.x, Position.y);
3671 if (!RectVisible(hdc, &rcItem)) continue;
3674 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, j.nItem, Position, cdmode);
3677 iterator_destroy(&i);
3682 * Draws listview items when in list display mode.
3685 * [I] infoPtr : valid pointer to the listview structure
3686 * [I] hdc : device context handle
3687 * [I] cdmode : custom draw mode
3692 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3694 POINT Origin, Position;
3697 /* Get scroll info once before loop */
3698 LISTVIEW_GetOrigin(infoPtr, &Origin);
3700 /* figure out what we need to draw */
3701 iterator_visibleitems(&i, infoPtr, hdc);
3703 while(iterator_prev(&i))
3705 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3706 Position.x += Origin.x;
3707 Position.y += Origin.y;
3709 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3711 iterator_destroy(&i);
3717 * Draws listview items.
3720 * [I] infoPtr : valid pointer to the listview structure
3721 * [I] hdc : device context handle
3726 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3728 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3729 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3730 NMLVCUSTOMDRAW nmlvcd;
3736 LISTVIEW_DUMP(infoPtr);
3738 infoPtr->bIsDrawing = TRUE;
3740 /* save dc values we're gonna trash while drawing */
3741 hOldFont = SelectObject(hdc, infoPtr->hFont);
3742 oldBkMode = GetBkMode(hdc);
3743 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3744 oldTextColor = GetTextColor(hdc);
3746 oldClrTextBk = infoPtr->clrTextBk;
3747 oldClrText = infoPtr->clrText;
3749 GetClientRect(infoPtr->hwndSelf, &rcClient);
3750 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3751 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3752 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3754 /* Use these colors to draw the items */
3755 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3756 infoPtr->clrText = nmlvcd.clrText;
3758 /* nothing to draw */
3759 if(infoPtr->nItemCount == 0) goto enddraw;
3761 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3762 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3765 if (uView == LVS_REPORT)
3766 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3767 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3768 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3770 /* if we have a focus rect, draw it */
3771 if (infoPtr->bFocus)
3772 DrawFocusRect(hdc, &infoPtr->rcFocus);
3776 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3777 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3779 infoPtr->clrTextBk = oldClrTextBk;
3780 infoPtr->clrText = oldClrText;
3782 SelectObject(hdc, hOldFont);
3783 SetBkMode(hdc, oldBkMode);
3784 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3785 SetTextColor(hdc, oldTextColor);
3786 infoPtr->bIsDrawing = FALSE;
3792 * Calculates the approximate width and height of a given number of items.
3795 * [I] infoPtr : valid pointer to the listview structure
3796 * [I] nItemCount : number of items
3797 * [I] wWidth : width
3798 * [I] wHeight : height
3801 * Returns a DWORD. The width in the low word and the height in high word.
3803 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3804 WORD wWidth, WORD wHeight)
3806 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3807 INT nItemCountPerColumn = 1;
3808 INT nColumnCount = 0;
3809 DWORD dwViewRect = 0;
3811 if (nItemCount == -1)
3812 nItemCount = infoPtr->nItemCount;
3814 if (uView == LVS_LIST)
3816 if (wHeight == 0xFFFF)
3818 /* use current height */
3819 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3822 if (wHeight < infoPtr->nItemHeight)
3823 wHeight = infoPtr->nItemHeight;
3827 if (infoPtr->nItemHeight > 0)
3829 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3830 if (nItemCountPerColumn == 0)
3831 nItemCountPerColumn = 1;
3833 if (nItemCount % nItemCountPerColumn != 0)
3834 nColumnCount = nItemCount / nItemCountPerColumn;
3836 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3840 /* Microsoft padding magic */
3841 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3842 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3844 dwViewRect = MAKELONG(wWidth, wHeight);
3846 else if (uView == LVS_REPORT)
3847 FIXME("uView == LVS_REPORT: not implemented\n");
3848 else if (uView == LVS_SMALLICON)
3849 FIXME("uView == LVS_SMALLICON: not implemented\n");
3850 else if (uView == LVS_ICON)
3851 FIXME("uView == LVS_ICON: not implemented\n");
3856 /* << LISTVIEW_CreateDragImage >> */
3861 * Removes all listview items and subitems.
3864 * [I] infoPtr : valid pointer to the listview structure
3870 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3873 HDPA hdpaSubItems = NULL;
3880 /* we do it directly, to avoid notifications */
3881 ranges_clear(infoPtr->selectionRanges);
3882 infoPtr->nSelectionMark = -1;
3883 infoPtr->nFocusedItem = -1;
3884 SetRectEmpty(&infoPtr->rcFocus);
3885 /* But we are supposed to leave nHotItem as is! */
3888 /* send LVN_DELETEALLITEMS notification */
3889 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3891 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3893 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
3895 /* send LVN_DELETEITEM notification, if not supressed */
3899 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3901 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3903 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3904 for (j = 0; j < hdpaSubItems->nItemCount; j++)
3906 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
3907 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
3908 COMCTL32_Free(hdrItem);
3910 DPA_Destroy(hdpaSubItems);
3911 DPA_DeletePtr(infoPtr->hdpaItems, i);
3913 DPA_DeletePtr(infoPtr->hdpaPosX, i);
3914 DPA_DeletePtr(infoPtr->hdpaPosY, i);
3915 infoPtr->nItemCount --;
3918 LISTVIEW_UpdateScroll(infoPtr);
3920 LISTVIEW_InvalidateList(infoPtr);
3927 * Scrolls, and updates the columns, when a column is changing width.
3930 * [I] infoPtr : valid pointer to the listview structure
3931 * [I] nColumn : column to scroll
3932 * [I] dx : amount of scroll, in pixels
3937 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
3939 COLUMN_INFO *lpColumnInfo;
3943 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, infoPtr->hdpaColumns->nItemCount - 1));
3944 rcCol = lpColumnInfo->rcHeader;
3945 if (nColumn >= infoPtr->hdpaColumns->nItemCount)
3946 rcCol.left = rcCol.right;
3948 /* ajust the other columns */
3949 for (nCol = nColumn; nCol < infoPtr->hdpaColumns->nItemCount; nCol++)
3951 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
3952 lpColumnInfo->rcHeader.left += dx;
3953 lpColumnInfo->rcHeader.right += dx;
3956 /* do not update screen if not in report mode */
3957 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
3959 /* if we have a focus, must first erase the focus rect */
3960 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3962 /* Need to reset the item width when inserting a new column */
3963 infoPtr->nItemWidth += dx;
3965 LISTVIEW_UpdateScroll(infoPtr);
3967 /* scroll to cover the deleted column, and invalidate for redraw */
3968 rcOld = infoPtr->rcList;
3969 rcOld.left = rcCol.left;
3970 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3972 /* we can restore focus now */
3973 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3978 * Removes a column from the listview control.
3981 * [I] infoPtr : valid pointer to the listview structure
3982 * [I] nColumn : column index
3988 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3992 TRACE("nColumn=%d\n", nColumn);
3994 if (nColumn <= 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3996 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
3998 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4001 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4002 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4004 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4006 SUBITEM_INFO *lpSubItem, *lpDelItem;
4008 INT nItem, nSubItem, i;
4010 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4012 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4015 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4017 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4018 if (lpSubItem->iSubItem == nColumn)
4021 lpDelItem = lpSubItem;
4023 else if (lpSubItem->iSubItem > nColumn)
4025 lpSubItem->iSubItem--;
4029 /* if we found our subitem, zapp it */
4033 if (is_textW(lpDelItem->hdr.pszText))
4034 COMCTL32_Free(lpDelItem->hdr.pszText);
4037 COMCTL32_Free(lpDelItem);
4039 /* free dpa memory */
4040 DPA_DeletePtr(hdpaSubItems, nSubItem);
4045 /* update the other column info */
4046 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4053 * Invalidates the listview after an item's insertion or deletion.
4056 * [I] infoPtr : valid pointer to the listview structure
4057 * [I] nItem : item index
4058 * [I] dir : -1 if deleting, 1 if inserting
4063 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4065 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4066 INT nPerCol, nItemCol, nItemRow;
4070 /* if we don't refresh, what's the point of scrolling? */
4071 if (!is_redrawing(infoPtr)) return;
4073 assert (abs(dir) == 1);
4075 /* arrange icons if autoarrange is on */
4076 if (is_autoarrange(infoPtr))
4078 BOOL arrange = TRUE;
4079 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4080 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4081 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4084 /* scrollbars need updating */
4085 LISTVIEW_UpdateScroll(infoPtr);
4087 /* figure out the item's position */
4088 if (uView == LVS_REPORT)
4089 nPerCol = infoPtr->nItemCount + 1;
4090 else if (uView == LVS_LIST)
4091 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4092 else /* LVS_ICON, or LVS_SMALLICON */
4095 nItemCol = nItem / nPerCol;
4096 nItemRow = nItem % nPerCol;
4097 LISTVIEW_GetOrigin(infoPtr, &Origin);
4099 /* move the items below up a slot */
4100 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4101 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4102 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4103 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4104 OffsetRect(&rcScroll, Origin.x, Origin.y);
4105 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4106 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4107 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4109 /* report has only that column, so we're done */
4110 if (uView == LVS_REPORT) return;
4112 /* now for LISTs, we have to deal with the columns to the right */
4113 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4115 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4116 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4117 OffsetRect(&rcScroll, Origin.x, Origin.y);
4118 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4119 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4120 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4125 * Removes an item from the listview control.
4128 * [I] infoPtr : valid pointer to the listview structure
4129 * [I] nItem : item index
4135 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4137 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4141 TRACE("(nItem=%d)\n", nItem);
4143 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4145 /* remove selection, and focus */
4147 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4148 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4150 /* send LVN_DELETEITEM notification. */
4151 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
4153 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
4155 /* we need to do this here, because we'll be deleting stuff */
4156 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4157 LISTVIEW_InvalidateItem(infoPtr, nItem);
4159 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4165 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4166 for (i = 0; i < hdpaSubItems->nItemCount; i++)
4168 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4169 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4170 COMCTL32_Free(hdrItem);
4172 DPA_Destroy(hdpaSubItems);
4175 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4177 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4178 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4181 infoPtr->nItemCount--;
4182 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4184 /* now is the invalidation fun */
4185 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4192 * Callback implementation for editlabel control
4195 * [I] infoPtr : valid pointer to the listview structure
4196 * [I] pszText : modified text
4197 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4203 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4205 NMLVDISPINFOW dispInfo;
4207 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4209 ZeroMemory(&dispInfo, sizeof(dispInfo));
4210 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4211 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4212 dispInfo.item.iSubItem = 0;
4213 dispInfo.item.stateMask = ~0;
4214 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4215 /* add the text from the edit in */
4216 dispInfo.item.mask |= LVIF_TEXT;
4217 dispInfo.item.pszText = pszText;
4218 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4220 /* Do we need to update the Item Text */
4221 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4222 if (!pszText) return TRUE;
4224 ZeroMemory(&dispInfo, sizeof(dispInfo));
4225 dispInfo.item.mask = LVIF_TEXT;
4226 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4227 dispInfo.item.iSubItem = 0;
4228 dispInfo.item.pszText = pszText;
4229 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4230 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4235 * Begin in place editing of specified list view item
4238 * [I] infoPtr : valid pointer to the listview structure
4239 * [I] nItem : item index
4240 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4246 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4248 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4249 NMLVDISPINFOW dispInfo;
4252 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4254 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4255 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4257 infoPtr->nEditLabelItem = nItem;
4259 /* Is the EditBox still there, if so remove it */
4260 if(infoPtr->hwndEdit != 0)
4262 SetFocus(infoPtr->hwndSelf);
4263 infoPtr->hwndEdit = 0;
4266 LISTVIEW_SetSelection(infoPtr, nItem);
4267 LISTVIEW_SetItemFocus(infoPtr, nItem);
4269 rect.left = LVIR_LABEL;
4270 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4272 ZeroMemory(&dispInfo, sizeof(dispInfo));
4273 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4274 dispInfo.item.iItem = nItem;
4275 dispInfo.item.iSubItem = 0;
4276 dispInfo.item.stateMask = ~0;
4277 dispInfo.item.pszText = szDispText;
4278 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4279 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4281 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4282 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4283 if (!infoPtr->hwndEdit) return 0;
4285 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4287 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4288 infoPtr->hwndEdit = 0;
4292 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4293 SetFocus(infoPtr->hwndEdit);
4294 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4295 return infoPtr->hwndEdit;
4301 * Ensures the specified item is visible, scrolling into view if necessary.
4304 * [I] infoPtr : valid pointer to the listview structure
4305 * [I] nItem : item index
4306 * [I] bPartial : partially or entirely visible
4312 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4314 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4315 INT nScrollPosHeight = 0;
4316 INT nScrollPosWidth = 0;
4317 INT nHorzAdjust = 0;
4318 INT nVertAdjust = 0;
4321 RECT rcItem, rcTemp;
4323 rcItem.left = LVIR_BOUNDS;
4324 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4326 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4328 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4330 /* scroll left/right, but in LVS_REPORT mode */
4331 if (uView == LVS_LIST)
4332 nScrollPosWidth = infoPtr->nItemWidth;
4333 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4334 nScrollPosWidth = 1;
4336 if (rcItem.left < infoPtr->rcList.left)
4339 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4344 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4348 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4350 /* scroll up/down, but not in LVS_LIST mode */
4351 if (uView == LVS_REPORT)
4352 nScrollPosHeight = infoPtr->nItemHeight;
4353 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4354 nScrollPosHeight = 1;
4356 if (rcItem.top < infoPtr->rcList.top)
4359 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4364 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4368 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4370 if (nScrollPosWidth)
4372 INT diff = nHorzDiff / nScrollPosWidth;
4373 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4374 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4377 if (nScrollPosHeight)
4379 INT diff = nVertDiff / nScrollPosHeight;
4380 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4381 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4389 * Searches for an item with specific characteristics.
4392 * [I] hwnd : window handle
4393 * [I] nStart : base item index
4394 * [I] lpFindInfo : item information to look for
4397 * SUCCESS : index of item
4400 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4401 LPLVFINDINFOW lpFindInfo)
4403 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4404 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4405 BOOL bWrap = FALSE, bNearest = FALSE;
4406 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4407 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4408 POINT Position, Destination;
4411 if (!lpFindInfo || nItem < 0) return -1;
4414 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4416 lvItem.mask |= LVIF_TEXT;
4417 lvItem.pszText = szDispText;
4418 lvItem.cchTextMax = DISP_TEXT_SIZE;
4421 if (lpFindInfo->flags & LVFI_WRAP)
4424 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4425 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4430 LISTVIEW_GetOrigin(infoPtr, &Origin);
4431 Destination.x = lpFindInfo->pt.x - Origin.x;
4432 Destination.y = lpFindInfo->pt.y - Origin.y;
4433 switch(lpFindInfo->vkDirection)
4435 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4436 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4437 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4438 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4439 case VK_HOME: Destination.x = Destination.y = 0; break;
4440 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4441 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4443 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4444 Destination.x = rcArea.right;
4445 Destination.y = rcArea.bottom;
4447 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4452 /* if LVFI_PARAM is specified, all other flags are ignored */
4453 if (lpFindInfo->flags & LVFI_PARAM)
4455 lvItem.mask |= LVIF_PARAM;
4457 lvItem.mask &= ~LVIF_TEXT;
4461 for (; nItem < nLast; nItem++)
4463 lvItem.iItem = nItem;
4464 lvItem.iSubItem = 0;
4465 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4467 if (lvItem.mask & LVIF_PARAM)
4469 if (lpFindInfo->lParam == lvItem.lParam)
4475 if (lvItem.mask & LVIF_TEXT)
4477 if (lpFindInfo->flags & LVFI_PARTIAL)
4479 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4483 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4487 if (!bNearest) return nItem;
4489 /* This is very inefficient. To do a good job here,
4490 * we need a sorted array of (x,y) item positions */
4491 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4493 /* compute the distance^2 to the destination */
4494 xdist = Destination.x - Position.x;
4495 ydist = Destination.y - Position.y;
4496 dist = xdist * xdist + ydist * ydist;
4498 /* remember the distance, and item if it's closer */
4502 nNearestItem = nItem;
4509 nLast = min(nStart + 1, infoPtr->nItemCount);
4514 return nNearestItem;
4519 * Searches for an item with specific characteristics.
4522 * [I] hwnd : window handle
4523 * [I] nStart : base item index
4524 * [I] lpFindInfo : item information to look for
4527 * SUCCESS : index of item
4530 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4531 LPLVFINDINFOA lpFindInfo)
4533 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4537 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4538 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4539 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4540 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4546 * Retrieves the background image of the listview control.
4549 * [I] infoPtr : valid pointer to the listview structure
4550 * [O] lpBkImage : background image attributes
4556 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4558 /* FIXME (listview, "empty stub!\n"); */
4564 * Retrieves column attributes.
4567 * [I] infoPtr : valid pointer to the listview structure
4568 * [I] nColumn : column index
4569 * [IO] lpColumn : column information
4570 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4571 * otherwise it is in fact a LPLVCOLUMNA
4577 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4579 COLUMN_INFO *lpColumnInfo;
4582 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4583 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4585 /* initialize memory */
4586 ZeroMemory(&hdi, sizeof(hdi));
4588 if (lpColumn->mask & LVCF_TEXT)
4590 hdi.mask |= HDI_TEXT;
4591 hdi.pszText = lpColumn->pszText;
4592 hdi.cchTextMax = lpColumn->cchTextMax;
4595 if (lpColumn->mask & LVCF_IMAGE)
4596 hdi.mask |= HDI_IMAGE;
4598 if (lpColumn->mask & LVCF_ORDER)
4599 hdi.mask |= HDI_ORDER;
4601 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4603 if (lpColumn->mask & LVCF_FMT)
4604 lpColumn->fmt = lpColumnInfo->fmt;
4606 if (lpColumn->mask & LVCF_WIDTH)
4607 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4609 if (lpColumn->mask & LVCF_IMAGE)
4610 lpColumn->iImage = hdi.iImage;
4612 if (lpColumn->mask & LVCF_ORDER)
4613 lpColumn->iOrder = hdi.iOrder;
4619 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4626 /* FIXME: little hack */
4627 for (i = 0; i < iCount; i++)
4635 * Retrieves the column width.
4638 * [I] infoPtr : valid pointer to the listview structure
4639 * [I] int : column index
4642 * SUCCESS : column width
4645 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4647 INT nColumnWidth = 0;
4650 TRACE("nColumn=%d\n", nColumn);
4652 /* we have a 'column' in LIST and REPORT mode only */
4653 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4656 nColumnWidth = infoPtr->nItemWidth;
4659 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return 0;
4660 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4661 nColumnWidth = rcHeader.right - rcHeader.left;
4665 TRACE("nColumnWidth=%d\n", nColumnWidth);
4666 return nColumnWidth;
4671 * In list or report display mode, retrieves the number of items that can fit
4672 * vertically in the visible area. In icon or small icon display mode,
4673 * retrieves the total number of visible items.
4676 * [I] infoPtr : valid pointer to the listview structure
4679 * Number of fully visible items.
4681 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4683 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4687 return infoPtr->nItemCount;
4689 return LISTVIEW_GetCountPerColumn(infoPtr);
4691 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4698 * Retrieves an image list handle.
4701 * [I] infoPtr : valid pointer to the listview structure
4702 * [I] nImageList : image list identifier
4705 * SUCCESS : image list handle
4708 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4712 case LVSIL_NORMAL: return infoPtr->himlNormal;
4713 case LVSIL_SMALL: return infoPtr->himlSmall;
4714 case LVSIL_STATE: return infoPtr->himlState;
4719 /* LISTVIEW_GetISearchString */
4723 * Retrieves item attributes.
4726 * [I] hwnd : window handle
4727 * [IO] lpLVItem : item info
4728 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4729 * if FALSE, the lpLVItem is a LPLVITEMA.
4732 * This is the internal 'GetItem' interface -- it tries to
4733 * be smart, and avoids text copies, if possible, by modifing
4734 * lpLVItem->pszText to point to the text string. Please note
4735 * that this is not always possible (e.g. OWNERDATA), so on
4736 * entry you *must* supply valid values for pszText, and cchTextMax.
4737 * The only difference to the documented interface is that upon
4738 * return, you should use *only* the lpLVItem->pszText, rather than
4739 * the buffer pointer you provided on input. Most code already does
4740 * that, so it's not a problem.
4741 * For the two cases when the text must be copied (that is,
4742 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4748 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4750 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
4751 NMLVDISPINFOW dispInfo;
4756 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4758 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4761 if (lpLVItem->mask == 0) return TRUE;
4763 /* a quick optimization if all we're asked is the focus state
4764 * these queries are worth optimising since they are common,
4765 * and can be answered in constant time, without the heavy accesses */
4766 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4767 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4769 lpLVItem->state = 0;
4770 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4771 lpLVItem->state |= LVIS_FOCUSED;
4775 ZeroMemory(&dispInfo, sizeof(dispInfo));
4777 /* if the app stores all the data, handle it separately */
4778 if (infoPtr->dwStyle & LVS_OWNERDATA)
4780 dispInfo.item.state = 0;
4782 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
4783 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
4785 /* NOTE: copy only fields which we _know_ are initialized, some apps
4786 * depend on the uninitialized fields being 0 */
4787 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
4788 dispInfo.item.iItem = lpLVItem->iItem;
4789 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4790 if (lpLVItem->mask & LVIF_TEXT)
4792 dispInfo.item.pszText = lpLVItem->pszText;
4793 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4795 if (lpLVItem->mask & LVIF_STATE)
4796 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4797 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4798 dispInfo.item.stateMask = lpLVItem->stateMask;
4799 *lpLVItem = dispInfo.item;
4800 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4803 /* make sure lParam is zeroed out */
4804 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
4806 /* we store only a little state, so if we're not asked, we're done */
4807 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4809 /* if focus is handled by us, report it */
4810 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4812 lpLVItem->state &= ~LVIS_FOCUSED;
4813 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4814 lpLVItem->state |= LVIS_FOCUSED;
4817 /* and do the same for selection, if we handle it */
4818 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4820 lpLVItem->state &= ~LVIS_SELECTED;
4821 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4822 lpLVItem->state |= LVIS_SELECTED;
4828 /* find the item and subitem structures before we proceed */
4829 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4830 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4833 if (lpLVItem->iSubItem)
4835 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4836 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
4839 pItemHdr = &lpItem->hdr;
4841 /* Do we need to query the state from the app? */
4842 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4844 dispInfo.item.mask |= LVIF_STATE;
4845 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4848 /* Do we need to enquire about the image? */
4849 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4850 dispInfo.item.mask |= LVIF_IMAGE;
4852 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4853 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4855 dispInfo.item.mask |= LVIF_TEXT;
4856 dispInfo.item.pszText = lpLVItem->pszText;
4857 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4858 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4859 *dispInfo.item.pszText = '\0';
4862 /* If we don't have all the requested info, query the application */
4863 if (dispInfo.item.mask != 0)
4865 dispInfo.item.iItem = lpLVItem->iItem;
4866 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4867 dispInfo.item.lParam = lpItem->lParam;
4868 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4869 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4872 /* we should not store values for subitems */
4873 if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
4875 /* Now, handle the iImage field */
4876 if (dispInfo.item.mask & LVIF_IMAGE)
4878 lpLVItem->iImage = dispInfo.item.iImage;
4879 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
4880 pItemHdr->iImage = dispInfo.item.iImage;
4882 else if (lpLVItem->mask & LVIF_IMAGE)
4883 lpLVItem->iImage = pItemHdr->iImage;
4885 /* The pszText field */
4886 if (dispInfo.item.mask & LVIF_TEXT)
4888 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4889 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4891 lpLVItem->pszText = dispInfo.item.pszText;
4893 else if (lpLVItem->mask & LVIF_TEXT)
4895 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4896 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4899 /* if this is a subitem, we're done */
4900 if (lpLVItem->iSubItem) return TRUE;
4902 /* Next is the lParam field */
4903 if (dispInfo.item.mask & LVIF_PARAM)
4905 lpLVItem->lParam = dispInfo.item.lParam;
4906 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4907 lpItem->lParam = dispInfo.item.lParam;
4909 else if (lpLVItem->mask & LVIF_PARAM)
4910 lpLVItem->lParam = lpItem->lParam;
4912 /* ... the state field (this one is different due to uCallbackmask) */
4913 if (lpLVItem->mask & LVIF_STATE)
4915 lpLVItem->state = lpItem->state;
4916 if (dispInfo.item.mask & LVIF_STATE)
4918 lpLVItem->state &= ~dispInfo.item.stateMask;
4919 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4921 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4923 lpLVItem->state &= ~LVIS_FOCUSED;
4924 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4925 lpLVItem->state |= LVIS_FOCUSED;
4927 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4929 lpLVItem->state &= ~LVIS_SELECTED;
4930 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4931 lpLVItem->state |= LVIS_SELECTED;
4935 /* and last, but not least, the indent field */
4936 if (lpLVItem->mask & LVIF_INDENT)
4937 lpLVItem->iIndent = lpItem->iIndent;
4944 * Retrieves item attributes.
4947 * [I] hwnd : window handle
4948 * [IO] lpLVItem : item info
4949 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4950 * if FALSE, the lpLVItem is a LPLVITEMA.
4953 * This is the external 'GetItem' interface -- it properly copies
4954 * the text in the provided buffer.
4960 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4965 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4968 pszText = lpLVItem->pszText;
4969 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4970 if (bResult && lpLVItem->pszText != pszText)
4971 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4972 lpLVItem->pszText = pszText;
4980 * Retrieves the position (upper-left) of the listview control item.
4981 * Note that for LVS_ICON style, the upper-left is that of the icon
4982 * and not the bounding box.
4985 * [I] infoPtr : valid pointer to the listview structure
4986 * [I] nItem : item index
4987 * [O] lpptPosition : coordinate information
4993 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4995 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4998 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5000 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5002 LISTVIEW_GetOrigin(infoPtr, &Origin);
5003 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5005 if (uView == LVS_ICON)
5007 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5008 lpptPosition->y += ICON_TOP_PADDING;
5010 lpptPosition->x += Origin.x;
5011 lpptPosition->y += Origin.y;
5013 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5020 * Retrieves the bounding rectangle for a listview control item.
5023 * [I] infoPtr : valid pointer to the listview structure
5024 * [I] nItem : item index
5025 * [IO] lprc : bounding rectangle coordinates
5026 * lprc->left specifies the portion of the item for which the bounding
5027 * rectangle will be retrieved.
5029 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5030 * including the icon and label.
5033 * * Experiment shows that native control returns:
5034 * * width = min (48, length of text line)
5035 * * .left = position.x - (width - iconsize.cx)/2
5036 * * .right = .left + width
5037 * * height = #lines of text * ntmHeight + icon height + 8
5038 * * .top = position.y - 2
5039 * * .bottom = .top + height
5040 * * separation between items .y = itemSpacing.cy - height
5041 * * .x = itemSpacing.cx - width
5042 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5045 * * Experiment shows that native control returns:
5046 * * width = iconSize.cx + 16
5047 * * .left = position.x - (width - iconsize.cx)/2
5048 * * .right = .left + width
5049 * * height = iconSize.cy + 4
5050 * * .top = position.y - 2
5051 * * .bottom = .top + height
5052 * * separation between items .y = itemSpacing.cy - height
5053 * * .x = itemSpacing.cx - width
5054 * LVIR_LABEL Returns the bounding rectangle of the item text.
5057 * * Experiment shows that native control returns:
5058 * * width = text length
5059 * * .left = position.x - width/2
5060 * * .right = .left + width
5061 * * height = ntmH * linecount + 2
5062 * * .top = position.y + iconSize.cy + 6
5063 * * .bottom = .top + height
5064 * * separation between items .y = itemSpacing.cy - height
5065 * * .x = itemSpacing.cx - width
5066 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5067 * rectangles, but excludes columns in report view.
5074 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5075 * upon whether the window has the focus currently and on whether the item
5076 * is the one with the focus. Ensure that the control's record of which
5077 * item has the focus agrees with the items' records.
5079 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5081 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5082 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5083 BOOL doLabel = TRUE, oversizedBox = FALSE;
5084 POINT Position, Origin;
5088 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5090 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5092 LISTVIEW_GetOrigin(infoPtr, &Origin);
5093 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5095 /* Be smart and try to figure out the minimum we have to do */
5096 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5097 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5098 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5099 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5100 oversizedBox = TRUE;
5102 /* get what we need from the item before hand, so we make
5103 * only one request. This can speed up things, if data
5104 * is stored on the app side */
5106 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5107 if (doLabel) lvItem.mask |= LVIF_TEXT;
5108 lvItem.iItem = nItem;
5109 lvItem.iSubItem = 0;
5110 lvItem.pszText = szDispText;
5111 lvItem.cchTextMax = DISP_TEXT_SIZE;
5112 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5113 /* we got the state already up, simulate it here, to avoid a reget */
5114 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5116 lvItem.mask |= LVIF_STATE;
5117 lvItem.stateMask = LVIS_FOCUSED;
5118 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5121 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5122 lprc->left = LVIR_BOUNDS;
5126 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5130 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5134 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5137 case LVIR_SELECTBOUNDS:
5138 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5139 UnionRect(lprc, lprc, &label_rect);
5143 WARN("Unknown value: %d\n", lprc->left);
5147 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5149 TRACE(" rect=%s\n", debugrect(lprc));
5156 * Retrieves the spacing between listview control items.
5159 * [I] infoPtr : valid pointer to the listview structure
5160 * [IO] lprc : rectangle to receive the output
5161 * on input, lprc->top = nSubItem
5162 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5164 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5165 * not only those of the first column.
5166 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5172 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5174 POINT Position, Origin;
5177 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5179 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5181 LISTVIEW_GetOrigin(infoPtr, &Origin);
5182 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5184 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5185 lvItem.iItem = nItem;
5186 lvItem.iSubItem = lprc->top;
5188 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5192 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5197 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5201 ERR("Unknown bounds=%d\n", lprc->left);
5205 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5212 * Retrieves the width of a label.
5215 * [I] infoPtr : valid pointer to the listview structure
5218 * SUCCESS : string width (in pixels)
5221 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5223 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5226 TRACE("(nItem=%d)\n", nItem);
5228 lvItem.mask = LVIF_TEXT;
5229 lvItem.iItem = nItem;
5230 lvItem.iSubItem = 0;
5231 lvItem.pszText = szDispText;
5232 lvItem.cchTextMax = DISP_TEXT_SIZE;
5233 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5235 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5240 * Retrieves the spacing between listview control items.
5243 * [I] infoPtr : valid pointer to the listview structure
5244 * [I] bSmall : flag for small or large icon
5247 * Horizontal + vertical spacing
5249 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5255 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5259 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5260 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5262 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5269 * Retrieves the state of a listview control item.
5272 * [I] infoPtr : valid pointer to the listview structure
5273 * [I] nItem : item index
5274 * [I] uMask : state mask
5277 * State specified by the mask.
5279 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5283 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5285 lvItem.iItem = nItem;
5286 lvItem.iSubItem = 0;
5287 lvItem.mask = LVIF_STATE;
5288 lvItem.stateMask = uMask;
5289 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5291 return lvItem.state & uMask;
5296 * Retrieves the text of a listview control item or subitem.
5299 * [I] hwnd : window handle
5300 * [I] nItem : item index
5301 * [IO] lpLVItem : item information
5302 * [I] isW : TRUE if lpLVItem is Unicode
5305 * SUCCESS : string length
5308 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5310 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5312 lpLVItem->mask = LVIF_TEXT;
5313 lpLVItem->iItem = nItem;
5314 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5316 return textlenT(lpLVItem->pszText, isW);
5321 * Searches for an item based on properties + relationships.
5324 * [I] infoPtr : valid pointer to the listview structure
5325 * [I] nItem : item index
5326 * [I] uFlags : relationship flag
5329 * SUCCESS : item index
5332 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5334 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5336 LVFINDINFOW lvFindInfo;
5337 INT nCountPerColumn;
5340 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5341 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5343 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5345 if (uFlags & LVNI_CUT)
5348 if (uFlags & LVNI_DROPHILITED)
5349 uMask |= LVIS_DROPHILITED;
5351 if (uFlags & LVNI_FOCUSED)
5352 uMask |= LVIS_FOCUSED;
5354 if (uFlags & LVNI_SELECTED)
5355 uMask |= LVIS_SELECTED;
5357 /* if we're asked for the focused item, that's only one,
5358 * so it's worth optimizing */
5359 if (uFlags & LVNI_FOCUSED)
5361 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5362 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5365 if (uFlags & LVNI_ABOVE)
5367 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5372 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5378 lvFindInfo.flags = LVFI_NEARESTXY;
5379 lvFindInfo.vkDirection = VK_UP;
5380 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5381 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5383 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5388 else if (uFlags & LVNI_BELOW)
5390 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5392 while (nItem < infoPtr->nItemCount)
5395 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5401 lvFindInfo.flags = LVFI_NEARESTXY;
5402 lvFindInfo.vkDirection = VK_DOWN;
5403 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5404 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5406 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5411 else if (uFlags & LVNI_TOLEFT)
5413 if (uView == LVS_LIST)
5415 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5416 while (nItem - nCountPerColumn >= 0)
5418 nItem -= nCountPerColumn;
5419 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5423 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5425 lvFindInfo.flags = LVFI_NEARESTXY;
5426 lvFindInfo.vkDirection = VK_LEFT;
5427 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5428 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5430 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5435 else if (uFlags & LVNI_TORIGHT)
5437 if (uView == LVS_LIST)
5439 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5440 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5442 nItem += nCountPerColumn;
5443 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5447 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5449 lvFindInfo.flags = LVFI_NEARESTXY;
5450 lvFindInfo.vkDirection = VK_RIGHT;
5451 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5452 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5454 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5463 /* search by index */
5464 for (i = nItem; i < infoPtr->nItemCount; i++)
5466 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5474 /* LISTVIEW_GetNumberOfWorkAreas */
5478 * Retrieves the origin coordinates when in icon or small icon display mode.
5481 * [I] infoPtr : valid pointer to the listview structure
5482 * [O] lpptOrigin : coordinate information
5487 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5489 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5490 INT nHorzPos = 0, nVertPos = 0;
5491 SCROLLINFO scrollInfo;
5493 scrollInfo.cbSize = sizeof(SCROLLINFO);
5494 scrollInfo.fMask = SIF_POS;
5496 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5497 nHorzPos = scrollInfo.nPos;
5498 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5499 nVertPos = scrollInfo.nPos;
5501 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5503 lpptOrigin->x = infoPtr->rcList.left;
5504 lpptOrigin->y = infoPtr->rcList.top;
5505 if (uView == LVS_LIST)
5506 nHorzPos *= infoPtr->nItemWidth;
5507 else if (uView == LVS_REPORT)
5508 nVertPos *= infoPtr->nItemHeight;
5510 lpptOrigin->x -= nHorzPos;
5511 lpptOrigin->y -= nVertPos;
5513 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5518 * Retrieves the width of a string.
5521 * [I] hwnd : window handle
5522 * [I] lpszText : text string to process
5523 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5526 * SUCCESS : string width (in pixels)
5529 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5534 if (is_textT(lpszText, isW))
5536 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5537 HDC hdc = GetDC(infoPtr->hwndSelf);
5538 HFONT hOldFont = SelectObject(hdc, hFont);
5541 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5543 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5544 SelectObject(hdc, hOldFont);
5545 ReleaseDC(infoPtr->hwndSelf, hdc);
5547 return stringSize.cx;
5552 * Determines which listview item is located at the specified position.
5555 * [I] infoPtr : valid pointer to the listview structure
5556 * [IO] lpht : hit test information
5557 * [I] subitem : fill out iSubItem.
5558 * [I] select : return the index only if the hit selects the item
5561 * (mm 20001022): We must not allow iSubItem to be touched, for
5562 * an app might pass only a structure with space up to iItem!
5563 * (MS Office 97 does that for instance in the file open dialog)
5566 * SUCCESS : item index
5569 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5571 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5572 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5573 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5574 POINT Origin, Position, opt;
5578 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5582 if (subitem) lpht->iSubItem = 0;
5584 if (infoPtr->rcList.left > lpht->pt.x)
5585 lpht->flags |= LVHT_TOLEFT;
5586 else if (infoPtr->rcList.right < lpht->pt.x)
5587 lpht->flags |= LVHT_TORIGHT;
5589 if (infoPtr->rcList.top > lpht->pt.y)
5590 lpht->flags |= LVHT_ABOVE;
5591 else if (infoPtr->rcList.bottom < lpht->pt.y)
5592 lpht->flags |= LVHT_BELOW;
5594 TRACE("lpht->flags=0x%x\n", lpht->flags);
5595 if (lpht->flags) return -1;
5597 lpht->flags |= LVHT_NOWHERE;
5599 LISTVIEW_GetOrigin(infoPtr, &Origin);
5601 /* first deal with the large items */
5602 rcSearch.left = lpht->pt.x;
5603 rcSearch.top = lpht->pt.y;
5604 rcSearch.right = rcSearch.left + 1;
5605 rcSearch.bottom = rcSearch.top + 1;
5607 iterator_frameditems(&i, infoPtr, &rcSearch);
5608 iterator_next(&i); /* go to first item in the sequence */
5609 lpht->iItem = i.nItem;
5610 iterator_destroy(&i);
5612 TRACE("lpht->iItem=%d\n", lpht->iItem);
5613 if (lpht->iItem == -1) return -1;
5615 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5616 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5617 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5618 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5619 lvItem.iItem = lpht->iItem;
5620 lvItem.iSubItem = 0;
5621 lvItem.pszText = szDispText;
5622 lvItem.cchTextMax = DISP_TEXT_SIZE;
5623 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5624 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5626 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5627 LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position);
5628 opt.x = lpht->pt.x - Position.x - Origin.x;
5629 opt.y = lpht->pt.y - Position.y - Origin.y;
5631 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5634 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5635 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5636 if (!PtInRect(&rcBounds, opt)) return -1;
5638 if (PtInRect(&rcIcon, opt))
5639 lpht->flags |= LVHT_ONITEMICON;
5640 else if (PtInRect(&rcLabel, opt))
5641 lpht->flags |= LVHT_ONITEMLABEL;
5642 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5643 lpht->flags |= LVHT_ONITEMSTATEICON;
5644 if (lpht->flags & LVHT_ONITEM)
5645 lpht->flags &= ~LVHT_NOWHERE;
5647 TRACE("lpht->flags=0x%x\n", lpht->flags);
5648 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5652 rcBounds.right = rcBounds.left;
5653 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5655 rcBounds.left = rcBounds.right;
5656 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5657 if (PtInRect(&rcBounds, opt))
5665 if (!select || lpht->iItem == -1) return lpht->iItem;
5667 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5669 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5670 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5674 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5675 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5676 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5677 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5678 their own sort proc. when sending LVM_SORTITEMS.
5681 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5683 LVS_SORTXXX must be specified,
5684 LVS_OWNERDRAW is not set,
5685 <item>.pszText is not LPSTR_TEXTCALLBACK.
5687 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5688 are sorted based on item text..."
5690 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5692 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5693 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5694 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5696 /* if we're sorting descending, negate the return value */
5697 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5702 * Inserts a new item in the listview control.
5705 * [I] infoPtr : valid pointer to the listview structure
5706 * [I] lpLVItem : item information
5707 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5710 * SUCCESS : new item index
5713 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5715 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5720 BOOL is_sorted, has_changed;
5723 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5725 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5727 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5728 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5730 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5732 if ( !(lpItem = (ITEM_INFO *)COMCTL32_Alloc(sizeof(ITEM_INFO))) )
5735 /* insert item in listview control data structure */
5736 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5737 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5739 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5740 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5742 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5743 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5744 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5745 if (nItem == -1) goto fail;
5746 infoPtr->nItemCount++;
5748 /* set the item attributes */
5751 item.state &= ~LVIS_STATEIMAGEMASK;
5752 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5754 /* if we're sorted, sort the list, and update the index */
5757 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5758 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5759 assert(nItem != -1);
5762 /* make room for the position, if we are in the right mode */
5763 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5765 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5767 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5769 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5774 /* Add the subitem list to the items array. Do this last in case we go to
5775 * fail during the above.
5777 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5779 /* send LVN_INSERTITEM notification */
5780 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5782 nmlv.lParam = lpItem->lParam;
5783 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5785 /* align items (set position of each item) */
5786 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
5790 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
5791 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
5793 LISTVIEW_NextIconPosTop(infoPtr, &pt);
5795 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
5798 /* now is the invalidation fun */
5799 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
5803 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5804 infoPtr->nItemCount--;
5806 DPA_DeletePtr(hdpaSubItems, 0);
5807 DPA_Destroy (hdpaSubItems);
5808 COMCTL32_Free (lpItem);
5814 * Redraws a range of items.
5817 * [I] infoPtr : valid pointer to the listview structure
5818 * [I] nFirst : first item
5819 * [I] nLast : last item
5825 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5829 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5830 max(nFirst, nLast) >= infoPtr->nItemCount)
5833 for (i = nFirst; i <= nLast; i++)
5834 LISTVIEW_InvalidateItem(infoPtr, i);
5841 * Scroll the content of a listview.
5844 * [I] infoPtr : valid pointer to the listview structure
5845 * [I] dx : horizontal scroll amount in pixels
5846 * [I] dy : vertical scroll amount in pixels
5853 * If the control is in report mode (LVS_REPORT) the control can
5854 * be scrolled only in line increments. "dy" will be rounded to the
5855 * nearest number of pixels that are a whole line. Ex: if line height
5856 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5857 * is passed the the scroll will be 0. (per MSDN 7/2002)
5859 * For: (per experimentaion with native control and CSpy ListView)
5860 * LVS_ICON dy=1 = 1 pixel (vertical only)
5862 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5864 * LVS_LIST dx=1 = 1 column (horizontal only)
5865 * but will only scroll 1 column per message
5866 * no matter what the value.
5867 * dy must be 0 or FALSE returned.
5868 * LVS_REPORT dx=1 = 1 pixel
5872 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5874 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5876 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5877 dy /= infoPtr->nItemHeight;
5880 if (dy != 0) return FALSE;
5887 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5888 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5895 * Sets the background color.
5898 * [I] infoPtr : valid pointer to the listview structure
5899 * [I] clrBk : background color
5905 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5907 TRACE("(clrBk=%lx)\n", clrBk);
5909 if(infoPtr->clrBk != clrBk) {
5910 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5911 infoPtr->clrBk = clrBk;
5912 if (clrBk == CLR_NONE)
5913 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5915 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5916 LISTVIEW_InvalidateList(infoPtr);
5922 /* LISTVIEW_SetBkImage */
5924 /*** Helper for {Insert,Set}ColumnT *only* */
5925 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5927 if (lpColumn->mask & LVCF_FMT)
5929 /* format member is valid */
5930 lphdi->mask |= HDI_FORMAT;
5932 /* set text alignment (leftmost column must be left-aligned) */
5933 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5934 lphdi->fmt |= HDF_LEFT;
5935 else if (lpColumn->fmt & LVCFMT_RIGHT)
5936 lphdi->fmt |= HDF_RIGHT;
5937 else if (lpColumn->fmt & LVCFMT_CENTER)
5938 lphdi->fmt |= HDF_CENTER;
5940 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5941 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
5943 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5945 lphdi->fmt |= HDF_IMAGE;
5946 lphdi->iImage = I_IMAGECALLBACK;
5950 if (lpColumn->mask & LVCF_WIDTH)
5952 lphdi->mask |= HDI_WIDTH;
5953 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5955 /* make it fill the remainder of the controls width */
5959 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5961 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
5962 lphdi->cxy += rcHeader.right - rcHeader.left;
5965 /* retrieve the layout of the header */
5966 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5967 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
5969 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
5972 lphdi->cxy = lpColumn->cx;
5975 if (lpColumn->mask & LVCF_TEXT)
5977 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
5978 lphdi->fmt |= HDF_STRING;
5979 lphdi->pszText = lpColumn->pszText;
5980 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
5983 if (lpColumn->mask & LVCF_IMAGE)
5985 lphdi->mask |= HDI_IMAGE;
5986 lphdi->iImage = lpColumn->iImage;
5989 if (lpColumn->mask & LVCF_ORDER)
5991 lphdi->mask |= HDI_ORDER;
5992 lphdi->iOrder = lpColumn->iOrder;
5999 * Inserts a new column.
6002 * [I] infoPtr : valid pointer to the listview structure
6003 * [I] nColumn : column index
6004 * [I] lpColumn : column information
6005 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6008 * SUCCESS : new column index
6011 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6012 LPLVCOLUMNW lpColumn, BOOL isW)
6014 COLUMN_INFO *lpColumnInfo;
6018 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6020 if (!lpColumn || nColumn < 0 || nColumn > infoPtr->hdpaColumns->nItemCount) return -1;
6022 ZeroMemory(&hdi, sizeof(HDITEMW));
6023 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6025 /* insert item in header control */
6026 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6027 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6028 (WPARAM)nColumn, (LPARAM)&hdi);
6029 if (nNewColumn == -1) return -1;
6030 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6032 /* create our own column info */
6033 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
6034 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6036 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6037 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6039 /* now we have to actually adjust the data */
6040 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6042 SUBITEM_INFO *lpSubItem, *lpMainItem, **lpNewItems = 0;
6046 /* preallocate memory, so we can fail gracefully */
6047 if (nNewColumn == 0)
6049 lpNewItems = COMCTL32_Alloc(sizeof(SUBITEM_INFO *) * infoPtr->nItemCount);
6050 if (!lpNewItems) goto fail;
6051 for (i = 0; i < infoPtr->nItemCount; i++)
6052 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(SUBITEM_INFO)))) break;
6053 if (i != infoPtr->nItemCount)
6055 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
6056 COMCTL32_Free(lpNewItems);
6061 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6063 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6064 for (i = 1; i < hdpaSubItems->nItemCount; i++)
6066 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6067 if (lpSubItem->iSubItem >= nNewColumn)
6068 lpSubItem->iSubItem++;
6071 /* for inserting column 0, we have to special-case the main item */
6072 if (nNewColumn == 0)
6074 lpMainItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6075 lpSubItem = lpNewItems[nItem];
6076 lpSubItem->hdr = lpMainItem->hdr;
6077 lpSubItem->iSubItem = 1;
6078 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
6079 lpMainItem->iSubItem = 0;
6080 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
6084 COMCTL32_Free(lpNewItems);
6087 /* make space for the new column */
6088 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6093 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6096 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6097 COMCTL32_Free(lpColumnInfo);
6104 * Sets the attributes of a header item.
6107 * [I] infoPtr : valid pointer to the listview structure
6108 * [I] nColumn : column index
6109 * [I] lpColumn : column attributes
6110 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6116 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6117 LPLVCOLUMNW lpColumn, BOOL isW)
6119 HDITEMW hdi, hdiget;
6122 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6124 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6126 ZeroMemory(&hdi, sizeof(HDITEMW));
6127 if (lpColumn->mask & LVCF_FMT)
6129 hdi.mask |= HDI_FORMAT;
6130 hdiget.mask = HDI_FORMAT;
6131 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6132 hdi.fmt = hdiget.fmt & HDF_STRING;
6134 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6136 /* set header item attributes */
6137 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6138 if (!bResult) return FALSE;
6140 if (lpColumn->mask & LVCF_FMT)
6142 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6143 int oldFmt = lpColumnInfo->fmt;
6145 lpColumnInfo->fmt = lpColumn->fmt;
6146 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6148 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6149 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6158 * Sets the column order array
6161 * [I] infoPtr : valid pointer to the listview structure
6162 * [I] iCount : number of elements in column order array
6163 * [I] lpiArray : pointer to column order array
6169 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6171 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6182 * Sets the width of a column
6185 * [I] infoPtr : valid pointer to the listview structure
6186 * [I] nColumn : column index
6187 * [I] cx : column width
6193 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6195 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6196 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6200 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6202 /* set column width only if in report or list mode */
6203 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6205 /* take care of invalid cx values */
6206 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6207 else if (uView == LVS_LIST && cx < 1) return FALSE;
6209 /* resize all columns if in LVS_LIST mode */
6210 if(uView == LVS_LIST)
6212 infoPtr->nItemWidth = cx;
6213 LISTVIEW_InvalidateList(infoPtr);
6217 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6219 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6224 lvItem.mask = LVIF_TEXT;
6226 lvItem.iSubItem = nColumn;
6227 lvItem.pszText = szDispText;
6228 lvItem.cchTextMax = DISP_TEXT_SIZE;
6229 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6231 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6232 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6233 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6235 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6236 max_cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6237 max_cx += REPORT_MARGINX + TRAILING_PADDING;
6240 /* autosize based on listview items width */
6241 if(cx == LVSCW_AUTOSIZE)
6243 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6245 /* if iCol is the last column make it fill the remainder of the controls width */
6246 if(nColumn == infoPtr->hdpaColumns->nItemCount - 1)
6251 LISTVIEW_GetOrigin(infoPtr, &Origin);
6252 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6254 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6258 /* Despite what the MS docs say, if this is not the last
6259 column, then MS resizes the column to the width of the
6260 largest text string in the column, including headers
6261 and items. This is different from LVSCW_AUTOSIZE in that
6262 LVSCW_AUTOSIZE ignores the header string length. */
6265 /* retrieve header text */
6266 hdi.mask = HDI_TEXT;
6267 hdi.cchTextMax = DISP_TEXT_SIZE;
6268 hdi.pszText = szDispText;
6269 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6271 HDC hdc = GetDC(infoPtr->hwndSelf);
6272 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6275 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6277 /* FIXME: Take into account the header image, if one is present */
6278 SelectObject(hdc, old_font);
6279 ReleaseDC(infoPtr->hwndSelf, hdc);
6281 cx = max (cx, max_cx);
6285 if (cx < 0) return FALSE;
6287 /* call header to update the column change */
6288 hdi.mask = HDI_WIDTH;
6290 TRACE("hdi.cxy=%d\n", hdi.cxy);
6291 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6296 * Sets the extended listview style.
6299 * [I] infoPtr : valid pointer to the listview structure
6301 * [I] dwStyle : style
6304 * SUCCESS : previous style
6307 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6309 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6313 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6315 infoPtr->dwLvExStyle = dwStyle;
6322 * Sets the new hot cursor used during hot tracking and hover selection.
6325 * [I] infoPtr : valid pointer to the listview structure
6326 * [I} hCurosr : the new hot cursor handle
6329 * Returns the previous hot cursor
6331 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6333 HCURSOR oldCursor = infoPtr->hHotCursor;
6335 infoPtr->hHotCursor = hCursor;
6343 * Sets the hot item index.
6346 * [I] infoPtr : valid pointer to the listview structure
6347 * [I] iIndex : index
6350 * SUCCESS : previous hot item index
6351 * FAILURE : -1 (no hot item)
6353 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6355 INT iOldIndex = infoPtr->nHotItem;
6357 infoPtr->nHotItem = iIndex;
6365 * Sets the amount of time the cursor must hover over an item before it is selected.
6368 * [I] infoPtr : valid pointer to the listview structure
6369 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6372 * Returns the previous hover time
6374 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6376 DWORD oldHoverTime = infoPtr->dwHoverTime;
6378 infoPtr->dwHoverTime = dwHoverTime;
6380 return oldHoverTime;
6385 * Sets spacing for icons of LVS_ICON style.
6388 * [I] infoPtr : valid pointer to the listview structure
6389 * [I] spacing : MAKELONG(cx, cy)
6392 * MAKELONG(oldcx, oldcy)
6394 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6396 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6397 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6398 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6400 TRACE("requested=(%d,%d)\n", cx, cy);
6402 /* this is supported only for LVS_ICON style */
6403 if (uView != LVS_ICON) return oldspacing;
6405 /* set to defaults, if instructed to */
6406 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6407 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6409 /* if 0 then compute width
6410 * FIXME: Should scan each item and determine max width of
6411 * icon or label, then make that the width */
6413 cx = infoPtr->iconSpacing.cx;
6415 /* if 0 then compute height */
6417 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6418 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6421 infoPtr->iconSpacing.cx = cx;
6422 infoPtr->iconSpacing.cy = cy;
6424 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6425 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6426 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6427 infoPtr->ntmHeight);
6429 /* these depend on the iconSpacing */
6430 LISTVIEW_UpdateItemSize(infoPtr);
6435 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6439 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6446 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6447 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6456 * [I] infoPtr : valid pointer to the listview structure
6457 * [I] nType : image list type
6458 * [I] himl : image list handle
6461 * SUCCESS : old image list
6464 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6466 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6467 INT oldHeight = infoPtr->nItemHeight;
6468 HIMAGELIST himlOld = 0;
6470 TRACE("(nType=%d, himl=%p\n", nType, himl);
6475 himlOld = infoPtr->himlNormal;
6476 infoPtr->himlNormal = himl;
6477 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6478 LISTVIEW_SetIconSpacing(infoPtr, 0);
6482 himlOld = infoPtr->himlSmall;
6483 infoPtr->himlSmall = himl;
6484 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6488 himlOld = infoPtr->himlState;
6489 infoPtr->himlState = himl;
6490 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6491 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6495 ERR("Unknown icon type=%d\n", nType);
6499 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6500 if (infoPtr->nItemHeight != oldHeight)
6501 LISTVIEW_UpdateScroll(infoPtr);
6508 * Preallocates memory (does *not* set the actual count of items !)
6511 * [I] infoPtr : valid pointer to the listview structure
6512 * [I] nItems : item count (projected number of items to allocate)
6513 * [I] dwFlags : update flags
6519 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6521 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6523 if (infoPtr->dwStyle & LVS_OWNERDATA)
6525 int precount,topvisible;
6527 TRACE("LVS_OWNERDATA is set!\n");
6528 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6529 FIXME("flags %s %s not implemented\n",
6530 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6532 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6534 LISTVIEW_DeselectAll(infoPtr);
6536 precount = infoPtr->nItemCount;
6537 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6538 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6540 infoPtr->nItemCount = nItems;
6541 LISTVIEW_UpdateItemSize(infoPtr);
6543 LISTVIEW_UpdateSize(infoPtr);
6544 LISTVIEW_UpdateScroll(infoPtr);
6546 if (min(precount,infoPtr->nItemCount) < topvisible)
6547 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6551 /* According to MSDN for non-LVS_OWNERDATA this is just
6552 * a performance issue. The control allocates its internal
6553 * data structures for the number of items specified. It
6554 * cuts down on the number of memory allocations. Therefore
6555 * we will just issue a WARN here
6557 WARN("for non-ownerdata performance option not implemented.\n");
6565 * Sets the position of an item.
6568 * [I] infoPtr : valid pointer to the listview structure
6569 * [I] nItem : item index
6570 * [I] pt : coordinate
6576 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6578 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6581 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6583 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6584 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6586 LISTVIEW_GetOrigin(infoPtr, &Origin);
6588 /* This point value seems to be an undocumented feature.
6589 * The best guess is that it means either at the origin,
6590 * or at true beginning of the list. I will assume the origin. */
6591 if ((pt.x == -1) && (pt.y == -1))
6594 if (uView == LVS_ICON)
6596 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6597 pt.y -= ICON_TOP_PADDING;
6602 infoPtr->bAutoarrange = FALSE;
6604 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
6609 * Sets the state of one or many items.
6612 * [I] infoPtr : valid pointer to the listview structure
6613 * [I] nItem : item index
6614 * [I] lpLVItem : item or subitem info
6620 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6622 BOOL bResult = TRUE;
6625 lvItem.iItem = nItem;
6626 lvItem.iSubItem = 0;
6627 lvItem.mask = LVIF_STATE;
6628 lvItem.state = lpLVItem->state;
6629 lvItem.stateMask = lpLVItem->stateMask;
6630 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6634 /* apply to all items */
6635 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6636 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6639 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6646 * Sets the text of an item or subitem.
6649 * [I] hwnd : window handle
6650 * [I] nItem : item index
6651 * [I] lpLVItem : item or subitem info
6652 * [I] isW : TRUE if input is Unicode
6658 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6662 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6664 lvItem.iItem = nItem;
6665 lvItem.iSubItem = lpLVItem->iSubItem;
6666 lvItem.mask = LVIF_TEXT;
6667 lvItem.pszText = lpLVItem->pszText;
6668 lvItem.cchTextMax = lpLVItem->cchTextMax;
6670 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6672 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6677 * Set item index that marks the start of a multiple selection.
6680 * [I] infoPtr : valid pointer to the listview structure
6681 * [I] nIndex : index
6684 * Index number or -1 if there is no selection mark.
6686 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6688 INT nOldIndex = infoPtr->nSelectionMark;
6690 TRACE("(nIndex=%d)\n", nIndex);
6692 infoPtr->nSelectionMark = nIndex;
6699 * Sets the text background color.
6702 * [I] infoPtr : valid pointer to the listview structure
6703 * [I] clrTextBk : text background color
6709 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6711 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6713 if (infoPtr->clrTextBk != clrTextBk)
6715 infoPtr->clrTextBk = clrTextBk;
6716 LISTVIEW_InvalidateList(infoPtr);
6724 * Sets the text foreground color.
6727 * [I] infoPtr : valid pointer to the listview structure
6728 * [I] clrText : text color
6734 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6736 TRACE("(clrText=%lx)\n", clrText);
6738 if (infoPtr->clrText != clrText)
6740 infoPtr->clrText = clrText;
6741 LISTVIEW_InvalidateList(infoPtr);
6747 /* LISTVIEW_SetToolTips */
6748 /* LISTVIEW_SetUnicodeFormat */
6749 /* LISTVIEW_SetWorkAreas */
6753 * Callback internally used by LISTVIEW_SortItems()
6756 * [I] first : pointer to first ITEM_INFO to compare
6757 * [I] second : pointer to second ITEM_INFO to compare
6758 * [I] lParam : HWND of control
6761 * if first comes before second : negative
6762 * if first comes after second : positive
6763 * if first and second are equivalent : zero
6765 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6767 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6768 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6769 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6771 /* Forward the call to the client defined callback */
6772 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6777 * Sorts the listview items.
6780 * [I] infoPtr : valid pointer to the listview structure
6781 * [I] pfnCompare : application-defined value
6782 * [I] lParamSort : pointer to comparision callback
6788 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6790 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6793 LPVOID selectionMarkItem;
6797 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6799 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
6801 if (!infoPtr->hdpaItems) return FALSE;
6803 /* if there are 0 or 1 items, there is no need to sort */
6804 if (infoPtr->nItemCount < 2) return TRUE;
6806 if (infoPtr->nFocusedItem >= 0)
6808 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6809 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6810 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6812 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6813 /* clear the lpItem->state for non-selected ones */
6814 /* remove the selection ranges */
6816 infoPtr->pfnCompare = pfnCompare;
6817 infoPtr->lParamSort = lParamSort;
6818 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6820 /* Adjust selections and indices so that they are the way they should
6821 * be after the sort (otherwise, the list items move around, but
6822 * whatever is at the item's previous original position will be
6825 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6826 for (i=0; i < infoPtr->nItemCount; i++)
6828 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6829 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6831 if (lpItem->state & LVIS_SELECTED)
6833 item.state = LVIS_SELECTED;
6834 item.stateMask = LVIS_SELECTED;
6835 LISTVIEW_SetItemState(infoPtr, i, &item);
6837 if (lpItem->state & LVIS_FOCUSED)
6839 infoPtr->nFocusedItem = i;
6840 lpItem->state &= ~LVIS_FOCUSED;
6843 if (selectionMarkItem != NULL)
6844 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6845 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6847 /* refresh the display */
6848 if (uView != LVS_ICON && uView != LVS_SMALLICON)
6849 LISTVIEW_InvalidateList(infoPtr);
6856 * Updates an items or rearranges the listview control.
6859 * [I] infoPtr : valid pointer to the listview structure
6860 * [I] nItem : item index
6866 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6868 TRACE("(nItem=%d)\n", nItem);
6870 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6872 /* rearrange with default alignment style */
6873 if (is_autoarrange(infoPtr))
6874 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
6876 LISTVIEW_InvalidateItem(infoPtr, nItem);
6884 * Creates the listview control.
6887 * [I] hwnd : window handle
6888 * [I] lpcs : the create parameters
6894 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6896 LISTVIEW_INFO *infoPtr;
6897 UINT uView = lpcs->style & LVS_TYPEMASK;
6900 TRACE("(lpcs=%p)\n", lpcs);
6902 /* initialize info pointer */
6903 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6904 if (!infoPtr) return -1;
6906 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6908 infoPtr->hwndSelf = hwnd;
6909 infoPtr->dwStyle = lpcs->style;
6910 /* determine the type of structures to use */
6911 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6912 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6914 /* initialize color information */
6915 infoPtr->clrBk = CLR_NONE;
6916 infoPtr->clrText = comctl32_color.clrWindowText;
6917 infoPtr->clrTextBk = CLR_DEFAULT;
6918 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6920 /* set default values */
6921 infoPtr->nFocusedItem = -1;
6922 infoPtr->nSelectionMark = -1;
6923 infoPtr->nHotItem = -1;
6924 infoPtr->bRedraw = TRUE;
6925 infoPtr->bFirstPaint = TRUE;
6926 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6927 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6928 infoPtr->nEditLabelItem = -1;
6929 infoPtr->dwHoverTime = -1; /* default system hover time */
6931 /* get default font (icon title) */
6932 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6933 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6934 infoPtr->hFont = infoPtr->hDefaultFont;
6935 LISTVIEW_SaveTextMetrics(infoPtr);
6938 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6939 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6940 0, 0, 0, 0, hwnd, (HMENU)0,
6941 lpcs->hInstance, NULL);
6943 /* set header unicode format */
6944 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
6946 /* set header font */
6947 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
6949 /* allocate memory for the selection ranges */
6950 if (!(infoPtr->selectionRanges = ranges_create(10))) return -1;
6952 /* allocate memory for the data structure */
6953 /* FIXME: what if we fail? */
6954 infoPtr->hdpaItems = DPA_Create(10);
6955 infoPtr->hdpaPosX = DPA_Create(10);
6956 infoPtr->hdpaPosY = DPA_Create(10);
6957 infoPtr->hdpaColumns = DPA_Create(10);
6959 /* initialize the icon sizes */
6960 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
6961 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
6963 /* init item size to avoid division by 0 */
6964 LISTVIEW_UpdateItemSize (infoPtr);
6966 if (uView == LVS_REPORT)
6968 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6970 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6974 /* set HDS_HIDDEN flag to hide the header bar */
6975 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6976 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6985 * Erases the background of the listview control.
6988 * [I] infoPtr : valid pointer to the listview structure
6989 * [I] hdc : device context handle
6995 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6999 TRACE("(hdc=%p)\n", hdc);
7001 if (!GetClipBox(hdc, &rc)) return FALSE;
7003 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7009 * Helper function for LISTVIEW_[HV]Scroll *only*.
7010 * Performs vertical/horizontal scrolling by a give amount.
7013 * [I] infoPtr : valid pointer to the listview structure
7014 * [I] dx : amount of horizontal scroll
7015 * [I] dy : amount of vertical scroll
7017 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7019 /* now we can scroll the list */
7020 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7021 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7022 /* if we have focus, adjust rect */
7023 OffsetRect(&infoPtr->rcFocus, dx, dy);
7024 UpdateWindow(infoPtr->hwndSelf);
7029 * Performs vertical scrolling.
7032 * [I] infoPtr : valid pointer to the listview structure
7033 * [I] nScrollCode : scroll code
7034 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7035 * [I] hScrollWnd : scrollbar control window handle
7041 * SB_LINEUP/SB_LINEDOWN:
7042 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7043 * for LVS_REPORT is 1 line
7044 * for LVS_LIST cannot occur
7047 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7048 INT nScrollDiff, HWND hScrollWnd)
7050 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7051 INT nOldScrollPos, nNewScrollPos;
7052 SCROLLINFO scrollInfo;
7055 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7057 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7059 scrollInfo.cbSize = sizeof(SCROLLINFO);
7060 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7062 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7064 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7066 nOldScrollPos = scrollInfo.nPos;
7067 switch (nScrollCode)
7073 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7077 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7081 nScrollDiff = -scrollInfo.nPage;
7085 nScrollDiff = scrollInfo.nPage;
7088 case SB_THUMBPOSITION:
7090 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7097 /* quit right away if pos isn't changing */
7098 if (nScrollDiff == 0) return 0;
7100 /* calculate new position, and handle overflows */
7101 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7102 if (nScrollDiff > 0) {
7103 if (nNewScrollPos < nOldScrollPos ||
7104 nNewScrollPos > scrollInfo.nMax)
7105 nNewScrollPos = scrollInfo.nMax;
7107 if (nNewScrollPos > nOldScrollPos ||
7108 nNewScrollPos < scrollInfo.nMin)
7109 nNewScrollPos = scrollInfo.nMin;
7112 /* set the new position, and reread in case it changed */
7113 scrollInfo.fMask = SIF_POS;
7114 scrollInfo.nPos = nNewScrollPos;
7115 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7117 /* carry on only if it really changed */
7118 if (nNewScrollPos == nOldScrollPos) return 0;
7120 /* now adjust to client coordinates */
7121 nScrollDiff = nOldScrollPos - nNewScrollPos;
7122 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7124 /* and scroll the window */
7125 scroll_list(infoPtr, 0, nScrollDiff);
7132 * Performs horizontal scrolling.
7135 * [I] infoPtr : valid pointer to the listview structure
7136 * [I] nScrollCode : scroll code
7137 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7138 * [I] hScrollWnd : scrollbar control window handle
7144 * SB_LINELEFT/SB_LINERIGHT:
7145 * for LVS_ICON, LVS_SMALLICON 1 pixel
7146 * for LVS_REPORT is 1 pixel
7147 * for LVS_LIST is 1 column --> which is a 1 because the
7148 * scroll is based on columns not pixels
7151 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7152 INT nScrollDiff, HWND hScrollWnd)
7154 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7155 INT nOldScrollPos, nNewScrollPos;
7156 SCROLLINFO scrollInfo;
7158 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7160 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7162 scrollInfo.cbSize = sizeof(SCROLLINFO);
7163 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7165 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7167 nOldScrollPos = scrollInfo.nPos;
7169 switch (nScrollCode)
7183 nScrollDiff = -scrollInfo.nPage;
7187 nScrollDiff = scrollInfo.nPage;
7190 case SB_THUMBPOSITION:
7192 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7199 /* quit right away if pos isn't changing */
7200 if (nScrollDiff == 0) return 0;
7202 /* calculate new position, and handle overflows */
7203 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7204 if (nScrollDiff > 0) {
7205 if (nNewScrollPos < nOldScrollPos ||
7206 nNewScrollPos > scrollInfo.nMax)
7207 nNewScrollPos = scrollInfo.nMax;
7209 if (nNewScrollPos > nOldScrollPos ||
7210 nNewScrollPos < scrollInfo.nMin)
7211 nNewScrollPos = scrollInfo.nMin;
7214 /* set the new position, and reread in case it changed */
7215 scrollInfo.fMask = SIF_POS;
7216 scrollInfo.nPos = nNewScrollPos;
7217 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7219 /* carry on only if it really changed */
7220 if (nNewScrollPos == nOldScrollPos) return 0;
7222 if(uView == LVS_REPORT)
7223 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7225 /* now adjust to client coordinates */
7226 nScrollDiff = nOldScrollPos - nNewScrollPos;
7227 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7229 /* and scroll the window */
7230 scroll_list(infoPtr, nScrollDiff, 0);
7235 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7237 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7238 INT gcWheelDelta = 0;
7239 UINT pulScrollLines = 3;
7240 SCROLLINFO scrollInfo;
7242 TRACE("(wheelDelta=%d)\n", wheelDelta);
7244 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7245 gcWheelDelta -= wheelDelta;
7247 scrollInfo.cbSize = sizeof(SCROLLINFO);
7248 scrollInfo.fMask = SIF_POS;
7255 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7256 * should be fixed in the future.
7258 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7259 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7260 scrollInfo.nPos + (gcWheelDelta < 0) ?
7261 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7262 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7266 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7268 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7270 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7271 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7272 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7278 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7289 * [I] infoPtr : valid pointer to the listview structure
7290 * [I] nVirtualKey : virtual key
7291 * [I] lKeyData : key data
7296 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7298 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7300 NMLVKEYDOWN nmKeyDown;
7302 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7304 /* send LVN_KEYDOWN notification */
7305 nmKeyDown.wVKey = nVirtualKey;
7306 nmKeyDown.flags = 0;
7307 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7309 switch (nVirtualKey)
7312 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7314 notify(infoPtr, NM_RETURN);
7315 notify(infoPtr, LVN_ITEMACTIVATE);
7320 if (infoPtr->nItemCount > 0)
7325 if (infoPtr->nItemCount > 0)
7326 nItem = infoPtr->nItemCount - 1;
7330 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7334 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7338 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7342 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7346 if (uView == LVS_REPORT)
7347 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7349 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7350 * LISTVIEW_GetCountPerRow(infoPtr);
7351 if(nItem < 0) nItem = 0;
7355 if (uView == LVS_REPORT)
7356 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7358 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7359 * LISTVIEW_GetCountPerRow(infoPtr);
7360 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7364 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7365 LISTVIEW_KeySelection(infoPtr, nItem);
7375 * [I] infoPtr : valid pointer to the listview structure
7380 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7384 /* if we did not have the focus, there's nothing to do */
7385 if (!infoPtr->bFocus) return 0;
7387 /* send NM_KILLFOCUS notification */
7388 notify(infoPtr, NM_KILLFOCUS);
7390 /* if we have a focus rectagle, get rid of it */
7391 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7393 /* set window focus flag */
7394 infoPtr->bFocus = FALSE;
7396 /* invalidate the selected items before reseting focus flag */
7397 LISTVIEW_InvalidateSelectedItems(infoPtr);
7404 * Processes double click messages (left mouse button).
7407 * [I] infoPtr : valid pointer to the listview structure
7408 * [I] wKey : key flag
7409 * [I] pts : mouse coordinate
7414 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7416 LVHITTESTINFO htInfo;
7418 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7420 /* send NM_RELEASEDCAPTURE notification */
7421 notify(infoPtr, NM_RELEASEDCAPTURE);
7423 htInfo.pt.x = pts.x;
7424 htInfo.pt.y = pts.y;
7426 /* send NM_DBLCLK notification */
7427 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7428 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7430 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7431 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7438 * Processes mouse down messages (left mouse button).
7441 * [I] infoPtr : valid pointer to the listview structure
7442 * [I] wKey : key flag
7443 * [I] pts : mouse coordinate
7448 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7450 LVHITTESTINFO lvHitTestInfo;
7451 static BOOL bGroupSelect = TRUE;
7452 POINT pt = { pts.x, pts.y };
7455 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7457 /* send NM_RELEASEDCAPTURE notification */
7458 notify(infoPtr, NM_RELEASEDCAPTURE);
7460 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7462 /* set left button down flag */
7463 infoPtr->bLButtonDown = TRUE;
7465 lvHitTestInfo.pt.x = pts.x;
7466 lvHitTestInfo.pt.y = pts.y;
7468 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7469 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7470 infoPtr->nEditLabelItem = -1;
7471 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7473 if (infoPtr->dwStyle & LVS_SINGLESEL)
7475 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7476 infoPtr->nEditLabelItem = nItem;
7478 LISTVIEW_SetSelection(infoPtr, nItem);
7482 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7486 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7487 LISTVIEW_SetItemFocus(infoPtr, nItem);
7488 infoPtr->nSelectionMark = nItem;
7494 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7495 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7497 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7498 infoPtr->nSelectionMark = nItem;
7501 else if (wKey & MK_CONTROL)
7505 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7507 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7508 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7509 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7510 infoPtr->nSelectionMark = nItem;
7512 else if (wKey & MK_SHIFT)
7514 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7518 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7519 infoPtr->nEditLabelItem = nItem;
7521 /* set selection (clears other pre-existing selections) */
7522 LISTVIEW_SetSelection(infoPtr, nItem);
7528 /* remove all selections */
7529 LISTVIEW_DeselectAll(infoPtr);
7537 * Processes mouse up messages (left mouse button).
7540 * [I] infoPtr : valid pointer to the listview structure
7541 * [I] wKey : key flag
7542 * [I] pts : mouse coordinate
7547 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7549 LVHITTESTINFO lvHitTestInfo;
7551 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7553 if (!infoPtr->bLButtonDown) return 0;
7555 lvHitTestInfo.pt.x = pts.x;
7556 lvHitTestInfo.pt.y = pts.y;
7558 /* send NM_CLICK notification */
7559 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7560 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7562 /* set left button flag */
7563 infoPtr->bLButtonDown = FALSE;
7565 /* if we clicked on a selected item, edit the label */
7566 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7567 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7574 * Destroys the listview control (called after WM_DESTROY).
7577 * [I] infoPtr : valid pointer to the listview structure
7582 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7586 /* delete all items */
7587 LISTVIEW_DeleteAllItems(infoPtr);
7589 /* destroy data structure */
7590 DPA_Destroy(infoPtr->hdpaItems);
7591 if (infoPtr->selectionRanges) ranges_destroy(infoPtr->selectionRanges);
7593 /* destroy image lists */
7594 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7596 if (infoPtr->himlNormal)
7597 ImageList_Destroy(infoPtr->himlNormal);
7598 if (infoPtr->himlSmall)
7599 ImageList_Destroy(infoPtr->himlSmall);
7600 if (infoPtr->himlState)
7601 ImageList_Destroy(infoPtr->himlState);
7604 /* destroy font, bkgnd brush */
7606 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7607 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7609 /* free listview info pointer*/
7610 COMCTL32_Free(infoPtr);
7612 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7618 * Handles notifications from header.
7621 * [I] infoPtr : valid pointer to the listview structure
7622 * [I] nCtrlId : control identifier
7623 * [I] lpnmh : notification information
7628 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, LPNMHEADERW lpnmh)
7630 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7632 TRACE("(lpnmh=%p)\n", lpnmh);
7634 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7636 switch (lpnmh->hdr.code)
7640 case HDN_ITEMCHANGEDW:
7641 case HDN_ITEMCHANGEDA:
7643 COLUMN_INFO *lpColumnInfo;
7646 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7650 hdi.mask = HDI_WIDTH;
7651 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7655 cxy = lpnmh->pitem->cxy;
7657 /* determine how much we change since the last know position */
7658 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7659 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7662 RECT rcCol = lpColumnInfo->rcHeader;
7664 lpColumnInfo->rcHeader.right += dx;
7665 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7666 if (uView == LVS_REPORT && is_redrawing(infoPtr))
7668 /* this trick works for left aligned columns only */
7669 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7671 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
7672 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
7674 rcCol.top = infoPtr->rcList.top;
7675 rcCol.bottom = infoPtr->rcList.bottom;
7676 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7682 case HDN_ITEMCLICKW:
7683 case HDN_ITEMCLICKA:
7685 /* Handle sorting by Header Column */
7688 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7690 nmlv.iSubItem = lpnmh->iItem;
7691 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7701 * Determines the type of structure to use.
7704 * [I] infoPtr : valid pointer to the listview structureof the sender
7705 * [I] hwndFrom : listview window handle
7706 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7711 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7713 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
7715 if (nCommand != NF_REQUERY) return 0;
7717 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
7724 * Paints/Repaints the listview control.
7727 * [I] infoPtr : valid pointer to the listview structure
7728 * [I] hdc : device context handle
7733 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7735 TRACE("(hdc=%p)\n", hdc);
7737 if (infoPtr->bFirstPaint)
7739 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7741 infoPtr->bFirstPaint = FALSE;
7742 LISTVIEW_UpdateSize(infoPtr);
7743 LISTVIEW_UpdateItemSize(infoPtr);
7744 if (uView == LVS_ICON || uView == LVS_SMALLICON)
7745 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7746 LISTVIEW_UpdateScroll(infoPtr);
7749 LISTVIEW_Refresh(infoPtr, hdc);
7754 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7756 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7757 LISTVIEW_Refresh(infoPtr, hdc);
7758 EndPaint(infoPtr->hwndSelf, &ps);
7766 * Processes double click messages (right mouse button).
7769 * [I] infoPtr : valid pointer to the listview structure
7770 * [I] wKey : key flag
7771 * [I] pts : mouse coordinate
7776 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7778 LVHITTESTINFO lvHitTestInfo;
7780 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7782 /* send NM_RELEASEDCAPTURE notification */
7783 notify(infoPtr, NM_RELEASEDCAPTURE);
7785 /* send NM_RDBLCLK notification */
7786 lvHitTestInfo.pt.x = pts.x;
7787 lvHitTestInfo.pt.y = pts.y;
7788 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7789 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7796 * Processes mouse down messages (right mouse button).
7799 * [I] infoPtr : valid pointer to the listview structure
7800 * [I] wKey : key flag
7801 * [I] pts : mouse coordinate
7806 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7808 LVHITTESTINFO lvHitTestInfo;
7811 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7813 /* send NM_RELEASEDCAPTURE notification */
7814 notify(infoPtr, NM_RELEASEDCAPTURE);
7816 /* make sure the listview control window has the focus */
7817 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7819 /* set right button down flag */
7820 infoPtr->bRButtonDown = TRUE;
7822 /* determine the index of the selected item */
7823 lvHitTestInfo.pt.x = pts.x;
7824 lvHitTestInfo.pt.y = pts.y;
7825 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7827 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7829 LISTVIEW_SetItemFocus(infoPtr, nItem);
7830 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7831 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7832 LISTVIEW_SetSelection(infoPtr, nItem);
7836 LISTVIEW_DeselectAll(infoPtr);
7844 * Processes mouse up messages (right mouse button).
7847 * [I] infoPtr : valid pointer to the listview structure
7848 * [I] wKey : key flag
7849 * [I] pts : mouse coordinate
7854 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7856 LVHITTESTINFO lvHitTestInfo;
7859 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7861 if (!infoPtr->bRButtonDown) return 0;
7863 /* set button flag */
7864 infoPtr->bRButtonDown = FALSE;
7866 /* Send NM_RClICK notification */
7867 lvHitTestInfo.pt.x = pts.x;
7868 lvHitTestInfo.pt.y = pts.y;
7869 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7870 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7872 /* Change to screen coordinate for WM_CONTEXTMENU */
7873 pt = lvHitTestInfo.pt;
7874 ClientToScreen(infoPtr->hwndSelf, &pt);
7876 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7877 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7878 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7889 * [I] infoPtr : valid pointer to the listview structure
7890 * [I] hwnd : window handle of window containing the cursor
7891 * [I] nHittest : hit-test code
7892 * [I] wMouseMsg : ideintifier of the mouse message
7895 * TRUE if cursor is set
7898 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7900 LVHITTESTINFO lvHitTestInfo;
7902 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7904 if(!infoPtr->hHotCursor) return FALSE;
7906 GetCursorPos(&lvHitTestInfo.pt);
7907 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7909 SetCursor(infoPtr->hHotCursor);
7919 * [I] infoPtr : valid pointer to the listview structure
7920 * [I] hwndLoseFocus : handle of previously focused window
7925 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7927 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
7929 /* if we have the focus already, there's nothing to do */
7930 if (infoPtr->bFocus) return 0;
7932 /* send NM_SETFOCUS notification */
7933 notify(infoPtr, NM_SETFOCUS);
7935 /* set window focus flag */
7936 infoPtr->bFocus = TRUE;
7938 /* put the focus rect back on */
7939 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7941 /* redraw all visible selected items */
7942 LISTVIEW_InvalidateSelectedItems(infoPtr);
7952 * [I] infoPtr : valid pointer to the listview structure
7953 * [I] fRedraw : font handle
7954 * [I] fRedraw : redraw flag
7959 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7961 HFONT oldFont = infoPtr->hFont;
7963 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
7965 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7966 if (infoPtr->hFont == oldFont) return 0;
7968 LISTVIEW_SaveTextMetrics(infoPtr);
7970 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7971 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7973 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7980 * Message handling for WM_SETREDRAW.
7981 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7984 * [I] infoPtr : valid pointer to the listview structure
7985 * [I] bRedraw: state of redraw flag
7988 * DefWinProc return value
7990 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7992 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7994 infoPtr->bRedraw = bRedraw;
7996 if(!bRedraw) return 0;
7998 LISTVIEW_UpdateSize(infoPtr);
7999 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8000 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8001 LISTVIEW_UpdateScroll(infoPtr);
8002 LISTVIEW_InvalidateList(infoPtr);
8009 * Resizes the listview control. This function processes WM_SIZE
8010 * messages. At this time, the width and height are not used.
8013 * [I] infoPtr : valid pointer to the listview structure
8014 * [I] Width : new width
8015 * [I] Height : new height
8020 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8022 TRACE("(width=%d, height=%d)\n", Width, Height);
8024 if (!is_redrawing(infoPtr)) return 0;
8026 if (!LISTVIEW_UpdateSize(infoPtr)) return 0;
8028 if (is_autoarrange(infoPtr))
8029 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8031 LISTVIEW_UpdateScroll(infoPtr);
8033 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8040 * Sets the size information.
8043 * [I] infoPtr : valid pointer to the listview structure
8046 * Zero if no size change
8049 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8051 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8055 GetClientRect(infoPtr->hwndSelf, &rcList);
8056 CopyRect(&rcOld,&(infoPtr->rcList));
8057 infoPtr->rcList.left = 0;
8058 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8059 infoPtr->rcList.top = 0;
8060 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8062 if (uView == LVS_LIST)
8064 /* Apparently the "LIST" style is supposed to have the same
8065 * number of items in a column even if there is no scroll bar.
8066 * Since if a scroll bar already exists then the bottom is already
8067 * reduced, only reduce if the scroll bar does not currently exist.
8068 * The "2" is there to mimic the native control. I think it may be
8069 * related to either padding or edges. (GLA 7/2002)
8071 if (!(infoPtr->dwStyle & WS_HSCROLL))
8073 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8074 if (infoPtr->rcList.bottom > nHScrollHeight)
8075 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8079 if (infoPtr->rcList.bottom > 2)
8080 infoPtr->rcList.bottom -= 2;
8083 else if (uView == LVS_REPORT)
8090 Header_Layout(infoPtr->hwndHeader, &hl);
8092 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8094 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
8095 infoPtr->rcList.top = max(wp.cy, 0);
8097 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8102 * Processes WM_STYLECHANGED messages.
8105 * [I] infoPtr : valid pointer to the listview structure
8106 * [I] wStyleType : window style type (normal or extended)
8107 * [I] lpss : window style information
8112 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8115 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8116 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8118 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8119 wStyleType, lpss->styleOld, lpss->styleNew);
8121 if (wStyleType != GWL_STYLE) return 0;
8123 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8124 /* what if LVS_OWNERDATA changed? */
8125 /* or LVS_SINGLESEL */
8126 /* or LVS_SORT{AS,DES}CENDING */
8128 infoPtr->dwStyle = lpss->styleNew;
8130 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8131 ((lpss->styleNew & WS_HSCROLL) == 0))
8132 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8134 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8135 ((lpss->styleNew & WS_VSCROLL) == 0))
8136 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8138 if (uNewView != uOldView)
8140 SIZE oldIconSize = infoPtr->iconSize;
8143 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8144 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8146 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8147 SetRectEmpty(&infoPtr->rcFocus);
8149 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8150 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8152 if (uNewView == LVS_ICON)
8154 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8156 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8157 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8158 LISTVIEW_SetIconSpacing(infoPtr, 0);
8161 else if (uNewView == LVS_REPORT)
8166 hl.prc = &infoPtr->rcList;
8168 Header_Layout(infoPtr->hwndHeader, &hl);
8169 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8172 LISTVIEW_UpdateItemSize(infoPtr);
8175 if (uNewView == LVS_REPORT)
8176 ShowWindow(infoPtr->hwndHeader, (LVS_NOCOLUMNHEADER & lpss->styleNew) ? SW_HIDE : SW_SHOWNORMAL);
8178 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8179 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8180 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8182 /* update the size of the client area */
8183 LISTVIEW_UpdateSize(infoPtr);
8185 /* add scrollbars if needed */
8186 LISTVIEW_UpdateScroll(infoPtr);
8188 /* invalidate client area + erase background */
8189 LISTVIEW_InvalidateList(infoPtr);
8196 * Window procedure of the listview control.
8199 static LRESULT WINAPI
8200 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8202 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8204 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8206 if (!infoPtr && (uMsg != WM_CREATE))
8207 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8211 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8216 case LVM_APPROXIMATEVIEWRECT:
8217 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8218 LOWORD(lParam), HIWORD(lParam));
8220 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8222 /* case LVM_CANCELEDITLABEL: */
8224 /* case LVM_CREATEDRAGIMAGE: */
8226 case LVM_DELETEALLITEMS:
8227 return LISTVIEW_DeleteAllItems(infoPtr);
8229 case LVM_DELETECOLUMN:
8230 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8232 case LVM_DELETEITEM:
8233 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8235 case LVM_EDITLABELW:
8236 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8238 case LVM_EDITLABELA:
8239 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8241 /* case LVM_ENABLEGROUPVIEW: */
8243 case LVM_ENSUREVISIBLE:
8244 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8247 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8250 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8252 case LVM_GETBKCOLOR:
8253 return infoPtr->clrBk;
8255 /* case LVM_GETBKIMAGE: */
8257 case LVM_GETCALLBACKMASK:
8258 return infoPtr->uCallbackMask;
8260 case LVM_GETCOLUMNA:
8261 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8263 case LVM_GETCOLUMNW:
8264 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8266 case LVM_GETCOLUMNORDERARRAY:
8267 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8269 case LVM_GETCOLUMNWIDTH:
8270 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8272 case LVM_GETCOUNTPERPAGE:
8273 return LISTVIEW_GetCountPerPage(infoPtr);
8275 case LVM_GETEDITCONTROL:
8276 return (LRESULT)infoPtr->hwndEdit;
8278 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8279 return infoPtr->dwLvExStyle;
8281 /* case LVM_GETGROUPINFO: */
8283 /* case LVM_GETGROUPMETRICS: */
8286 return (LRESULT)infoPtr->hwndHeader;
8288 case LVM_GETHOTCURSOR:
8289 return (LRESULT)infoPtr->hHotCursor;
8291 case LVM_GETHOTITEM:
8292 return infoPtr->nHotItem;
8294 case LVM_GETHOVERTIME:
8295 return infoPtr->dwHoverTime;
8297 case LVM_GETIMAGELIST:
8298 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8300 /* case LVM_GETINSERTMARK: */
8302 /* case LVM_GETINSERTMARKCOLOR: */
8304 /* case LVM_GETINSERTMARKRECT: */
8306 case LVM_GETISEARCHSTRINGA:
8307 case LVM_GETISEARCHSTRINGW:
8308 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8312 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8315 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8317 case LVM_GETITEMCOUNT:
8318 return infoPtr->nItemCount;
8320 case LVM_GETITEMPOSITION:
8321 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8323 case LVM_GETITEMRECT:
8324 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8326 case LVM_GETITEMSPACING:
8327 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8329 case LVM_GETITEMSTATE:
8330 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8332 case LVM_GETITEMTEXTA:
8333 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8335 case LVM_GETITEMTEXTW:
8336 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8338 case LVM_GETNEXTITEM:
8339 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8341 case LVM_GETNUMBEROFWORKAREAS:
8342 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8346 if (!lParam) return FALSE;
8347 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8350 /* case LVM_GETOUTLINECOLOR: */
8352 /* case LVM_GETSELECTEDCOLUMN: */
8354 case LVM_GETSELECTEDCOUNT:
8355 return LISTVIEW_GetSelectedCount(infoPtr);
8357 case LVM_GETSELECTIONMARK:
8358 return infoPtr->nSelectionMark;
8360 case LVM_GETSTRINGWIDTHA:
8361 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8363 case LVM_GETSTRINGWIDTHW:
8364 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8366 case LVM_GETSUBITEMRECT:
8367 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8369 case LVM_GETTEXTBKCOLOR:
8370 return infoPtr->clrTextBk;
8372 case LVM_GETTEXTCOLOR:
8373 return infoPtr->clrText;
8375 /* case LVM_GETTILEINFO: */
8377 /* case LVM_GETTILEVIEWINFO: */
8379 case LVM_GETTOOLTIPS:
8380 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8383 case LVM_GETTOPINDEX:
8384 return LISTVIEW_GetTopIndex(infoPtr);
8386 /*case LVM_GETUNICODEFORMAT:
8387 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8390 /* case LVM_GETVIEW: */
8392 case LVM_GETVIEWRECT:
8393 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8395 case LVM_GETWORKAREAS:
8396 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8399 /* case LVM_HASGROUP: */
8402 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8404 case LVM_INSERTCOLUMNA:
8405 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8407 case LVM_INSERTCOLUMNW:
8408 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8410 /* case LVM_INSERTGROUP: */
8412 /* case LVM_INSERTGROUPSORTED: */
8414 case LVM_INSERTITEMA:
8415 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8417 case LVM_INSERTITEMW:
8418 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8420 /* case LVM_INSERTMARKHITTEST: */
8422 /* case LVM_ISGROUPVIEWENABLED: */
8424 /* case LVM_MAPIDTOINDEX: */
8426 /* case LVM_MAPINDEXTOID: */
8428 /* case LVM_MOVEGROUP: */
8430 /* case LVM_MOVEITEMTOGROUP: */
8432 case LVM_REDRAWITEMS:
8433 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8435 /* case LVM_REMOVEALLGROUPS: */
8437 /* case LVM_REMOVEGROUP: */
8440 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8442 case LVM_SETBKCOLOR:
8443 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8445 /* case LVM_SETBKIMAGE: */
8447 case LVM_SETCALLBACKMASK:
8448 infoPtr->uCallbackMask = (UINT)wParam;
8451 case LVM_SETCOLUMNA:
8452 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8454 case LVM_SETCOLUMNW:
8455 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8457 case LVM_SETCOLUMNORDERARRAY:
8458 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8460 case LVM_SETCOLUMNWIDTH:
8461 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8463 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8464 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8466 /* case LVM_SETGROUPINFO: */
8468 /* case LVM_SETGROUPMETRICS: */
8470 case LVM_SETHOTCURSOR:
8471 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8473 case LVM_SETHOTITEM:
8474 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8476 case LVM_SETHOVERTIME:
8477 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8479 case LVM_SETICONSPACING:
8480 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8482 case LVM_SETIMAGELIST:
8483 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8485 /* case LVM_SETINFOTIP: */
8487 /* case LVM_SETINSERTMARK: */
8489 /* case LVM_SETINSERTMARKCOLOR: */
8492 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8495 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8497 case LVM_SETITEMCOUNT:
8498 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8500 case LVM_SETITEMPOSITION:
8502 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8503 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8506 case LVM_SETITEMPOSITION32:
8507 if (lParam == 0) return FALSE;
8508 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8510 case LVM_SETITEMSTATE:
8511 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8513 case LVM_SETITEMTEXTA:
8514 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8516 case LVM_SETITEMTEXTW:
8517 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8519 /* case LVM_SETOUTLINECOLOR: */
8521 /* case LVM_SETSELECTEDCOLUMN: */
8523 case LVM_SETSELECTIONMARK:
8524 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8526 case LVM_SETTEXTBKCOLOR:
8527 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8529 case LVM_SETTEXTCOLOR:
8530 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8532 /* case LVM_SETTILEINFO: */
8534 /* case LVM_SETTILEVIEWINFO: */
8536 /* case LVM_SETTILEWIDTH: */
8538 /* case LVM_SETTOOLTIPS: */
8540 /* case LVM_SETUNICODEFORMAT: */
8542 /* case LVM_SETVIEW: */
8544 /* case LVM_SETWORKAREAS: */
8546 /* case LVM_SORTGROUPS: */
8549 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8551 /* LVM_SORTITEMSEX: */
8553 case LVM_SUBITEMHITTEST:
8554 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8557 return LISTVIEW_Update(infoPtr, (INT)wParam);
8560 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8563 return LISTVIEW_Command(infoPtr, wParam, lParam);
8566 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8569 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8572 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8575 return (LRESULT)infoPtr->hFont;
8578 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8581 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8584 return LISTVIEW_KillFocus(infoPtr);
8586 case WM_LBUTTONDBLCLK:
8587 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8589 case WM_LBUTTONDOWN:
8590 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8593 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8596 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8599 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8602 return LISTVIEW_NCDestroy(infoPtr);
8605 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8606 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8609 case WM_NOTIFYFORMAT:
8610 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8613 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8615 case WM_RBUTTONDBLCLK:
8616 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8618 case WM_RBUTTONDOWN:
8619 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8622 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8625 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8630 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8633 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8636 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8639 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8641 case WM_STYLECHANGED:
8642 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8644 case WM_SYSCOLORCHANGE:
8645 COMCTL32_RefreshSysColors();
8648 /* case WM_TIMER: */
8651 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8654 if (wParam & (MK_SHIFT | MK_CONTROL))
8655 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8656 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8658 case WM_WINDOWPOSCHANGED:
8659 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
8661 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8662 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8663 /* FIXME: why do we need this here? */
8664 LISTVIEW_UpdateSize(infoPtr);
8665 LISTVIEW_UpdateScroll(infoPtr);
8667 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8669 /* case WM_WININICHANGE: */
8672 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8673 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8676 /* call default window procedure */
8677 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8685 * Registers the window class.
8693 void LISTVIEW_Register(void)
8697 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8698 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8699 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8700 wndClass.cbClsExtra = 0;
8701 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8702 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8703 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8704 wndClass.lpszClassName = WC_LISTVIEWW;
8705 RegisterClassW(&wndClass);
8710 * Unregisters the window class.
8718 void LISTVIEW_Unregister(void)
8720 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8725 * Handle any WM_COMMAND messages
8728 * [I] infoPtr : valid pointer to the listview structure
8729 * [I] wParam : the first message parameter
8730 * [I] lParam : the second message parameter
8735 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8737 switch (HIWORD(wParam))
8742 * Adjust the edit window size
8745 HDC hdc = GetDC(infoPtr->hwndEdit);
8746 HFONT hFont, hOldFont = 0;
8751 if (!infoPtr->hwndEdit || !hdc) return 0;
8752 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8753 GetWindowRect(infoPtr->hwndEdit, &rect);
8755 /* Select font to get the right dimension of the string */
8756 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8759 hOldFont = SelectObject(hdc, hFont);
8762 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8764 TEXTMETRICW textMetric;
8766 /* Add Extra spacing for the next character */
8767 GetTextMetricsW(hdc, &textMetric);
8768 sz.cx += (textMetric.tmMaxCharWidth * 2);
8776 rect.bottom - rect.top,
8777 SWP_DRAWFRAME|SWP_NOMOVE);
8780 SelectObject(hdc, hOldFont);
8782 ReleaseDC(infoPtr->hwndSelf, hdc);
8788 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8797 * Subclassed edit control windproc function
8800 * [I] hwnd : the edit window handle
8801 * [I] uMsg : the message that is to be processed
8802 * [I] wParam : first message parameter
8803 * [I] lParam : second message parameter
8804 * [I] isW : TRUE if input is Unicode
8809 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
8811 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8812 BOOL cancel = FALSE;
8814 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8815 hwnd, uMsg, wParam, lParam, isW);
8820 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8827 WNDPROC editProc = infoPtr->EditWndProc;
8828 infoPtr->EditWndProc = 0;
8829 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8830 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8834 if (VK_ESCAPE == (INT)wParam)
8839 else if (VK_RETURN == (INT)wParam)
8843 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8847 if (infoPtr->hwndEdit)
8849 LPWSTR buffer = NULL;
8851 infoPtr->hwndEdit = 0;
8854 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8858 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8860 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8861 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8865 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8867 if (buffer) COMCTL32_Free(buffer);
8871 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8877 * Subclassed edit control Unicode windproc function
8880 * [I] hwnd : the edit window handle
8881 * [I] uMsg : the message that is to be processed
8882 * [I] wParam : first message parameter
8883 * [I] lParam : second message parameter
8887 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8889 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8894 * Subclassed edit control ANSI windproc function
8897 * [I] hwnd : the edit window handle
8898 * [I] uMsg : the message that is to be processed
8899 * [I] wParam : first message parameter
8900 * [I] lParam : second message parameter
8904 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8906 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8911 * Creates a subclassed edit cotrol
8914 * [I] infoPtr : valid pointer to the listview structure
8915 * [I] text : initial text for the edit
8916 * [I] style : the window style
8917 * [I] isW : TRUE if input is Unicode
8921 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8922 INT x, INT y, INT width, INT height, BOOL isW)
8924 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8929 TEXTMETRICW textMetric;
8930 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8932 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8934 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8935 hdc = GetDC(infoPtr->hwndSelf);
8937 /* Select the font to get appropriate metric dimensions */
8938 if(infoPtr->hFont != 0)
8939 hOldFont = SelectObject(hdc, infoPtr->hFont);
8941 /*Get String Lenght in pixels */
8942 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8944 /*Add Extra spacing for the next character */
8945 GetTextMetricsW(hdc, &textMetric);
8946 sz.cx += (textMetric.tmMaxCharWidth * 2);
8948 if(infoPtr->hFont != 0)
8949 SelectObject(hdc, hOldFont);
8951 ReleaseDC(infoPtr->hwndSelf, hdc);
8953 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8955 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8957 if (!hedit) return 0;
8959 infoPtr->EditWndProc = (WNDPROC)
8960 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8961 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8963 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);