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 believe 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, we would send LVN_ODSTATECHANGED
45 * -- LVA_SNAPTOGRID not implemented
46 * -- LISTVIEW_ApproximateViewRect partially implemented
47 * -- LISTVIEW_[GS]etColumnOrderArray stubs
48 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
49 * -- LISTVIEW_SetIconSpacing is incomplete
50 * -- LISTVIEW_SortItems is broken
51 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
54 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
55 * linear in the number of items in the list, and this is
56 * unacceptable for large lists.
57 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
58 * instead of inserting in the right spot
59 * -- we should keep an ordered array of coordinates in iconic mode
60 * this would allow to frame items (iterator_frameditems),
61 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
69 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
76 * -- LVS_NOSCROLL (see Q137520)
77 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
80 * -- LVS_EX_BORDERSELECT
83 * -- LVS_EX_HEADERDRAGDROP
86 * -- LVS_EX_MULTIWORKAREAS
87 * -- LVS_EX_ONECLICKACTIVATE
89 * -- LVS_EX_SIMPLESELECT
90 * -- LVS_EX_TRACKSELECT
91 * -- LVS_EX_TWOCLICKACTIVATE
92 * -- LVS_EX_UNDERLINECOLD
93 * -- LVS_EX_UNDERLINEHOT
96 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
101 * -- LVN_ODSTATECHANGED
106 * -- LVM_CANCELEDITLABEL
107 * -- LVM_ENABLEGROUPVIEW
108 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
109 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
110 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
111 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
112 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
113 * -- LVM_GETINSERTMARKRECT
114 * -- LVM_GETNUMBEROFWORKAREAS
115 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
116 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
117 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
118 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
119 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
120 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
121 * -- LVM_GETVIEW, LVM_SETVIEW
122 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
123 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
124 * -- LVM_INSERTGROUPSORTED
125 * -- LVM_INSERTMARKHITTEST
126 * -- LVM_ISGROUPVIEWENABLED
127 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
129 * -- LVM_MOVEITEMTOGROUP
131 * -- LVM_SETTILEWIDTH
135 * Known differences in message stream from native control (not known if
136 * these differences cause problems):
137 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
138 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
139 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
140 * processing for "USEDOUBLECLICKTIME".
144 #include "wine/port.h"
159 #include "commctrl.h"
160 #include "comctl32.h"
162 #include "wine/debug.h"
163 #include "wine/unicode.h"
165 WINE_DEFAULT_DEBUG_CHANNEL(listview);
167 /* make sure you set this to 0 for production use! */
168 #define DEBUG_RANGES 1
170 typedef struct tagCOLUMN_INFO
172 RECT rcHeader; /* tracks the header's rectangle */
173 int fmt; /* same as LVCOLUMN.fmt */
176 typedef struct tagITEMHDR
180 } ITEMHDR, *LPITEMHDR;
182 typedef struct tagSUBITEM_INFO
188 typedef struct tagITEM_INFO
196 typedef struct tagRANGE
202 typedef struct tagRANGES
207 typedef struct tagITERATOR
216 typedef struct tagLISTVIEW_INFO
223 COLORREF clrTextBkDefault;
224 HIMAGELIST himlNormal;
225 HIMAGELIST himlSmall;
226 HIMAGELIST himlState;
229 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
232 RANGES selectionRanges;
237 RECT rcList; /* This rectangle is really the window
238 * client rectangle possibly reduced by the
239 * horizontal scroll bar and/or header - see
240 * LISTVIEW_UpdateSize. This rectangle offset
241 * by the LISTVIEW_GetOrigin value is in
242 * client coordinates */
251 INT ntmHeight; /* Some cached metrics of the font used */
252 INT ntmAveCharWidth; /* by the listview to draw items */
253 BOOL bRedraw; /* Turns on/off repaints & invalidations */
254 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
256 BOOL bDoChangeNotify; /* send change notification messages? */
259 DWORD dwStyle; /* the cached window GWL_STYLE */
260 DWORD dwLvExStyle; /* extended listview style */
261 INT nItemCount; /* the number of items in the list */
262 HDPA hdpaItems; /* array ITEM_INFO pointers */
263 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
264 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
265 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
266 POINT currIconPos; /* this is the position next icon will be placed */
267 PFNLVCOMPARE pfnCompare;
275 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
277 DWORD lastKeyPressTimestamp;
279 INT nSearchParamLength;
280 WCHAR szSearchParam[ MAX_PATH ];
287 /* How many we debug buffer to allocate */
288 #define DEBUG_BUFFERS 20
289 /* The size of a single debug bbuffer */
290 #define DEBUG_BUFFER_SIZE 256
292 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
293 #define SB_INTERNAL -1
295 /* maximum size of a label */
296 #define DISP_TEXT_SIZE 512
298 /* padding for items in list and small icon display modes */
299 #define WIDTH_PADDING 12
301 /* padding for items in list, report and small icon display modes */
302 #define HEIGHT_PADDING 1
304 /* offset of items in report display mode */
305 #define REPORT_MARGINX 2
307 /* padding for icon in large icon display mode
308 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
309 * that HITTEST will see.
310 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
311 * ICON_TOP_PADDING - sum of the two above.
312 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
313 * LABEL_HOR_PADDING - between text and sides of box
314 * LABEL_VERT_PADDING - between bottom of text and end of box
316 * ICON_LR_PADDING - additional width above icon size.
317 * ICON_LR_HALF - half of the above value
319 #define ICON_TOP_PADDING_NOTHITABLE 2
320 #define ICON_TOP_PADDING_HITABLE 2
321 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
322 #define ICON_BOTTOM_PADDING 4
323 #define LABEL_HOR_PADDING 5
324 #define LABEL_VERT_PADDING 7
325 #define ICON_LR_PADDING 16
326 #define ICON_LR_HALF (ICON_LR_PADDING/2)
328 /* default label width for items in list and small icon display modes */
329 #define DEFAULT_LABEL_WIDTH 40
331 /* default column width for items in list display mode */
332 #define DEFAULT_COLUMN_WIDTH 128
334 /* Size of "line" scroll for V & H scrolls */
335 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
337 /* Padding betwen image and label */
338 #define IMAGE_PADDING 2
340 /* Padding behind the label */
341 #define TRAILING_LABEL_PADDING 12
342 #define TRAILING_HEADER_PADDING 11
344 /* Border for the icon caption */
345 #define CAPTION_BORDER 2
347 /* Standard DrawText flags */
348 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
349 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
350 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
352 /* The time in milliseconds to reset the search in the list */
353 #define KEY_DELAY 450
355 /* Dump the LISTVIEW_INFO structure to the debug channel */
356 #define LISTVIEW_DUMP(iP) do { \
357 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
358 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
359 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
360 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
361 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
362 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
363 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
364 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
365 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
366 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
370 * forward declarations
372 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
373 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
374 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
375 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
376 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
377 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
378 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
379 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
380 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
381 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
382 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
383 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
384 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
385 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
386 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
387 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
388 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
389 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
390 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
391 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
392 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
393 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
394 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
395 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
396 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
397 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
399 /******** Text handling functions *************************************/
401 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
402 * text string. The string may be ANSI or Unicode, in which case
403 * the boolean isW tells us the type of the string.
405 * The name of the function tell what type of strings it expects:
406 * W: Unicode, T: ANSI/Unicode - function of isW
409 static inline BOOL is_textW(LPCWSTR text)
411 return text != NULL && text != LPSTR_TEXTCALLBACKW;
414 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
416 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
417 return is_textW(text);
420 static inline int textlenT(LPCWSTR text, BOOL isW)
422 return !is_textT(text, isW) ? 0 :
423 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
426 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
429 if (isSrcW) lstrcpynW(dest, src, max);
430 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
432 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
433 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
436 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
438 LPWSTR wstr = (LPWSTR)text;
440 if (!isW && is_textT(text, isW))
442 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
443 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
444 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
446 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
450 static inline void textfreeT(LPWSTR wstr, BOOL isW)
452 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
456 * dest is a pointer to a Unicode string
457 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
459 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
463 if (src == LPSTR_TEXTCALLBACKW)
465 if (is_textW(*dest)) Free(*dest);
466 *dest = LPSTR_TEXTCALLBACKW;
470 LPWSTR pszText = textdupTtoW(src, isW);
471 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
472 bResult = Str_SetPtrW(dest, pszText);
473 textfreeT(pszText, isW);
479 * compares a Unicode to a Unicode/ANSI text string
481 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
483 if (!aw) return bt ? -1 : 0;
484 if (!bt) return aw ? 1 : 0;
485 if (aw == LPSTR_TEXTCALLBACKW)
486 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
487 if (bt != LPSTR_TEXTCALLBACKW)
489 LPWSTR bw = textdupTtoW(bt, isW);
490 int r = bw ? lstrcmpW(aw, bw) : 1;
498 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
502 n = min(min(n, strlenW(s1)), strlenW(s2));
503 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
504 return res ? res - sizeof(WCHAR) : res;
507 /******** Debugging functions *****************************************/
509 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
511 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
512 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
515 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
517 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
518 n = min(textlenT(text, isW), n);
519 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
522 static char* debug_getbuf()
524 static int index = 0;
525 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
526 return buffers[index++ % DEBUG_BUFFERS];
529 static inline const char* debugrange(const RANGE *lprng)
533 char* buf = debug_getbuf();
534 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
536 } else return "(null)";
539 static inline const char* debugpoint(const POINT *lppt)
543 char* buf = debug_getbuf();
544 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
546 } else return "(null)";
549 static inline const char* debugrect(const RECT *rect)
553 char* buf = debug_getbuf();
554 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
555 rect->left, rect->top, rect->right, rect->bottom);
557 } else return "(null)";
560 static const char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
562 char* buf = debug_getbuf(), *text = buf;
563 int len, size = DEBUG_BUFFER_SIZE;
565 if (pScrollInfo == NULL) return "(null)";
566 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
567 if (len == -1) goto end; buf += len; size -= len;
568 if (pScrollInfo->fMask & SIF_RANGE)
569 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
571 if (len == -1) goto end; buf += len; size -= len;
572 if (pScrollInfo->fMask & SIF_PAGE)
573 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
575 if (len == -1) goto end; buf += len; size -= len;
576 if (pScrollInfo->fMask & SIF_POS)
577 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
579 if (len == -1) goto end; buf += len; size -= len;
580 if (pScrollInfo->fMask & SIF_TRACKPOS)
581 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
583 if (len == -1) goto end; buf += len; size -= len;
586 buf = text + strlen(text);
588 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
592 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
596 char* buf = debug_getbuf();
597 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
598 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
599 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
600 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
602 } else return "(null)";
605 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
607 char* buf = debug_getbuf(), *text = buf;
608 int len, size = DEBUG_BUFFER_SIZE;
610 if (lpLVItem == NULL) return "(null)";
611 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
612 if (len == -1) goto end; buf += len; size -= len;
613 if (lpLVItem->mask & LVIF_STATE)
614 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
616 if (len == -1) goto end; buf += len; size -= len;
617 if (lpLVItem->mask & LVIF_TEXT)
618 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
620 if (len == -1) goto end; buf += len; size -= len;
621 if (lpLVItem->mask & LVIF_IMAGE)
622 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
624 if (len == -1) goto end; buf += len; size -= len;
625 if (lpLVItem->mask & LVIF_PARAM)
626 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
628 if (len == -1) goto end; buf += len; size -= len;
629 if (lpLVItem->mask & LVIF_INDENT)
630 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
632 if (len == -1) goto end; buf += len; size -= len;
635 buf = text + strlen(text);
637 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
641 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
643 char* buf = debug_getbuf(), *text = buf;
644 int len, size = DEBUG_BUFFER_SIZE;
646 if (lpColumn == NULL) return "(null)";
647 len = snprintf(buf, size, "{");
648 if (len == -1) goto end; buf += len; size -= len;
649 if (lpColumn->mask & LVCF_SUBITEM)
650 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
652 if (len == -1) goto end; buf += len; size -= len;
653 if (lpColumn->mask & LVCF_FMT)
654 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpColumn->mask & LVCF_WIDTH)
658 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpColumn->mask & LVCF_TEXT)
662 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpColumn->mask & LVCF_IMAGE)
666 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
668 if (len == -1) goto end; buf += len; size -= len;
669 if (lpColumn->mask & LVCF_ORDER)
670 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
672 if (len == -1) goto end; buf += len; size -= len;
675 buf = text + strlen(text);
677 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
681 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
685 char* buf = debug_getbuf();
686 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
687 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
689 } else return "(null)";
692 /* Return the corresponding text for a given scroll value */
693 static inline LPCSTR debugscrollcode(int nScrollCode)
697 case SB_LINELEFT: return "SB_LINELEFT";
698 case SB_LINERIGHT: return "SB_LINERIGHT";
699 case SB_PAGELEFT: return "SB_PAGELEFT";
700 case SB_PAGERIGHT: return "SB_PAGERIGHT";
701 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
702 case SB_THUMBTRACK: return "SB_THUMBTRACK";
703 case SB_ENDSCROLL: return "SB_ENDSCROLL";
704 case SB_INTERNAL: return "SB_INTERNAL";
705 default: return "unknown";
710 /******** Notification functions i************************************/
712 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
716 TRACE("(code=%d)\n", code);
718 pnmh->hwndFrom = infoPtr->hwndSelf;
719 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
721 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
722 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
724 TRACE(" <= %ld\n", result);
729 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
732 return notify_hdr(infoPtr, code, &nmh);
735 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
746 item.mask = LVIF_PARAM|LVIF_STATE;
747 item.iItem = htInfo->iItem;
749 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
750 nmia.lParam = item.lParam;
751 nmia.uOldState = item.state;
752 nmia.uNewState = item.state | LVIS_ACTIVATING;
753 nmia.uChanged = LVIF_STATE;
756 nmia.iItem = htInfo->iItem;
757 nmia.iSubItem = htInfo->iSubItem;
758 nmia.ptAction = htInfo->pt;
760 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
761 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
762 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
764 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
767 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
769 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
770 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
773 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
778 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
779 ZeroMemory(&nmlv, sizeof(nmlv));
780 nmlv.iItem = lvht->iItem;
781 nmlv.iSubItem = lvht->iSubItem;
782 nmlv.ptAction = lvht->pt;
783 item.mask = LVIF_PARAM;
784 item.iItem = lvht->iItem;
786 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
787 return notify_listview(infoPtr, code, &nmlv);
790 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
795 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
797 item.mask = LVIF_PARAM;
800 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
801 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
804 static int get_ansi_notification(INT unicodeNotificationCode)
806 switch (unicodeNotificationCode)
808 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
809 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
810 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
811 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
812 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
813 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
815 ERR("unknown notification %x\n", unicodeNotificationCode);
821 With testing on Windows 2000 it looks like the notify format
822 has nothing to do with this message. It ALWAYS seems to be
825 infoPtr : listview struct
826 notificationCode : *Unicode* notification code
827 pdi : dispinfo structure (can be unicode or ansi)
828 isW : TRUE if dispinfo is Unicode
830 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
832 BOOL bResult = FALSE;
833 BOOL convertToAnsi = FALSE;
834 INT cchTempBufMax = 0, savCchTextMax = 0;
835 LPWSTR pszTempBuf = NULL, savPszText = NULL;
837 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
842 if (notificationCode != LVN_GETDISPINFOW)
844 cchTempBufMax = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText,
845 -1, NULL, 0, NULL, NULL);
849 cchTempBufMax = pdi->item.cchTextMax;
850 *pdi->item.pszText = 0; /* make sure we don't process garbage */
853 pszTempBuf = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) *
855 if (!pszTempBuf) return FALSE;
857 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR)
858 pszTempBuf, cchTempBufMax, NULL, NULL);
860 savCchTextMax = pdi->item.cchTextMax;
861 savPszText = pdi->item.pszText;
862 pdi->item.pszText = pszTempBuf;
863 pdi->item.cchTextMax = cchTempBufMax;
866 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat !=
869 bResult = notify_hdr(infoPtr, get_ansi_notification(notificationCode),
874 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
875 savPszText, savCchTextMax);
876 pdi->item.pszText = savPszText; /* restores our buffer */
877 pdi->item.cchTextMax = savCchTextMax;
878 HeapFree(GetProcessHeap(), 0, pszTempBuf);
883 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
884 const RECT *rcBounds, const LVITEMW *lplvItem)
886 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
887 lpnmlvcd->nmcd.hdc = hdc;
888 lpnmlvcd->nmcd.rc = *rcBounds;
889 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
890 lpnmlvcd->clrText = infoPtr->clrText;
891 if (!lplvItem) return;
892 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
893 lpnmlvcd->iSubItem = lplvItem->iSubItem;
894 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
895 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
896 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
897 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
900 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
902 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
905 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
906 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
907 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
908 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
909 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
910 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
914 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
916 /* apprently, for selected items, we have to override the returned values */
917 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
921 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
922 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
924 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
926 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
927 lpnmlvcd->clrText = comctl32_color.clrBtnText;
931 /* Set the text attributes */
932 if (lpnmlvcd->clrTextBk != CLR_NONE)
934 SetBkMode(hdc, OPAQUE);
935 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
936 SetBkColor(hdc, infoPtr->clrTextBkDefault);
938 SetBkColor(hdc,lpnmlvcd->clrTextBk);
941 SetBkMode(hdc, TRANSPARENT);
942 SetTextColor(hdc, lpnmlvcd->clrText);
945 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
947 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
950 /******** Item iterator functions **********************************/
952 static RANGES ranges_create(int count);
953 static void ranges_destroy(RANGES ranges);
954 static BOOL ranges_add(RANGES ranges, RANGE range);
955 static BOOL ranges_del(RANGES ranges, RANGE range);
956 static void ranges_dump(RANGES ranges);
958 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
960 RANGE range = { nItem, nItem + 1 };
962 return ranges_add(ranges, range);
965 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
967 RANGE range = { nItem, nItem + 1 };
969 return ranges_del(ranges, range);
973 * ITERATOR DOCUMENTATION
975 * The iterator functions allow for easy, and convenient iteration
976 * over items of iterest in the list. Typically, you create a
977 * iterator, use it, and destroy it, as such:
980 * iterator_xxxitems(&i, ...);
981 * while (iterator_{prev,next}(&i)
983 * //code which uses i.nItem
985 * iterator_destroy(&i);
987 * where xxx is either: framed, or visible.
988 * Note that it is important that the code destroys the iterator
989 * after it's done with it, as the creation of the iterator may
990 * allocate memory, which thus needs to be freed.
992 * You can iterate both forwards, and backwards through the list,
993 * by using iterator_next or iterator_prev respectively.
995 * Lower numbered items are draw on top of higher number items in
996 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
997 * items may overlap). So, to test items, you should use
999 * which lists the items top to bottom (in Z-order).
1000 * For drawing items, you should use
1002 * which lists the items bottom to top (in Z-order).
1003 * If you keep iterating over the items after the end-of-items
1004 * marker (-1) is returned, the iterator will start from the
1005 * beginning. Typically, you don't need to test for -1,
1006 * because iterator_{next,prev} will return TRUE if more items
1007 * are to be iterated over, or FALSE otherwise.
1009 * Note: the iterator is defined to be bidirectional. That is,
1010 * any number of prev followed by any number of next, or
1011 * five versa, should leave the iterator at the same item:
1012 * prev * n, next * n = next * n, prev * n
1014 * The iterator has a notion of a out-of-order, special item,
1015 * which sits at the start of the list. This is used in
1016 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1017 * which needs to be first, as it may overlap other items.
1019 * The code is a bit messy because we have:
1020 * - a special item to deal with
1021 * - simple range, or composite range
1023 * If you find bugs, or want to add features, please make sure you
1024 * always check/modify *both* iterator_prev, and iterator_next.
1028 * This function iterates through the items in increasing order,
1029 * but prefixed by the special item, then -1. That is:
1030 * special, 1, 2, 3, ..., n, -1.
1031 * Each item is listed only once.
1033 static inline BOOL iterator_next(ITERATOR* i)
1037 i->nItem = i->nSpecial;
1038 if (i->nItem != -1) return TRUE;
1040 if (i->nItem == i->nSpecial)
1042 if (i->ranges) i->index = 0;
1048 if (i->nItem == i->nSpecial) i->nItem++;
1049 if (i->nItem < i->range.upper) return TRUE;
1054 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1055 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1058 else if (i->nItem >= i->range.upper) goto end;
1060 i->nItem = i->range.lower;
1061 if (i->nItem >= 0) goto testitem;
1068 * This function iterates through the items in decreasing order,
1069 * followed by the special item, then -1. That is:
1070 * n, n-1, ..., 3, 2, 1, special, -1.
1071 * Each item is listed only once.
1073 static inline BOOL iterator_prev(ITERATOR* i)
1080 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1083 if (i->nItem == i->nSpecial)
1091 if (i->nItem == i->nSpecial) i->nItem--;
1092 if (i->nItem >= i->range.lower) return TRUE;
1098 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1101 else if (!start && i->nItem < i->range.lower) goto end;
1103 i->nItem = i->range.upper;
1104 if (i->nItem > 0) goto testitem;
1106 return (i->nItem = i->nSpecial) != -1;
1109 static RANGE iterator_range(ITERATOR* i)
1113 if (!i->ranges) return i->range;
1115 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1117 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1118 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1120 else range.lower = range.upper = 0;
1126 * Releases resources associated with this ierator.
1128 static inline void iterator_destroy(ITERATOR* i)
1130 ranges_destroy(i->ranges);
1134 * Create an empty iterator.
1136 static inline BOOL iterator_empty(ITERATOR* i)
1138 ZeroMemory(i, sizeof(*i));
1139 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1144 * Create an iterator over a range.
1146 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1154 * Create an iterator over a bunch of ranges.
1155 * Please note that the iterator will take ownership of the ranges,
1156 * and will free them upon destruction.
1158 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1166 * Creates an iterator over the items which intersect lprc.
1168 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1170 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1171 RECT frame = *lprc, rcItem, rcTemp;
1174 /* in case we fail, we want to return an empty iterator */
1175 if (!iterator_empty(i)) return FALSE;
1177 LISTVIEW_GetOrigin(infoPtr, &Origin);
1179 TRACE("(lprc=%s)\n", debugrect(lprc));
1180 OffsetRect(&frame, -Origin.x, -Origin.y);
1182 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1186 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1188 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1189 if (IntersectRect(&rcTemp, &rcItem, lprc))
1190 i->nSpecial = infoPtr->nFocusedItem;
1192 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1193 /* to do better here, we need to have PosX, and PosY sorted */
1194 TRACE("building icon ranges:\n");
1195 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1197 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1198 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1199 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1200 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1201 if (IntersectRect(&rcTemp, &rcItem, &frame))
1202 ranges_additem(i->ranges, nItem);
1206 else if (uView == LVS_REPORT)
1210 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1211 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1213 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1214 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1215 if (range.upper <= range.lower) return TRUE;
1216 if (!iterator_rangeitems(i, range)) return FALSE;
1217 TRACE(" report=%s\n", debugrange(&i->range));
1221 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1222 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1223 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1224 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1225 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1226 INT lower = nFirstCol * nPerCol + nFirstRow;
1230 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1231 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1233 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1235 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1236 TRACE("building list ranges:\n");
1237 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1239 item_range.lower = nCol * nPerCol + nFirstRow;
1240 if(item_range.lower >= infoPtr->nItemCount) break;
1241 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1242 TRACE(" list=%s\n", debugrange(&item_range));
1243 ranges_add(i->ranges, item_range);
1251 * Creates an iterator over the items which intersect the visible region of hdc.
1253 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1255 POINT Origin, Position;
1256 RECT rcItem, rcClip;
1259 rgntype = GetClipBox(hdc, &rcClip);
1260 if (rgntype == NULLREGION) return iterator_empty(i);
1261 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1262 if (rgntype == SIMPLEREGION) return TRUE;
1264 /* first deal with the special item */
1265 if (i->nSpecial != -1)
1267 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1268 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1271 /* if we can't deal with the region, we'll just go with the simple range */
1272 LISTVIEW_GetOrigin(infoPtr, &Origin);
1273 TRACE("building visible range:\n");
1274 if (!i->ranges && i->range.lower < i->range.upper)
1276 if (!(i->ranges = ranges_create(50))) return TRUE;
1277 if (!ranges_add(i->ranges, i->range))
1279 ranges_destroy(i->ranges);
1285 /* now delete the invisible items from the list */
1286 while(iterator_next(i))
1288 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1289 rcItem.left = Position.x + Origin.x;
1290 rcItem.top = Position.y + Origin.y;
1291 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1292 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1293 if (!RectVisible(hdc, &rcItem))
1294 ranges_delitem(i->ranges, i->nItem);
1296 /* the iterator should restart on the next iterator_next */
1302 /******** Misc helper functions ************************************/
1304 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1305 WPARAM wParam, LPARAM lParam, BOOL isW)
1307 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1308 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1311 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1313 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1315 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1316 (uView == LVS_ICON || uView == LVS_SMALLICON);
1319 /******** Internal API functions ************************************/
1321 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1323 static COLUMN_INFO mainItem;
1325 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1326 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1327 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1330 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1332 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1335 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1337 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1340 /* Listview invalidation functions: use _only_ these functions to invalidate */
1342 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1344 return infoPtr->bRedraw;
1347 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1349 if(!is_redrawing(infoPtr)) return;
1350 TRACE(" invalidating rect=%s\n", debugrect(rect));
1351 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1354 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1358 if(!is_redrawing(infoPtr)) return;
1359 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1360 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1363 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1365 POINT Origin, Position;
1368 if(!is_redrawing(infoPtr)) return;
1369 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1370 LISTVIEW_GetOrigin(infoPtr, &Origin);
1371 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1372 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1374 rcBox.bottom = infoPtr->nItemHeight;
1375 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1376 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1379 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1381 LISTVIEW_InvalidateRect(infoPtr, NULL);
1384 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1388 if(!is_redrawing(infoPtr)) return;
1389 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1390 rcCol.top = infoPtr->rcList.top;
1391 rcCol.bottom = infoPtr->rcList.bottom;
1392 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1397 * Retrieves the number of items that can fit vertically in the client area.
1400 * [I] infoPtr : valid pointer to the listview structure
1403 * Number of items per row.
1405 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1407 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1409 return max(nListWidth/infoPtr->nItemWidth, 1);
1414 * Retrieves the number of items that can fit horizontally in the client
1418 * [I] infoPtr : valid pointer to the listview structure
1421 * Number of items per column.
1423 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1425 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1427 return max(nListHeight / infoPtr->nItemHeight, 1);
1431 /*************************************************************************
1432 * LISTVIEW_ProcessLetterKeys
1434 * Processes keyboard messages generated by pressing the letter keys
1436 * What this does is perform a case insensitive search from the
1437 * current position with the following quirks:
1438 * - If two chars or more are pressed in quick succession we search
1439 * for the corresponding string (e.g. 'abc').
1440 * - If there is a delay we wipe away the current search string and
1441 * restart with just that char.
1442 * - If the user keeps pressing the same character, whether slowly or
1443 * fast, so that the search string is entirely composed of this
1444 * character ('aaaaa' for instance), then we search for first item
1445 * that starting with that character.
1446 * - If the user types the above character in quick succession, then
1447 * we must also search for the corresponding string ('aaaaa'), and
1448 * go to that string if there is a match.
1451 * [I] hwnd : handle to the window
1452 * [I] charCode : the character code, the actual character
1453 * [I] keyData : key data
1461 * - The current implementation has a list of characters it will
1462 * accept and it ignores averything else. In particular it will
1463 * ignore accentuated characters which seems to match what
1464 * Windows does. But I'm not sure it makes sense to follow
1466 * - We don't sound a beep when the search fails.
1470 * TREEVIEW_ProcessLetterKeys
1472 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1477 WCHAR buffer[MAX_PATH];
1478 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1480 /* simple parameter checking */
1481 if (!charCode || !keyData) return 0;
1483 /* only allow the valid WM_CHARs through */
1484 if (!isalnum(charCode) &&
1485 charCode != '.' && charCode != '`' && charCode != '!' &&
1486 charCode != '@' && charCode != '#' && charCode != '$' &&
1487 charCode != '%' && charCode != '^' && charCode != '&' &&
1488 charCode != '*' && charCode != '(' && charCode != ')' &&
1489 charCode != '-' && charCode != '_' && charCode != '+' &&
1490 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1491 charCode != '}' && charCode != '[' && charCode != '{' &&
1492 charCode != '/' && charCode != '?' && charCode != '>' &&
1493 charCode != '<' && charCode != ',' && charCode != '~')
1496 /* if there's one item or less, there is no where to go */
1497 if (infoPtr->nItemCount <= 1) return 0;
1499 /* update the search parameters */
1500 infoPtr->lastKeyPressTimestamp = GetTickCount();
1501 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1502 if (infoPtr->nSearchParamLength < MAX_PATH)
1503 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1504 if (infoPtr->charCode != charCode)
1505 infoPtr->charCode = charCode = 0;
1507 infoPtr->charCode=charCode;
1508 infoPtr->szSearchParam[0]=charCode;
1509 infoPtr->nSearchParamLength=1;
1510 /* Redundant with the 1 char string */
1514 /* and search from the current position */
1516 if (infoPtr->nFocusedItem >= 0) {
1517 endidx=infoPtr->nFocusedItem;
1519 /* if looking for single character match,
1520 * then we must always move forward
1522 if (infoPtr->nSearchParamLength == 1)
1525 endidx=infoPtr->nItemCount;
1529 if (idx == infoPtr->nItemCount) {
1530 if (endidx == infoPtr->nItemCount || endidx == 0)
1536 item.mask = LVIF_TEXT;
1539 item.pszText = buffer;
1540 item.cchTextMax = MAX_PATH;
1541 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1543 /* check for a match */
1544 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1547 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1548 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1549 /* This would work but we must keep looking for a longer match */
1553 } while (idx != endidx);
1556 LISTVIEW_KeySelection(infoPtr, nItem);
1561 /*************************************************************************
1562 * LISTVIEW_UpdateHeaderSize [Internal]
1564 * Function to resize the header control
1567 * [I] hwnd : handle to a window
1568 * [I] nNewScrollPos : scroll pos to set
1573 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1578 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1580 GetWindowRect(infoPtr->hwndHeader, &winRect);
1581 point[0].x = winRect.left;
1582 point[0].y = winRect.top;
1583 point[1].x = winRect.right;
1584 point[1].y = winRect.bottom;
1586 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1587 point[0].x = -nNewScrollPos;
1588 point[1].x += nNewScrollPos;
1590 SetWindowPos(infoPtr->hwndHeader,0,
1591 point[0].x,point[0].y,point[1].x,point[1].y,
1592 SWP_NOZORDER | SWP_NOACTIVATE);
1597 * Update the scrollbars. This functions should be called whenever
1598 * the content, size or view changes.
1601 * [I] infoPtr : valid pointer to the listview structure
1606 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1608 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1609 SCROLLINFO horzInfo, vertInfo;
1611 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1613 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1614 horzInfo.cbSize = sizeof(SCROLLINFO);
1615 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1617 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1618 if (uView == LVS_LIST)
1620 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1621 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1623 /* scroll by at least one column per page */
1624 if(horzInfo.nPage < infoPtr->nItemWidth)
1625 horzInfo.nPage = infoPtr->nItemWidth;
1627 horzInfo.nPage /= infoPtr->nItemWidth;
1629 else if (uView == LVS_REPORT)
1631 horzInfo.nMax = infoPtr->nItemWidth;
1633 else /* LVS_ICON, or LVS_SMALLICON */
1637 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1640 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1641 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1642 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1643 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1645 /* Setting the horizontal scroll can change the listview size
1646 * (and potentially everything else) so we need to recompute
1647 * everything again for the vertical scroll
1650 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1651 vertInfo.cbSize = sizeof(SCROLLINFO);
1652 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1654 if (uView == LVS_REPORT)
1656 vertInfo.nMax = infoPtr->nItemCount;
1658 /* scroll by at least one page */
1659 if(vertInfo.nPage < infoPtr->nItemHeight)
1660 vertInfo.nPage = infoPtr->nItemHeight;
1662 vertInfo.nPage /= infoPtr->nItemHeight;
1664 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1668 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1671 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1672 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1673 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1674 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1676 /* Update the Header Control */
1677 if (uView == LVS_REPORT)
1679 horzInfo.fMask = SIF_POS;
1680 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1681 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1688 * Shows/hides the focus rectangle.
1691 * [I] infoPtr : valid pointer to the listview structure
1692 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1697 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1699 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1702 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1704 if (infoPtr->nFocusedItem < 0) return;
1706 /* we need some gymnastics in ICON mode to handle large items */
1707 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1711 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1712 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1714 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1719 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1721 /* for some reason, owner draw should work only in report mode */
1722 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1727 item.iItem = infoPtr->nFocusedItem;
1729 item.mask = LVIF_PARAM;
1730 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1732 ZeroMemory(&dis, sizeof(dis));
1733 dis.CtlType = ODT_LISTVIEW;
1734 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1735 dis.itemID = item.iItem;
1736 dis.itemAction = ODA_FOCUS;
1737 if (fShow) dis.itemState |= ODS_FOCUS;
1738 dis.hwndItem = infoPtr->hwndSelf;
1740 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1741 dis.itemData = item.lParam;
1743 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1747 DrawFocusRect(hdc, &infoPtr->rcFocus);
1750 ReleaseDC(infoPtr->hwndSelf, hdc);
1754 * Invalidates all visible selected items.
1756 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1760 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1761 while(iterator_next(&i))
1763 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1764 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1766 iterator_destroy(&i);
1771 * DESCRIPTION: [INTERNAL]
1772 * Computes an item's (left,top) corner, relative to rcView.
1773 * That is, the position has NOT been made relative to the Origin.
1774 * This is deliberate, to avoid computing the Origin over, and
1775 * over again, when this function is call in a loop. Instead,
1776 * one ca factor the computation of the Origin before the loop,
1777 * and offset the value retured by this function, on every iteration.
1780 * [I] infoPtr : valid pointer to the listview structure
1781 * [I] nItem : item number
1782 * [O] lpptOrig : item top, left corner
1787 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1789 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1791 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1793 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1795 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1796 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1798 else if (uView == LVS_LIST)
1800 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1801 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1802 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1804 else /* LVS_REPORT */
1806 lpptPosition->x = 0;
1807 lpptPosition->y = nItem * infoPtr->nItemHeight;
1812 * DESCRIPTION: [INTERNAL]
1813 * Compute the rectangles of an item. This is to localize all
1814 * the computations in one place. If you are not interested in some
1815 * of these values, simply pass in a NULL -- the fucntion is smart
1816 * enough to compute only what's necessary. The function computes
1817 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1818 * one, the BOX rectangle. This rectangle is very cheap to compute,
1819 * and is guaranteed to contain all the other rectangles. Computing
1820 * the ICON rect is also cheap, but all the others are potentaily
1821 * expensive. This gives an easy and effective optimization when
1822 * searching (like point inclusion, or rectangle intersection):
1823 * first test against the BOX, and if TRUE, test agains the desired
1825 * If the function does not have all the necessary information
1826 * to computed the requested rectangles, will crash with a
1827 * failed assertion. This is done so we catch all programming
1828 * errors, given that the function is called only from our code.
1830 * We have the following 'special' meanings for a few fields:
1831 * * If LVIS_FOCUSED is set, we assume the item has the focus
1832 * This is important in ICON mode, where it might get a larger
1833 * then usual rectange
1835 * Please note that subitem support works only in REPORT mode.
1838 * [I] infoPtr : valid pointer to the listview structure
1839 * [I] lpLVItem : item to compute the measures for
1840 * [O] lprcBox : ptr to Box rectangle
1841 * The internal LVIR_BOX rectangle
1842 * [0] lprcState : ptr to State icon rectangle
1843 * The internal LVIR_STATE rectangle
1844 * [O] lprcIcon : ptr to Icon rectangle
1845 * Same as LVM_GETITEMRECT with LVIR_ICON
1846 * [O] lprcLabel : ptr to Label rectangle
1847 * Same as LVM_GETITEMRECT with LVIR_LABEL
1852 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1853 LPRECT lprcBox, LPRECT lprcState,
1854 LPRECT lprcIcon, LPRECT lprcLabel)
1856 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1857 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1858 RECT Box, State, Icon, Label;
1859 COLUMN_INFO *lpColumnInfo = NULL;
1861 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1863 /* Be smart and try to figure out the minimum we have to do */
1864 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1865 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1867 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1868 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1870 if (lprcLabel) doLabel = TRUE;
1871 if (doLabel || lprcIcon) doIcon = TRUE;
1872 if (doIcon || lprcState) doState = TRUE;
1874 /************************************************************/
1875 /* compute the box rectangle (it should be cheap to do) */
1876 /************************************************************/
1877 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1878 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1880 if (lpLVItem->iSubItem)
1882 Box = lpColumnInfo->rcHeader;
1887 Box.right = infoPtr->nItemWidth;
1890 Box.bottom = infoPtr->nItemHeight;
1892 /************************************************************/
1893 /* compute STATEICON bounding box */
1894 /************************************************************/
1897 if (uView == LVS_ICON)
1899 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1900 if (infoPtr->himlNormal)
1901 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1902 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1906 /* we need the ident in report mode, if we don't have it, we fail */
1907 State.left = Box.left;
1908 if (uView == LVS_REPORT)
1910 if (lpLVItem->iSubItem == 0)
1912 State.left += REPORT_MARGINX;
1913 assert(lpLVItem->mask & LVIF_INDENT);
1914 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1917 State.top = Box.top;
1919 State.right = State.left;
1920 State.bottom = State.top;
1921 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1923 State.right += infoPtr->iconStateSize.cx;
1924 State.bottom += infoPtr->iconStateSize.cy;
1926 if (lprcState) *lprcState = State;
1927 TRACE(" - state=%s\n", debugrect(&State));
1930 /************************************************************/
1931 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1932 /************************************************************/
1935 if (uView == LVS_ICON)
1937 Icon.left = Box.left;
1938 if (infoPtr->himlNormal)
1939 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1940 Icon.top = Box.top + ICON_TOP_PADDING;
1941 Icon.right = Icon.left;
1942 Icon.bottom = Icon.top;
1943 if (infoPtr->himlNormal)
1945 Icon.right += infoPtr->iconSize.cx;
1946 Icon.bottom += infoPtr->iconSize.cy;
1949 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1951 Icon.left = State.right;
1953 Icon.right = Icon.left;
1954 if (infoPtr->himlSmall &&
1955 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1956 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1957 Icon.right += infoPtr->iconSize.cx;
1958 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1960 if(lprcIcon) *lprcIcon = Icon;
1961 TRACE(" - icon=%s\n", debugrect(&Icon));
1964 /************************************************************/
1965 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1966 /************************************************************/
1969 SIZE labelSize = { 0, 0 };
1971 /* calculate how far to the right can the label strech */
1972 Label.right = Box.right;
1973 if (uView == LVS_REPORT)
1975 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1978 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1980 labelSize.cx = infoPtr->nItemWidth;
1981 labelSize.cy = infoPtr->nItemHeight;
1985 /* we need the text in non owner draw mode */
1986 assert(lpLVItem->mask & LVIF_TEXT);
1987 if (is_textT(lpLVItem->pszText, TRUE))
1989 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1990 HDC hdc = GetDC(infoPtr->hwndSelf);
1991 HFONT hOldFont = SelectObject(hdc, hFont);
1995 /* compute rough rectangle where the label will go */
1996 SetRectEmpty(&rcText);
1997 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1998 rcText.bottom = infoPtr->nItemHeight;
1999 if (uView == LVS_ICON)
2000 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2002 /* now figure out the flags */
2003 if (uView == LVS_ICON)
2004 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2006 uFormat = LV_SL_DT_FLAGS;
2008 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2010 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2011 labelSize.cy = rcText.bottom - rcText.top;
2013 SelectObject(hdc, hOldFont);
2014 ReleaseDC(infoPtr->hwndSelf, hdc);
2018 if (uView == LVS_ICON)
2020 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2021 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2022 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2023 Label.right = Label.left + labelSize.cx;
2024 Label.bottom = Label.top + infoPtr->nItemHeight;
2025 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2027 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2028 labelSize.cy /= infoPtr->ntmHeight;
2029 labelSize.cy = max(labelSize.cy, 1);
2030 labelSize.cy *= infoPtr->ntmHeight;
2032 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2034 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2036 Label.left = Icon.right;
2037 Label.top = Box.top;
2038 Label.right = min(Label.left + labelSize.cx, Label.right);
2039 Label.bottom = Label.top + infoPtr->nItemHeight;
2042 if (lprcLabel) *lprcLabel = Label;
2043 TRACE(" - label=%s\n", debugrect(&Label));
2046 /* Fix the Box if necessary */
2049 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2050 else *lprcBox = Box;
2052 TRACE(" - box=%s\n", debugrect(&Box));
2056 * DESCRIPTION: [INTERNAL]
2059 * [I] infoPtr : valid pointer to the listview structure
2060 * [I] nItem : item number
2061 * [O] lprcBox : ptr to Box rectangle
2066 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2068 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2069 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2070 POINT Position, Origin;
2073 LISTVIEW_GetOrigin(infoPtr, &Origin);
2074 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2076 /* Be smart and try to figure out the minimum we have to do */
2078 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2079 lvItem.mask |= LVIF_TEXT;
2080 lvItem.iItem = nItem;
2081 lvItem.iSubItem = 0;
2082 lvItem.pszText = szDispText;
2083 lvItem.cchTextMax = DISP_TEXT_SIZE;
2084 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2085 if (uView == LVS_ICON)
2087 lvItem.mask |= LVIF_STATE;
2088 lvItem.stateMask = LVIS_FOCUSED;
2089 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2091 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2093 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2099 * Returns the current icon position, and advances it along the top.
2100 * The returned position is not offset by Origin.
2103 * [I] infoPtr : valid pointer to the listview structure
2104 * [O] lpPos : will get the current icon position
2109 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2111 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2113 *lpPos = infoPtr->currIconPos;
2115 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2116 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2118 infoPtr->currIconPos.x = 0;
2119 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2125 * Returns the current icon position, and advances it down the left edge.
2126 * The returned position is not offset by Origin.
2129 * [I] infoPtr : valid pointer to the listview structure
2130 * [O] lpPos : will get the current icon position
2135 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2137 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2139 *lpPos = infoPtr->currIconPos;
2141 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2142 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2144 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2145 infoPtr->currIconPos.y = 0;
2151 * Moves an icon to the specified position.
2152 * It takes care of invalidating the item, etc.
2155 * [I] infoPtr : valid pointer to the listview structure
2156 * [I] nItem : the item to move
2157 * [I] lpPos : the new icon position
2158 * [I] isNew : flags the item as being new
2164 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2170 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2171 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2173 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2174 LISTVIEW_InvalidateItem(infoPtr, nItem);
2177 /* Allocating a POINTER for every item is too resource intensive,
2178 * so we'll keep the (x,y) in different arrays */
2179 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2180 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2182 LISTVIEW_InvalidateItem(infoPtr, nItem);
2189 * Arranges listview items in icon display mode.
2192 * [I] infoPtr : valid pointer to the listview structure
2193 * [I] nAlignCode : alignment code
2199 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2201 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2202 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2206 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2208 TRACE("nAlignCode=%d\n", nAlignCode);
2210 if (nAlignCode == LVA_DEFAULT)
2212 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2213 else nAlignCode = LVA_ALIGNTOP;
2218 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2219 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2220 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2221 default: return FALSE;
2224 infoPtr->bAutoarrange = TRUE;
2225 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2226 for (i = 0; i < infoPtr->nItemCount; i++)
2228 next_pos(infoPtr, &pos);
2229 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2237 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2240 * [I] infoPtr : valid pointer to the listview structure
2241 * [O] lprcView : bounding rectangle
2247 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2251 SetRectEmpty(lprcView);
2253 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2257 for (i = 0; i < infoPtr->nItemCount; i++)
2259 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2260 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2261 lprcView->right = max(lprcView->right, x);
2262 lprcView->bottom = max(lprcView->bottom, y);
2264 if (infoPtr->nItemCount > 0)
2266 lprcView->right += infoPtr->nItemWidth;
2267 lprcView->bottom += infoPtr->nItemHeight;
2272 y = LISTVIEW_GetCountPerColumn(infoPtr);
2273 x = infoPtr->nItemCount / y;
2274 if (infoPtr->nItemCount % y) x++;
2275 lprcView->right = x * infoPtr->nItemWidth;
2276 lprcView->bottom = y * infoPtr->nItemHeight;
2280 lprcView->right = infoPtr->nItemWidth;
2281 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2288 * Retrieves the bounding rectangle of all the items.
2291 * [I] infoPtr : valid pointer to the listview structure
2292 * [O] lprcView : bounding rectangle
2298 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2302 TRACE("(lprcView=%p)\n", lprcView);
2304 if (!lprcView) return FALSE;
2306 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2307 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2308 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2310 TRACE("lprcView=%s\n", debugrect(lprcView));
2317 * Retrieves the subitem pointer associated with the subitem index.
2320 * [I] hdpaSubItems : DPA handle for a specific item
2321 * [I] nSubItem : index of subitem
2324 * SUCCESS : subitem pointer
2327 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2329 SUBITEM_INFO *lpSubItem;
2332 /* we should binary search here if need be */
2333 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2335 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2336 if (lpSubItem->iSubItem == nSubItem)
2346 * Caclulates the desired item width.
2349 * [I] infoPtr : valid pointer to the listview structure
2352 * The desired item width.
2354 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2356 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2359 TRACE("uView=%d\n", uView);
2361 if (uView == LVS_ICON)
2362 nItemWidth = infoPtr->iconSpacing.cx;
2363 else if (uView == LVS_REPORT)
2367 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2369 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2370 nItemWidth = rcHeader.right;
2373 else /* LVS_SMALLICON, or LVS_LIST */
2377 for (i = 0; i < infoPtr->nItemCount; i++)
2378 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2380 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2381 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2383 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2386 return max(nItemWidth, 1);
2391 * Caclulates the desired item height.
2394 * [I] infoPtr : valid pointer to the listview structure
2397 * The desired item height.
2399 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2401 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2404 TRACE("uView=%d\n", uView);
2406 if (uView == LVS_ICON)
2407 nItemHeight = infoPtr->iconSpacing.cy;
2410 nItemHeight = infoPtr->ntmHeight;
2411 if (infoPtr->himlState)
2412 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2413 if (infoPtr->himlSmall)
2414 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2415 if (infoPtr->himlState || infoPtr->himlSmall)
2416 nItemHeight += HEIGHT_PADDING;
2419 return max(nItemHeight, 1);
2424 * Updates the width, and height of an item.
2427 * [I] infoPtr : valid pointer to the listview structure
2432 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2434 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2435 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2441 * Retrieves and saves important text metrics info for the current
2445 * [I] infoPtr : valid pointer to the listview structure
2448 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2450 HDC hdc = GetDC(infoPtr->hwndSelf);
2451 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2452 HFONT hOldFont = SelectObject(hdc, hFont);
2455 if (GetTextMetricsW(hdc, &tm))
2457 infoPtr->ntmHeight = tm.tmHeight;
2458 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2460 SelectObject(hdc, hOldFont);
2461 ReleaseDC(infoPtr->hwndSelf, hdc);
2463 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2468 * A compare function for ranges
2471 * [I] range1 : pointer to range 1;
2472 * [I] range2 : pointer to range 2;
2476 * > 0 : if range 1 > range 2
2477 * < 0 : if range 2 > range 1
2478 * = 0 : if range intersects range 2
2480 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2484 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2486 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2491 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2497 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2499 #define ranges_check(ranges, desc) do { } while(0)
2502 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2507 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2509 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2510 ranges_dump(ranges);
2511 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2512 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2513 assert (prev->lower >= 0 && prev->lower < prev->upper);
2514 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2516 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2517 assert (prev->upper <= curr->lower);
2518 assert (curr->lower < curr->upper);
2521 TRACE("--- Done checking---\n");
2524 static RANGES ranges_create(int count)
2526 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2527 if (!ranges) return NULL;
2528 ranges->hdpa = DPA_Create(count);
2529 if (ranges->hdpa) return ranges;
2534 static void ranges_clear(RANGES ranges)
2538 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2539 Free(DPA_GetPtr(ranges->hdpa, i));
2540 DPA_DeleteAllPtrs(ranges->hdpa);
2544 static void ranges_destroy(RANGES ranges)
2546 if (!ranges) return;
2547 ranges_clear(ranges);
2548 DPA_Destroy(ranges->hdpa);
2552 static RANGES ranges_clone(RANGES ranges)
2557 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2559 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2561 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2562 if (!newrng) goto fail;
2563 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2564 DPA_SetPtr(clone->hdpa, i, newrng);
2569 TRACE ("clone failed\n");
2570 ranges_destroy(clone);
2574 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2578 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2579 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2584 static void ranges_dump(RANGES ranges)
2588 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2589 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2592 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2594 RANGE srchrng = { nItem, nItem + 1 };
2596 TRACE("(nItem=%d)\n", nItem);
2597 ranges_check(ranges, "before contain");
2598 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2601 static INT ranges_itemcount(RANGES ranges)
2605 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2607 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2608 count += sel->upper - sel->lower;
2614 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2616 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2619 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2620 if (index == -1) return TRUE;
2622 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2624 chkrng = DPA_GetPtr(ranges->hdpa, index);
2625 if (chkrng->lower >= nItem)
2626 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2627 if (chkrng->upper > nItem)
2628 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2633 static BOOL ranges_add(RANGES ranges, RANGE range)
2638 TRACE("(%s)\n", debugrange(&range));
2639 ranges_check(ranges, "before add");
2641 /* try find overlapping regions first */
2642 srchrgn.lower = range.lower - 1;
2643 srchrgn.upper = range.upper + 1;
2644 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2650 TRACE("Adding new range\n");
2652 /* create the brand new range to insert */
2653 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2654 if(!newrgn) goto fail;
2657 /* figure out where to insert it */
2658 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2659 TRACE("index=%d\n", index);
2660 if (index == -1) index = 0;
2662 /* and get it over with */
2663 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2671 RANGE *chkrgn, *mrgrgn;
2672 INT fromindex, mergeindex;
2674 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2675 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2677 chkrgn->lower = min(range.lower, chkrgn->lower);
2678 chkrgn->upper = max(range.upper, chkrgn->upper);
2680 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2682 /* merge now common anges */
2684 srchrgn.lower = chkrgn->lower - 1;
2685 srchrgn.upper = chkrgn->upper + 1;
2689 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2690 if (mergeindex == -1) break;
2691 if (mergeindex == index)
2693 fromindex = index + 1;
2697 TRACE("Merge with index %i\n", mergeindex);
2699 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2700 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2701 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2703 DPA_DeletePtr(ranges->hdpa, mergeindex);
2704 if (mergeindex < index) index --;
2708 ranges_check(ranges, "after add");
2712 ranges_check(ranges, "failed add");
2716 static BOOL ranges_del(RANGES ranges, RANGE range)
2721 TRACE("(%s)\n", debugrange(&range));
2722 ranges_check(ranges, "before del");
2724 /* we don't use DPAS_SORTED here, since we need *
2725 * to find the first overlapping range */
2726 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2729 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2731 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2733 /* case 1: Same range */
2734 if ( (chkrgn->upper == range.upper) &&
2735 (chkrgn->lower == range.lower) )
2737 DPA_DeletePtr(ranges->hdpa, index);
2740 /* case 2: engulf */
2741 else if ( (chkrgn->upper <= range.upper) &&
2742 (chkrgn->lower >= range.lower) )
2744 DPA_DeletePtr(ranges->hdpa, index);
2746 /* case 3: overlap upper */
2747 else if ( (chkrgn->upper <= range.upper) &&
2748 (chkrgn->lower < range.lower) )
2750 chkrgn->upper = range.lower;
2752 /* case 4: overlap lower */
2753 else if ( (chkrgn->upper > range.upper) &&
2754 (chkrgn->lower >= range.lower) )
2756 chkrgn->lower = range.upper;
2759 /* case 5: fully internal */
2762 RANGE tmprgn = *chkrgn, *newrgn;
2764 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2765 newrgn->lower = chkrgn->lower;
2766 newrgn->upper = range.lower;
2767 chkrgn->lower = range.upper;
2768 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2777 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2780 ranges_check(ranges, "after del");
2784 ranges_check(ranges, "failed del");
2790 * Removes all selection ranges
2793 * [I] infoPtr : valid pointer to the listview structure
2794 * [I] toSkip : item range to skip removing the selection
2800 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2809 lvItem.stateMask = LVIS_SELECTED;
2811 /* need to clone the DPA because callbacks can change it */
2812 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2813 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2814 while(iterator_next(&i))
2815 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2816 /* note that the iterator destructor will free the cloned range */
2817 iterator_destroy(&i);
2822 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2826 if (!(toSkip = ranges_create(1))) return FALSE;
2827 if (nItem != -1) ranges_additem(toSkip, nItem);
2828 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2829 ranges_destroy(toSkip);
2833 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2835 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2840 * Retrieves the number of items that are marked as selected.
2843 * [I] infoPtr : valid pointer to the listview structure
2846 * Number of items selected.
2848 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2850 INT nSelectedCount = 0;
2852 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2855 for (i = 0; i < infoPtr->nItemCount; i++)
2857 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2862 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2864 TRACE("nSelectedCount=%d\n", nSelectedCount);
2865 return nSelectedCount;
2870 * Manages the item focus.
2873 * [I] infoPtr : valid pointer to the listview structure
2874 * [I] nItem : item index
2877 * TRUE : focused item changed
2878 * FALSE : focused item has NOT changed
2880 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2882 INT oldFocus = infoPtr->nFocusedItem;
2885 if (nItem == infoPtr->nFocusedItem) return FALSE;
2887 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2888 lvItem.stateMask = LVIS_FOCUSED;
2889 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2891 return oldFocus != infoPtr->nFocusedItem;
2894 /* Helper function for LISTVIEW_ShiftIndices *only* */
2895 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2897 if (nShiftItem < nItem) return nShiftItem;
2899 if (nShiftItem > nItem) return nShiftItem + direction;
2901 if (direction > 0) return nShiftItem + direction;
2903 return min(nShiftItem, infoPtr->nItemCount - 1);
2908 * Updates the various indices after an item has been inserted or deleted.
2911 * [I] infoPtr : valid pointer to the listview structure
2912 * [I] nItem : item index
2913 * [I] direction : Direction of shift, +1 or -1.
2918 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2923 /* temporarily disable change notification while shifting items */
2924 bOldChange = infoPtr->bDoChangeNotify;
2925 infoPtr->bDoChangeNotify = FALSE;
2927 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2929 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2931 assert(abs(direction) == 1);
2933 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2935 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2936 if (nNewFocus != infoPtr->nFocusedItem)
2937 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2939 /* But we are not supposed to modify nHotItem! */
2941 infoPtr->bDoChangeNotify = bOldChange;
2947 * Adds a block of selections.
2950 * [I] infoPtr : valid pointer to the listview structure
2951 * [I] nItem : item index
2956 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2958 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2959 INT nLast = max(infoPtr->nSelectionMark, nItem);
2963 if (nFirst == -1) nFirst = nItem;
2965 item.state = LVIS_SELECTED;
2966 item.stateMask = LVIS_SELECTED;
2968 /* FIXME: this is not correct LVS_OWNERDATA
2969 * setting the item states individually will generate
2970 * a LVN_ITEMCHANGED notification for each one. Instead,
2971 * we have to send a LVN_ODSTATECHANGED notification.
2972 * See MSDN documentation for LVN_ITEMCHANGED.
2974 for (i = nFirst; i <= nLast; i++)
2975 LISTVIEW_SetItemState(infoPtr,i,&item);
2981 * Sets a single group selection.
2984 * [I] infoPtr : valid pointer to the listview structure
2985 * [I] nItem : item index
2990 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2992 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2997 if (!(selection = ranges_create(100))) return;
2999 item.state = LVIS_SELECTED;
3000 item.stateMask = LVIS_SELECTED;
3002 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3004 if (infoPtr->nSelectionMark == -1)
3006 infoPtr->nSelectionMark = nItem;
3007 ranges_additem(selection, nItem);
3013 sel.lower = min(infoPtr->nSelectionMark, nItem);
3014 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3015 ranges_add(selection, sel);
3020 RECT rcItem, rcSel, rcSelMark;
3023 rcItem.left = LVIR_BOUNDS;
3024 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3025 rcSelMark.left = LVIR_BOUNDS;
3026 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3027 UnionRect(&rcSel, &rcItem, &rcSelMark);
3028 iterator_frameditems(&i, infoPtr, &rcSel);
3029 while(iterator_next(&i))
3031 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3032 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3034 iterator_destroy(&i);
3037 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3038 iterator_rangesitems(&i, selection);
3039 while(iterator_next(&i))
3040 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3041 /* this will also destroy the selection */
3042 iterator_destroy(&i);
3044 LISTVIEW_SetItemFocus(infoPtr, nItem);
3049 * Sets a single selection.
3052 * [I] infoPtr : valid pointer to the listview structure
3053 * [I] nItem : item index
3058 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3062 TRACE("nItem=%d\n", nItem);
3064 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3066 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3067 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3068 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3070 infoPtr->nSelectionMark = nItem;
3075 * Set selection(s) with keyboard.
3078 * [I] infoPtr : valid pointer to the listview structure
3079 * [I] nItem : item index
3082 * SUCCESS : TRUE (needs to be repainted)
3083 * FAILURE : FALSE (nothing has changed)
3085 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3087 /* FIXME: pass in the state */
3088 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3089 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3090 BOOL bResult = FALSE;
3092 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3094 if (infoPtr->dwStyle & LVS_SINGLESEL)
3097 LISTVIEW_SetSelection(infoPtr, nItem);
3104 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3108 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3113 LISTVIEW_SetSelection(infoPtr, nItem);
3116 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3119 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3126 * Called when the mouse is being actively tracked and has hovered for a specified
3130 * [I] infoPtr : valid pointer to the listview structure
3131 * [I] fwKeys : key indicator
3132 * [I] pts : mouse position
3135 * 0 if the message was processed, non-zero if there was an error
3138 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3139 * over the item for a certain period of time.
3142 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3144 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3145 /* FIXME: select the item!!! */
3146 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3153 * Called whenever WM_MOUSEMOVE is received.
3156 * [I] infoPtr : valid pointer to the listview structure
3157 * [I] fwKeys : key indicator
3158 * [I] pts : mouse position
3161 * 0 if the message is processed, non-zero if there was an error
3163 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3165 TRACKMOUSEEVENT trackinfo;
3167 /* see if we are supposed to be tracking mouse hovering */
3168 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3169 /* fill in the trackinfo struct */
3170 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3171 trackinfo.dwFlags = TME_QUERY;
3172 trackinfo.hwndTrack = infoPtr->hwndSelf;
3173 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3175 /* see if we are already tracking this hwnd */
3176 _TrackMouseEvent(&trackinfo);
3178 if(!(trackinfo.dwFlags & TME_HOVER)) {
3179 trackinfo.dwFlags = TME_HOVER;
3181 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3182 _TrackMouseEvent(&trackinfo);
3191 * Tests wheather the item is assignable to a list with style lStyle
3193 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3195 if ( (lpLVItem->mask & LVIF_TEXT) &&
3196 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3197 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3205 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3208 * [I] infoPtr : valid pointer to the listview structure
3209 * [I] lpLVItem : valid pointer to new item atttributes
3210 * [I] isNew : the item being set is being inserted
3211 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3212 * [O] bChanged : will be set to TRUE if the item really changed
3218 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3220 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3228 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3230 if (lpLVItem->mask == 0) return TRUE;
3232 if (infoPtr->dwStyle & LVS_OWNERDATA)
3234 /* a virtual listview we stores only selection and focus */
3235 if (lpLVItem->mask & ~LVIF_STATE)
3241 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3242 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3246 /* we need to get the lParam and state of the item */
3247 item.iItem = lpLVItem->iItem;
3248 item.iSubItem = lpLVItem->iSubItem;
3249 item.mask = LVIF_STATE | LVIF_PARAM;
3250 item.stateMask = ~0;
3253 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3255 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3256 /* determine what fields will change */
3257 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3258 uChanged |= LVIF_STATE;
3260 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3261 uChanged |= LVIF_IMAGE;
3263 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3264 uChanged |= LVIF_PARAM;
3266 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3267 uChanged |= LVIF_INDENT;
3269 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3270 uChanged |= LVIF_TEXT;
3272 TRACE("uChanged=0x%x\n", uChanged);
3273 if (!uChanged) return TRUE;
3276 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3277 nmlv.iItem = lpLVItem->iItem;
3278 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3279 nmlv.uOldState = item.state;
3280 nmlv.uChanged = uChanged;
3281 nmlv.lParam = item.lParam;
3283 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3284 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3286 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3287 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3290 /* copy information */
3291 if (lpLVItem->mask & LVIF_TEXT)
3292 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3294 if (lpLVItem->mask & LVIF_IMAGE)
3295 lpItem->hdr.iImage = lpLVItem->iImage;
3297 if (lpLVItem->mask & LVIF_PARAM)
3298 lpItem->lParam = lpLVItem->lParam;
3300 if (lpLVItem->mask & LVIF_INDENT)
3301 lpItem->iIndent = lpLVItem->iIndent;
3303 if (uChanged & LVIF_STATE)
3305 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3307 lpItem->state &= ~lpLVItem->stateMask;
3308 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3310 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3312 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3313 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3315 else if (lpLVItem->stateMask & LVIS_SELECTED)
3316 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3318 /* if we are asked to change focus, and we manage it, do it */
3319 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3321 if (lpLVItem->state & LVIS_FOCUSED)
3323 LISTVIEW_SetItemFocus(infoPtr, -1);
3324 infoPtr->nFocusedItem = lpLVItem->iItem;
3325 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3327 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3328 infoPtr->nFocusedItem = -1;
3332 /* if we're inserting the item, we're done */
3333 if (isNew) return TRUE;
3335 /* send LVN_ITEMCHANGED notification */
3336 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3337 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3344 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3347 * [I] infoPtr : valid pointer to the listview structure
3348 * [I] lpLVItem : valid pointer to new subitem atttributes
3349 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3350 * [O] bChanged : will be set to TRUE if the item really changed
3356 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3359 SUBITEM_INFO *lpSubItem;
3361 /* we do not support subitems for virtual listviews */
3362 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3364 /* set subitem only if column is present */
3365 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3367 /* First do some sanity checks */
3368 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3369 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3371 /* get the subitem structure, and create it if not there */
3372 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3373 assert (hdpaSubItems);
3375 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3378 SUBITEM_INFO *tmpSubItem;
3381 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3382 if (!lpSubItem) return FALSE;
3383 /* we could binary search here, if need be...*/
3384 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3386 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3387 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3389 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3394 lpSubItem->iSubItem = lpLVItem->iSubItem;
3395 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3399 if (lpLVItem->mask & LVIF_IMAGE)
3400 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3402 lpSubItem->hdr.iImage = lpLVItem->iImage;
3406 if (lpLVItem->mask & LVIF_TEXT)
3407 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3409 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3418 * Sets item attributes.
3421 * [I] infoPtr : valid pointer to the listview structure
3422 * [I] lpLVItem : new item atttributes
3423 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3429 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3431 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3432 LPWSTR pszText = NULL;
3433 BOOL bResult, bChanged = FALSE;
3435 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3437 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3440 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3441 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3443 pszText = lpLVItem->pszText;
3444 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3447 /* actually set the fields */
3448 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3450 if (lpLVItem->iSubItem)
3451 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3453 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3455 /* redraw item, if necessary */
3456 if (bChanged && !infoPtr->bIsDrawing)
3458 /* this little optimization eliminates some nasty flicker */
3459 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3460 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3461 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3463 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3468 textfreeT(lpLVItem->pszText, isW);
3469 ((LVITEMW *)lpLVItem)->pszText = pszText;
3477 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3480 * [I] infoPtr : valid pointer to the listview structure
3485 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3487 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3489 SCROLLINFO scrollInfo;
3491 scrollInfo.cbSize = sizeof(SCROLLINFO);
3492 scrollInfo.fMask = SIF_POS;
3494 if (uView == LVS_LIST)
3496 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3497 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3499 else if (uView == LVS_REPORT)
3501 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3502 nItem = scrollInfo.nPos;
3506 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3507 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3510 TRACE("nItem=%d\n", nItem);
3518 * Erases the background of the given rectangle
3521 * [I] infoPtr : valid pointer to the listview structure
3522 * [I] hdc : device context handle
3523 * [I] lprcBox : clipping rectangle
3529 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3531 if (!infoPtr->hBkBrush) return FALSE;
3533 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3535 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3543 * [I] infoPtr : valid pointer to the listview structure
3544 * [I] hdc : device context handle
3545 * [I] nItem : item index
3546 * [I] nSubItem : subitem index
3547 * [I] pos : item position in client coordinates
3548 * [I] cdmode : custom draw mode
3554 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3556 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3557 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3558 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3559 DWORD cdsubitemmode = CDRF_DODEFAULT;
3560 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3561 NMLVCUSTOMDRAW nmlvcd;
3565 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3567 /* get information needed for drawing the item */
3568 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3569 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3570 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3571 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3572 lvItem.iItem = nItem;
3573 lvItem.iSubItem = nSubItem;
3576 lvItem.cchTextMax = DISP_TEXT_SIZE;
3577 lvItem.pszText = szDispText;
3578 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3579 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3580 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3581 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3582 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3584 /* now check if we need to update the focus rectangle */
3585 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3587 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3588 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3589 OffsetRect(&rcBox, pos.x, pos.y);
3590 OffsetRect(&rcState, pos.x, pos.y);
3591 OffsetRect(&rcIcon, pos.x, pos.y);
3592 OffsetRect(&rcLabel, pos.x, pos.y);
3593 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3594 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3596 /* fill in the custom draw structure */
3597 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3599 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3600 if (cdmode & CDRF_NOTIFYITEMDRAW)
3601 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3602 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3603 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3604 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3605 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3607 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3608 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3610 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3611 prepaint_setup(infoPtr, hdc, &nmlvcd);
3613 /* in full row select, subitems, will just use main item's colors */
3614 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3615 nmlvcd.clrTextBk = CLR_NONE;
3618 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3620 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3623 TRACE("uStateImage=%d\n", uStateImage);
3624 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3629 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3630 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3632 TRACE("iImage=%d\n", lvItem.iImage);
3633 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3634 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3637 /* Don't bother painting item being edited */
3638 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3640 /* draw the selection background, if we're drawing the main item */
3644 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3645 rcSelect.right = rcBox.right;
3647 if (nmlvcd.clrTextBk != CLR_NONE)
3648 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3649 if(lprcFocus) *lprcFocus = rcSelect;
3652 /* figure out the text drawing flags */
3653 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3654 if (uView == LVS_ICON)
3655 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3658 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3660 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3661 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3662 default: uFormat |= DT_LEFT;
3665 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3667 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3668 else rcLabel.left += LABEL_HOR_PADDING;
3670 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3671 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3674 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3675 notify_postpaint(infoPtr, &nmlvcd);
3681 * Draws listview items when in owner draw mode.
3684 * [I] infoPtr : valid pointer to the listview structure
3685 * [I] hdc : device context handle
3690 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3692 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3693 DWORD cditemmode = CDRF_DODEFAULT;
3694 NMLVCUSTOMDRAW nmlvcd;
3695 POINT Origin, Position;
3701 ZeroMemory(&dis, sizeof(dis));
3703 /* Get scroll info once before loop */
3704 LISTVIEW_GetOrigin(infoPtr, &Origin);
3706 /* iterate through the invalidated rows */
3707 while(iterator_next(i))
3709 item.iItem = i->nItem;
3711 item.mask = LVIF_PARAM | LVIF_STATE;
3712 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3713 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3715 dis.CtlType = ODT_LISTVIEW;
3717 dis.itemID = item.iItem;
3718 dis.itemAction = ODA_DRAWENTIRE;
3720 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3721 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3722 dis.hwndItem = infoPtr->hwndSelf;
3724 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3725 dis.rcItem.left = Position.x + Origin.x;
3726 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3727 dis.rcItem.top = Position.y + Origin.y;
3728 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3729 dis.itemData = item.lParam;
3731 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3734 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3735 * structure for the rest. of the paint cycle
3737 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3738 if (cdmode & CDRF_NOTIFYITEMDRAW)
3739 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3741 if (!(cditemmode & CDRF_SKIPDEFAULT))
3743 prepaint_setup (infoPtr, hdc, &nmlvcd);
3744 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3747 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3748 notify_postpaint(infoPtr, &nmlvcd);
3754 * Draws listview items when in report display mode.
3757 * [I] infoPtr : valid pointer to the listview structure
3758 * [I] hdc : device context handle
3759 * [I] cdmode : custom draw mode
3764 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3767 RECT rcClip, rcItem;
3768 POINT Origin, Position;
3774 /* figure out what to draw */
3775 rgntype = GetClipBox(hdc, &rcClip);
3776 if (rgntype == NULLREGION) return;
3778 /* Get scroll info once before loop */
3779 LISTVIEW_GetOrigin(infoPtr, &Origin);
3781 /* narrow down the columns we need to paint */
3782 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3784 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3785 if (rcItem.right + Origin.x >= rcClip.left) break;
3787 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3789 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3790 if (rcItem.left + Origin.x < rcClip.right) break;
3792 iterator_rangeitems(&j, colRange);
3794 /* in full row select, we _have_ to draw the main item */
3795 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3798 /* iterate through the invalidated rows */
3799 while(iterator_next(i))
3801 /* iterate through the invalidated columns */
3802 while(iterator_next(&j))
3804 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3805 Position.x += Origin.x;
3806 Position.y += Origin.y;
3808 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3810 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3812 rcItem.bottom = infoPtr->nItemHeight;
3813 OffsetRect(&rcItem, Position.x, Position.y);
3814 if (!RectVisible(hdc, &rcItem)) continue;
3817 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3820 iterator_destroy(&j);
3825 * Draws listview items when in list display mode.
3828 * [I] infoPtr : valid pointer to the listview structure
3829 * [I] hdc : device context handle
3830 * [I] cdmode : custom draw mode
3835 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3837 POINT Origin, Position;
3839 /* Get scroll info once before loop */
3840 LISTVIEW_GetOrigin(infoPtr, &Origin);
3842 while(iterator_prev(i))
3844 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3845 Position.x += Origin.x;
3846 Position.y += Origin.y;
3848 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3855 * Draws listview items.
3858 * [I] infoPtr : valid pointer to the listview structure
3859 * [I] hdc : device context handle
3864 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3866 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3867 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3868 NMLVCUSTOMDRAW nmlvcd;
3875 LISTVIEW_DUMP(infoPtr);
3877 infoPtr->bIsDrawing = TRUE;
3879 /* save dc values we're gonna trash while drawing */
3880 hOldFont = SelectObject(hdc, infoPtr->hFont);
3881 oldBkMode = GetBkMode(hdc);
3882 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3883 oldTextColor = GetTextColor(hdc);
3885 oldClrTextBk = infoPtr->clrTextBk;
3886 oldClrText = infoPtr->clrText;
3888 infoPtr->cditemmode = CDRF_DODEFAULT;
3890 GetClientRect(infoPtr->hwndSelf, &rcClient);
3891 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3892 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3893 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3894 prepaint_setup(infoPtr, hdc, &nmlvcd);
3896 /* Use these colors to draw the items */
3897 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3898 infoPtr->clrText = nmlvcd.clrText;
3900 /* nothing to draw */
3901 if(infoPtr->nItemCount == 0) goto enddraw;
3903 /* figure out what we need to draw */
3904 iterator_visibleitems(&i, infoPtr, hdc);
3906 /* send cache hint notification */
3907 if (infoPtr->dwStyle & LVS_OWNERDATA)
3909 RANGE range = iterator_range(&i);
3912 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3913 nmlv.iFrom = range.lower;
3914 nmlv.iTo = range.upper - 1;
3915 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3918 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3919 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3922 if (uView == LVS_REPORT)
3923 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3924 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3925 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3927 /* if we have a focus rect, draw it */
3928 if (infoPtr->bFocus)
3929 DrawFocusRect(hdc, &infoPtr->rcFocus);
3931 iterator_destroy(&i);
3934 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3935 notify_postpaint(infoPtr, &nmlvcd);
3937 infoPtr->clrTextBk = oldClrTextBk;
3938 infoPtr->clrText = oldClrText;
3940 SelectObject(hdc, hOldFont);
3941 SetBkMode(hdc, oldBkMode);
3942 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3943 SetTextColor(hdc, oldTextColor);
3944 infoPtr->bIsDrawing = FALSE;
3950 * Calculates the approximate width and height of a given number of items.
3953 * [I] infoPtr : valid pointer to the listview structure
3954 * [I] nItemCount : number of items
3955 * [I] wWidth : width
3956 * [I] wHeight : height
3959 * Returns a DWORD. The width in the low word and the height in high word.
3961 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3962 WORD wWidth, WORD wHeight)
3964 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3965 INT nItemCountPerColumn = 1;
3966 INT nColumnCount = 0;
3967 DWORD dwViewRect = 0;
3969 if (nItemCount == -1)
3970 nItemCount = infoPtr->nItemCount;
3972 if (uView == LVS_LIST)
3974 if (wHeight == 0xFFFF)
3976 /* use current height */
3977 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3980 if (wHeight < infoPtr->nItemHeight)
3981 wHeight = infoPtr->nItemHeight;
3985 if (infoPtr->nItemHeight > 0)
3987 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3988 if (nItemCountPerColumn == 0)
3989 nItemCountPerColumn = 1;
3991 if (nItemCount % nItemCountPerColumn != 0)
3992 nColumnCount = nItemCount / nItemCountPerColumn;
3994 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3998 /* Microsoft padding magic */
3999 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4000 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4002 dwViewRect = MAKELONG(wWidth, wHeight);
4004 else if (uView == LVS_REPORT)
4008 if (infoPtr->nItemCount > 0)
4010 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4011 wWidth = rcBox.right - rcBox.left;
4012 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4016 /* use current height and width */
4017 if (wHeight == 0xffff)
4018 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4019 if (wWidth == 0xffff)
4020 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4023 dwViewRect = MAKELONG(wWidth, wHeight);
4025 else if (uView == LVS_SMALLICON)
4026 FIXME("uView == LVS_SMALLICON: not implemented\n");
4027 else if (uView == LVS_ICON)
4028 FIXME("uView == LVS_ICON: not implemented\n");
4036 * Create a drag image list for the specified item.
4039 * [I] infoPtr : valid pointer to the listview structure
4040 * [I] iItem : index of item
4041 * [O] lppt : Upperr-left corner of the image
4044 * Returns a handle to the image list if successful, NULL otherwise.
4046 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4052 HBITMAP hbmp, hOldbmp;
4053 HIMAGELIST dragList = 0;
4054 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4056 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4059 rcItem.left = LVIR_BOUNDS;
4060 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4063 lppt->x = rcItem.left;
4064 lppt->y = rcItem.top;
4066 size.cx = rcItem.right - rcItem.left;
4067 size.cy = rcItem.bottom - rcItem.top;
4069 hdcOrig = GetDC(infoPtr->hwndSelf);
4070 hdc = CreateCompatibleDC(hdcOrig);
4071 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4072 hOldbmp = SelectObject(hdc, hbmp);
4074 rcItem.left = rcItem.top = 0;
4075 rcItem.right = size.cx;
4076 rcItem.bottom = size.cy;
4077 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4080 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4082 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4083 SelectObject(hdc, hOldbmp);
4084 ImageList_Add(dragList, hbmp, 0);
4087 SelectObject(hdc, hOldbmp);
4091 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4093 TRACE("ret=%p\n", dragList);
4101 * Removes all listview items and subitems.
4104 * [I] infoPtr : valid pointer to the listview structure
4110 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4113 HDPA hdpaSubItems = NULL;
4120 /* we do it directly, to avoid notifications */
4121 ranges_clear(infoPtr->selectionRanges);
4122 infoPtr->nSelectionMark = -1;
4123 infoPtr->nFocusedItem = -1;
4124 SetRectEmpty(&infoPtr->rcFocus);
4125 /* But we are supposed to leave nHotItem as is! */
4128 /* send LVN_DELETEALLITEMS notification */
4129 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4131 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4133 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4135 /* send LVN_DELETEITEM notification, if not supressed */
4136 if (!bSuppress) notify_deleteitem(infoPtr, i);
4137 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4139 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4140 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4142 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4143 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4146 DPA_Destroy(hdpaSubItems);
4147 DPA_DeletePtr(infoPtr->hdpaItems, i);
4149 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4150 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4151 infoPtr->nItemCount --;
4154 LISTVIEW_UpdateScroll(infoPtr);
4156 LISTVIEW_InvalidateList(infoPtr);
4163 * Scrolls, and updates the columns, when a column is changing width.
4166 * [I] infoPtr : valid pointer to the listview structure
4167 * [I] nColumn : column to scroll
4168 * [I] dx : amount of scroll, in pixels
4173 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4175 COLUMN_INFO *lpColumnInfo;
4179 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4180 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4181 rcCol = lpColumnInfo->rcHeader;
4182 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4183 rcCol.left = rcCol.right;
4185 /* ajust the other columns */
4186 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4188 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4189 lpColumnInfo->rcHeader.left += dx;
4190 lpColumnInfo->rcHeader.right += dx;
4193 /* do not update screen if not in report mode */
4194 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4196 /* if we have a focus, must first erase the focus rect */
4197 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4199 /* Need to reset the item width when inserting a new column */
4200 infoPtr->nItemWidth += dx;
4202 LISTVIEW_UpdateScroll(infoPtr);
4204 /* scroll to cover the deleted column, and invalidate for redraw */
4205 rcOld = infoPtr->rcList;
4206 rcOld.left = rcCol.left;
4207 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4209 /* we can restore focus now */
4210 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4215 * Removes a column from the listview control.
4218 * [I] infoPtr : valid pointer to the listview structure
4219 * [I] nColumn : column index
4225 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4229 TRACE("nColumn=%d\n", nColumn);
4231 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4232 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4234 /* While the MSDN specifically says that column zero should not be deleted,
4235 what actually happens is that the column itself is deleted but no items or subitems
4239 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4241 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4244 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4245 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4247 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4249 SUBITEM_INFO *lpSubItem, *lpDelItem;
4251 INT nItem, nSubItem, i;
4253 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4255 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4258 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4260 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4261 if (lpSubItem->iSubItem == nColumn)
4264 lpDelItem = lpSubItem;
4266 else if (lpSubItem->iSubItem > nColumn)
4268 lpSubItem->iSubItem--;
4272 /* if we found our subitem, zapp it */
4276 if (is_textW(lpDelItem->hdr.pszText))
4277 Free(lpDelItem->hdr.pszText);
4282 /* free dpa memory */
4283 DPA_DeletePtr(hdpaSubItems, nSubItem);
4288 /* update the other column info */
4289 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4290 LISTVIEW_InvalidateList(infoPtr);
4292 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4299 * Invalidates the listview after an item's insertion or deletion.
4302 * [I] infoPtr : valid pointer to the listview structure
4303 * [I] nItem : item index
4304 * [I] dir : -1 if deleting, 1 if inserting
4309 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4311 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4312 INT nPerCol, nItemCol, nItemRow;
4316 /* if we don't refresh, what's the point of scrolling? */
4317 if (!is_redrawing(infoPtr)) return;
4319 assert (abs(dir) == 1);
4321 /* arrange icons if autoarrange is on */
4322 if (is_autoarrange(infoPtr))
4324 BOOL arrange = TRUE;
4325 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4326 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4327 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4330 /* scrollbars need updating */
4331 LISTVIEW_UpdateScroll(infoPtr);
4333 /* figure out the item's position */
4334 if (uView == LVS_REPORT)
4335 nPerCol = infoPtr->nItemCount + 1;
4336 else if (uView == LVS_LIST)
4337 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4338 else /* LVS_ICON, or LVS_SMALLICON */
4341 nItemCol = nItem / nPerCol;
4342 nItemRow = nItem % nPerCol;
4343 LISTVIEW_GetOrigin(infoPtr, &Origin);
4345 /* move the items below up a slot */
4346 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4347 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4348 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4349 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4350 OffsetRect(&rcScroll, Origin.x, Origin.y);
4351 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4352 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4354 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4355 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4356 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4359 /* report has only that column, so we're done */
4360 if (uView == LVS_REPORT) return;
4362 /* now for LISTs, we have to deal with the columns to the right */
4363 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4365 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4366 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4367 OffsetRect(&rcScroll, Origin.x, Origin.y);
4368 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4369 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4370 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4375 * Removes an item from the listview control.
4378 * [I] infoPtr : valid pointer to the listview structure
4379 * [I] nItem : item index
4385 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4387 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4390 TRACE("(nItem=%d)\n", nItem);
4392 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4394 /* remove selection, and focus */
4396 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4397 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4399 /* send LVN_DELETEITEM notification. */
4400 notify_deleteitem(infoPtr, nItem);
4402 /* we need to do this here, because we'll be deleting stuff */
4403 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4404 LISTVIEW_InvalidateItem(infoPtr, nItem);
4406 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4412 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4413 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4415 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4416 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4419 DPA_Destroy(hdpaSubItems);
4422 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4424 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4425 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4428 infoPtr->nItemCount--;
4429 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4431 /* now is the invalidation fun */
4432 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4439 * Callback implementation for editlabel control
4442 * [I] infoPtr : valid pointer to the listview structure
4443 * [I] pszText : modified text
4444 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4450 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4452 NMLVDISPINFOW dispInfo;
4454 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4456 ZeroMemory(&dispInfo, sizeof(dispInfo));
4457 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4458 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4459 dispInfo.item.iSubItem = 0;
4460 dispInfo.item.stateMask = ~0;
4461 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4462 /* add the text from the edit in */
4463 dispInfo.item.mask |= LVIF_TEXT;
4464 dispInfo.item.pszText = pszText;
4465 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4467 /* Do we need to update the Item Text */
4468 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4469 if (!pszText) return TRUE;
4471 ZeroMemory(&dispInfo, sizeof(dispInfo));
4472 dispInfo.item.mask = LVIF_TEXT;
4473 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4474 dispInfo.item.iSubItem = 0;
4475 dispInfo.item.pszText = pszText;
4476 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4477 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4482 * Begin in place editing of specified list view item
4485 * [I] infoPtr : valid pointer to the listview structure
4486 * [I] nItem : item index
4487 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4493 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4495 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4496 NMLVDISPINFOW dispInfo;
4499 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4501 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4502 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4504 infoPtr->nEditLabelItem = nItem;
4506 /* Is the EditBox still there, if so remove it */
4507 if(infoPtr->hwndEdit != 0)
4509 SetFocus(infoPtr->hwndSelf);
4510 infoPtr->hwndEdit = 0;
4513 LISTVIEW_SetSelection(infoPtr, nItem);
4514 LISTVIEW_SetItemFocus(infoPtr, nItem);
4515 LISTVIEW_InvalidateItem(infoPtr, nItem);
4517 rect.left = LVIR_LABEL;
4518 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4520 ZeroMemory(&dispInfo, sizeof(dispInfo));
4521 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4522 dispInfo.item.iItem = nItem;
4523 dispInfo.item.iSubItem = 0;
4524 dispInfo.item.stateMask = ~0;
4525 dispInfo.item.pszText = szDispText;
4526 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4527 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4529 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4530 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4531 if (!infoPtr->hwndEdit) return 0;
4533 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4535 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4536 infoPtr->hwndEdit = 0;
4540 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4541 SetFocus(infoPtr->hwndEdit);
4542 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4543 return infoPtr->hwndEdit;
4549 * Ensures the specified item is visible, scrolling into view if necessary.
4552 * [I] infoPtr : valid pointer to the listview structure
4553 * [I] nItem : item index
4554 * [I] bPartial : partially or entirely visible
4560 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4562 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4563 INT nScrollPosHeight = 0;
4564 INT nScrollPosWidth = 0;
4565 INT nHorzAdjust = 0;
4566 INT nVertAdjust = 0;
4569 RECT rcItem, rcTemp;
4571 rcItem.left = LVIR_BOUNDS;
4572 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4574 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4576 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4578 /* scroll left/right, but in LVS_REPORT mode */
4579 if (uView == LVS_LIST)
4580 nScrollPosWidth = infoPtr->nItemWidth;
4581 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4582 nScrollPosWidth = 1;
4584 if (rcItem.left < infoPtr->rcList.left)
4587 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4592 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4596 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4598 /* scroll up/down, but not in LVS_LIST mode */
4599 if (uView == LVS_REPORT)
4600 nScrollPosHeight = infoPtr->nItemHeight;
4601 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4602 nScrollPosHeight = 1;
4604 if (rcItem.top < infoPtr->rcList.top)
4607 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4612 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4616 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4618 if (nScrollPosWidth)
4620 INT diff = nHorzDiff / nScrollPosWidth;
4621 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4622 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4625 if (nScrollPosHeight)
4627 INT diff = nVertDiff / nScrollPosHeight;
4628 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4629 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4637 * Searches for an item with specific characteristics.
4640 * [I] hwnd : window handle
4641 * [I] nStart : base item index
4642 * [I] lpFindInfo : item information to look for
4645 * SUCCESS : index of item
4648 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4649 const LVFINDINFOW *lpFindInfo)
4651 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4652 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4653 BOOL bWrap = FALSE, bNearest = FALSE;
4654 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4655 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4656 POINT Position, Destination;
4659 if (!lpFindInfo || nItem < 0) return -1;
4662 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4664 lvItem.mask |= LVIF_TEXT;
4665 lvItem.pszText = szDispText;
4666 lvItem.cchTextMax = DISP_TEXT_SIZE;
4669 if (lpFindInfo->flags & LVFI_WRAP)
4672 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4673 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4678 LISTVIEW_GetOrigin(infoPtr, &Origin);
4679 Destination.x = lpFindInfo->pt.x - Origin.x;
4680 Destination.y = lpFindInfo->pt.y - Origin.y;
4681 switch(lpFindInfo->vkDirection)
4683 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4684 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4685 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4686 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4687 case VK_HOME: Destination.x = Destination.y = 0; break;
4688 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4689 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4691 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4692 Destination.x = rcArea.right;
4693 Destination.y = rcArea.bottom;
4695 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4700 /* if LVFI_PARAM is specified, all other flags are ignored */
4701 if (lpFindInfo->flags & LVFI_PARAM)
4703 lvItem.mask |= LVIF_PARAM;
4705 lvItem.mask &= ~LVIF_TEXT;
4709 for (; nItem < nLast; nItem++)
4711 lvItem.iItem = nItem;
4712 lvItem.iSubItem = 0;
4713 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4715 if (lvItem.mask & LVIF_PARAM)
4717 if (lpFindInfo->lParam == lvItem.lParam)
4723 if (lvItem.mask & LVIF_TEXT)
4725 if (lpFindInfo->flags & LVFI_PARTIAL)
4727 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4731 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4735 if (!bNearest) return nItem;
4737 /* This is very inefficient. To do a good job here,
4738 * we need a sorted array of (x,y) item positions */
4739 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4741 /* compute the distance^2 to the destination */
4742 xdist = Destination.x - Position.x;
4743 ydist = Destination.y - Position.y;
4744 dist = xdist * xdist + ydist * ydist;
4746 /* remember the distance, and item if it's closer */
4750 nNearestItem = nItem;
4757 nLast = min(nStart + 1, infoPtr->nItemCount);
4762 return nNearestItem;
4767 * Searches for an item with specific characteristics.
4770 * [I] hwnd : window handle
4771 * [I] nStart : base item index
4772 * [I] lpFindInfo : item information to look for
4775 * SUCCESS : index of item
4778 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4779 const LVFINDINFOA *lpFindInfo)
4781 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4785 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4786 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4787 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4788 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4794 * Retrieves the background image of the listview control.
4797 * [I] infoPtr : valid pointer to the listview structure
4798 * [O] lpBkImage : background image attributes
4804 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4806 /* FIXME (listview, "empty stub!\n"); */
4812 * Retrieves column attributes.
4815 * [I] infoPtr : valid pointer to the listview structure
4816 * [I] nColumn : column index
4817 * [IO] lpColumn : column information
4818 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4819 * otherwise it is in fact a LPLVCOLUMNA
4825 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4827 COLUMN_INFO *lpColumnInfo;
4830 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4831 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4833 /* initialize memory */
4834 ZeroMemory(&hdi, sizeof(hdi));
4836 if (lpColumn->mask & LVCF_TEXT)
4838 hdi.mask |= HDI_TEXT;
4839 hdi.pszText = lpColumn->pszText;
4840 hdi.cchTextMax = lpColumn->cchTextMax;
4843 if (lpColumn->mask & LVCF_IMAGE)
4844 hdi.mask |= HDI_IMAGE;
4846 if (lpColumn->mask & LVCF_ORDER)
4847 hdi.mask |= HDI_ORDER;
4849 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4851 if (lpColumn->mask & LVCF_FMT)
4852 lpColumn->fmt = lpColumnInfo->fmt;
4854 if (lpColumn->mask & LVCF_WIDTH)
4855 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4857 if (lpColumn->mask & LVCF_IMAGE)
4858 lpColumn->iImage = hdi.iImage;
4860 if (lpColumn->mask & LVCF_ORDER)
4861 lpColumn->iOrder = hdi.iOrder;
4867 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4874 /* FIXME: little hack */
4875 for (i = 0; i < iCount; i++)
4883 * Retrieves the column width.
4886 * [I] infoPtr : valid pointer to the listview structure
4887 * [I] int : column index
4890 * SUCCESS : column width
4893 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4895 INT nColumnWidth = 0;
4898 TRACE("nColumn=%d\n", nColumn);
4900 /* we have a 'column' in LIST and REPORT mode only */
4901 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4904 nColumnWidth = infoPtr->nItemWidth;
4907 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4908 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4909 nColumnWidth = rcHeader.right - rcHeader.left;
4913 TRACE("nColumnWidth=%d\n", nColumnWidth);
4914 return nColumnWidth;
4919 * In list or report display mode, retrieves the number of items that can fit
4920 * vertically in the visible area. In icon or small icon display mode,
4921 * retrieves the total number of visible items.
4924 * [I] infoPtr : valid pointer to the listview structure
4927 * Number of fully visible items.
4929 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4931 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4935 return infoPtr->nItemCount;
4937 return LISTVIEW_GetCountPerColumn(infoPtr);
4939 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4947 * Retrieves an image list handle.
4950 * [I] infoPtr : valid pointer to the listview structure
4951 * [I] nImageList : image list identifier
4954 * SUCCESS : image list handle
4957 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4961 case LVSIL_NORMAL: return infoPtr->himlNormal;
4962 case LVSIL_SMALL: return infoPtr->himlSmall;
4963 case LVSIL_STATE: return infoPtr->himlState;
4968 /* LISTVIEW_GetISearchString */
4972 * Retrieves item attributes.
4975 * [I] hwnd : window handle
4976 * [IO] lpLVItem : item info
4977 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4978 * if FALSE, the lpLVItem is a LPLVITEMA.
4981 * This is the internal 'GetItem' interface -- it tries to
4982 * be smart, and avoids text copies, if possible, by modifing
4983 * lpLVItem->pszText to point to the text string. Please note
4984 * that this is not always possible (e.g. OWNERDATA), so on
4985 * entry you *must* supply valid values for pszText, and cchTextMax.
4986 * The only difference to the documented interface is that upon
4987 * return, you should use *only* the lpLVItem->pszText, rather than
4988 * the buffer pointer you provided on input. Most code already does
4989 * that, so it's not a problem.
4990 * For the two cases when the text must be copied (that is,
4991 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
4997 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4999 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5000 NMLVDISPINFOW dispInfo;
5006 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5008 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5011 if (lpLVItem->mask == 0) return TRUE;
5013 /* make a local copy */
5014 isubitem = lpLVItem->iSubItem;
5016 /* a quick optimization if all we're asked is the focus state
5017 * these queries are worth optimising since they are common,
5018 * and can be answered in constant time, without the heavy accesses */
5019 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5020 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5022 lpLVItem->state = 0;
5023 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5024 lpLVItem->state |= LVIS_FOCUSED;
5028 ZeroMemory(&dispInfo, sizeof(dispInfo));
5030 /* if the app stores all the data, handle it separately */
5031 if (infoPtr->dwStyle & LVS_OWNERDATA)
5033 dispInfo.item.state = 0;
5035 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5036 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5038 /* NOTE: copy only fields which we _know_ are initialized, some apps
5039 * depend on the uninitialized fields being 0 */
5040 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5041 dispInfo.item.iItem = lpLVItem->iItem;
5042 dispInfo.item.iSubItem = isubitem;
5043 if (lpLVItem->mask & LVIF_TEXT)
5045 dispInfo.item.pszText = lpLVItem->pszText;
5046 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5048 if (lpLVItem->mask & LVIF_STATE)
5049 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5050 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5051 dispInfo.item.stateMask = lpLVItem->stateMask;
5052 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5054 /* full size structure expected - _WIN32IE >= 0x560 */
5055 *lpLVItem = dispInfo.item;
5057 else if (lpLVItem->mask & LVIF_INDENT)
5059 /* indent member expected - _WIN32IE >= 0x300 */
5060 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5064 /* minimal structure expected */
5065 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5067 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5070 /* make sure lParam is zeroed out */
5071 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5073 /* we store only a little state, so if we're not asked, we're done */
5074 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5076 /* if focus is handled by us, report it */
5077 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5079 lpLVItem->state &= ~LVIS_FOCUSED;
5080 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5081 lpLVItem->state |= LVIS_FOCUSED;
5084 /* and do the same for selection, if we handle it */
5085 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5087 lpLVItem->state &= ~LVIS_SELECTED;
5088 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5089 lpLVItem->state |= LVIS_SELECTED;
5095 /* find the item and subitem structures before we proceed */
5096 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5097 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5102 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5103 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5106 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5111 pItemHdr = &lpItem->hdr;
5113 /* Do we need to query the state from the app? */
5114 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5116 dispInfo.item.mask |= LVIF_STATE;
5117 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5120 /* Do we need to enquire about the image? */
5121 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5122 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5124 dispInfo.item.mask |= LVIF_IMAGE;
5125 dispInfo.item.iImage = I_IMAGECALLBACK;
5128 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5129 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5131 dispInfo.item.mask |= LVIF_TEXT;
5132 dispInfo.item.pszText = lpLVItem->pszText;
5133 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5134 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5135 *dispInfo.item.pszText = '\0';
5138 /* If we don't have all the requested info, query the application */
5139 if (dispInfo.item.mask != 0)
5141 dispInfo.item.iItem = lpLVItem->iItem;
5142 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5143 dispInfo.item.lParam = lpItem->lParam;
5144 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5145 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5148 /* we should not store values for subitems */
5149 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5151 /* Now, handle the iImage field */
5152 if (dispInfo.item.mask & LVIF_IMAGE)
5154 lpLVItem->iImage = dispInfo.item.iImage;
5155 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5156 pItemHdr->iImage = dispInfo.item.iImage;
5158 else if (lpLVItem->mask & LVIF_IMAGE)
5160 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5161 lpLVItem->iImage = pItemHdr->iImage;
5163 lpLVItem->iImage = 0;
5166 /* The pszText field */
5167 if (dispInfo.item.mask & LVIF_TEXT)
5169 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5170 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5172 lpLVItem->pszText = dispInfo.item.pszText;
5174 else if (lpLVItem->mask & LVIF_TEXT)
5176 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5177 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5180 /* if this is a subitem, we're done */
5181 if (isubitem) return TRUE;
5183 /* Next is the lParam field */
5184 if (dispInfo.item.mask & LVIF_PARAM)
5186 lpLVItem->lParam = dispInfo.item.lParam;
5187 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5188 lpItem->lParam = dispInfo.item.lParam;
5190 else if (lpLVItem->mask & LVIF_PARAM)
5191 lpLVItem->lParam = lpItem->lParam;
5193 /* ... the state field (this one is different due to uCallbackmask) */
5194 if (lpLVItem->mask & LVIF_STATE)
5196 lpLVItem->state = lpItem->state;
5197 if (dispInfo.item.mask & LVIF_STATE)
5199 lpLVItem->state &= ~dispInfo.item.stateMask;
5200 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5202 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5204 lpLVItem->state &= ~LVIS_FOCUSED;
5205 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5206 lpLVItem->state |= LVIS_FOCUSED;
5208 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5210 lpLVItem->state &= ~LVIS_SELECTED;
5211 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5212 lpLVItem->state |= LVIS_SELECTED;
5216 /* and last, but not least, the indent field */
5217 if (lpLVItem->mask & LVIF_INDENT)
5218 lpLVItem->iIndent = lpItem->iIndent;
5225 * Retrieves item attributes.
5228 * [I] hwnd : window handle
5229 * [IO] lpLVItem : item info
5230 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5231 * if FALSE, the lpLVItem is a LPLVITEMA.
5234 * This is the external 'GetItem' interface -- it properly copies
5235 * the text in the provided buffer.
5241 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5246 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5249 pszText = lpLVItem->pszText;
5250 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5251 if (bResult && lpLVItem->pszText != pszText)
5252 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5253 lpLVItem->pszText = pszText;
5261 * Retrieves the position (upper-left) of the listview control item.
5262 * Note that for LVS_ICON style, the upper-left is that of the icon
5263 * and not the bounding box.
5266 * [I] infoPtr : valid pointer to the listview structure
5267 * [I] nItem : item index
5268 * [O] lpptPosition : coordinate information
5274 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5276 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5279 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5281 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5283 LISTVIEW_GetOrigin(infoPtr, &Origin);
5284 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5286 if (uView == LVS_ICON)
5288 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5289 lpptPosition->y += ICON_TOP_PADDING;
5291 lpptPosition->x += Origin.x;
5292 lpptPosition->y += Origin.y;
5294 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5301 * Retrieves the bounding rectangle for a listview control item.
5304 * [I] infoPtr : valid pointer to the listview structure
5305 * [I] nItem : item index
5306 * [IO] lprc : bounding rectangle coordinates
5307 * lprc->left specifies the portion of the item for which the bounding
5308 * rectangle will be retrieved.
5310 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5311 * including the icon and label.
5314 * * Experiment shows that native control returns:
5315 * * width = min (48, length of text line)
5316 * * .left = position.x - (width - iconsize.cx)/2
5317 * * .right = .left + width
5318 * * height = #lines of text * ntmHeight + icon height + 8
5319 * * .top = position.y - 2
5320 * * .bottom = .top + height
5321 * * separation between items .y = itemSpacing.cy - height
5322 * * .x = itemSpacing.cx - width
5323 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5326 * * Experiment shows that native control returns:
5327 * * width = iconSize.cx + 16
5328 * * .left = position.x - (width - iconsize.cx)/2
5329 * * .right = .left + width
5330 * * height = iconSize.cy + 4
5331 * * .top = position.y - 2
5332 * * .bottom = .top + height
5333 * * separation between items .y = itemSpacing.cy - height
5334 * * .x = itemSpacing.cx - width
5335 * LVIR_LABEL Returns the bounding rectangle of the item text.
5338 * * Experiment shows that native control returns:
5339 * * width = text length
5340 * * .left = position.x - width/2
5341 * * .right = .left + width
5342 * * height = ntmH * linecount + 2
5343 * * .top = position.y + iconSize.cy + 6
5344 * * .bottom = .top + height
5345 * * separation between items .y = itemSpacing.cy - height
5346 * * .x = itemSpacing.cx - width
5347 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5348 * rectangles, but excludes columns in report view.
5355 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5356 * upon whether the window has the focus currently and on whether the item
5357 * is the one with the focus. Ensure that the control's record of which
5358 * item has the focus agrees with the items' records.
5360 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5362 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5363 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5364 BOOL doLabel = TRUE, oversizedBox = FALSE;
5365 POINT Position, Origin;
5369 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5371 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5373 LISTVIEW_GetOrigin(infoPtr, &Origin);
5374 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5376 /* Be smart and try to figure out the minimum we have to do */
5377 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5378 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5379 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5380 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5381 oversizedBox = TRUE;
5383 /* get what we need from the item before hand, so we make
5384 * only one request. This can speed up things, if data
5385 * is stored on the app side */
5387 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5388 if (doLabel) lvItem.mask |= LVIF_TEXT;
5389 lvItem.iItem = nItem;
5390 lvItem.iSubItem = 0;
5391 lvItem.pszText = szDispText;
5392 lvItem.cchTextMax = DISP_TEXT_SIZE;
5393 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5394 /* we got the state already up, simulate it here, to avoid a reget */
5395 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5397 lvItem.mask |= LVIF_STATE;
5398 lvItem.stateMask = LVIS_FOCUSED;
5399 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5402 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5403 lprc->left = LVIR_BOUNDS;
5407 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5411 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5415 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5418 case LVIR_SELECTBOUNDS:
5419 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5420 UnionRect(lprc, lprc, &label_rect);
5424 WARN("Unknown value: %ld\n", lprc->left);
5428 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5430 TRACE(" rect=%s\n", debugrect(lprc));
5437 * Retrieves the spacing between listview control items.
5440 * [I] infoPtr : valid pointer to the listview structure
5441 * [IO] lprc : rectangle to receive the output
5442 * on input, lprc->top = nSubItem
5443 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5445 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5446 * not only those of the first column.
5447 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5453 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5458 if (!lprc) return FALSE;
5460 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5461 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5463 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5465 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5467 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5470 lvItem.iItem = nItem;
5471 lvItem.iSubItem = lprc->top;
5473 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5477 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5482 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5486 ERR("Unknown bounds=%ld\n", lprc->left);
5490 OffsetRect(lprc, Position.x, Position.y);
5497 * Retrieves the width of a label.
5500 * [I] infoPtr : valid pointer to the listview structure
5503 * SUCCESS : string width (in pixels)
5506 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5508 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5511 TRACE("(nItem=%d)\n", nItem);
5513 lvItem.mask = LVIF_TEXT;
5514 lvItem.iItem = nItem;
5515 lvItem.iSubItem = 0;
5516 lvItem.pszText = szDispText;
5517 lvItem.cchTextMax = DISP_TEXT_SIZE;
5518 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5520 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5525 * Retrieves the spacing between listview control items.
5528 * [I] infoPtr : valid pointer to the listview structure
5529 * [I] bSmall : flag for small or large icon
5532 * Horizontal + vertical spacing
5534 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5540 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5544 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5545 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5547 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5554 * Retrieves the state of a listview control item.
5557 * [I] infoPtr : valid pointer to the listview structure
5558 * [I] nItem : item index
5559 * [I] uMask : state mask
5562 * State specified by the mask.
5564 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5568 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5570 lvItem.iItem = nItem;
5571 lvItem.iSubItem = 0;
5572 lvItem.mask = LVIF_STATE;
5573 lvItem.stateMask = uMask;
5574 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5576 return lvItem.state & uMask;
5581 * Retrieves the text of a listview control item or subitem.
5584 * [I] hwnd : window handle
5585 * [I] nItem : item index
5586 * [IO] lpLVItem : item information
5587 * [I] isW : TRUE if lpLVItem is Unicode
5590 * SUCCESS : string length
5593 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5595 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5597 lpLVItem->mask = LVIF_TEXT;
5598 lpLVItem->iItem = nItem;
5599 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5601 return textlenT(lpLVItem->pszText, isW);
5606 * Searches for an item based on properties + relationships.
5609 * [I] infoPtr : valid pointer to the listview structure
5610 * [I] nItem : item index
5611 * [I] uFlags : relationship flag
5614 * SUCCESS : item index
5617 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5619 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5621 LVFINDINFOW lvFindInfo;
5622 INT nCountPerColumn;
5626 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5627 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5629 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5631 if (uFlags & LVNI_CUT)
5634 if (uFlags & LVNI_DROPHILITED)
5635 uMask |= LVIS_DROPHILITED;
5637 if (uFlags & LVNI_FOCUSED)
5638 uMask |= LVIS_FOCUSED;
5640 if (uFlags & LVNI_SELECTED)
5641 uMask |= LVIS_SELECTED;
5643 /* if we're asked for the focused item, that's only one,
5644 * so it's worth optimizing */
5645 if (uFlags & LVNI_FOCUSED)
5647 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5648 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5651 if (uFlags & LVNI_ABOVE)
5653 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5658 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5664 /* Special case for autoarrange - move 'til the top of a list */
5665 if (is_autoarrange(infoPtr))
5667 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5668 while (nItem - nCountPerRow >= 0)
5670 nItem -= nCountPerRow;
5671 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5676 lvFindInfo.flags = LVFI_NEARESTXY;
5677 lvFindInfo.vkDirection = VK_UP;
5678 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5679 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5681 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5686 else if (uFlags & LVNI_BELOW)
5688 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5690 while (nItem < infoPtr->nItemCount)
5693 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5699 /* Special case for autoarrange - move 'til the bottom of a list */
5700 if (is_autoarrange(infoPtr))
5702 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5703 while (nItem + nCountPerRow < infoPtr->nItemCount )
5705 nItem += nCountPerRow;
5706 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5711 lvFindInfo.flags = LVFI_NEARESTXY;
5712 lvFindInfo.vkDirection = VK_DOWN;
5713 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5714 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5716 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5721 else if (uFlags & LVNI_TOLEFT)
5723 if (uView == LVS_LIST)
5725 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5726 while (nItem - nCountPerColumn >= 0)
5728 nItem -= nCountPerColumn;
5729 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5733 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5735 /* Special case for autoarrange - move 'ti the beginning of a row */
5736 if (is_autoarrange(infoPtr))
5738 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5739 while (nItem % nCountPerRow > 0)
5742 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5747 lvFindInfo.flags = LVFI_NEARESTXY;
5748 lvFindInfo.vkDirection = VK_LEFT;
5749 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5750 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5752 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5757 else if (uFlags & LVNI_TORIGHT)
5759 if (uView == LVS_LIST)
5761 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5762 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5764 nItem += nCountPerColumn;
5765 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5769 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5771 /* Special case for autoarrange - move 'til the end of a row */
5772 if (is_autoarrange(infoPtr))
5774 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5775 while (nItem % nCountPerRow < nCountPerRow - 1 )
5778 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5783 lvFindInfo.flags = LVFI_NEARESTXY;
5784 lvFindInfo.vkDirection = VK_RIGHT;
5785 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5786 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5788 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5797 /* search by index */
5798 for (i = nItem; i < infoPtr->nItemCount; i++)
5800 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5808 /* LISTVIEW_GetNumberOfWorkAreas */
5812 * Retrieves the origin coordinates when in icon or small icon display mode.
5815 * [I] infoPtr : valid pointer to the listview structure
5816 * [O] lpptOrigin : coordinate information
5821 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5823 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5824 INT nHorzPos = 0, nVertPos = 0;
5825 SCROLLINFO scrollInfo;
5827 scrollInfo.cbSize = sizeof(SCROLLINFO);
5828 scrollInfo.fMask = SIF_POS;
5830 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5831 nHorzPos = scrollInfo.nPos;
5832 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5833 nVertPos = scrollInfo.nPos;
5835 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5837 lpptOrigin->x = infoPtr->rcList.left;
5838 lpptOrigin->y = infoPtr->rcList.top;
5839 if (uView == LVS_LIST)
5840 nHorzPos *= infoPtr->nItemWidth;
5841 else if (uView == LVS_REPORT)
5842 nVertPos *= infoPtr->nItemHeight;
5844 lpptOrigin->x -= nHorzPos;
5845 lpptOrigin->y -= nVertPos;
5847 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5852 * Retrieves the width of a string.
5855 * [I] hwnd : window handle
5856 * [I] lpszText : text string to process
5857 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5860 * SUCCESS : string width (in pixels)
5863 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5868 if (is_textT(lpszText, isW))
5870 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5871 HDC hdc = GetDC(infoPtr->hwndSelf);
5872 HFONT hOldFont = SelectObject(hdc, hFont);
5875 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5877 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5878 SelectObject(hdc, hOldFont);
5879 ReleaseDC(infoPtr->hwndSelf, hdc);
5881 return stringSize.cx;
5886 * Determines which listview item is located at the specified position.
5889 * [I] infoPtr : valid pointer to the listview structure
5890 * [IO] lpht : hit test information
5891 * [I] subitem : fill out iSubItem.
5892 * [I] select : return the index only if the hit selects the item
5895 * (mm 20001022): We must not allow iSubItem to be touched, for
5896 * an app might pass only a structure with space up to iItem!
5897 * (MS Office 97 does that for instance in the file open dialog)
5900 * SUCCESS : item index
5903 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5905 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5906 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5907 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5908 POINT Origin, Position, opt;
5913 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5917 if (subitem) lpht->iSubItem = 0;
5919 if (infoPtr->rcList.left > lpht->pt.x)
5920 lpht->flags |= LVHT_TOLEFT;
5921 else if (infoPtr->rcList.right < lpht->pt.x)
5922 lpht->flags |= LVHT_TORIGHT;
5924 if (infoPtr->rcList.top > lpht->pt.y)
5925 lpht->flags |= LVHT_ABOVE;
5926 else if (infoPtr->rcList.bottom < lpht->pt.y)
5927 lpht->flags |= LVHT_BELOW;
5929 TRACE("lpht->flags=0x%x\n", lpht->flags);
5930 if (lpht->flags) return -1;
5932 lpht->flags |= LVHT_NOWHERE;
5934 LISTVIEW_GetOrigin(infoPtr, &Origin);
5936 /* first deal with the large items */
5937 rcSearch.left = lpht->pt.x;
5938 rcSearch.top = lpht->pt.y;
5939 rcSearch.right = rcSearch.left + 1;
5940 rcSearch.bottom = rcSearch.top + 1;
5942 iterator_frameditems(&i, infoPtr, &rcSearch);
5943 iterator_next(&i); /* go to first item in the sequence */
5945 iterator_destroy(&i);
5947 TRACE("lpht->iItem=%d\n", iItem);
5948 if (iItem == -1) return -1;
5950 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5951 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5952 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5953 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5954 lvItem.iItem = iItem;
5955 lvItem.iSubItem = 0;
5956 lvItem.pszText = szDispText;
5957 lvItem.cchTextMax = DISP_TEXT_SIZE;
5958 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5959 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5961 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5962 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5963 opt.x = lpht->pt.x - Position.x - Origin.x;
5964 opt.y = lpht->pt.y - Position.y - Origin.y;
5966 if (uView == LVS_REPORT)
5969 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5970 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5971 if (!PtInRect(&rcBounds, opt)) return -1;
5973 if (PtInRect(&rcIcon, opt))
5974 lpht->flags |= LVHT_ONITEMICON;
5975 else if (PtInRect(&rcLabel, opt))
5976 lpht->flags |= LVHT_ONITEMLABEL;
5977 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5978 lpht->flags |= LVHT_ONITEMSTATEICON;
5979 if (lpht->flags & LVHT_ONITEM)
5980 lpht->flags &= ~LVHT_NOWHERE;
5982 TRACE("lpht->flags=0x%x\n", lpht->flags);
5983 if (uView == LVS_REPORT && subitem)
5987 rcBounds.right = rcBounds.left;
5988 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
5990 rcBounds.left = rcBounds.right;
5991 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5992 if (PtInRect(&rcBounds, opt))
6000 if (select && !(uView == LVS_REPORT &&
6001 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6002 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6004 if (uView == LVS_REPORT)
6006 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6007 UnionRect(&rcBounds, &rcBounds, &rcState);
6009 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6011 return lpht->iItem = iItem;
6015 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6016 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6017 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6018 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6019 their own sort proc. when sending LVM_SORTITEMS.
6022 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6024 LVS_SORTXXX must be specified,
6025 LVS_OWNERDRAW is not set,
6026 <item>.pszText is not LPSTR_TEXTCALLBACK.
6028 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6029 are sorted based on item text..."
6031 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6033 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6034 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6035 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6037 /* if we're sorting descending, negate the return value */
6038 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6043 * Inserts a new item in the listview control.
6046 * [I] infoPtr : valid pointer to the listview structure
6047 * [I] lpLVItem : item information
6048 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6051 * SUCCESS : new item index
6054 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6056 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6061 BOOL is_sorted, has_changed;
6064 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6066 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6068 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6069 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6071 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6073 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6075 /* insert item in listview control data structure */
6076 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6077 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6079 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6080 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6082 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6083 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6084 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6085 if (nItem == -1) goto fail;
6086 infoPtr->nItemCount++;
6088 /* shift indices first so they don't get tangled */
6089 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6091 /* set the item attributes */
6092 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6094 /* full size structure expected - _WIN32IE >= 0x560 */
6097 else if (lpLVItem->mask & LVIF_INDENT)
6099 /* indent member expected - _WIN32IE >= 0x300 */
6100 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6104 /* minimal structure expected */
6105 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6108 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6109 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6111 /* if we're sorted, sort the list, and update the index */
6114 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6115 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6116 assert(nItem != -1);
6119 /* make room for the position, if we are in the right mode */
6120 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6122 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6124 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6126 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6131 /* send LVN_INSERTITEM notification */
6132 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6134 nmlv.lParam = lpItem->lParam;
6135 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6137 /* align items (set position of each item) */
6138 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6142 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6143 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6145 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6147 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6150 /* now is the invalidation fun */
6151 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6155 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6156 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6157 infoPtr->nItemCount--;
6159 DPA_DeletePtr(hdpaSubItems, 0);
6160 DPA_Destroy (hdpaSubItems);
6167 * Redraws a range of items.
6170 * [I] infoPtr : valid pointer to the listview structure
6171 * [I] nFirst : first item
6172 * [I] nLast : last item
6178 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6182 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6183 max(nFirst, nLast) >= infoPtr->nItemCount)
6186 for (i = nFirst; i <= nLast; i++)
6187 LISTVIEW_InvalidateItem(infoPtr, i);
6194 * Scroll the content of a listview.
6197 * [I] infoPtr : valid pointer to the listview structure
6198 * [I] dx : horizontal scroll amount in pixels
6199 * [I] dy : vertical scroll amount in pixels
6206 * If the control is in report mode (LVS_REPORT) the control can
6207 * be scrolled only in line increments. "dy" will be rounded to the
6208 * nearest number of pixels that are a whole line. Ex: if line height
6209 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6210 * is passed the the scroll will be 0. (per MSDN 7/2002)
6212 * For: (per experimentaion with native control and CSpy ListView)
6213 * LVS_ICON dy=1 = 1 pixel (vertical only)
6215 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6217 * LVS_LIST dx=1 = 1 column (horizontal only)
6218 * but will only scroll 1 column per message
6219 * no matter what the value.
6220 * dy must be 0 or FALSE returned.
6221 * LVS_REPORT dx=1 = 1 pixel
6225 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6227 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6229 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6230 dy /= infoPtr->nItemHeight;
6233 if (dy != 0) return FALSE;
6240 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6241 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6248 * Sets the background color.
6251 * [I] infoPtr : valid pointer to the listview structure
6252 * [I] clrBk : background color
6258 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6260 TRACE("(clrBk=%lx)\n", clrBk);
6262 if(infoPtr->clrBk != clrBk) {
6263 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6264 infoPtr->clrBk = clrBk;
6265 if (clrBk == CLR_NONE)
6266 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6268 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6269 LISTVIEW_InvalidateList(infoPtr);
6275 /* LISTVIEW_SetBkImage */
6277 /*** Helper for {Insert,Set}ColumnT *only* */
6278 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6280 if (lpColumn->mask & LVCF_FMT)
6282 /* format member is valid */
6283 lphdi->mask |= HDI_FORMAT;
6285 /* set text alignment (leftmost column must be left-aligned) */
6286 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6287 lphdi->fmt |= HDF_LEFT;
6288 else if (lpColumn->fmt & LVCFMT_RIGHT)
6289 lphdi->fmt |= HDF_RIGHT;
6290 else if (lpColumn->fmt & LVCFMT_CENTER)
6291 lphdi->fmt |= HDF_CENTER;
6293 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6294 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6296 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6298 lphdi->fmt |= HDF_IMAGE;
6299 lphdi->iImage = I_IMAGECALLBACK;
6303 if (lpColumn->mask & LVCF_WIDTH)
6305 lphdi->mask |= HDI_WIDTH;
6306 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6308 /* make it fill the remainder of the controls width */
6312 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6314 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6315 lphdi->cxy += rcHeader.right - rcHeader.left;
6318 /* retrieve the layout of the header */
6319 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6320 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6322 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6325 lphdi->cxy = lpColumn->cx;
6328 if (lpColumn->mask & LVCF_TEXT)
6330 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6331 lphdi->fmt |= HDF_STRING;
6332 lphdi->pszText = lpColumn->pszText;
6333 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6336 if (lpColumn->mask & LVCF_IMAGE)
6338 lphdi->mask |= HDI_IMAGE;
6339 lphdi->iImage = lpColumn->iImage;
6342 if (lpColumn->mask & LVCF_ORDER)
6344 lphdi->mask |= HDI_ORDER;
6345 lphdi->iOrder = lpColumn->iOrder;
6352 * Inserts a new column.
6355 * [I] infoPtr : valid pointer to the listview structure
6356 * [I] nColumn : column index
6357 * [I] lpColumn : column information
6358 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6361 * SUCCESS : new column index
6364 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6365 const LVCOLUMNW *lpColumn, BOOL isW)
6367 COLUMN_INFO *lpColumnInfo;
6371 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6373 if (!lpColumn || nColumn < 0) return -1;
6374 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6376 ZeroMemory(&hdi, sizeof(HDITEMW));
6377 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6379 /* insert item in header control */
6380 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6381 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6382 (WPARAM)nColumn, (LPARAM)&hdi);
6383 if (nNewColumn == -1) return -1;
6384 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6386 /* create our own column info */
6387 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6388 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6390 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6391 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6393 /* now we have to actually adjust the data */
6394 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6396 SUBITEM_INFO *lpSubItem;
6400 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6402 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6403 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6405 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6406 if (lpSubItem->iSubItem >= nNewColumn)
6407 lpSubItem->iSubItem++;
6412 /* make space for the new column */
6413 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6418 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6421 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6429 * Sets the attributes of a header item.
6432 * [I] infoPtr : valid pointer to the listview structure
6433 * [I] nColumn : column index
6434 * [I] lpColumn : column attributes
6435 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6441 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6442 const LVCOLUMNW *lpColumn, BOOL isW)
6444 HDITEMW hdi, hdiget;
6447 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6449 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6451 ZeroMemory(&hdi, sizeof(HDITEMW));
6452 if (lpColumn->mask & LVCF_FMT)
6454 hdi.mask |= HDI_FORMAT;
6455 hdiget.mask = HDI_FORMAT;
6456 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6457 hdi.fmt = hdiget.fmt & HDF_STRING;
6459 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6461 /* set header item attributes */
6462 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6463 if (!bResult) return FALSE;
6465 if (lpColumn->mask & LVCF_FMT)
6467 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6468 int oldFmt = lpColumnInfo->fmt;
6470 lpColumnInfo->fmt = lpColumn->fmt;
6471 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6473 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6474 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6483 * Sets the column order array
6486 * [I] infoPtr : valid pointer to the listview structure
6487 * [I] iCount : number of elements in column order array
6488 * [I] lpiArray : pointer to column order array
6494 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6496 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6507 * Sets the width of a column
6510 * [I] infoPtr : valid pointer to the listview structure
6511 * [I] nColumn : column index
6512 * [I] cx : column width
6518 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6520 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6521 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6525 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6527 /* set column width only if in report or list mode */
6528 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6530 /* take care of invalid cx values */
6531 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6532 else if (uView == LVS_LIST && cx < 1) return FALSE;
6534 /* resize all columns if in LVS_LIST mode */
6535 if(uView == LVS_LIST)
6537 infoPtr->nItemWidth = cx;
6538 LISTVIEW_InvalidateList(infoPtr);
6542 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6544 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6549 lvItem.mask = LVIF_TEXT;
6551 lvItem.iSubItem = nColumn;
6552 lvItem.pszText = szDispText;
6553 lvItem.cchTextMax = DISP_TEXT_SIZE;
6554 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6556 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6557 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6558 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6560 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6561 max_cx += infoPtr->iconSize.cx;
6562 max_cx += TRAILING_LABEL_PADDING;
6565 /* autosize based on listview items width */
6566 if(cx == LVSCW_AUTOSIZE)
6568 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6570 /* if iCol is the last column make it fill the remainder of the controls width */
6571 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6576 LISTVIEW_GetOrigin(infoPtr, &Origin);
6577 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6579 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6583 /* Despite what the MS docs say, if this is not the last
6584 column, then MS resizes the column to the width of the
6585 largest text string in the column, including headers
6586 and items. This is different from LVSCW_AUTOSIZE in that
6587 LVSCW_AUTOSIZE ignores the header string length. */
6590 /* retrieve header text */
6591 hdi.mask = HDI_TEXT;
6592 hdi.cchTextMax = DISP_TEXT_SIZE;
6593 hdi.pszText = szDispText;
6594 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6596 HDC hdc = GetDC(infoPtr->hwndSelf);
6597 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6600 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6601 cx = size.cx + TRAILING_HEADER_PADDING;
6602 /* FIXME: Take into account the header image, if one is present */
6603 SelectObject(hdc, old_font);
6604 ReleaseDC(infoPtr->hwndSelf, hdc);
6606 cx = max (cx, max_cx);
6610 if (cx < 0) return FALSE;
6612 /* call header to update the column change */
6613 hdi.mask = HDI_WIDTH;
6615 TRACE("hdi.cxy=%d\n", hdi.cxy);
6616 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6620 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6623 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6626 HBITMAP hbm_im, hbm_mask, hbm_orig;
6628 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6629 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6632 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6633 ILC_COLOR | ILC_MASK, 2, 2);
6634 hdc_wnd = GetDC(infoPtr->hwndSelf);
6635 hdc = CreateCompatibleDC(hdc_wnd);
6636 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6637 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6638 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6640 rc.left = rc.top = 0;
6641 rc.right = GetSystemMetrics(SM_CXSMICON);
6642 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6644 hbm_orig = SelectObject(hdc, hbm_mask);
6645 FillRect(hdc, &rc, hbr_white);
6646 InflateRect(&rc, -3, -3);
6647 FillRect(hdc, &rc, hbr_black);
6649 SelectObject(hdc, hbm_im);
6650 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6651 SelectObject(hdc, hbm_orig);
6652 ImageList_Add(himl, hbm_im, hbm_mask);
6654 SelectObject(hdc, hbm_im);
6655 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6656 SelectObject(hdc, hbm_orig);
6657 ImageList_Add(himl, hbm_im, hbm_mask);
6659 DeleteObject(hbm_mask);
6660 DeleteObject(hbm_im);
6668 * Sets the extended listview style.
6671 * [I] infoPtr : valid pointer to the listview structure
6673 * [I] dwStyle : style
6676 * SUCCESS : previous style
6679 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6681 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6685 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6687 infoPtr->dwLvExStyle = dwStyle;
6689 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6691 HIMAGELIST himl = 0;
6692 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6693 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6694 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6702 * Sets the new hot cursor used during hot tracking and hover selection.
6705 * [I] infoPtr : valid pointer to the listview structure
6706 * [I} hCurosr : the new hot cursor handle
6709 * Returns the previous hot cursor
6711 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6713 HCURSOR oldCursor = infoPtr->hHotCursor;
6715 infoPtr->hHotCursor = hCursor;
6723 * Sets the hot item index.
6726 * [I] infoPtr : valid pointer to the listview structure
6727 * [I] iIndex : index
6730 * SUCCESS : previous hot item index
6731 * FAILURE : -1 (no hot item)
6733 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6735 INT iOldIndex = infoPtr->nHotItem;
6737 infoPtr->nHotItem = iIndex;
6745 * Sets the amount of time the cursor must hover over an item before it is selected.
6748 * [I] infoPtr : valid pointer to the listview structure
6749 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6752 * Returns the previous hover time
6754 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6756 DWORD oldHoverTime = infoPtr->dwHoverTime;
6758 infoPtr->dwHoverTime = dwHoverTime;
6760 return oldHoverTime;
6765 * Sets spacing for icons of LVS_ICON style.
6768 * [I] infoPtr : valid pointer to the listview structure
6769 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6770 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6773 * MAKELONG(oldcx, oldcy)
6775 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6777 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6778 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6780 TRACE("requested=(%d,%d)\n", cx, cy);
6782 /* this is supported only for LVS_ICON style */
6783 if (uView != LVS_ICON) return oldspacing;
6785 /* set to defaults, if instructed to */
6786 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6787 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6789 /* if 0 then compute width
6790 * FIXME: Should scan each item and determine max width of
6791 * icon or label, then make that the width */
6793 cx = infoPtr->iconSpacing.cx;
6795 /* if 0 then compute height */
6797 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6798 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6801 infoPtr->iconSpacing.cx = cx;
6802 infoPtr->iconSpacing.cy = cy;
6804 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6805 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6806 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6807 infoPtr->ntmHeight);
6809 /* these depend on the iconSpacing */
6810 LISTVIEW_UpdateItemSize(infoPtr);
6815 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6819 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6826 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6827 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6836 * [I] infoPtr : valid pointer to the listview structure
6837 * [I] nType : image list type
6838 * [I] himl : image list handle
6841 * SUCCESS : old image list
6844 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6846 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6847 INT oldHeight = infoPtr->nItemHeight;
6848 HIMAGELIST himlOld = 0;
6850 TRACE("(nType=%d, himl=%p\n", nType, himl);
6855 himlOld = infoPtr->himlNormal;
6856 infoPtr->himlNormal = himl;
6857 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6858 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6862 himlOld = infoPtr->himlSmall;
6863 infoPtr->himlSmall = himl;
6864 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6868 himlOld = infoPtr->himlState;
6869 infoPtr->himlState = himl;
6870 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6871 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6875 ERR("Unknown icon type=%d\n", nType);
6879 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6880 if (infoPtr->nItemHeight != oldHeight)
6881 LISTVIEW_UpdateScroll(infoPtr);
6888 * Preallocates memory (does *not* set the actual count of items !)
6891 * [I] infoPtr : valid pointer to the listview structure
6892 * [I] nItems : item count (projected number of items to allocate)
6893 * [I] dwFlags : update flags
6899 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6901 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6903 if (infoPtr->dwStyle & LVS_OWNERDATA)
6905 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6906 INT nOldCount = infoPtr->nItemCount;
6908 if (nItems < nOldCount)
6910 RANGE range = { nItems, nOldCount };
6911 ranges_del(infoPtr->selectionRanges, range);
6912 if (infoPtr->nFocusedItem >= nItems)
6914 infoPtr->nFocusedItem = -1;
6915 SetRectEmpty(&infoPtr->rcFocus);
6919 infoPtr->nItemCount = nItems;
6920 LISTVIEW_UpdateScroll(infoPtr);
6922 /* the flags are valid only in ownerdata report and list modes */
6923 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6925 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6926 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6928 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6929 LISTVIEW_InvalidateList(infoPtr);
6936 LISTVIEW_GetOrigin(infoPtr, &Origin);
6937 nFrom = min(nOldCount, nItems);
6938 nTo = max(nOldCount, nItems);
6940 if (uView == LVS_REPORT)
6943 rcErase.top = nFrom * infoPtr->nItemHeight;
6944 rcErase.right = infoPtr->nItemWidth;
6945 rcErase.bottom = nTo * infoPtr->nItemHeight;
6946 OffsetRect(&rcErase, Origin.x, Origin.y);
6947 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6948 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6952 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6954 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6955 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6956 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6957 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6958 OffsetRect(&rcErase, Origin.x, Origin.y);
6959 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6960 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6962 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6964 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6965 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6966 OffsetRect(&rcErase, Origin.x, Origin.y);
6967 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6968 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6974 /* According to MSDN for non-LVS_OWNERDATA this is just
6975 * a performance issue. The control allocates its internal
6976 * data structures for the number of items specified. It
6977 * cuts down on the number of memory allocations. Therefore
6978 * we will just issue a WARN here
6980 WARN("for non-ownerdata performance option not implemented.\n");
6988 * Sets the position of an item.
6991 * [I] infoPtr : valid pointer to the listview structure
6992 * [I] nItem : item index
6993 * [I] pt : coordinate
6999 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7001 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7004 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7006 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7007 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7009 LISTVIEW_GetOrigin(infoPtr, &Origin);
7011 /* This point value seems to be an undocumented feature.
7012 * The best guess is that it means either at the origin,
7013 * or at true beginning of the list. I will assume the origin. */
7014 if ((pt.x == -1) && (pt.y == -1))
7017 if (uView == LVS_ICON)
7019 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7020 pt.y -= ICON_TOP_PADDING;
7025 infoPtr->bAutoarrange = FALSE;
7027 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7032 * Sets the state of one or many items.
7035 * [I] infoPtr : valid pointer to the listview structure
7036 * [I] nItem : item index
7037 * [I] lpLVItem : item or subitem info
7043 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7045 BOOL bResult = TRUE;
7048 lvItem.iItem = nItem;
7049 lvItem.iSubItem = 0;
7050 lvItem.mask = LVIF_STATE;
7051 lvItem.state = lpLVItem->state;
7052 lvItem.stateMask = lpLVItem->stateMask;
7053 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7057 /* apply to all items */
7058 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7059 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7062 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7065 *update selection mark
7067 * Investigation on windows 2k showed that selection mark was updated
7068 * whenever a new selection was made, but if the selected item was
7069 * unselected it was not updated.
7071 * we are probably still not 100% accurate, but this at least sets the
7072 * proper selection mark when it is needed
7075 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7076 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7079 infoPtr->nSelectionMark = -1;
7080 for (i = 0; i < infoPtr->nItemCount; i++)
7082 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7084 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7086 infoPtr->nSelectionMark = i;
7090 else if (ranges_contain(infoPtr->selectionRanges, i))
7092 infoPtr->nSelectionMark = i;
7103 * Sets the text of an item or subitem.
7106 * [I] hwnd : window handle
7107 * [I] nItem : item index
7108 * [I] lpLVItem : item or subitem info
7109 * [I] isW : TRUE if input is Unicode
7115 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7119 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7121 lvItem.iItem = nItem;
7122 lvItem.iSubItem = lpLVItem->iSubItem;
7123 lvItem.mask = LVIF_TEXT;
7124 lvItem.pszText = lpLVItem->pszText;
7125 lvItem.cchTextMax = lpLVItem->cchTextMax;
7127 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7129 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7134 * Set item index that marks the start of a multiple selection.
7137 * [I] infoPtr : valid pointer to the listview structure
7138 * [I] nIndex : index
7141 * Index number or -1 if there is no selection mark.
7143 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7145 INT nOldIndex = infoPtr->nSelectionMark;
7147 TRACE("(nIndex=%d)\n", nIndex);
7149 infoPtr->nSelectionMark = nIndex;
7156 * Sets the text background color.
7159 * [I] infoPtr : valid pointer to the listview structure
7160 * [I] clrTextBk : text background color
7166 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7168 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7170 if (infoPtr->clrTextBk != clrTextBk)
7172 infoPtr->clrTextBk = clrTextBk;
7173 LISTVIEW_InvalidateList(infoPtr);
7181 * Sets the text foreground color.
7184 * [I] infoPtr : valid pointer to the listview structure
7185 * [I] clrText : text color
7191 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7193 TRACE("(clrText=%lx)\n", clrText);
7195 if (infoPtr->clrText != clrText)
7197 infoPtr->clrText = clrText;
7198 LISTVIEW_InvalidateList(infoPtr);
7206 * Determines which listview item is located at the specified position.
7209 * [I] infoPtr : valid pointer to the listview structure
7210 * [I] hwndNewToolTip : handle to new ToolTip
7215 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7217 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7218 infoPtr->hwndToolTip = hwndNewToolTip;
7219 return hwndOldToolTip;
7222 /* LISTVIEW_SetUnicodeFormat */
7223 /* LISTVIEW_SetWorkAreas */
7227 * Callback internally used by LISTVIEW_SortItems()
7230 * [I] first : pointer to first ITEM_INFO to compare
7231 * [I] second : pointer to second ITEM_INFO to compare
7232 * [I] lParam : HWND of control
7235 * if first comes before second : negative
7236 * if first comes after second : positive
7237 * if first and second are equivalent : zero
7239 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7241 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7242 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7243 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7245 /* Forward the call to the client defined callback */
7246 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7251 * Sorts the listview items.
7254 * [I] infoPtr : valid pointer to the listview structure
7255 * [I] pfnCompare : application-defined value
7256 * [I] lParamSort : pointer to comparision callback
7262 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7264 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7267 LPVOID selectionMarkItem;
7271 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7273 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7275 if (!pfnCompare) return FALSE;
7276 if (!infoPtr->hdpaItems) return FALSE;
7278 /* if there are 0 or 1 items, there is no need to sort */
7279 if (infoPtr->nItemCount < 2) return TRUE;
7281 if (infoPtr->nFocusedItem >= 0)
7283 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7284 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7285 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7287 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7288 /* clear the lpItem->state for non-selected ones */
7289 /* remove the selection ranges */
7291 infoPtr->pfnCompare = pfnCompare;
7292 infoPtr->lParamSort = lParamSort;
7293 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7295 /* Adjust selections and indices so that they are the way they should
7296 * be after the sort (otherwise, the list items move around, but
7297 * whatever is at the item's previous original position will be
7300 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7301 for (i=0; i < infoPtr->nItemCount; i++)
7303 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7304 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7306 if (lpItem->state & LVIS_SELECTED)
7308 item.state = LVIS_SELECTED;
7309 item.stateMask = LVIS_SELECTED;
7310 LISTVIEW_SetItemState(infoPtr, i, &item);
7312 if (lpItem->state & LVIS_FOCUSED)
7314 infoPtr->nFocusedItem = i;
7315 lpItem->state &= ~LVIS_FOCUSED;
7318 if (selectionMarkItem != NULL)
7319 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7320 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7322 /* refresh the display */
7323 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7324 LISTVIEW_InvalidateList(infoPtr);
7331 * Updates an items or rearranges the listview control.
7334 * [I] infoPtr : valid pointer to the listview structure
7335 * [I] nItem : item index
7341 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7343 TRACE("(nItem=%d)\n", nItem);
7345 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7347 /* rearrange with default alignment style */
7348 if (is_autoarrange(infoPtr))
7349 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7351 LISTVIEW_InvalidateItem(infoPtr, nItem);
7359 * Creates the listview control.
7362 * [I] hwnd : window handle
7363 * [I] lpcs : the create parameters
7369 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7371 LISTVIEW_INFO *infoPtr;
7372 UINT uView = lpcs->style & LVS_TYPEMASK;
7375 TRACE("(lpcs=%p)\n", lpcs);
7377 /* initialize info pointer */
7378 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7379 if (!infoPtr) return -1;
7381 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7383 infoPtr->hwndSelf = hwnd;
7384 infoPtr->dwStyle = lpcs->style;
7385 /* determine the type of structures to use */
7386 infoPtr->hwndNotify = lpcs->hwndParent;
7387 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7388 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7390 /* initialize color information */
7391 infoPtr->clrBk = CLR_NONE;
7392 infoPtr->clrText = comctl32_color.clrWindowText;
7393 infoPtr->clrTextBk = CLR_DEFAULT;
7394 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7396 /* set default values */
7397 infoPtr->nFocusedItem = -1;
7398 infoPtr->nSelectionMark = -1;
7399 infoPtr->nHotItem = -1;
7400 infoPtr->bRedraw = TRUE;
7401 infoPtr->bNoItemMetrics = TRUE;
7402 infoPtr->bDoChangeNotify = TRUE;
7403 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7404 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7405 infoPtr->nEditLabelItem = -1;
7406 infoPtr->dwHoverTime = -1; /* default system hover time */
7408 /* get default font (icon title) */
7409 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7410 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7411 infoPtr->hFont = infoPtr->hDefaultFont;
7412 LISTVIEW_SaveTextMetrics(infoPtr);
7415 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7416 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7417 0, 0, 0, 0, hwnd, NULL,
7418 lpcs->hInstance, NULL);
7419 if (!infoPtr->hwndHeader) goto fail;
7421 /* set header unicode format */
7422 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7424 /* set header font */
7425 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7427 /* allocate memory for the data structure */
7428 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7429 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7430 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7431 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7432 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7434 /* initialize the icon sizes */
7435 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7436 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7438 /* init item size to avoid division by 0 */
7439 LISTVIEW_UpdateItemSize (infoPtr);
7441 if (uView == LVS_REPORT)
7443 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7445 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7449 /* set HDS_HIDDEN flag to hide the header bar */
7450 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7451 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7458 DestroyWindow(infoPtr->hwndHeader);
7459 ranges_destroy(infoPtr->selectionRanges);
7460 DPA_Destroy(infoPtr->hdpaItems);
7461 DPA_Destroy(infoPtr->hdpaPosX);
7462 DPA_Destroy(infoPtr->hdpaPosY);
7463 DPA_Destroy(infoPtr->hdpaColumns);
7470 * Erases the background of the listview control.
7473 * [I] infoPtr : valid pointer to the listview structure
7474 * [I] hdc : device context handle
7480 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7484 TRACE("(hdc=%p)\n", hdc);
7486 if (!GetClipBox(hdc, &rc)) return FALSE;
7488 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7494 * Helper function for LISTVIEW_[HV]Scroll *only*.
7495 * Performs vertical/horizontal scrolling by a give amount.
7498 * [I] infoPtr : valid pointer to the listview structure
7499 * [I] dx : amount of horizontal scroll
7500 * [I] dy : amount of vertical scroll
7502 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7504 /* now we can scroll the list */
7505 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7506 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7507 /* if we have focus, adjust rect */
7508 OffsetRect(&infoPtr->rcFocus, dx, dy);
7509 UpdateWindow(infoPtr->hwndSelf);
7514 * Performs vertical scrolling.
7517 * [I] infoPtr : valid pointer to the listview structure
7518 * [I] nScrollCode : scroll code
7519 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7520 * [I] hScrollWnd : scrollbar control window handle
7526 * SB_LINEUP/SB_LINEDOWN:
7527 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7528 * for LVS_REPORT is 1 line
7529 * for LVS_LIST cannot occur
7532 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7533 INT nScrollDiff, HWND hScrollWnd)
7535 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7536 INT nOldScrollPos, nNewScrollPos;
7537 SCROLLINFO scrollInfo;
7540 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7541 debugscrollcode(nScrollCode), nScrollDiff);
7543 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7545 scrollInfo.cbSize = sizeof(SCROLLINFO);
7546 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7548 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7550 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7552 nOldScrollPos = scrollInfo.nPos;
7553 switch (nScrollCode)
7559 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7563 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7567 nScrollDiff = -scrollInfo.nPage;
7571 nScrollDiff = scrollInfo.nPage;
7574 case SB_THUMBPOSITION:
7576 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7583 /* quit right away if pos isn't changing */
7584 if (nScrollDiff == 0) return 0;
7586 /* calculate new position, and handle overflows */
7587 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7588 if (nScrollDiff > 0) {
7589 if (nNewScrollPos < nOldScrollPos ||
7590 nNewScrollPos > scrollInfo.nMax)
7591 nNewScrollPos = scrollInfo.nMax;
7593 if (nNewScrollPos > nOldScrollPos ||
7594 nNewScrollPos < scrollInfo.nMin)
7595 nNewScrollPos = scrollInfo.nMin;
7598 /* set the new position, and reread in case it changed */
7599 scrollInfo.fMask = SIF_POS;
7600 scrollInfo.nPos = nNewScrollPos;
7601 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7603 /* carry on only if it really changed */
7604 if (nNewScrollPos == nOldScrollPos) return 0;
7606 /* now adjust to client coordinates */
7607 nScrollDiff = nOldScrollPos - nNewScrollPos;
7608 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7610 /* and scroll the window */
7611 scroll_list(infoPtr, 0, nScrollDiff);
7618 * Performs horizontal scrolling.
7621 * [I] infoPtr : valid pointer to the listview structure
7622 * [I] nScrollCode : scroll code
7623 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7624 * [I] hScrollWnd : scrollbar control window handle
7630 * SB_LINELEFT/SB_LINERIGHT:
7631 * for LVS_ICON, LVS_SMALLICON 1 pixel
7632 * for LVS_REPORT is 1 pixel
7633 * for LVS_LIST is 1 column --> which is a 1 because the
7634 * scroll is based on columns not pixels
7637 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7638 INT nScrollDiff, HWND hScrollWnd)
7640 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7641 INT nOldScrollPos, nNewScrollPos;
7642 SCROLLINFO scrollInfo;
7644 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7645 debugscrollcode(nScrollCode), nScrollDiff);
7647 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7649 scrollInfo.cbSize = sizeof(SCROLLINFO);
7650 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7652 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7654 nOldScrollPos = scrollInfo.nPos;
7656 switch (nScrollCode)
7670 nScrollDiff = -scrollInfo.nPage;
7674 nScrollDiff = scrollInfo.nPage;
7677 case SB_THUMBPOSITION:
7679 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7686 /* quit right away if pos isn't changing */
7687 if (nScrollDiff == 0) return 0;
7689 /* calculate new position, and handle overflows */
7690 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7691 if (nScrollDiff > 0) {
7692 if (nNewScrollPos < nOldScrollPos ||
7693 nNewScrollPos > scrollInfo.nMax)
7694 nNewScrollPos = scrollInfo.nMax;
7696 if (nNewScrollPos > nOldScrollPos ||
7697 nNewScrollPos < scrollInfo.nMin)
7698 nNewScrollPos = scrollInfo.nMin;
7701 /* set the new position, and reread in case it changed */
7702 scrollInfo.fMask = SIF_POS;
7703 scrollInfo.nPos = nNewScrollPos;
7704 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7706 /* carry on only if it really changed */
7707 if (nNewScrollPos == nOldScrollPos) return 0;
7709 if(uView == LVS_REPORT)
7710 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7712 /* now adjust to client coordinates */
7713 nScrollDiff = nOldScrollPos - nNewScrollPos;
7714 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7716 /* and scroll the window */
7717 scroll_list(infoPtr, nScrollDiff, 0);
7722 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7724 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7725 INT gcWheelDelta = 0;
7726 INT pulScrollLines = 3;
7727 SCROLLINFO scrollInfo;
7729 TRACE("(wheelDelta=%d)\n", wheelDelta);
7731 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7732 gcWheelDelta -= wheelDelta;
7734 scrollInfo.cbSize = sizeof(SCROLLINFO);
7735 scrollInfo.fMask = SIF_POS;
7742 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7743 * should be fixed in the future.
7745 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7746 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7750 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7752 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7753 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7754 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7759 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7770 * [I] infoPtr : valid pointer to the listview structure
7771 * [I] nVirtualKey : virtual key
7772 * [I] lKeyData : key data
7777 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7779 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7781 NMLVKEYDOWN nmKeyDown;
7783 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7785 /* send LVN_KEYDOWN notification */
7786 nmKeyDown.wVKey = nVirtualKey;
7787 nmKeyDown.flags = 0;
7788 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7790 switch (nVirtualKey)
7793 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7795 notify(infoPtr, NM_RETURN);
7796 notify(infoPtr, LVN_ITEMACTIVATE);
7801 if (infoPtr->nItemCount > 0)
7806 if (infoPtr->nItemCount > 0)
7807 nItem = infoPtr->nItemCount - 1;
7811 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7815 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7819 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7823 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7827 if (uView == LVS_REPORT)
7828 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7830 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7831 * LISTVIEW_GetCountPerRow(infoPtr);
7832 if(nItem < 0) nItem = 0;
7836 if (uView == LVS_REPORT)
7837 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7839 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7840 * LISTVIEW_GetCountPerRow(infoPtr);
7841 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7845 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7846 LISTVIEW_KeySelection(infoPtr, nItem);
7856 * [I] infoPtr : valid pointer to the listview structure
7861 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7865 /* if we did not have the focus, there's nothing to do */
7866 if (!infoPtr->bFocus) return 0;
7868 /* send NM_KILLFOCUS notification */
7869 notify(infoPtr, NM_KILLFOCUS);
7871 /* if we have a focus rectagle, get rid of it */
7872 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7874 /* set window focus flag */
7875 infoPtr->bFocus = FALSE;
7877 /* invalidate the selected items before reseting focus flag */
7878 LISTVIEW_InvalidateSelectedItems(infoPtr);
7886 * Track mouse/dragging
7889 * [I] infoPtr : valid pointer to the listview structure
7890 * [I] pt : mouse coordinate
7895 static LRESULT LISTVIEW_TrackMouse(LISTVIEW_INFO *infoPtr, POINT pt)
7897 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
7898 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
7904 r.top = pt.y - cyDrag;
7905 r.left = pt.x - cxDrag;
7906 r.bottom = pt.y + cyDrag;
7907 r.right = pt.x + cxDrag;
7909 SetCapture(infoPtr->hwndSelf);
7913 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
7915 if (msg.message == WM_MOUSEMOVE)
7917 pt.x = (short)LOWORD(msg.lParam);
7918 pt.y = (short)HIWORD(msg.lParam);
7919 if (PtInRect(&r, pt))
7927 else if (msg.message >= WM_LBUTTONDOWN &&
7928 msg.message <= WM_RBUTTONDBLCLK)
7933 DispatchMessageW(&msg);
7936 if (GetCapture() != infoPtr->hwndSelf)
7947 * Processes double click messages (left mouse button).
7950 * [I] infoPtr : valid pointer to the listview structure
7951 * [I] wKey : key flag
7952 * [I] pts : mouse coordinate
7957 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7959 LVHITTESTINFO htInfo;
7961 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7963 /* send NM_RELEASEDCAPTURE notification */
7964 notify(infoPtr, NM_RELEASEDCAPTURE);
7966 htInfo.pt.x = pts.x;
7967 htInfo.pt.y = pts.y;
7969 /* send NM_DBLCLK notification */
7970 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7971 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7973 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7974 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
7981 * Processes mouse down messages (left mouse button).
7984 * [I] infoPtr : valid pointer to the listview structure
7985 * [I] wKey : key flag
7986 * [I] pts : mouse coordinate
7991 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7993 LVHITTESTINFO lvHitTestInfo;
7994 static BOOL bGroupSelect = TRUE;
7995 POINT pt = { pts.x, pts.y };
7998 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8000 /* send NM_RELEASEDCAPTURE notification */
8001 notify(infoPtr, NM_RELEASEDCAPTURE);
8003 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8005 /* set left button down flag */
8006 infoPtr->bLButtonDown = TRUE;
8008 lvHitTestInfo.pt.x = pts.x;
8009 lvHitTestInfo.pt.y = pts.y;
8011 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8012 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8013 infoPtr->nEditLabelItem = -1;
8014 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8016 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8018 DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
8019 if(state == 1 || state == 2)
8023 lvitem.state = state << 12;
8024 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8025 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8029 if (LISTVIEW_TrackMouse(infoPtr, lvHitTestInfo.pt))
8033 ZeroMemory(&nmlv, sizeof(nmlv));
8035 nmlv.ptAction.x = lvHitTestInfo.pt.x;
8036 nmlv.ptAction.y = lvHitTestInfo.pt.y;
8038 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
8043 if (infoPtr->dwStyle & LVS_SINGLESEL)
8045 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8046 infoPtr->nEditLabelItem = nItem;
8048 LISTVIEW_SetSelection(infoPtr, nItem);
8052 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8056 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8057 LISTVIEW_SetItemFocus(infoPtr, nItem);
8058 infoPtr->nSelectionMark = nItem;
8064 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8065 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8067 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8068 infoPtr->nSelectionMark = nItem;
8071 else if (wKey & MK_CONTROL)
8075 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8077 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8078 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8079 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8080 infoPtr->nSelectionMark = nItem;
8082 else if (wKey & MK_SHIFT)
8084 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8088 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8089 infoPtr->nEditLabelItem = nItem;
8091 /* set selection (clears other pre-existing selections) */
8092 LISTVIEW_SetSelection(infoPtr, nItem);
8098 /* remove all selections */
8099 LISTVIEW_DeselectAll(infoPtr);
8108 * Processes mouse up messages (left mouse button).
8111 * [I] infoPtr : valid pointer to the listview structure
8112 * [I] wKey : key flag
8113 * [I] pts : mouse coordinate
8118 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8120 LVHITTESTINFO lvHitTestInfo;
8122 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8124 if (!infoPtr->bLButtonDown) return 0;
8126 lvHitTestInfo.pt.x = pts.x;
8127 lvHitTestInfo.pt.y = pts.y;
8129 /* send NM_CLICK notification */
8130 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8131 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8133 /* set left button flag */
8134 infoPtr->bLButtonDown = FALSE;
8136 /* if we clicked on a selected item, edit the label */
8137 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8138 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8145 * Destroys the listview control (called after WM_DESTROY).
8148 * [I] infoPtr : valid pointer to the listview structure
8153 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8157 /* delete all items */
8158 LISTVIEW_DeleteAllItems(infoPtr);
8160 /* destroy data structure */
8161 DPA_Destroy(infoPtr->hdpaItems);
8162 DPA_Destroy(infoPtr->hdpaPosX);
8163 DPA_Destroy(infoPtr->hdpaPosY);
8164 DPA_Destroy(infoPtr->hdpaColumns);
8165 ranges_destroy(infoPtr->selectionRanges);
8167 /* destroy image lists */
8168 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8170 if (infoPtr->himlNormal)
8171 ImageList_Destroy(infoPtr->himlNormal);
8172 if (infoPtr->himlSmall)
8173 ImageList_Destroy(infoPtr->himlSmall);
8174 if (infoPtr->himlState)
8175 ImageList_Destroy(infoPtr->himlState);
8178 /* destroy font, bkgnd brush */
8180 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8181 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8183 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8185 /* free listview info pointer*/
8193 * Handles notifications from header.
8196 * [I] infoPtr : valid pointer to the listview structure
8197 * [I] nCtrlId : control identifier
8198 * [I] lpnmh : notification information
8203 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8205 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8207 TRACE("(lpnmh=%p)\n", lpnmh);
8209 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8211 switch (lpnmh->hdr.code)
8215 case HDN_ITEMCHANGEDW:
8216 case HDN_ITEMCHANGEDA:
8218 COLUMN_INFO *lpColumnInfo;
8221 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8225 hdi.mask = HDI_WIDTH;
8226 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8230 cxy = lpnmh->pitem->cxy;
8232 /* determine how much we change since the last know position */
8233 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8234 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8237 RECT rcCol = lpColumnInfo->rcHeader;
8239 lpColumnInfo->rcHeader.right += dx;
8240 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8241 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8243 /* this trick works for left aligned columns only */
8244 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8246 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8247 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8249 rcCol.top = infoPtr->rcList.top;
8250 rcCol.bottom = infoPtr->rcList.bottom;
8251 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8257 case HDN_ITEMCLICKW:
8258 case HDN_ITEMCLICKA:
8260 /* Handle sorting by Header Column */
8263 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8265 nmlv.iSubItem = lpnmh->iItem;
8266 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8276 * Determines the type of structure to use.
8279 * [I] infoPtr : valid pointer to the listview structureof the sender
8280 * [I] hwndFrom : listview window handle
8281 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8286 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8288 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8290 if (nCommand != NF_REQUERY) return 0;
8292 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8299 * Paints/Repaints the listview control.
8302 * [I] infoPtr : valid pointer to the listview structure
8303 * [I] hdc : device context handle
8308 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8310 TRACE("(hdc=%p)\n", hdc);
8312 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8314 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8316 infoPtr->bNoItemMetrics = FALSE;
8317 LISTVIEW_UpdateItemSize(infoPtr);
8318 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8319 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8320 LISTVIEW_UpdateScroll(infoPtr);
8323 LISTVIEW_Refresh(infoPtr, hdc);
8328 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8330 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8331 LISTVIEW_Refresh(infoPtr, hdc);
8332 EndPaint(infoPtr->hwndSelf, &ps);
8340 * Processes double click messages (right mouse button).
8343 * [I] infoPtr : valid pointer to the listview structure
8344 * [I] wKey : key flag
8345 * [I] pts : mouse coordinate
8350 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8352 LVHITTESTINFO lvHitTestInfo;
8354 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8356 /* send NM_RELEASEDCAPTURE notification */
8357 notify(infoPtr, NM_RELEASEDCAPTURE);
8359 /* send NM_RDBLCLK notification */
8360 lvHitTestInfo.pt.x = pts.x;
8361 lvHitTestInfo.pt.y = pts.y;
8362 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8363 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8370 * Processes mouse down messages (right mouse button).
8373 * [I] infoPtr : valid pointer to the listview structure
8374 * [I] wKey : key flag
8375 * [I] pts : mouse coordinate
8380 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8382 LVHITTESTINFO lvHitTestInfo;
8385 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8387 /* send NM_RELEASEDCAPTURE notification */
8388 notify(infoPtr, NM_RELEASEDCAPTURE);
8390 /* make sure the listview control window has the focus */
8391 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8393 /* set right button down flag */
8394 infoPtr->bRButtonDown = TRUE;
8396 /* determine the index of the selected item */
8397 lvHitTestInfo.pt.x = pts.x;
8398 lvHitTestInfo.pt.y = pts.y;
8399 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8401 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8403 LISTVIEW_SetItemFocus(infoPtr, nItem);
8404 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8405 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8406 LISTVIEW_SetSelection(infoPtr, nItem);
8410 LISTVIEW_DeselectAll(infoPtr);
8418 * Processes mouse up messages (right mouse button).
8421 * [I] infoPtr : valid pointer to the listview structure
8422 * [I] wKey : key flag
8423 * [I] pts : mouse coordinate
8428 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8430 LVHITTESTINFO lvHitTestInfo;
8433 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8435 if (!infoPtr->bRButtonDown) return 0;
8437 /* set button flag */
8438 infoPtr->bRButtonDown = FALSE;
8440 /* Send NM_RClICK notification */
8441 lvHitTestInfo.pt.x = pts.x;
8442 lvHitTestInfo.pt.y = pts.y;
8443 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8444 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8446 /* Change to screen coordinate for WM_CONTEXTMENU */
8447 pt = lvHitTestInfo.pt;
8448 ClientToScreen(infoPtr->hwndSelf, &pt);
8450 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8451 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8452 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8463 * [I] infoPtr : valid pointer to the listview structure
8464 * [I] hwnd : window handle of window containing the cursor
8465 * [I] nHittest : hit-test code
8466 * [I] wMouseMsg : ideintifier of the mouse message
8469 * TRUE if cursor is set
8472 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8474 LVHITTESTINFO lvHitTestInfo;
8476 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8478 if(!infoPtr->hHotCursor) return FALSE;
8480 GetCursorPos(&lvHitTestInfo.pt);
8481 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8483 SetCursor(infoPtr->hHotCursor);
8493 * [I] infoPtr : valid pointer to the listview structure
8494 * [I] hwndLoseFocus : handle of previously focused window
8499 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8501 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8503 /* if we have the focus already, there's nothing to do */
8504 if (infoPtr->bFocus) return 0;
8506 /* send NM_SETFOCUS notification */
8507 notify(infoPtr, NM_SETFOCUS);
8509 /* set window focus flag */
8510 infoPtr->bFocus = TRUE;
8512 /* put the focus rect back on */
8513 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8515 /* redraw all visible selected items */
8516 LISTVIEW_InvalidateSelectedItems(infoPtr);
8526 * [I] infoPtr : valid pointer to the listview structure
8527 * [I] fRedraw : font handle
8528 * [I] fRedraw : redraw flag
8533 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8535 HFONT oldFont = infoPtr->hFont;
8537 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8539 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8540 if (infoPtr->hFont == oldFont) return 0;
8542 LISTVIEW_SaveTextMetrics(infoPtr);
8544 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8545 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8547 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8554 * Message handling for WM_SETREDRAW.
8555 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8558 * [I] infoPtr : valid pointer to the listview structure
8559 * [I] bRedraw: state of redraw flag
8562 * DefWinProc return value
8564 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8566 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8568 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8569 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8571 infoPtr->bRedraw = bRedraw;
8573 if(!bRedraw) return 0;
8575 if (is_autoarrange(infoPtr))
8576 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8577 LISTVIEW_UpdateScroll(infoPtr);
8579 /* despite what the WM_SETREDRAW docs says, apps expect us
8580 * to invalidate the listview here... stupid! */
8581 LISTVIEW_InvalidateList(infoPtr);
8588 * Resizes the listview control. This function processes WM_SIZE
8589 * messages. At this time, the width and height are not used.
8592 * [I] infoPtr : valid pointer to the listview structure
8593 * [I] Width : new width
8594 * [I] Height : new height
8599 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8601 RECT rcOld = infoPtr->rcList;
8603 TRACE("(width=%d, height=%d)\n", Width, Height);
8605 LISTVIEW_UpdateSize(infoPtr);
8606 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8608 /* do not bother with display related stuff if we're not redrawing */
8609 if (!is_redrawing(infoPtr)) return 0;
8611 if (is_autoarrange(infoPtr))
8612 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8614 LISTVIEW_UpdateScroll(infoPtr);
8616 /* refresh all only for lists whose height changed significantly */
8617 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8618 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8619 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8620 LISTVIEW_InvalidateList(infoPtr);
8627 * Sets the size information.
8630 * [I] infoPtr : valid pointer to the listview structure
8635 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8637 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8639 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8641 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8643 if (uView == LVS_LIST)
8645 /* Apparently the "LIST" style is supposed to have the same
8646 * number of items in a column even if there is no scroll bar.
8647 * Since if a scroll bar already exists then the bottom is already
8648 * reduced, only reduce if the scroll bar does not currently exist.
8649 * The "2" is there to mimic the native control. I think it may be
8650 * related to either padding or edges. (GLA 7/2002)
8652 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8653 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8654 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8656 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8661 hl.prc = &infoPtr->rcList;
8663 Header_Layout(infoPtr->hwndHeader, &hl);
8665 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8667 infoPtr->rcList.top = max(wp.cy, 0);
8670 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8675 * Processes WM_STYLECHANGED messages.
8678 * [I] infoPtr : valid pointer to the listview structure
8679 * [I] wStyleType : window style type (normal or extended)
8680 * [I] lpss : window style information
8685 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8686 const STYLESTRUCT *lpss)
8688 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8689 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8691 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8692 wStyleType, lpss->styleOld, lpss->styleNew);
8694 if (wStyleType != GWL_STYLE) return 0;
8696 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8697 /* what if LVS_OWNERDATA changed? */
8698 /* or LVS_SINGLESEL */
8699 /* or LVS_SORT{AS,DES}CENDING */
8701 infoPtr->dwStyle = lpss->styleNew;
8703 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8704 ((lpss->styleNew & WS_HSCROLL) == 0))
8705 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8707 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8708 ((lpss->styleNew & WS_VSCROLL) == 0))
8709 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8711 if (uNewView != uOldView)
8713 SIZE oldIconSize = infoPtr->iconSize;
8716 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8717 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8719 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8720 SetRectEmpty(&infoPtr->rcFocus);
8722 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8723 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8725 if (uNewView == LVS_ICON)
8727 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8729 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8730 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8731 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8734 else if (uNewView == LVS_REPORT)
8739 hl.prc = &infoPtr->rcList;
8741 Header_Layout(infoPtr->hwndHeader, &hl);
8742 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8745 LISTVIEW_UpdateItemSize(infoPtr);
8748 if (uNewView == LVS_REPORT)
8749 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8751 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8752 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8753 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8755 /* update the size of the client area */
8756 LISTVIEW_UpdateSize(infoPtr);
8758 /* add scrollbars if needed */
8759 LISTVIEW_UpdateScroll(infoPtr);
8761 /* invalidate client area + erase background */
8762 LISTVIEW_InvalidateList(infoPtr);
8769 * Window procedure of the listview control.
8772 static LRESULT WINAPI
8773 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8775 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8777 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8779 if (!infoPtr && (uMsg != WM_CREATE))
8780 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8784 case LVM_APPROXIMATEVIEWRECT:
8785 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8786 LOWORD(lParam), HIWORD(lParam));
8788 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8790 /* case LVM_CANCELEDITLABEL: */
8792 case LVM_CREATEDRAGIMAGE:
8793 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8795 case LVM_DELETEALLITEMS:
8796 return LISTVIEW_DeleteAllItems(infoPtr);
8798 case LVM_DELETECOLUMN:
8799 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8801 case LVM_DELETEITEM:
8802 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8804 case LVM_EDITLABELW:
8805 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8807 case LVM_EDITLABELA:
8808 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8810 /* case LVM_ENABLEGROUPVIEW: */
8812 case LVM_ENSUREVISIBLE:
8813 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8816 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8819 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8821 case LVM_GETBKCOLOR:
8822 return infoPtr->clrBk;
8824 /* case LVM_GETBKIMAGE: */
8826 case LVM_GETCALLBACKMASK:
8827 return infoPtr->uCallbackMask;
8829 case LVM_GETCOLUMNA:
8830 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8832 case LVM_GETCOLUMNW:
8833 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8835 case LVM_GETCOLUMNORDERARRAY:
8836 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8838 case LVM_GETCOLUMNWIDTH:
8839 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8841 case LVM_GETCOUNTPERPAGE:
8842 return LISTVIEW_GetCountPerPage(infoPtr);
8844 case LVM_GETEDITCONTROL:
8845 return (LRESULT)infoPtr->hwndEdit;
8847 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8848 return infoPtr->dwLvExStyle;
8850 /* case LVM_GETGROUPINFO: */
8852 /* case LVM_GETGROUPMETRICS: */
8855 return (LRESULT)infoPtr->hwndHeader;
8857 case LVM_GETHOTCURSOR:
8858 return (LRESULT)infoPtr->hHotCursor;
8860 case LVM_GETHOTITEM:
8861 return infoPtr->nHotItem;
8863 case LVM_GETHOVERTIME:
8864 return infoPtr->dwHoverTime;
8866 case LVM_GETIMAGELIST:
8867 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8869 /* case LVM_GETINSERTMARK: */
8871 /* case LVM_GETINSERTMARKCOLOR: */
8873 /* case LVM_GETINSERTMARKRECT: */
8875 case LVM_GETISEARCHSTRINGA:
8876 case LVM_GETISEARCHSTRINGW:
8877 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8881 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8884 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8886 case LVM_GETITEMCOUNT:
8887 return infoPtr->nItemCount;
8889 case LVM_GETITEMPOSITION:
8890 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8892 case LVM_GETITEMRECT:
8893 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8895 case LVM_GETITEMSPACING:
8896 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8898 case LVM_GETITEMSTATE:
8899 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8901 case LVM_GETITEMTEXTA:
8902 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8904 case LVM_GETITEMTEXTW:
8905 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8907 case LVM_GETNEXTITEM:
8908 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8910 case LVM_GETNUMBEROFWORKAREAS:
8911 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8915 if (!lParam) return FALSE;
8916 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8919 /* case LVM_GETOUTLINECOLOR: */
8921 /* case LVM_GETSELECTEDCOLUMN: */
8923 case LVM_GETSELECTEDCOUNT:
8924 return LISTVIEW_GetSelectedCount(infoPtr);
8926 case LVM_GETSELECTIONMARK:
8927 return infoPtr->nSelectionMark;
8929 case LVM_GETSTRINGWIDTHA:
8930 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8932 case LVM_GETSTRINGWIDTHW:
8933 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8935 case LVM_GETSUBITEMRECT:
8936 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8938 case LVM_GETTEXTBKCOLOR:
8939 return infoPtr->clrTextBk;
8941 case LVM_GETTEXTCOLOR:
8942 return infoPtr->clrText;
8944 /* case LVM_GETTILEINFO: */
8946 /* case LVM_GETTILEVIEWINFO: */
8948 case LVM_GETTOOLTIPS:
8949 if( !infoPtr->hwndToolTip )
8950 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
8951 return (LRESULT)infoPtr->hwndToolTip;
8953 case LVM_GETTOPINDEX:
8954 return LISTVIEW_GetTopIndex(infoPtr);
8956 /*case LVM_GETUNICODEFORMAT:
8957 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8960 /* case LVM_GETVIEW: */
8962 case LVM_GETVIEWRECT:
8963 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8965 case LVM_GETWORKAREAS:
8966 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8969 /* case LVM_HASGROUP: */
8972 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8974 case LVM_INSERTCOLUMNA:
8975 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8977 case LVM_INSERTCOLUMNW:
8978 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8980 /* case LVM_INSERTGROUP: */
8982 /* case LVM_INSERTGROUPSORTED: */
8984 case LVM_INSERTITEMA:
8985 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8987 case LVM_INSERTITEMW:
8988 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8990 /* case LVM_INSERTMARKHITTEST: */
8992 /* case LVM_ISGROUPVIEWENABLED: */
8994 /* case LVM_MAPIDTOINDEX: */
8996 /* case LVM_MAPINDEXTOID: */
8998 /* case LVM_MOVEGROUP: */
9000 /* case LVM_MOVEITEMTOGROUP: */
9002 case LVM_REDRAWITEMS:
9003 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9005 /* case LVM_REMOVEALLGROUPS: */
9007 /* case LVM_REMOVEGROUP: */
9010 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9012 case LVM_SETBKCOLOR:
9013 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9015 /* case LVM_SETBKIMAGE: */
9017 case LVM_SETCALLBACKMASK:
9018 infoPtr->uCallbackMask = (UINT)wParam;
9021 case LVM_SETCOLUMNA:
9022 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9024 case LVM_SETCOLUMNW:
9025 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9027 case LVM_SETCOLUMNORDERARRAY:
9028 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9030 case LVM_SETCOLUMNWIDTH:
9031 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9033 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9034 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9036 /* case LVM_SETGROUPINFO: */
9038 /* case LVM_SETGROUPMETRICS: */
9040 case LVM_SETHOTCURSOR:
9041 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9043 case LVM_SETHOTITEM:
9044 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9046 case LVM_SETHOVERTIME:
9047 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9049 case LVM_SETICONSPACING:
9050 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9052 case LVM_SETIMAGELIST:
9053 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9055 /* case LVM_SETINFOTIP: */
9057 /* case LVM_SETINSERTMARK: */
9059 /* case LVM_SETINSERTMARKCOLOR: */
9062 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9065 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9067 case LVM_SETITEMCOUNT:
9068 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9070 case LVM_SETITEMPOSITION:
9073 pt.x = (short)LOWORD(lParam);
9074 pt.y = (short)HIWORD(lParam);
9075 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9078 case LVM_SETITEMPOSITION32:
9079 if (lParam == 0) return FALSE;
9080 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9082 case LVM_SETITEMSTATE:
9083 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9085 case LVM_SETITEMTEXTA:
9086 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9088 case LVM_SETITEMTEXTW:
9089 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9091 /* case LVM_SETOUTLINECOLOR: */
9093 /* case LVM_SETSELECTEDCOLUMN: */
9095 case LVM_SETSELECTIONMARK:
9096 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9098 case LVM_SETTEXTBKCOLOR:
9099 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9101 case LVM_SETTEXTCOLOR:
9102 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9104 /* case LVM_SETTILEINFO: */
9106 /* case LVM_SETTILEVIEWINFO: */
9108 /* case LVM_SETTILEWIDTH: */
9110 case LVM_SETTOOLTIPS:
9111 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9113 /* case LVM_SETUNICODEFORMAT: */
9115 /* case LVM_SETVIEW: */
9117 /* case LVM_SETWORKAREAS: */
9119 /* case LVM_SORTGROUPS: */
9122 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9124 /* LVM_SORTITEMSEX: */
9126 case LVM_SUBITEMHITTEST:
9127 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9130 return LISTVIEW_Update(infoPtr, (INT)wParam);
9133 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9136 return LISTVIEW_Command(infoPtr, wParam, lParam);
9139 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9142 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9145 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9148 return (LRESULT)infoPtr->hFont;
9151 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9154 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9157 return LISTVIEW_KillFocus(infoPtr);
9159 case WM_LBUTTONDBLCLK:
9160 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9162 case WM_LBUTTONDOWN:
9163 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9166 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9169 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9172 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9175 return LISTVIEW_NCDestroy(infoPtr);
9178 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9179 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9182 case WM_NOTIFYFORMAT:
9183 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9186 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9188 case WM_RBUTTONDBLCLK:
9189 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9191 case WM_RBUTTONDOWN:
9192 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9195 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9198 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9203 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9206 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9209 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9212 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9214 case WM_STYLECHANGED:
9215 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9217 case WM_SYSCOLORCHANGE:
9218 COMCTL32_RefreshSysColors();
9221 /* case WM_TIMER: */
9224 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9227 if (wParam & (MK_SHIFT | MK_CONTROL))
9228 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9229 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9231 case WM_WINDOWPOSCHANGED:
9232 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9234 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9235 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9236 LISTVIEW_UpdateSize(infoPtr);
9237 LISTVIEW_UpdateScroll(infoPtr);
9239 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9241 /* case WM_WININICHANGE: */
9244 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9245 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9248 /* call default window procedure */
9249 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9257 * Registers the window class.
9265 void LISTVIEW_Register(void)
9269 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9270 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9271 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9272 wndClass.cbClsExtra = 0;
9273 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9274 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9275 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9276 wndClass.lpszClassName = WC_LISTVIEWW;
9277 RegisterClassW(&wndClass);
9282 * Unregisters the window class.
9290 void LISTVIEW_Unregister(void)
9292 UnregisterClassW(WC_LISTVIEWW, NULL);
9297 * Handle any WM_COMMAND messages
9300 * [I] infoPtr : valid pointer to the listview structure
9301 * [I] wParam : the first message parameter
9302 * [I] lParam : the second message parameter
9307 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9309 switch (HIWORD(wParam))
9314 * Adjust the edit window size
9317 HDC hdc = GetDC(infoPtr->hwndEdit);
9318 HFONT hFont, hOldFont = 0;
9323 if (!infoPtr->hwndEdit || !hdc) return 0;
9324 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9325 GetWindowRect(infoPtr->hwndEdit, &rect);
9327 /* Select font to get the right dimension of the string */
9328 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9331 hOldFont = SelectObject(hdc, hFont);
9334 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9336 TEXTMETRICW textMetric;
9338 /* Add Extra spacing for the next character */
9339 GetTextMetricsW(hdc, &textMetric);
9340 sz.cx += (textMetric.tmMaxCharWidth * 2);
9348 rect.bottom - rect.top,
9349 SWP_DRAWFRAME|SWP_NOMOVE);
9352 SelectObject(hdc, hOldFont);
9354 ReleaseDC(infoPtr->hwndSelf, hdc);
9360 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9369 * Subclassed edit control windproc function
9372 * [I] hwnd : the edit window handle
9373 * [I] uMsg : the message that is to be processed
9374 * [I] wParam : first message parameter
9375 * [I] lParam : second message parameter
9376 * [I] isW : TRUE if input is Unicode
9381 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9383 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9384 BOOL cancel = FALSE;
9386 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9387 hwnd, uMsg, wParam, lParam, isW);
9392 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9399 WNDPROC editProc = infoPtr->EditWndProc;
9400 infoPtr->EditWndProc = 0;
9401 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9402 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9406 if (VK_ESCAPE == (INT)wParam)
9411 else if (VK_RETURN == (INT)wParam)
9415 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9419 if (infoPtr->hwndEdit)
9421 LPWSTR buffer = NULL;
9423 infoPtr->hwndEdit = 0;
9426 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9430 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9432 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9433 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9437 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9439 if (buffer) Free(buffer);
9443 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9449 * Subclassed edit control Unicode windproc function
9452 * [I] hwnd : the edit window handle
9453 * [I] uMsg : the message that is to be processed
9454 * [I] wParam : first message parameter
9455 * [I] lParam : second message parameter
9459 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9461 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9466 * Subclassed edit control ANSI windproc function
9469 * [I] hwnd : the edit window handle
9470 * [I] uMsg : the message that is to be processed
9471 * [I] wParam : first message parameter
9472 * [I] lParam : second message parameter
9476 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9478 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9483 * Creates a subclassed edit cotrol
9486 * [I] infoPtr : valid pointer to the listview structure
9487 * [I] text : initial text for the edit
9488 * [I] style : the window style
9489 * [I] isW : TRUE if input is Unicode
9493 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9494 INT x, INT y, INT width, INT height, BOOL isW)
9496 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9501 TEXTMETRICW textMetric;
9502 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9504 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9506 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9507 hdc = GetDC(infoPtr->hwndSelf);
9509 /* Select the font to get appropriate metric dimensions */
9510 if(infoPtr->hFont != 0)
9511 hOldFont = SelectObject(hdc, infoPtr->hFont);
9513 /*Get String Length in pixels */
9514 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9516 /*Add Extra spacing for the next character */
9517 GetTextMetricsW(hdc, &textMetric);
9518 sz.cx += (textMetric.tmMaxCharWidth * 2);
9520 if(infoPtr->hFont != 0)
9521 SelectObject(hdc, hOldFont);
9523 ReleaseDC(infoPtr->hwndSelf, hdc);
9525 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9527 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9529 if (!hedit) return 0;
9531 infoPtr->EditWndProc = (WNDPROC)
9532 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9533 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9535 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);