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; /* From GetTextMetrics from above font */
252 BOOL bRedraw; /* Turns on/off repaints & invalidations */
253 BOOL bFirstPaint; /* Flags if the control has never painted before */
254 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
258 DWORD dwStyle; /* the cached window GWL_STYLE */
259 DWORD dwLvExStyle; /* extended listview style */
260 INT nItemCount; /* the number of items in the list */
261 HDPA hdpaItems; /* array ITEM_INFO pointers */
262 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
263 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
264 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
265 POINT currIconPos; /* this is the position next icon will be placed */
266 PFNLVCOMPARE pfnCompare;
273 DWORD lastKeyPressTimestamp;
275 INT nSearchParamLength;
276 WCHAR szSearchParam[ MAX_PATH ];
283 /* How many we debug buffer to allocate */
284 #define DEBUG_BUFFERS 20
285 /* The size of a single debug bbuffer */
286 #define DEBUG_BUFFER_SIZE 256
288 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
289 #define SB_INTERNAL -1
291 /* maximum size of a label */
292 #define DISP_TEXT_SIZE 512
294 /* padding for items in list and small icon display modes */
295 #define WIDTH_PADDING 12
297 /* padding for items in list, report and small icon display modes */
298 #define HEIGHT_PADDING 1
300 /* offset of items in report display mode */
301 #define REPORT_MARGINX 2
303 /* padding for icon in large icon display mode
304 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
305 * that HITTEST will see.
306 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
307 * ICON_TOP_PADDING - sum of the two above.
308 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
309 * LABEL_VERT_PADDING - between bottom of text and end of box
311 * ICON_LR_PADDING - additional width above icon size.
312 * ICON_LR_HALF - half of the above value
314 #define ICON_TOP_PADDING_NOTHITABLE 2
315 #define ICON_TOP_PADDING_HITABLE 2
316 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
317 #define ICON_BOTTOM_PADDING 4
318 #define LABEL_VERT_PADDING 7
319 #define ICON_LR_PADDING 16
320 #define ICON_LR_HALF (ICON_LR_PADDING/2)
322 /* default label width for items in list and small icon display modes */
323 #define DEFAULT_LABEL_WIDTH 40
325 /* default column width for items in list display mode */
326 #define DEFAULT_COLUMN_WIDTH 128
328 /* Size of "line" scroll for V & H scrolls */
329 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
331 /* Padding betwen image and label */
332 #define IMAGE_PADDING 2
334 /* Padding behind the label */
335 #define TRAILING_PADDING 5
337 /* Border for the icon caption */
338 #define CAPTION_BORDER 2
340 /* Standard DrawText flags */
341 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
342 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
343 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
345 /* The time in milisecods to reset the search in the list */
346 #define KEY_DELAY 450
348 /* Dump the LISTVIEW_INFO structure to the debug channel */
349 #define LISTVIEW_DUMP(iP) do { \
350 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
351 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
352 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
353 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
354 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
355 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
356 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
357 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
358 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
359 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
364 * forward declarations
366 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
367 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
368 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
369 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
370 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
371 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
372 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
373 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
374 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
375 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
376 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
377 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
378 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
379 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
380 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
381 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
382 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
383 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
384 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
385 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
386 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
387 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
388 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
389 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
390 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
392 /******** Text handling functions *************************************/
394 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
395 * text string. The string may be ANSI or Unicode, in which case
396 * the boolean isW tells us the type of the string.
398 * The name of the function tell what type of strings it expects:
399 * W: Unicode, T: ANSI/Unicode - function of isW
402 static inline BOOL is_textW(LPCWSTR text)
404 return text != NULL && text != LPSTR_TEXTCALLBACKW;
407 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
409 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
410 return is_textW(text);
413 static inline int textlenT(LPCWSTR text, BOOL isW)
415 return !is_textT(text, isW) ? 0 :
416 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
419 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
422 if (isSrcW) lstrcpynW(dest, src, max);
423 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
425 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
426 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
429 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
431 LPWSTR wstr = (LPWSTR)text;
433 if (!isW && is_textT(text, isW))
435 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
436 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
437 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
439 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
443 static inline void textfreeT(LPWSTR wstr, BOOL isW)
445 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
449 * dest is a pointer to a Unicode string
450 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
452 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
456 if (src == LPSTR_TEXTCALLBACKW)
458 if (is_textW(*dest)) COMCTL32_Free(*dest);
459 *dest = LPSTR_TEXTCALLBACKW;
463 LPWSTR pszText = textdupTtoW(src, isW);
464 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
465 bResult = Str_SetPtrW(dest, pszText);
466 textfreeT(pszText, isW);
472 * compares a Unicode to a Unicode/ANSI text string
474 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
476 if (!aw) return bt ? -1 : 0;
477 if (!bt) return aw ? 1 : 0;
478 if (aw == LPSTR_TEXTCALLBACKW)
479 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
480 if (bt != LPSTR_TEXTCALLBACKW)
482 LPWSTR bw = textdupTtoW(bt, isW);
483 int r = bw ? lstrcmpW(aw, bw) : 1;
491 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
495 n = min(min(n, strlenW(s1)), strlenW(s2));
496 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
497 return res ? res - sizeof(WCHAR) : res;
500 /******** Debugging functions *****************************************/
502 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
504 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
505 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
508 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
510 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
511 n = min(textlenT(text, isW), n);
512 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
515 static char* debug_getbuf()
517 static int index = 0;
518 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
519 return buffers[index++ % DEBUG_BUFFERS];
522 static inline char* debugrange(const RANGE* lprng)
526 char* buf = debug_getbuf();
527 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
529 } else return "(null)";
532 static inline char* debugpoint(const POINT* lppt)
536 char* buf = debug_getbuf();
537 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
539 } else return "(null)";
542 static inline char* debugrect(const RECT* rect)
546 char* buf = debug_getbuf();
547 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
548 rect->left, rect->top, rect->right, rect->bottom);
550 } else return "(null)";
553 static char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
555 char* buf = debug_getbuf(), *text = buf;
556 int len, size = DEBUG_BUFFER_SIZE;
558 if (pScrollInfo == NULL) return "(null)";
559 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
560 if (len == -1) goto end; buf += len; size -= len;
561 if (pScrollInfo->fMask & SIF_RANGE)
562 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
564 if (len == -1) goto end; buf += len; size -= len;
565 if (pScrollInfo->fMask & SIF_PAGE)
566 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
568 if (len == -1) goto end; buf += len; size -= len;
569 if (pScrollInfo->fMask & SIF_POS)
570 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
572 if (len == -1) goto end; buf += len; size -= len;
573 if (pScrollInfo->fMask & SIF_TRACKPOS)
574 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
576 if (len == -1) goto end; buf += len; size -= len;
579 buf = text + strlen(text);
581 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
585 static char* debugnmlistview(LPNMLISTVIEW plvnm)
589 char* buf = debug_getbuf();
590 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
591 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
592 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
593 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
595 } else return "(null)";
598 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
600 char* buf = debug_getbuf(), *text = buf;
601 int len, size = DEBUG_BUFFER_SIZE;
603 if (lpLVItem == NULL) return "(null)";
604 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
605 if (len == -1) goto end; buf += len; size -= len;
606 if (lpLVItem->mask & LVIF_STATE)
607 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
609 if (len == -1) goto end; buf += len; size -= len;
610 if (lpLVItem->mask & LVIF_TEXT)
611 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
613 if (len == -1) goto end; buf += len; size -= len;
614 if (lpLVItem->mask & LVIF_IMAGE)
615 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
617 if (len == -1) goto end; buf += len; size -= len;
618 if (lpLVItem->mask & LVIF_PARAM)
619 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
621 if (len == -1) goto end; buf += len; size -= len;
622 if (lpLVItem->mask & LVIF_INDENT)
623 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
625 if (len == -1) goto end; buf += len; size -= len;
628 buf = text + strlen(text);
630 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
634 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
636 char* buf = debug_getbuf(), *text = buf;
637 int len, size = DEBUG_BUFFER_SIZE;
639 if (lpColumn == NULL) return "(null)";
640 len = snprintf(buf, size, "{");
641 if (len == -1) goto end; buf += len; size -= len;
642 if (lpColumn->mask & LVCF_SUBITEM)
643 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
645 if (len == -1) goto end; buf += len; size -= len;
646 if (lpColumn->mask & LVCF_FMT)
647 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
649 if (len == -1) goto end; buf += len; size -= len;
650 if (lpColumn->mask & LVCF_WIDTH)
651 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
653 if (len == -1) goto end; buf += len; size -= len;
654 if (lpColumn->mask & LVCF_TEXT)
655 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
657 if (len == -1) goto end; buf += len; size -= len;
658 if (lpColumn->mask & LVCF_IMAGE)
659 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
661 if (len == -1) goto end; buf += len; size -= len;
662 if (lpColumn->mask & LVCF_ORDER)
663 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
665 if (len == -1) goto end; buf += len; size -= len;
668 buf = text + strlen(text);
670 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
675 /******** Notification functions i************************************/
677 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
681 TRACE("(code=%d)\n", code);
683 pnmh->hwndFrom = infoPtr->hwndSelf;
684 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
686 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
687 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
689 TRACE(" <= %ld\n", result);
694 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
697 return notify_hdr(infoPtr, code, &nmh);
700 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
702 notify(infoPtr, LVN_ITEMACTIVATE);
705 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
707 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
708 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
711 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
715 ZeroMemory(&nmlv, sizeof(nmlv));
716 nmlv.iItem = lvht->iItem;
717 nmlv.iSubItem = lvht->iSubItem;
718 nmlv.ptAction = lvht->pt;
719 return notify_listview(infoPtr, code, &nmlv);
722 static int get_ansi_notification(INT unicodeNotificationCode)
724 switch (unicodeNotificationCode)
726 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
727 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
728 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
729 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
730 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
731 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
733 ERR("unknown notification %x\n", unicodeNotificationCode);
738 Send notification. depends on dispinfoW having same
739 structure as dispinfoA.
740 infoPtr : listview struct
741 notificationCode : *Unicode* notification code
742 pdi : dispinfo structure (can be unicode or ansi)
743 isW : TRUE if dispinfo is Unicode
745 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
747 BOOL bResult = FALSE;
748 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
750 INT cchTempBufMax = 0, savCchTextMax = 0;
751 LPWSTR pszTempBuf = NULL, savPszText = NULL;
753 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
755 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
756 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
759 if (convertToAnsi || convertToUnicode)
761 if (notificationCode != LVN_GETDISPINFOW)
763 cchTempBufMax = convertToUnicode ?
764 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
765 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
769 cchTempBufMax = pdi->item.cchTextMax;
770 *pdi->item.pszText = 0; /* make sure we don't process garbage */
773 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
774 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
775 if (!pszTempBuf) return FALSE;
776 if (convertToUnicode)
777 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
778 pszTempBuf, cchTempBufMax);
780 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
781 cchTempBufMax, NULL, NULL);
782 savCchTextMax = pdi->item.cchTextMax;
783 savPszText = pdi->item.pszText;
784 pdi->item.pszText = pszTempBuf;
785 pdi->item.cchTextMax = cchTempBufMax;
788 if (infoPtr->notifyFormat == NFR_ANSI)
789 realNotifCode = get_ansi_notification(notificationCode);
791 realNotifCode = notificationCode;
792 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
793 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
795 if (convertToUnicode || convertToAnsi)
797 if (convertToUnicode) /* note : pointer can be changed by app ! */
798 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
799 savCchTextMax, NULL, NULL);
801 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
802 savPszText, savCchTextMax);
803 pdi->item.pszText = savPszText; /* restores our buffer */
804 pdi->item.cchTextMax = savCchTextMax;
805 HeapFree(GetProcessHeap(), 0, pszTempBuf);
810 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds)
812 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
813 lpnmlvcd->nmcd.hdc = hdc;
814 lpnmlvcd->nmcd.rc = *rcBounds;
815 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
816 lpnmlvcd->clrText = infoPtr->clrText;
819 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
821 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
822 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
825 /******** Item iterator functions **********************************/
827 static RANGES ranges_create(int count);
828 static void ranges_destroy(RANGES ranges);
829 static BOOL ranges_add(RANGES ranges, RANGE range);
830 static BOOL ranges_del(RANGES ranges, RANGE range);
831 static void ranges_dump(RANGES ranges);
833 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
835 RANGE range = { nItem, nItem + 1 };
837 return ranges_add(ranges, range);
840 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
842 RANGE range = { nItem, nItem + 1 };
844 return ranges_del(ranges, range);
848 * ITERATOR DOCUMENTATION
850 * The iterator functions allow for easy, and convenient iteration
851 * over items of iterest in the list. Typically, you create a
852 * iterator, use it, and destroy it, as such:
855 * iterator_xxxitems(&i, ...);
856 * while (iterator_{prev,next}(&i)
858 * //code which uses i.nItem
860 * iterator_destroy(&i);
862 * where xxx is either: framed, or visible.
863 * Note that it is important that the code destroys the iterator
864 * after it's done with it, as the creation of the iterator may
865 * allocate memory, which thus needs to be freed.
867 * You can iterate both forwards, and backwards through the list,
868 * by using iterator_next or iterator_prev respectively.
870 * Lower numbered items are draw on top of higher number items in
871 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
872 * items may overlap). So, to test items, you should use
874 * which lists the items top to bottom (in Z-order).
875 * For drawing items, you should use
877 * which lists the items bottom to top (in Z-order).
878 * If you keep iterating over the items after the end-of-items
879 * marker (-1) is returned, the iterator will start from the
880 * beginning. Typically, you don't need to test for -1,
881 * because iterator_{next,prev} will return TRUE if more items
882 * are to be iterated over, or FALSE otherwise.
884 * Note: the iterator is defined to be bidirectional. That is,
885 * any number of prev followed by any number of next, or
886 * five versa, should leave the iterator at the same item:
887 * prev * n, next * n = next * n, prev * n
889 * The iterator has a notion of a out-of-order, special item,
890 * which sits at the start of the list. This is used in
891 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
892 * which needs to be first, as it may overlap other items.
894 * The code is a bit messy because we have:
895 * - a special item to deal with
896 * - simple range, or composite range
898 * If find bugs, or want to add features, please make sure you
899 * always check/modify *both* iterator_prev, and iterator_next.
903 * This function iterates through the items in increasing order,
904 * but prefixed by the special item, then -1. That is:
905 * special, 1, 2, 3, ..., n, -1.
906 * Each item is listed only once.
908 static inline BOOL iterator_next(ITERATOR* i)
912 i->nItem = i->nSpecial;
913 if (i->nItem != -1) return TRUE;
915 if (i->nItem == i->nSpecial)
917 if (i->ranges) i->index = 0;
923 if (i->nItem == i->nSpecial) i->nItem++;
924 if (i->nItem < i->range.upper) return TRUE;
929 if (i->index < i->ranges->hdpa->nItemCount)
930 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
933 else if (i->nItem >= i->range.upper) goto end;
935 i->nItem = i->range.lower;
936 if (i->nItem >= 0) goto testitem;
943 * This function iterates through the items in decreasing order,
944 * followed by the special item, then -1. That is:
945 * n, n-1, ..., 3, 2, 1, special, -1.
946 * Each item is listed only once.
948 static inline BOOL iterator_prev(ITERATOR* i)
955 if (i->ranges) i->index = i->ranges->hdpa->nItemCount;
958 if (i->nItem == i->nSpecial)
966 if (i->nItem == i->nSpecial) i->nItem--;
967 if (i->nItem >= i->range.lower) return TRUE;
973 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
976 else if (!start && i->nItem < i->range.lower) goto end;
978 i->nItem = i->range.upper;
979 if (i->nItem > 0) goto testitem;
981 return (i->nItem = i->nSpecial) != -1;
984 static RANGE iterator_range(ITERATOR* i)
988 if (!i->ranges) return i->range;
990 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
991 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->ranges->hdpa->nItemCount - 1)).upper;
996 * Releases resources associated with this ierator.
998 static inline void iterator_destroy(ITERATOR* i)
1000 if (i->ranges) ranges_destroy(i->ranges);
1004 * Create an empty iterator.
1006 static inline BOOL iterator_empty(ITERATOR* i)
1008 ZeroMemory(i, sizeof(*i));
1009 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1015 * Create an iterator over a bunch of ranges.
1016 * Please note that the iterator will take ownership of the ranges,
1017 * and will free them upon destruction.
1019 static inline BOOL iterator_ranges(ITERATOR* i, RANGES ranges)
1027 * Creates an iterator over the items which intersect lprc.
1029 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
1031 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1032 RECT frame = *lprc, rcItem, rcTemp;
1035 /* in case we fail, we want to return an empty iterator */
1036 if (!iterator_empty(i)) return FALSE;
1038 LISTVIEW_GetOrigin(infoPtr, &Origin);
1040 TRACE("(lprc=%s)\n", debugrect(lprc));
1041 OffsetRect(&frame, -Origin.x, -Origin.y);
1043 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1047 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1049 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1050 if (IntersectRect(&rcTemp, &rcItem, lprc))
1051 i->nSpecial = infoPtr->nFocusedItem;
1053 if (!(i->ranges = ranges_create(50))) return FALSE;
1054 /* to do better here, we need to have PosX, and PosY sorted */
1055 TRACE("building icon ranges:\n");
1056 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1058 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1059 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1060 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1061 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1062 if (IntersectRect(&rcTemp, &rcItem, &frame))
1063 ranges_additem(i->ranges, nItem);
1067 else if (uView == LVS_REPORT)
1071 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1072 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1074 lower = max(frame.top / infoPtr->nItemHeight, 0);
1075 upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1);
1076 if (upper < lower) return TRUE;
1077 i->range.lower = lower;
1078 i->range.upper = upper + 1;
1079 TRACE(" report=%s\n", debugrange(&i->range));
1083 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1084 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1085 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1086 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1087 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1088 INT lower = nFirstCol * nPerCol + nFirstRow;
1092 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1093 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1095 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1097 if (!(i->ranges = ranges_create(nLastCol - nFirstCol + 1))) return FALSE;
1098 TRACE("building list ranges:\n");
1099 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1101 item_range.lower = nCol * nPerCol + nFirstRow;
1102 if(item_range.lower >= infoPtr->nItemCount) break;
1103 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1104 TRACE(" list=%s\n", debugrange(&item_range));
1105 ranges_add(i->ranges, item_range);
1113 * Creates an iterator over the items which intersect the visible region of hdc.
1115 static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
1117 POINT Origin, Position;
1118 RECT rcItem, rcClip;
1121 rgntype = GetClipBox(hdc, &rcClip);
1122 if (rgntype == NULLREGION) return iterator_empty(i);
1123 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1124 if (rgntype == SIMPLEREGION) return TRUE;
1126 /* first deal with the special item */
1127 if (i->nSpecial != -1)
1129 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1130 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1133 /* if we can't deal with the region, we'll just go with the simple range */
1134 LISTVIEW_GetOrigin(infoPtr, &Origin);
1135 TRACE("building visible range:\n");
1136 if (!i->ranges && i->range.lower < i->range.upper)
1138 if (!(i->ranges = ranges_create(50))) return TRUE;
1139 if (!ranges_add(i->ranges, i->range))
1141 ranges_destroy(i->ranges);
1147 /* now delete the invisible items from the list */
1148 while(iterator_next(i))
1150 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1151 rcItem.left = Position.x + Origin.x;
1152 rcItem.top = Position.y + Origin.y;
1153 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1154 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1155 if (!RectVisible(hdc, &rcItem))
1156 ranges_delitem(i->ranges, i->nItem);
1158 /* the iterator should restart on the next iterator_next */
1164 /******** Misc helper functions ************************************/
1166 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1167 WPARAM wParam, LPARAM lParam, BOOL isW)
1169 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1170 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1173 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1175 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1177 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1178 (uView == LVS_ICON || uView == LVS_SMALLICON);
1181 /******** Internal API functions ************************************/
1183 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1185 assert (nSubItem >= 0 && nSubItem < infoPtr->hdpaColumns->nItemCount);
1186 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1189 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1191 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1194 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1196 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1199 /* Listview invlaidation functions: use _only_ these function to invalidate */
1201 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1203 return infoPtr->bRedraw && !infoPtr->bFirstPaint;
1206 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT*rect)
1208 if(!is_redrawing(infoPtr)) return;
1209 TRACE(" invalidating rect=%s\n", debugrect(rect));
1210 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1213 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1217 if(!is_redrawing(infoPtr)) return;
1218 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1219 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1222 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1224 POINT Origin, Position;
1227 if(!is_redrawing(infoPtr)) return;
1228 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1229 LISTVIEW_GetOrigin(infoPtr, &Origin);
1230 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1231 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1233 rcBox.bottom = infoPtr->nItemHeight;
1234 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1235 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1238 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1240 LISTVIEW_InvalidateRect(infoPtr, NULL);
1243 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1247 if(!infoPtr->bRedraw) return;
1248 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1249 rcCol.top = infoPtr->rcList.top;
1250 rcCol.bottom = infoPtr->rcList.bottom;
1251 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1256 * Retrieves the number of items that can fit vertically in the client area.
1259 * [I] infoPtr : valid pointer to the listview structure
1262 * Number of items per row.
1264 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1266 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1268 return max(nListWidth/infoPtr->nItemWidth, 1);
1273 * Retrieves the number of items that can fit horizontally in the client
1277 * [I] infoPtr : valid pointer to the listview structure
1280 * Number of items per column.
1282 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1284 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1286 return max(nListHeight / infoPtr->nItemHeight, 1);
1290 /*************************************************************************
1291 * LISTVIEW_ProcessLetterKeys
1293 * Processes keyboard messages generated by pressing the letter keys
1295 * What this does is perform a case insensitive search from the
1296 * current position with the following quirks:
1297 * - If two chars or more are pressed in quick succession we search
1298 * for the corresponding string (e.g. 'abc').
1299 * - If there is a delay we wipe away the current search string and
1300 * restart with just that char.
1301 * - If the user keeps pressing the same character, whether slowly or
1302 * fast, so that the search string is entirely composed of this
1303 * character ('aaaaa' for instance), then we search for first item
1304 * that starting with that character.
1305 * - If the user types the above character in quick succession, then
1306 * we must also search for the corresponding string ('aaaaa'), and
1307 * go to that string if there is a match.
1310 * [I] hwnd : handle to the window
1311 * [I] charCode : the character code, the actual character
1312 * [I] keyData : key data
1320 * - The current implementation has a list of characters it will
1321 * accept and it ignores averything else. In particular it will
1322 * ignore accentuated characters which seems to match what
1323 * Windows does. But I'm not sure it makes sense to follow
1325 * - We don't sound a beep when the search fails.
1329 * TREEVIEW_ProcessLetterKeys
1331 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1336 WCHAR buffer[MAX_PATH];
1337 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1339 /* simple parameter checking */
1340 if (!charCode || !keyData) return 0;
1342 /* only allow the valid WM_CHARs through */
1343 if (!isalnum(charCode) &&
1344 charCode != '.' && charCode != '`' && charCode != '!' &&
1345 charCode != '@' && charCode != '#' && charCode != '$' &&
1346 charCode != '%' && charCode != '^' && charCode != '&' &&
1347 charCode != '*' && charCode != '(' && charCode != ')' &&
1348 charCode != '-' && charCode != '_' && charCode != '+' &&
1349 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1350 charCode != '}' && charCode != '[' && charCode != '{' &&
1351 charCode != '/' && charCode != '?' && charCode != '>' &&
1352 charCode != '<' && charCode != ',' && charCode != '~')
1355 /* if there's one item or less, there is no where to go */
1356 if (infoPtr->nItemCount <= 1) return 0;
1358 /* update the search parameters */
1359 infoPtr->lastKeyPressTimestamp = GetTickCount();
1360 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1361 if (infoPtr->nSearchParamLength < MAX_PATH)
1362 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1363 if (infoPtr->charCode != charCode)
1364 infoPtr->charCode = charCode = 0;
1366 infoPtr->charCode=charCode;
1367 infoPtr->szSearchParam[0]=charCode;
1368 infoPtr->nSearchParamLength=1;
1369 /* Redundant with the 1 char string */
1373 /* and search from the current position */
1375 if (infoPtr->nFocusedItem >= 0) {
1376 endidx=infoPtr->nFocusedItem;
1378 /* if looking for single character match,
1379 * then we must always move forward
1381 if (infoPtr->nSearchParamLength == 1)
1384 endidx=infoPtr->nItemCount;
1388 if (idx == infoPtr->nItemCount) {
1389 if (endidx == infoPtr->nItemCount || endidx == 0)
1395 item.mask = LVIF_TEXT;
1398 item.pszText = buffer;
1399 item.cchTextMax = MAX_PATH;
1400 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1402 /* check for a match */
1403 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1406 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1407 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1408 /* This would work but we must keep looking for a longer match */
1412 } while (idx != endidx);
1415 LISTVIEW_KeySelection(infoPtr, nItem);
1420 /*************************************************************************
1421 * LISTVIEW_UpdateHeaderSize [Internal]
1423 * Function to resize the header control
1426 * [I] hwnd : handle to a window
1427 * [I] nNewScrollPos : scroll pos to set
1432 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1437 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1439 GetWindowRect(infoPtr->hwndHeader, &winRect);
1440 point[0].x = winRect.left;
1441 point[0].y = winRect.top;
1442 point[1].x = winRect.right;
1443 point[1].y = winRect.bottom;
1445 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1446 point[0].x = -nNewScrollPos;
1447 point[1].x += nNewScrollPos;
1449 SetWindowPos(infoPtr->hwndHeader,0,
1450 point[0].x,point[0].y,point[1].x,point[1].y,
1451 SWP_NOZORDER | SWP_NOACTIVATE);
1456 * Update the scrollbars. This functions should be called whenever
1457 * the content, size or view changes.
1460 * [I] infoPtr : valid pointer to the listview structure
1465 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1467 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1468 SCROLLINFO horzInfo, vertInfo;
1470 if (infoPtr->dwStyle & LVS_NOSCROLL) return;
1471 if (!is_redrawing(infoPtr)) return;
1473 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1474 horzInfo.cbSize = sizeof(SCROLLINFO);
1475 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1477 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1478 vertInfo.cbSize = sizeof(SCROLLINFO);
1479 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1481 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1482 if (uView == LVS_LIST)
1484 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1485 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1486 horzInfo.nPage /= infoPtr->nItemWidth;
1488 else if (uView == LVS_REPORT)
1490 horzInfo.nMax = infoPtr->nItemWidth;
1491 vertInfo.nMax = infoPtr->nItemCount;
1492 vertInfo.nPage /= infoPtr->nItemHeight;
1494 else /* LVS_ICON, or LVS_SMALLICON */
1498 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1500 horzInfo.nMax = rcView.right - rcView.left;
1501 vertInfo.nMax = rcView.bottom - rcView.top;
1505 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1506 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1507 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1508 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1510 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1511 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1512 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1513 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1515 /* Update the Header Control */
1516 if (uView == LVS_REPORT)
1518 horzInfo.fMask = SIF_POS;
1519 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1520 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1527 * Shows/hides the focus rectangle.
1530 * [I] infoPtr : valid pointer to the listview structure
1531 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1536 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1538 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1541 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1543 if (infoPtr->nFocusedItem < 0) return;
1545 /* we need some gymnastics in ICON mode to handle large items */
1546 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1550 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1551 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1553 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1558 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1560 /* for some reason, owner draw should work only in report mode */
1561 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1566 item.iItem = infoPtr->nFocusedItem;
1568 item.mask = LVIF_PARAM;
1569 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1571 ZeroMemory(&dis, sizeof(dis));
1572 dis.CtlType = ODT_LISTVIEW;
1573 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1574 dis.itemID = item.iItem;
1575 dis.itemAction = ODA_FOCUS;
1576 if (fShow) dis.itemState |= ODS_FOCUS;
1577 dis.hwndItem = infoPtr->hwndSelf;
1579 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1580 dis.itemData = item.lParam;
1582 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1586 DrawFocusRect(hdc, &infoPtr->rcFocus);
1589 ReleaseDC(infoPtr->hwndSelf, hdc);
1593 * Invalidates all visible selected items.
1595 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1599 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1600 while(iterator_next(&i))
1602 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1603 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1605 iterator_destroy(&i);
1610 * DESCRIPTION: [INTERNAL]
1611 * Computes an item's (left,top) corner, relative to rcView.
1612 * That is, the position has NOT been made relative to the Origin.
1613 * This is deliberate, to avoid computing the Origin over, and
1614 * over again, when this function is call in a loop. Instead,
1615 * one ca factor the computation of the Origin before the loop,
1616 * and offset the value retured by this function, on every iteration.
1619 * [I] infoPtr : valid pointer to the listview structure
1620 * [I] nItem : item number
1621 * [O] lpptOrig : item top, left corner
1626 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1628 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1630 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1632 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1634 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1635 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1637 else if (uView == LVS_LIST)
1639 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1640 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1641 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1643 else /* LVS_REPORT */
1645 lpptPosition->x = REPORT_MARGINX;
1646 lpptPosition->y = nItem * infoPtr->nItemHeight;
1651 * DESCRIPTION: [INTERNAL]
1652 * Compute the rectangles of an item. This is to localize all
1653 * the computations in one place. If you are not interested in some
1654 * of these values, simply pass in a NULL -- the fucntion is smart
1655 * enough to compute only what's necessary. The function computes
1656 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1657 * one, the BOX rectangle. This rectangle is very cheap to compute,
1658 * and is guaranteed to contain all the other rectangles. Computing
1659 * the ICON rect is also cheap, but all the others are potentaily
1660 * expensive. This gives an easy and effective optimization when
1661 * searching (like point inclusion, or rectangle intersection):
1662 * first test against the BOX, and if TRUE, test agains the desired
1664 * If the function does not have all the necessary information
1665 * to computed the requested rectangles, will crash with a
1666 * failed assertion. This is done so we catch all programming
1667 * errors, given that the function is called only from our code.
1669 * We have the following 'special' meanings for a few fields:
1670 * * If LVIS_FOCUSED is set, we assume the item has the focus
1671 * This is important in ICON mode, where it might get a larger
1672 * then usual rectange
1674 * Please note that subitem support works only in REPORT mode.
1677 * [I] infoPtr : valid pointer to the listview structure
1678 * [I] lpLVItem : item to compute the measures for
1679 * [O] lprcBox : ptr to Box rectangle
1680 * The internal LVIR_BOX rectangle
1681 * [0] lprcState : ptr to State icon rectangle
1682 * The internal LVIR_STATE rectangle
1683 * [O] lprcIcon : ptr to Icon rectangle
1684 * Same as LVM_GETITEMRECT with LVIR_ICON
1685 * [O] lprcLabel : ptr to Label rectangle
1686 * Same as LVM_GETITEMRECT with LVIR_LABEL
1691 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1692 LPRECT lprcBox, LPRECT lprcState,
1693 LPRECT lprcIcon, LPRECT lprcLabel)
1695 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1696 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1697 RECT Box, State, Icon, Label;
1698 COLUMN_INFO *lpColumnInfo = NULL;
1700 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1702 /* Be smart and try to figure out the minimum we have to do */
1703 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1704 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1706 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1707 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1709 if (lprcLabel) doLabel = TRUE;
1710 if (doLabel || lprcIcon) doIcon = TRUE;
1711 if (doIcon || lprcState) doState = TRUE;
1713 /************************************************************/
1714 /* compute the box rectangle (it should be cheap to do) */
1715 /************************************************************/
1716 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1717 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1719 if (lpLVItem->iSubItem)
1721 Box = lpColumnInfo->rcHeader;
1726 Box.right = infoPtr->nItemWidth;
1729 Box.bottom = infoPtr->nItemHeight;
1731 /************************************************************/
1732 /* compute STATEICON bounding box */
1733 /************************************************************/
1736 if (uView == LVS_ICON)
1738 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1739 if (infoPtr->himlNormal)
1740 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1741 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1745 /* we need the ident in report mode, if we don't have it, we fail */
1746 State.left = Box.left;
1747 if (uView == LVS_REPORT)
1749 State.left += REPORT_MARGINX;
1750 if (lpLVItem->iSubItem == 0)
1752 assert(lpLVItem->mask & LVIF_INDENT);
1753 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1756 State.top = Box.top;
1758 State.right = State.left;
1759 State.bottom = State.top;
1760 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1762 State.right += infoPtr->iconStateSize.cx;
1763 State.bottom += infoPtr->iconStateSize.cy;
1765 if (lprcState) *lprcState = State;
1766 TRACE(" - state=%s\n", debugrect(&State));
1769 /************************************************************/
1770 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1771 /************************************************************/
1774 if (uView == LVS_ICON)
1776 Icon.left = Box.left;
1777 if (infoPtr->himlNormal)
1778 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1779 Icon.top = Box.top + ICON_TOP_PADDING;
1780 Icon.right = Icon.left;
1781 Icon.bottom = Icon.top;
1782 if (infoPtr->himlNormal)
1784 Icon.right += infoPtr->iconSize.cx;
1785 Icon.bottom += infoPtr->iconSize.cy;
1788 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1790 Icon.left = State.right;
1791 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1793 Icon.right = Icon.left;
1794 if (infoPtr->himlSmall && (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE)))
1795 Icon.right += infoPtr->iconSize.cx;
1796 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1798 if(lprcIcon) *lprcIcon = Icon;
1799 TRACE(" - icon=%s\n", debugrect(&Icon));
1802 /************************************************************/
1803 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1804 /************************************************************/
1807 SIZE labelSize = { 0, 0 };
1809 /* calculate how far to the right can the label strech */
1810 Label.right = Box.right;
1811 if (uView == LVS_REPORT)
1813 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1814 Label.right -= REPORT_MARGINX;
1817 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1819 labelSize.cx = infoPtr->nItemWidth;
1820 labelSize.cy = infoPtr->nItemHeight;
1824 /* we need the text in non owner draw mode */
1825 assert(lpLVItem->mask & LVIF_TEXT);
1826 if (is_textT(lpLVItem->pszText, TRUE))
1828 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1829 HDC hdc = GetDC(infoPtr->hwndSelf);
1830 HFONT hOldFont = SelectObject(hdc, hFont);
1834 /* compute rough rectangle where the label will go */
1835 SetRectEmpty(&rcText);
1836 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1837 rcText.bottom = infoPtr->nItemHeight;
1838 if (uView == LVS_ICON)
1839 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1841 /* now figure out the flags */
1842 if (uView == LVS_ICON)
1843 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1845 uFormat = LV_SL_DT_FLAGS;
1847 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1849 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1850 labelSize.cy = rcText.bottom - rcText.top;
1852 SelectObject(hdc, hOldFont);
1853 ReleaseDC(infoPtr->hwndSelf, hdc);
1857 if (uView == LVS_ICON)
1859 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1860 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1861 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1862 Label.right = Label.left + labelSize.cx;
1863 Label.bottom = Label.top + infoPtr->nItemHeight;
1864 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1866 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1867 labelSize.cy /= infoPtr->ntmHeight;
1868 labelSize.cy = max(labelSize.cy, 1);
1869 labelSize.cy *= infoPtr->ntmHeight;
1871 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1873 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1875 Label.left = Icon.right;
1876 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1877 Label.top = Box.top;
1878 Label.right = min(Label.left + labelSize.cx, Label.right);
1879 Label.bottom = Label.top + infoPtr->nItemHeight;
1882 if (lprcLabel) *lprcLabel = Label;
1883 TRACE(" - label=%s\n", debugrect(&Label));
1886 /* Fix the Box if necessary */
1889 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1890 else *lprcBox = Box;
1892 TRACE(" - box=%s\n", debugrect(&Box));
1896 * DESCRIPTION: [INTERNAL]
1899 * [I] infoPtr : valid pointer to the listview structure
1900 * [I] nItem : item number
1901 * [O] lprcBox : ptr to Box rectangle
1906 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1908 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1909 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1910 POINT Position, Origin;
1913 LISTVIEW_GetOrigin(infoPtr, &Origin);
1914 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1916 /* Be smart and try to figure out the minimum we have to do */
1918 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1919 lvItem.mask |= LVIF_TEXT;
1920 lvItem.iItem = nItem;
1921 lvItem.iSubItem = 0;
1922 lvItem.pszText = szDispText;
1923 lvItem.cchTextMax = DISP_TEXT_SIZE;
1924 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
1925 if (uView == LVS_ICON)
1927 lvItem.mask |= LVIF_STATE;
1928 lvItem.stateMask = LVIS_FOCUSED;
1929 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1931 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
1933 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1939 * Returns the current icon position, and advances it along the top.
1940 * The returned position is not offset by Origin.
1943 * [I] infoPtr : valid pointer to the listview structure
1944 * [O] lpPos : will get the current icon position
1949 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
1951 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1953 *lpPos = infoPtr->currIconPos;
1955 infoPtr->currIconPos.x += infoPtr->nItemWidth;
1956 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
1958 infoPtr->currIconPos.x = 0;
1959 infoPtr->currIconPos.y += infoPtr->nItemHeight;
1965 * Returns the current icon position, and advances it down the left edge.
1966 * The returned position is not offset by Origin.
1969 * [I] infoPtr : valid pointer to the listview structure
1970 * [O] lpPos : will get the current icon position
1975 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
1977 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1979 *lpPos = infoPtr->currIconPos;
1981 infoPtr->currIconPos.y += infoPtr->nItemHeight;
1982 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
1984 infoPtr->currIconPos.x += infoPtr->nItemWidth;
1985 infoPtr->currIconPos.y = 0;
1991 * Moves an icon to the specified position.
1992 * It takes care of invalidating the item, etc.
1995 * [I] infoPtr : valid pointer to the listview structure
1996 * [I] nItem : the item to move
1997 * [I] lpPos : the new icon position
1998 * [I] isNew : flags the item as being new
2004 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lppt, BOOL isNew)
2010 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2011 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2013 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2014 LISTVIEW_InvalidateItem(infoPtr, nItem);
2017 /* Allocating a POINTER for every item is too resource intensive,
2018 * so we'll keep the (x,y) in different arrays */
2019 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2020 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2022 LISTVIEW_InvalidateItem(infoPtr, nItem);
2029 * Arranges listview items in icon display mode.
2032 * [I] infoPtr : valid pointer to the listview structure
2033 * [I] nAlignCode : alignment code
2039 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2041 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2042 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2046 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2048 if (nAlignCode == LVA_DEFAULT)
2050 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2051 else nAlignCode = LVA_ALIGNTOP;
2056 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2057 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2058 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2059 default: return FALSE;
2062 infoPtr->bAutoarrange = TRUE;
2063 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2064 for (i = 0; i < infoPtr->nItemCount; i++)
2066 next_pos(infoPtr, &pos);
2067 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2075 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2078 * [I] infoPtr : valid pointer to the listview structure
2079 * [O] lprcView : bounding rectangle
2085 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2089 SetRectEmpty(lprcView);
2091 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2095 for (i = 0; i < infoPtr->nItemCount; i++)
2097 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2098 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2099 lprcView->right = max(lprcView->right, x);
2100 lprcView->bottom = max(lprcView->bottom, y);
2102 if (infoPtr->nItemCount > 0)
2104 lprcView->right += infoPtr->nItemWidth;
2105 lprcView->bottom += infoPtr->nItemHeight;
2110 y = LISTVIEW_GetCountPerColumn(infoPtr);
2111 x = infoPtr->nItemCount / y;
2112 if (infoPtr->nItemCount % y) x++;
2113 lprcView->right = x * infoPtr->nItemWidth;
2114 lprcView->bottom = y * infoPtr->nItemHeight;
2118 lprcView->right = infoPtr->nItemWidth;
2119 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2126 * Retrieves the bounding rectangle of all the items.
2129 * [I] infoPtr : valid pointer to the listview structure
2130 * [O] lprcView : bounding rectangle
2136 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2140 TRACE("(lprcView=%p)\n", lprcView);
2142 if (!lprcView) return FALSE;
2144 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2145 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2146 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2148 TRACE("lprcView=%s\n", debugrect(lprcView));
2155 * Retrieves the subitem pointer associated with the subitem index.
2158 * [I] hdpaSubItems : DPA handle for a specific item
2159 * [I] nSubItem : index of subitem
2162 * SUCCESS : subitem pointer
2165 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2167 SUBITEM_INFO *lpSubItem;
2170 /* we should binary search here if need be */
2171 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2173 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2174 if (lpSubItem->iSubItem == nSubItem)
2184 * Caclulates the desired item width.
2187 * [I] infoPtr : valid pointer to the listview structure
2190 * The desired item width.
2192 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2194 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2197 if (uView == LVS_ICON)
2198 nItemWidth = infoPtr->iconSpacing.cx;
2199 else if (uView == LVS_REPORT)
2203 if (infoPtr->hdpaColumns->nItemCount > 0)
2205 LISTVIEW_GetHeaderRect(infoPtr, infoPtr->hdpaColumns->nItemCount - 1, &rcHeader);
2206 nItemWidth = rcHeader.right;
2208 else nItemWidth = 0;
2210 else /* LVS_SMALLICON, or LVS_LIST */
2214 for (nItemWidth = i = 0; i < infoPtr->nItemCount; i++)
2215 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2217 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
2218 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx + IMAGE_PADDING;
2220 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2223 return max(nItemWidth, 1);
2228 * Caclulates the desired item height.
2231 * [I] infoPtr : valid pointer to the listview structure
2234 * The desired item height.
2236 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2238 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2241 if (uView == LVS_ICON)
2242 nItemHeight = infoPtr->iconSpacing.cy;
2245 nItemHeight = infoPtr->ntmHeight;
2246 if (infoPtr->himlState)
2247 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2248 if (infoPtr->himlSmall)
2249 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2250 if (infoPtr->himlState || infoPtr->himlSmall)
2251 nItemHeight += HEIGHT_PADDING;
2254 return max(nItemHeight, 1);
2259 * Updates the width, and height of an item.
2262 * [I] infoPtr : valid pointer to the listview structure
2267 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2269 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2270 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2276 * Retrieves and saves important text metrics info for the current
2280 * [I] infoPtr : valid pointer to the listview structure
2283 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2285 HDC hdc = GetDC(infoPtr->hwndSelf);
2286 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2287 HFONT hOldFont = SelectObject(hdc, hFont);
2290 if (GetTextMetricsW(hdc, &tm))
2291 infoPtr->ntmHeight = tm.tmHeight;
2292 SelectObject(hdc, hOldFont);
2293 ReleaseDC(infoPtr->hwndSelf, hdc);
2295 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2300 * A compare function for ranges
2303 * [I] range1 : pointer to range 1;
2304 * [I] range2 : pointer to range 2;
2308 * > 0 : if range 1 > range 2
2309 * < 0 : if range 2 > range 1
2310 * = 0 : if range intersects range 2
2312 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2316 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2318 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2323 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2329 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2331 #define ranges_check(ranges, desc) do { } while(0)
2334 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2339 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2341 assert (ranges->hdpa->nItemCount >= 0);
2342 ranges_dump(ranges);
2343 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2344 if (ranges->hdpa->nItemCount > 0)
2345 assert (prev->lower >= 0 && prev->lower < prev->upper);
2346 for (i = 1; i < ranges->hdpa->nItemCount; i++)
2348 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2349 assert (prev->upper <= curr->lower);
2350 assert (curr->lower < curr->upper);
2353 TRACE("--- Done checking---\n");
2356 static RANGES ranges_create(int count)
2358 RANGES ranges = (RANGES)COMCTL32_Alloc(sizeof(struct tagRANGES));
2359 if (!ranges) return NULL;
2360 ranges->hdpa = DPA_Create(count);
2361 if (ranges->hdpa) return ranges;
2362 COMCTL32_Free(ranges);
2366 static void ranges_clear(RANGES ranges)
2370 for(i = 0; i < ranges->hdpa->nItemCount; i++)
2371 COMCTL32_Free(DPA_GetPtr(ranges->hdpa, i));
2372 DPA_DeleteAllPtrs(ranges->hdpa);
2376 static void ranges_destroy(RANGES ranges)
2378 ranges_clear(ranges);
2379 DPA_Destroy(ranges->hdpa);
2380 COMCTL32_Free(ranges);
2383 static RANGES ranges_clone(RANGES ranges)
2388 if (!(clone = ranges_create(ranges->hdpa->nItemCount))) goto fail;
2390 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2392 RANGE *newrng = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2393 if (!newrng) goto fail;
2394 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2395 DPA_SetPtr(clone->hdpa, i, newrng);
2400 TRACE ("clone failed\n");
2401 if (clone) ranges_destroy(clone);
2405 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2409 for (i = 0; i < sub->hdpa->nItemCount; i++)
2410 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2415 static void ranges_dump(RANGES ranges)
2419 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2420 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2423 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2425 RANGE srchrng = { nItem, nItem + 1 };
2427 TRACE("(nItem=%d)\n", nItem);
2428 ranges_check(ranges, "before contain");
2429 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2432 static INT ranges_itemcount(RANGES ranges)
2436 for (i = 0; i < ranges->hdpa->nItemCount; i++)
2438 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2439 count += sel->upper - sel->lower;
2445 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2447 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2450 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2451 if (index == -1) return TRUE;
2453 for (; index < ranges->hdpa->nItemCount; index++)
2455 chkrng = DPA_GetPtr(ranges->hdpa, index);
2456 if (chkrng->lower >= nItem)
2457 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2458 if (chkrng->upper > nItem)
2459 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2464 static BOOL ranges_add(RANGES ranges, RANGE range)
2469 TRACE("(%s)\n", debugrange(&range));
2470 ranges_check(ranges, "before add");
2472 /* try find overlapping regions first */
2473 srchrgn.lower = range.lower - 1;
2474 srchrgn.upper = range.upper + 1;
2475 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2481 TRACE("Adding new range\n");
2483 /* create the brand new range to insert */
2484 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2485 if(!newrgn) goto fail;
2488 /* figure out where to insert it */
2489 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2490 TRACE("index=%d\n", index);
2491 if (index == -1) index = 0;
2493 /* and get it over with */
2494 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2496 COMCTL32_Free(newrgn);
2502 RANGE *chkrgn, *mrgrgn;
2503 INT fromindex, mergeindex;
2505 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2506 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2508 chkrgn->lower = min(range.lower, chkrgn->lower);
2509 chkrgn->upper = max(range.upper, chkrgn->upper);
2511 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2513 /* merge now common anges */
2515 srchrgn.lower = chkrgn->lower - 1;
2516 srchrgn.upper = chkrgn->upper + 1;
2520 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2521 if (mergeindex == -1) break;
2522 if (mergeindex == index)
2524 fromindex = index + 1;
2528 TRACE("Merge with index %i\n", mergeindex);
2530 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2531 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2532 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2533 COMCTL32_Free(mrgrgn);
2534 DPA_DeletePtr(ranges->hdpa, mergeindex);
2535 if (mergeindex < index) index --;
2539 ranges_check(ranges, "after add");
2543 ranges_check(ranges, "failed add");
2547 static BOOL ranges_del(RANGES ranges, RANGE range)
2552 TRACE("(%s)\n", debugrange(&range));
2553 ranges_check(ranges, "before del");
2555 /* we don't use DPAS_SORTED here, since we need *
2556 * to find the first overlapping range */
2557 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2560 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2562 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2564 /* case 1: Same range */
2565 if ( (chkrgn->upper == range.upper) &&
2566 (chkrgn->lower == range.lower) )
2568 DPA_DeletePtr(ranges->hdpa, index);
2571 /* case 2: engulf */
2572 else if ( (chkrgn->upper <= range.upper) &&
2573 (chkrgn->lower >= range.lower) )
2575 DPA_DeletePtr(ranges->hdpa, index);
2577 /* case 3: overlap upper */
2578 else if ( (chkrgn->upper <= range.upper) &&
2579 (chkrgn->lower < range.lower) )
2581 chkrgn->upper = range.lower;
2583 /* case 4: overlap lower */
2584 else if ( (chkrgn->upper > range.upper) &&
2585 (chkrgn->lower >= range.lower) )
2587 chkrgn->lower = range.upper;
2590 /* case 5: fully internal */
2593 RANGE tmprgn = *chkrgn, *newrgn;
2595 if (!(newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)))) goto fail;
2596 newrgn->lower = chkrgn->lower;
2597 newrgn->upper = range.lower;
2598 chkrgn->lower = range.upper;
2599 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2601 COMCTL32_Free(newrgn);
2608 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2611 ranges_check(ranges, "after del");
2615 ranges_check(ranges, "failed del");
2621 * Removes all selection ranges
2624 * [I] infoPtr : valid pointer to the listview structure
2625 * [I] toSkip : item range to skip removing the selection
2631 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2640 lvItem.stateMask = LVIS_SELECTED;
2642 /* need to clone the DPA because callbacks can change it */
2643 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2644 iterator_ranges(&i, ranges_diff(clone, toSkip));
2645 while(iterator_next(&i))
2646 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2647 /* note that the iterator destructor will free the cloned range */
2648 iterator_destroy(&i);
2653 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2657 if (!(toSkip = ranges_create(1))) return FALSE;
2658 if (nItem != -1) ranges_additem(toSkip, nItem);
2659 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2660 ranges_destroy(toSkip);
2664 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2666 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2671 * Retrieves the number of items that are marked as selected.
2674 * [I] infoPtr : valid pointer to the listview structure
2677 * Number of items selected.
2679 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2681 INT nSelectedCount = 0;
2683 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2686 for (i = 0; i < infoPtr->nItemCount; i++)
2688 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2693 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2695 TRACE("nSelectedCount=%d\n", nSelectedCount);
2696 return nSelectedCount;
2701 * Manages the item focus.
2704 * [I] infoPtr : valid pointer to the listview structure
2705 * [I] nItem : item index
2708 * TRUE : focused item changed
2709 * FALSE : focused item has NOT changed
2711 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2713 INT oldFocus = infoPtr->nFocusedItem;
2716 if (nItem == infoPtr->nFocusedItem) return FALSE;
2718 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2719 lvItem.stateMask = LVIS_FOCUSED;
2720 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2722 return oldFocus != infoPtr->nFocusedItem;
2725 /* Helper function for LISTVIEW_ShiftIndices *only* */
2726 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2728 if (nShiftItem < nItem) return nShiftItem;
2730 if (nShiftItem > nItem) return nShiftItem + direction;
2732 if (direction > 0) return nShiftItem + direction;
2734 return min(nShiftItem, infoPtr->nItemCount - 1);
2739 * Updates the various indices after an item has been inserted or deleted.
2742 * [I] infoPtr : valid pointer to the listview structure
2743 * [I] nItem : item index
2744 * [I] direction : Direction of shift, +1 or -1.
2749 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2753 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2755 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2757 assert(abs(direction) == 1);
2759 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2761 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2762 if (nNewFocus != infoPtr->nFocusedItem)
2763 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2765 /* But we are not supposed to modify nHotItem! */
2771 * Adds a block of selections.
2774 * [I] infoPtr : valid pointer to the listview structure
2775 * [I] nItem : item index
2780 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2782 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2783 INT nLast = max(infoPtr->nSelectionMark, nItem);
2787 if (nFirst == -1) nFirst = nItem;
2789 item.state = LVIS_SELECTED;
2790 item.stateMask = LVIS_SELECTED;
2792 /* FIXME: this is not correct LVS_OWNERDATA
2793 * setting the item states individually will generate
2794 * a LVN_ITEMCHANGED notification for each one. Instead,
2795 * we have to send a LVN_ODSTATECHANGED notification.
2796 * See MSDN documentation for LVN_ITEMCHANGED.
2798 for (i = nFirst; i <= nLast; i++)
2799 LISTVIEW_SetItemState(infoPtr,i,&item);
2805 * Sets a single group selection.
2808 * [I] infoPtr : valid pointer to the listview structure
2809 * [I] nItem : item index
2814 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2816 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2821 if (!(selection = ranges_create(100))) return;
2823 item.state = LVIS_SELECTED;
2824 item.stateMask = LVIS_SELECTED;
2826 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2828 if (infoPtr->nSelectionMark == -1)
2830 infoPtr->nSelectionMark = nItem;
2831 ranges_additem(selection, nItem);
2837 sel.lower = min(infoPtr->nSelectionMark, nItem);
2838 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
2839 ranges_add(selection, sel);
2844 RECT rcItem, rcSel, rcSelMark;
2847 rcItem.left = LVIR_BOUNDS;
2848 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2849 rcSelMark.left = LVIR_BOUNDS;
2850 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2851 UnionRect(&rcSel, &rcItem, &rcSelMark);
2852 iterator_frameditems(&i, infoPtr, &rcSel);
2853 while(iterator_next(&i))
2855 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2856 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
2858 iterator_destroy(&i);
2861 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
2862 iterator_ranges(&i, selection);
2863 while(iterator_next(&i))
2864 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2865 /* this will also destroy the selection */
2866 iterator_destroy(&i);
2868 LISTVIEW_SetItemFocus(infoPtr, nItem);
2873 * Sets a single selection.
2876 * [I] infoPtr : valid pointer to the listview structure
2877 * [I] nItem : item index
2882 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2886 TRACE("nItem=%d\n", nItem);
2888 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
2890 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2891 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2892 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2894 infoPtr->nSelectionMark = nItem;
2899 * Set selection(s) with keyboard.
2902 * [I] infoPtr : valid pointer to the listview structure
2903 * [I] nItem : item index
2906 * SUCCESS : TRUE (needs to be repainted)
2907 * FAILURE : FALSE (nothing has changed)
2909 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2911 /* FIXME: pass in the state */
2912 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2913 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2914 BOOL bResult = FALSE;
2916 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2918 if (infoPtr->dwStyle & LVS_SINGLESEL)
2921 LISTVIEW_SetSelection(infoPtr, nItem);
2928 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2932 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2937 LISTVIEW_SetSelection(infoPtr, nItem);
2940 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2943 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2950 * Called when the mouse is being actively tracked and has hovered for a specified
2954 * [I] infoPtr : valid pointer to the listview structure
2955 * [I] fwKeys : key indicator
2956 * [I] pts : mouse position
2959 * 0 if the message was processed, non-zero if there was an error
2962 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2963 * over the item for a certain period of time.
2966 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2968 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2969 /* FIXME: select the item!!! */
2970 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2977 * Called whenever WM_MOUSEMOVE is received.
2980 * [I] infoPtr : valid pointer to the listview structure
2981 * [I] fwKeys : key indicator
2982 * [I] pts : mouse position
2985 * 0 if the message is processed, non-zero if there was an error
2987 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2989 TRACKMOUSEEVENT trackinfo;
2991 /* see if we are supposed to be tracking mouse hovering */
2992 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2993 /* fill in the trackinfo struct */
2994 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2995 trackinfo.dwFlags = TME_QUERY;
2996 trackinfo.hwndTrack = infoPtr->hwndSelf;
2997 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2999 /* see if we are already tracking this hwnd */
3000 _TrackMouseEvent(&trackinfo);
3002 if(!(trackinfo.dwFlags & TME_HOVER)) {
3003 trackinfo.dwFlags = TME_HOVER;
3005 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3006 _TrackMouseEvent(&trackinfo);
3015 * Tests wheather the item is assignable to a list with style lStyle
3017 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
3019 if ( (lpLVItem->mask & LVIF_TEXT) &&
3020 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3021 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3029 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3032 * [I] infoPtr : valid pointer to the listview structure
3033 * [I] lpLVItem : valid pointer to new item atttributes
3034 * [I] isNew : the item being set is being inserted
3035 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3036 * [O] bChanged : will be set to TRUE if the item really changed
3042 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3051 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3053 if (lpLVItem->mask == 0) return TRUE;
3055 if (infoPtr->dwStyle & LVS_OWNERDATA)
3057 /* a virtual listview we stores only selection and focus */
3058 if ((lpLVItem->mask & ~LVIF_STATE) || (lpLVItem->stateMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3064 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3065 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3069 /* we need to get the lParam and state of the item */
3070 item.iItem = lpLVItem->iItem;
3071 item.iSubItem = lpLVItem->iSubItem;
3072 item.mask = LVIF_STATE | LVIF_PARAM;
3073 item.stateMask = ~0;
3076 if (!isNew && !LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
3078 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3079 /* determine what fields will change */
3080 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3081 uChanged |= LVIF_STATE;
3083 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3084 uChanged |= LVIF_IMAGE;
3086 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3087 uChanged |= LVIF_PARAM;
3089 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3090 uChanged |= LVIF_INDENT;
3092 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3093 uChanged |= LVIF_TEXT;
3095 TRACE("uChanged=0x%x\n", uChanged);
3096 if (!uChanged) return TRUE;
3099 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3100 nmlv.iItem = lpLVItem->iItem;
3101 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3102 nmlv.uOldState = item.state;
3103 nmlv.uChanged = uChanged;
3104 nmlv.lParam = item.lParam;
3106 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3107 /* and we are _NOT_ virtual (LVS_OWERNDATA) */
3108 if(lpItem && !isNew && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3111 /* copy information */
3112 if (lpLVItem->mask & LVIF_TEXT)
3113 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3115 if (lpLVItem->mask & LVIF_IMAGE)
3116 lpItem->hdr.iImage = lpLVItem->iImage;
3118 if (lpLVItem->mask & LVIF_PARAM)
3119 lpItem->lParam = lpLVItem->lParam;
3121 if (lpLVItem->mask & LVIF_INDENT)
3122 lpItem->iIndent = lpLVItem->iIndent;
3124 if (uChanged & LVIF_STATE)
3126 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED))
3128 lpItem->state &= ~lpLVItem->stateMask;
3129 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3131 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3133 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3134 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3136 else if (lpLVItem->stateMask & LVIS_SELECTED)
3137 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3139 /* if we are asked to change focus, and we manage it, do it */
3140 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3142 if (lpLVItem->state & LVIS_FOCUSED)
3144 LISTVIEW_SetItemFocus(infoPtr, -1);
3145 infoPtr->nFocusedItem = lpLVItem->iItem;
3146 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
3148 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3149 infoPtr->nFocusedItem = -1;
3153 /* if we're inserting the item, we're done */
3154 if (isNew) return TRUE;
3156 /* send LVN_ITEMCHANGED notification */
3157 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3158 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3165 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3168 * [I] infoPtr : valid pointer to the listview structure
3169 * [I] lpLVItem : valid pointer to new subitem atttributes
3170 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3171 * [O] bChanged : will be set to TRUE if the item really changed
3177 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
3180 SUBITEM_INFO *lpSubItem;
3182 /* we do not support subitems for virtual listviews */
3183 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3185 /* set subitem only if column is present */
3186 if (lpLVItem->iSubItem >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3188 /* First do some sanity checks */
3189 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3190 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3192 /* get the subitem structure, and create it if not there */
3193 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3194 assert (hdpaSubItems);
3196 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3199 SUBITEM_INFO *tmpSubItem;
3202 lpSubItem = (SUBITEM_INFO *)COMCTL32_Alloc(sizeof(SUBITEM_INFO));
3203 if (!lpSubItem) return FALSE;
3204 /* we could binary search here, if need be...*/
3205 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3207 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3208 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3210 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3212 COMCTL32_Free(lpSubItem);
3215 lpSubItem->iSubItem = lpLVItem->iSubItem;
3219 if (lpLVItem->mask & LVIF_IMAGE)
3220 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3222 lpSubItem->hdr.iImage = lpLVItem->iImage;
3226 if (lpLVItem->mask & LVIF_TEXT)
3227 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3229 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3238 * Sets item attributes.
3241 * [I] infoPtr : valid pointer to the listview structure
3242 * [I] lpLVItem : new item atttributes
3243 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3249 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3251 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3252 LPWSTR pszText = NULL;
3253 BOOL bResult, bChanged = FALSE;
3255 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3257 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3260 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3261 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3263 pszText = lpLVItem->pszText;
3264 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3267 /* actually set the fields */
3268 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3270 if (lpLVItem->iSubItem)
3271 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3273 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3275 /* redraw item, if necessary */
3276 if (bChanged && !infoPtr->bIsDrawing)
3278 /* this little optimization eliminates some nasty flicker */
3279 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3280 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3281 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3283 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3288 textfreeT(lpLVItem->pszText, isW);
3289 lpLVItem->pszText = pszText;
3297 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3300 * [I] infoPtr : valid pointer to the listview structure
3305 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3307 LONG lStyle = infoPtr->dwStyle;
3308 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3310 SCROLLINFO scrollInfo;
3312 scrollInfo.cbSize = sizeof(SCROLLINFO);
3313 scrollInfo.fMask = SIF_POS;
3315 if (uView == LVS_LIST)
3317 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3318 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3320 else if (uView == LVS_REPORT)
3322 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3323 nItem = scrollInfo.nPos;
3327 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3328 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3331 TRACE("nItem=%d\n", nItem);
3339 * Erases the background of the given rectangle
3342 * [I] infoPtr : valid pointer to the listview structure
3343 * [I] hdc : device context handle
3344 * [I] lprcBox : clipping rectangle
3350 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3352 if (!infoPtr->hBkBrush) return FALSE;
3354 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3356 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3364 * [I] infoPtr : valid pointer to the listview structure
3365 * [I] hdc : device context handle
3366 * [I] nItem : item index
3367 * [I] nSubItem : subitem index
3368 * [I] pos : item position in client coordinates
3369 * [I] cdmode : custom draw mode
3375 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3377 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3378 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3379 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3380 DWORD cditemmode = CDRF_DODEFAULT;
3381 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3382 NMLVCUSTOMDRAW nmlvcd;
3386 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3388 /* get information needed for drawing the item */
3389 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3390 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3391 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3392 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3393 lvItem.iItem = nItem;
3394 lvItem.iSubItem = nSubItem;
3397 lvItem.cchTextMax = DISP_TEXT_SIZE;
3398 lvItem.pszText = szDispText;
3399 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3400 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3401 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3402 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3403 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3405 /* now check if we need to update the focus rectangle */
3406 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3408 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3409 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3410 OffsetRect(&rcBox, pos.x, pos.y);
3411 OffsetRect(&rcState, pos.x, pos.y);
3412 OffsetRect(&rcIcon, pos.x, pos.y);
3413 OffsetRect(&rcLabel, pos.x, pos.y);
3414 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3415 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3417 /* fill in the custom draw structure */
3418 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox);
3419 nmlvcd.nmcd.dwItemSpec = lvItem.iItem;
3420 nmlvcd.iSubItem = lvItem.iSubItem;
3421 if (lvItem.state & LVIS_SELECTED) nmlvcd.nmcd.uItemState |= CDIS_SELECTED;
3422 if (lvItem.state & LVIS_FOCUSED) nmlvcd.nmcd.uItemState |= CDIS_FOCUS;
3423 if (lvItem.iItem == infoPtr->nHotItem) nmlvcd.nmcd.uItemState |= CDIS_HOT;
3424 nmlvcd.nmcd.lItemlParam = lvItem.lParam;
3426 if (cdmode & CDRF_NOTIFYITEMDRAW)
3427 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3428 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3430 /* apprently, for selected items, we have to override the returned values */
3431 if (lvItem.state & LVIS_SELECTED)
3433 if (infoPtr->bFocus)
3435 nmlvcd.clrTextBk = comctl32_color.clrHighlight;
3436 nmlvcd.clrText = comctl32_color.clrHighlightText;
3438 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
3440 nmlvcd.clrTextBk = comctl32_color.clr3dFace;
3441 nmlvcd.clrText = comctl32_color.clrBtnText;
3446 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3448 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3451 TRACE("uStateImage=%d\n", uStateImage);
3452 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3457 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3458 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3460 TRACE("iImage=%d\n", lvItem.iImage);
3461 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3462 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3465 /* Don't bother painting item being edited */
3466 if (infoPtr->hwndEdit && lprcFocus && nSubItem == 0) goto postpaint;
3468 /* Set the text attributes */
3469 if (nmlvcd.clrTextBk != CLR_NONE)
3471 SetBkMode(hdc, OPAQUE);
3472 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3475 SetBkMode(hdc, TRANSPARENT);
3476 SetTextColor(hdc, nmlvcd.clrText);
3478 /* draw the selection background, if we're drawing the main item */
3482 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3483 rcSelect.right = rcBox.right;
3485 if (nmlvcd.clrTextBk != CLR_NONE)
3486 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3487 if(lprcFocus) *lprcFocus = rcSelect;
3490 /* figure out the text drawing flags */
3491 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3492 if (uView == LVS_ICON)
3493 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3496 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3498 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3499 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3500 default: uFormat |= DT_LEFT;
3503 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3504 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3507 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3508 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3514 * Draws listview items when in owner draw mode.
3517 * [I] infoPtr : valid pointer to the listview structure
3518 * [I] hdc : device context handle
3523 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3525 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3526 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3527 POINT Origin, Position;
3534 ZeroMemory(&dis, sizeof(dis));
3536 /* Get scroll info once before loop */
3537 LISTVIEW_GetOrigin(infoPtr, &Origin);
3539 /* figure out what we need to draw */
3540 iterator_visibleitems(&i, infoPtr, hdc);
3542 /* send cache hint notification */
3543 if (infoPtr->dwStyle & LVS_OWNERDATA)
3545 RANGE range = iterator_range(&i);
3548 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3549 nmlv.iFrom = range.lower;
3550 nmlv.iTo = range.upper - 1;
3551 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3554 /* iterate through the invalidated rows */
3555 while(iterator_next(&i))
3557 item.iItem = i.nItem;
3559 item.mask = LVIF_PARAM | LVIF_STATE;
3560 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3561 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3563 dis.CtlType = ODT_LISTVIEW;
3565 dis.itemID = item.iItem;
3566 dis.itemAction = ODA_DRAWENTIRE;
3568 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3569 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3570 dis.hwndItem = infoPtr->hwndSelf;
3572 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3573 dis.rcItem.left = Position.x + Origin.x;
3574 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3575 dis.rcItem.top = Position.y + Origin.y;
3576 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3577 dis.itemData = item.lParam;
3579 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3580 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3582 iterator_destroy(&i);
3587 * Draws listview items when in report display mode.
3590 * [I] infoPtr : valid pointer to the listview structure
3591 * [I] hdc : device context handle
3592 * [I] cdmode : custom draw mode
3597 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3599 INT rgntype, nFirstCol, nLastCol, nCol;
3600 RECT rcClip, rcItem;
3601 POINT Origin, Position;
3606 /* figure out what to draw */
3607 rgntype = GetClipBox(hdc, &rcClip);
3608 if (rgntype == NULLREGION) return;
3610 /* Get scroll info once before loop */
3611 LISTVIEW_GetOrigin(infoPtr, &Origin);
3613 /* narrow down the columns we need to paint */
3614 for(nFirstCol = 0; nFirstCol < infoPtr->hdpaColumns->nItemCount; nFirstCol++)
3616 LISTVIEW_GetHeaderRect(infoPtr, nFirstCol, &rcItem);
3617 if (rcItem.right + Origin.x >= rcClip.left) break;
3619 for(nLastCol = infoPtr->hdpaColumns->nItemCount - 1; nLastCol >= 0; nLastCol--)
3621 LISTVIEW_GetHeaderRect(infoPtr, nLastCol, &rcItem);
3622 if (rcItem.left + Origin.x < rcClip.right) break;
3625 /* figure out what we need to draw */
3626 iterator_visibleitems(&i, infoPtr, hdc);
3628 /* a last few bits before we start drawing */
3629 TRACE("Colums=(%d - %d)\n", nFirstCol, nLastCol);
3631 /* iterate through the invalidated rows */
3632 while(iterator_next(&i))
3634 /* iterate through the invalidated columns */
3635 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
3637 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3638 Position.x += Origin.x;
3639 Position.y += Origin.y;
3641 if (rgntype == COMPLEXREGION)
3643 LISTVIEW_GetHeaderRect(infoPtr, nCol, &rcItem);
3645 rcItem.bottom = infoPtr->nItemHeight;
3646 OffsetRect(&rcItem, Position.x, Position.y);
3647 if (!RectVisible(hdc, &rcItem)) continue;
3650 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, nCol, Position, cdmode);
3653 iterator_destroy(&i);
3658 * Draws listview items when in list display mode.
3661 * [I] infoPtr : valid pointer to the listview structure
3662 * [I] hdc : device context handle
3663 * [I] cdmode : custom draw mode
3668 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3670 POINT Origin, Position;
3673 /* Get scroll info once before loop */
3674 LISTVIEW_GetOrigin(infoPtr, &Origin);
3676 /* figure out what we need to draw */
3677 iterator_visibleitems(&i, infoPtr, hdc);
3679 while(iterator_prev(&i))
3681 LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position);
3682 Position.x += Origin.x;
3683 Position.y += Origin.y;
3685 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3687 iterator_destroy(&i);
3693 * Draws listview items.
3696 * [I] infoPtr : valid pointer to the listview structure
3697 * [I] hdc : device context handle
3702 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3704 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3705 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3706 NMLVCUSTOMDRAW nmlvcd;
3712 LISTVIEW_DUMP(infoPtr);
3714 infoPtr->bIsDrawing = TRUE;
3716 /* save dc values we're gonna trash while drawing */
3717 hOldFont = SelectObject(hdc, infoPtr->hFont);
3718 oldBkMode = GetBkMode(hdc);
3719 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3720 oldTextColor = GetTextColor(hdc);
3722 oldClrTextBk = infoPtr->clrTextBk;
3723 oldClrText = infoPtr->clrText;
3725 GetClientRect(infoPtr->hwndSelf, &rcClient);
3726 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3727 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3728 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3730 /* Use these colors to draw the items */
3731 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3732 infoPtr->clrText = nmlvcd.clrText;
3734 /* nothing to draw */
3735 if(infoPtr->nItemCount == 0) goto enddraw;
3737 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3738 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3741 if (uView == LVS_REPORT)
3742 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3743 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3744 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3746 /* if we have a focus rect, draw it */
3747 if (infoPtr->bFocus)
3748 DrawFocusRect(hdc, &infoPtr->rcFocus);
3752 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3753 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3755 infoPtr->clrTextBk = oldClrTextBk;
3756 infoPtr->clrText = oldClrText;
3758 SelectObject(hdc, hOldFont);
3759 SetBkMode(hdc, oldBkMode);
3760 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3761 SetTextColor(hdc, oldTextColor);
3762 infoPtr->bIsDrawing = FALSE;
3768 * Calculates the approximate width and height of a given number of items.
3771 * [I] infoPtr : valid pointer to the listview structure
3772 * [I] nItemCount : number of items
3773 * [I] wWidth : width
3774 * [I] wHeight : height
3777 * Returns a DWORD. The width in the low word and the height in high word.
3779 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3780 WORD wWidth, WORD wHeight)
3782 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3783 INT nItemCountPerColumn = 1;
3784 INT nColumnCount = 0;
3785 DWORD dwViewRect = 0;
3787 if (nItemCount == -1)
3788 nItemCount = infoPtr->nItemCount;
3790 if (uView == LVS_LIST)
3792 if (wHeight == 0xFFFF)
3794 /* use current height */
3795 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3798 if (wHeight < infoPtr->nItemHeight)
3799 wHeight = infoPtr->nItemHeight;
3803 if (infoPtr->nItemHeight > 0)
3805 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3806 if (nItemCountPerColumn == 0)
3807 nItemCountPerColumn = 1;
3809 if (nItemCount % nItemCountPerColumn != 0)
3810 nColumnCount = nItemCount / nItemCountPerColumn;
3812 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3816 /* Microsoft padding magic */
3817 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3818 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3820 dwViewRect = MAKELONG(wWidth, wHeight);
3822 else if (uView == LVS_REPORT)
3823 FIXME("uView == LVS_REPORT: not implemented\n");
3824 else if (uView == LVS_SMALLICON)
3825 FIXME("uView == LVS_SMALLICON: not implemented\n");
3826 else if (uView == LVS_ICON)
3827 FIXME("uView == LVS_ICON: not implemented\n");
3832 /* << LISTVIEW_CreateDragImage >> */
3837 * Removes all listview items and subitems.
3840 * [I] infoPtr : valid pointer to the listview structure
3846 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3849 HDPA hdpaSubItems = NULL;
3856 /* we do it directly, to avoid notifications */
3857 ranges_clear(infoPtr->selectionRanges);
3858 infoPtr->nSelectionMark = -1;
3859 infoPtr->nFocusedItem = -1;
3860 SetRectEmpty(&infoPtr->rcFocus);
3861 /* But we are supposed to leave nHotItem as is! */
3864 /* send LVN_DELETEALLITEMS notification */
3865 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3867 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3869 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
3871 /* send LVN_DELETEITEM notification, if not supressed */
3875 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3877 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3879 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3880 for (j = 0; j < hdpaSubItems->nItemCount; j++)
3882 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
3883 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
3884 COMCTL32_Free(hdrItem);
3886 DPA_Destroy(hdpaSubItems);
3887 DPA_DeletePtr(infoPtr->hdpaItems, i);
3889 DPA_DeletePtr(infoPtr->hdpaPosX, i);
3890 DPA_DeletePtr(infoPtr->hdpaPosY, i);
3891 infoPtr->nItemCount --;
3894 LISTVIEW_UpdateScroll(infoPtr);
3896 LISTVIEW_InvalidateList(infoPtr);
3903 * Scrolls, and updates the columns, when a column is changing width.
3906 * [I] infoPtr : valid pointer to the listview structure
3907 * [I] nColumn : column to scroll
3908 * [I] dx : amount of scroll, in pixels
3913 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
3915 COLUMN_INFO *lpColumnInfo;
3919 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, infoPtr->hdpaColumns->nItemCount - 1));
3920 rcCol = lpColumnInfo->rcHeader;
3921 if (nColumn >= infoPtr->hdpaColumns->nItemCount)
3922 rcCol.left = rcCol.right;
3924 /* ajust the other columns */
3925 for (nCol = nColumn; nCol < infoPtr->hdpaColumns->nItemCount; nCol++)
3927 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
3928 lpColumnInfo->rcHeader.left += dx;
3929 lpColumnInfo->rcHeader.right += dx;
3932 /* do not update screen if not in report mode */
3933 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
3935 /* if we have a focus, must first erase the focus rect */
3936 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3938 /* Need to reset the item width when inserting a new column */
3939 infoPtr->nItemWidth += dx;
3941 LISTVIEW_UpdateScroll(infoPtr);
3943 /* scroll to cover the deleted column, and invalidate for redraw */
3944 rcOld = infoPtr->rcList;
3945 rcOld.left = rcCol.left;
3946 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3948 /* we can restore focus now */
3949 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3954 * Removes a column from the listview control.
3957 * [I] infoPtr : valid pointer to the listview structure
3958 * [I] nColumn : column index
3964 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3968 TRACE("nColumn=%d\n", nColumn);
3970 if (nColumn <= 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
3972 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
3974 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3977 COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
3978 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
3980 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3982 SUBITEM_INFO *lpSubItem, *lpDelItem;
3984 INT nItem, nSubItem, i;
3986 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3988 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3991 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3993 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3994 if (lpSubItem->iSubItem == nColumn)
3997 lpDelItem = lpSubItem;
3999 else if (lpSubItem->iSubItem > nColumn)
4001 lpSubItem->iSubItem--;
4005 /* if we found our subitem, zapp it */
4009 if (is_textW(lpDelItem->hdr.pszText))
4010 COMCTL32_Free(lpDelItem->hdr.pszText);
4013 COMCTL32_Free(lpDelItem);
4015 /* free dpa memory */
4016 DPA_DeletePtr(hdpaSubItems, nSubItem);
4021 /* update the other column info */
4022 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4029 * Invalidates the listview after an item's insertion or deletion.
4032 * [I] infoPtr : valid pointer to the listview structure
4033 * [I] nItem : item index
4034 * [I] dir : -1 if deleting, 1 if inserting
4039 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4041 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4042 INT nPerCol, nItemCol, nItemRow;
4046 /* if we don't refresh, what's the point of scrolling? */
4047 if (!is_redrawing(infoPtr)) return;
4049 assert (abs(dir) == 1);
4051 /* arrange icons if autoarrange is on */
4052 if (is_autoarrange(infoPtr))
4054 BOOL arrange = TRUE;
4055 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4056 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4057 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4060 /* scrollbars need updating */
4061 LISTVIEW_UpdateScroll(infoPtr);
4063 /* figure out the item's position */
4064 if (uView == LVS_REPORT)
4065 nPerCol = infoPtr->nItemCount + 1;
4066 else if (uView == LVS_LIST)
4067 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4068 else /* LVS_ICON, or LVS_SMALLICON */
4071 nItemCol = nItem / nPerCol;
4072 nItemRow = nItem % nPerCol;
4073 LISTVIEW_GetOrigin(infoPtr, &Origin);
4075 /* move the items below up a slot */
4076 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4077 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4078 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4079 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4080 OffsetRect(&rcScroll, Origin.x, Origin.y);
4081 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4082 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4083 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4085 /* report has only that column, so we're done */
4086 if (uView == LVS_REPORT) return;
4088 /* now for LISTs, we have to deal with the columns to the right */
4089 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4091 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4092 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4093 OffsetRect(&rcScroll, Origin.x, Origin.y);
4094 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4095 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4096 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4101 * Removes an item from the listview control.
4104 * [I] infoPtr : valid pointer to the listview structure
4105 * [I] nItem : item index
4111 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4113 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4117 TRACE("(nItem=%d)\n", nItem);
4119 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4121 /* remove selection, and focus */
4123 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4124 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4126 /* send LVN_DELETEITEM notification. */
4127 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
4129 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
4131 /* we need to do this here, because we'll be deleting stuff */
4132 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4133 LISTVIEW_InvalidateItem(infoPtr, nItem);
4135 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4141 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4142 for (i = 0; i < hdpaSubItems->nItemCount; i++)
4144 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4145 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4146 COMCTL32_Free(hdrItem);
4148 DPA_Destroy(hdpaSubItems);
4151 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4153 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4154 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4157 infoPtr->nItemCount--;
4158 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4160 /* now is the invalidation fun */
4161 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4168 * Callback implementation for editlabel control
4171 * [I] infoPtr : valid pointer to the listview structure
4172 * [I] pszText : modified text
4173 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4179 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4181 NMLVDISPINFOW dispInfo;
4183 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4185 ZeroMemory(&dispInfo, sizeof(dispInfo));
4186 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4187 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4188 dispInfo.item.iSubItem = 0;
4189 dispInfo.item.stateMask = ~0;
4190 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4191 /* add the text from the edit in */
4192 dispInfo.item.mask |= LVIF_TEXT;
4193 dispInfo.item.pszText = pszText;
4194 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4196 /* Do we need to update the Item Text */
4197 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4198 if (!pszText) return TRUE;
4200 ZeroMemory(&dispInfo, sizeof(dispInfo));
4201 dispInfo.item.mask = LVIF_TEXT;
4202 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4203 dispInfo.item.iSubItem = 0;
4204 dispInfo.item.pszText = pszText;
4205 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4206 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4211 * Begin in place editing of specified list view item
4214 * [I] infoPtr : valid pointer to the listview structure
4215 * [I] nItem : item index
4216 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4222 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4224 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4225 NMLVDISPINFOW dispInfo;
4228 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4230 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4231 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4233 infoPtr->nEditLabelItem = nItem;
4235 /* Is the EditBox still there, if so remove it */
4236 if(infoPtr->hwndEdit != 0)
4238 SetFocus(infoPtr->hwndSelf);
4239 infoPtr->hwndEdit = 0;
4242 LISTVIEW_SetSelection(infoPtr, nItem);
4243 LISTVIEW_SetItemFocus(infoPtr, nItem);
4245 rect.left = LVIR_LABEL;
4246 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4248 ZeroMemory(&dispInfo, sizeof(dispInfo));
4249 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4250 dispInfo.item.iItem = nItem;
4251 dispInfo.item.iSubItem = 0;
4252 dispInfo.item.stateMask = ~0;
4253 dispInfo.item.pszText = szDispText;
4254 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4255 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4257 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4258 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4259 if (!infoPtr->hwndEdit) return 0;
4261 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4263 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4264 infoPtr->hwndEdit = 0;
4268 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4269 SetFocus(infoPtr->hwndEdit);
4270 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4271 return infoPtr->hwndEdit;
4277 * Ensures the specified item is visible, scrolling into view if necessary.
4280 * [I] infoPtr : valid pointer to the listview structure
4281 * [I] nItem : item index
4282 * [I] bPartial : partially or entirely visible
4288 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4290 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4291 INT nScrollPosHeight = 0;
4292 INT nScrollPosWidth = 0;
4293 INT nHorzAdjust = 0;
4294 INT nVertAdjust = 0;
4297 RECT rcItem, rcTemp;
4299 rcItem.left = LVIR_BOUNDS;
4300 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4302 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4304 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4306 /* scroll left/right, but in LVS_REPORT mode */
4307 if (uView == LVS_LIST)
4308 nScrollPosWidth = infoPtr->nItemWidth;
4309 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4310 nScrollPosWidth = 1;
4312 if (rcItem.left < infoPtr->rcList.left)
4315 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4320 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4324 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4326 /* scroll up/down, but not in LVS_LIST mode */
4327 if (uView == LVS_REPORT)
4328 nScrollPosHeight = infoPtr->nItemHeight;
4329 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4330 nScrollPosHeight = 1;
4332 if (rcItem.top < infoPtr->rcList.top)
4335 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4340 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4344 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4346 if (nScrollPosWidth)
4348 INT diff = nHorzDiff / nScrollPosWidth;
4349 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4350 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4353 if (nScrollPosHeight)
4355 INT diff = nVertDiff / nScrollPosHeight;
4356 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4357 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4365 * Searches for an item with specific characteristics.
4368 * [I] hwnd : window handle
4369 * [I] nStart : base item index
4370 * [I] lpFindInfo : item information to look for
4373 * SUCCESS : index of item
4376 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4377 LPLVFINDINFOW lpFindInfo)
4379 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4380 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4381 BOOL bWrap = FALSE, bNearest = FALSE;
4382 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4383 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4384 POINT Position, Destination;
4387 if (!lpFindInfo || nItem < 0) return -1;
4390 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4392 lvItem.mask |= LVIF_TEXT;
4393 lvItem.pszText = szDispText;
4394 lvItem.cchTextMax = DISP_TEXT_SIZE;
4397 if (lpFindInfo->flags & LVFI_WRAP)
4400 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4401 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4406 LISTVIEW_GetOrigin(infoPtr, &Origin);
4407 Destination.x = lpFindInfo->pt.x - Origin.x;
4408 Destination.y = lpFindInfo->pt.y - Origin.y;
4409 switch(lpFindInfo->vkDirection)
4411 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4412 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4413 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4414 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4415 case VK_HOME: Destination.x = Destination.y = 0; break;
4416 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4417 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4419 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4420 Destination.x = rcArea.right;
4421 Destination.y = rcArea.bottom;
4423 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4428 /* if LVFI_PARAM is specified, all other flags are ignored */
4429 if (lpFindInfo->flags & LVFI_PARAM)
4431 lvItem.mask |= LVIF_PARAM;
4433 lvItem.mask &= ~LVIF_TEXT;
4437 for (; nItem < nLast; nItem++)
4439 lvItem.iItem = nItem;
4440 lvItem.iSubItem = 0;
4441 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4443 if (lvItem.mask & LVIF_PARAM)
4445 if (lpFindInfo->lParam == lvItem.lParam)
4451 if (lvItem.mask & LVIF_TEXT)
4453 if (lpFindInfo->flags & LVFI_PARTIAL)
4455 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4459 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4463 if (!bNearest) return nItem;
4465 /* This is very inefficient. To do a good job here,
4466 * we need a sorted array of (x,y) item positions */
4467 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4469 /* compute the distance^2 to the destination */
4470 xdist = Destination.x - Position.x;
4471 ydist = Destination.y - Position.y;
4472 dist = xdist * xdist + ydist * ydist;
4474 /* remember the distance, and item if it's closer */
4478 nNearestItem = nItem;
4485 nLast = min(nStart + 1, infoPtr->nItemCount);
4490 return nNearestItem;
4495 * Searches for an item with specific characteristics.
4498 * [I] hwnd : window handle
4499 * [I] nStart : base item index
4500 * [I] lpFindInfo : item information to look for
4503 * SUCCESS : index of item
4506 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4507 LPLVFINDINFOA lpFindInfo)
4509 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4513 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4514 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4515 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4516 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4522 * Retrieves the background image of the listview control.
4525 * [I] infoPtr : valid pointer to the listview structure
4526 * [O] lpBkImage : background image attributes
4532 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4534 /* FIXME (listview, "empty stub!\n"); */
4540 * Retrieves column attributes.
4543 * [I] infoPtr : valid pointer to the listview structure
4544 * [I] nColumn : column index
4545 * [IO] lpColumn : column information
4546 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4547 * otherwise it is in fact a LPLVCOLUMNA
4553 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4555 COLUMN_INFO *lpColumnInfo;
4558 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4559 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4561 /* initialize memory */
4562 ZeroMemory(&hdi, sizeof(hdi));
4564 if (lpColumn->mask & LVCF_TEXT)
4566 hdi.mask |= HDI_TEXT;
4567 hdi.pszText = lpColumn->pszText;
4568 hdi.cchTextMax = lpColumn->cchTextMax;
4571 if (lpColumn->mask & LVCF_IMAGE)
4572 hdi.mask |= HDI_IMAGE;
4574 if (lpColumn->mask & LVCF_ORDER)
4575 hdi.mask |= HDI_ORDER;
4577 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4579 if (lpColumn->mask & LVCF_FMT)
4580 lpColumn->fmt = lpColumnInfo->fmt;
4582 if (lpColumn->mask & LVCF_WIDTH)
4583 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4585 if (lpColumn->mask & LVCF_IMAGE)
4586 lpColumn->iImage = hdi.iImage;
4588 if (lpColumn->mask & LVCF_ORDER)
4589 lpColumn->iOrder = hdi.iOrder;
4595 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4602 /* FIXME: little hack */
4603 for (i = 0; i < iCount; i++)
4611 * Retrieves the column width.
4614 * [I] infoPtr : valid pointer to the listview structure
4615 * [I] int : column index
4618 * SUCCESS : column width
4621 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4623 INT nColumnWidth = 0;
4626 TRACE("nColumn=%d\n", nColumn);
4628 /* we have a 'column' in LIST and REPORT mode only */
4629 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4632 nColumnWidth = infoPtr->nItemWidth;
4635 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return 0;
4636 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4637 nColumnWidth = rcHeader.right - rcHeader.left;
4641 TRACE("nColumnWidth=%d\n", nColumnWidth);
4642 return nColumnWidth;
4647 * In list or report display mode, retrieves the number of items that can fit
4648 * vertically in the visible area. In icon or small icon display mode,
4649 * retrieves the total number of visible items.
4652 * [I] infoPtr : valid pointer to the listview structure
4655 * Number of fully visible items.
4657 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4659 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4663 return infoPtr->nItemCount;
4665 return LISTVIEW_GetCountPerColumn(infoPtr);
4667 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4674 * Retrieves an image list handle.
4677 * [I] infoPtr : valid pointer to the listview structure
4678 * [I] nImageList : image list identifier
4681 * SUCCESS : image list handle
4684 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4688 case LVSIL_NORMAL: return infoPtr->himlNormal;
4689 case LVSIL_SMALL: return infoPtr->himlSmall;
4690 case LVSIL_STATE: return infoPtr->himlState;
4695 /* LISTVIEW_GetISearchString */
4699 * Retrieves item attributes.
4702 * [I] hwnd : window handle
4703 * [IO] lpLVItem : item info
4704 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4705 * if FALSE, the lpLVItem is a LPLVITEMA.
4708 * This is the internal 'GetItem' interface -- it tries to
4709 * be smart, and avoids text copies, if possible, by modifing
4710 * lpLVItem->pszText to point to the text string. Please note
4711 * that this is not always possible (e.g. OWNERDATA), so on
4712 * entry you *must* supply valid values for pszText, and cchTextMax.
4713 * The only difference to the documented interface is that upon
4714 * return, you should use *only* the lpLVItem->pszText, rather than
4715 * the buffer pointer you provided on input. Most code already does
4716 * that, so it's not a problem.
4717 * For the two cases when the text must be copied (that is,
4718 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4724 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4726 NMLVDISPINFOW dispInfo;
4731 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4733 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4736 if (lpLVItem->mask == 0) return TRUE;
4738 /* a quick optimization if all we're asked is the focus state
4739 * these queries are worth optimising since they are common,
4740 * and can be answered in constant time, without the heavy accesses */
4741 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4742 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4744 lpLVItem->state = 0;
4745 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4746 lpLVItem->state |= LVIS_FOCUSED;
4750 ZeroMemory(&dispInfo, sizeof(dispInfo));
4752 /* if the app stores all the data, handle it separately */
4753 if (infoPtr->dwStyle & LVS_OWNERDATA)
4755 dispInfo.item.state = 0;
4757 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
4758 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
4760 /* NOTE: copy only fields which we _know_ are initialized, some apps
4761 * depend on the uninitialized fields being 0 */
4762 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
4763 dispInfo.item.iItem = lpLVItem->iItem;
4764 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4765 if (lpLVItem->mask & LVIF_TEXT)
4767 dispInfo.item.pszText = lpLVItem->pszText;
4768 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4770 if (lpLVItem->mask & LVIF_STATE)
4771 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4772 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4773 dispInfo.item.stateMask = lpLVItem->stateMask;
4774 *lpLVItem = dispInfo.item;
4775 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4778 /* make sure lParam is zeroed out */
4779 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
4781 /* we store only a little state, so if we're not asked, we're done */
4782 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4784 /* if focus is handled by us, report it */
4785 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4787 lpLVItem->state &= ~LVIS_FOCUSED;
4788 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4789 lpLVItem->state |= LVIS_FOCUSED;
4792 /* and do the same for selection, if we handle it */
4793 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4795 lpLVItem->state &= ~LVIS_SELECTED;
4796 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4797 lpLVItem->state |= LVIS_SELECTED;
4803 /* find the item and subitem structures before we proceed */
4804 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4805 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4808 if (lpLVItem->iSubItem)
4810 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4811 if(!lpSubItem) return FALSE;
4812 pItemHdr = &lpSubItem->hdr;
4815 pItemHdr = &lpItem->hdr;
4817 /* Do we need to query the state from the app? */
4818 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4820 dispInfo.item.mask |= LVIF_STATE;
4821 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4824 /* Do we need to enquire about the image? */
4825 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4826 dispInfo.item.mask |= LVIF_IMAGE;
4828 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4829 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4831 dispInfo.item.mask |= LVIF_TEXT;
4832 dispInfo.item.pszText = lpLVItem->pszText;
4833 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4834 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4835 *dispInfo.item.pszText = '\0';
4838 /* If we don't have all the requested info, query the application */
4839 if (dispInfo.item.mask != 0)
4841 dispInfo.item.iItem = lpLVItem->iItem;
4842 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4843 dispInfo.item.lParam = lpItem->lParam;
4844 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4845 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4848 /* Now, handle the iImage field */
4849 if (dispInfo.item.mask & LVIF_IMAGE)
4851 lpLVItem->iImage = dispInfo.item.iImage;
4852 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
4853 pItemHdr->iImage = dispInfo.item.iImage;
4855 else if (lpLVItem->mask & LVIF_IMAGE)
4856 lpLVItem->iImage = pItemHdr->iImage;
4858 /* The pszText field */
4859 if (dispInfo.item.mask & LVIF_TEXT)
4861 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4862 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4864 lpLVItem->pszText = dispInfo.item.pszText;
4866 else if (lpLVItem->mask & LVIF_TEXT)
4868 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4869 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4872 /* if this is a subitem, we're done */
4873 if (lpLVItem->iSubItem) return TRUE;
4875 /* Next is the lParam field */
4876 if (dispInfo.item.mask & LVIF_PARAM)
4878 lpLVItem->lParam = dispInfo.item.lParam;
4879 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4880 lpItem->lParam = dispInfo.item.lParam;
4882 else if (lpLVItem->mask & LVIF_PARAM)
4883 lpLVItem->lParam = lpItem->lParam;
4885 /* ... the state field (this one is different due to uCallbackmask) */
4886 if (lpLVItem->mask & LVIF_STATE)
4888 lpLVItem->state = lpItem->state;
4889 if (dispInfo.item.mask & LVIF_STATE)
4891 lpLVItem->state &= ~dispInfo.item.stateMask;
4892 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4894 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4896 lpLVItem->state &= ~LVIS_FOCUSED;
4897 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4898 lpLVItem->state |= LVIS_FOCUSED;
4900 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4902 lpLVItem->state &= ~LVIS_SELECTED;
4903 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4904 lpLVItem->state |= LVIS_SELECTED;
4908 /* and last, but not least, the indent field */
4909 if (lpLVItem->mask & LVIF_INDENT)
4910 lpLVItem->iIndent = lpItem->iIndent;
4917 * Retrieves item attributes.
4920 * [I] hwnd : window handle
4921 * [IO] lpLVItem : item info
4922 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4923 * if FALSE, the lpLVItem is a LPLVITEMA.
4926 * This is the external 'GetItem' interface -- it properly copies
4927 * the text in the provided buffer.
4933 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4938 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4941 pszText = lpLVItem->pszText;
4942 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4943 if (bResult && lpLVItem->pszText != pszText)
4944 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4945 lpLVItem->pszText = pszText;
4953 * Retrieves the position (upper-left) of the listview control item.
4954 * Note that for LVS_ICON style, the upper-left is that of the icon
4955 * and not the bounding box.
4958 * [I] infoPtr : valid pointer to the listview structure
4959 * [I] nItem : item index
4960 * [O] lpptPosition : coordinate information
4966 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4968 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4971 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4973 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4975 LISTVIEW_GetOrigin(infoPtr, &Origin);
4976 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
4978 if (uView == LVS_ICON)
4980 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4981 lpptPosition->y += ICON_TOP_PADDING;
4983 lpptPosition->x += Origin.x;
4984 lpptPosition->y += Origin.y;
4986 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4993 * Retrieves the bounding rectangle for a listview control item.
4996 * [I] infoPtr : valid pointer to the listview structure
4997 * [I] nItem : item index
4998 * [IO] lprc : bounding rectangle coordinates
4999 * lprc->left specifies the portion of the item for which the bounding
5000 * rectangle will be retrieved.
5002 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5003 * including the icon and label.
5006 * * Experiment shows that native control returns:
5007 * * width = min (48, length of text line)
5008 * * .left = position.x - (width - iconsize.cx)/2
5009 * * .right = .left + width
5010 * * height = #lines of text * ntmHeight + icon height + 8
5011 * * .top = position.y - 2
5012 * * .bottom = .top + height
5013 * * separation between items .y = itemSpacing.cy - height
5014 * * .x = itemSpacing.cx - width
5015 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5018 * * Experiment shows that native control returns:
5019 * * width = iconSize.cx + 16
5020 * * .left = position.x - (width - iconsize.cx)/2
5021 * * .right = .left + width
5022 * * height = iconSize.cy + 4
5023 * * .top = position.y - 2
5024 * * .bottom = .top + height
5025 * * separation between items .y = itemSpacing.cy - height
5026 * * .x = itemSpacing.cx - width
5027 * LVIR_LABEL Returns the bounding rectangle of the item text.
5030 * * Experiment shows that native control returns:
5031 * * width = text length
5032 * * .left = position.x - width/2
5033 * * .right = .left + width
5034 * * height = ntmH * linecount + 2
5035 * * .top = position.y + iconSize.cy + 6
5036 * * .bottom = .top + height
5037 * * separation between items .y = itemSpacing.cy - height
5038 * * .x = itemSpacing.cx - width
5039 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5040 * rectangles, but excludes columns in report view.
5047 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5048 * upon whether the window has the focus currently and on whether the item
5049 * is the one with the focus. Ensure that the control's record of which
5050 * item has the focus agrees with the items' records.
5052 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5054 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5055 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5056 BOOL doLabel = TRUE, oversizedBox = FALSE;
5057 POINT Position, Origin;
5061 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5063 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5065 LISTVIEW_GetOrigin(infoPtr, &Origin);
5066 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5068 /* Be smart and try to figure out the minimum we have to do */
5069 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5070 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5071 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5072 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5073 oversizedBox = TRUE;
5075 /* get what we need from the item before hand, so we make
5076 * only one request. This can speed up things, if data
5077 * is stored on the app side */
5079 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5080 if (doLabel) lvItem.mask |= LVIF_TEXT;
5081 lvItem.iItem = nItem;
5082 lvItem.iSubItem = 0;
5083 lvItem.pszText = szDispText;
5084 lvItem.cchTextMax = DISP_TEXT_SIZE;
5085 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5086 /* we got the state already up, simulate it here, to avoid a reget */
5087 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5089 lvItem.mask |= LVIF_STATE;
5090 lvItem.stateMask = LVIS_FOCUSED;
5091 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5094 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5095 lprc->left = LVIR_BOUNDS;
5099 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5103 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5107 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5110 case LVIR_SELECTBOUNDS:
5111 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5112 UnionRect(lprc, lprc, &label_rect);
5116 WARN("Unknown value: %d\n", lprc->left);
5120 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5122 TRACE(" rect=%s\n", debugrect(lprc));
5129 * Retrieves the spacing between listview control items.
5132 * [I] infoPtr : valid pointer to the listview structure
5133 * [IO] lprc : rectangle to receive the output
5134 * on input, lprc->top = nSubItem
5135 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5137 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5138 * not only those of the first column.
5139 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5145 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5147 POINT Position, Origin;
5150 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5152 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5154 LISTVIEW_GetOrigin(infoPtr, &Origin);
5155 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5157 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5158 lvItem.iItem = nItem;
5159 lvItem.iSubItem = lprc->top;
5161 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5165 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5170 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5174 ERR("Unknown bounds=%d\n", lprc->left);
5178 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5185 * Retrieves the width of a label.
5188 * [I] infoPtr : valid pointer to the listview structure
5191 * SUCCESS : string width (in pixels)
5194 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5196 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5199 TRACE("(nItem=%d)\n", nItem);
5201 lvItem.mask = LVIF_TEXT;
5202 lvItem.iItem = nItem;
5203 lvItem.iSubItem = 0;
5204 lvItem.pszText = szDispText;
5205 lvItem.cchTextMax = DISP_TEXT_SIZE;
5206 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5208 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5213 * Retrieves the spacing between listview control items.
5216 * [I] infoPtr : valid pointer to the listview structure
5217 * [I] bSmall : flag for small or large icon
5220 * Horizontal + vertical spacing
5222 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5228 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5232 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5233 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5235 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5242 * Retrieves the state of a listview control item.
5245 * [I] infoPtr : valid pointer to the listview structure
5246 * [I] nItem : item index
5247 * [I] uMask : state mask
5250 * State specified by the mask.
5252 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5256 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5258 lvItem.iItem = nItem;
5259 lvItem.iSubItem = 0;
5260 lvItem.mask = LVIF_STATE;
5261 lvItem.stateMask = uMask;
5262 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5264 return lvItem.state & uMask;
5269 * Retrieves the text of a listview control item or subitem.
5272 * [I] hwnd : window handle
5273 * [I] nItem : item index
5274 * [IO] lpLVItem : item information
5275 * [I] isW : TRUE if lpLVItem is Unicode
5278 * SUCCESS : string length
5281 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5283 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5285 lpLVItem->mask = LVIF_TEXT;
5286 lpLVItem->iItem = nItem;
5287 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5289 return textlenT(lpLVItem->pszText, isW);
5294 * Searches for an item based on properties + relationships.
5297 * [I] infoPtr : valid pointer to the listview structure
5298 * [I] nItem : item index
5299 * [I] uFlags : relationship flag
5302 * SUCCESS : item index
5305 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5307 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5309 LVFINDINFOW lvFindInfo;
5310 INT nCountPerColumn;
5313 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5314 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5316 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5318 if (uFlags & LVNI_CUT)
5321 if (uFlags & LVNI_DROPHILITED)
5322 uMask |= LVIS_DROPHILITED;
5324 if (uFlags & LVNI_FOCUSED)
5325 uMask |= LVIS_FOCUSED;
5327 if (uFlags & LVNI_SELECTED)
5328 uMask |= LVIS_SELECTED;
5330 /* if we're asked for the focused item, that's only one,
5331 * so it's worth optimizing */
5332 if (uFlags & LVNI_FOCUSED)
5334 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5335 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5338 if (uFlags & LVNI_ABOVE)
5340 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5345 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5351 lvFindInfo.flags = LVFI_NEARESTXY;
5352 lvFindInfo.vkDirection = VK_UP;
5353 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5354 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5356 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5361 else if (uFlags & LVNI_BELOW)
5363 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5365 while (nItem < infoPtr->nItemCount)
5368 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5374 lvFindInfo.flags = LVFI_NEARESTXY;
5375 lvFindInfo.vkDirection = VK_DOWN;
5376 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5377 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5379 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5384 else if (uFlags & LVNI_TOLEFT)
5386 if (uView == LVS_LIST)
5388 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5389 while (nItem - nCountPerColumn >= 0)
5391 nItem -= nCountPerColumn;
5392 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5396 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5398 lvFindInfo.flags = LVFI_NEARESTXY;
5399 lvFindInfo.vkDirection = VK_LEFT;
5400 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5401 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5403 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5408 else if (uFlags & LVNI_TORIGHT)
5410 if (uView == LVS_LIST)
5412 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5413 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5415 nItem += nCountPerColumn;
5416 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5420 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5422 lvFindInfo.flags = LVFI_NEARESTXY;
5423 lvFindInfo.vkDirection = VK_RIGHT;
5424 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5425 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5427 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5436 /* search by index */
5437 for (i = nItem; i < infoPtr->nItemCount; i++)
5439 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5447 /* LISTVIEW_GetNumberOfWorkAreas */
5451 * Retrieves the origin coordinates when in icon or small icon display mode.
5454 * [I] infoPtr : valid pointer to the listview structure
5455 * [O] lpptOrigin : coordinate information
5460 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5462 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5463 INT nHorzPos = 0, nVertPos = 0;
5464 SCROLLINFO scrollInfo;
5466 scrollInfo.cbSize = sizeof(SCROLLINFO);
5467 scrollInfo.fMask = SIF_POS;
5469 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5470 nHorzPos = scrollInfo.nPos;
5471 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5472 nVertPos = scrollInfo.nPos;
5474 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5476 lpptOrigin->x = infoPtr->rcList.left;
5477 lpptOrigin->y = infoPtr->rcList.top;
5478 if (uView == LVS_LIST)
5479 nHorzPos *= infoPtr->nItemWidth;
5480 else if (uView == LVS_REPORT)
5481 nVertPos *= infoPtr->nItemHeight;
5483 lpptOrigin->x -= nHorzPos;
5484 lpptOrigin->y -= nVertPos;
5486 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5491 * Retrieves the width of a string.
5494 * [I] hwnd : window handle
5495 * [I] lpszText : text string to process
5496 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5499 * SUCCESS : string width (in pixels)
5502 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5507 if (is_textT(lpszText, isW))
5509 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5510 HDC hdc = GetDC(infoPtr->hwndSelf);
5511 HFONT hOldFont = SelectObject(hdc, hFont);
5514 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5516 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5517 SelectObject(hdc, hOldFont);
5518 ReleaseDC(infoPtr->hwndSelf, hdc);
5520 return stringSize.cx;
5525 * Determines which listview item is located at the specified position.
5528 * [I] infoPtr : valid pointer to the listview structure
5529 * [IO] lpht : hit test information
5530 * [I] subitem : fill out iSubItem.
5531 * [I] select : return the index only if the hit selects the item
5534 * (mm 20001022): We must not allow iSubItem to be touched, for
5535 * an app might pass only a structure with space up to iItem!
5536 * (MS Office 97 does that for instance in the file open dialog)
5539 * SUCCESS : item index
5542 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5544 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5545 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5546 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5547 POINT Origin, Position, opt;
5551 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5555 if (subitem) lpht->iSubItem = 0;
5557 if (infoPtr->rcList.left > lpht->pt.x)
5558 lpht->flags |= LVHT_TOLEFT;
5559 else if (infoPtr->rcList.right < lpht->pt.x)
5560 lpht->flags |= LVHT_TORIGHT;
5562 if (infoPtr->rcList.top > lpht->pt.y)
5563 lpht->flags |= LVHT_ABOVE;
5564 else if (infoPtr->rcList.bottom < lpht->pt.y)
5565 lpht->flags |= LVHT_BELOW;
5567 TRACE("lpht->flags=0x%x\n", lpht->flags);
5568 if (lpht->flags) return -1;
5570 lpht->flags |= LVHT_NOWHERE;
5572 LISTVIEW_GetOrigin(infoPtr, &Origin);
5574 /* first deal with the large items */
5575 rcSearch.left = lpht->pt.x;
5576 rcSearch.top = lpht->pt.y;
5577 rcSearch.right = rcSearch.left + 1;
5578 rcSearch.bottom = rcSearch.top + 1;
5580 iterator_frameditems(&i, infoPtr, &rcSearch);
5581 iterator_next(&i); /* go to first item in the sequence */
5582 lpht->iItem = i.nItem;
5583 iterator_destroy(&i);
5585 TRACE("lpht->iItem=%d\n", lpht->iItem);
5586 if (lpht->iItem == -1) return -1;
5588 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5589 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5590 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5591 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5592 lvItem.iItem = lpht->iItem;
5593 lvItem.iSubItem = 0;
5594 lvItem.pszText = szDispText;
5595 lvItem.cchTextMax = DISP_TEXT_SIZE;
5596 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5597 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5599 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5600 LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position);
5601 opt.x = lpht->pt.x - Position.x - Origin.x;
5602 opt.y = lpht->pt.y - Position.y - Origin.y;
5604 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5607 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5608 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5609 if (!PtInRect(&rcBounds, opt)) return -1;
5611 if (PtInRect(&rcIcon, opt))
5612 lpht->flags |= LVHT_ONITEMICON;
5613 else if (PtInRect(&rcLabel, opt))
5614 lpht->flags |= LVHT_ONITEMLABEL;
5615 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5616 lpht->flags |= LVHT_ONITEMSTATEICON;
5617 if (lpht->flags & LVHT_ONITEM)
5618 lpht->flags &= ~LVHT_NOWHERE;
5620 TRACE("lpht->flags=0x%x\n", lpht->flags);
5621 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5625 rcBounds.right = rcBounds.left;
5626 for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5628 rcBounds.left = rcBounds.right;
5629 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5630 if (PtInRect(&rcBounds, opt))
5638 if (!select || lpht->iItem == -1) return lpht->iItem;
5640 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5642 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5643 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5647 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5648 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5649 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5650 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5651 their own sort proc. when sending LVM_SORTITEMS.
5654 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5656 LVS_SORTXXX must be specified,
5657 LVS_OWNERDRAW is not set,
5658 <item>.pszText is not LPSTR_TEXTCALLBACK.
5660 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5661 are sorted based on item text..."
5663 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5665 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5666 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5667 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5669 /* if we're sorting descending, negate the return value */
5670 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5675 * Inserts a new item in the listview control.
5678 * [I] infoPtr : valid pointer to the listview structure
5679 * [I] lpLVItem : item information
5680 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5683 * SUCCESS : new item index
5686 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5688 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5693 BOOL is_sorted, has_changed;
5696 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5698 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5700 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5701 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5703 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5705 if ( !(lpItem = (ITEM_INFO *)COMCTL32_Alloc(sizeof(ITEM_INFO))) )
5708 /* insert item in listview control data structure */
5709 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5710 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5712 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5713 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5715 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5716 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5717 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5718 if (nItem == -1) goto fail;
5719 infoPtr->nItemCount++;
5721 /* set the item attributes */
5724 item.state &= ~LVIS_STATEIMAGEMASK;
5725 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5727 /* if we're sorted, sort the list, and update the index */
5730 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5731 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5732 assert(nItem != -1);
5735 /* make room for the position, if we are in the right mode */
5736 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5738 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5740 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5742 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5747 /* Add the subitem list to the items array. Do this last in case we go to
5748 * fail during the above.
5750 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5752 /* send LVN_INSERTITEM notification */
5753 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5755 nmlv.lParam = lpItem->lParam;
5756 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5758 /* align items (set position of each item) */
5759 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
5763 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
5764 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
5766 LISTVIEW_NextIconPosTop(infoPtr, &pt);
5768 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
5771 /* now is the invalidation fun */
5772 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
5776 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5777 infoPtr->nItemCount--;
5779 DPA_DeletePtr(hdpaSubItems, 0);
5780 DPA_Destroy (hdpaSubItems);
5781 COMCTL32_Free (lpItem);
5787 * Redraws a range of items.
5790 * [I] infoPtr : valid pointer to the listview structure
5791 * [I] nFirst : first item
5792 * [I] nLast : last item
5798 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5802 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5803 max(nFirst, nLast) >= infoPtr->nItemCount)
5806 for (i = nFirst; i <= nLast; i++)
5807 LISTVIEW_InvalidateItem(infoPtr, i);
5814 * Scroll the content of a listview.
5817 * [I] infoPtr : valid pointer to the listview structure
5818 * [I] dx : horizontal scroll amount in pixels
5819 * [I] dy : vertical scroll amount in pixels
5826 * If the control is in report mode (LVS_REPORT) the control can
5827 * be scrolled only in line increments. "dy" will be rounded to the
5828 * nearest number of pixels that are a whole line. Ex: if line height
5829 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5830 * is passed the the scroll will be 0. (per MSDN 7/2002)
5832 * For: (per experimentaion with native control and CSpy ListView)
5833 * LVS_ICON dy=1 = 1 pixel (vertical only)
5835 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5837 * LVS_LIST dx=1 = 1 column (horizontal only)
5838 * but will only scroll 1 column per message
5839 * no matter what the value.
5840 * dy must be 0 or FALSE returned.
5841 * LVS_REPORT dx=1 = 1 pixel
5845 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5847 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5849 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5850 dy /= infoPtr->nItemHeight;
5853 if (dy != 0) return FALSE;
5860 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5861 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5868 * Sets the background color.
5871 * [I] infoPtr : valid pointer to the listview structure
5872 * [I] clrBk : background color
5878 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5880 TRACE("(clrBk=%lx)\n", clrBk);
5882 if(infoPtr->clrBk != clrBk) {
5883 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5884 infoPtr->clrBk = clrBk;
5885 if (clrBk == CLR_NONE)
5886 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5888 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5889 LISTVIEW_InvalidateList(infoPtr);
5895 /* LISTVIEW_SetBkImage */
5897 /*** Helper for {Insert,Set}ColumnT *only* */
5898 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5900 if (lpColumn->mask & LVCF_FMT)
5902 /* format member is valid */
5903 lphdi->mask |= HDI_FORMAT;
5905 /* set text alignment (leftmost column must be left-aligned) */
5906 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5907 lphdi->fmt |= HDF_LEFT;
5908 else if (lpColumn->fmt & LVCFMT_RIGHT)
5909 lphdi->fmt |= HDF_RIGHT;
5910 else if (lpColumn->fmt & LVCFMT_CENTER)
5911 lphdi->fmt |= HDF_CENTER;
5913 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5914 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
5916 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5918 lphdi->fmt |= HDF_IMAGE;
5919 lphdi->iImage = I_IMAGECALLBACK;
5923 if (lpColumn->mask & LVCF_WIDTH)
5925 lphdi->mask |= HDI_WIDTH;
5926 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5928 /* make it fill the remainder of the controls width */
5932 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5934 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
5935 lphdi->cxy += rcHeader.right - rcHeader.left;
5938 /* retrieve the layout of the header */
5939 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5940 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
5942 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
5945 lphdi->cxy = lpColumn->cx;
5948 if (lpColumn->mask & LVCF_TEXT)
5950 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
5951 lphdi->fmt |= HDF_STRING;
5952 lphdi->pszText = lpColumn->pszText;
5953 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
5956 if (lpColumn->mask & LVCF_IMAGE)
5958 lphdi->mask |= HDI_IMAGE;
5959 lphdi->iImage = lpColumn->iImage;
5962 if (lpColumn->mask & LVCF_ORDER)
5964 lphdi->mask |= HDI_ORDER;
5965 lphdi->iOrder = lpColumn->iOrder;
5972 * Inserts a new column.
5975 * [I] infoPtr : valid pointer to the listview structure
5976 * [I] nColumn : column index
5977 * [I] lpColumn : column information
5978 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
5981 * SUCCESS : new column index
5984 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5985 LPLVCOLUMNW lpColumn, BOOL isW)
5987 COLUMN_INFO *lpColumnInfo;
5991 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5993 if (!lpColumn || nColumn < 0 || nColumn > infoPtr->hdpaColumns->nItemCount) return -1;
5995 ZeroMemory(&hdi, sizeof(HDITEMW));
5996 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
5998 /* insert item in header control */
5999 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6000 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6001 (WPARAM)nColumn, (LPARAM)&hdi);
6002 if (nNewColumn == -1) return -1;
6003 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6005 /* create our own column info */
6006 if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
6007 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6009 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6010 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6012 /* now we have to actually adjust the data */
6013 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6015 SUBITEM_INFO *lpSubItem, *lpMainItem, **lpNewItems = 0;
6019 /* preallocate memory, so we can fail gracefully */
6020 if (nNewColumn == 0)
6022 lpNewItems = COMCTL32_Alloc(sizeof(SUBITEM_INFO *) * infoPtr->nItemCount);
6023 if (!lpNewItems) goto fail;
6024 for (i = 0; i < infoPtr->nItemCount; i++)
6025 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(SUBITEM_INFO)))) break;
6026 if (i != infoPtr->nItemCount)
6028 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
6029 COMCTL32_Free(lpNewItems);
6034 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6036 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6037 for (i = 1; i < hdpaSubItems->nItemCount; i++)
6039 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6040 if (lpSubItem->iSubItem >= nNewColumn)
6041 lpSubItem->iSubItem++;
6044 /* for inserting column 0, we have to special-case the main item */
6045 if (nNewColumn == 0)
6047 lpMainItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6048 lpSubItem = lpNewItems[nItem];
6049 lpSubItem->hdr = lpMainItem->hdr;
6050 lpSubItem->iSubItem = 1;
6051 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
6052 lpMainItem->iSubItem = 0;
6053 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
6057 COMCTL32_Free(lpNewItems);
6060 /* make space for the new column */
6061 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6066 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6069 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6070 COMCTL32_Free(lpColumnInfo);
6077 * Sets the attributes of a header item.
6080 * [I] infoPtr : valid pointer to the listview structure
6081 * [I] nColumn : column index
6082 * [I] lpColumn : column attributes
6083 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6089 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6090 LPLVCOLUMNW lpColumn, BOOL isW)
6092 HDITEMW hdi, hdiget;
6095 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6097 if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6099 ZeroMemory(&hdi, sizeof(HDITEMW));
6100 if (lpColumn->mask & LVCF_FMT)
6102 hdi.mask |= HDI_FORMAT;
6103 hdiget.mask = HDI_FORMAT;
6104 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6105 hdi.fmt = hdiget.fmt & HDF_STRING;
6107 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6109 /* set header item attributes */
6110 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6111 if (!bResult) return FALSE;
6113 if (lpColumn->mask & LVCF_FMT)
6115 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6116 int oldFmt = lpColumnInfo->fmt;
6118 lpColumnInfo->fmt = lpColumn->fmt;
6119 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6121 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6122 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6131 * Sets the column order array
6134 * [I] infoPtr : valid pointer to the listview structure
6135 * [I] iCount : number of elements in column order array
6136 * [I] lpiArray : pointer to column order array
6142 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6144 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6155 * Sets the width of a column
6158 * [I] infoPtr : valid pointer to the listview structure
6159 * [I] nColumn : column index
6160 * [I] cx : column width
6166 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6168 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6169 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6173 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6175 /* set column width only if in report or list mode */
6176 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6178 /* take care of invalid cx values */
6179 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6180 else if (uView == LVS_LIST && cx < 1) return FALSE;
6182 /* resize all columns if in LVS_LIST mode */
6183 if(uView == LVS_LIST)
6185 infoPtr->nItemWidth = cx;
6186 LISTVIEW_InvalidateList(infoPtr);
6190 if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6192 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6197 lvItem.mask = LVIF_TEXT;
6199 lvItem.iSubItem = nColumn;
6200 lvItem.pszText = szDispText;
6201 lvItem.cchTextMax = DISP_TEXT_SIZE;
6202 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6204 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6205 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6206 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6208 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6209 max_cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6210 max_cx += REPORT_MARGINX + TRAILING_PADDING;
6213 /* autosize based on listview items width */
6214 if(cx == LVSCW_AUTOSIZE)
6216 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6218 /* if iCol is the last column make it fill the remainder of the controls width */
6219 if(nColumn == infoPtr->hdpaColumns->nItemCount - 1)
6224 LISTVIEW_GetOrigin(infoPtr, &Origin);
6225 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6227 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6231 /* Despite what the MS docs say, if this is not the last
6232 column, then MS resizes the column to the width of the
6233 largest text string in the column, including headers
6234 and items. This is different from LVSCW_AUTOSIZE in that
6235 LVSCW_AUTOSIZE ignores the header string length. */
6238 /* retrieve header text */
6239 hdi.mask = HDI_TEXT;
6240 hdi.cchTextMax = DISP_TEXT_SIZE;
6241 hdi.pszText = szDispText;
6242 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6244 HDC hdc = GetDC(infoPtr->hwndSelf);
6245 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6248 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6250 /* FIXME: Take into account the header image, if one is present */
6251 SelectObject(hdc, old_font);
6252 ReleaseDC(infoPtr->hwndSelf, hdc);
6254 cx = max (cx, max_cx);
6258 if (cx < 0) return FALSE;
6260 /* call header to update the column change */
6261 hdi.mask = HDI_WIDTH;
6263 TRACE("hdi.cxy=%d\n", hdi.cxy);
6264 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6269 * Sets the extended listview style.
6272 * [I] infoPtr : valid pointer to the listview structure
6274 * [I] dwStyle : style
6277 * SUCCESS : previous style
6280 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6282 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6286 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6288 infoPtr->dwLvExStyle = dwStyle;
6295 * Sets the new hot cursor used during hot tracking and hover selection.
6298 * [I] infoPtr : valid pointer to the listview structure
6299 * [I} hCurosr : the new hot cursor handle
6302 * Returns the previous hot cursor
6304 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6306 HCURSOR oldCursor = infoPtr->hHotCursor;
6308 infoPtr->hHotCursor = hCursor;
6316 * Sets the hot item index.
6319 * [I] infoPtr : valid pointer to the listview structure
6320 * [I] iIndex : index
6323 * SUCCESS : previous hot item index
6324 * FAILURE : -1 (no hot item)
6326 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6328 INT iOldIndex = infoPtr->nHotItem;
6330 infoPtr->nHotItem = iIndex;
6338 * Sets the amount of time the cursor must hover over an item before it is selected.
6341 * [I] infoPtr : valid pointer to the listview structure
6342 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6345 * Returns the previous hover time
6347 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6349 DWORD oldHoverTime = infoPtr->dwHoverTime;
6351 infoPtr->dwHoverTime = dwHoverTime;
6353 return oldHoverTime;
6358 * Sets spacing for icons of LVS_ICON style.
6361 * [I] infoPtr : valid pointer to the listview structure
6362 * [I] spacing : MAKELONG(cx, cy)
6365 * MAKELONG(oldcx, oldcy)
6367 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6369 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6370 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6371 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6373 TRACE("requested=(%d,%d)\n", cx, cy);
6375 /* this is supported only for LVS_ICON style */
6376 if (uView != LVS_ICON) return oldspacing;
6378 /* set to defaults, if instructed to */
6379 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6380 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6382 /* if 0 then compute width
6383 * FIXME: Should scan each item and determine max width of
6384 * icon or label, then make that the width */
6386 cx = infoPtr->iconSpacing.cx;
6388 /* if 0 then compute height */
6390 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6391 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6394 infoPtr->iconSpacing.cx = cx;
6395 infoPtr->iconSpacing.cy = cy;
6397 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6398 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6399 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6400 infoPtr->ntmHeight);
6402 /* these depend on the iconSpacing */
6403 LISTVIEW_UpdateItemSize(infoPtr);
6408 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6412 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6419 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6420 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6429 * [I] infoPtr : valid pointer to the listview structure
6430 * [I] nType : image list type
6431 * [I] himl : image list handle
6434 * SUCCESS : old image list
6437 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6439 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6440 INT oldHeight = infoPtr->nItemHeight;
6441 HIMAGELIST himlOld = 0;
6446 himlOld = infoPtr->himlNormal;
6447 infoPtr->himlNormal = himl;
6448 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6449 LISTVIEW_SetIconSpacing(infoPtr, 0);
6453 himlOld = infoPtr->himlSmall;
6454 infoPtr->himlSmall = himl;
6455 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6459 himlOld = infoPtr->himlState;
6460 infoPtr->himlState = himl;
6461 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6462 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6466 ERR("Unknown icon type=%d\n", nType);
6470 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6471 if (infoPtr->nItemHeight != oldHeight)
6472 LISTVIEW_UpdateScroll(infoPtr);
6479 * Preallocates memory (does *not* set the actual count of items !)
6482 * [I] infoPtr : valid pointer to the listview structure
6483 * [I] nItems : item count (projected number of items to allocate)
6484 * [I] dwFlags : update flags
6490 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6492 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6494 if (infoPtr->dwStyle & LVS_OWNERDATA)
6496 int precount,topvisible;
6498 TRACE("LVS_OWNERDATA is set!\n");
6499 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6500 FIXME("flags %s %s not implemented\n",
6501 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6503 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6505 LISTVIEW_DeselectAll(infoPtr);
6507 precount = infoPtr->nItemCount;
6508 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6509 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6511 infoPtr->nItemCount = nItems;
6512 LISTVIEW_UpdateItemSize(infoPtr);
6514 LISTVIEW_UpdateSize(infoPtr);
6515 LISTVIEW_UpdateScroll(infoPtr);
6517 if (min(precount,infoPtr->nItemCount) < topvisible)
6518 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6522 /* According to MSDN for non-LVS_OWNERDATA this is just
6523 * a performance issue. The control allocates its internal
6524 * data structures for the number of items specified. It
6525 * cuts down on the number of memory allocations. Therefore
6526 * we will just issue a WARN here
6528 WARN("for non-ownerdata performance option not implemented.\n");
6536 * Sets the position of an item.
6539 * [I] infoPtr : valid pointer to the listview structure
6540 * [I] nItem : item index
6541 * [I] pt : coordinate
6547 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6549 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6552 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6554 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6555 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6557 LISTVIEW_GetOrigin(infoPtr, &Origin);
6559 /* This point value seems to be an undocumented feature.
6560 * The best guess is that it means either at the origin,
6561 * or at true beginning of the list. I will assume the origin. */
6562 if ((pt.x == -1) && (pt.y == -1))
6565 if (uView == LVS_ICON)
6567 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6568 pt.y -= ICON_TOP_PADDING;
6573 infoPtr->bAutoarrange = FALSE;
6575 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
6580 * Sets the state of one or many items.
6583 * [I] infoPtr : valid pointer to the listview structure
6584 * [I] nItem : item index
6585 * [I] lpLVItem : item or subitem info
6591 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6593 BOOL bResult = TRUE;
6596 lvItem.iItem = nItem;
6597 lvItem.iSubItem = 0;
6598 lvItem.mask = LVIF_STATE;
6599 lvItem.state = lpLVItem->state;
6600 lvItem.stateMask = lpLVItem->stateMask;
6601 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6605 /* apply to all items */
6606 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6607 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6610 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6617 * Sets the text of an item or subitem.
6620 * [I] hwnd : window handle
6621 * [I] nItem : item index
6622 * [I] lpLVItem : item or subitem info
6623 * [I] isW : TRUE if input is Unicode
6629 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6633 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6635 lvItem.iItem = nItem;
6636 lvItem.iSubItem = lpLVItem->iSubItem;
6637 lvItem.mask = LVIF_TEXT;
6638 lvItem.pszText = lpLVItem->pszText;
6639 lvItem.cchTextMax = lpLVItem->cchTextMax;
6641 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6643 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6648 * Set item index that marks the start of a multiple selection.
6651 * [I] infoPtr : valid pointer to the listview structure
6652 * [I] nIndex : index
6655 * Index number or -1 if there is no selection mark.
6657 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6659 INT nOldIndex = infoPtr->nSelectionMark;
6661 TRACE("(nIndex=%d)\n", nIndex);
6663 infoPtr->nSelectionMark = nIndex;
6670 * Sets the text background color.
6673 * [I] infoPtr : valid pointer to the listview structure
6674 * [I] clrTextBk : text background color
6680 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6682 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6684 if (infoPtr->clrTextBk != clrTextBk)
6686 infoPtr->clrTextBk = clrTextBk;
6687 LISTVIEW_InvalidateList(infoPtr);
6695 * Sets the text foreground color.
6698 * [I] infoPtr : valid pointer to the listview structure
6699 * [I] clrText : text color
6705 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6707 TRACE("(clrText=%lx)\n", clrText);
6709 if (infoPtr->clrText != clrText)
6711 infoPtr->clrText = clrText;
6712 LISTVIEW_InvalidateList(infoPtr);
6718 /* LISTVIEW_SetToolTips */
6719 /* LISTVIEW_SetUnicodeFormat */
6720 /* LISTVIEW_SetWorkAreas */
6724 * Callback internally used by LISTVIEW_SortItems()
6727 * [I] first : pointer to first ITEM_INFO to compare
6728 * [I] second : pointer to second ITEM_INFO to compare
6729 * [I] lParam : HWND of control
6732 * if first comes before second : negative
6733 * if first comes after second : positive
6734 * if first and second are equivalent : zero
6736 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6738 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6739 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6740 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6742 /* Forward the call to the client defined callback */
6743 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6748 * Sorts the listview items.
6751 * [I] infoPtr : valid pointer to the listview structure
6752 * [I] pfnCompare : application-defined value
6753 * [I] lParamSort : pointer to comparision callback
6759 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6761 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6764 LPVOID selectionMarkItem;
6768 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6770 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
6772 if (!infoPtr->hdpaItems) return FALSE;
6774 /* if there are 0 or 1 items, there is no need to sort */
6775 if (infoPtr->nItemCount < 2) return TRUE;
6777 if (infoPtr->nFocusedItem >= 0)
6779 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6780 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6781 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6783 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6784 /* clear the lpItem->state for non-selected ones */
6785 /* remove the selection ranges */
6787 infoPtr->pfnCompare = pfnCompare;
6788 infoPtr->lParamSort = lParamSort;
6789 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6791 /* Adjust selections and indices so that they are the way they should
6792 * be after the sort (otherwise, the list items move around, but
6793 * whatever is at the item's previous original position will be
6796 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6797 for (i=0; i < infoPtr->nItemCount; i++)
6799 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6800 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6802 if (lpItem->state & LVIS_SELECTED)
6804 item.state = LVIS_SELECTED;
6805 item.stateMask = LVIS_SELECTED;
6806 LISTVIEW_SetItemState(infoPtr, i, &item);
6808 if (lpItem->state & LVIS_FOCUSED)
6810 infoPtr->nFocusedItem = i;
6811 lpItem->state &= ~LVIS_FOCUSED;
6814 if (selectionMarkItem != NULL)
6815 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6816 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6818 /* refresh the display */
6819 if (uView != LVS_ICON && uView != LVS_SMALLICON)
6820 LISTVIEW_InvalidateList(infoPtr);
6827 * Updates an items or rearranges the listview control.
6830 * [I] infoPtr : valid pointer to the listview structure
6831 * [I] nItem : item index
6837 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6839 TRACE("(nItem=%d)\n", nItem);
6841 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6843 /* rearrange with default alignment style */
6844 if (is_autoarrange(infoPtr))
6845 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
6847 LISTVIEW_InvalidateItem(infoPtr, nItem);
6855 * Creates the listview control.
6858 * [I] hwnd : window handle
6859 * [I] lpcs : the create parameters
6865 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6867 LISTVIEW_INFO *infoPtr;
6868 UINT uView = lpcs->style & LVS_TYPEMASK;
6871 TRACE("(lpcs=%p)\n", lpcs);
6873 /* initialize info pointer */
6874 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6875 if (!infoPtr) return -1;
6877 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6879 infoPtr->hwndSelf = hwnd;
6880 infoPtr->dwStyle = lpcs->style;
6881 /* determine the type of structures to use */
6882 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6883 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6885 /* initialize color information */
6886 infoPtr->clrBk = CLR_NONE;
6887 infoPtr->clrText = comctl32_color.clrWindowText;
6888 infoPtr->clrTextBk = CLR_DEFAULT;
6889 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6891 /* set default values */
6892 infoPtr->nFocusedItem = -1;
6893 infoPtr->nSelectionMark = -1;
6894 infoPtr->nHotItem = -1;
6895 infoPtr->bRedraw = TRUE;
6896 infoPtr->bFirstPaint = TRUE;
6897 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6898 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6899 infoPtr->nEditLabelItem = -1;
6900 infoPtr->dwHoverTime = -1; /* default system hover time */
6902 /* get default font (icon title) */
6903 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6904 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6905 infoPtr->hFont = infoPtr->hDefaultFont;
6906 LISTVIEW_SaveTextMetrics(infoPtr);
6909 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6910 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6911 0, 0, 0, 0, hwnd, (HMENU)0,
6912 lpcs->hInstance, NULL);
6914 /* set header unicode format */
6915 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
6917 /* set header font */
6918 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
6920 /* allocate memory for the selection ranges */
6921 if (!(infoPtr->selectionRanges = ranges_create(10))) return -1;
6923 /* allocate memory for the data structure */
6924 /* FIXME: what if we fail? */
6925 infoPtr->hdpaItems = DPA_Create(10);
6926 infoPtr->hdpaPosX = DPA_Create(10);
6927 infoPtr->hdpaPosY = DPA_Create(10);
6928 infoPtr->hdpaColumns = DPA_Create(10);
6930 /* initialize the icon sizes */
6931 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
6932 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
6934 /* init item size to avoid division by 0 */
6935 LISTVIEW_UpdateItemSize (infoPtr);
6937 if (uView == LVS_REPORT)
6939 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6941 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6945 /* set HDS_HIDDEN flag to hide the header bar */
6946 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6947 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6956 * Erases the background of the listview control.
6959 * [I] infoPtr : valid pointer to the listview structure
6960 * [I] hdc : device context handle
6966 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6970 TRACE("(hdc=%p)\n", hdc);
6972 if (!GetClipBox(hdc, &rc)) return FALSE;
6974 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
6980 * Helper function for LISTVIEW_[HV]Scroll *only*.
6981 * Performs vertical/horizontal scrolling by a give amount.
6984 * [I] infoPtr : valid pointer to the listview structure
6985 * [I] dx : amount of horizontal scroll
6986 * [I] dy : amount of vertical scroll
6988 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6990 /* now we can scroll the list */
6991 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
6992 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
6993 /* if we have focus, adjust rect */
6994 OffsetRect(&infoPtr->rcFocus, dx, dy);
6995 UpdateWindow(infoPtr->hwndSelf);
7000 * Performs vertical scrolling.
7003 * [I] infoPtr : valid pointer to the listview structure
7004 * [I] nScrollCode : scroll code
7005 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7006 * [I] hScrollWnd : scrollbar control window handle
7012 * SB_LINEUP/SB_LINEDOWN:
7013 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7014 * for LVS_REPORT is 1 line
7015 * for LVS_LIST cannot occur
7018 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7019 INT nScrollDiff, HWND hScrollWnd)
7021 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7022 INT nOldScrollPos, nNewScrollPos;
7023 SCROLLINFO scrollInfo;
7026 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7028 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7030 scrollInfo.cbSize = sizeof(SCROLLINFO);
7031 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7033 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7035 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7037 nOldScrollPos = scrollInfo.nPos;
7038 switch (nScrollCode)
7044 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7048 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7052 nScrollDiff = -scrollInfo.nPage;
7056 nScrollDiff = scrollInfo.nPage;
7059 case SB_THUMBPOSITION:
7061 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7068 /* quit right away if pos isn't changing */
7069 if (nScrollDiff == 0) return 0;
7071 /* calculate new position, and handle overflows */
7072 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7073 if (nScrollDiff > 0) {
7074 if (nNewScrollPos < nOldScrollPos ||
7075 nNewScrollPos > scrollInfo.nMax)
7076 nNewScrollPos = scrollInfo.nMax;
7078 if (nNewScrollPos > nOldScrollPos ||
7079 nNewScrollPos < scrollInfo.nMin)
7080 nNewScrollPos = scrollInfo.nMin;
7083 /* set the new position, and reread in case it changed */
7084 scrollInfo.fMask = SIF_POS;
7085 scrollInfo.nPos = nNewScrollPos;
7086 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7088 /* carry on only if it really changed */
7089 if (nNewScrollPos == nOldScrollPos) return 0;
7091 /* now adjust to client coordinates */
7092 nScrollDiff = nOldScrollPos - nNewScrollPos;
7093 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7095 /* and scroll the window */
7096 scroll_list(infoPtr, 0, nScrollDiff);
7103 * Performs horizontal scrolling.
7106 * [I] infoPtr : valid pointer to the listview structure
7107 * [I] nScrollCode : scroll code
7108 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7109 * [I] hScrollWnd : scrollbar control window handle
7115 * SB_LINELEFT/SB_LINERIGHT:
7116 * for LVS_ICON, LVS_SMALLICON 1 pixel
7117 * for LVS_REPORT is 1 pixel
7118 * for LVS_LIST is 1 column --> which is a 1 because the
7119 * scroll is based on columns not pixels
7122 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7123 INT nScrollDiff, HWND hScrollWnd)
7125 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7126 INT nOldScrollPos, nNewScrollPos;
7127 SCROLLINFO scrollInfo;
7129 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7131 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7133 scrollInfo.cbSize = sizeof(SCROLLINFO);
7134 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7136 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7138 nOldScrollPos = scrollInfo.nPos;
7140 switch (nScrollCode)
7154 nScrollDiff = -scrollInfo.nPage;
7158 nScrollDiff = scrollInfo.nPage;
7161 case SB_THUMBPOSITION:
7163 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7170 /* quit right away if pos isn't changing */
7171 if (nScrollDiff == 0) return 0;
7173 /* calculate new position, and handle overflows */
7174 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7175 if (nScrollDiff > 0) {
7176 if (nNewScrollPos < nOldScrollPos ||
7177 nNewScrollPos > scrollInfo.nMax)
7178 nNewScrollPos = scrollInfo.nMax;
7180 if (nNewScrollPos > nOldScrollPos ||
7181 nNewScrollPos < scrollInfo.nMin)
7182 nNewScrollPos = scrollInfo.nMin;
7185 /* set the new position, and reread in case it changed */
7186 scrollInfo.fMask = SIF_POS;
7187 scrollInfo.nPos = nNewScrollPos;
7188 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7190 /* carry on only if it really changed */
7191 if (nNewScrollPos == nOldScrollPos) return 0;
7193 if(uView == LVS_REPORT)
7194 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7196 /* now adjust to client coordinates */
7197 nScrollDiff = nOldScrollPos - nNewScrollPos;
7198 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7200 /* and scroll the window */
7201 scroll_list(infoPtr, nScrollDiff, 0);
7206 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7208 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7209 INT gcWheelDelta = 0;
7210 UINT pulScrollLines = 3;
7211 SCROLLINFO scrollInfo;
7213 TRACE("(wheelDelta=%d)\n", wheelDelta);
7215 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7216 gcWheelDelta -= wheelDelta;
7218 scrollInfo.cbSize = sizeof(SCROLLINFO);
7219 scrollInfo.fMask = SIF_POS;
7226 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7227 * should be fixed in the future.
7229 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7230 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7231 scrollInfo.nPos + (gcWheelDelta < 0) ?
7232 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7233 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7237 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7239 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7241 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7242 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7243 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7249 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7260 * [I] infoPtr : valid pointer to the listview structure
7261 * [I] nVirtualKey : virtual key
7262 * [I] lKeyData : key data
7267 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7269 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7271 NMLVKEYDOWN nmKeyDown;
7273 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7275 /* send LVN_KEYDOWN notification */
7276 nmKeyDown.wVKey = nVirtualKey;
7277 nmKeyDown.flags = 0;
7278 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7280 switch (nVirtualKey)
7283 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7285 notify(infoPtr, NM_RETURN);
7286 notify(infoPtr, LVN_ITEMACTIVATE);
7291 if (infoPtr->nItemCount > 0)
7296 if (infoPtr->nItemCount > 0)
7297 nItem = infoPtr->nItemCount - 1;
7301 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7305 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7309 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7313 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7317 if (uView == LVS_REPORT)
7318 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7320 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7321 * LISTVIEW_GetCountPerRow(infoPtr);
7322 if(nItem < 0) nItem = 0;
7326 if (uView == LVS_REPORT)
7327 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7329 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7330 * LISTVIEW_GetCountPerRow(infoPtr);
7331 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7335 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7336 LISTVIEW_KeySelection(infoPtr, nItem);
7346 * [I] infoPtr : valid pointer to the listview structure
7351 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7355 /* if we did not have the focus, there's nothing to do */
7356 if (!infoPtr->bFocus) return 0;
7358 /* send NM_KILLFOCUS notification */
7359 notify(infoPtr, NM_KILLFOCUS);
7361 /* if we have a focus rectagle, get rid of it */
7362 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7364 /* set window focus flag */
7365 infoPtr->bFocus = FALSE;
7367 /* invalidate the selected items before reseting focus flag */
7368 LISTVIEW_InvalidateSelectedItems(infoPtr);
7375 * Processes double click messages (left mouse button).
7378 * [I] infoPtr : valid pointer to the listview structure
7379 * [I] wKey : key flag
7380 * [I] pts : mouse coordinate
7385 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7387 LVHITTESTINFO htInfo;
7389 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7391 /* send NM_RELEASEDCAPTURE notification */
7392 notify(infoPtr, NM_RELEASEDCAPTURE);
7394 htInfo.pt.x = pts.x;
7395 htInfo.pt.y = pts.y;
7397 /* send NM_DBLCLK notification */
7398 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7399 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7401 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7402 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7409 * Processes mouse down messages (left mouse button).
7412 * [I] infoPtr : valid pointer to the listview structure
7413 * [I] wKey : key flag
7414 * [I] pts : mouse coordinate
7419 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7421 LVHITTESTINFO lvHitTestInfo;
7422 static BOOL bGroupSelect = TRUE;
7423 POINT pt = { pts.x, pts.y };
7426 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7428 /* send NM_RELEASEDCAPTURE notification */
7429 notify(infoPtr, NM_RELEASEDCAPTURE);
7431 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7433 /* set left button down flag */
7434 infoPtr->bLButtonDown = TRUE;
7436 lvHitTestInfo.pt.x = pts.x;
7437 lvHitTestInfo.pt.y = pts.y;
7439 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7440 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7441 infoPtr->nEditLabelItem = -1;
7442 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7444 if (infoPtr->dwStyle & LVS_SINGLESEL)
7446 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7447 infoPtr->nEditLabelItem = nItem;
7449 LISTVIEW_SetSelection(infoPtr, nItem);
7453 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7457 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7458 LISTVIEW_SetItemFocus(infoPtr, nItem);
7459 infoPtr->nSelectionMark = nItem;
7465 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7466 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7468 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7469 infoPtr->nSelectionMark = nItem;
7472 else if (wKey & MK_CONTROL)
7476 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7478 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7479 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7480 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7481 infoPtr->nSelectionMark = nItem;
7483 else if (wKey & MK_SHIFT)
7485 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7489 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7490 infoPtr->nEditLabelItem = nItem;
7492 /* set selection (clears other pre-existing selections) */
7493 LISTVIEW_SetSelection(infoPtr, nItem);
7499 /* remove all selections */
7500 LISTVIEW_DeselectAll(infoPtr);
7508 * Processes mouse up messages (left mouse button).
7511 * [I] infoPtr : valid pointer to the listview structure
7512 * [I] wKey : key flag
7513 * [I] pts : mouse coordinate
7518 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7520 LVHITTESTINFO lvHitTestInfo;
7522 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7524 if (!infoPtr->bLButtonDown) return 0;
7526 lvHitTestInfo.pt.x = pts.x;
7527 lvHitTestInfo.pt.y = pts.y;
7529 /* send NM_CLICK notification */
7530 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7531 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7533 /* set left button flag */
7534 infoPtr->bLButtonDown = FALSE;
7536 /* if we clicked on a selected item, edit the label */
7537 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7538 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7545 * Destroys the listview control (called after WM_DESTROY).
7548 * [I] infoPtr : valid pointer to the listview structure
7553 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7557 /* delete all items */
7558 LISTVIEW_DeleteAllItems(infoPtr);
7560 /* destroy data structure */
7561 DPA_Destroy(infoPtr->hdpaItems);
7562 if (infoPtr->selectionRanges) ranges_destroy(infoPtr->selectionRanges);
7564 /* destroy image lists */
7565 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7567 if (infoPtr->himlNormal)
7568 ImageList_Destroy(infoPtr->himlNormal);
7569 if (infoPtr->himlSmall)
7570 ImageList_Destroy(infoPtr->himlSmall);
7571 if (infoPtr->himlState)
7572 ImageList_Destroy(infoPtr->himlState);
7575 /* destroy font, bkgnd brush */
7577 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7578 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7580 /* free listview info pointer*/
7581 COMCTL32_Free(infoPtr);
7583 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7589 * Handles notifications from header.
7592 * [I] infoPtr : valid pointer to the listview structure
7593 * [I] nCtrlId : control identifier
7594 * [I] lpnmh : notification information
7599 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, LPNMHEADERW lpnmh)
7601 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7603 TRACE("(lpnmh=%p)\n", lpnmh);
7605 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7607 switch (lpnmh->hdr.code)
7611 case HDN_ITEMCHANGEDW:
7612 case HDN_ITEMCHANGEDA:
7614 COLUMN_INFO *lpColumnInfo;
7617 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7621 hdi.mask = HDI_WIDTH;
7622 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7626 cxy = lpnmh->pitem->cxy;
7628 /* determine how much we change since the last know position */
7629 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7630 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7633 lpColumnInfo->rcHeader.right += dx;
7634 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7635 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, lpnmh->iItem);
7640 case HDN_ITEMCLICKW:
7641 case HDN_ITEMCLICKA:
7643 /* Handle sorting by Header Column */
7646 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7648 nmlv.iSubItem = lpnmh->iItem;
7649 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7659 * Determines the type of structure to use.
7662 * [I] infoPtr : valid pointer to the listview structureof the sender
7663 * [I] hwndFrom : listview window handle
7664 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7669 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7671 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
7673 if (nCommand != NF_REQUERY) return 0;
7675 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
7682 * Paints/Repaints the listview control.
7685 * [I] infoPtr : valid pointer to the listview structure
7686 * [I] hdc : device context handle
7691 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7693 TRACE("(hdc=%p)\n", hdc);
7695 if (infoPtr->bFirstPaint)
7697 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7699 infoPtr->bFirstPaint = FALSE;
7700 LISTVIEW_UpdateSize(infoPtr);
7701 LISTVIEW_UpdateItemSize(infoPtr);
7702 if (uView == LVS_ICON || uView == LVS_SMALLICON)
7703 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7704 LISTVIEW_UpdateScroll(infoPtr);
7707 LISTVIEW_Refresh(infoPtr, hdc);
7712 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7714 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7715 LISTVIEW_Refresh(infoPtr, hdc);
7716 EndPaint(infoPtr->hwndSelf, &ps);
7724 * Processes double click messages (right mouse button).
7727 * [I] infoPtr : valid pointer to the listview structure
7728 * [I] wKey : key flag
7729 * [I] pts : mouse coordinate
7734 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7736 LVHITTESTINFO lvHitTestInfo;
7738 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7740 /* send NM_RELEASEDCAPTURE notification */
7741 notify(infoPtr, NM_RELEASEDCAPTURE);
7743 /* send NM_RDBLCLK notification */
7744 lvHitTestInfo.pt.x = pts.x;
7745 lvHitTestInfo.pt.y = pts.y;
7746 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7747 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7754 * Processes mouse down messages (right mouse button).
7757 * [I] infoPtr : valid pointer to the listview structure
7758 * [I] wKey : key flag
7759 * [I] pts : mouse coordinate
7764 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7766 LVHITTESTINFO lvHitTestInfo;
7769 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7771 /* send NM_RELEASEDCAPTURE notification */
7772 notify(infoPtr, NM_RELEASEDCAPTURE);
7774 /* make sure the listview control window has the focus */
7775 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7777 /* set right button down flag */
7778 infoPtr->bRButtonDown = TRUE;
7780 /* determine the index of the selected item */
7781 lvHitTestInfo.pt.x = pts.x;
7782 lvHitTestInfo.pt.y = pts.y;
7783 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7785 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7787 LISTVIEW_SetItemFocus(infoPtr, nItem);
7788 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7789 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7790 LISTVIEW_SetSelection(infoPtr, nItem);
7794 LISTVIEW_DeselectAll(infoPtr);
7802 * Processes mouse up messages (right mouse button).
7805 * [I] infoPtr : valid pointer to the listview structure
7806 * [I] wKey : key flag
7807 * [I] pts : mouse coordinate
7812 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7814 LVHITTESTINFO lvHitTestInfo;
7817 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7819 if (!infoPtr->bRButtonDown) return 0;
7821 /* set button flag */
7822 infoPtr->bRButtonDown = FALSE;
7824 /* Send NM_RClICK notification */
7825 lvHitTestInfo.pt.x = pts.x;
7826 lvHitTestInfo.pt.y = pts.y;
7827 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7828 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7830 /* Change to screen coordinate for WM_CONTEXTMENU */
7831 pt = lvHitTestInfo.pt;
7832 ClientToScreen(infoPtr->hwndSelf, &pt);
7834 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7835 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7836 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7847 * [I] infoPtr : valid pointer to the listview structure
7848 * [I] hwnd : window handle of window containing the cursor
7849 * [I] nHittest : hit-test code
7850 * [I] wMouseMsg : ideintifier of the mouse message
7853 * TRUE if cursor is set
7856 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7858 LVHITTESTINFO lvHitTestInfo;
7860 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7862 if(!infoPtr->hHotCursor) return FALSE;
7864 GetCursorPos(&lvHitTestInfo.pt);
7865 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7867 SetCursor(infoPtr->hHotCursor);
7877 * [I] infoPtr : valid pointer to the listview structure
7878 * [I] hwndLoseFocus : handle of previously focused window
7883 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7885 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
7887 /* if we have the focus already, there's nothing to do */
7888 if (infoPtr->bFocus) return 0;
7890 /* send NM_SETFOCUS notification */
7891 notify(infoPtr, NM_SETFOCUS);
7893 /* set window focus flag */
7894 infoPtr->bFocus = TRUE;
7896 /* put the focus rect back on */
7897 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7899 /* redraw all visible selected items */
7900 LISTVIEW_InvalidateSelectedItems(infoPtr);
7910 * [I] infoPtr : valid pointer to the listview structure
7911 * [I] fRedraw : font handle
7912 * [I] fRedraw : redraw flag
7917 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7919 HFONT oldFont = infoPtr->hFont;
7921 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
7923 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7924 if (infoPtr->hFont == oldFont) return 0;
7926 LISTVIEW_SaveTextMetrics(infoPtr);
7928 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7929 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7931 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7938 * Message handling for WM_SETREDRAW.
7939 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7942 * [I] infoPtr : valid pointer to the listview structure
7943 * [I] bRedraw: state of redraw flag
7946 * DefWinProc return value
7948 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7950 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7952 infoPtr->bRedraw = bRedraw;
7954 if(!bRedraw) return 0;
7956 LISTVIEW_UpdateSize(infoPtr);
7957 if (uView == LVS_ICON || uView == LVS_SMALLICON)
7958 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7959 LISTVIEW_UpdateScroll(infoPtr);
7960 LISTVIEW_InvalidateList(infoPtr);
7967 * Resizes the listview control. This function processes WM_SIZE
7968 * messages. At this time, the width and height are not used.
7971 * [I] infoPtr : valid pointer to the listview structure
7972 * [I] Width : new width
7973 * [I] Height : new height
7978 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
7980 TRACE("(width=%d, height=%d)\n", Width, Height);
7982 if (!is_redrawing(infoPtr)) return 0;
7984 if (!LISTVIEW_UpdateSize(infoPtr)) return 0;
7986 if (is_autoarrange(infoPtr))
7987 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7989 LISTVIEW_UpdateScroll(infoPtr);
7991 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7998 * Sets the size information.
8001 * [I] infoPtr : valid pointer to the listview structure
8004 * Zero if no size change
8007 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8009 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8013 GetClientRect(infoPtr->hwndSelf, &rcList);
8014 CopyRect(&rcOld,&(infoPtr->rcList));
8015 infoPtr->rcList.left = 0;
8016 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8017 infoPtr->rcList.top = 0;
8018 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8020 if (uView == LVS_LIST)
8022 /* Apparently the "LIST" style is supposed to have the same
8023 * number of items in a column even if there is no scroll bar.
8024 * Since if a scroll bar already exists then the bottom is already
8025 * reduced, only reduce if the scroll bar does not currently exist.
8026 * The "2" is there to mimic the native control. I think it may be
8027 * related to either padding or edges. (GLA 7/2002)
8029 if (!(infoPtr->dwStyle & WS_HSCROLL))
8031 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8032 if (infoPtr->rcList.bottom > nHScrollHeight)
8033 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8037 if (infoPtr->rcList.bottom > 2)
8038 infoPtr->rcList.bottom -= 2;
8041 else if (uView == LVS_REPORT)
8048 Header_Layout(infoPtr->hwndHeader, &hl);
8050 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8052 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
8053 infoPtr->rcList.top = max(wp.cy, 0);
8055 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8060 * Processes WM_STYLECHANGED messages.
8063 * [I] infoPtr : valid pointer to the listview structure
8064 * [I] wStyleType : window style type (normal or extended)
8065 * [I] lpss : window style information
8070 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8073 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8074 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8076 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8077 wStyleType, lpss->styleOld, lpss->styleNew);
8079 if (wStyleType != GWL_STYLE) return 0;
8081 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8082 /* what if LVS_OWNERDATA changed? */
8083 /* or LVS_SINGLESEL */
8084 /* or LVS_SORT{AS,DES}CENDING */
8086 infoPtr->dwStyle = lpss->styleNew;
8088 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8089 ((lpss->styleNew & WS_HSCROLL) == 0))
8090 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8092 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8093 ((lpss->styleNew & WS_VSCROLL) == 0))
8094 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8096 if (uNewView != uOldView)
8098 SIZE oldIconSize = infoPtr->iconSize;
8101 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8102 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8104 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8105 SetRectEmpty(&infoPtr->rcFocus);
8107 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8108 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8110 if (uNewView == LVS_ICON)
8112 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8114 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8115 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8116 LISTVIEW_SetIconSpacing(infoPtr, 0);
8119 else if (uNewView == LVS_REPORT)
8124 hl.prc = &infoPtr->rcList;
8126 Header_Layout(infoPtr->hwndHeader, &hl);
8127 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8130 LISTVIEW_UpdateItemSize(infoPtr);
8133 if (uNewView == LVS_REPORT)
8134 ShowWindow(infoPtr->hwndHeader, (LVS_NOCOLUMNHEADER & lpss->styleNew) ? SW_HIDE : SW_SHOWNORMAL);
8136 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8137 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8138 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8140 /* update the size of the client area */
8141 LISTVIEW_UpdateSize(infoPtr);
8143 /* add scrollbars if needed */
8144 LISTVIEW_UpdateScroll(infoPtr);
8146 /* invalidate client area + erase background */
8147 LISTVIEW_InvalidateList(infoPtr);
8154 * Window procedure of the listview control.
8157 static LRESULT WINAPI
8158 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8160 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8162 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8164 if (!infoPtr && (uMsg != WM_CREATE))
8165 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8169 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8174 case LVM_APPROXIMATEVIEWRECT:
8175 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8176 LOWORD(lParam), HIWORD(lParam));
8178 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8180 /* case LVM_CANCELEDITLABEL: */
8182 /* case LVM_CREATEDRAGIMAGE: */
8184 case LVM_DELETEALLITEMS:
8185 return LISTVIEW_DeleteAllItems(infoPtr);
8187 case LVM_DELETECOLUMN:
8188 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8190 case LVM_DELETEITEM:
8191 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8193 case LVM_EDITLABELW:
8194 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8196 case LVM_EDITLABELA:
8197 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8199 /* case LVM_ENABLEGROUPVIEW: */
8201 case LVM_ENSUREVISIBLE:
8202 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8205 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8208 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8210 case LVM_GETBKCOLOR:
8211 return infoPtr->clrBk;
8213 /* case LVM_GETBKIMAGE: */
8215 case LVM_GETCALLBACKMASK:
8216 return infoPtr->uCallbackMask;
8218 case LVM_GETCOLUMNA:
8219 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8221 case LVM_GETCOLUMNW:
8222 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8224 case LVM_GETCOLUMNORDERARRAY:
8225 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8227 case LVM_GETCOLUMNWIDTH:
8228 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8230 case LVM_GETCOUNTPERPAGE:
8231 return LISTVIEW_GetCountPerPage(infoPtr);
8233 case LVM_GETEDITCONTROL:
8234 return (LRESULT)infoPtr->hwndEdit;
8236 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8237 return infoPtr->dwLvExStyle;
8239 /* case LVM_GETGROUPINFO: */
8241 /* case LVM_GETGROUPMETRICS: */
8244 return (LRESULT)infoPtr->hwndHeader;
8246 case LVM_GETHOTCURSOR:
8247 return (LRESULT)infoPtr->hHotCursor;
8249 case LVM_GETHOTITEM:
8250 return infoPtr->nHotItem;
8252 case LVM_GETHOVERTIME:
8253 return infoPtr->dwHoverTime;
8255 case LVM_GETIMAGELIST:
8256 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8258 /* case LVM_GETINSERTMARK: */
8260 /* case LVM_GETINSERTMARKCOLOR: */
8262 /* case LVM_GETINSERTMARKRECT: */
8264 case LVM_GETISEARCHSTRINGA:
8265 case LVM_GETISEARCHSTRINGW:
8266 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8270 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8273 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8275 case LVM_GETITEMCOUNT:
8276 return infoPtr->nItemCount;
8278 case LVM_GETITEMPOSITION:
8279 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8281 case LVM_GETITEMRECT:
8282 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8284 case LVM_GETITEMSPACING:
8285 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8287 case LVM_GETITEMSTATE:
8288 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8290 case LVM_GETITEMTEXTA:
8291 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8293 case LVM_GETITEMTEXTW:
8294 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8296 case LVM_GETNEXTITEM:
8297 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8299 case LVM_GETNUMBEROFWORKAREAS:
8300 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8304 if (!lParam) return FALSE;
8305 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8308 /* case LVM_GETOUTLINECOLOR: */
8310 /* case LVM_GETSELECTEDCOLUMN: */
8312 case LVM_GETSELECTEDCOUNT:
8313 return LISTVIEW_GetSelectedCount(infoPtr);
8315 case LVM_GETSELECTIONMARK:
8316 return infoPtr->nSelectionMark;
8318 case LVM_GETSTRINGWIDTHA:
8319 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8321 case LVM_GETSTRINGWIDTHW:
8322 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8324 case LVM_GETSUBITEMRECT:
8325 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8327 case LVM_GETTEXTBKCOLOR:
8328 return infoPtr->clrTextBk;
8330 case LVM_GETTEXTCOLOR:
8331 return infoPtr->clrText;
8333 /* case LVM_GETTILEINFO: */
8335 /* case LVM_GETTILEVIEWINFO: */
8337 case LVM_GETTOOLTIPS:
8338 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8341 case LVM_GETTOPINDEX:
8342 return LISTVIEW_GetTopIndex(infoPtr);
8344 /*case LVM_GETUNICODEFORMAT:
8345 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8348 /* case LVM_GETVIEW: */
8350 case LVM_GETVIEWRECT:
8351 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8353 case LVM_GETWORKAREAS:
8354 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8357 /* case LVM_HASGROUP: */
8360 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8362 case LVM_INSERTCOLUMNA:
8363 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8365 case LVM_INSERTCOLUMNW:
8366 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8368 /* case LVM_INSERTGROUP: */
8370 /* case LVM_INSERTGROUPSORTED: */
8372 case LVM_INSERTITEMA:
8373 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8375 case LVM_INSERTITEMW:
8376 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8378 /* case LVM_INSERTMARKHITTEST: */
8380 /* case LVM_ISGROUPVIEWENABLED: */
8382 /* case LVM_MAPIDTOINDEX: */
8384 /* case LVM_MAPINDEXTOID: */
8386 /* case LVM_MOVEGROUP: */
8388 /* case LVM_MOVEITEMTOGROUP: */
8390 case LVM_REDRAWITEMS:
8391 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8393 /* case LVM_REMOVEALLGROUPS: */
8395 /* case LVM_REMOVEGROUP: */
8398 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8400 case LVM_SETBKCOLOR:
8401 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8403 /* case LVM_SETBKIMAGE: */
8405 case LVM_SETCALLBACKMASK:
8406 infoPtr->uCallbackMask = (UINT)wParam;
8409 case LVM_SETCOLUMNA:
8410 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8412 case LVM_SETCOLUMNW:
8413 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8415 case LVM_SETCOLUMNORDERARRAY:
8416 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8418 case LVM_SETCOLUMNWIDTH:
8419 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8421 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8422 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8424 /* case LVM_SETGROUPINFO: */
8426 /* case LVM_SETGROUPMETRICS: */
8428 case LVM_SETHOTCURSOR:
8429 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8431 case LVM_SETHOTITEM:
8432 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8434 case LVM_SETHOVERTIME:
8435 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8437 case LVM_SETICONSPACING:
8438 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8440 case LVM_SETIMAGELIST:
8441 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8443 /* case LVM_SETINFOTIP: */
8445 /* case LVM_SETINSERTMARK: */
8447 /* case LVM_SETINSERTMARKCOLOR: */
8450 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8453 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8455 case LVM_SETITEMCOUNT:
8456 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8458 case LVM_SETITEMPOSITION:
8460 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8461 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8464 case LVM_SETITEMPOSITION32:
8465 if (lParam == 0) return FALSE;
8466 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8468 case LVM_SETITEMSTATE:
8469 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8471 case LVM_SETITEMTEXTA:
8472 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8474 case LVM_SETITEMTEXTW:
8475 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8477 /* case LVM_SETOUTLINECOLOR: */
8479 /* case LVM_SETSELECTEDCOLUMN: */
8481 case LVM_SETSELECTIONMARK:
8482 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8484 case LVM_SETTEXTBKCOLOR:
8485 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8487 case LVM_SETTEXTCOLOR:
8488 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8490 /* case LVM_SETTILEINFO: */
8492 /* case LVM_SETTILEVIEWINFO: */
8494 /* case LVM_SETTILEWIDTH: */
8496 /* case LVM_SETTOOLTIPS: */
8498 /* case LVM_SETUNICODEFORMAT: */
8500 /* case LVM_SETVIEW: */
8502 /* case LVM_SETWORKAREAS: */
8504 /* case LVM_SORTGROUPS: */
8507 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8509 /* LVM_SORTITEMSEX: */
8511 case LVM_SUBITEMHITTEST:
8512 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8515 return LISTVIEW_Update(infoPtr, (INT)wParam);
8518 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8521 return LISTVIEW_Command(infoPtr, wParam, lParam);
8524 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8527 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8530 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8533 return (LRESULT)infoPtr->hFont;
8536 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8539 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8542 return LISTVIEW_KillFocus(infoPtr);
8544 case WM_LBUTTONDBLCLK:
8545 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8547 case WM_LBUTTONDOWN:
8548 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8551 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8554 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8557 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8560 return LISTVIEW_NCDestroy(infoPtr);
8563 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8564 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8567 case WM_NOTIFYFORMAT:
8568 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8571 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8573 case WM_RBUTTONDBLCLK:
8574 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8576 case WM_RBUTTONDOWN:
8577 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8580 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8583 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8588 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8591 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8594 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8597 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8599 case WM_STYLECHANGED:
8600 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8602 case WM_SYSCOLORCHANGE:
8603 COMCTL32_RefreshSysColors();
8606 /* case WM_TIMER: */
8609 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8612 if (wParam & (MK_SHIFT | MK_CONTROL))
8613 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8614 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8616 case WM_WINDOWPOSCHANGED:
8617 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
8619 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8620 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8621 /* FIXME: why do we need this here? */
8622 LISTVIEW_UpdateSize(infoPtr);
8623 LISTVIEW_UpdateScroll(infoPtr);
8625 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8627 /* case WM_WININICHANGE: */
8630 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8631 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8634 /* call default window procedure */
8635 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8643 * Registers the window class.
8651 void LISTVIEW_Register(void)
8655 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8656 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8657 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8658 wndClass.cbClsExtra = 0;
8659 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8660 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8661 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8662 wndClass.lpszClassName = WC_LISTVIEWW;
8663 RegisterClassW(&wndClass);
8668 * Unregisters the window class.
8676 void LISTVIEW_Unregister(void)
8678 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8683 * Handle any WM_COMMAND messages
8686 * [I] infoPtr : valid pointer to the listview structure
8687 * [I] wParam : the first message parameter
8688 * [I] lParam : the second message parameter
8693 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8695 switch (HIWORD(wParam))
8700 * Adjust the edit window size
8703 HDC hdc = GetDC(infoPtr->hwndEdit);
8704 HFONT hFont, hOldFont = 0;
8709 if (!infoPtr->hwndEdit || !hdc) return 0;
8710 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8711 GetWindowRect(infoPtr->hwndEdit, &rect);
8713 /* Select font to get the right dimension of the string */
8714 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8717 hOldFont = SelectObject(hdc, hFont);
8720 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8722 TEXTMETRICW textMetric;
8724 /* Add Extra spacing for the next character */
8725 GetTextMetricsW(hdc, &textMetric);
8726 sz.cx += (textMetric.tmMaxCharWidth * 2);
8734 rect.bottom - rect.top,
8735 SWP_DRAWFRAME|SWP_NOMOVE);
8738 SelectObject(hdc, hOldFont);
8740 ReleaseDC(infoPtr->hwndSelf, hdc);
8746 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8755 * Subclassed edit control windproc function
8758 * [I] hwnd : the edit window handle
8759 * [I] uMsg : the message that is to be processed
8760 * [I] wParam : first message parameter
8761 * [I] lParam : second message parameter
8762 * [I] isW : TRUE if input is Unicode
8767 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
8769 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8770 BOOL cancel = FALSE;
8772 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8773 hwnd, uMsg, wParam, lParam, isW);
8778 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8785 WNDPROC editProc = infoPtr->EditWndProc;
8786 infoPtr->EditWndProc = 0;
8787 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8788 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8792 if (VK_ESCAPE == (INT)wParam)
8797 else if (VK_RETURN == (INT)wParam)
8801 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8805 if (infoPtr->hwndEdit)
8807 LPWSTR buffer = NULL;
8809 infoPtr->hwndEdit = 0;
8812 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8816 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8818 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8819 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8823 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8825 if (buffer) COMCTL32_Free(buffer);
8829 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8835 * Subclassed edit control Unicode windproc function
8838 * [I] hwnd : the edit window handle
8839 * [I] uMsg : the message that is to be processed
8840 * [I] wParam : first message parameter
8841 * [I] lParam : second message parameter
8845 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8847 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8852 * Subclassed edit control ANSI windproc function
8855 * [I] hwnd : the edit window handle
8856 * [I] uMsg : the message that is to be processed
8857 * [I] wParam : first message parameter
8858 * [I] lParam : second message parameter
8862 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8864 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8869 * Creates a subclassed edit cotrol
8872 * [I] infoPtr : valid pointer to the listview structure
8873 * [I] text : initial text for the edit
8874 * [I] style : the window style
8875 * [I] isW : TRUE if input is Unicode
8879 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8880 INT x, INT y, INT width, INT height, BOOL isW)
8882 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8887 TEXTMETRICW textMetric;
8888 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8890 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8892 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8893 hdc = GetDC(infoPtr->hwndSelf);
8895 /* Select the font to get appropriate metric dimensions */
8896 if(infoPtr->hFont != 0)
8897 hOldFont = SelectObject(hdc, infoPtr->hFont);
8899 /*Get String Lenght in pixels */
8900 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8902 /*Add Extra spacing for the next character */
8903 GetTextMetricsW(hdc, &textMetric);
8904 sz.cx += (textMetric.tmMaxCharWidth * 2);
8906 if(infoPtr->hFont != 0)
8907 SelectObject(hdc, hOldFont);
8909 ReleaseDC(infoPtr->hwndSelf, hdc);
8911 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8913 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8915 if (!hedit) return 0;
8917 infoPtr->EditWndProc = (WNDPROC)
8918 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8919 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8921 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);