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
9 * Copyright 2009-2015 Nikolay Sivov
10 * Copyright 2009 Owen Rudge for CodeWeavers
11 * Copyright 2012-2013 Daniel Jelinski
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 * This code was audited for completeness against the documented features
30 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
32 * Unless otherwise noted, we believe this code to be complete, as per
33 * the specification mentioned above.
34 * If you discover missing features, or bugs, please note them below.
38 * Default Message Processing
39 * -- WM_CREATE: create the icon and small icon image lists at this point only if
40 * the LVS_SHAREIMAGELISTS style is not specified.
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
60 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
61 * linear in the number of items in the list, and this is
62 * unacceptable for large lists.
63 * -- if list is sorted by item text LISTVIEW_InsertItemT could use
64 * binary search to calculate item index (e.g. DPA_Search()).
65 * This requires sorted state to be reliably tracked in item modifiers.
66 * -- we should keep an ordered array of coordinates in iconic mode.
67 * This would allow framing items (iterator_frameditems),
68 * and finding the nearest item (LVFI_NEARESTXY) a lot more efficiently.
75 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
80 * -- LVS_NOSCROLL (see Q137520)
84 * -- LVS_EX_BORDERSELECT
88 * -- LVS_EX_MULTIWORKAREAS
90 * -- LVS_EX_SIMPLESELECT
91 * -- LVS_EX_TWOCLICKACTIVATE
92 * -- LVS_EX_UNDERLINECOLD
93 * -- LVS_EX_UNDERLINEHOT
96 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
102 * -- LVM_ENABLEGROUPVIEW
103 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
104 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
105 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
106 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
107 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
108 * -- LVM_GETINSERTMARKRECT
109 * -- LVM_GETNUMBEROFWORKAREAS
110 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
111 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
112 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
113 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
114 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
115 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
116 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
117 * -- LVM_INSERTGROUPSORTED
118 * -- LVM_INSERTMARKHITTEST
119 * -- LVM_ISGROUPVIEWENABLED
121 * -- LVM_MOVEITEMTOGROUP
123 * -- LVM_SETTILEWIDTH
127 * -- ListView_GetHoverTime, ListView_SetHoverTime
128 * -- ListView_GetISearchString
129 * -- ListView_GetNumberOfWorkAreas
130 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
136 #include "comctl32.h"
140 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
142 typedef struct tagCOLUMN_INFO
144 RECT rcHeader
; /* tracks the header's rectangle */
145 INT fmt
; /* same as LVCOLUMN.fmt */
149 typedef struct tagITEMHDR
153 } ITEMHDR
, *LPITEMHDR
;
155 typedef struct tagSUBITEM_INFO
161 typedef struct tagITEM_ID ITEM_ID
;
163 typedef struct tagITEM_INFO
174 UINT id
; /* item id */
175 HDPA item
; /* link to item data */
178 typedef struct tagRANGE
184 typedef struct tagRANGES
189 typedef struct tagITERATOR
198 typedef struct tagDELAYED_ITEM_EDIT
204 typedef struct tagLISTVIEW_INFO
208 RECT rcList
; /* This rectangle is really the window
209 * client rectangle possibly reduced by the
210 * horizontal scroll bar and/or header - see
211 * LISTVIEW_UpdateSize. This rectangle offset
212 * by the LISTVIEW_GetOrigin value is in
213 * client coordinates */
215 /* notification window */
218 BOOL bDoChangeNotify
; /* send change notification messages? */
225 INT nItemCount
; /* the number of items in the list */
226 HDPA hdpaItems
; /* array ITEM_INFO pointers */
227 HDPA hdpaItemIds
; /* array of ITEM_ID pointers */
228 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
229 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
230 RANGES selectionRanges
;
231 INT nSelectionMark
; /* item to start next multiselection from */
233 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
236 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
237 BOOL colRectsDirty
; /* trigger column rectangles requery from header */
240 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
245 PFNLVCOMPARE pfnCompare
; /* sorting callback pointer */
249 DWORD dwStyle
; /* the cached window GWL_STYLE */
250 DWORD dwLvExStyle
; /* extended listview style */
251 DWORD uView
; /* current view available through LVM_[G,S]ETVIEW */
257 DELAYED_ITEM_EDIT itemEdit
; /* Pointer to this structure will be the timer ID */
260 HIMAGELIST himlNormal
;
261 HIMAGELIST himlSmall
;
262 HIMAGELIST himlState
;
267 POINT currIconPos
; /* this is the position next icon will be placed */
271 INT xTrackLine
; /* The x coefficient of the track line or -1 if none */
273 /* marquee selection */
274 BOOL bMarqueeSelect
; /* marquee selection/highlight underway */
276 RECT marqueeRect
; /* absolute coordinates of marquee selection */
277 RECT marqueeDrawRect
; /* relative coordinates for drawing marquee */
278 POINT marqueeOrigin
; /* absolute coordinates of marquee click origin */
281 BOOL bFocus
; /* control has focus */
283 RECT rcFocus
; /* focus bounds */
291 BOOL bDefaultBkColor
;
297 INT ntmHeight
; /* Some cached metrics of the font used */
298 INT ntmMaxCharWidth
; /* by the listview to draw items */
301 /* mouse operation */
304 POINT ptClickPos
; /* point where the user clicked */
305 INT nLButtonDownItem
; /* tracks item to reset multiselection on WM_LBUTTONUP */
310 /* keyboard operation */
311 DWORD lastKeyPressTimestamp
;
313 INT nSearchParamLength
;
314 WCHAR szSearchParam
[ MAX_PATH
];
317 BOOL bIsDrawing
; /* Drawing in progress */
318 INT nMeasureItemHeight
; /* WM_MEASUREITEM result */
319 BOOL bRedraw
; /* WM_SETREDRAW switch */
322 DWORD iVersion
; /* CCM_[G,S]ETVERSION */
328 /* How many we debug buffer to allocate */
329 #define DEBUG_BUFFERS 20
330 /* The size of a single debug buffer */
331 #define DEBUG_BUFFER_SIZE 256
333 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
334 #define SB_INTERNAL -1
336 /* maximum size of a label */
337 #define DISP_TEXT_SIZE 260
339 /* padding for items in list and small icon display modes */
340 #define WIDTH_PADDING 12
342 /* padding for items in list, report and small icon display modes */
343 #define HEIGHT_PADDING 1
345 /* offset of items in report display mode */
346 #define REPORT_MARGINX 2
348 /* padding for icon in large icon display mode
349 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
350 * that HITTEST will see.
351 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
352 * ICON_TOP_PADDING - sum of the two above.
353 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
354 * LABEL_HOR_PADDING - between text and sides of box
355 * LABEL_VERT_PADDING - between bottom of text and end of box
357 * ICON_LR_PADDING - additional width above icon size.
358 * ICON_LR_HALF - half of the above value
360 #define ICON_TOP_PADDING_NOTHITABLE 2
361 #define ICON_TOP_PADDING_HITABLE 2
362 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
363 #define ICON_BOTTOM_PADDING 4
364 #define LABEL_HOR_PADDING 5
365 #define LABEL_VERT_PADDING 7
366 #define ICON_LR_PADDING 16
367 #define ICON_LR_HALF (ICON_LR_PADDING/2)
369 /* default label width for items in list and small icon display modes */
370 #define DEFAULT_LABEL_WIDTH 40
371 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
372 #define MAX_EMPTYTEXT_SELECT_WIDTH 80
374 /* default column width for items in list display mode */
375 #define DEFAULT_COLUMN_WIDTH 128
377 /* Size of "line" scroll for V & H scrolls */
378 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
380 /* Padding between image and label */
381 #define IMAGE_PADDING 2
383 /* Padding behind the label */
384 #define TRAILING_LABEL_PADDING 12
385 #define TRAILING_HEADER_PADDING 11
387 /* Border for the icon caption */
388 #define CAPTION_BORDER 2
390 /* Standard DrawText flags */
391 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
392 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
393 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
395 /* Image index from state */
396 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
398 /* The time in milliseconds to reset the search in the list */
399 #define KEY_DELAY 450
401 /* Dump the LISTVIEW_INFO structure to the debug channel */
402 #define LISTVIEW_DUMP(iP) do { \
403 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
404 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
405 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
406 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
407 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
408 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
409 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
410 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
411 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
412 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
415 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
418 * forward declarations
420 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
421 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*, INT
, LPRECT
);
422 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*, INT
, LPPOINT
);
423 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*, INT
, LPPOINT
);
424 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*, INT
, LPRECT
);
425 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*, LPPOINT
);
426 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*, LPRECT
);
427 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
428 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
429 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
430 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
, BOOL
);
431 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*, INT
, UINT
);
432 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
433 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
);
434 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
);
435 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
436 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
437 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
438 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*, BOOL
, BOOL
);
439 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*, INT
, INT
);
441 /******** Text handling functions *************************************/
443 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
444 * text string. The string may be ANSI or Unicode, in which case
445 * the boolean isW tells us the type of the string.
447 * The name of the function tell what type of strings it expects:
448 * W: Unicode, T: ANSI/Unicode - function of isW
451 static inline BOOL
is_text(LPCWSTR text
)
453 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
456 static inline int textlenT(LPCWSTR text
, BOOL isW
)
458 return !is_text(text
) ? 0 :
459 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
462 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
465 if (isSrcW
) lstrcpynW(dest
, src
, max
);
466 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
468 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
469 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
472 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
474 LPWSTR wstr
= (LPWSTR
)text
;
476 if (!isW
&& is_text(text
))
478 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
479 wstr
= Alloc(len
* sizeof(WCHAR
));
480 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
482 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
486 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
488 if (!isW
&& is_text(wstr
)) Free (wstr
);
492 * dest is a pointer to a Unicode string
493 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
495 static BOOL
textsetptrT(LPWSTR
*dest
, LPCWSTR src
, BOOL isW
)
499 if (src
== LPSTR_TEXTCALLBACKW
)
501 if (is_text(*dest
)) Free(*dest
);
502 *dest
= LPSTR_TEXTCALLBACKW
;
506 LPWSTR pszText
= textdupTtoW(src
, isW
);
507 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
508 bResult
= Str_SetPtrW(dest
, pszText
);
509 textfreeT(pszText
, isW
);
515 * compares a Unicode to a Unicode/ANSI text string
517 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
519 if (!aw
) return bt
? -1 : 0;
521 if (aw
== LPSTR_TEXTCALLBACKW
)
522 return bt
== LPSTR_TEXTCALLBACKW
? 1 : -1;
523 if (bt
!= LPSTR_TEXTCALLBACKW
)
525 LPWSTR bw
= textdupTtoW(bt
, isW
);
526 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
534 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
536 n
= min(min(n
, lstrlenW(s1
)), lstrlenW(s2
));
537 return CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
) - CSTR_EQUAL
;
540 /******** Debugging functions *****************************************/
542 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
544 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
545 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
548 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
550 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
551 n
= min(textlenT(text
, isW
), n
);
552 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
555 static char* debug_getbuf(void)
557 static int index
= 0;
558 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
559 return buffers
[index
++ % DEBUG_BUFFERS
];
562 static inline const char* debugrange(const RANGE
*lprng
)
564 if (!lprng
) return "(null)";
565 return wine_dbg_sprintf("[%d, %d]", lprng
->lower
, lprng
->upper
);
568 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
570 char* buf
= debug_getbuf(), *text
= buf
;
571 int len
, size
= DEBUG_BUFFER_SIZE
;
573 if (pScrollInfo
== NULL
) return "(null)";
574 len
= snprintf(buf
, size
, "{cbSize=%u, ", pScrollInfo
->cbSize
);
575 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
576 if (pScrollInfo
->fMask
& SIF_RANGE
)
577 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
579 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
580 if (pScrollInfo
->fMask
& SIF_PAGE
)
581 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
583 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
584 if (pScrollInfo
->fMask
& SIF_POS
)
585 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
587 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
588 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
589 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
591 if (len
== -1) goto end
; buf
+= len
;
594 buf
= text
+ strlen(text
);
596 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
600 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
602 if (!plvnm
) return "(null)";
603 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
604 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
605 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
606 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
609 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
611 char* buf
= debug_getbuf(), *text
= buf
;
612 int len
, size
= DEBUG_BUFFER_SIZE
;
614 if (lpLVItem
== NULL
) return "(null)";
615 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
616 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
617 if (lpLVItem
->mask
& LVIF_STATE
)
618 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
620 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
621 if (lpLVItem
->mask
& LVIF_TEXT
)
622 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
624 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
625 if (lpLVItem
->mask
& LVIF_IMAGE
)
626 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
628 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
629 if (lpLVItem
->mask
& LVIF_PARAM
)
630 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
632 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
633 if (lpLVItem
->mask
& LVIF_INDENT
)
634 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
636 if (len
== -1) goto end
; buf
+= len
;
639 buf
= text
+ strlen(text
);
641 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
645 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
647 char* buf
= debug_getbuf(), *text
= buf
;
648 int len
, size
= DEBUG_BUFFER_SIZE
;
650 if (lpColumn
== NULL
) return "(null)";
651 len
= snprintf(buf
, size
, "{");
652 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
653 if (lpColumn
->mask
& LVCF_SUBITEM
)
654 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
656 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
657 if (lpColumn
->mask
& LVCF_FMT
)
658 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
660 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
661 if (lpColumn
->mask
& LVCF_WIDTH
)
662 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
664 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
665 if (lpColumn
->mask
& LVCF_TEXT
)
666 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
668 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
669 if (lpColumn
->mask
& LVCF_IMAGE
)
670 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
672 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
673 if (lpColumn
->mask
& LVCF_ORDER
)
674 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
676 if (len
== -1) goto end
; buf
+= len
;
679 buf
= text
+ strlen(text
);
681 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
685 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
687 if (!lpht
) return "(null)";
689 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
690 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
693 /* Return the corresponding text for a given scroll value */
694 static inline LPCSTR
debugscrollcode(int nScrollCode
)
698 case SB_LINELEFT
: return "SB_LINELEFT";
699 case SB_LINERIGHT
: return "SB_LINERIGHT";
700 case SB_PAGELEFT
: return "SB_PAGELEFT";
701 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
702 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
703 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
704 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
705 case SB_INTERNAL
: return "SB_INTERNAL";
706 default: return "unknown";
711 /******** Notification functions ************************************/
713 static int get_ansi_notification(UINT unicodeNotificationCode
)
715 switch (unicodeNotificationCode
)
717 case LVN_BEGINLABELEDITA
:
718 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
719 case LVN_ENDLABELEDITA
:
720 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
721 case LVN_GETDISPINFOA
:
722 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
723 case LVN_SETDISPINFOA
:
724 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
725 case LVN_ODFINDITEMA
:
726 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
727 case LVN_GETINFOTIPA
:
728 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
729 /* header forwards */
731 case HDN_TRACKW
: return HDN_TRACKA
;
733 case HDN_ENDTRACKW
: return HDN_ENDTRACKA
;
734 case HDN_BEGINDRAG
: return HDN_BEGINDRAG
;
735 case HDN_ENDDRAG
: return HDN_ENDDRAG
;
736 case HDN_ITEMCHANGINGA
:
737 case HDN_ITEMCHANGINGW
: return HDN_ITEMCHANGINGA
;
738 case HDN_ITEMCHANGEDA
:
739 case HDN_ITEMCHANGEDW
: return HDN_ITEMCHANGEDA
;
741 case HDN_ITEMCLICKW
: return HDN_ITEMCLICKA
;
742 case HDN_DIVIDERDBLCLICKA
:
743 case HDN_DIVIDERDBLCLICKW
: return HDN_DIVIDERDBLCLICKA
;
746 FIXME("unknown notification %x\n", unicodeNotificationCode
);
747 return unicodeNotificationCode
;
750 /* forwards header notifications to listview parent */
751 static LRESULT
notify_forward_header(const LISTVIEW_INFO
*infoPtr
, NMHEADERW
*lpnmhW
)
753 LPCWSTR text
= NULL
, filter
= NULL
;
755 NMHEADERA
*lpnmh
= (NMHEADERA
*) lpnmhW
;
757 /* on unicode format exit earlier */
758 if (infoPtr
->notifyFormat
== NFR_UNICODE
)
759 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, lpnmh
->hdr
.idFrom
,
762 /* header always supplies unicode notifications,
763 all we have to do is to convert strings to ANSI */
766 /* convert item text */
767 if (lpnmh
->pitem
->mask
& HDI_TEXT
)
769 text
= (LPCWSTR
)lpnmh
->pitem
->pszText
;
770 lpnmh
->pitem
->pszText
= NULL
;
771 Str_SetPtrWtoA(&lpnmh
->pitem
->pszText
, text
);
773 /* convert filter text */
774 if ((lpnmh
->pitem
->mask
& HDI_FILTER
) && (lpnmh
->pitem
->type
== HDFT_ISSTRING
) &&
775 lpnmh
->pitem
->pvFilter
)
777 filter
= (LPCWSTR
)((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
;
778 ((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
= NULL
;
779 Str_SetPtrWtoA(&((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
, filter
);
782 lpnmh
->hdr
.code
= get_ansi_notification(lpnmh
->hdr
.code
);
784 ret
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, lpnmh
->hdr
.idFrom
,
790 Free(lpnmh
->pitem
->pszText
);
791 lpnmh
->pitem
->pszText
= (LPSTR
)text
;
795 Free(((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
);
796 ((HD_TEXTFILTERA
*)lpnmh
->pitem
->pvFilter
)->pszText
= (LPSTR
)filter
;
802 static LRESULT
notify_hdr(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
806 TRACE("(code=%d)\n", code
);
808 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
809 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
811 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, pnmh
->idFrom
, (LPARAM
)pnmh
);
813 TRACE(" <= %ld\n", result
);
818 static inline BOOL
notify(const LISTVIEW_INFO
*infoPtr
, INT code
)
821 HWND hwnd
= infoPtr
->hwndSelf
;
822 notify_hdr(infoPtr
, code
, &nmh
);
823 return IsWindow(hwnd
);
826 static inline void notify_itemactivate(const LISTVIEW_INFO
*infoPtr
, const LVHITTESTINFO
*htInfo
)
837 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
838 item
.iItem
= htInfo
->iItem
;
840 item
.stateMask
= (UINT
)-1;
841 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
842 nmia
.lParam
= item
.lParam
;
843 nmia
.uOldState
= item
.state
;
844 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
845 nmia
.uChanged
= LVIF_STATE
;
848 nmia
.iItem
= htInfo
->iItem
;
849 nmia
.iSubItem
= htInfo
->iSubItem
;
850 nmia
.ptAction
= htInfo
->pt
;
852 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
853 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
854 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
856 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
859 static inline LRESULT
notify_listview(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
861 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
862 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
865 /* Handles NM_DBLCLK, NM_CLICK, NM_RDBLCLK, NM_RCLICK. Only NM_RCLICK return value is used. */
866 static BOOL
notify_click(const LISTVIEW_INFO
*infoPtr
, INT code
, const LVHITTESTINFO
*lvht
)
870 HWND hwnd
= infoPtr
->hwndSelf
;
873 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
874 ZeroMemory(&nmia
, sizeof(nmia
));
875 nmia
.iItem
= lvht
->iItem
;
876 nmia
.iSubItem
= lvht
->iSubItem
;
877 nmia
.ptAction
= lvht
->pt
;
878 item
.mask
= LVIF_PARAM
;
879 item
.iItem
= lvht
->iItem
;
881 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmia
.lParam
= item
.lParam
;
882 ret
= notify_hdr(infoPtr
, code
, (NMHDR
*)&nmia
);
883 return IsWindow(hwnd
) && (code
== NM_RCLICK
? !ret
: TRUE
);
886 static BOOL
notify_deleteitem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
890 HWND hwnd
= infoPtr
->hwndSelf
;
892 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
894 item
.mask
= LVIF_PARAM
;
897 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
898 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
899 return IsWindow(hwnd
);
903 Send notification. depends on dispinfoW having same
904 structure as dispinfoA.
905 infoPtr : listview struct
906 code : *Unicode* notification code
907 pdi : dispinfo structure (can be unicode or ansi)
908 isW : TRUE if dispinfo is Unicode
910 static BOOL
notify_dispinfoT(const LISTVIEW_INFO
*infoPtr
, UINT code
, LPNMLVDISPINFOW pdi
, BOOL isW
)
912 INT length
= 0, ret_length
;
913 LPWSTR buffer
= NULL
, ret_text
;
914 BOOL return_ansi
= FALSE
;
915 BOOL return_unicode
= FALSE
;
918 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_text(pdi
->item
.pszText
))
920 return_unicode
= ( isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
921 return_ansi
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
924 ret_length
= pdi
->item
.cchTextMax
;
925 ret_text
= pdi
->item
.pszText
;
927 if (return_unicode
|| return_ansi
)
929 if (code
!= LVN_GETDISPINFOW
)
931 length
= return_ansi
?
932 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
933 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
937 length
= pdi
->item
.cchTextMax
;
938 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
941 buffer
= Alloc( (return_ansi
? sizeof(WCHAR
) : sizeof(CHAR
)) * length
);
942 if (!buffer
) return FALSE
;
945 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
948 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) buffer
,
951 pdi
->item
.pszText
= buffer
;
952 pdi
->item
.cchTextMax
= length
;
955 if (infoPtr
->notifyFormat
== NFR_ANSI
)
956 code
= get_ansi_notification(code
);
958 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
959 ret
= notify_hdr(infoPtr
, code
, &pdi
->hdr
);
960 TRACE(" resulting code=%d\n", pdi
->hdr
.code
);
962 if (return_ansi
|| return_unicode
)
964 if (return_ansi
&& (pdi
->hdr
.code
== LVN_GETDISPINFOA
))
966 strcpy((char*)ret_text
, (char*)pdi
->item
.pszText
);
968 else if (return_unicode
&& (pdi
->hdr
.code
== LVN_GETDISPINFOW
))
970 strcpyW(ret_text
, pdi
->item
.pszText
);
972 else if (return_ansi
) /* note : pointer can be changed by app ! */
974 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) ret_text
,
975 ret_length
, NULL
, NULL
);
978 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
979 ret_text
, ret_length
);
981 pdi
->item
.pszText
= ret_text
; /* restores our buffer */
982 pdi
->item
.cchTextMax
= ret_length
;
988 /* if dipsinfo holder changed notification code then convert */
989 if (!isW
&& (pdi
->hdr
.code
== LVN_GETDISPINFOW
) && (pdi
->item
.mask
& LVIF_TEXT
))
991 length
= WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
993 buffer
= Alloc(length
* sizeof(CHAR
));
994 if (!buffer
) return FALSE
;
996 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) buffer
,
997 ret_length
, NULL
, NULL
);
999 strcpy((LPSTR
)pdi
->item
.pszText
, (LPSTR
)buffer
);
1006 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
,
1007 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
1009 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
1010 lpnmlvcd
->nmcd
.hdc
= hdc
;
1011 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
1012 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
1013 lpnmlvcd
->clrText
= infoPtr
->clrText
;
1014 if (!lplvItem
) return;
1015 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
1016 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
1017 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
1018 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
1019 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
1020 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
1023 static inline DWORD
notify_customdraw (const LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
1025 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
1028 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
1029 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
1030 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
1031 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
1032 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
1033 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
1037 static void prepaint_setup (const LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
, BOOL SubItem
)
1039 COLORREF backcolor
, textcolor
;
1041 /* apparently, for selected items, we have to override the returned values */
1044 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
1046 if (infoPtr
->bFocus
)
1048 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
1049 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
1051 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
1053 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
1054 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
1059 backcolor
= lpnmlvcd
->clrTextBk
;
1060 textcolor
= lpnmlvcd
->clrText
;
1062 if (backcolor
== CLR_DEFAULT
)
1063 backcolor
= comctl32_color
.clrWindow
;
1064 if (textcolor
== CLR_DEFAULT
)
1065 textcolor
= comctl32_color
.clrWindowText
;
1067 /* Set the text attributes */
1068 if (backcolor
!= CLR_NONE
)
1070 SetBkMode(hdc
, OPAQUE
);
1071 SetBkColor(hdc
, backcolor
);
1074 SetBkMode(hdc
, TRANSPARENT
);
1075 SetTextColor(hdc
, textcolor
);
1078 static inline DWORD
notify_postpaint (const LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
1080 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
1083 /* returns TRUE when repaint needed, FALSE otherwise */
1084 static BOOL
notify_measureitem(LISTVIEW_INFO
*infoPtr
)
1086 MEASUREITEMSTRUCT mis
;
1087 mis
.CtlType
= ODT_LISTVIEW
;
1088 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1092 mis
.itemHeight
= infoPtr
->nItemHeight
;
1093 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
1094 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
1096 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
1102 /******** Item iterator functions **********************************/
1104 static RANGES
ranges_create(int count
);
1105 static void ranges_destroy(RANGES ranges
);
1106 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
1107 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
1108 static void ranges_dump(RANGES ranges
);
1110 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
1112 RANGE range
= { nItem
, nItem
+ 1 };
1114 return ranges_add(ranges
, range
);
1117 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
1119 RANGE range
= { nItem
, nItem
+ 1 };
1121 return ranges_del(ranges
, range
);
1125 * ITERATOR DOCUMENTATION
1127 * The iterator functions allow for easy, and convenient iteration
1128 * over items of interest in the list. Typically, you create an
1129 * iterator, use it, and destroy it, as such:
1132 * iterator_xxxitems(&i, ...);
1133 * while (iterator_{prev,next}(&i)
1135 * //code which uses i.nItem
1137 * iterator_destroy(&i);
1139 * where xxx is either: framed, or visible.
1140 * Note that it is important that the code destroys the iterator
1141 * after it's done with it, as the creation of the iterator may
1142 * allocate memory, which thus needs to be freed.
1144 * You can iterate both forwards, and backwards through the list,
1145 * by using iterator_next or iterator_prev respectively.
1147 * Lower numbered items are draw on top of higher number items in
1148 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1149 * items may overlap). So, to test items, you should use
1151 * which lists the items top to bottom (in Z-order).
1152 * For drawing items, you should use
1154 * which lists the items bottom to top (in Z-order).
1155 * If you keep iterating over the items after the end-of-items
1156 * marker (-1) is returned, the iterator will start from the
1157 * beginning. Typically, you don't need to test for -1,
1158 * because iterator_{next,prev} will return TRUE if more items
1159 * are to be iterated over, or FALSE otherwise.
1161 * Note: the iterator is defined to be bidirectional. That is,
1162 * any number of prev followed by any number of next, or
1163 * five versa, should leave the iterator at the same item:
1164 * prev * n, next * n = next * n, prev * n
1166 * The iterator has a notion of an out-of-order, special item,
1167 * which sits at the start of the list. This is used in
1168 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1169 * which needs to be first, as it may overlap other items.
1171 * The code is a bit messy because we have:
1172 * - a special item to deal with
1173 * - simple range, or composite range
1175 * If you find bugs, or want to add features, please make sure you
1176 * always check/modify *both* iterator_prev, and iterator_next.
1180 * This function iterates through the items in increasing order,
1181 * but prefixed by the special item, then -1. That is:
1182 * special, 1, 2, 3, ..., n, -1.
1183 * Each item is listed only once.
1185 static inline BOOL
iterator_next(ITERATOR
* i
)
1189 i
->nItem
= i
->nSpecial
;
1190 if (i
->nItem
!= -1) return TRUE
;
1192 if (i
->nItem
== i
->nSpecial
)
1194 if (i
->ranges
) i
->index
= 0;
1200 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1201 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1206 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1207 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1210 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1212 i
->nItem
= i
->range
.lower
;
1213 if (i
->nItem
>= 0) goto testitem
;
1220 * This function iterates through the items in decreasing order,
1221 * followed by the special item, then -1. That is:
1222 * n, n-1, ..., 3, 2, 1, special, -1.
1223 * Each item is listed only once.
1225 static inline BOOL
iterator_prev(ITERATOR
* i
)
1232 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1235 if (i
->nItem
== i
->nSpecial
)
1243 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1244 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1250 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1253 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1255 i
->nItem
= i
->range
.upper
;
1256 if (i
->nItem
> 0) goto testitem
;
1258 return (i
->nItem
= i
->nSpecial
) != -1;
1261 static RANGE
iterator_range(const ITERATOR
*i
)
1265 if (!i
->ranges
) return i
->range
;
1267 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1269 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1270 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1272 else range
.lower
= range
.upper
= 0;
1278 * Releases resources associated with this iterator.
1280 static inline void iterator_destroy(const ITERATOR
*i
)
1282 ranges_destroy(i
->ranges
);
1286 * Create an empty iterator.
1288 static inline BOOL
iterator_empty(ITERATOR
* i
)
1290 ZeroMemory(i
, sizeof(*i
));
1291 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1296 * Create an iterator over a range.
1298 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1306 * Create an iterator over a bunch of ranges.
1307 * Please note that the iterator will take ownership of the ranges,
1308 * and will free them upon destruction.
1310 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1318 * Creates an iterator over the items which intersect frame.
1319 * Uses absolute coordinates rather than compensating for the current offset.
1321 static BOOL
iterator_frameditems_absolute(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*frame
)
1323 RECT rcItem
, rcTemp
;
1325 /* in case we fail, we want to return an empty iterator */
1326 if (!iterator_empty(i
)) return FALSE
;
1328 TRACE("(frame=%s)\n", wine_dbgstr_rect(frame
));
1330 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
1334 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->nFocusedItem
!= -1)
1336 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1337 if (IntersectRect(&rcTemp
, &rcItem
, frame
))
1338 i
->nSpecial
= infoPtr
->nFocusedItem
;
1340 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1341 /* to do better here, we need to have PosX, and PosY sorted */
1342 TRACE("building icon ranges:\n");
1343 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1345 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1346 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1347 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1348 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1349 if (IntersectRect(&rcTemp
, &rcItem
, frame
))
1350 ranges_additem(i
->ranges
, nItem
);
1354 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
1358 if (frame
->left
>= infoPtr
->nItemWidth
) return TRUE
;
1359 if (frame
->top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1361 range
.lower
= max(frame
->top
/ infoPtr
->nItemHeight
, 0);
1362 range
.upper
= min((frame
->bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1363 if (range
.upper
<= range
.lower
) return TRUE
;
1364 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1365 TRACE(" report=%s\n", debugrange(&i
->range
));
1369 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1370 INT nFirstRow
= max(frame
->top
/ infoPtr
->nItemHeight
, 0);
1371 INT nLastRow
= min((frame
->bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1378 if (infoPtr
->nItemWidth
)
1380 nFirstCol
= max(frame
->left
/ infoPtr
->nItemWidth
, 0);
1381 nLastCol
= min((frame
->right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1385 nFirstCol
= max(frame
->left
, 0);
1386 nLastCol
= min(frame
->right
- 1, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1389 lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1391 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1392 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1394 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1396 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1397 TRACE("building list ranges:\n");
1398 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1400 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1401 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1402 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1403 TRACE(" list=%s\n", debugrange(&item_range
));
1404 ranges_add(i
->ranges
, item_range
);
1412 * Creates an iterator over the items which intersect lprc.
1414 static BOOL
iterator_frameditems(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1419 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1421 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1422 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1424 return iterator_frameditems_absolute(i
, infoPtr
, &frame
);
1428 * Creates an iterator over the items which intersect the visible region of hdc.
1430 static BOOL
iterator_visibleitems(ITERATOR
*i
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1432 POINT Origin
, Position
;
1433 RECT rcItem
, rcClip
;
1436 rgntype
= GetClipBox(hdc
, &rcClip
);
1437 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1438 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1439 if (rgntype
== SIMPLEREGION
) return TRUE
;
1441 /* first deal with the special item */
1442 if (i
->nSpecial
!= -1)
1444 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1445 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1448 /* if we can't deal with the region, we'll just go with the simple range */
1449 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1450 TRACE("building visible range:\n");
1451 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1453 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1454 if (!ranges_add(i
->ranges
, i
->range
))
1456 ranges_destroy(i
->ranges
);
1462 /* now delete the invisible items from the list */
1463 while(iterator_next(i
))
1465 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1466 rcItem
.left
= (infoPtr
->uView
== LV_VIEW_DETAILS
) ? Origin
.x
: Position
.x
+ Origin
.x
;
1467 rcItem
.top
= Position
.y
+ Origin
.y
;
1468 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1469 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1470 if (!RectVisible(hdc
, &rcItem
))
1471 ranges_delitem(i
->ranges
, i
->nItem
);
1473 /* the iterator should restart on the next iterator_next */
1479 /* Remove common elements from two iterators */
1480 /* Passed iterators have to point on the first elements */
1481 static BOOL
iterator_remove_common_items(ITERATOR
*iter1
, ITERATOR
*iter2
)
1483 if(!iter1
->ranges
|| !iter2
->ranges
) {
1486 if(iter1
->ranges
|| iter2
->ranges
||
1487 (iter1
->range
.lower
<iter2
->range
.lower
&& iter1
->range
.upper
>iter2
->range
.upper
) ||
1488 (iter1
->range
.lower
>iter2
->range
.lower
&& iter1
->range
.upper
<iter2
->range
.upper
)) {
1489 ERR("result is not a one range iterator\n");
1493 if(iter1
->range
.lower
==-1 || iter2
->range
.lower
==-1)
1496 lower
= iter1
->range
.lower
;
1497 upper
= iter1
->range
.upper
;
1499 if(lower
< iter2
->range
.lower
)
1500 iter1
->range
.upper
= iter2
->range
.lower
;
1501 else if(upper
> iter2
->range
.upper
)
1502 iter1
->range
.lower
= iter2
->range
.upper
;
1504 iter1
->range
.lower
= iter1
->range
.upper
= -1;
1506 if(iter2
->range
.lower
< lower
)
1507 iter2
->range
.upper
= lower
;
1508 else if(iter2
->range
.upper
> upper
)
1509 iter2
->range
.lower
= upper
;
1511 iter2
->range
.lower
= iter2
->range
.upper
= -1;
1516 iterator_next(iter1
);
1517 iterator_next(iter2
);
1520 if(iter1
->nItem
==-1 || iter2
->nItem
==-1)
1523 if(iter1
->nItem
== iter2
->nItem
) {
1524 int delete = iter1
->nItem
;
1526 iterator_prev(iter1
);
1527 iterator_prev(iter2
);
1528 ranges_delitem(iter1
->ranges
, delete);
1529 ranges_delitem(iter2
->ranges
, delete);
1530 iterator_next(iter1
);
1531 iterator_next(iter2
);
1532 } else if(iter1
->nItem
> iter2
->nItem
)
1533 iterator_next(iter2
);
1535 iterator_next(iter1
);
1538 iter1
->nItem
= iter1
->range
.lower
= iter1
->range
.upper
= -1;
1539 iter2
->nItem
= iter2
->range
.lower
= iter2
->range
.upper
= -1;
1543 /******** Misc helper functions ************************************/
1545 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1546 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1548 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1549 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1552 static inline BOOL
is_autoarrange(const LISTVIEW_INFO
*infoPtr
)
1554 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1555 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
);
1558 static void toggle_checkbox_state(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1560 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
1561 if(state
== 1 || state
== 2)
1565 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
1566 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
1567 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
1571 /* this should be called after window style got updated,
1572 it used to reset view state to match current window style */
1573 static inline void map_style_view(LISTVIEW_INFO
*infoPtr
)
1575 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
1578 infoPtr
->uView
= LV_VIEW_ICON
;
1581 infoPtr
->uView
= LV_VIEW_DETAILS
;
1584 infoPtr
->uView
= LV_VIEW_SMALLICON
;
1587 infoPtr
->uView
= LV_VIEW_LIST
;
1591 /* computes next item id value */
1592 static DWORD
get_next_itemid(const LISTVIEW_INFO
*infoPtr
)
1594 INT count
= DPA_GetPtrCount(infoPtr
->hdpaItemIds
);
1598 ITEM_ID
*lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, count
- 1);
1599 return lpID
->id
+ 1;
1604 /******** Internal API functions ************************************/
1606 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1608 static COLUMN_INFO mainItem
;
1610 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1611 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1613 /* update cached column rectangles */
1614 if (infoPtr
->colRectsDirty
)
1617 LISTVIEW_INFO
*Ptr
= (LISTVIEW_INFO
*)infoPtr
;
1620 for (i
= 0; i
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); i
++) {
1621 info
= DPA_GetPtr(infoPtr
->hdpaColumns
, i
);
1622 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, i
, (LPARAM
)&info
->rcHeader
);
1624 Ptr
->colRectsDirty
= FALSE
;
1627 return DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1630 static INT
LISTVIEW_CreateHeader(LISTVIEW_INFO
*infoPtr
)
1632 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
1635 if (infoPtr
->hwndHeader
) return 0;
1637 TRACE("Creating header for list %p\n", infoPtr
->hwndSelf
);
1639 /* setup creation flags */
1640 dFlags
|= (LVS_NOSORTHEADER
& infoPtr
->dwStyle
) ? 0 : HDS_BUTTONS
;
1641 dFlags
|= (LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) ? HDS_HIDDEN
: 0;
1643 hInst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
1646 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
1647 0, 0, 0, 0, infoPtr
->hwndSelf
, NULL
, hInst
, NULL
);
1648 if (!infoPtr
->hwndHeader
) return -1;
1650 /* set header unicode format */
1651 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
1653 /* set header font */
1654 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, TRUE
);
1656 /* set header image list */
1657 if (infoPtr
->himlSmall
)
1658 SendMessageW(infoPtr
->hwndHeader
, HDM_SETIMAGELIST
, 0, (LPARAM
)infoPtr
->himlSmall
);
1660 LISTVIEW_UpdateSize(infoPtr
);
1665 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1667 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1670 static inline BOOL
LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO
*infoPtr
)
1672 return (infoPtr
->uView
== LV_VIEW_DETAILS
||
1673 infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
) &&
1674 !(infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
);
1677 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1679 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1682 /* used to handle collapse main item column case */
1683 static inline BOOL
LISTVIEW_DrawFocusRect(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1688 if (infoPtr
->rcFocus
.left
< infoPtr
->rcFocus
.right
)
1690 DWORD dwOldBkColor
, dwOldTextColor
;
1692 dwOldBkColor
= SetBkColor(hdc
, RGB(255, 255, 255));
1693 dwOldTextColor
= SetBkColor(hdc
, RGB(0, 0, 0));
1694 Ret
= DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1695 SetBkColor(hdc
, dwOldBkColor
);
1696 SetBkColor(hdc
, dwOldTextColor
);
1700 return (infoPtr
->rcFocus
.left
< infoPtr
->rcFocus
.right
) ?
1701 DrawFocusRect(hdc
, &infoPtr
->rcFocus
) : FALSE
;
1705 /* Listview invalidation functions: use _only_ these functions to invalidate */
1707 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1709 return infoPtr
->bRedraw
;
1712 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1714 if(!is_redrawing(infoPtr
)) return;
1715 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1716 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1719 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1723 if(!is_redrawing(infoPtr
)) return;
1724 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1725 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1728 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1730 POINT Origin
, Position
;
1733 if(!is_redrawing(infoPtr
)) return;
1734 assert (infoPtr
->uView
== LV_VIEW_DETAILS
);
1735 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1736 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1737 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1739 rcBox
.bottom
= infoPtr
->nItemHeight
;
1740 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1741 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1744 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1746 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1749 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1753 if(!is_redrawing(infoPtr
)) return;
1754 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1755 rcCol
.top
= infoPtr
->rcList
.top
;
1756 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1757 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1762 * Retrieves the number of items that can fit vertically in the client area.
1765 * [I] infoPtr : valid pointer to the listview structure
1768 * Number of items per row.
1770 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1772 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1774 return max(nListWidth
/(infoPtr
->nItemWidth
? infoPtr
->nItemWidth
: 1), 1);
1779 * Retrieves the number of items that can fit horizontally in the client
1783 * [I] infoPtr : valid pointer to the listview structure
1786 * Number of items per column.
1788 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1790 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1792 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1796 /*************************************************************************
1797 * LISTVIEW_ProcessLetterKeys
1799 * Processes keyboard messages generated by pressing the letter keys
1801 * What this does is perform a case insensitive search from the
1802 * current position with the following quirks:
1803 * - If two chars or more are pressed in quick succession we search
1804 * for the corresponding string (e.g. 'abc').
1805 * - If there is a delay we wipe away the current search string and
1806 * restart with just that char.
1807 * - If the user keeps pressing the same character, whether slowly or
1808 * fast, so that the search string is entirely composed of this
1809 * character ('aaaaa' for instance), then we search for first item
1810 * that starting with that character.
1811 * - If the user types the above character in quick succession, then
1812 * we must also search for the corresponding string ('aaaaa'), and
1813 * go to that string if there is a match.
1816 * [I] hwnd : handle to the window
1817 * [I] charCode : the character code, the actual character
1818 * [I] keyData : key data
1826 * - The current implementation has a list of characters it will
1827 * accept and it ignores everything else. In particular it will
1828 * ignore accentuated characters which seems to match what
1829 * Windows does. But I'm not sure it makes sense to follow
1831 * - We don't sound a beep when the search fails.
1835 * TREEVIEW_ProcessLetterKeys
1837 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1839 WCHAR buffer
[MAX_PATH
];
1846 /* simple parameter checking */
1847 if (!charCode
|| !keyData
|| infoPtr
->nItemCount
== 0) return 0;
1849 /* only allow the valid WM_CHARs through */
1850 if (!isalnumW(charCode
) &&
1851 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1852 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1853 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1854 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1855 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1856 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1857 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1858 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1859 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1862 /* update the search parameters */
1863 prevTime
= infoPtr
->lastKeyPressTimestamp
;
1864 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1865 diff
= infoPtr
->lastKeyPressTimestamp
- prevTime
;
1867 if (diff
>= 0 && diff
< KEY_DELAY
)
1869 if (infoPtr
->nSearchParamLength
< MAX_PATH
- 1)
1870 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++] = charCode
;
1872 if (infoPtr
->charCode
!= charCode
)
1873 infoPtr
->charCode
= charCode
= 0;
1877 infoPtr
->charCode
= charCode
;
1878 infoPtr
->szSearchParam
[0] = charCode
;
1879 infoPtr
->nSearchParamLength
= 1;
1882 /* should start from next after focused item, so next item that matches
1883 will be selected, if there isn't any and focused matches it will be selected
1884 on second search stage from beginning of the list */
1885 if (infoPtr
->nFocusedItem
>= 0 && infoPtr
->nItemCount
> 1)
1887 /* with some accumulated search data available start with current focus, otherwise
1888 it's excluded from search */
1889 startidx
= infoPtr
->nSearchParamLength
> 1 ? infoPtr
->nFocusedItem
: infoPtr
->nFocusedItem
+ 1;
1890 if (startidx
== infoPtr
->nItemCount
) startidx
= 0;
1895 /* let application handle this for virtual listview */
1896 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
1900 memset(&nmlv
.lvfi
, 0, sizeof(nmlv
.lvfi
));
1901 nmlv
.lvfi
.flags
= (LVFI_WRAP
| LVFI_PARTIAL
);
1902 nmlv
.lvfi
.psz
= infoPtr
->szSearchParam
;
1903 nmlv
.iStart
= startidx
;
1905 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
] = 0;
1907 nItem
= notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
1911 int i
= startidx
, endidx
;
1913 /* and search from the current position */
1915 endidx
= infoPtr
->nItemCount
;
1917 /* first search in [startidx, endidx), on failure continue in [0, startidx) */
1920 /* start from first item if not found with >= startidx */
1921 if (i
== infoPtr
->nItemCount
&& startidx
> 0)
1927 for (i
= startidx
; i
< endidx
; i
++)
1930 item
.mask
= LVIF_TEXT
;
1933 item
.pszText
= buffer
;
1934 item
.cchTextMax
= MAX_PATH
;
1935 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1937 if (!lstrncmpiW(item
.pszText
, infoPtr
->szSearchParam
, infoPtr
->nSearchParamLength
))
1942 /* this is used to find first char match when search string is not available yet,
1943 otherwise every WM_CHAR will search to next item by first char, ignoring that we're
1944 already waiting for user to complete a string */
1945 else if (nItem
== -1 && infoPtr
->nSearchParamLength
== 1 && !lstrncmpiW(item
.pszText
, infoPtr
->szSearchParam
, 1))
1947 /* this would work but we must keep looking for a longer match */
1952 if ( nItem
!= -1 || /* found something */
1953 endidx
!= infoPtr
->nItemCount
|| /* second search done */
1954 (startidx
== 0 && endidx
== infoPtr
->nItemCount
) /* full range for first search */ )
1960 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1965 /*************************************************************************
1966 * LISTVIEW_UpdateHeaderSize [Internal]
1968 * Function to resize the header control
1971 * [I] hwnd : handle to a window
1972 * [I] nNewScrollPos : scroll pos to set
1977 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1982 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1984 if (!infoPtr
->hwndHeader
) return;
1986 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1987 point
[0].x
= winRect
.left
;
1988 point
[0].y
= winRect
.top
;
1989 point
[1].x
= winRect
.right
;
1990 point
[1].y
= winRect
.bottom
;
1992 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1993 point
[0].x
= -nNewScrollPos
;
1994 point
[1].x
+= nNewScrollPos
;
1996 SetWindowPos(infoPtr
->hwndHeader
,0,
1997 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1998 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
1999 SWP_NOZORDER
| SWP_NOACTIVATE
);
2004 * Update the scrollbars. This functions should be called whenever
2005 * the content, size or view changes.
2008 * [I] infoPtr : valid pointer to the listview structure
2013 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*infoPtr
)
2015 SCROLLINFO horzInfo
, vertInfo
;
2018 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
2020 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
2021 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
2022 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2024 /* for now, we'll set info.nMax to the _count_, and adjust it later */
2025 if (infoPtr
->uView
== LV_VIEW_LIST
)
2027 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
2028 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
2030 /* scroll by at least one column per page */
2031 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
2032 horzInfo
.nPage
= infoPtr
->nItemWidth
;
2034 if (infoPtr
->nItemWidth
)
2035 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
2037 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2039 horzInfo
.nMax
= infoPtr
->nItemWidth
;
2041 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2045 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
2048 if (LISTVIEW_IsHeaderEnabled(infoPtr
))
2050 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
))
2055 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
2056 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
2058 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcHeader
);
2059 horzInfo
.nMax
= rcHeader
.right
;
2060 TRACE("horzInfo.nMax=%d\n", horzInfo
.nMax
);
2064 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
2065 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
2066 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
2067 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
2068 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
2070 /* Setting the horizontal scroll can change the listview size
2071 * (and potentially everything else) so we need to recompute
2072 * everything again for the vertical scroll
2075 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
2076 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
2077 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2079 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2081 vertInfo
.nMax
= infoPtr
->nItemCount
;
2083 /* scroll by at least one page */
2084 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
2085 vertInfo
.nPage
= infoPtr
->nItemHeight
;
2087 if (infoPtr
->nItemHeight
> 0)
2088 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
2090 else if (infoPtr
->uView
!= LV_VIEW_LIST
) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2094 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
2097 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
2098 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
2099 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
2100 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
2101 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
2103 /* Change of the range may have changed the scroll pos. If so move the content */
2104 if (dx
!= 0 || dy
!= 0)
2107 listRect
= infoPtr
->rcList
;
2108 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
2109 SW_ERASE
| SW_INVALIDATE
);
2112 /* Update the Header Control */
2113 if (infoPtr
->hwndHeader
)
2115 horzInfo
.fMask
= SIF_POS
;
2116 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
2117 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
2124 * Shows/hides the focus rectangle.
2127 * [I] infoPtr : valid pointer to the listview structure
2128 * [I] fShow : TRUE to show the focus, FALSE to hide it.
2133 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
2137 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
2139 if (infoPtr
->nFocusedItem
< 0) return;
2141 /* we need some gymnastics in ICON mode to handle large items */
2142 if (infoPtr
->uView
== LV_VIEW_ICON
)
2146 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
2147 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
2149 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
2154 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
2156 /* for some reason, owner draw should work only in report mode */
2157 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
2162 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2163 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2165 item
.iItem
= infoPtr
->nFocusedItem
;
2167 item
.mask
= LVIF_PARAM
;
2168 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
2170 ZeroMemory(&dis
, sizeof(dis
));
2171 dis
.CtlType
= ODT_LISTVIEW
;
2172 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
2173 dis
.itemID
= item
.iItem
;
2174 dis
.itemAction
= ODA_FOCUS
;
2175 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
2176 dis
.hwndItem
= infoPtr
->hwndSelf
;
2178 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
2179 dis
.itemData
= item
.lParam
;
2181 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
2183 SelectObject(hdc
, hOldFont
);
2187 LISTVIEW_DrawFocusRect(infoPtr
, hdc
);
2190 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2194 * Invalidates all visible selected items.
2196 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
2200 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
2201 while(iterator_next(&i
))
2203 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
2204 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
2206 iterator_destroy(&i
);
2211 * DESCRIPTION: [INTERNAL]
2212 * Computes an item's (left,top) corner, relative to rcView.
2213 * That is, the position has NOT been made relative to the Origin.
2214 * This is deliberate, to avoid computing the Origin over, and
2215 * over again, when this function is called in a loop. Instead,
2216 * one can factor the computation of the Origin before the loop,
2217 * and offset the value returned by this function, on every iteration.
2220 * [I] infoPtr : valid pointer to the listview structure
2221 * [I] nItem : item number
2222 * [O] lpptOrig : item top, left corner
2227 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
2229 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
2231 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
2233 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2234 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2236 else if (infoPtr
->uView
== LV_VIEW_LIST
)
2238 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
2239 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
2240 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
2242 else /* LV_VIEW_DETAILS */
2244 lpptPosition
->x
= REPORT_MARGINX
;
2245 /* item is always at zero indexed column */
2246 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2247 lpptPosition
->x
+= LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
;
2248 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
2253 * DESCRIPTION: [INTERNAL]
2254 * Compute the rectangles of an item. This is to localize all
2255 * the computations in one place. If you are not interested in some
2256 * of these values, simply pass in a NULL -- the function is smart
2257 * enough to compute only what's necessary. The function computes
2258 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2259 * one, the BOX rectangle. This rectangle is very cheap to compute,
2260 * and is guaranteed to contain all the other rectangles. Computing
2261 * the ICON rect is also cheap, but all the others are potentially
2262 * expensive. This gives an easy and effective optimization when
2263 * searching (like point inclusion, or rectangle intersection):
2264 * first test against the BOX, and if TRUE, test against the desired
2266 * If the function does not have all the necessary information
2267 * to computed the requested rectangles, will crash with a
2268 * failed assertion. This is done so we catch all programming
2269 * errors, given that the function is called only from our code.
2271 * We have the following 'special' meanings for a few fields:
2272 * * If LVIS_FOCUSED is set, we assume the item has the focus
2273 * This is important in ICON mode, where it might get a larger
2274 * then usual rectangle
2276 * Please note that subitem support works only in REPORT mode.
2279 * [I] infoPtr : valid pointer to the listview structure
2280 * [I] lpLVItem : item to compute the measures for
2281 * [O] lprcBox : ptr to Box rectangle
2282 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2283 * [0] lprcSelectBox : ptr to select box rectangle
2284 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2285 * [O] lprcIcon : ptr to Icon rectangle
2286 * Same as LVM_GETITEMRECT with LVIR_ICON
2287 * [O] lprcStateIcon: ptr to State Icon rectangle
2288 * [O] lprcLabel : ptr to Label rectangle
2289 * Same as LVM_GETITEMRECT with LVIR_LABEL
2294 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
2295 LPRECT lprcBox
, LPRECT lprcSelectBox
,
2296 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
2298 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
2299 RECT Box
, SelectBox
, Icon
, Label
;
2300 COLUMN_INFO
*lpColumnInfo
= NULL
;
2301 SIZE labelSize
= { 0, 0 };
2303 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
2305 /* Be smart and try to figure out the minimum we have to do */
2306 if (lpLVItem
->iSubItem
) assert(infoPtr
->uView
== LV_VIEW_DETAILS
);
2307 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprcBox
|| lprcLabel
))
2309 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
2310 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
2312 if (lprcSelectBox
) doSelectBox
= TRUE
;
2313 if (lprcLabel
) doLabel
= TRUE
;
2314 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
2321 /************************************************************/
2322 /* compute the box rectangle (it should be cheap to do) */
2323 /************************************************************/
2324 if (lpLVItem
->iSubItem
|| infoPtr
->uView
== LV_VIEW_DETAILS
)
2325 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
2327 if (lpLVItem
->iSubItem
)
2329 Box
= lpColumnInfo
->rcHeader
;
2334 Box
.right
= infoPtr
->nItemWidth
;
2337 Box
.bottom
= infoPtr
->nItemHeight
;
2339 /******************************************************************/
2340 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2341 /******************************************************************/
2344 LONG state_width
= 0;
2346 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
2347 state_width
= infoPtr
->iconStateSize
.cx
;
2349 if (infoPtr
->uView
== LV_VIEW_ICON
)
2351 Icon
.left
= Box
.left
+ state_width
;
2352 if (infoPtr
->himlNormal
)
2353 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
2354 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2355 Icon
.right
= Icon
.left
;
2356 Icon
.bottom
= Icon
.top
;
2357 if (infoPtr
->himlNormal
)
2359 Icon
.right
+= infoPtr
->iconSize
.cx
;
2360 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2363 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2365 Icon
.left
= Box
.left
+ state_width
;
2367 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lpLVItem
->iSubItem
== 0)
2369 /* we need the indent in report mode */
2370 assert(lpLVItem
->mask
& LVIF_INDENT
);
2371 Icon
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
+ REPORT_MARGINX
;
2375 Icon
.right
= Icon
.left
;
2376 if (infoPtr
->himlSmall
&&
2377 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 ||
2378 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2379 Icon
.right
+= infoPtr
->iconSize
.cx
;
2380 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2382 if(lprcIcon
) *lprcIcon
= Icon
;
2383 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2385 /* TODO: is this correct? */
2388 lprcStateIcon
->left
= Icon
.left
- state_width
;
2389 lprcStateIcon
->right
= Icon
.left
;
2390 lprcStateIcon
->top
= Icon
.top
;
2391 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2392 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2395 else Icon
.right
= 0;
2397 /************************************************************/
2398 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2399 /************************************************************/
2402 /* calculate how far to the right can the label stretch */
2403 Label
.right
= Box
.right
;
2404 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2406 if (lpLVItem
->iSubItem
== 0)
2408 /* we need a zero based rect here */
2409 Label
= lpColumnInfo
->rcHeader
;
2410 OffsetRect(&Label
, -Label
.left
, 0);
2414 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && infoPtr
->uView
== LV_VIEW_DETAILS
))
2416 labelSize
.cx
= infoPtr
->nItemWidth
;
2417 labelSize
.cy
= infoPtr
->nItemHeight
;
2421 /* we need the text in non owner draw mode */
2422 assert(lpLVItem
->mask
& LVIF_TEXT
);
2423 if (is_text(lpLVItem
->pszText
))
2425 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2426 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2427 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2431 /* compute rough rectangle where the label will go */
2432 SetRectEmpty(&rcText
);
2433 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2434 rcText
.bottom
= infoPtr
->nItemHeight
;
2435 if (infoPtr
->uView
== LV_VIEW_ICON
)
2436 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2438 /* now figure out the flags */
2439 if (infoPtr
->uView
== LV_VIEW_ICON
)
2440 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2442 uFormat
= LV_SL_DT_FLAGS
;
2444 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2446 if (rcText
.right
!= rcText
.left
)
2447 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2449 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2451 SelectObject(hdc
, hOldFont
);
2452 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2456 if (infoPtr
->uView
== LV_VIEW_ICON
)
2458 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2459 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2460 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2461 Label
.right
= Label
.left
+ labelSize
.cx
;
2462 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2463 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2465 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2466 labelSize
.cy
/= infoPtr
->ntmHeight
;
2467 labelSize
.cy
= max(labelSize
.cy
, 1);
2468 labelSize
.cy
*= infoPtr
->ntmHeight
;
2470 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2472 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2474 Label
.left
= Icon
.right
;
2475 Label
.top
= Box
.top
;
2476 Label
.right
= lpLVItem
->iSubItem
? lpColumnInfo
->rcHeader
.right
:
2477 lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
2478 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2480 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2482 Label
.left
= Icon
.right
;
2483 Label
.top
= Box
.top
;
2484 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2485 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2488 if (lprcLabel
) *lprcLabel
= Label
;
2489 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2492 /************************************************************/
2493 /* compute SELECT bounding box */
2494 /************************************************************/
2497 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2499 SelectBox
.left
= Icon
.left
;
2500 SelectBox
.top
= Box
.top
;
2501 SelectBox
.bottom
= Box
.bottom
;
2504 SelectBox
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2506 SelectBox
.right
= min(Label
.left
+ MAX_EMPTYTEXT_SELECT_WIDTH
, Label
.right
);
2510 UnionRect(&SelectBox
, &Icon
, &Label
);
2512 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2513 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2516 /* Fix the Box if necessary */
2519 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2520 else *lprcBox
= Box
;
2522 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2526 * DESCRIPTION: [INTERNAL]
2529 * [I] infoPtr : valid pointer to the listview structure
2530 * [I] nItem : item number
2531 * [O] lprcBox : ptr to Box rectangle
2536 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2538 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2539 POINT Position
, Origin
;
2542 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2543 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2545 /* Be smart and try to figure out the minimum we have to do */
2547 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2548 lvItem
.mask
|= LVIF_TEXT
;
2549 lvItem
.iItem
= nItem
;
2550 lvItem
.iSubItem
= 0;
2551 lvItem
.pszText
= szDispText
;
2552 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2553 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2554 if (infoPtr
->uView
== LV_VIEW_ICON
)
2556 lvItem
.mask
|= LVIF_STATE
;
2557 lvItem
.stateMask
= LVIS_FOCUSED
;
2558 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2560 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2562 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
&&
2563 SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, 0, 0))
2565 OffsetRect(lprcBox
, Origin
.x
, Position
.y
+ Origin
.y
);
2568 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2571 /* LISTVIEW_MapIdToIndex helper */
2572 static INT CALLBACK
MapIdSearchCompare(LPVOID p1
, LPVOID p2
, LPARAM lParam
)
2574 ITEM_ID
*id1
= (ITEM_ID
*)p1
;
2575 ITEM_ID
*id2
= (ITEM_ID
*)p2
;
2577 if (id1
->id
== id2
->id
) return 0;
2579 return (id1
->id
< id2
->id
) ? -1 : 1;
2584 * Returns the item index for id specified.
2587 * [I] infoPtr : valid pointer to the listview structure
2588 * [I] iID : item id to get index for
2591 * Item index, or -1 on failure.
2593 static INT
LISTVIEW_MapIdToIndex(const LISTVIEW_INFO
*infoPtr
, UINT iID
)
2598 TRACE("iID=%d\n", iID
);
2600 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return -1;
2601 if (infoPtr
->nItemCount
== 0) return -1;
2604 index
= DPA_Search(infoPtr
->hdpaItemIds
, &ID
, -1, MapIdSearchCompare
, 0, DPAS_SORTED
);
2608 ITEM_ID
*lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, index
);
2609 return DPA_GetPtrIndex(infoPtr
->hdpaItems
, lpID
->item
);
2617 * Returns the item id for index given.
2620 * [I] infoPtr : valid pointer to the listview structure
2621 * [I] iItem : item index to get id for
2626 static DWORD
LISTVIEW_MapIndexToId(const LISTVIEW_INFO
*infoPtr
, INT iItem
)
2631 TRACE("iItem=%d\n", iItem
);
2633 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return -1;
2634 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
) return -1;
2636 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, iItem
);
2637 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
2639 return lpItem
->id
->id
;
2644 * Returns the current icon position, and advances it along the top.
2645 * The returned position is not offset by Origin.
2648 * [I] infoPtr : valid pointer to the listview structure
2649 * [O] lpPos : will get the current icon position
2654 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2656 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2658 *lpPos
= infoPtr
->currIconPos
;
2660 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2661 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2663 infoPtr
->currIconPos
.x
= 0;
2664 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2670 * Returns the current icon position, and advances it down the left edge.
2671 * The returned position is not offset by Origin.
2674 * [I] infoPtr : valid pointer to the listview structure
2675 * [O] lpPos : will get the current icon position
2680 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2682 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2684 *lpPos
= infoPtr
->currIconPos
;
2686 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2687 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2689 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2690 infoPtr
->currIconPos
.y
= 0;
2696 * Moves an icon to the specified position.
2697 * It takes care of invalidating the item, etc.
2700 * [I] infoPtr : valid pointer to the listview structure
2701 * [I] nItem : the item to move
2702 * [I] lpPos : the new icon position
2703 * [I] isNew : flags the item as being new
2709 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2715 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2716 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2718 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2719 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2722 /* Allocating a POINTER for every item is too resource intensive,
2723 * so we'll keep the (x,y) in different arrays */
2724 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2725 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2727 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2734 * Arranges listview items in icon display mode.
2737 * [I] infoPtr : valid pointer to the listview structure
2738 * [I] nAlignCode : alignment code
2744 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2746 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2750 if (infoPtr
->uView
!= LV_VIEW_ICON
&& infoPtr
->uView
!= LV_VIEW_SMALLICON
) return FALSE
;
2752 TRACE("nAlignCode=%d\n", nAlignCode
);
2754 if (nAlignCode
== LVA_DEFAULT
)
2756 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2757 else nAlignCode
= LVA_ALIGNTOP
;
2762 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2763 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2764 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2765 default: return FALSE
;
2768 infoPtr
->bAutoarrange
= TRUE
;
2769 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2770 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2772 next_pos(infoPtr
, &pos
);
2773 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2781 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2782 * For LVS_REPORT always returns empty rectangle.
2785 * [I] infoPtr : valid pointer to the listview structure
2786 * [O] lprcView : bounding rectangle
2792 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2796 SetRectEmpty(lprcView
);
2798 switch (infoPtr
->uView
)
2801 case LV_VIEW_SMALLICON
:
2802 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2804 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2805 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2806 lprcView
->right
= max(lprcView
->right
, x
);
2807 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2809 if (infoPtr
->nItemCount
> 0)
2811 lprcView
->right
+= infoPtr
->nItemWidth
;
2812 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2817 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2818 x
= infoPtr
->nItemCount
/ y
;
2819 if (infoPtr
->nItemCount
% y
) x
++;
2820 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2821 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2828 * Retrieves the bounding rectangle of all the items.
2831 * [I] infoPtr : valid pointer to the listview structure
2832 * [O] lprcView : bounding rectangle
2838 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2842 TRACE("(lprcView=%p)\n", lprcView
);
2844 if (!lprcView
) return FALSE
;
2846 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2848 if (infoPtr
->uView
!= LV_VIEW_DETAILS
)
2850 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2851 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2854 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2861 * Retrieves the subitem pointer associated with the subitem index.
2864 * [I] hdpaSubItems : DPA handle for a specific item
2865 * [I] nSubItem : index of subitem
2868 * SUCCESS : subitem pointer
2871 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2873 SUBITEM_INFO
*lpSubItem
;
2876 /* we should binary search here if need be */
2877 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2879 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
2880 if (lpSubItem
->iSubItem
== nSubItem
)
2890 * Calculates the desired item width.
2893 * [I] infoPtr : valid pointer to the listview structure
2896 * The desired item width.
2898 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2902 TRACE("uView=%d\n", infoPtr
->uView
);
2904 if (infoPtr
->uView
== LV_VIEW_ICON
)
2905 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2906 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2908 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2913 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
2914 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
2916 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcHeader
);
2917 nItemWidth
= rcHeader
.right
;
2920 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2922 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2926 lvItem
.mask
= LVIF_TEXT
;
2927 lvItem
.iSubItem
= 0;
2929 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2932 lvItem
.pszText
= szDispText
;
2933 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2934 if (LISTVIEW_GetItemW(infoPtr
, &lvItem
))
2935 nItemWidth
= max(LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
),
2939 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2940 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2942 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2950 * Calculates the desired item height.
2953 * [I] infoPtr : valid pointer to the listview structure
2956 * The desired item height.
2958 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
2962 TRACE("uView=%d\n", infoPtr
->uView
);
2964 if (infoPtr
->uView
== LV_VIEW_ICON
)
2965 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2968 nItemHeight
= infoPtr
->ntmHeight
;
2969 if (infoPtr
->himlState
)
2970 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2971 if (infoPtr
->himlSmall
)
2972 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2973 nItemHeight
+= HEIGHT_PADDING
;
2974 if (infoPtr
->nMeasureItemHeight
> 0)
2975 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2978 return max(nItemHeight
, 1);
2983 * Updates the width, and height of an item.
2986 * [I] infoPtr : valid pointer to the listview structure
2991 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2993 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2994 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
3000 * Retrieves and saves important text metrics info for the current
3004 * [I] infoPtr : valid pointer to the listview structure
3007 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
3009 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
3010 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
3011 HFONT hOldFont
= SelectObject(hdc
, hFont
);
3015 if (GetTextMetricsW(hdc
, &tm
))
3017 infoPtr
->ntmHeight
= tm
.tmHeight
;
3018 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
3021 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
3022 infoPtr
->nEllipsisWidth
= sz
.cx
;
3024 SelectObject(hdc
, hOldFont
);
3025 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
3027 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
3032 * A compare function for ranges
3035 * [I] range1 : pointer to range 1;
3036 * [I] range2 : pointer to range 2;
3040 * > 0 : if range 1 > range 2
3041 * < 0 : if range 2 > range 1
3042 * = 0 : if range intersects range 2
3044 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
3048 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
3050 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
3055 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1
), debugrange(range2
), cmp
);
3060 #define ranges_check(ranges, desc) if (TRACE_ON(listview)) ranges_assert(ranges, desc, __FILE__, __LINE__)
3062 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *file
, int line
)
3067 TRACE("*** Checking %s:%d:%s ***\n", file
, line
, desc
);
3069 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
3070 ranges_dump(ranges
);
3071 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
3073 prev
= DPA_GetPtr(ranges
->hdpa
, 0);
3074 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
3075 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3077 curr
= DPA_GetPtr(ranges
->hdpa
, i
);
3078 assert (prev
->upper
<= curr
->lower
);
3079 assert (curr
->lower
< curr
->upper
);
3083 TRACE("--- Done checking---\n");
3086 static RANGES
ranges_create(int count
)
3088 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
3089 if (!ranges
) return NULL
;
3090 ranges
->hdpa
= DPA_Create(count
);
3091 if (ranges
->hdpa
) return ranges
;
3096 static void ranges_clear(RANGES ranges
)
3100 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3101 Free(DPA_GetPtr(ranges
->hdpa
, i
));
3102 DPA_DeleteAllPtrs(ranges
->hdpa
);
3106 static void ranges_destroy(RANGES ranges
)
3108 if (!ranges
) return;
3109 ranges_clear(ranges
);
3110 DPA_Destroy(ranges
->hdpa
);
3114 static RANGES
ranges_clone(RANGES ranges
)
3119 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
3121 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3123 RANGE
*newrng
= Alloc(sizeof(RANGE
));
3124 if (!newrng
) goto fail
;
3125 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
3126 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
3131 TRACE ("clone failed\n");
3132 ranges_destroy(clone
);
3136 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
3140 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
3141 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
3146 static void ranges_dump(RANGES ranges
)
3150 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3151 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
3154 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
3156 RANGE srchrng
= { nItem
, nItem
+ 1 };
3158 TRACE("(nItem=%d)\n", nItem
);
3159 ranges_check(ranges
, "before contain");
3160 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
3163 static INT
ranges_itemcount(RANGES ranges
)
3167 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
3169 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
3170 count
+= sel
->upper
- sel
->lower
;
3176 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
3178 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
3181 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
3182 if (index
== -1) return TRUE
;
3184 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
3186 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
3187 if (chkrng
->lower
>= nItem
)
3188 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
3189 if (chkrng
->upper
> nItem
)
3190 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
3195 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
3200 TRACE("(%s)\n", debugrange(&range
));
3201 ranges_check(ranges
, "before add");
3203 /* try find overlapping regions first */
3204 srchrgn
.lower
= range
.lower
- 1;
3205 srchrgn
.upper
= range
.upper
+ 1;
3206 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
3212 TRACE("Adding new range\n");
3214 /* create the brand new range to insert */
3215 newrgn
= Alloc(sizeof(RANGE
));
3216 if(!newrgn
) goto fail
;
3219 /* figure out where to insert it */
3220 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
3221 TRACE("index=%d\n", index
);
3222 if (index
== -1) index
= 0;
3224 /* and get it over with */
3225 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
3233 RANGE
*chkrgn
, *mrgrgn
;
3234 INT fromindex
, mergeindex
;
3236 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
3237 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
3239 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
3240 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
3242 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
3244 /* merge now common ranges */
3246 srchrgn
.lower
= chkrgn
->lower
- 1;
3247 srchrgn
.upper
= chkrgn
->upper
+ 1;
3251 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
3252 if (mergeindex
== -1) break;
3253 if (mergeindex
== index
)
3255 fromindex
= index
+ 1;
3259 TRACE("Merge with index %i\n", mergeindex
);
3261 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
3262 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
3263 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
3265 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
3266 if (mergeindex
< index
) index
--;
3270 ranges_check(ranges
, "after add");
3274 ranges_check(ranges
, "failed add");
3278 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
3283 TRACE("(%s)\n", debugrange(&range
));
3284 ranges_check(ranges
, "before del");
3286 /* we don't use DPAS_SORTED here, since we need *
3287 * to find the first overlapping range */
3288 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
3291 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
3293 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
3295 /* case 1: Same range */
3296 if ( (chkrgn
->upper
== range
.upper
) &&
3297 (chkrgn
->lower
== range
.lower
) )
3299 DPA_DeletePtr(ranges
->hdpa
, index
);
3303 /* case 2: engulf */
3304 else if ( (chkrgn
->upper
<= range
.upper
) &&
3305 (chkrgn
->lower
>= range
.lower
) )
3307 DPA_DeletePtr(ranges
->hdpa
, index
);
3310 /* case 3: overlap upper */
3311 else if ( (chkrgn
->upper
<= range
.upper
) &&
3312 (chkrgn
->lower
< range
.lower
) )
3314 chkrgn
->upper
= range
.lower
;
3316 /* case 4: overlap lower */
3317 else if ( (chkrgn
->upper
> range
.upper
) &&
3318 (chkrgn
->lower
>= range
.lower
) )
3320 chkrgn
->lower
= range
.upper
;
3323 /* case 5: fully internal */
3328 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
3329 newrgn
->lower
= chkrgn
->lower
;
3330 newrgn
->upper
= range
.lower
;
3331 chkrgn
->lower
= range
.upper
;
3332 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
3340 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
3343 ranges_check(ranges
, "after del");
3347 ranges_check(ranges
, "failed del");
3353 * Removes all selection ranges
3356 * [I] infoPtr : valid pointer to the listview structure
3357 * [I] toSkip : item range to skip removing the selection
3363 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
3372 lvItem
.stateMask
= LVIS_SELECTED
;
3374 /* need to clone the DPA because callbacks can change it */
3375 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
3376 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
3377 while(iterator_next(&i
))
3378 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
3379 /* note that the iterator destructor will free the cloned range */
3380 iterator_destroy(&i
);
3385 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3389 if (!(toSkip
= ranges_create(1))) return FALSE
;
3390 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
3391 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
3392 ranges_destroy(toSkip
);
3396 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
3398 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
3403 * Retrieves the number of items that are marked as selected.
3406 * [I] infoPtr : valid pointer to the listview structure
3409 * Number of items selected.
3411 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
3413 INT nSelectedCount
= 0;
3415 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3418 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3420 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
3425 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
3427 TRACE("nSelectedCount=%d\n", nSelectedCount
);
3428 return nSelectedCount
;
3433 * Manages the item focus.
3436 * [I] infoPtr : valid pointer to the listview structure
3437 * [I] nItem : item index
3440 * TRUE : focused item changed
3441 * FALSE : focused item has NOT changed
3443 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3445 INT oldFocus
= infoPtr
->nFocusedItem
;
3448 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
3450 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
3451 lvItem
.stateMask
= LVIS_FOCUSED
;
3452 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
3454 return oldFocus
!= infoPtr
->nFocusedItem
;
3457 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
3459 if (nShiftItem
< nItem
) return nShiftItem
;
3461 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
3463 if (direction
> 0) return nShiftItem
+ direction
;
3465 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3468 /* This function updates focus index.
3471 focus : current focus index
3472 item : index of item to be added/removed
3473 direction : add/remove flag
3475 static void LISTVIEW_ShiftFocus(LISTVIEW_INFO
*infoPtr
, INT focus
, INT item
, INT direction
)
3477 BOOL old_change
= infoPtr
->bDoChangeNotify
;
3479 infoPtr
->bDoChangeNotify
= FALSE
;
3480 focus
= shift_item(infoPtr
, focus
, item
, direction
);
3481 if (focus
!= infoPtr
->nFocusedItem
)
3482 LISTVIEW_SetItemFocus(infoPtr
, focus
);
3483 infoPtr
->bDoChangeNotify
= old_change
;
3488 * Updates the various indices after an item has been inserted or deleted.
3491 * [I] infoPtr : valid pointer to the listview structure
3492 * [I] nItem : item index
3493 * [I] direction : Direction of shift, +1 or -1.
3498 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3500 TRACE("Shifting %i, %i steps\n", nItem
, direction
);
3502 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3503 assert(abs(direction
) == 1);
3504 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3506 /* But we are not supposed to modify nHotItem! */
3511 * Adds a block of selections.
3514 * [I] infoPtr : valid pointer to the listview structure
3515 * [I] nItem : item index
3518 * Whether the window is still valid.
3520 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3522 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3523 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3524 HWND hwndSelf
= infoPtr
->hwndSelf
;
3525 NMLVODSTATECHANGE nmlv
;
3530 /* Temporarily disable change notification
3531 * If the control is LVS_OWNERDATA, we need to send
3532 * only one LVN_ODSTATECHANGED notification.
3533 * See MSDN documentation for LVN_ITEMCHANGED.
3535 bOldChange
= infoPtr
->bDoChangeNotify
;
3536 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3538 if (nFirst
== -1) nFirst
= nItem
;
3540 item
.state
= LVIS_SELECTED
;
3541 item
.stateMask
= LVIS_SELECTED
;
3543 for (i
= nFirst
; i
<= nLast
; i
++)
3544 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3546 ZeroMemory(&nmlv
, sizeof(nmlv
));
3547 nmlv
.iFrom
= nFirst
;
3550 nmlv
.uNewState
= item
.state
;
3552 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3553 if (!IsWindow(hwndSelf
))
3555 infoPtr
->bDoChangeNotify
= bOldChange
;
3562 * Sets a single group selection.
3565 * [I] infoPtr : valid pointer to the listview structure
3566 * [I] nItem : item index
3571 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3578 if (!(selection
= ranges_create(100))) return;
3580 item
.state
= LVIS_SELECTED
;
3581 item
.stateMask
= LVIS_SELECTED
;
3583 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
3585 if (infoPtr
->nSelectionMark
== -1)
3587 infoPtr
->nSelectionMark
= nItem
;
3588 ranges_additem(selection
, nItem
);
3594 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3595 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3596 ranges_add(selection
, sel
);
3601 RECT rcItem
, rcSel
, rcSelMark
;
3604 rcItem
.left
= LVIR_BOUNDS
;
3605 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) {
3606 ranges_destroy (selection
);
3609 rcSelMark
.left
= LVIR_BOUNDS
;
3610 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) {
3611 ranges_destroy (selection
);
3614 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3615 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3616 while(iterator_next(&i
))
3618 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3619 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3621 iterator_destroy(&i
);
3624 /* disable per item notifications on LVS_OWNERDATA style
3625 FIXME: single LVN_ODSTATECHANGED should be used */
3626 bOldChange
= infoPtr
->bDoChangeNotify
;
3627 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3629 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3632 iterator_rangesitems(&i
, selection
);
3633 while(iterator_next(&i
))
3634 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3635 /* this will also destroy the selection */
3636 iterator_destroy(&i
);
3638 infoPtr
->bDoChangeNotify
= bOldChange
;
3640 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3645 * Sets a single selection.
3648 * [I] infoPtr : valid pointer to the listview structure
3649 * [I] nItem : item index
3654 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3658 TRACE("nItem=%d\n", nItem
);
3660 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3662 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3663 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3664 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3666 infoPtr
->nSelectionMark
= nItem
;
3671 * Set selection(s) with keyboard.
3674 * [I] infoPtr : valid pointer to the listview structure
3675 * [I] nItem : item index
3676 * [I] space : VK_SPACE code sent
3679 * SUCCESS : TRUE (needs to be repainted)
3680 * FAILURE : FALSE (nothing has changed)
3682 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL space
)
3684 /* FIXME: pass in the state */
3685 WORD wShift
= GetKeyState(VK_SHIFT
) & 0x8000;
3686 WORD wCtrl
= GetKeyState(VK_CONTROL
) & 0x8000;
3687 BOOL bResult
= FALSE
;
3689 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3690 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3694 if (infoPtr
->dwStyle
& LVS_SINGLESEL
|| (wShift
== 0 && wCtrl
== 0))
3695 LISTVIEW_SetSelection(infoPtr
, nItem
);
3699 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3703 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3704 lvItem
.stateMask
= LVIS_SELECTED
;
3707 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3708 if (lvItem
.state
& LVIS_SELECTED
)
3709 infoPtr
->nSelectionMark
= nItem
;
3711 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3714 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3717 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3721 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3723 LVHITTESTINFO lvHitTestInfo
;
3725 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3726 lvHitTestInfo
.pt
.x
= pt
.x
;
3727 lvHitTestInfo
.pt
.y
= pt
.y
;
3729 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3731 lpLVItem
->mask
= LVIF_PARAM
;
3732 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3733 lpLVItem
->iSubItem
= 0;
3735 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3738 static inline BOOL
LISTVIEW_IsHotTracking(const LISTVIEW_INFO
*infoPtr
)
3740 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3741 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3742 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3747 * Called when the mouse is being actively tracked and has hovered for a specified
3751 * [I] infoPtr : valid pointer to the listview structure
3752 * [I] fwKeys : key indicator
3753 * [I] x,y : mouse position
3756 * 0 if the message was processed, non-zero if there was an error
3759 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3760 * over the item for a certain period of time.
3763 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, INT x
, INT y
)
3767 if (notify_hdr(infoPtr
, NM_HOVER
, &hdr
)) return 0;
3769 if (LISTVIEW_IsHotTracking(infoPtr
))
3777 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3778 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3780 SetFocus(infoPtr
->hwndSelf
);
3786 #define SCROLL_LEFT 0x1
3787 #define SCROLL_RIGHT 0x2
3788 #define SCROLL_UP 0x4
3789 #define SCROLL_DOWN 0x8
3793 * Utility routine to draw and highlight items within a marquee selection rectangle.
3796 * [I] infoPtr : valid pointer to the listview structure
3797 * [I] coords_orig : original co-ordinates of the cursor
3798 * [I] coords_offs : offsetted coordinates of the cursor
3799 * [I] offset : offset amount
3800 * [I] scroll : Bitmask of which directions we should scroll, if at all
3805 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO
*infoPtr
, const POINT
*coords_orig
,
3806 const POINT
*coords_offs
, const POINT
*offset
,
3809 BOOL controlDown
= FALSE
;
3811 ITERATOR old_elems
, new_elems
;
3814 if (coords_offs
->x
> infoPtr
->marqueeOrigin
.x
)
3816 rect
.left
= infoPtr
->marqueeOrigin
.x
;
3817 rect
.right
= coords_offs
->x
;
3821 rect
.left
= coords_offs
->x
;
3822 rect
.right
= infoPtr
->marqueeOrigin
.x
;
3825 if (coords_offs
->y
> infoPtr
->marqueeOrigin
.y
)
3827 rect
.top
= infoPtr
->marqueeOrigin
.y
;
3828 rect
.bottom
= coords_offs
->y
;
3832 rect
.top
= coords_offs
->y
;
3833 rect
.bottom
= infoPtr
->marqueeOrigin
.y
;
3836 /* Cancel out the old marquee rectangle and draw the new one */
3837 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
3839 /* Scroll by the appropriate distance if applicable - speed up scrolling as
3840 the cursor is further away */
3842 if ((scroll
& SCROLL_LEFT
) && (coords_orig
->x
<= 0))
3843 LISTVIEW_Scroll(infoPtr
, coords_orig
->x
, 0);
3845 if ((scroll
& SCROLL_RIGHT
) && (coords_orig
->x
>= infoPtr
->rcList
.right
))
3846 LISTVIEW_Scroll(infoPtr
, (coords_orig
->x
- infoPtr
->rcList
.right
), 0);
3848 if ((scroll
& SCROLL_UP
) && (coords_orig
->y
<= 0))
3849 LISTVIEW_Scroll(infoPtr
, 0, coords_orig
->y
);
3851 if ((scroll
& SCROLL_DOWN
) && (coords_orig
->y
>= infoPtr
->rcList
.bottom
))
3852 LISTVIEW_Scroll(infoPtr
, 0, (coords_orig
->y
- infoPtr
->rcList
.bottom
));
3854 iterator_frameditems_absolute(&old_elems
, infoPtr
, &infoPtr
->marqueeRect
);
3856 CopyRect(&infoPtr
->marqueeRect
, &rect
);
3858 CopyRect(&infoPtr
->marqueeDrawRect
, &rect
);
3859 OffsetRect(&infoPtr
->marqueeDrawRect
, offset
->x
, offset
->y
);
3861 iterator_frameditems_absolute(&new_elems
, infoPtr
, &infoPtr
->marqueeRect
);
3862 iterator_remove_common_items(&old_elems
, &new_elems
);
3864 /* Iterate over no longer selected items */
3865 while (iterator_next(&old_elems
))
3867 if (old_elems
.nItem
> -1)
3869 if (LISTVIEW_GetItemState(infoPtr
, old_elems
.nItem
, LVIS_SELECTED
) == LVIS_SELECTED
)
3872 item
.state
= LVIS_SELECTED
;
3874 item
.stateMask
= LVIS_SELECTED
;
3876 LISTVIEW_SetItemState(infoPtr
, old_elems
.nItem
, &item
);
3879 iterator_destroy(&old_elems
);
3882 /* Iterate over newly selected items */
3883 if (GetKeyState(VK_CONTROL
) & 0x8000)
3886 while (iterator_next(&new_elems
))
3888 if (new_elems
.nItem
> -1)
3890 /* If CTRL is pressed, invert. If not, always select the item. */
3891 if ((controlDown
) && (LISTVIEW_GetItemState(infoPtr
, new_elems
.nItem
, LVIS_SELECTED
)))
3894 item
.state
= LVIS_SELECTED
;
3896 item
.stateMask
= LVIS_SELECTED
;
3898 LISTVIEW_SetItemState(infoPtr
, new_elems
.nItem
, &item
);
3901 iterator_destroy(&new_elems
);
3903 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
3908 * Called when we are in a marquee selection that involves scrolling the listview (ie,
3909 * the cursor is outside the bounds of the client area). This is a TIMERPROC.
3912 * [I] hwnd : Handle to the listview
3913 * [I] uMsg : WM_TIMER (ignored)
3914 * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3915 * [I] dwTimer : The elapsed time (ignored)
3920 static VOID CALLBACK
LISTVIEW_ScrollTimer(HWND hWnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
3922 LISTVIEW_INFO
*infoPtr
;
3923 SCROLLINFO scrollInfo
;
3929 infoPtr
= (LISTVIEW_INFO
*) idEvent
;
3934 /* Get the current cursor position and convert to client coordinates */
3935 GetCursorPos(&coords_orig
);
3936 ScreenToClient(hWnd
, &coords_orig
);
3938 /* Ensure coordinates are within client bounds */
3939 coords_offs
.x
= max(min(coords_orig
.x
, infoPtr
->rcList
.right
), 0);
3940 coords_offs
.y
= max(min(coords_orig
.y
, infoPtr
->rcList
.bottom
), 0);
3943 LISTVIEW_GetOrigin(infoPtr
, &offset
);
3945 /* Offset coordinates by the appropriate amount */
3946 coords_offs
.x
-= offset
.x
;
3947 coords_offs
.y
-= offset
.y
;
3949 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3950 scrollInfo
.fMask
= SIF_ALL
;
3952 /* Work out in which directions we can scroll */
3953 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3955 if (scrollInfo
.nPos
!= scrollInfo
.nMin
)
3956 scroll
|= SCROLL_UP
;
3958 if (((scrollInfo
.nPage
+ scrollInfo
.nPos
) - 1) != scrollInfo
.nMax
)
3959 scroll
|= SCROLL_DOWN
;
3962 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3964 if (scrollInfo
.nPos
!= scrollInfo
.nMin
)
3965 scroll
|= SCROLL_LEFT
;
3967 if (((scrollInfo
.nPage
+ scrollInfo
.nPos
) - 1) != scrollInfo
.nMax
)
3968 scroll
|= SCROLL_RIGHT
;
3971 if (((coords_orig
.x
<= 0) && (scroll
& SCROLL_LEFT
)) ||
3972 ((coords_orig
.y
<= 0) && (scroll
& SCROLL_UP
)) ||
3973 ((coords_orig
.x
>= infoPtr
->rcList
.right
) && (scroll
& SCROLL_RIGHT
)) ||
3974 ((coords_orig
.y
>= infoPtr
->rcList
.bottom
) && (scroll
& SCROLL_DOWN
)))
3976 LISTVIEW_MarqueeHighlight(infoPtr
, &coords_orig
, &coords_offs
, &offset
, scroll
);
3982 * Called whenever WM_MOUSEMOVE is received.
3985 * [I] infoPtr : valid pointer to the listview structure
3986 * [I] fwKeys : key indicator
3987 * [I] x,y : mouse position
3990 * 0 if the message is processed, non-zero if there was an error
3992 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3998 if (!(fwKeys
& MK_LBUTTON
))
3999 infoPtr
->bLButtonDown
= FALSE
;
4001 if (infoPtr
->bLButtonDown
)
4003 rect
.left
= rect
.right
= infoPtr
->ptClickPos
.x
;
4004 rect
.top
= rect
.bottom
= infoPtr
->ptClickPos
.y
;
4006 InflateRect(&rect
, GetSystemMetrics(SM_CXDRAG
), GetSystemMetrics(SM_CYDRAG
));
4008 if (infoPtr
->bMarqueeSelect
)
4018 LISTVIEW_GetOrigin(infoPtr
, &offset
);
4020 /* Ensure coordinates are within client bounds */
4021 coords_offs
.x
= max(min(x
, infoPtr
->rcList
.right
), 0);
4022 coords_offs
.y
= max(min(y
, infoPtr
->rcList
.bottom
), 0);
4024 /* Offset coordinates by the appropriate amount */
4025 coords_offs
.x
-= offset
.x
;
4026 coords_offs
.y
-= offset
.y
;
4028 /* Enable the timer if we're going outside our bounds, in case the user doesn't
4029 move the mouse again */
4031 if ((x
<= 0) || (y
<= 0) || (x
>= infoPtr
->rcList
.right
) ||
4032 (y
>= infoPtr
->rcList
.bottom
))
4034 if (!infoPtr
->bScrolling
)
4036 infoPtr
->bScrolling
= TRUE
;
4037 SetTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
, 1, LISTVIEW_ScrollTimer
);
4042 infoPtr
->bScrolling
= FALSE
;
4043 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
4046 LISTVIEW_MarqueeHighlight(infoPtr
, &coords_orig
, &coords_offs
, &offset
, 0);
4054 LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, TRUE
);
4056 /* reset item marker */
4057 if (infoPtr
->nLButtonDownItem
!= ht
.iItem
)
4058 infoPtr
->nLButtonDownItem
= -1;
4060 if (!PtInRect(&rect
, pt
))
4062 /* this path covers the following:
4063 1. WM_LBUTTONDOWN over selected item (sets focus on it)
4064 2. change focus with keys
4065 3. move mouse over item from step 1 selects it and moves focus on it */
4066 if (infoPtr
->nLButtonDownItem
!= -1 &&
4067 !LISTVIEW_GetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, LVIS_SELECTED
))
4071 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
4072 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
4074 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, &lvItem
);
4075 infoPtr
->nLButtonDownItem
= -1;
4078 if (!infoPtr
->bDragging
)
4080 ht
.pt
= infoPtr
->ptClickPos
;
4081 LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, TRUE
);
4083 /* If the click is outside the range of an item, begin a
4084 highlight. If not, begin an item drag. */
4089 /* If we're allowing multiple selections, send notification.
4090 If return value is non-zero, cancel. */
4091 if (!(infoPtr
->dwStyle
& LVS_SINGLESEL
) && (notify_hdr(infoPtr
, LVN_MARQUEEBEGIN
, &hdr
) == 0))
4093 /* Store the absolute coordinates of the click */
4095 LISTVIEW_GetOrigin(infoPtr
, &offset
);
4097 infoPtr
->marqueeOrigin
.x
= infoPtr
->ptClickPos
.x
- offset
.x
;
4098 infoPtr
->marqueeOrigin
.y
= infoPtr
->ptClickPos
.y
- offset
.y
;
4100 /* Begin selection and capture mouse */
4101 infoPtr
->bMarqueeSelect
= TRUE
;
4102 SetCapture(infoPtr
->hwndSelf
);
4109 ZeroMemory(&nmlv
, sizeof(nmlv
));
4110 nmlv
.iItem
= ht
.iItem
;
4111 nmlv
.ptAction
= infoPtr
->ptClickPos
;
4113 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
4114 infoPtr
->bDragging
= TRUE
;
4122 /* see if we are supposed to be tracking mouse hovering */
4123 if (LISTVIEW_IsHotTracking(infoPtr
)) {
4124 TRACKMOUSEEVENT trackinfo
;
4127 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
4128 trackinfo
.dwFlags
= TME_QUERY
;
4130 /* see if we are already tracking this hwnd */
4131 _TrackMouseEvent(&trackinfo
);
4134 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)
4137 if((trackinfo
.dwFlags
& flags
) != flags
|| trackinfo
.hwndTrack
!= infoPtr
->hwndSelf
) {
4138 trackinfo
.dwFlags
= flags
;
4139 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
4140 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
4142 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
4143 _TrackMouseEvent(&trackinfo
);
4152 * Tests whether the item is assignable to a list with style lStyle
4154 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
4156 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
4157 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
4158 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
4166 * Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes.
4169 * [I] infoPtr : valid pointer to the listview structure
4170 * [I] lpLVItem : valid pointer to new item attributes
4171 * [I] isNew : the item being set is being inserted
4172 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4173 * [O] bChanged : will be set to TRUE if the item really changed
4179 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
4185 /* stateMask is ignored for LVM_INSERTITEM */
4186 UINT stateMask
= isNew
? ~0 : lpLVItem
->stateMask
;
4190 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
4192 if (lpLVItem
->mask
== 0) return TRUE
;
4194 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4196 /* a virtual listview only stores selection and focus */
4197 if (lpLVItem
->mask
& ~LVIF_STATE
)
4203 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4204 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
4208 /* we need to get the lParam and state of the item */
4209 item
.iItem
= lpLVItem
->iItem
;
4210 item
.iSubItem
= lpLVItem
->iSubItem
;
4211 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
4212 item
.stateMask
= (infoPtr
->dwStyle
& LVS_OWNERDATA
) ? LVIS_FOCUSED
| LVIS_SELECTED
: ~0;
4216 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
4218 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
4219 /* determine what fields will change */
4220 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & stateMask
& ~infoPtr
->uCallbackMask
))
4221 uChanged
|= LVIF_STATE
;
4223 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
4224 uChanged
|= LVIF_IMAGE
;
4226 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
4227 uChanged
|= LVIF_PARAM
;
4229 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
4230 uChanged
|= LVIF_INDENT
;
4232 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
4233 uChanged
|= LVIF_TEXT
;
4235 TRACE("change mask=0x%x\n", uChanged
);
4237 memset(&nmlv
, 0, sizeof(NMLISTVIEW
));
4238 nmlv
.iItem
= lpLVItem
->iItem
;
4239 nmlv
.uNewState
= (item
.state
& ~stateMask
) | (lpLVItem
->state
& stateMask
);
4240 nmlv
.uOldState
= item
.state
;
4241 nmlv
.uChanged
= uChanged
? uChanged
: lpLVItem
->mask
;
4242 nmlv
.lParam
= item
.lParam
;
4244 /* Send LVN_ITEMCHANGING notification, if the item is not being inserted
4245 and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications
4246 are enabled. Even nothing really changed we still need to send this,
4247 in this case uChanged mask is just set to passed item mask. */
4248 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
4250 HWND hwndSelf
= infoPtr
->hwndSelf
;
4252 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
4254 if (!IsWindow(hwndSelf
))
4258 /* When item is inserted we need to shift existing focus index if new item has lower index. */
4259 if (isNew
&& (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
) &&
4260 /* this means we won't hit a focus change path later */
4261 ((uChanged
& LVIF_STATE
) == 0 || (!(lpLVItem
->state
& LVIS_FOCUSED
) && (infoPtr
->nFocusedItem
!= lpLVItem
->iItem
))))
4263 if (infoPtr
->nFocusedItem
!= -1 && (lpLVItem
->iItem
<= infoPtr
->nFocusedItem
))
4264 infoPtr
->nFocusedItem
++;
4267 if (!uChanged
) return TRUE
;
4270 /* copy information */
4271 if (lpLVItem
->mask
& LVIF_TEXT
)
4272 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
4274 if (lpLVItem
->mask
& LVIF_IMAGE
)
4275 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
4277 if (lpLVItem
->mask
& LVIF_PARAM
)
4278 lpItem
->lParam
= lpLVItem
->lParam
;
4280 if (lpLVItem
->mask
& LVIF_INDENT
)
4281 lpItem
->iIndent
= lpLVItem
->iIndent
;
4283 if (uChanged
& LVIF_STATE
)
4285 if (lpItem
&& (stateMask
& ~infoPtr
->uCallbackMask
))
4287 lpItem
->state
&= ~stateMask
;
4288 lpItem
->state
|= (lpLVItem
->state
& stateMask
);
4290 if (lpLVItem
->state
& stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
4292 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
4293 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
4295 else if (stateMask
& LVIS_SELECTED
)
4297 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
4299 /* If we are asked to change focus, and we manage it, do it.
4300 It's important to have all new item data stored at this point,
4301 because changing existing focus could result in a redrawing operation,
4302 which in turn could ask for disp data, application should see all data
4303 for inserted item when processing LVN_GETDISPINFO.
4305 The way this works application will see nested item change notifications -
4306 changed item notifications interrupted by ones from item losing focus. */
4307 if (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
4309 if (lpLVItem
->state
& LVIS_FOCUSED
)
4311 /* update selection mark */
4312 if (infoPtr
->nFocusedItem
== -1 && infoPtr
->nSelectionMark
== -1)
4313 infoPtr
->nSelectionMark
= lpLVItem
->iItem
;
4315 if (infoPtr
->nFocusedItem
!= -1)
4317 /* remove current focus */
4318 item
.mask
= LVIF_STATE
;
4320 item
.stateMask
= LVIS_FOCUSED
;
4322 /* recurse with redrawing an item */
4323 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nFocusedItem
, &item
);
4326 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
4327 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, infoPtr
->uView
== LV_VIEW_LIST
);
4329 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
4331 infoPtr
->nFocusedItem
= -1;
4336 /* if we're inserting the item, we're done */
4337 if (isNew
) return TRUE
;
4339 /* send LVN_ITEMCHANGED notification */
4340 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
4341 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
4348 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4351 * [I] infoPtr : valid pointer to the listview structure
4352 * [I] lpLVItem : valid pointer to new subitem attributes
4353 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4354 * [O] bChanged : will be set to TRUE if the item really changed
4360 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
4363 SUBITEM_INFO
*lpSubItem
;
4365 /* we do not support subitems for virtual listviews */
4366 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
4368 /* set subitem only if column is present */
4369 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4371 /* First do some sanity checks */
4372 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
4373 particularly useful. We currently do not actually do anything with
4374 the flag on subitems.
4376 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
| LVIF_DI_SETITEM
)) return FALSE
;
4377 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
4379 /* get the subitem structure, and create it if not there */
4380 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
4381 assert (hdpaSubItems
);
4383 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
4386 SUBITEM_INFO
*tmpSubItem
;
4389 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
4390 if (!lpSubItem
) return FALSE
;
4391 /* we could binary search here, if need be...*/
4392 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4394 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4395 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
4397 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
4402 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
4403 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
4407 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
))
4409 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
4413 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
4415 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
4424 * Sets item attributes.
4427 * [I] infoPtr : valid pointer to the listview structure
4428 * [I] lpLVItem : new item attributes
4429 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4435 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
4437 HWND hwndSelf
= infoPtr
->hwndSelf
;
4438 LPWSTR pszText
= NULL
;
4439 BOOL bResult
, bChanged
= FALSE
;
4442 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
4444 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
4447 /* Store old item area */
4448 LISTVIEW_GetItemBox(infoPtr
, lpLVItem
->iItem
, &oldItemArea
);
4450 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4451 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_text(lpLVItem
->pszText
))
4453 pszText
= lpLVItem
->pszText
;
4454 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
4457 /* actually set the fields */
4458 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
4460 if (lpLVItem
->iSubItem
)
4461 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
4463 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
4464 if (!IsWindow(hwndSelf
))
4467 /* redraw item, if necessary */
4468 if (bChanged
&& !infoPtr
->bIsDrawing
)
4470 /* this little optimization eliminates some nasty flicker */
4471 if ( infoPtr
->uView
== LV_VIEW_DETAILS
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
4472 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
4473 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
4474 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
4477 LISTVIEW_InvalidateRect(infoPtr
, &oldItemArea
);
4478 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
4484 textfreeT(lpLVItem
->pszText
, isW
);
4485 lpLVItem
->pszText
= pszText
;
4493 * Retrieves the index of the item at coordinate (0, 0) of the client area.
4496 * [I] infoPtr : valid pointer to the listview structure
4501 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
4504 SCROLLINFO scrollInfo
;
4506 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
4507 scrollInfo
.fMask
= SIF_POS
;
4509 if (infoPtr
->uView
== LV_VIEW_LIST
)
4511 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
4512 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
4514 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4516 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4517 nItem
= scrollInfo
.nPos
;
4521 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
4522 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
4525 TRACE("nItem=%d\n", nItem
);
4533 * Erases the background of the given rectangle
4536 * [I] infoPtr : valid pointer to the listview structure
4537 * [I] hdc : device context handle
4538 * [I] lprcBox : clipping rectangle
4544 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
4546 if (!infoPtr
->hBkBrush
) return FALSE
;
4548 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
4550 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
4553 /* Draw main item or subitem */
4554 static void LISTVIEW_DrawItemPart(LISTVIEW_INFO
*infoPtr
, LVITEMW
*item
, const NMLVCUSTOMDRAW
*nmlvcd
, const POINT
*pos
)
4556 RECT rcSelect
, rcLabel
, rcBox
, rcStateIcon
, rcIcon
;
4557 const RECT
*background
;
4562 /* now check if we need to update the focus rectangle */
4563 focus
= infoPtr
->bFocus
&& (item
->state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
4564 if (!focus
) item
->state
&= ~LVIS_FOCUSED
;
4566 LISTVIEW_GetItemMetrics(infoPtr
, item
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
4567 OffsetRect(&rcBox
, pos
->x
, pos
->y
);
4568 OffsetRect(&rcSelect
, pos
->x
, pos
->y
);
4569 OffsetRect(&rcIcon
, pos
->x
, pos
->y
);
4570 OffsetRect(&rcStateIcon
, pos
->x
, pos
->y
);
4571 OffsetRect(&rcLabel
, pos
->x
, pos
->y
);
4572 TRACE("%d: box=%s, select=%s, icon=%s. label=%s\n", item
->iSubItem
,
4573 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
4574 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
4576 /* FIXME: temporary hack */
4577 rcSelect
.left
= rcLabel
.left
;
4579 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& item
->iSubItem
== 0)
4581 if (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
4582 OffsetRect(&rcSelect
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4583 OffsetRect(&rcIcon
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4584 OffsetRect(&rcStateIcon
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4585 OffsetRect(&rcLabel
, LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
, 0);
4588 /* in icon mode, the label rect is really what we want to draw the
4590 /* in detail mode, we want to paint background for label rect when
4591 * item is not selected or listview has full row select; otherwise paint
4592 * background for text only */
4593 if ( infoPtr
->uView
== LV_VIEW_ICON
||
4594 (infoPtr
->uView
== LV_VIEW_DETAILS
&& (!(item
->state
& LVIS_SELECTED
) ||
4595 (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))))
4596 background
= &rcLabel
;
4598 background
= &rcSelect
;
4600 if (nmlvcd
->clrTextBk
!= CLR_NONE
)
4601 ExtTextOutW(nmlvcd
->nmcd
.hdc
, background
->left
, background
->top
, ETO_OPAQUE
, background
, NULL
, 0, NULL
);
4603 if (item
->state
& LVIS_FOCUSED
)
4605 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4607 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4609 /* we have to update left focus bound too if item isn't in leftmost column
4610 and reduce right box bound */
4611 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
4615 if ((leftmost
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, 0, 0)))
4617 INT Originx
= pos
->x
- LISTVIEW_GetColumnInfo(infoPtr
, leftmost
)->rcHeader
.left
;
4618 INT rightmost
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
4619 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
4621 rcBox
.right
= LISTVIEW_GetColumnInfo(infoPtr
, rightmost
)->rcHeader
.right
+ Originx
;
4622 rcSelect
.left
= LISTVIEW_GetColumnInfo(infoPtr
, leftmost
)->rcHeader
.left
+ Originx
;
4625 rcSelect
.right
= rcBox
.right
;
4627 infoPtr
->rcFocus
= rcSelect
;
4630 infoPtr
->rcFocus
= rcLabel
;
4634 if (infoPtr
->himlState
&& STATEIMAGEINDEX(item
->state
) && (item
->iSubItem
== 0))
4636 UINT stateimage
= STATEIMAGEINDEX(item
->state
);
4639 TRACE("stateimage=%d\n", stateimage
);
4640 ImageList_Draw(infoPtr
->himlState
, stateimage
-1, nmlvcd
->nmcd
.hdc
, rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
4645 himl
= (infoPtr
->uView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
4646 if (himl
&& item
->iImage
>= 0 && !IsRectEmpty(&rcIcon
))
4650 TRACE("iImage=%d\n", item
->iImage
);
4652 if (item
->state
& (LVIS_SELECTED
| LVIS_CUT
) && infoPtr
->bFocus
)
4653 style
= ILD_SELECTED
;
4657 ImageList_DrawEx(himl
, item
->iImage
, nmlvcd
->nmcd
.hdc
, rcIcon
.left
, rcIcon
.top
,
4658 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
,
4659 item
->state
& LVIS_CUT
? RGB(255, 255, 255) : CLR_DEFAULT
,
4660 style
| (item
->state
& LVIS_OVERLAYMASK
));
4663 /* Don't bother painting item being edited */
4664 if (infoPtr
->hwndEdit
&& item
->iItem
== infoPtr
->nEditLabelItem
&& item
->iSubItem
== 0) return;
4666 /* figure out the text drawing flags */
4667 format
= (infoPtr
->uView
== LV_VIEW_ICON
? (focus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
4668 if (infoPtr
->uView
== LV_VIEW_ICON
)
4669 format
= (focus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
4670 else if (item
->iSubItem
)
4672 switch (LISTVIEW_GetColumnInfo(infoPtr
, item
->iSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
4674 case LVCFMT_RIGHT
: format
|= DT_RIGHT
; break;
4675 case LVCFMT_CENTER
: format
|= DT_CENTER
; break;
4676 default: format
|= DT_LEFT
;
4679 if (!(format
& (DT_RIGHT
| DT_CENTER
)))
4681 if (himl
&& item
->iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
4682 else rcLabel
.left
+= LABEL_HOR_PADDING
;
4684 else if (format
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
4686 /* for GRIDLINES reduce the bottom so the text formats correctly */
4687 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4691 if ((!(item
->state
& LVIS_SELECTED
) || !infoPtr
->bFocus
) && (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTSHADOWTEXT
))
4692 DrawShadowText(nmlvcd
->nmcd
.hdc
, item
->pszText
, -1, &rcLabel
, format
, RGB(255, 255, 255), RGB(0, 0, 0), 2, 2);
4695 DrawTextW(nmlvcd
->nmcd
.hdc
, item
->pszText
, -1, &rcLabel
, format
);
4703 * [I] infoPtr : valid pointer to the listview structure
4704 * [I] hdc : device context handle
4705 * [I] nItem : item index
4706 * [I] nSubItem : subitem index
4707 * [I] pos : item position in client coordinates
4708 * [I] cdmode : custom draw mode
4714 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, ITERATOR
*subitems
, POINT pos
, DWORD cdmode
)
4716 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
4717 static WCHAR callbackW
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4718 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
4720 NMLVCUSTOMDRAW nmlvcd
;
4723 TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc
, nItem
, subitems
, wine_dbgstr_point(&pos
));
4725 /* get information needed for drawing the item */
4726 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
| LVIF_STATE
;
4727 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
4728 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
| LVIS_CUT
| LVIS_OVERLAYMASK
;
4729 lvItem
.iItem
= nItem
;
4730 lvItem
.iSubItem
= 0;
4733 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4734 lvItem
.pszText
= szDispText
;
4735 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
4736 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= callbackW
;
4737 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
4739 /* now check if we need to update the focus rectangle */
4740 focus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
4741 if (!focus
) lvItem
.state
&= ~LVIS_FOCUSED
;
4743 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, NULL
, NULL
, NULL
);
4744 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
4746 /* Full custom draw stage sequence looks like this:
4751 - CDDS_ITEMPREPAINT|CDDS_SUBITEM | => sent n times, where n is number of subitems,
4752 CDDS_ITEMPOSTPAINT|CDDS_SUBITEM | including item iself
4753 - CDDS_ITEMPOSTPAINT
4758 - CDDS_ITEMPOSTPAINT
4761 /* fill in the custom draw structure */
4762 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
4763 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4764 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_ITEMPREPAINT
, &nmlvcd
);
4765 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
4769 while (iterator_next(subitems
))
4771 DWORD subitemstage
= CDRF_DODEFAULT
;
4773 /* We need to query for each subitem, item's data (subitem == 0) is already here at this point */
4774 if (subitems
->nItem
)
4776 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
| LVIF_INDENT
;
4777 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
| LVIS_CUT
| LVIS_OVERLAYMASK
;
4778 lvItem
.iItem
= nItem
;
4779 lvItem
.iSubItem
= subitems
->nItem
;
4782 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
4783 lvItem
.pszText
= szDispText
;
4784 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
4785 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4786 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
4787 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= callbackW
;
4788 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
4790 /* update custom draw data */
4791 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &nmlvcd
.nmcd
.rc
, NULL
, NULL
, NULL
, NULL
);
4792 OffsetRect(&nmlvcd
.nmcd
.rc
, pos
.x
, pos
.y
);
4793 nmlvcd
.iSubItem
= subitems
->nItem
;
4796 if (cdsubitemmode
& CDRF_NOTIFYSUBITEMDRAW
)
4797 subitemstage
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
4800 nmlvcd
.clrTextBk
= infoPtr
->clrTextBk
;
4801 nmlvcd
.clrText
= infoPtr
->clrText
;
4804 if (subitems
->nItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
4805 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4806 else if (!(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
4807 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
4809 if (!(subitemstage
& CDRF_SKIPDEFAULT
))
4810 LISTVIEW_DrawItemPart(infoPtr
, &lvItem
, &nmlvcd
, &pos
);
4812 if (subitemstage
& CDRF_NOTIFYPOSTPAINT
)
4813 subitemstage
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPOSTPAINT
, &nmlvcd
);
4818 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4819 LISTVIEW_DrawItemPart(infoPtr
, &lvItem
, &nmlvcd
, &pos
);
4823 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
4825 nmlvcd
.iSubItem
= 0;
4826 notify_customdraw(infoPtr
, CDDS_ITEMPOSTPAINT
, &nmlvcd
);
4834 * Draws listview items when in owner draw mode.
4837 * [I] infoPtr : valid pointer to the listview structure
4838 * [I] hdc : device context handle
4843 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4845 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
4846 DWORD cditemmode
= CDRF_DODEFAULT
;
4847 NMLVCUSTOMDRAW nmlvcd
;
4848 POINT Origin
, Position
;
4854 ZeroMemory(&dis
, sizeof(dis
));
4856 /* Get scroll info once before loop */
4857 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4859 /* iterate through the invalidated rows */
4860 while(iterator_next(i
))
4862 item
.iItem
= i
->nItem
;
4864 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4865 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4866 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
4868 dis
.CtlType
= ODT_LISTVIEW
;
4870 dis
.itemID
= item
.iItem
;
4871 dis
.itemAction
= ODA_DRAWENTIRE
;
4873 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
4874 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
4875 dis
.hwndItem
= infoPtr
->hwndSelf
;
4877 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
4878 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
4879 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
4880 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
4881 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
4882 dis
.itemData
= item
.lParam
;
4884 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
4887 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4888 * structure for the rest. of the paint cycle
4890 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4891 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4892 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4894 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4896 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4897 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4900 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4901 notify_postpaint(infoPtr
, &nmlvcd
);
4907 * Draws listview items when in report display mode.
4910 * [I] infoPtr : valid pointer to the listview structure
4911 * [I] hdc : device context handle
4912 * [I] cdmode : custom draw mode
4917 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4920 RECT rcClip
, rcItem
;
4928 /* figure out what to draw */
4929 rgntype
= GetClipBox(hdc
, &rcClip
);
4930 if (rgntype
== NULLREGION
) return;
4932 /* Get scroll info once before loop */
4933 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4935 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4937 /* narrow down the columns we need to paint */
4938 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
4940 INT index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
4942 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4943 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
4944 ranges_additem(colRanges
, index
);
4946 iterator_rangesitems(&j
, colRanges
);
4948 /* in full row select, we _have_ to draw the main item */
4949 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4952 /* iterate through the invalidated rows */
4953 while(iterator_next(i
))
4959 SelectObject(hdc
, infoPtr
->hFont
);
4960 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4961 Position
.x
= Origin
.x
;
4962 Position
.y
+= Origin
.y
;
4964 subitems
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4966 /* iterate through the invalidated columns */
4967 while(iterator_next(&j
))
4969 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4971 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4974 rcItem
.bottom
= infoPtr
->nItemHeight
;
4975 OffsetRect(&rcItem
, Origin
.x
, Position
.y
);
4976 if (!RectVisible(hdc
, &rcItem
)) continue;
4979 ranges_additem(subitems
, j
.nItem
);
4982 iterator_rangesitems(&k
, subitems
);
4983 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, &k
, Position
, cdmode
);
4984 iterator_destroy(&k
);
4986 iterator_destroy(&j
);
4991 * Draws the gridlines if necessary when in report display mode.
4994 * [I] infoPtr : valid pointer to the listview structure
4995 * [I] hdc : device context handle
5000 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
5006 RECT rcClip
, rcItem
= {0};
5014 /* figure out what to draw */
5015 rgntype
= GetClipBox(hdc
, &rcClip
);
5016 if (rgntype
== NULLREGION
) return;
5018 /* Get scroll info once before loop */
5019 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5021 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
5023 /* narrow down the columns we need to paint */
5024 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
5026 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
5028 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
5029 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
5030 ranges_additem(colRanges
, index
);
5033 /* is right most vertical line visible? */
5034 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
5036 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
5037 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
5038 rmost
= (rcItem
.right
+ Origin
.x
< rcClip
.right
);
5041 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
5043 hOldPen
= SelectObject ( hdc
, hPen
);
5045 /* draw the vertical lines for the columns */
5046 iterator_rangesitems(&j
, colRanges
);
5047 while(iterator_next(&j
))
5049 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
5050 if (rcItem
.left
== 0) continue; /* skip leftmost column */
5051 rcItem
.left
+= Origin
.x
;
5052 rcItem
.right
+= Origin
.x
;
5053 rcItem
.top
= infoPtr
->rcList
.top
;
5054 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
5055 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
5056 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
5057 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
5059 iterator_destroy(&j
);
5060 /* draw rightmost grid line if visible */
5063 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
5064 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
5065 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
5067 rcItem
.right
+= Origin
.x
;
5069 MoveToEx (hdc
, rcItem
.right
, infoPtr
->rcList
.top
, NULL
);
5070 LineTo (hdc
, rcItem
.right
, infoPtr
->rcList
.bottom
);
5073 /* draw the horizontal lines for the rows */
5074 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
5075 rcItem
.left
= infoPtr
->rcList
.left
;
5076 rcItem
.right
= infoPtr
->rcList
.right
;
5077 for(y
= Origin
.y
> 1 ? Origin
.y
- 1 : itemheight
- 1 + Origin
.y
% itemheight
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
5079 rcItem
.bottom
= rcItem
.top
= y
;
5080 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
5081 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
5082 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
5085 SelectObject( hdc
, hOldPen
);
5086 DeleteObject( hPen
);
5089 ranges_destroy(colRanges
);
5094 * Draws listview items when in list display mode.
5097 * [I] infoPtr : valid pointer to the listview structure
5098 * [I] hdc : device context handle
5099 * [I] cdmode : custom draw mode
5104 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
5106 POINT Origin
, Position
;
5108 /* Get scroll info once before loop */
5109 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5111 while(iterator_prev(i
))
5113 SelectObject(hdc
, infoPtr
->hFont
);
5114 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
5115 Position
.x
+= Origin
.x
;
5116 Position
.y
+= Origin
.y
;
5118 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, NULL
, Position
, cdmode
);
5125 * Draws listview items.
5128 * [I] infoPtr : valid pointer to the listview structure
5129 * [I] hdc : device context handle
5130 * [I] prcErase : rect to be erased before refresh (may be NULL)
5135 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
5137 COLORREF oldTextColor
= 0, oldBkColor
= 0;
5138 NMLVCUSTOMDRAW nmlvcd
;
5145 HBITMAP hbmp
= NULL
;
5148 LISTVIEW_DUMP(infoPtr
);
5150 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
5151 TRACE("double buffering\n");
5153 hdc
= CreateCompatibleDC(hdcOrig
);
5155 ERR("Failed to create DC for backbuffer\n");
5158 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
5159 infoPtr
->rcList
.bottom
);
5161 ERR("Failed to create bitmap for backbuffer\n");
5166 SelectObject(hdc
, hbmp
);
5167 SelectObject(hdc
, infoPtr
->hFont
);
5169 if(GetClipBox(hdcOrig
, &rcClient
))
5170 IntersectClipRect(hdc
, rcClient
.left
, rcClient
.top
, rcClient
.right
, rcClient
.bottom
);
5172 /* Save dc values we're gonna trash while drawing
5173 * FIXME: Should be done in LISTVIEW_DrawItem() */
5174 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
5175 oldBkMode
= GetBkMode(hdc
);
5176 oldBkColor
= GetBkColor(hdc
);
5177 oldTextColor
= GetTextColor(hdc
);
5180 infoPtr
->bIsDrawing
= TRUE
;
5183 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
5184 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
5185 /* If no erasing was done (usually because RedrawWindow was called
5186 * with RDW_INVALIDATE only) we need to copy the old contents into
5187 * the backbuffer before continuing. */
5188 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
5189 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
5190 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
5191 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
5194 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
5195 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
5196 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
5197 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
5199 /* nothing to draw */
5200 if(infoPtr
->nItemCount
== 0) goto enddraw
;
5202 /* figure out what we need to draw */
5203 iterator_visibleitems(&i
, infoPtr
, hdc
);
5204 range
= iterator_range(&i
);
5206 /* send cache hint notification */
5207 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5211 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
5212 nmlv
.iFrom
= range
.lower
;
5213 nmlv
.iTo
= range
.upper
- 1;
5214 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
5217 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
5218 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
5221 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5222 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
5223 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
5224 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
5226 /* if we have a focus rect and it's visible, draw it */
5227 if (infoPtr
->bFocus
&& range
.lower
<= infoPtr
->nFocusedItem
&&
5228 (range
.upper
- 1) >= infoPtr
->nFocusedItem
)
5229 LISTVIEW_DrawFocusRect(infoPtr
, hdc
);
5231 iterator_destroy(&i
);
5234 /* For LVS_EX_GRIDLINES go and draw lines */
5235 /* This includes the case where there were *no* items */
5236 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
5237 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
5239 /* Draw marquee rectangle if appropriate */
5240 if (infoPtr
->bMarqueeSelect
)
5242 SetBkColor(hdc
, RGB(255, 255, 255));
5243 SetTextColor(hdc
, RGB(0, 0, 0));
5244 DrawFocusRect(hdc
, &infoPtr
->marqueeDrawRect
);
5247 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
5248 notify_postpaint(infoPtr
, &nmlvcd
);
5251 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
5252 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
5253 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
5254 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
5259 SelectObject(hdc
, hOldFont
);
5260 SetBkMode(hdc
, oldBkMode
);
5261 SetBkColor(hdc
, oldBkColor
);
5262 SetTextColor(hdc
, oldTextColor
);
5265 infoPtr
->bIsDrawing
= FALSE
;
5271 * Calculates the approximate width and height of a given number of items.
5274 * [I] infoPtr : valid pointer to the listview structure
5275 * [I] nItemCount : number of items
5276 * [I] wWidth : width
5277 * [I] wHeight : height
5280 * Returns a DWORD. The width in the low word and the height in high word.
5282 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
5283 WORD wWidth
, WORD wHeight
)
5285 DWORD dwViewRect
= 0;
5287 if (nItemCount
== -1)
5288 nItemCount
= infoPtr
->nItemCount
;
5290 if (infoPtr
->uView
== LV_VIEW_LIST
)
5292 INT nItemCountPerColumn
= 1;
5293 INT nColumnCount
= 0;
5295 if (wHeight
== 0xFFFF)
5297 /* use current height */
5298 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
5301 if (wHeight
< infoPtr
->nItemHeight
)
5302 wHeight
= infoPtr
->nItemHeight
;
5306 if (infoPtr
->nItemHeight
> 0)
5308 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
5309 if (nItemCountPerColumn
== 0)
5310 nItemCountPerColumn
= 1;
5312 if (nItemCount
% nItemCountPerColumn
!= 0)
5313 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
5315 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
5319 /* Microsoft padding magic */
5320 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
5321 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
5323 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5325 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5329 if (infoPtr
->nItemCount
> 0)
5331 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
5332 wWidth
= rcBox
.right
- rcBox
.left
;
5333 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
5337 /* use current height and width */
5338 if (wHeight
== 0xffff)
5339 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
5340 if (wWidth
== 0xffff)
5341 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
5344 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5346 else if (infoPtr
->uView
== LV_VIEW_ICON
)
5352 nItemWidth
= infoPtr
->iconSpacing
.cx
;
5353 nItemHeight
= infoPtr
->iconSpacing
.cy
;
5355 if (wWidth
== 0xffff)
5356 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
5358 if (wWidth
< nItemWidth
)
5359 wWidth
= nItemWidth
;
5361 cols
= wWidth
/ nItemWidth
;
5362 if (cols
> nItemCount
)
5369 rows
= nItemCount
/ cols
;
5370 if (nItemCount
% cols
)
5376 wHeight
= (nItemHeight
* rows
)+2;
5377 wWidth
= (nItemWidth
* cols
)+2;
5379 dwViewRect
= MAKELONG(wWidth
, wHeight
);
5381 else if (infoPtr
->uView
== LV_VIEW_SMALLICON
)
5382 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5389 * Cancel edit label with saving item text.
5392 * [I] infoPtr : valid pointer to the listview structure
5395 * Always returns TRUE.
5397 static LRESULT
LISTVIEW_CancelEditLabel(LISTVIEW_INFO
*infoPtr
)
5399 if (infoPtr
->hwndEdit
)
5401 /* handle value will be lost after LISTVIEW_EndEditLabelT */
5402 HWND edit
= infoPtr
->hwndEdit
;
5404 LISTVIEW_EndEditLabelT(infoPtr
, TRUE
, IsWindowUnicode(infoPtr
->hwndEdit
));
5405 SendMessageW(edit
, WM_CLOSE
, 0, 0);
5413 * Create a drag image list for the specified item.
5416 * [I] infoPtr : valid pointer to the listview structure
5417 * [I] iItem : index of item
5418 * [O] lppt : Upper-left corner of the image
5421 * Returns a handle to the image list if successful, NULL otherwise.
5423 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
5429 HBITMAP hbmp
, hOldbmp
;
5431 HIMAGELIST dragList
= 0;
5432 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
5434 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
|| !lppt
)
5437 rcItem
.left
= LVIR_BOUNDS
;
5438 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
5441 lppt
->x
= rcItem
.left
;
5442 lppt
->y
= rcItem
.top
;
5444 size
.cx
= rcItem
.right
- rcItem
.left
;
5445 size
.cy
= rcItem
.bottom
- rcItem
.top
;
5447 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
5448 hdc
= CreateCompatibleDC(hdcOrig
);
5449 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
5450 hOldbmp
= SelectObject(hdc
, hbmp
);
5451 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
5453 rcItem
.left
= rcItem
.top
= 0;
5454 rcItem
.right
= size
.cx
;
5455 rcItem
.bottom
= size
.cy
;
5456 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
5459 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, NULL
, pos
, CDRF_DODEFAULT
))
5461 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
5462 SelectObject(hdc
, hOldbmp
);
5463 ImageList_Add(dragList
, hbmp
, 0);
5466 SelectObject(hdc
, hOldbmp
);
5468 SelectObject(hdc
, hOldFont
);
5471 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
5473 TRACE("ret=%p\n", dragList
);
5481 * Removes all listview items and subitems.
5484 * [I] infoPtr : valid pointer to the listview structure
5490 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
5492 HDPA hdpaSubItems
= NULL
;
5493 BOOL suppress
= FALSE
;
5501 /* we do it directly, to avoid notifications */
5502 ranges_clear(infoPtr
->selectionRanges
);
5503 infoPtr
->nSelectionMark
= -1;
5504 infoPtr
->nFocusedItem
= -1;
5505 SetRectEmpty(&infoPtr
->rcFocus
);
5506 /* But we are supposed to leave nHotItem as is! */
5508 /* send LVN_DELETEALLITEMS notification */
5509 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) || !destroy
)
5513 memset(&nmlv
, 0, sizeof(NMLISTVIEW
));
5515 suppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
5518 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
5520 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5522 /* send LVN_DELETEITEM notification, if not suppressed
5523 and if it is not a virtual listview */
5524 if (!suppress
) notify_deleteitem(infoPtr
, i
);
5525 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
5526 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5527 /* free id struct */
5528 j
= DPA_GetPtrIndex(infoPtr
->hdpaItemIds
, lpItem
->id
);
5529 lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, j
);
5530 DPA_DeletePtr(infoPtr
->hdpaItemIds
, j
);
5532 /* both item and subitem start with ITEMHDR header */
5533 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
5535 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
5536 if (is_text(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
5539 DPA_Destroy(hdpaSubItems
);
5540 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
5542 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
5543 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
5544 infoPtr
->nItemCount
--;
5549 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
5550 LISTVIEW_UpdateScroll(infoPtr
);
5552 LISTVIEW_InvalidateList(infoPtr
);
5559 * Scrolls, and updates the columns, when a column is changing width.
5562 * [I] infoPtr : valid pointer to the listview structure
5563 * [I] nColumn : column to scroll
5564 * [I] dx : amount of scroll, in pixels
5569 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
5571 COLUMN_INFO
*lpColumnInfo
;
5577 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
5578 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
5579 rcCol
= lpColumnInfo
->rcHeader
;
5580 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
5581 rcCol
.left
= rcCol
.right
;
5583 /* adjust the other columns */
5584 hdi
.mask
= HDI_ORDER
;
5585 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdi
))
5587 INT nOrder
= hdi
.iOrder
;
5588 for (nCol
= 0; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
5590 hdi
.mask
= HDI_ORDER
;
5591 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nCol
, (LPARAM
)&hdi
);
5592 if (hdi
.iOrder
>= nOrder
) {
5593 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
5594 lpColumnInfo
->rcHeader
.left
+= dx
;
5595 lpColumnInfo
->rcHeader
.right
+= dx
;
5600 /* do not update screen if not in report mode */
5601 if (!is_redrawing(infoPtr
) || infoPtr
->uView
!= LV_VIEW_DETAILS
) return;
5603 /* Need to reset the item width when inserting a new column */
5604 infoPtr
->nItemWidth
+= dx
;
5606 LISTVIEW_UpdateScroll(infoPtr
);
5607 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
5609 /* scroll to cover the deleted column, and invalidate for redraw */
5610 rcOld
= infoPtr
->rcList
;
5611 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
5612 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
5617 * Removes a column from the listview control.
5620 * [I] infoPtr : valid pointer to the listview structure
5621 * [I] nColumn : column index
5627 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5631 TRACE("nColumn=%d\n", nColumn
);
5633 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
5634 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5636 /* While the MSDN specifically says that column zero should not be deleted,
5637 what actually happens is that the column itself is deleted but no items or subitems
5641 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
5643 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nColumn
, 0))
5646 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
5647 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
5649 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
5651 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
5653 INT nItem
, nSubItem
, i
;
5655 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
5657 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
5660 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
5662 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
5663 if (lpSubItem
->iSubItem
== nColumn
)
5666 lpDelItem
= lpSubItem
;
5668 else if (lpSubItem
->iSubItem
> nColumn
)
5670 lpSubItem
->iSubItem
--;
5674 /* if we found our subitem, zap it */
5678 if (is_text(lpDelItem
->hdr
.pszText
))
5679 Free(lpDelItem
->hdr
.pszText
);
5684 /* free dpa memory */
5685 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
5690 /* update the other column info */
5691 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
5692 LISTVIEW_InvalidateList(infoPtr
);
5694 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
5695 LISTVIEW_UpdateItemSize(infoPtr
);
5702 * Invalidates the listview after an item's insertion or deletion.
5705 * [I] infoPtr : valid pointer to the listview structure
5706 * [I] nItem : item index
5707 * [I] dir : -1 if deleting, 1 if inserting
5712 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
5714 INT nPerCol
, nItemCol
, nItemRow
;
5718 /* if we don't refresh, what's the point of scrolling? */
5719 if (!is_redrawing(infoPtr
)) return;
5721 assert (abs(dir
) == 1);
5723 /* arrange icons if autoarrange is on */
5724 if (is_autoarrange(infoPtr
))
5726 BOOL arrange
= TRUE
;
5727 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
5728 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
5729 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
5732 /* scrollbars need updating */
5733 LISTVIEW_UpdateScroll(infoPtr
);
5735 /* figure out the item's position */
5736 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5737 nPerCol
= infoPtr
->nItemCount
+ 1;
5738 else if (infoPtr
->uView
== LV_VIEW_LIST
)
5739 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
5740 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5743 nItemCol
= nItem
/ nPerCol
;
5744 nItemRow
= nItem
% nPerCol
;
5745 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5747 /* move the items below up a slot */
5748 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
5749 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
5750 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
5751 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
5752 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
5753 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
5754 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
5756 TRACE("Invalidating rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
5757 InvalidateRect(infoPtr
->hwndSelf
, &rcScroll
, TRUE
);
5760 /* report has only that column, so we're done */
5761 if (infoPtr
->uView
== LV_VIEW_DETAILS
) return;
5763 /* now for LISTs, we have to deal with the columns to the right */
5764 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
5766 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
5767 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
5768 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
5769 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
5770 InvalidateRect(infoPtr
->hwndSelf
, &rcScroll
, TRUE
);
5775 * Removes an item from the listview control.
5778 * [I] infoPtr : valid pointer to the listview structure
5779 * [I] nItem : item index
5785 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
5788 const BOOL is_icon
= (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
);
5789 INT focus
= infoPtr
->nFocusedItem
;
5791 TRACE("(nItem=%d)\n", nItem
);
5793 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5795 /* remove selection, and focus */
5797 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
5798 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
5800 /* send LVN_DELETEITEM notification. */
5801 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
5803 /* we need to do this here, because we'll be deleting stuff */
5805 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
5807 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5815 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
5816 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5818 /* free id struct */
5819 i
= DPA_GetPtrIndex(infoPtr
->hdpaItemIds
, lpItem
->id
);
5820 lpID
= DPA_GetPtr(infoPtr
->hdpaItemIds
, i
);
5821 DPA_DeletePtr(infoPtr
->hdpaItemIds
, i
);
5823 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
5825 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
5826 if (is_text(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
5829 DPA_Destroy(hdpaSubItems
);
5834 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
5835 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
5838 infoPtr
->nItemCount
--;
5839 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
5840 LISTVIEW_ShiftFocus(infoPtr
, focus
, nItem
, -1);
5842 /* now is the invalidation fun */
5844 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
5851 * Callback implementation for editlabel control
5854 * [I] infoPtr : valid pointer to the listview structure
5855 * [I] storeText : store edit box text as item text
5856 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5862 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, BOOL storeText
, BOOL isW
)
5864 HWND hwndSelf
= infoPtr
->hwndSelf
;
5865 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
5866 NMLVDISPINFOW dispInfo
;
5867 INT editedItem
= infoPtr
->nEditLabelItem
;
5869 WCHAR
*pszText
= NULL
;
5874 DWORD len
= isW
? GetWindowTextLengthW(infoPtr
->hwndEdit
) : GetWindowTextLengthA(infoPtr
->hwndEdit
);
5878 if ((pszText
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))))
5880 if (isW
) GetWindowTextW(infoPtr
->hwndEdit
, pszText
, len
+1);
5881 else GetWindowTextA(infoPtr
->hwndEdit
, (CHAR
*)pszText
, len
+1);
5886 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
5888 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5889 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
5890 dispInfo
.item
.iItem
= editedItem
;
5891 dispInfo
.item
.iSubItem
= 0;
5892 dispInfo
.item
.stateMask
= ~0;
5893 dispInfo
.item
.pszText
= szDispText
;
5894 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
5895 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
))
5902 same
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
5905 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
5906 same
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
5907 textfreeT(tmp
, FALSE
);
5910 /* add the text from the edit in */
5911 dispInfo
.item
.mask
|= LVIF_TEXT
;
5912 dispInfo
.item
.pszText
= same
? NULL
: pszText
;
5913 dispInfo
.item
.cchTextMax
= textlenT(dispInfo
.item
.pszText
, isW
);
5915 /* Do we need to update the Item Text */
5916 res
= notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
);
5918 infoPtr
->nEditLabelItem
= -1;
5919 infoPtr
->hwndEdit
= 0;
5921 if (!res
) goto cleanup
;
5923 if (!IsWindow(hwndSelf
))
5928 if (!pszText
) return TRUE
;
5935 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5937 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
5938 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5939 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
5941 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
5947 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5948 dispInfo
.item
.mask
= LVIF_TEXT
;
5949 dispInfo
.item
.iItem
= editedItem
;
5950 dispInfo
.item
.iSubItem
= 0;
5951 dispInfo
.item
.pszText
= pszText
;
5952 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
5953 res
= LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
5963 * Subclassed edit control windproc function
5966 * [I] hwnd : the edit window handle
5967 * [I] uMsg : the message that is to be processed
5968 * [I] wParam : first message parameter
5969 * [I] lParam : second message parameter
5970 * [I] isW : TRUE if input is Unicode
5975 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
5977 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
5980 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
5981 hwnd
, uMsg
, wParam
, lParam
, isW
);
5986 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
5990 WNDPROC editProc
= infoPtr
->EditWndProc
;
5991 infoPtr
->EditWndProc
= 0;
5992 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
5993 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
5997 if (VK_ESCAPE
== (INT
)wParam
)
6002 else if (VK_RETURN
== (INT
)wParam
)
6006 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
6010 if (infoPtr
->hwndEdit
)
6011 LISTVIEW_EndEditLabelT(infoPtr
, save
, isW
);
6013 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
6019 * Subclassed edit control Unicode windproc function
6022 * [I] hwnd : the edit window handle
6023 * [I] uMsg : the message that is to be processed
6024 * [I] wParam : first message parameter
6025 * [I] lParam : second message parameter
6029 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
6031 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
6036 * Subclassed edit control ANSI windproc function
6039 * [I] hwnd : the edit window handle
6040 * [I] uMsg : the message that is to be processed
6041 * [I] wParam : first message parameter
6042 * [I] lParam : second message parameter
6046 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
6048 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
6053 * Creates a subclassed edit control
6056 * [I] infoPtr : valid pointer to the listview structure
6057 * [I] text : initial text for the edit
6058 * [I] style : the window style
6059 * [I] isW : TRUE if input is Unicode
6063 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, BOOL isW
)
6065 static const DWORD style
= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
|WS_VISIBLE
;
6066 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
6069 TRACE("(%p, text=%s, isW=%d)\n", infoPtr
, debugtext_t(text
, isW
), isW
);
6071 /* window will be resized and positioned after LVN_BEGINLABELEDIT */
6073 hedit
= CreateWindowW(WC_EDITW
, text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
6075 hedit
= CreateWindowA(WC_EDITA
, (LPCSTR
)text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
6077 if (!hedit
) return 0;
6079 infoPtr
->EditWndProc
= (WNDPROC
)
6080 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
6081 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
6083 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);
6084 SendMessageW(hedit
, EM_SETLIMITTEXT
, DISP_TEXT_SIZE
-1, 0);
6091 * Begin in place editing of specified list view item
6094 * [I] infoPtr : valid pointer to the listview structure
6095 * [I] nItem : item index
6096 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
6102 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
6104 WCHAR disptextW
[DISP_TEXT_SIZE
] = { 0 };
6105 HWND hwndSelf
= infoPtr
->hwndSelf
;
6106 NMLVDISPINFOW dispInfo
;
6107 HFONT hOldFont
= NULL
;
6113 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
6115 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
6117 /* remove existing edit box */
6118 if (infoPtr
->hwndEdit
)
6120 SetFocus(infoPtr
->hwndSelf
);
6121 infoPtr
->hwndEdit
= 0;
6124 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6126 infoPtr
->nEditLabelItem
= nItem
;
6128 LISTVIEW_SetSelection(infoPtr
, nItem
);
6129 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
6130 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
6132 rect
.left
= LVIR_LABEL
;
6133 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
6135 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
6136 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
6137 dispInfo
.item
.iItem
= nItem
;
6138 dispInfo
.item
.iSubItem
= 0;
6139 dispInfo
.item
.stateMask
= ~0;
6140 dispInfo
.item
.pszText
= disptextW
;
6141 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
6142 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
6144 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, isW
);
6145 if (!infoPtr
->hwndEdit
) return 0;
6147 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
6149 if (!IsWindow(hwndSelf
))
6151 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
6152 infoPtr
->hwndEdit
= 0;
6156 TRACE("disp text=%s\n", debugtext_t(dispInfo
.item
.pszText
, isW
));
6158 /* position and display edit box */
6159 hdc
= GetDC(infoPtr
->hwndSelf
);
6161 /* select the font to get appropriate metric dimensions */
6163 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
6165 /* use real edit box content, it could be altered during LVN_BEGINLABELEDIT notification */
6166 GetWindowTextW(infoPtr
->hwndEdit
, disptextW
, DISP_TEXT_SIZE
);
6167 TRACE("edit box text=%s\n", debugstr_w(disptextW
));
6169 /* get string length in pixels */
6170 GetTextExtentPoint32W(hdc
, disptextW
, lstrlenW(disptextW
), &sz
);
6172 /* add extra spacing for the next character */
6173 GetTextMetricsW(hdc
, &tm
);
6174 sz
.cx
+= tm
.tmMaxCharWidth
* 2;
6177 SelectObject(hdc
, hOldFont
);
6179 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6181 sz
.cy
= rect
.bottom
- rect
.top
+ 2;
6184 TRACE("moving edit=(%d,%d)-(%d,%d)\n", rect
.left
, rect
.top
, sz
.cx
, sz
.cy
);
6185 MoveWindow(infoPtr
->hwndEdit
, rect
.left
, rect
.top
, sz
.cx
, sz
.cy
, FALSE
);
6186 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
6187 SetFocus(infoPtr
->hwndEdit
);
6188 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
6189 return infoPtr
->hwndEdit
;
6195 * Ensures the specified item is visible, scrolling into view if necessary.
6198 * [I] infoPtr : valid pointer to the listview structure
6199 * [I] nItem : item index
6200 * [I] bPartial : partially or entirely visible
6206 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
6208 INT nScrollPosHeight
= 0;
6209 INT nScrollPosWidth
= 0;
6210 INT nHorzAdjust
= 0;
6211 INT nVertAdjust
= 0;
6214 RECT rcItem
, rcTemp
;
6216 rcItem
.left
= LVIR_BOUNDS
;
6217 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
6219 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
6221 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
6223 /* scroll left/right, but in LV_VIEW_DETAILS mode */
6224 if (infoPtr
->uView
== LV_VIEW_LIST
)
6225 nScrollPosWidth
= infoPtr
->nItemWidth
;
6226 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
6227 nScrollPosWidth
= 1;
6229 if (rcItem
.left
< infoPtr
->rcList
.left
)
6232 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
6237 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
6241 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
6243 /* scroll up/down, but not in LVS_LIST mode */
6244 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
6245 nScrollPosHeight
= infoPtr
->nItemHeight
;
6246 else if ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
))
6247 nScrollPosHeight
= 1;
6249 if (rcItem
.top
< infoPtr
->rcList
.top
)
6252 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
6257 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
6261 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
6263 if (nScrollPosWidth
)
6265 INT diff
= nHorzDiff
/ nScrollPosWidth
;
6266 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
6267 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
);
6270 if (nScrollPosHeight
)
6272 INT diff
= nVertDiff
/ nScrollPosHeight
;
6273 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
6274 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
);
6282 * Searches for an item with specific characteristics.
6285 * [I] hwnd : window handle
6286 * [I] nStart : base item index
6287 * [I] lpFindInfo : item information to look for
6290 * SUCCESS : index of item
6293 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
6294 const LVFINDINFOW
*lpFindInfo
)
6296 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6297 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
6298 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
6299 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
6300 POINT Position
, Destination
;
6303 /* Search in virtual listviews should be done by application, not by
6304 listview control, so we just send LVN_ODFINDITEMW and return the result */
6305 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6309 nmlv
.iStart
= nStart
;
6310 nmlv
.lvfi
= *lpFindInfo
;
6311 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
6314 if (!lpFindInfo
|| nItem
< 0) return -1;
6317 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
) ||
6318 lpFindInfo
->flags
& LVFI_SUBSTRING
)
6320 lvItem
.mask
|= LVIF_TEXT
;
6321 lvItem
.pszText
= szDispText
;
6322 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6325 if (lpFindInfo
->flags
& LVFI_WRAP
)
6328 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
6329 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
))
6334 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6335 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
6336 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
6337 switch(lpFindInfo
->vkDirection
)
6339 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
6340 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
6341 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
6342 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
6343 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
6344 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
6345 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
6347 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
6348 Destination
.x
= rcArea
.right
;
6349 Destination
.y
= rcArea
.bottom
;
6351 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
6355 else Destination
.x
= Destination
.y
= 0;
6357 /* if LVFI_PARAM is specified, all other flags are ignored */
6358 if (lpFindInfo
->flags
& LVFI_PARAM
)
6360 lvItem
.mask
|= LVIF_PARAM
;
6362 lvItem
.mask
&= ~LVIF_TEXT
;
6366 for (; nItem
< nLast
; nItem
++)
6368 lvItem
.iItem
= nItem
;
6369 lvItem
.iSubItem
= 0;
6370 lvItem
.pszText
= szDispText
;
6371 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
6373 if (lvItem
.mask
& LVIF_PARAM
)
6375 if (lpFindInfo
->lParam
== lvItem
.lParam
)
6381 if (lvItem
.mask
& LVIF_TEXT
)
6383 if (lpFindInfo
->flags
& (LVFI_PARTIAL
| LVFI_SUBSTRING
))
6385 WCHAR
*p
= strstrW(lvItem
.pszText
, lpFindInfo
->psz
);
6386 if (!p
|| p
!= lvItem
.pszText
) continue;
6390 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
6394 if (!bNearest
) return nItem
;
6396 /* This is very inefficient. To do a good job here,
6397 * we need a sorted array of (x,y) item positions */
6398 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
6400 /* compute the distance^2 to the destination */
6401 xdist
= Destination
.x
- Position
.x
;
6402 ydist
= Destination
.y
- Position
.y
;
6403 dist
= xdist
* xdist
+ ydist
* ydist
;
6405 /* remember the distance, and item if it's closer */
6409 nNearestItem
= nItem
;
6416 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
6421 return nNearestItem
;
6426 * Searches for an item with specific characteristics.
6429 * [I] hwnd : window handle
6430 * [I] nStart : base item index
6431 * [I] lpFindInfo : item information to look for
6434 * SUCCESS : index of item
6437 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
6438 const LVFINDINFOA
*lpFindInfo
)
6440 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
) ||
6441 lpFindInfo
->flags
& LVFI_SUBSTRING
;
6446 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
6447 if (hasText
) fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
6448 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
6449 textfreeT(strW
, FALSE
);
6455 * Retrieves column attributes.
6458 * [I] infoPtr : valid pointer to the listview structure
6459 * [I] nColumn : column index
6460 * [IO] lpColumn : column information
6461 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6462 * otherwise it is in fact a LPLVCOLUMNA
6468 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
6470 COLUMN_INFO
*lpColumnInfo
;
6473 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6474 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6476 /* initialize memory */
6477 ZeroMemory(&hdi
, sizeof(hdi
));
6479 if (lpColumn
->mask
& LVCF_TEXT
)
6481 hdi
.mask
|= HDI_TEXT
;
6482 hdi
.pszText
= lpColumn
->pszText
;
6483 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
6486 if (lpColumn
->mask
& LVCF_IMAGE
)
6487 hdi
.mask
|= HDI_IMAGE
;
6489 if (lpColumn
->mask
& LVCF_ORDER
)
6490 hdi
.mask
|= HDI_ORDER
;
6492 if (lpColumn
->mask
& LVCF_SUBITEM
)
6493 hdi
.mask
|= HDI_LPARAM
;
6495 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
6497 if (lpColumn
->mask
& LVCF_FMT
)
6498 lpColumn
->fmt
= lpColumnInfo
->fmt
;
6500 if (lpColumn
->mask
& LVCF_WIDTH
)
6501 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
6503 if (lpColumn
->mask
& LVCF_IMAGE
)
6504 lpColumn
->iImage
= hdi
.iImage
;
6506 if (lpColumn
->mask
& LVCF_ORDER
)
6507 lpColumn
->iOrder
= hdi
.iOrder
;
6509 if (lpColumn
->mask
& LVCF_SUBITEM
)
6510 lpColumn
->iSubItem
= hdi
.lParam
;
6512 if (lpColumn
->mask
& LVCF_MINWIDTH
)
6513 lpColumn
->cxMin
= lpColumnInfo
->cxMin
;
6518 static inline BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
6520 if (!infoPtr
->hwndHeader
) return FALSE
;
6521 return SendMessageW(infoPtr
->hwndHeader
, HDM_GETORDERARRAY
, iCount
, (LPARAM
)lpiArray
);
6526 * Retrieves the column width.
6529 * [I] infoPtr : valid pointer to the listview structure
6530 * [I] int : column index
6533 * SUCCESS : column width
6536 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
6538 INT nColumnWidth
= 0;
6541 TRACE("nColumn=%d\n", nColumn
);
6543 /* we have a 'column' in LIST and REPORT mode only */
6544 switch(infoPtr
->uView
)
6547 nColumnWidth
= infoPtr
->nItemWidth
;
6549 case LV_VIEW_DETAILS
:
6550 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6551 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6552 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6554 hdItem
.mask
= HDI_WIDTH
;
6555 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
6557 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
6560 nColumnWidth
= hdItem
.cxy
;
6564 TRACE("nColumnWidth=%d\n", nColumnWidth
);
6565 return nColumnWidth
;
6570 * In list or report display mode, retrieves the number of items that can fit
6571 * vertically in the visible area. In icon or small icon display mode,
6572 * retrieves the total number of visible items.
6575 * [I] infoPtr : valid pointer to the listview structure
6578 * Number of fully visible items.
6580 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
6582 switch (infoPtr
->uView
)
6585 case LV_VIEW_SMALLICON
:
6586 return infoPtr
->nItemCount
;
6587 case LV_VIEW_DETAILS
:
6588 return LISTVIEW_GetCountPerColumn(infoPtr
);
6590 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
6598 * Retrieves an image list handle.
6601 * [I] infoPtr : valid pointer to the listview structure
6602 * [I] nImageList : image list identifier
6605 * SUCCESS : image list handle
6608 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
6612 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
6613 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
6614 case LVSIL_STATE
: return infoPtr
->himlState
;
6615 case LVSIL_GROUPHEADER
:
6616 FIXME("LVSIL_GROUPHEADER not supported\n");
6619 WARN("got unknown imagelist index - %d\n", nImageList
);
6624 /* LISTVIEW_GetISearchString */
6628 * Retrieves item attributes.
6631 * [I] hwnd : window handle
6632 * [IO] lpLVItem : item info
6633 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6634 * if FALSE, then lpLVItem is a LPLVITEMA.
6637 * This is the internal 'GetItem' interface -- it tries to
6638 * be smart and avoid text copies, if possible, by modifying
6639 * lpLVItem->pszText to point to the text string. Please note
6640 * that this is not always possible (e.g. OWNERDATA), so on
6641 * entry you *must* supply valid values for pszText, and cchTextMax.
6642 * The only difference to the documented interface is that upon
6643 * return, you should use *only* the lpLVItem->pszText, rather than
6644 * the buffer pointer you provided on input. Most code already does
6645 * that, so it's not a problem.
6646 * For the two cases when the text must be copied (that is,
6647 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6653 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
6655 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
6656 NMLVDISPINFOW dispInfo
;
6662 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6664 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
6667 if (lpLVItem
->mask
== 0) return TRUE
;
6668 TRACE("mask=%x\n", lpLVItem
->mask
);
6670 /* make a local copy */
6671 isubitem
= lpLVItem
->iSubItem
;
6673 /* a quick optimization if all we're asked is the focus state
6674 * these queries are worth optimising since they are common,
6675 * and can be answered in constant time, without the heavy accesses */
6676 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
6677 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
6679 lpLVItem
->state
= 0;
6680 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6681 lpLVItem
->state
|= LVIS_FOCUSED
;
6685 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
6687 /* if the app stores all the data, handle it separately */
6688 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
6690 dispInfo
.item
.state
= 0;
6692 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
6693 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) ||
6694 ((lpLVItem
->mask
& LVIF_STATE
) && (infoPtr
->uCallbackMask
& lpLVItem
->stateMask
)))
6696 UINT mask
= lpLVItem
->mask
;
6698 /* NOTE: copy only fields which we _know_ are initialized, some apps
6699 * depend on the uninitialized fields being 0 */
6700 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
6701 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
6702 dispInfo
.item
.iSubItem
= isubitem
;
6703 if (lpLVItem
->mask
& LVIF_TEXT
)
6705 if (lpLVItem
->mask
& LVIF_NORECOMPUTE
)
6707 dispInfo
.item
.mask
&= ~(LVIF_TEXT
| LVIF_NORECOMPUTE
);
6710 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
6711 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
6714 if (lpLVItem
->mask
& LVIF_STATE
)
6715 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
6716 /* could be zeroed on LVIF_NORECOMPUTE case */
6717 if (dispInfo
.item
.mask
)
6719 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
6720 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
6721 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6723 /* full size structure expected - _WIN32IE >= 0x560 */
6724 *lpLVItem
= dispInfo
.item
;
6726 else if (lpLVItem
->mask
& LVIF_INDENT
)
6728 /* indent member expected - _WIN32IE >= 0x300 */
6729 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
6733 /* minimal structure expected */
6734 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
6736 lpLVItem
->mask
= mask
;
6737 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
6741 /* make sure lParam is zeroed out */
6742 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
6744 /* callback marked pointer required here */
6745 if ((lpLVItem
->mask
& LVIF_TEXT
) && (lpLVItem
->mask
& LVIF_NORECOMPUTE
))
6746 lpLVItem
->pszText
= LPSTR_TEXTCALLBACKW
;
6748 /* we store only a little state, so if we're not asked, we're done */
6749 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
6751 /* if focus is handled by us, report it */
6752 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
6754 lpLVItem
->state
&= ~LVIS_FOCUSED
;
6755 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6756 lpLVItem
->state
|= LVIS_FOCUSED
;
6759 /* and do the same for selection, if we handle it */
6760 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
6762 lpLVItem
->state
&= ~LVIS_SELECTED
;
6763 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
6764 lpLVItem
->state
|= LVIS_SELECTED
;
6770 /* find the item and subitem structures before we proceed */
6771 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
6772 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
6777 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
6778 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
6781 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
6786 pItemHdr
= &lpItem
->hdr
;
6788 /* Do we need to query the state from the app? */
6789 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
6791 dispInfo
.item
.mask
|= LVIF_STATE
;
6792 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
6795 /* Do we need to enquire about the image? */
6796 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
6797 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
6799 dispInfo
.item
.mask
|= LVIF_IMAGE
;
6800 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
6803 /* Only items support indentation */
6804 if ((lpLVItem
->mask
& LVIF_INDENT
) && lpItem
->iIndent
== I_INDENTCALLBACK
&&
6807 dispInfo
.item
.mask
|= LVIF_INDENT
;
6808 dispInfo
.item
.iIndent
= I_INDENTCALLBACK
;
6811 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6812 if ((lpLVItem
->mask
& LVIF_TEXT
) && !(lpLVItem
->mask
& LVIF_NORECOMPUTE
) &&
6813 !is_text(pItemHdr
->pszText
))
6815 dispInfo
.item
.mask
|= LVIF_TEXT
;
6816 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
6817 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
6818 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
6819 *dispInfo
.item
.pszText
= '\0';
6822 /* If we don't have all the requested info, query the application */
6823 if (dispInfo
.item
.mask
)
6825 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
6826 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
6827 dispInfo
.item
.lParam
= lpItem
->lParam
;
6828 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
6829 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
6832 /* we should not store values for subitems */
6833 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
6835 /* Now, handle the iImage field */
6836 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
6838 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
6839 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
6840 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
6842 else if (lpLVItem
->mask
& LVIF_IMAGE
)
6844 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
6845 lpLVItem
->iImage
= pItemHdr
->iImage
;
6847 lpLVItem
->iImage
= 0;
6850 /* The pszText field */
6851 if (dispInfo
.item
.mask
& LVIF_TEXT
)
6853 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
6854 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
6856 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
6858 else if (lpLVItem
->mask
& LVIF_TEXT
)
6860 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6861 if (isW
|| !is_text(pItemHdr
->pszText
)) lpLVItem
->pszText
= pItemHdr
->pszText
;
6862 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
6865 /* Next is the lParam field */
6866 if (dispInfo
.item
.mask
& LVIF_PARAM
)
6868 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
6869 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
6870 lpItem
->lParam
= dispInfo
.item
.lParam
;
6872 else if (lpLVItem
->mask
& LVIF_PARAM
)
6873 lpLVItem
->lParam
= lpItem
->lParam
;
6875 /* if this is a subitem, we're done */
6876 if (isubitem
) return TRUE
;
6878 /* ... the state field (this one is different due to uCallbackmask) */
6879 if (lpLVItem
->mask
& LVIF_STATE
)
6881 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
6882 if (dispInfo
.item
.mask
& LVIF_STATE
)
6884 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
6885 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
6887 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
6889 lpLVItem
->state
&= ~LVIS_FOCUSED
;
6890 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
6891 lpLVItem
->state
|= LVIS_FOCUSED
;
6893 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
6895 lpLVItem
->state
&= ~LVIS_SELECTED
;
6896 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
6897 lpLVItem
->state
|= LVIS_SELECTED
;
6901 /* and last, but not least, the indent field */
6902 if (dispInfo
.item
.mask
& LVIF_INDENT
)
6904 lpLVItem
->iIndent
= dispInfo
.item
.iIndent
;
6905 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && lpItem
->iIndent
== I_INDENTCALLBACK
)
6906 lpItem
->iIndent
= dispInfo
.item
.iIndent
;
6908 else if (lpLVItem
->mask
& LVIF_INDENT
)
6910 lpLVItem
->iIndent
= lpItem
->iIndent
;
6918 * Retrieves item attributes.
6921 * [I] hwnd : window handle
6922 * [IO] lpLVItem : item info
6923 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6924 * if FALSE, then lpLVItem is a LPLVITEMA.
6927 * This is the external 'GetItem' interface -- it properly copies
6928 * the text in the provided buffer.
6934 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
6939 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
6942 pszText
= lpLVItem
->pszText
;
6943 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
6944 if (bResult
&& (lpLVItem
->mask
& LVIF_TEXT
) && lpLVItem
->pszText
!= pszText
)
6946 if (lpLVItem
->pszText
!= LPSTR_TEXTCALLBACKW
)
6947 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
6949 pszText
= LPSTR_TEXTCALLBACKW
;
6951 lpLVItem
->pszText
= pszText
;
6959 * Retrieves the position (upper-left) of the listview control item.
6960 * Note that for LVS_ICON style, the upper-left is that of the icon
6961 * and not the bounding box.
6964 * [I] infoPtr : valid pointer to the listview structure
6965 * [I] nItem : item index
6966 * [O] lpptPosition : coordinate information
6972 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
6976 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
6978 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
6980 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6981 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
6983 if (infoPtr
->uView
== LV_VIEW_ICON
)
6985 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
6986 lpptPosition
->y
+= ICON_TOP_PADDING
;
6988 lpptPosition
->x
+= Origin
.x
;
6989 lpptPosition
->y
+= Origin
.y
;
6991 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
6998 * Retrieves the bounding rectangle for a listview control item.
7001 * [I] infoPtr : valid pointer to the listview structure
7002 * [I] nItem : item index
7003 * [IO] lprc : bounding rectangle coordinates
7004 * lprc->left specifies the portion of the item for which the bounding
7005 * rectangle will be retrieved.
7007 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
7008 * including the icon and label.
7011 * * Experiment shows that native control returns:
7012 * * width = min (48, length of text line)
7013 * * .left = position.x - (width - iconsize.cx)/2
7014 * * .right = .left + width
7015 * * height = #lines of text * ntmHeight + icon height + 8
7016 * * .top = position.y - 2
7017 * * .bottom = .top + height
7018 * * separation between items .y = itemSpacing.cy - height
7019 * * .x = itemSpacing.cx - width
7020 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
7023 * * Experiment shows that native control returns:
7024 * * width = iconSize.cx + 16
7025 * * .left = position.x - (width - iconsize.cx)/2
7026 * * .right = .left + width
7027 * * height = iconSize.cy + 4
7028 * * .top = position.y - 2
7029 * * .bottom = .top + height
7030 * * separation between items .y = itemSpacing.cy - height
7031 * * .x = itemSpacing.cx - width
7032 * LVIR_LABEL Returns the bounding rectangle of the item text.
7035 * * Experiment shows that native control returns:
7036 * * width = text length
7037 * * .left = position.x - width/2
7038 * * .right = .left + width
7039 * * height = ntmH * linecount + 2
7040 * * .top = position.y + iconSize.cy + 6
7041 * * .bottom = .top + height
7042 * * separation between items .y = itemSpacing.cy - height
7043 * * .x = itemSpacing.cx - width
7044 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
7045 * rectangles, but excludes columns in report view.
7052 * Note that the bounding rectangle of the label in the LVS_ICON view depends
7053 * upon whether the window has the focus currently and on whether the item
7054 * is the one with the focus. Ensure that the control's record of which
7055 * item has the focus agrees with the items' records.
7057 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
7059 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
7060 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
7061 POINT Position
, Origin
;
7065 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
7067 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7069 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7070 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
7072 /* Be smart and try to figure out the minimum we have to do */
7073 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
7074 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
7075 if (infoPtr
->uView
== LV_VIEW_ICON
&& lprc
->left
!= LVIR_ICON
&&
7076 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
7077 oversizedBox
= TRUE
;
7079 /* get what we need from the item before hand, so we make
7080 * only one request. This can speed up things, if data
7081 * is stored on the app side */
7083 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
7084 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
7085 lvItem
.iItem
= nItem
;
7086 lvItem
.iSubItem
= 0;
7087 lvItem
.pszText
= szDispText
;
7088 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7089 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
7090 /* we got the state already up, simulate it here, to avoid a reget */
7091 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprc
->left
!= LVIR_ICON
))
7093 lvItem
.mask
|= LVIF_STATE
;
7094 lvItem
.stateMask
= LVIS_FOCUSED
;
7095 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
7098 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
7099 lprc
->left
= LVIR_BOUNDS
;
7105 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
7109 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
7113 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
7116 case LVIR_SELECTBOUNDS
:
7117 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
7121 WARN("Unknown value: %d\n", lprc
->left
);
7125 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7127 if (mode
!= LVIR_BOUNDS
)
7128 OffsetRect(lprc
, Origin
.x
+ LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
,
7129 Position
.y
+ Origin
.y
);
7131 OffsetRect(lprc
, Origin
.x
, Position
.y
+ Origin
.y
);
7134 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
7136 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
7143 * Retrieves the spacing between listview control items.
7146 * [I] infoPtr : valid pointer to the listview structure
7147 * [IO] lprc : rectangle to receive the output
7148 * on input, lprc->top = nSubItem
7149 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
7151 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
7152 * not only those of the first column.
7158 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT item
, LPRECT lprc
)
7160 RECT rect
= { 0, 0, 0, 0 };
7164 if (!lprc
) return FALSE
;
7166 TRACE("(item=%d, subitem=%d, type=%d)\n", item
, lprc
->top
, lprc
->left
);
7167 /* Subitem of '0' means item itself, and this works for all control view modes */
7169 return LISTVIEW_GetItemRect(infoPtr
, item
, lprc
);
7171 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) return FALSE
;
7173 LISTVIEW_GetOrigin(infoPtr
, &origin
);
7174 /* this works for any item index, no matter if it exists or not */
7175 y
= item
* infoPtr
->nItemHeight
+ origin
.y
;
7177 if (infoPtr
->hwndHeader
&& SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, lprc
->top
, (LPARAM
)&rect
))
7180 rect
.bottom
= infoPtr
->nItemHeight
;
7184 /* Native implementation is broken for this case and garbage is left for left and right fields,
7185 we zero them to get predictable output */
7186 lprc
->left
= lprc
->right
= lprc
->top
= 0;
7187 lprc
->bottom
= infoPtr
->nItemHeight
;
7188 OffsetRect(lprc
, origin
.x
, y
);
7189 TRACE("return rect %s\n", wine_dbgstr_rect(lprc
));
7197 /* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */
7198 if (infoPtr
->himlSmall
)
7199 rect
.right
= rect
.left
+ infoPtr
->iconSize
.cx
;
7201 rect
.right
= rect
.left
;
7203 rect
.bottom
= rect
.top
+ infoPtr
->iconSize
.cy
;
7211 ERR("Unknown bounds=%d\n", lprc
->left
);
7215 OffsetRect(&rect
, origin
.x
, y
);
7217 TRACE("return rect %s\n", wine_dbgstr_rect(lprc
));
7224 * Retrieves the spacing between listview control items.
7227 * [I] infoPtr : valid pointer to the listview structure
7228 * [I] bSmall : flag for small or large icon
7231 * Horizontal + vertical spacing
7233 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
7239 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7243 if (infoPtr
->uView
== LV_VIEW_ICON
)
7244 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
7246 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
7253 * Retrieves the state of a listview control item.
7256 * [I] infoPtr : valid pointer to the listview structure
7257 * [I] nItem : item index
7258 * [I] uMask : state mask
7261 * State specified by the mask.
7263 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
7267 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
7269 lvItem
.iItem
= nItem
;
7270 lvItem
.iSubItem
= 0;
7271 lvItem
.mask
= LVIF_STATE
;
7272 lvItem
.stateMask
= uMask
;
7273 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
7275 return lvItem
.state
& uMask
;
7280 * Retrieves the text of a listview control item or subitem.
7283 * [I] hwnd : window handle
7284 * [I] nItem : item index
7285 * [IO] lpLVItem : item information
7286 * [I] isW : TRUE if lpLVItem is Unicode
7289 * SUCCESS : string length
7292 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
7294 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
7296 lpLVItem
->mask
= LVIF_TEXT
;
7297 lpLVItem
->iItem
= nItem
;
7298 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
7300 return textlenT(lpLVItem
->pszText
, isW
);
7305 * Searches for an item based on properties + relationships.
7308 * [I] infoPtr : valid pointer to the listview structure
7309 * [I] nItem : item index
7310 * [I] uFlags : relationship flag
7313 * SUCCESS : item index
7316 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
7319 LVFINDINFOW lvFindInfo
;
7320 INT nCountPerColumn
;
7324 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
7325 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
7327 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
7329 if (uFlags
& LVNI_CUT
)
7332 if (uFlags
& LVNI_DROPHILITED
)
7333 uMask
|= LVIS_DROPHILITED
;
7335 if (uFlags
& LVNI_FOCUSED
)
7336 uMask
|= LVIS_FOCUSED
;
7338 if (uFlags
& LVNI_SELECTED
)
7339 uMask
|= LVIS_SELECTED
;
7341 /* if we're asked for the focused item, that's only one,
7342 * so it's worth optimizing */
7343 if (uFlags
& LVNI_FOCUSED
)
7345 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
7346 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
7349 if (uFlags
& LVNI_ABOVE
)
7351 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
7356 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7362 /* Special case for autoarrange - move 'til the top of a list */
7363 if (is_autoarrange(infoPtr
))
7365 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7366 while (nItem
- nCountPerRow
>= 0)
7368 nItem
-= nCountPerRow
;
7369 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7374 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7375 lvFindInfo
.vkDirection
= VK_UP
;
7376 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7377 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7379 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7384 else if (uFlags
& LVNI_BELOW
)
7386 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
7388 while (nItem
< infoPtr
->nItemCount
)
7391 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7397 /* Special case for autoarrange - move 'til the bottom of a list */
7398 if (is_autoarrange(infoPtr
))
7400 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7401 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
7403 nItem
+= nCountPerRow
;
7404 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7409 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7410 lvFindInfo
.vkDirection
= VK_DOWN
;
7411 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7412 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7414 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7419 else if (uFlags
& LVNI_TOLEFT
)
7421 if (infoPtr
->uView
== LV_VIEW_LIST
)
7423 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
7424 while (nItem
- nCountPerColumn
>= 0)
7426 nItem
-= nCountPerColumn
;
7427 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7431 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7433 /* Special case for autoarrange - move 'til the beginning of a row */
7434 if (is_autoarrange(infoPtr
))
7436 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7437 while (nItem
% nCountPerRow
> 0)
7440 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7445 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7446 lvFindInfo
.vkDirection
= VK_LEFT
;
7447 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7448 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7450 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7455 else if (uFlags
& LVNI_TORIGHT
)
7457 if (infoPtr
->uView
== LV_VIEW_LIST
)
7459 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
7460 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
7462 nItem
+= nCountPerColumn
;
7463 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7467 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7469 /* Special case for autoarrange - move 'til the end of a row */
7470 if (is_autoarrange(infoPtr
))
7472 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
7473 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
7476 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7481 lvFindInfo
.flags
= LVFI_NEARESTXY
;
7482 lvFindInfo
.vkDirection
= VK_RIGHT
;
7483 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
7484 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
7486 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
7495 /* search by index */
7496 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
7498 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
7506 /* LISTVIEW_GetNumberOfWorkAreas */
7510 * Retrieves the origin coordinates when in icon or small icon display mode.
7513 * [I] infoPtr : valid pointer to the listview structure
7514 * [O] lpptOrigin : coordinate information
7519 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
7521 INT nHorzPos
= 0, nVertPos
= 0;
7522 SCROLLINFO scrollInfo
;
7524 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
7525 scrollInfo
.fMask
= SIF_POS
;
7527 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
7528 nHorzPos
= scrollInfo
.nPos
;
7529 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
7530 nVertPos
= scrollInfo
.nPos
;
7532 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
7534 lpptOrigin
->x
= infoPtr
->rcList
.left
;
7535 lpptOrigin
->y
= infoPtr
->rcList
.top
;
7536 if (infoPtr
->uView
== LV_VIEW_LIST
)
7537 nHorzPos
*= infoPtr
->nItemWidth
;
7538 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7539 nVertPos
*= infoPtr
->nItemHeight
;
7541 lpptOrigin
->x
-= nHorzPos
;
7542 lpptOrigin
->y
-= nVertPos
;
7544 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
7549 * Retrieves the width of a string.
7552 * [I] hwnd : window handle
7553 * [I] lpszText : text string to process
7554 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7557 * SUCCESS : string width (in pixels)
7560 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
7565 if (is_text(lpszText
))
7567 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
7568 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7569 HFONT hOldFont
= SelectObject(hdc
, hFont
);
7572 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
7574 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
7575 SelectObject(hdc
, hOldFont
);
7576 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7578 return stringSize
.cx
;
7583 * Determines which listview item is located at the specified position.
7586 * [I] infoPtr : valid pointer to the listview structure
7587 * [IO] lpht : hit test information
7588 * [I] subitem : fill out iSubItem.
7589 * [I] select : return the index only if the hit selects the item
7592 * (mm 20001022): We must not allow iSubItem to be touched, for
7593 * an app might pass only a structure with space up to iItem!
7594 * (MS Office 97 does that for instance in the file open dialog)
7597 * SUCCESS : item index
7600 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
7602 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
7603 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
7604 POINT Origin
, Position
, opt
;
7610 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
7614 if (subitem
) lpht
->iSubItem
= 0;
7616 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7618 /* set whole list relation flags */
7619 if (subitem
&& infoPtr
->uView
== LV_VIEW_DETAILS
)
7621 /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7622 if (infoPtr
->rcList
.left
> lpht
->pt
.x
&& Origin
.x
< lpht
->pt
.x
)
7623 lpht
->flags
|= LVHT_TOLEFT
;
7625 if (lpht
->pt
.y
< infoPtr
->rcList
.top
&& lpht
->pt
.y
>= 0)
7626 opt
.y
= lpht
->pt
.y
+ infoPtr
->rcList
.top
;
7630 if (infoPtr
->rcList
.bottom
< opt
.y
)
7631 lpht
->flags
|= LVHT_BELOW
;
7635 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
7636 lpht
->flags
|= LVHT_TOLEFT
;
7637 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
7638 lpht
->flags
|= LVHT_TORIGHT
;
7640 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
7641 lpht
->flags
|= LVHT_ABOVE
;
7642 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
7643 lpht
->flags
|= LVHT_BELOW
;
7646 /* even if item is invalid try to find subitem */
7647 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& subitem
)
7652 opt
.x
= lpht
->pt
.x
- Origin
.x
;
7654 lpht
->iSubItem
= -1;
7655 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
7657 pRect
= &LISTVIEW_GetColumnInfo(infoPtr
, j
)->rcHeader
;
7659 if ((opt
.x
>= pRect
->left
) && (opt
.x
< pRect
->right
))
7665 TRACE("lpht->iSubItem=%d\n", lpht
->iSubItem
);
7667 /* if we're outside horizontal columns bounds there's nothing to test further */
7668 if (lpht
->iSubItem
== -1)
7671 lpht
->flags
= LVHT_NOWHERE
;
7676 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
7677 if (lpht
->flags
) return -1;
7679 lpht
->flags
|= LVHT_NOWHERE
;
7681 /* first deal with the large items */
7682 rcSearch
.left
= lpht
->pt
.x
;
7683 rcSearch
.top
= lpht
->pt
.y
;
7684 rcSearch
.right
= rcSearch
.left
+ 1;
7685 rcSearch
.bottom
= rcSearch
.top
+ 1;
7687 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
7688 iterator_next(&i
); /* go to first item in the sequence */
7690 iterator_destroy(&i
);
7692 TRACE("lpht->iItem=%d\n", iItem
);
7693 if (iItem
== -1) return -1;
7695 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
7696 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
7697 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
7698 if (infoPtr
->uView
== LV_VIEW_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
7699 lvItem
.iItem
= iItem
;
7700 lvItem
.iSubItem
= subitem
? lpht
->iSubItem
: 0;
7701 lvItem
.pszText
= szDispText
;
7702 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7703 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
7704 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
7706 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
7707 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
7708 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
7710 if (lpht
->pt
.y
< infoPtr
->rcList
.top
&& lpht
->pt
.y
>= 0)
7711 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
+ infoPtr
->rcList
.top
;
7713 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
7715 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7718 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
7719 opt
.x
= lpht
->pt
.x
- Origin
.x
;
7723 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
7724 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
7726 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
7727 if (!PtInRect(&rcBounds
, opt
)) return -1;
7729 /* That's a special case - row rectangle is used as item rectangle and
7730 returned flags contain all item parts. */
7731 is_fullrow
= (infoPtr
->uView
== LV_VIEW_DETAILS
) && ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) || (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
));
7733 if (PtInRect(&rcIcon
, opt
))
7734 lpht
->flags
|= LVHT_ONITEMICON
;
7735 else if (PtInRect(&rcLabel
, opt
))
7736 lpht
->flags
|= LVHT_ONITEMLABEL
;
7737 else if (infoPtr
->himlState
&& PtInRect(&rcState
, opt
))
7738 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
7739 if (is_fullrow
&& !(lpht
->flags
& LVHT_ONITEM
))
7741 lpht
->flags
= LVHT_ONITEM
| LVHT_ABOVE
;
7743 if (lpht
->flags
& LVHT_ONITEM
)
7744 lpht
->flags
&= ~LVHT_NOWHERE
;
7745 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
7747 if (select
&& !is_fullrow
)
7749 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7751 /* get main item bounds */
7752 lvItem
.iSubItem
= 0;
7753 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
7754 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
7755 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
7757 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
7759 return lpht
->iItem
= iItem
;
7764 * Inserts a new item in the listview control.
7767 * [I] infoPtr : valid pointer to the listview structure
7768 * [I] lpLVItem : item information
7769 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7772 * SUCCESS : new item index
7775 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
7782 BOOL is_sorted
, has_changed
;
7784 HWND hwndSelf
= infoPtr
->hwndSelf
;
7786 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
7788 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
7790 /* make sure it's an item, and not a subitem; cannot insert a subitem */
7791 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
7793 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
7795 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
7797 /* insert item in listview control data structure */
7798 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
7799 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
7801 /* link with id struct */
7802 if (!(lpID
= Alloc(sizeof(ITEM_ID
)))) goto fail
;
7804 lpID
->item
= hdpaSubItems
;
7805 lpID
->id
= get_next_itemid(infoPtr
);
7806 if ( DPA_InsertPtr(infoPtr
->hdpaItemIds
, infoPtr
->nItemCount
, lpID
) == -1) goto fail
;
7808 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
7809 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
7811 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
7813 /* calculate new item index */
7820 while (i
< infoPtr
->nItemCount
)
7822 hItem
= DPA_GetPtr( infoPtr
->hdpaItems
, i
);
7823 item_s
= DPA_GetPtr(hItem
, 0);
7825 cmpv
= textcmpWT(item_s
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
7826 if (infoPtr
->dwStyle
& LVS_SORTDESCENDING
) cmpv
*= -1;
7828 if (cmpv
>= 0) break;
7834 nItem
= min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
7836 TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
7837 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
7838 if (nItem
== -1) goto fail
;
7839 infoPtr
->nItemCount
++;
7841 /* shift indices first so they don't get tangled */
7842 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
7844 /* set the item attributes */
7845 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
7847 /* full size structure expected - _WIN32IE >= 0x560 */
7850 else if (lpLVItem
->mask
& LVIF_INDENT
)
7852 /* indent member expected - _WIN32IE >= 0x300 */
7853 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
7857 /* minimal structure expected */
7858 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
7861 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7863 if (item
.mask
& LVIF_STATE
)
7865 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
7866 item
.state
&= ~LVIS_STATEIMAGEMASK
;
7867 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
7871 item
.mask
|= LVIF_STATE
;
7872 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7873 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7877 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
7879 /* make room for the position, if we are in the right mode */
7880 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
7882 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
7884 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
7886 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
7891 /* send LVN_INSERTITEM notification */
7892 memset(&nmlv
, 0, sizeof(NMLISTVIEW
));
7894 nmlv
.lParam
= lpItem
->lParam
;
7895 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
7896 if (!IsWindow(hwndSelf
))
7899 /* align items (set position of each item) */
7900 if (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
)
7904 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
7905 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
7907 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
7909 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
7912 /* now is the invalidation fun */
7913 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
7917 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
7918 LISTVIEW_ShiftFocus(infoPtr
, infoPtr
->nFocusedItem
, nItem
, -1);
7919 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
7920 infoPtr
->nItemCount
--;
7922 DPA_DeletePtr(hdpaSubItems
, 0);
7923 DPA_Destroy (hdpaSubItems
);
7930 * Checks item visibility.
7933 * [I] infoPtr : valid pointer to the listview structure
7934 * [I] nFirst : item index to check for
7937 * Item visible : TRUE
7938 * Item invisible or failure : FALSE
7940 static BOOL
LISTVIEW_IsItemVisible(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
7942 POINT Origin
, Position
;
7947 TRACE("nItem=%d\n", nItem
);
7949 if (nItem
< 0 || nItem
>= DPA_GetPtrCount(infoPtr
->hdpaItems
)) return FALSE
;
7951 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7952 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
7953 rcItem
.left
= Position
.x
+ Origin
.x
;
7954 rcItem
.top
= Position
.y
+ Origin
.y
;
7955 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
7956 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
7958 hdc
= GetDC(infoPtr
->hwndSelf
);
7959 if (!hdc
) return FALSE
;
7960 ret
= RectVisible(hdc
, &rcItem
);
7961 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7968 * Redraws a range of items.
7971 * [I] infoPtr : valid pointer to the listview structure
7972 * [I] nFirst : first item
7973 * [I] nLast : last item
7979 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
7983 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
7984 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
7987 for (i
= nFirst
; i
<= nLast
; i
++)
7988 LISTVIEW_InvalidateItem(infoPtr
, i
);
7995 * Scroll the content of a listview.
7998 * [I] infoPtr : valid pointer to the listview structure
7999 * [I] dx : horizontal scroll amount in pixels
8000 * [I] dy : vertical scroll amount in pixels
8007 * If the control is in report view (LV_VIEW_DETAILS) the control can
8008 * be scrolled only in line increments. "dy" will be rounded to the
8009 * nearest number of pixels that are a whole line. Ex: if line height
8010 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
8011 * is passed, then the scroll will be 0. (per MSDN 7/2002)
8013 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8015 switch(infoPtr
->uView
) {
8016 case LV_VIEW_DETAILS
:
8017 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
8018 dy
/= infoPtr
->nItemHeight
;
8021 if (dy
!= 0) return FALSE
;
8027 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
);
8028 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
);
8035 * Sets the background color.
8038 * [I] infoPtr : valid pointer to the listview structure
8039 * [I] color : background color
8045 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF color
)
8047 TRACE("(color=%x)\n", color
);
8050 infoPtr
->bDefaultBkColor
= FALSE
;
8052 if(infoPtr
->clrBk
!= color
) {
8053 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8054 infoPtr
->clrBk
= color
;
8055 if (color
== CLR_NONE
)
8056 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
8059 infoPtr
->hBkBrush
= CreateSolidBrush(color
);
8060 infoPtr
->dwLvExStyle
&= ~LVS_EX_TRANSPARENTBKGND
;
8067 /* LISTVIEW_SetBkImage */
8069 /*** Helper for {Insert,Set}ColumnT *only* */
8070 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
8071 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8073 if (lpColumn
->mask
& LVCF_FMT
)
8075 /* format member is valid */
8076 lphdi
->mask
|= HDI_FORMAT
;
8078 /* set text alignment (leftmost column must be left-aligned) */
8079 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8080 lphdi
->fmt
|= HDF_LEFT
;
8081 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
8082 lphdi
->fmt
|= HDF_RIGHT
;
8083 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
8084 lphdi
->fmt
|= HDF_CENTER
;
8086 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
8087 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
8089 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
8091 lphdi
->fmt
|= HDF_IMAGE
;
8092 lphdi
->iImage
= I_IMAGECALLBACK
;
8095 if (lpColumn
->fmt
& LVCFMT_FIXED_WIDTH
)
8096 lphdi
->fmt
|= HDF_FIXEDWIDTH
;
8099 if (lpColumn
->mask
& LVCF_WIDTH
)
8101 lphdi
->mask
|= HDI_WIDTH
;
8102 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
8104 /* make it fill the remainder of the controls width */
8108 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
8110 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
8111 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
8114 /* retrieve the layout of the header */
8115 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
8116 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
8118 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
8121 lphdi
->cxy
= lpColumn
->cx
;
8124 if (lpColumn
->mask
& LVCF_TEXT
)
8126 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
8127 lphdi
->fmt
|= HDF_STRING
;
8128 lphdi
->pszText
= lpColumn
->pszText
;
8129 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
8132 if (lpColumn
->mask
& LVCF_IMAGE
)
8134 lphdi
->mask
|= HDI_IMAGE
;
8135 lphdi
->iImage
= lpColumn
->iImage
;
8138 if (lpColumn
->mask
& LVCF_ORDER
)
8140 lphdi
->mask
|= HDI_ORDER
;
8141 lphdi
->iOrder
= lpColumn
->iOrder
;
8148 * Inserts a new column.
8151 * [I] infoPtr : valid pointer to the listview structure
8152 * [I] nColumn : column index
8153 * [I] lpColumn : column information
8154 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
8157 * SUCCESS : new column index
8160 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
8161 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8163 COLUMN_INFO
*lpColumnInfo
;
8167 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
8169 if (!lpColumn
|| nColumn
< 0) return -1;
8170 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
8172 ZeroMemory(&hdi
, sizeof(HDITEMW
));
8173 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
8176 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
8177 * (can be seen in SPY) otherwise column never gets added.
8179 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
8180 hdi
.mask
|= HDI_WIDTH
;
8185 * when the iSubItem is available Windows copies it to the header lParam. It seems
8186 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
8188 if (lpColumn
->mask
& LVCF_SUBITEM
)
8190 hdi
.mask
|= HDI_LPARAM
;
8191 hdi
.lParam
= lpColumn
->iSubItem
;
8194 /* create header if not present */
8195 LISTVIEW_CreateHeader(infoPtr
);
8196 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) &&
8197 (infoPtr
->uView
== LV_VIEW_DETAILS
) && (WS_VISIBLE
& infoPtr
->dwStyle
))
8199 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8202 /* insert item in header control */
8203 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
8204 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
8205 nColumn
, (LPARAM
)&hdi
);
8206 if (nNewColumn
== -1) return -1;
8207 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
8209 /* create our own column info */
8210 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
8211 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
8213 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
8214 if (lpColumn
->mask
& LVCF_MINWIDTH
) lpColumnInfo
->cxMin
= lpColumn
->cxMin
;
8215 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, nNewColumn
, (LPARAM
)&lpColumnInfo
->rcHeader
))
8218 /* now we have to actually adjust the data */
8219 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
8221 SUBITEM_INFO
*lpSubItem
;
8227 item
.iSubItem
= nNewColumn
;
8228 item
.mask
= LVIF_TEXT
| LVIF_IMAGE
;
8229 item
.iImage
= I_IMAGECALLBACK
;
8230 item
.pszText
= LPSTR_TEXTCALLBACKW
;
8232 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
8234 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
8235 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
8237 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
8238 if (lpSubItem
->iSubItem
>= nNewColumn
)
8239 lpSubItem
->iSubItem
++;
8242 /* add new subitem for each item */
8244 set_sub_item(infoPtr
, &item
, isW
, &changed
);
8248 /* make space for the new column */
8249 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8250 LISTVIEW_UpdateItemSize(infoPtr
);
8255 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
8258 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
8266 * Sets the attributes of a header item.
8269 * [I] infoPtr : valid pointer to the listview structure
8270 * [I] nColumn : column index
8271 * [I] lpColumn : column attributes
8272 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
8278 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
8279 const LVCOLUMNW
*lpColumn
, BOOL isW
)
8281 HDITEMW hdi
, hdiget
;
8284 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
8286 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
8288 ZeroMemory(&hdi
, sizeof(HDITEMW
));
8289 if (lpColumn
->mask
& LVCF_FMT
)
8291 hdi
.mask
|= HDI_FORMAT
;
8292 hdiget
.mask
= HDI_FORMAT
;
8293 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdiget
))
8294 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
8296 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
8298 /* set header item attributes */
8299 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, nColumn
, (LPARAM
)&hdi
);
8300 if (!bResult
) return FALSE
;
8302 if (lpColumn
->mask
& LVCF_FMT
)
8304 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
8305 INT oldFmt
= lpColumnInfo
->fmt
;
8307 lpColumnInfo
->fmt
= lpColumn
->fmt
;
8308 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
8310 if (infoPtr
->uView
== LV_VIEW_DETAILS
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
8314 if (lpColumn
->mask
& LVCF_MINWIDTH
)
8315 LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->cxMin
= lpColumn
->cxMin
;
8322 * Sets the column order array
8325 * [I] infoPtr : valid pointer to the listview structure
8326 * [I] iCount : number of elements in column order array
8327 * [I] lpiArray : pointer to column order array
8333 static BOOL
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
8335 if (!infoPtr
->hwndHeader
) return FALSE
;
8336 infoPtr
->colRectsDirty
= TRUE
;
8337 return SendMessageW(infoPtr
->hwndHeader
, HDM_SETORDERARRAY
, iCount
, (LPARAM
)lpiArray
);
8342 * Sets the width of a column
8345 * [I] infoPtr : valid pointer to the listview structure
8346 * [I] nColumn : column index
8347 * [I] cx : column width
8353 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
8355 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
8359 TRACE("(nColumn=%d, cx=%d)\n", nColumn
, cx
);
8361 /* set column width only if in report or list mode */
8362 if (infoPtr
->uView
!= LV_VIEW_DETAILS
&& infoPtr
->uView
!= LV_VIEW_LIST
) return FALSE
;
8364 /* take care of invalid cx values - LVSCW_AUTOSIZE_* values are negative,
8365 with _USEHEADER being the lowest */
8366 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& cx
< LVSCW_AUTOSIZE_USEHEADER
) cx
= LVSCW_AUTOSIZE
;
8367 else if (infoPtr
->uView
== LV_VIEW_LIST
&& cx
<= 0) return FALSE
;
8369 /* resize all columns if in LV_VIEW_LIST mode */
8370 if(infoPtr
->uView
== LV_VIEW_LIST
)
8372 infoPtr
->nItemWidth
= cx
;
8373 LISTVIEW_InvalidateList(infoPtr
);
8377 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
8379 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
8384 lvItem
.mask
= LVIF_TEXT
;
8386 lvItem
.iSubItem
= nColumn
;
8387 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
8388 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
8390 lvItem
.pszText
= szDispText
;
8391 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
8392 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
8393 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
8395 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
8396 max_cx
+= infoPtr
->iconSize
.cx
;
8397 max_cx
+= TRAILING_LABEL_PADDING
;
8400 /* autosize based on listview items width */
8401 if(cx
== LVSCW_AUTOSIZE
)
8403 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
8405 /* if iCol is the last column make it fill the remainder of the controls width */
8406 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
8411 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8412 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
8414 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
8418 /* Despite what the MS docs say, if this is not the last
8419 column, then MS resizes the column to the width of the
8420 largest text string in the column, including headers
8421 and items. This is different from LVSCW_AUTOSIZE in that
8422 LVSCW_AUTOSIZE ignores the header string length. */
8425 /* retrieve header text */
8426 hdi
.mask
= HDI_TEXT
|HDI_FORMAT
|HDI_IMAGE
|HDI_BITMAP
;
8427 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
8428 hdi
.pszText
= szDispText
;
8429 if (SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdi
))
8431 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
8432 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
8433 HIMAGELIST himl
= (HIMAGELIST
)SendMessageW(infoPtr
->hwndHeader
, HDM_GETIMAGELIST
, 0, 0);
8434 INT bitmap_margin
= 0;
8437 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
8438 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
8440 if (hdi
.fmt
& (HDF_IMAGE
|HDF_BITMAP
))
8441 bitmap_margin
= SendMessageW(infoPtr
->hwndHeader
, HDM_GETBITMAPMARGIN
, 0, 0);
8443 if ((hdi
.fmt
& HDF_IMAGE
) && himl
)
8445 INT icon_cx
, icon_cy
;
8447 if (!ImageList_GetIconSize(himl
, &icon_cx
, &icon_cy
))
8448 cx
+= icon_cx
+ 2*bitmap_margin
;
8450 else if (hdi
.fmt
& HDF_BITMAP
)
8454 GetObjectW(hdi
.hbm
, sizeof(BITMAP
), &bmp
);
8455 cx
+= bmp
.bmWidth
+ 2*bitmap_margin
;
8458 SelectObject(hdc
, old_font
);
8459 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
8461 cx
= max (cx
, max_cx
);
8465 if (cx
< 0) return FALSE
;
8467 /* call header to update the column change */
8468 hdi
.mask
= HDI_WIDTH
;
8469 hdi
.cxy
= max(cx
, LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->cxMin
);
8470 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
8471 return SendMessageW(infoPtr
->hwndHeader
, HDM_SETITEMW
, nColumn
, (LPARAM
)&hdi
);
8475 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
8478 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
8481 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
8483 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
8484 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
8487 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
8488 ILC_COLOR
| ILC_MASK
, 2, 2);
8489 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
8490 hdc
= CreateCompatibleDC(hdc_wnd
);
8491 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
8492 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
8493 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
8495 rc
.left
= rc
.top
= 0;
8496 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
8497 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
8499 hbm_orig
= SelectObject(hdc
, hbm_mask
);
8500 FillRect(hdc
, &rc
, hbr_white
);
8501 InflateRect(&rc
, -2, -2);
8502 FillRect(hdc
, &rc
, hbr_black
);
8504 SelectObject(hdc
, hbm_im
);
8505 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
8506 SelectObject(hdc
, hbm_orig
);
8507 ImageList_Add(himl
, hbm_im
, hbm_mask
);
8509 SelectObject(hdc
, hbm_im
);
8510 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
8511 SelectObject(hdc
, hbm_orig
);
8512 ImageList_Add(himl
, hbm_im
, hbm_mask
);
8514 DeleteObject(hbm_mask
);
8515 DeleteObject(hbm_im
);
8523 * Sets the extended listview style.
8526 * [I] infoPtr : valid pointer to the listview structure
8528 * [I] dwStyle : style
8531 * SUCCESS : previous style
8534 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD mask
, DWORD ex_style
)
8536 DWORD old_ex_style
= infoPtr
->dwLvExStyle
;
8538 TRACE("mask=0x%08x, ex_style=0x%08x\n", mask
, ex_style
);
8542 infoPtr
->dwLvExStyle
= (old_ex_style
& ~mask
) | (ex_style
& mask
);
8544 infoPtr
->dwLvExStyle
= ex_style
;
8546 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_CHECKBOXES
)
8548 HIMAGELIST himl
= 0;
8549 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8552 item
.mask
= LVIF_STATE
;
8553 item
.stateMask
= LVIS_STATEIMAGEMASK
;
8554 item
.state
= INDEXTOSTATEIMAGEMASK(1);
8555 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
8557 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
8558 if(!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8559 ImageList_Destroy(infoPtr
->himlState
);
8561 himl
= LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
8562 /* checkbox list replaces previous custom list or... */
8563 if(((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) &&
8564 !(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
)) ||
8565 /* ...previous was checkbox list */
8566 (old_ex_style
& LVS_EX_CHECKBOXES
))
8567 ImageList_Destroy(himl
);
8570 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_HEADERDRAGDROP
)
8574 /* if not already created */
8575 LISTVIEW_CreateHeader(infoPtr
);
8577 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
8578 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
8579 style
|= HDS_DRAGDROP
;
8581 style
&= ~HDS_DRAGDROP
;
8582 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
);
8585 /* GRIDLINES adds decoration at top so changes sizes */
8586 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_GRIDLINES
)
8588 LISTVIEW_CreateHeader(infoPtr
);
8589 LISTVIEW_UpdateSize(infoPtr
);
8592 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_FULLROWSELECT
)
8594 LISTVIEW_CreateHeader(infoPtr
);
8597 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_TRANSPARENTBKGND
)
8599 if (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTBKGND
)
8600 LISTVIEW_SetBkColor(infoPtr
, CLR_NONE
);
8603 if((infoPtr
->dwLvExStyle
^ old_ex_style
) & LVS_EX_HEADERINALLVIEWS
)
8605 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
)
8606 LISTVIEW_CreateHeader(infoPtr
);
8608 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8609 LISTVIEW_UpdateSize(infoPtr
);
8610 LISTVIEW_UpdateScroll(infoPtr
);
8613 LISTVIEW_InvalidateList(infoPtr
);
8614 return old_ex_style
;
8619 * Sets the new hot cursor used during hot tracking and hover selection.
8622 * [I] infoPtr : valid pointer to the listview structure
8623 * [I] hCursor : the new hot cursor handle
8626 * Returns the previous hot cursor
8628 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
8630 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
8632 infoPtr
->hHotCursor
= hCursor
;
8640 * Sets the hot item index.
8643 * [I] infoPtr : valid pointer to the listview structure
8644 * [I] iIndex : index
8647 * SUCCESS : previous hot item index
8648 * FAILURE : -1 (no hot item)
8650 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
8652 INT iOldIndex
= infoPtr
->nHotItem
;
8654 infoPtr
->nHotItem
= iIndex
;
8662 * Sets the amount of time the cursor must hover over an item before it is selected.
8665 * [I] infoPtr : valid pointer to the listview structure
8666 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8669 * Returns the previous hover time
8671 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
8673 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
8675 infoPtr
->dwHoverTime
= dwHoverTime
;
8677 return oldHoverTime
;
8682 * Sets spacing for icons of LVS_ICON style.
8685 * [I] infoPtr : valid pointer to the listview structure
8686 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8687 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8690 * MAKELONG(oldcx, oldcy)
8692 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
8694 INT iconWidth
= 0, iconHeight
= 0;
8695 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
8697 TRACE("requested=(%d,%d)\n", cx
, cy
);
8699 /* set to defaults, if instructed to */
8700 if (cx
== -1 && cy
== -1)
8702 infoPtr
->autoSpacing
= TRUE
;
8703 if (infoPtr
->himlNormal
)
8704 ImageList_GetIconSize(infoPtr
->himlNormal
, &iconWidth
, &iconHeight
);
8705 cx
= GetSystemMetrics(SM_CXICONSPACING
) - GetSystemMetrics(SM_CXICON
) + iconWidth
;
8706 cy
= GetSystemMetrics(SM_CYICONSPACING
) - GetSystemMetrics(SM_CYICON
) + iconHeight
;
8709 infoPtr
->autoSpacing
= FALSE
;
8711 /* if 0 then keep width */
8713 infoPtr
->iconSpacing
.cx
= cx
;
8715 /* if 0 then keep height */
8717 infoPtr
->iconSpacing
.cy
= cy
;
8719 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8720 LOWORD(oldspacing
), HIWORD(oldspacing
), infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
,
8721 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
8722 infoPtr
->ntmHeight
);
8724 /* these depend on the iconSpacing */
8725 LISTVIEW_UpdateItemSize(infoPtr
);
8730 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL is_small
)
8734 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
8741 size
->cx
= GetSystemMetrics(is_small
? SM_CXSMICON
: SM_CXICON
);
8742 size
->cy
= GetSystemMetrics(is_small
? SM_CYSMICON
: SM_CYICON
);
8751 * [I] infoPtr : valid pointer to the listview structure
8752 * [I] nType : image list type
8753 * [I] himl : image list handle
8756 * SUCCESS : old image list
8759 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
8761 INT oldHeight
= infoPtr
->nItemHeight
;
8762 HIMAGELIST himlOld
= 0;
8764 TRACE("(nType=%d, himl=%p)\n", nType
, himl
);
8769 himlOld
= infoPtr
->himlNormal
;
8770 infoPtr
->himlNormal
= himl
;
8771 if (infoPtr
->uView
== LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
8772 if (infoPtr
->autoSpacing
)
8773 LISTVIEW_SetIconSpacing(infoPtr
, -1, -1);
8777 himlOld
= infoPtr
->himlSmall
;
8778 infoPtr
->himlSmall
= himl
;
8779 if (infoPtr
->uView
!= LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
8780 if (infoPtr
->hwndHeader
)
8781 SendMessageW(infoPtr
->hwndHeader
, HDM_SETIMAGELIST
, 0, (LPARAM
)himl
);
8785 himlOld
= infoPtr
->himlState
;
8786 infoPtr
->himlState
= himl
;
8787 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
8788 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
8792 ERR("Unknown icon type=%d\n", nType
);
8796 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
8797 if (infoPtr
->nItemHeight
!= oldHeight
)
8798 LISTVIEW_UpdateScroll(infoPtr
);
8805 * Preallocates memory (does *not* set the actual count of items !)
8808 * [I] infoPtr : valid pointer to the listview structure
8809 * [I] nItems : item count (projected number of items to allocate)
8810 * [I] dwFlags : update flags
8816 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
8818 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
8820 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
8822 INT nOldCount
= infoPtr
->nItemCount
;
8824 if (nItems
< nOldCount
)
8826 RANGE range
= { nItems
, nOldCount
};
8827 ranges_del(infoPtr
->selectionRanges
, range
);
8828 if (infoPtr
->nFocusedItem
>= nItems
)
8830 LISTVIEW_SetItemFocus(infoPtr
, -1);
8831 SetRectEmpty(&infoPtr
->rcFocus
);
8835 infoPtr
->nItemCount
= nItems
;
8836 LISTVIEW_UpdateScroll(infoPtr
);
8838 /* the flags are valid only in ownerdata report and list modes */
8839 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
) dwFlags
= 0;
8841 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
8842 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
8844 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
8845 LISTVIEW_InvalidateList(infoPtr
);
8852 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8853 nFrom
= min(nOldCount
, nItems
);
8854 nTo
= max(nOldCount
, nItems
);
8856 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
8859 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
8860 rcErase
.right
= infoPtr
->nItemWidth
;
8861 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
8862 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8863 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8864 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8866 else /* LV_VIEW_LIST */
8868 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
8870 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
8871 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
8872 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
8873 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
8874 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8875 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8876 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8878 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
8880 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
8881 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
8882 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
8883 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
8884 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
8890 /* According to MSDN for non-LVS_OWNERDATA this is just
8891 * a performance issue. The control allocates its internal
8892 * data structures for the number of items specified. It
8893 * cuts down on the number of memory allocations. Therefore
8894 * we will just issue a WARN here
8896 WARN("for non-ownerdata performance option not implemented.\n");
8904 * Sets the position of an item.
8907 * [I] infoPtr : valid pointer to the listview structure
8908 * [I] nItem : item index
8909 * [I] pt : coordinate
8915 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*pt
)
8919 TRACE("(nItem=%d, pt=%s)\n", nItem
, wine_dbgstr_point(pt
));
8921 if (!pt
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
8922 !(infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)) return FALSE
;
8925 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
8927 /* This point value seems to be an undocumented feature.
8928 * The best guess is that it means either at the origin,
8929 * or at true beginning of the list. I will assume the origin. */
8930 if ((Pt
.x
== -1) && (Pt
.y
== -1))
8933 if (infoPtr
->uView
== LV_VIEW_ICON
)
8935 Pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
8936 Pt
.y
-= ICON_TOP_PADDING
;
8941 infoPtr
->bAutoarrange
= FALSE
;
8943 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &Pt
, FALSE
);
8948 * Sets the state of one or many items.
8951 * [I] infoPtr : valid pointer to the listview structure
8952 * [I] nItem : item index
8953 * [I] item : item or subitem info
8959 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*item
)
8964 if (!item
) return FALSE
;
8966 lvItem
.iItem
= nItem
;
8967 lvItem
.iSubItem
= 0;
8968 lvItem
.mask
= LVIF_STATE
;
8969 lvItem
.state
= item
->state
;
8970 lvItem
.stateMask
= item
->stateMask
;
8971 TRACE("item=%s\n", debuglvitem_t(&lvItem
, TRUE
));
8978 /* special case optimization for recurring attempt to deselect all */
8979 if (lvItem
.state
== 0 && lvItem
.stateMask
== LVIS_SELECTED
&& !LISTVIEW_GetSelectedCount(infoPtr
))
8982 /* select all isn't allowed in LVS_SINGLESEL */
8983 if ((lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) && (infoPtr
->dwStyle
& LVS_SINGLESEL
))
8986 /* focus all isn't allowed */
8987 if (lvItem
.state
& lvItem
.stateMask
& LVIS_FOCUSED
) return FALSE
;
8989 notify
= infoPtr
->bDoChangeNotify
;
8990 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
8992 infoPtr
->bDoChangeNotify
= FALSE
;
8993 if (!(lvItem
.state
& LVIS_SELECTED
) && LISTVIEW_GetSelectedCount(infoPtr
))
8994 oldstate
|= LVIS_SELECTED
;
8995 if (infoPtr
->nFocusedItem
!= -1) oldstate
|= LVIS_FOCUSED
;
8998 /* apply to all items */
8999 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
9000 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) ret
= FALSE
;
9002 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
9006 infoPtr
->bDoChangeNotify
= notify
;
9010 nmlv
.uNewState
= lvItem
.state
& lvItem
.stateMask
;
9011 nmlv
.uOldState
= oldstate
& lvItem
.stateMask
;
9012 nmlv
.uChanged
= LVIF_STATE
;
9013 nmlv
.ptAction
.x
= nmlv
.ptAction
.y
= 0;
9016 notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
9020 ret
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
9027 * Sets the text of an item or subitem.
9030 * [I] hwnd : window handle
9031 * [I] nItem : item index
9032 * [I] lpLVItem : item or subitem info
9033 * [I] isW : TRUE if input is Unicode
9039 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
9043 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
9044 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
9046 lvItem
.iItem
= nItem
;
9047 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
9048 lvItem
.mask
= LVIF_TEXT
;
9049 lvItem
.pszText
= lpLVItem
->pszText
;
9050 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
9052 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
9054 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
9059 * Set item index that marks the start of a multiple selection.
9062 * [I] infoPtr : valid pointer to the listview structure
9063 * [I] nIndex : index
9066 * Index number or -1 if there is no selection mark.
9068 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
9070 INT nOldIndex
= infoPtr
->nSelectionMark
;
9072 TRACE("(nIndex=%d)\n", nIndex
);
9074 infoPtr
->nSelectionMark
= nIndex
;
9081 * Sets the text background color.
9084 * [I] infoPtr : valid pointer to the listview structure
9085 * [I] color : text background color
9091 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF color
)
9093 TRACE("(color=%x)\n", color
);
9095 infoPtr
->clrTextBk
= color
;
9101 * Sets the text foreground color.
9104 * [I] infoPtr : valid pointer to the listview structure
9105 * [I] color : text color
9111 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF color
)
9113 TRACE("(color=%x)\n", color
);
9115 infoPtr
->clrText
= color
;
9121 * Sets new ToolTip window to ListView control.
9124 * [I] infoPtr : valid pointer to the listview structure
9125 * [I] hwndNewToolTip : handle to new ToolTip
9130 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
9132 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
9133 infoPtr
->hwndToolTip
= hwndNewToolTip
;
9134 return hwndOldToolTip
;
9139 * sets the Unicode character format flag for the control
9141 * [I] infoPtr :valid pointer to the listview structure
9142 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
9145 * Old Unicode Format
9147 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL unicode
)
9149 SHORT rc
= infoPtr
->notifyFormat
;
9150 infoPtr
->notifyFormat
= (unicode
) ? NFR_UNICODE
: NFR_ANSI
;
9151 return rc
== NFR_UNICODE
;
9156 * sets the control view mode
9158 * [I] infoPtr :valid pointer to the listview structure
9159 * [I] nView :new view mode value
9165 static INT
LISTVIEW_SetView(LISTVIEW_INFO
*infoPtr
, DWORD nView
)
9169 if (infoPtr
->uView
== nView
) return 1;
9171 if ((INT
)nView
< 0 || nView
> LV_VIEW_MAX
) return -1;
9172 if (nView
== LV_VIEW_TILE
)
9174 FIXME("View LV_VIEW_TILE unimplemented\n");
9178 infoPtr
->uView
= nView
;
9180 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9181 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9183 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9184 SetRectEmpty(&infoPtr
->rcFocus
);
9186 himl
= (nView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9187 set_icon_size(&infoPtr
->iconSize
, himl
, nView
!= LV_VIEW_ICON
);
9192 case LV_VIEW_SMALLICON
:
9193 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9195 case LV_VIEW_DETAILS
:
9200 LISTVIEW_CreateHeader( infoPtr
);
9202 hl
.prc
= &infoPtr
->rcList
;
9204 SendMessageW(infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9205 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9206 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9213 LISTVIEW_UpdateItemSize(infoPtr
);
9214 LISTVIEW_UpdateSize(infoPtr
);
9215 LISTVIEW_UpdateScroll(infoPtr
);
9216 LISTVIEW_InvalidateList(infoPtr
);
9218 TRACE("nView=%d\n", nView
);
9223 /* LISTVIEW_SetWorkAreas */
9227 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
9230 * [I] first : pointer to first ITEM_INFO to compare
9231 * [I] second : pointer to second ITEM_INFO to compare
9232 * [I] lParam : HWND of control
9235 * if first comes before second : negative
9236 * if first comes after second : positive
9237 * if first and second are equivalent : zero
9239 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
9241 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
9242 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
9243 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
9245 /* Forward the call to the client defined callback */
9246 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
9251 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
9254 * [I] first : pointer to first ITEM_INFO to compare
9255 * [I] second : pointer to second ITEM_INFO to compare
9256 * [I] lParam : HWND of control
9259 * if first comes before second : negative
9260 * if first comes after second : positive
9261 * if first and second are equivalent : zero
9263 static INT WINAPI
LISTVIEW_CallBackCompareEx(LPVOID first
, LPVOID second
, LPARAM lParam
)
9265 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
9266 INT first_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, first
);
9267 INT second_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, second
);
9269 /* Forward the call to the client defined callback */
9270 return (infoPtr
->pfnCompare
)( first_idx
, second_idx
, infoPtr
->lParamSort
);
9275 * Sorts the listview items.
9278 * [I] infoPtr : valid pointer to the listview structure
9279 * [I] pfnCompare : application-defined value
9280 * [I] lParamSort : pointer to comparison callback
9281 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
9287 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
,
9288 LPARAM lParamSort
, BOOL IsEx
)
9292 LPVOID selectionMarkItem
= NULL
;
9293 LPVOID focusedItem
= NULL
;
9296 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
9298 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
9300 if (!pfnCompare
) return FALSE
;
9301 if (!infoPtr
->hdpaItems
) return FALSE
;
9303 /* if there are 0 or 1 items, there is no need to sort */
9304 if (infoPtr
->nItemCount
< 2) return TRUE
;
9306 /* clear selection */
9307 ranges_clear(infoPtr
->selectionRanges
);
9309 /* save selection mark and focused item */
9310 if (infoPtr
->nSelectionMark
>= 0)
9311 selectionMarkItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
);
9312 if (infoPtr
->nFocusedItem
>= 0)
9313 focusedItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
9315 infoPtr
->pfnCompare
= pfnCompare
;
9316 infoPtr
->lParamSort
= lParamSort
;
9318 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompareEx
, (LPARAM
)infoPtr
);
9320 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
9322 /* restore selection ranges */
9323 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
9325 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
9326 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
9328 if (lpItem
->state
& LVIS_SELECTED
)
9329 ranges_additem(infoPtr
->selectionRanges
, i
);
9331 /* restore selection mark and focused item */
9332 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
9333 infoPtr
->nFocusedItem
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, focusedItem
);
9335 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
9337 /* refresh the display */
9338 LISTVIEW_InvalidateList(infoPtr
);
9344 * Update theme handle after a theme change.
9347 * [I] infoPtr : valid pointer to the listview structure
9351 * FAILURE : something else
9353 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
9355 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
9356 CloseThemeData(theme
);
9357 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
9363 * Updates an items or rearranges the listview control.
9366 * [I] infoPtr : valid pointer to the listview structure
9367 * [I] nItem : item index
9373 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
9375 TRACE("(nItem=%d)\n", nItem
);
9377 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
9379 /* rearrange with default alignment style */
9380 if (is_autoarrange(infoPtr
))
9381 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9383 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
9390 * Draw the track line at the place defined in the infoPtr structure.
9391 * The line is drawn with a XOR pen so drawing the line for the second time
9392 * in the same place erases the line.
9395 * [I] infoPtr : valid pointer to the listview structure
9401 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
9405 if (infoPtr
->xTrackLine
== -1)
9408 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
9410 PatBlt( hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
,
9411 1, infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
, DSTINVERT
);
9412 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
9418 * Called when an edit control should be displayed. This function is called after
9419 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
9422 * [I] hwnd : Handle to the listview
9423 * [I] uMsg : WM_TIMER (ignored)
9424 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
9425 * [I] dwTimer : The elapsed time (ignored)
9430 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
9432 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
9433 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9435 KillTimer(hwnd
, idEvent
);
9436 editItem
->fEnabled
= FALSE
;
9437 /* check if the item is still selected */
9438 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
9439 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
9444 * Creates the listview control - the WM_NCCREATE phase.
9447 * [I] hwnd : window handle
9448 * [I] lpcs : the create parameters
9454 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
9456 LISTVIEW_INFO
*infoPtr
;
9459 TRACE("(lpcs=%p)\n", lpcs
);
9461 /* initialize info pointer */
9462 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
9463 if (!infoPtr
) return FALSE
;
9465 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
9467 infoPtr
->hwndSelf
= hwnd
;
9468 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
9469 map_style_view(infoPtr
);
9470 /* determine the type of structures to use */
9471 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
9472 /* infoPtr->notifyFormat will be filled in WM_CREATE */
9474 /* initialize color information */
9475 infoPtr
->clrBk
= CLR_NONE
;
9476 infoPtr
->clrText
= CLR_DEFAULT
;
9477 infoPtr
->clrTextBk
= CLR_DEFAULT
;
9478 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
9480 infoPtr
->bDefaultBkColor
= TRUE
;
9483 /* set default values */
9484 infoPtr
->nFocusedItem
= -1;
9485 infoPtr
->nSelectionMark
= -1;
9486 infoPtr
->nHotItem
= -1;
9487 infoPtr
->bRedraw
= TRUE
;
9488 infoPtr
->bNoItemMetrics
= TRUE
;
9489 infoPtr
->bDoChangeNotify
= TRUE
;
9490 infoPtr
->autoSpacing
= TRUE
;
9491 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
) - GetSystemMetrics(SM_CXICON
);
9492 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
) - GetSystemMetrics(SM_CYICON
);
9493 infoPtr
->nEditLabelItem
= -1;
9494 infoPtr
->nLButtonDownItem
= -1;
9495 infoPtr
->dwHoverTime
= HOVER_DEFAULT
; /* default system hover time */
9496 infoPtr
->cWheelRemainder
= 0;
9497 infoPtr
->nMeasureItemHeight
= 0;
9498 infoPtr
->xTrackLine
= -1; /* no track line */
9499 infoPtr
->itemEdit
.fEnabled
= FALSE
;
9500 infoPtr
->iVersion
= COMCTL32_VERSION
;
9501 infoPtr
->colRectsDirty
= FALSE
;
9503 /* get default font (icon title) */
9504 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
9505 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
9506 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
9507 LISTVIEW_SaveTextMetrics(infoPtr
);
9509 /* allocate memory for the data structure */
9510 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
9511 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
9512 if (!(infoPtr
->hdpaItemIds
= DPA_Create(10))) goto fail
;
9513 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
9514 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
9515 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
9519 DestroyWindow(infoPtr
->hwndHeader
);
9520 ranges_destroy(infoPtr
->selectionRanges
);
9521 DPA_Destroy(infoPtr
->hdpaItems
);
9522 DPA_Destroy(infoPtr
->hdpaItemIds
);
9523 DPA_Destroy(infoPtr
->hdpaPosX
);
9524 DPA_Destroy(infoPtr
->hdpaPosY
);
9525 DPA_Destroy(infoPtr
->hdpaColumns
);
9532 * Creates the listview control - the WM_CREATE phase. Most of the data is
9533 * already set up in LISTVIEW_NCCreate
9536 * [I] hwnd : window handle
9537 * [I] lpcs : the create parameters
9543 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
9545 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9547 TRACE("(lpcs=%p, style=0x%08x)\n", lpcs
, lpcs
->style
);
9549 infoPtr
->dwStyle
= lpcs
->style
;
9550 map_style_view(infoPtr
);
9552 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
9553 (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9554 /* on error defaulting to ANSI notifications */
9555 if (infoPtr
->notifyFormat
== 0) infoPtr
->notifyFormat
= NFR_ANSI
;
9556 TRACE("notify format=%d\n", infoPtr
->notifyFormat
);
9558 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && (lpcs
->style
& WS_VISIBLE
))
9560 if (LISTVIEW_CreateHeader(infoPtr
) < 0) return -1;
9563 infoPtr
->hwndHeader
= 0;
9565 /* init item size to avoid division by 0 */
9566 LISTVIEW_UpdateItemSize (infoPtr
);
9567 LISTVIEW_UpdateSize (infoPtr
);
9569 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
9571 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
) && (WS_VISIBLE
& lpcs
->style
))
9573 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
9575 LISTVIEW_UpdateScroll(infoPtr
);
9576 /* send WM_MEASUREITEM notification */
9577 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) notify_measureitem(infoPtr
);
9580 OpenThemeData(hwnd
, themeClass
);
9582 /* initialize the icon sizes */
9583 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, infoPtr
->uView
!= LV_VIEW_ICON
);
9584 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
9590 * Destroys the listview control.
9593 * [I] infoPtr : valid pointer to the listview structure
9599 static LRESULT
LISTVIEW_Destroy(LISTVIEW_INFO
*infoPtr
)
9601 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
9602 CloseThemeData(theme
);
9604 /* delete all items */
9605 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
9612 * Enables the listview control.
9615 * [I] infoPtr : valid pointer to the listview structure
9616 * [I] bEnable : specifies whether to enable or disable the window
9622 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
)
9624 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
9625 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
9631 * Erases the background of the listview control.
9634 * [I] infoPtr : valid pointer to the listview structure
9635 * [I] hdc : device context handle
9641 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9645 TRACE("(hdc=%p)\n", hdc
);
9647 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
9649 if (infoPtr
->clrBk
== CLR_NONE
)
9651 if (infoPtr
->dwLvExStyle
& LVS_EX_TRANSPARENTBKGND
)
9652 return SendMessageW(infoPtr
->hwndNotify
, WM_PRINTCLIENT
,
9653 (WPARAM
)hdc
, PRF_ERASEBKGND
);
9655 return SendMessageW(infoPtr
->hwndNotify
, WM_ERASEBKGND
, (WPARAM
)hdc
, 0);
9658 /* for double buffered controls we need to do this during refresh */
9659 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
9661 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
9667 * Helper function for LISTVIEW_[HV]Scroll *only*.
9668 * Performs vertical/horizontal scrolling by a give amount.
9671 * [I] infoPtr : valid pointer to the listview structure
9672 * [I] dx : amount of horizontal scroll
9673 * [I] dy : amount of vertical scroll
9675 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
9677 /* now we can scroll the list */
9678 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
9679 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
9680 /* if we have focus, adjust rect */
9681 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
9682 UpdateWindow(infoPtr
->hwndSelf
);
9687 * Performs vertical scrolling.
9690 * [I] infoPtr : valid pointer to the listview structure
9691 * [I] nScrollCode : scroll code
9692 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9693 * [I] hScrollWnd : scrollbar control window handle
9699 * SB_LINEUP/SB_LINEDOWN:
9700 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
9701 * for LVS_REPORT is 1 line
9702 * for LVS_LIST cannot occur
9705 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
9708 INT nOldScrollPos
, nNewScrollPos
;
9709 SCROLLINFO scrollInfo
;
9712 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
9713 debugscrollcode(nScrollCode
), nScrollDiff
);
9715 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9717 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
9718 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
9720 is_an_icon
= ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
));
9722 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
9724 nOldScrollPos
= scrollInfo
.nPos
;
9725 switch (nScrollCode
)
9731 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
9735 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
9739 nScrollDiff
= -scrollInfo
.nPage
;
9743 nScrollDiff
= scrollInfo
.nPage
;
9746 case SB_THUMBPOSITION
:
9748 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
9755 /* quit right away if pos isn't changing */
9756 if (nScrollDiff
== 0) return 0;
9758 /* calculate new position, and handle overflows */
9759 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
9760 if (nScrollDiff
> 0) {
9761 if (nNewScrollPos
< nOldScrollPos
||
9762 nNewScrollPos
> scrollInfo
.nMax
)
9763 nNewScrollPos
= scrollInfo
.nMax
;
9765 if (nNewScrollPos
> nOldScrollPos
||
9766 nNewScrollPos
< scrollInfo
.nMin
)
9767 nNewScrollPos
= scrollInfo
.nMin
;
9770 /* set the new position, and reread in case it changed */
9771 scrollInfo
.fMask
= SIF_POS
;
9772 scrollInfo
.nPos
= nNewScrollPos
;
9773 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
9775 /* carry on only if it really changed */
9776 if (nNewScrollPos
== nOldScrollPos
) return 0;
9778 /* now adjust to client coordinates */
9779 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
9780 if (infoPtr
->uView
== LV_VIEW_DETAILS
) nScrollDiff
*= infoPtr
->nItemHeight
;
9782 /* and scroll the window */
9783 scroll_list(infoPtr
, 0, nScrollDiff
);
9790 * Performs horizontal scrolling.
9793 * [I] infoPtr : valid pointer to the listview structure
9794 * [I] nScrollCode : scroll code
9795 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9796 * [I] hScrollWnd : scrollbar control window handle
9802 * SB_LINELEFT/SB_LINERIGHT:
9803 * for LVS_ICON, LVS_SMALLICON 1 pixel
9804 * for LVS_REPORT is 1 pixel
9805 * for LVS_LIST is 1 column --> which is a 1 because the
9806 * scroll is based on columns not pixels
9809 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
9812 INT nOldScrollPos
, nNewScrollPos
;
9813 SCROLLINFO scrollInfo
;
9816 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
9817 debugscrollcode(nScrollCode
), nScrollDiff
);
9819 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9821 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
9822 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
9824 is_an_icon
= ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
));
9826 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
9828 nOldScrollPos
= scrollInfo
.nPos
;
9830 switch (nScrollCode
)
9836 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
9840 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
9844 nScrollDiff
= -scrollInfo
.nPage
;
9848 nScrollDiff
= scrollInfo
.nPage
;
9851 case SB_THUMBPOSITION
:
9853 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
9860 /* quit right away if pos isn't changing */
9861 if (nScrollDiff
== 0) return 0;
9863 /* calculate new position, and handle overflows */
9864 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
9865 if (nScrollDiff
> 0) {
9866 if (nNewScrollPos
< nOldScrollPos
||
9867 nNewScrollPos
> scrollInfo
.nMax
)
9868 nNewScrollPos
= scrollInfo
.nMax
;
9870 if (nNewScrollPos
> nOldScrollPos
||
9871 nNewScrollPos
< scrollInfo
.nMin
)
9872 nNewScrollPos
= scrollInfo
.nMin
;
9875 /* set the new position, and reread in case it changed */
9876 scrollInfo
.fMask
= SIF_POS
;
9877 scrollInfo
.nPos
= nNewScrollPos
;
9878 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
9880 /* carry on only if it really changed */
9881 if (nNewScrollPos
== nOldScrollPos
) return 0;
9883 if (infoPtr
->hwndHeader
) LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
9885 /* now adjust to client coordinates */
9886 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
9887 if (infoPtr
->uView
== LV_VIEW_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
9889 /* and scroll the window */
9890 scroll_list(infoPtr
, nScrollDiff
, 0);
9895 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
9897 UINT pulScrollLines
= 3;
9899 TRACE("(wheelDelta=%d)\n", wheelDelta
);
9901 switch(infoPtr
->uView
)
9904 case LV_VIEW_SMALLICON
:
9906 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9907 * should be fixed in the future.
9909 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (wheelDelta
> 0) ?
9910 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
);
9913 case LV_VIEW_DETAILS
:
9914 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
9916 /* if scrolling changes direction, ignore left overs */
9917 if ((wheelDelta
< 0 && infoPtr
->cWheelRemainder
< 0) ||
9918 (wheelDelta
> 0 && infoPtr
->cWheelRemainder
> 0))
9919 infoPtr
->cWheelRemainder
+= wheelDelta
;
9921 infoPtr
->cWheelRemainder
= wheelDelta
;
9922 if (infoPtr
->cWheelRemainder
&& pulScrollLines
)
9925 pulScrollLines
= min((UINT
)LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
9926 cLineScroll
= pulScrollLines
* (float)infoPtr
->cWheelRemainder
/ WHEEL_DELTA
;
9927 infoPtr
->cWheelRemainder
-= WHEEL_DELTA
* cLineScroll
/ (int)pulScrollLines
;
9928 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, -cLineScroll
);
9933 LISTVIEW_HScroll(infoPtr
, (wheelDelta
> 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0);
9944 * [I] infoPtr : valid pointer to the listview structure
9945 * [I] nVirtualKey : virtual key
9946 * [I] lKeyData : key data
9951 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
9953 HWND hwndSelf
= infoPtr
->hwndSelf
;
9955 NMLVKEYDOWN nmKeyDown
;
9957 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
9959 /* send LVN_KEYDOWN notification */
9960 nmKeyDown
.wVKey
= nVirtualKey
;
9961 nmKeyDown
.flags
= 0;
9962 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
9963 if (!IsWindow(hwndSelf
))
9966 switch (nVirtualKey
)
9969 nItem
= infoPtr
->nFocusedItem
;
9970 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
9971 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
9975 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
9977 if (!notify(infoPtr
, NM_RETURN
)) return 0;
9978 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
9983 if (infoPtr
->nItemCount
> 0)
9988 if (infoPtr
->nItemCount
> 0)
9989 nItem
= infoPtr
->nItemCount
- 1;
9993 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
9997 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
10001 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
10005 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
10009 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10011 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
10012 if (infoPtr
->nFocusedItem
== topidx
)
10013 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
10018 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
10019 * LISTVIEW_GetCountPerRow(infoPtr
);
10020 if(nItem
< 0) nItem
= 0;
10024 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10026 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
10027 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
10028 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
10029 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
10031 nItem
= topidx
+ cnt
- 1;
10034 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
10035 * LISTVIEW_GetCountPerRow(infoPtr
);
10036 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
10040 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
10041 LISTVIEW_KeySelection(infoPtr
, nItem
, nVirtualKey
== VK_SPACE
);
10051 * [I] infoPtr : valid pointer to the listview structure
10056 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
10060 /* drop any left over scroll amount */
10061 infoPtr
->cWheelRemainder
= 0;
10063 /* if we did not have the focus, there's nothing more to do */
10064 if (!infoPtr
->bFocus
) return 0;
10066 /* send NM_KILLFOCUS notification */
10067 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
10069 /* if we have a focus rectangle, get rid of it */
10070 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
10072 /* if have a marquee selection, stop it */
10073 if (infoPtr
->bMarqueeSelect
)
10075 /* Remove the marquee rectangle and release our mouse capture */
10076 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeRect
);
10079 SetRect(&infoPtr
->marqueeRect
, 0, 0, 0, 0);
10081 infoPtr
->bMarqueeSelect
= FALSE
;
10082 infoPtr
->bScrolling
= FALSE
;
10083 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
10086 /* set window focus flag */
10087 infoPtr
->bFocus
= FALSE
;
10089 /* invalidate the selected items before resetting focus flag */
10090 LISTVIEW_InvalidateSelectedItems(infoPtr
);
10097 * Processes double click messages (left mouse button).
10100 * [I] infoPtr : valid pointer to the listview structure
10101 * [I] wKey : key flag
10102 * [I] x,y : mouse coordinate
10107 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10109 LVHITTESTINFO htInfo
;
10111 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10113 /* Cancel the item edition if any */
10114 if (infoPtr
->itemEdit
.fEnabled
)
10116 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
10117 infoPtr
->itemEdit
.fEnabled
= FALSE
;
10120 /* send NM_RELEASEDCAPTURE notification */
10121 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10126 /* send NM_DBLCLK notification */
10127 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
10128 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
10130 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
10131 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
10136 static LRESULT
LISTVIEW_TrackMouse(const LISTVIEW_INFO
*infoPtr
, POINT pt
)
10141 r
.top
= r
.bottom
= pt
.y
;
10142 r
.left
= r
.right
= pt
.x
;
10144 InflateRect(&r
, GetSystemMetrics(SM_CXDRAG
), GetSystemMetrics(SM_CYDRAG
));
10146 SetCapture(infoPtr
->hwndSelf
);
10150 if (PeekMessageW(&msg
, 0, 0, 0, PM_REMOVE
| PM_NOYIELD
))
10152 if (msg
.message
== WM_MOUSEMOVE
)
10154 pt
.x
= (short)LOWORD(msg
.lParam
);
10155 pt
.y
= (short)HIWORD(msg
.lParam
);
10156 if (PtInRect(&r
, pt
))
10164 else if (msg
.message
>= WM_LBUTTONDOWN
&&
10165 msg
.message
<= WM_RBUTTONDBLCLK
)
10170 DispatchMessageW(&msg
);
10173 if (GetCapture() != infoPtr
->hwndSelf
)
10184 * Processes mouse down messages (left mouse button).
10187 * infoPtr [I ] valid pointer to the listview structure
10188 * wKey [I ] key flag
10189 * x,y [I ] mouse coordinate
10194 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10196 LVHITTESTINFO lvHitTestInfo
;
10197 static BOOL bGroupSelect
= TRUE
;
10198 POINT pt
= { x
, y
};
10201 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10203 /* send NM_RELEASEDCAPTURE notification */
10204 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10206 /* set left button down flag and record the click position */
10207 infoPtr
->bLButtonDown
= TRUE
;
10208 infoPtr
->ptClickPos
= pt
;
10209 infoPtr
->bDragging
= FALSE
;
10210 infoPtr
->bMarqueeSelect
= FALSE
;
10211 infoPtr
->bScrolling
= FALSE
;
10213 lvHitTestInfo
.pt
.x
= x
;
10214 lvHitTestInfo
.pt
.y
= y
;
10216 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
10217 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
10218 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
10220 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
10222 toggle_checkbox_state(infoPtr
, nItem
);
10226 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
10228 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
10229 infoPtr
->nEditLabelItem
= nItem
;
10231 LISTVIEW_SetSelection(infoPtr
, nItem
);
10235 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
10239 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
10240 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
10241 infoPtr
->nSelectionMark
= nItem
;
10247 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
10248 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
10250 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
10251 infoPtr
->nSelectionMark
= nItem
;
10254 else if (wKey
& MK_CONTROL
)
10258 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
10260 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
10261 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
10262 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
10263 infoPtr
->nSelectionMark
= nItem
;
10265 else if (wKey
& MK_SHIFT
)
10267 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
10271 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
10273 infoPtr
->nEditLabelItem
= nItem
;
10274 infoPtr
->nLButtonDownItem
= nItem
;
10276 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
10279 /* set selection (clears other pre-existing selections) */
10280 LISTVIEW_SetSelection(infoPtr
, nItem
);
10284 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
10285 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
10289 if (!infoPtr
->bFocus
)
10290 SetFocus(infoPtr
->hwndSelf
);
10292 /* remove all selections */
10293 if (!(wKey
& MK_CONTROL
) && !(wKey
& MK_SHIFT
))
10294 LISTVIEW_DeselectAll(infoPtr
);
10303 * Processes mouse up messages (left mouse button).
10306 * infoPtr [I ] valid pointer to the listview structure
10307 * wKey [I ] key flag
10308 * x,y [I ] mouse coordinate
10313 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10315 LVHITTESTINFO lvHitTestInfo
;
10317 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey
, x
, y
);
10319 if (!infoPtr
->bLButtonDown
) return 0;
10321 lvHitTestInfo
.pt
.x
= x
;
10322 lvHitTestInfo
.pt
.y
= y
;
10324 /* send NM_CLICK notification */
10325 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
10326 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
10328 /* set left button flag */
10329 infoPtr
->bLButtonDown
= FALSE
;
10331 /* set a single selection, reset others */
10332 if(lvHitTestInfo
.iItem
== infoPtr
->nLButtonDownItem
&& lvHitTestInfo
.iItem
!= -1)
10333 LISTVIEW_SetSelection(infoPtr
, infoPtr
->nLButtonDownItem
);
10334 infoPtr
->nLButtonDownItem
= -1;
10336 if (infoPtr
->bDragging
|| infoPtr
->bMarqueeSelect
)
10338 /* Remove the marquee rectangle and release our mouse capture */
10339 if (infoPtr
->bMarqueeSelect
)
10341 LISTVIEW_InvalidateRect(infoPtr
, &infoPtr
->marqueeDrawRect
);
10345 SetRect(&infoPtr
->marqueeRect
, 0, 0, 0, 0);
10346 SetRect(&infoPtr
->marqueeDrawRect
, 0, 0, 0, 0);
10348 infoPtr
->bDragging
= FALSE
;
10349 infoPtr
->bMarqueeSelect
= FALSE
;
10350 infoPtr
->bScrolling
= FALSE
;
10352 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
) infoPtr
);
10356 /* if we clicked on a selected item, edit the label */
10357 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
10359 /* we want to make sure the user doesn't want to do a double click. So we will
10360 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
10362 infoPtr
->itemEdit
.fEnabled
= TRUE
;
10363 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
10364 SetTimer(infoPtr
->hwndSelf
,
10365 (UINT_PTR
)&infoPtr
->itemEdit
,
10366 GetDoubleClickTime(),
10367 LISTVIEW_DelayedEditItem
);
10370 if (!infoPtr
->bFocus
)
10371 SetFocus(infoPtr
->hwndSelf
);
10378 * Destroys the listview control (called after WM_DESTROY).
10381 * [I] infoPtr : valid pointer to the listview structure
10386 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
10392 /* destroy data structure */
10393 DPA_Destroy(infoPtr
->hdpaItems
);
10394 DPA_Destroy(infoPtr
->hdpaItemIds
);
10395 DPA_Destroy(infoPtr
->hdpaPosX
);
10396 DPA_Destroy(infoPtr
->hdpaPosY
);
10398 for (i
= 0; i
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); i
++)
10399 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, i
));
10400 DPA_Destroy(infoPtr
->hdpaColumns
);
10401 ranges_destroy(infoPtr
->selectionRanges
);
10403 /* destroy image lists */
10404 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
10406 ImageList_Destroy(infoPtr
->himlNormal
);
10407 ImageList_Destroy(infoPtr
->himlSmall
);
10408 ImageList_Destroy(infoPtr
->himlState
);
10411 /* destroy font, bkgnd brush */
10412 infoPtr
->hFont
= 0;
10413 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
10414 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
10416 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
10418 /* free listview info pointer*/
10426 * Handles notifications.
10429 * [I] infoPtr : valid pointer to the listview structure
10430 * [I] lpnmhdr : notification information
10435 static LRESULT
LISTVIEW_Notify(LISTVIEW_INFO
*infoPtr
, NMHDR
*lpnmhdr
)
10439 TRACE("(lpnmhdr=%p)\n", lpnmhdr
);
10441 if (!lpnmhdr
|| lpnmhdr
->hwndFrom
!= infoPtr
->hwndHeader
) return 0;
10443 /* remember: HDN_LAST < HDN_FIRST */
10444 if (lpnmhdr
->code
> HDN_FIRST
|| lpnmhdr
->code
< HDN_LAST
) return 0;
10445 lpnmh
= (NMHEADERW
*)lpnmhdr
;
10447 if (lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
10449 switch (lpnmhdr
->code
)
10454 COLUMN_INFO
*lpColumnInfo
;
10458 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
10461 /* remove the old line (if any) */
10462 LISTVIEW_DrawTrackLine(infoPtr
);
10464 /* compute & draw the new line */
10465 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
10466 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
10467 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
10468 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
10469 LISTVIEW_DrawTrackLine(infoPtr
);
10470 return notify_forward_header(infoPtr
, lpnmh
);
10473 case HDN_ENDTRACKA
:
10474 case HDN_ENDTRACKW
:
10475 /* remove the track line (if any) */
10476 LISTVIEW_DrawTrackLine(infoPtr
);
10477 infoPtr
->xTrackLine
= -1;
10478 return notify_forward_header(infoPtr
, lpnmh
);
10480 case HDN_BEGINDRAG
:
10481 if ((infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
) == 0) return 1;
10482 return notify_forward_header(infoPtr
, lpnmh
);
10485 infoPtr
->colRectsDirty
= TRUE
;
10486 LISTVIEW_InvalidateList(infoPtr
);
10487 return notify_forward_header(infoPtr
, lpnmh
);
10489 case HDN_ITEMCHANGEDW
:
10490 case HDN_ITEMCHANGEDA
:
10492 COLUMN_INFO
*lpColumnInfo
;
10496 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
10498 hdi
.mask
= HDI_WIDTH
;
10499 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, lpnmh
->iItem
, (LPARAM
)&hdi
)) return 0;
10503 cxy
= lpnmh
->pitem
->cxy
;
10505 /* determine how much we change since the last know position */
10506 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
10507 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
10510 lpColumnInfo
->rcHeader
.right
+= dx
;
10512 hdi
.mask
= HDI_ORDER
;
10513 SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, lpnmh
->iItem
, (LPARAM
)&hdi
);
10515 /* not the rightmost one */
10516 if (hdi
.iOrder
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
10518 INT nIndex
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
10519 hdi
.iOrder
+ 1, 0);
10520 LISTVIEW_ScrollColumns(infoPtr
, nIndex
, dx
);
10524 /* only needs to update the scrolls */
10525 infoPtr
->nItemWidth
+= dx
;
10526 LISTVIEW_UpdateScroll(infoPtr
);
10528 LISTVIEW_UpdateItemSize(infoPtr
);
10529 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& is_redrawing(infoPtr
))
10532 RECT rcCol
= lpColumnInfo
->rcHeader
;
10534 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
10535 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
10537 rcCol
.top
= infoPtr
->rcList
.top
;
10538 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
10540 /* resizing left-aligned columns leaves most of the left side untouched */
10541 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
10543 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
10546 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
10549 /* when shrinking the last column clear the now unused field */
10550 if (hdi
.iOrder
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
10556 /* deal with right from rightmost column area */
10557 right
.left
= rcCol
.right
;
10558 right
.top
= rcCol
.top
;
10559 right
.bottom
= rcCol
.bottom
;
10560 right
.right
= infoPtr
->rcList
.right
;
10562 LISTVIEW_InvalidateRect(infoPtr
, &right
);
10565 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
10571 case HDN_ITEMCLICKW
:
10572 case HDN_ITEMCLICKA
:
10574 /* Handle sorting by Header Column */
10577 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
10579 nmlv
.iSubItem
= lpnmh
->iItem
;
10580 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
10581 return notify_forward_header(infoPtr
, lpnmh
);
10584 case HDN_DIVIDERDBLCLICKW
:
10585 case HDN_DIVIDERDBLCLICKA
:
10586 /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS
10587 we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
10588 split needed for that */
10589 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
10590 return notify_forward_header(infoPtr
, lpnmh
);
10597 * Paint non-client area of control.
10600 * [I] infoPtr : valid pointer to the listview structureof the sender
10601 * [I] region : update region
10604 * TRUE - frame was painted
10605 * FALSE - call default window proc
10607 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
10609 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
10613 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
10614 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
10617 return DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)region
, 0);
10619 GetWindowRect(infoPtr
->hwndSelf
, &r
);
10621 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
10622 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
10623 if (region
!= (HRGN
)1)
10624 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
10625 OffsetRect(&r
, -r
.left
, -r
.top
);
10627 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
10628 OffsetRect(&r
, -r
.left
, -r
.top
);
10630 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
10631 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
10632 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
10633 ReleaseDC(infoPtr
->hwndSelf
, dc
);
10635 /* Call default proc to get the scrollbars etc. painted */
10636 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
10643 * Determines the type of structure to use.
10646 * [I] infoPtr : valid pointer to the listview structureof the sender
10647 * [I] hwndFrom : listview window handle
10648 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10653 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
10655 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
10657 if (nCommand
== NF_REQUERY
)
10658 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
10660 return infoPtr
->notifyFormat
;
10665 * Paints/Repaints the listview control. Internal use.
10668 * [I] infoPtr : valid pointer to the listview structure
10669 * [I] hdc : device context handle
10674 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
10676 TRACE("(hdc=%p)\n", hdc
);
10678 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
10680 infoPtr
->bNoItemMetrics
= FALSE
;
10681 LISTVIEW_UpdateItemSize(infoPtr
);
10682 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
10683 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
10684 LISTVIEW_UpdateScroll(infoPtr
);
10687 if (infoPtr
->hwndHeader
) UpdateWindow(infoPtr
->hwndHeader
);
10690 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
10695 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
10696 if (!hdc
) return 1;
10697 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
10698 EndPaint(infoPtr
->hwndSelf
, &ps
);
10706 * Paints/Repaints the listview control, WM_PAINT handler.
10709 * [I] infoPtr : valid pointer to the listview structure
10710 * [I] hdc : device context handle
10715 static inline LRESULT
LISTVIEW_WMPaint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
10717 TRACE("(hdc=%p)\n", hdc
);
10719 if (!is_redrawing(infoPtr
))
10720 return DefWindowProcW (infoPtr
->hwndSelf
, WM_PAINT
, (WPARAM
)hdc
, 0);
10722 return LISTVIEW_Paint(infoPtr
, hdc
);
10727 * Paints/Repaints the listview control.
10730 * [I] infoPtr : valid pointer to the listview structure
10731 * [I] hdc : device context handle
10732 * [I] options : drawing options
10737 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
10739 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc
, options
);
10741 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
10744 if (options
& PRF_ERASEBKGND
)
10745 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
10747 if (options
& PRF_CLIENT
)
10748 LISTVIEW_Paint(infoPtr
, hdc
);
10756 * Processes double click messages (right mouse button).
10759 * [I] infoPtr : valid pointer to the listview structure
10760 * [I] wKey : key flag
10761 * [I] x,y : mouse coordinate
10766 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10768 LVHITTESTINFO lvHitTestInfo
;
10770 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey
, x
, y
);
10772 /* send NM_RELEASEDCAPTURE notification */
10773 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10775 /* send NM_RDBLCLK notification */
10776 lvHitTestInfo
.pt
.x
= x
;
10777 lvHitTestInfo
.pt
.y
= y
;
10778 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
10779 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
10786 * Processes WM_RBUTTONDOWN message and corresponding drag operation.
10789 * [I] infoPtr : valid pointer to the listview structure
10790 * [I] wKey : key flag
10791 * [I] x, y : mouse coordinate
10796 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
10801 TRACE("(key=%hu, x=%d, y=%d)\n", wKey
, x
, y
);
10803 /* send NM_RELEASEDCAPTURE notification */
10804 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
10806 /* determine the index of the selected item */
10809 item
= LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, TRUE
);
10811 /* make sure the listview control window has the focus */
10812 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
10814 if ((item
>= 0) && (item
< infoPtr
->nItemCount
))
10816 LISTVIEW_SetItemFocus(infoPtr
, item
);
10817 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
10818 !LISTVIEW_GetItemState(infoPtr
, item
, LVIS_SELECTED
))
10819 LISTVIEW_SetSelection(infoPtr
, item
);
10822 LISTVIEW_DeselectAll(infoPtr
);
10824 if (LISTVIEW_TrackMouse(infoPtr
, ht
.pt
))
10826 if (ht
.iItem
!= -1)
10830 memset(&nmlv
, 0, sizeof(nmlv
));
10831 nmlv
.iItem
= ht
.iItem
;
10832 nmlv
.ptAction
= ht
.pt
;
10834 notify_listview(infoPtr
, LVN_BEGINRDRAG
, &nmlv
);
10839 SetFocus(infoPtr
->hwndSelf
);
10843 LISTVIEW_HitTest(infoPtr
, &ht
, TRUE
, FALSE
);
10845 if (notify_click(infoPtr
, NM_RCLICK
, &ht
))
10847 /* Send a WM_CONTEXTMENU message in response to the WM_RBUTTONUP */
10848 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
10849 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)GetMessagePos());
10861 * [I] infoPtr : valid pointer to the listview structure
10862 * [I] hwnd : window handle of window containing the cursor
10863 * [I] nHittest : hit-test code
10864 * [I] wMouseMsg : ideintifier of the mouse message
10867 * TRUE if cursor is set
10870 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10872 LVHITTESTINFO lvHitTestInfo
;
10874 if (!LISTVIEW_IsHotTracking(infoPtr
)) goto forward
;
10876 if (!infoPtr
->hHotCursor
) goto forward
;
10878 GetCursorPos(&lvHitTestInfo
.pt
);
10879 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) goto forward
;
10881 SetCursor(infoPtr
->hHotCursor
);
10887 return DefWindowProcW(infoPtr
->hwndSelf
, WM_SETCURSOR
, wParam
, lParam
);
10895 * [I] infoPtr : valid pointer to the listview structure
10896 * [I] hwndLoseFocus : handle of previously focused window
10901 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
10903 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
10905 /* if we have the focus already, there's nothing to do */
10906 if (infoPtr
->bFocus
) return 0;
10908 /* send NM_SETFOCUS notification */
10909 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
10911 /* set window focus flag */
10912 infoPtr
->bFocus
= TRUE
;
10914 /* put the focus rect back on */
10915 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
10917 /* redraw all visible selected items */
10918 LISTVIEW_InvalidateSelectedItems(infoPtr
);
10928 * [I] infoPtr : valid pointer to the listview structure
10929 * [I] fRedraw : font handle
10930 * [I] fRedraw : redraw flag
10935 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
10937 HFONT oldFont
= infoPtr
->hFont
;
10938 INT oldHeight
= infoPtr
->nItemHeight
;
10940 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
10942 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
10943 if (infoPtr
->hFont
== oldFont
) return 0;
10945 LISTVIEW_SaveTextMetrics(infoPtr
);
10947 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
10949 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
10951 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
10952 LISTVIEW_UpdateSize(infoPtr
);
10953 LISTVIEW_UpdateScroll(infoPtr
);
10955 else if (infoPtr
->nItemHeight
!= oldHeight
)
10956 LISTVIEW_UpdateScroll(infoPtr
);
10958 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
10965 * Message handling for WM_SETREDRAW.
10966 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
10969 * [I] infoPtr : valid pointer to the listview structure
10970 * [I] bRedraw: state of redraw flag
10973 * DefWinProc return value
10975 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
10977 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
10979 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
10980 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
10982 infoPtr
->bRedraw
= bRedraw
;
10984 if(!bRedraw
) return 0;
10986 if (is_autoarrange(infoPtr
))
10987 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
10988 LISTVIEW_UpdateScroll(infoPtr
);
10990 /* despite what the WM_SETREDRAW docs says, apps expect us
10991 * to invalidate the listview here... stupid! */
10992 LISTVIEW_InvalidateList(infoPtr
);
10999 * Resizes the listview control. This function processes WM_SIZE
11000 * messages. At this time, the width and height are not used.
11003 * [I] infoPtr : valid pointer to the listview structure
11004 * [I] Width : new width
11005 * [I] Height : new height
11010 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
11012 RECT rcOld
= infoPtr
->rcList
;
11014 TRACE("(width=%d, height=%d)\n", Width
, Height
);
11016 LISTVIEW_UpdateSize(infoPtr
);
11017 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
11019 /* do not bother with display related stuff if we're not redrawing */
11020 if (!is_redrawing(infoPtr
)) return 0;
11022 if (is_autoarrange(infoPtr
))
11023 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
11025 LISTVIEW_UpdateScroll(infoPtr
);
11027 /* refresh all only for lists whose height changed significantly */
11028 if ((infoPtr
->uView
== LV_VIEW_LIST
) &&
11029 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
11030 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
11031 LISTVIEW_InvalidateList(infoPtr
);
11038 * Sets the size information.
11041 * [I] infoPtr : valid pointer to the listview structure
11046 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
11048 TRACE("uView=%d, rcList(old)=%s\n", infoPtr
->uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
11050 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
11052 if (infoPtr
->uView
== LV_VIEW_LIST
)
11054 /* Apparently the "LIST" style is supposed to have the same
11055 * number of items in a column even if there is no scroll bar.
11056 * Since if a scroll bar already exists then the bottom is already
11057 * reduced, only reduce if the scroll bar does not currently exist.
11058 * The "2" is there to mimic the native control. I think it may be
11059 * related to either padding or edges. (GLA 7/2002)
11061 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
11062 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
11063 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
11066 /* if control created invisible header isn't created */
11067 if (infoPtr
->hwndHeader
)
11072 hl
.prc
= &infoPtr
->rcList
;
11074 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
11075 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
11077 if (LISTVIEW_IsHeaderEnabled(infoPtr
))
11078 wp
.flags
|= SWP_SHOWWINDOW
;
11081 wp
.flags
|= SWP_HIDEWINDOW
;
11085 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
, wp
.flags
);
11086 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
11088 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
11090 /* extra padding for grid */
11091 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
11092 infoPtr
->rcList
.top
+= 2;
11094 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
11099 * Processes WM_STYLECHANGED messages.
11102 * [I] infoPtr : valid pointer to the listview structure
11103 * [I] wStyleType : window style type (normal or extended)
11104 * [I] lpss : window style information
11109 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
11110 const STYLESTRUCT
*lpss
)
11112 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
11113 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
11116 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11117 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
11119 if (wStyleType
!= GWL_STYLE
) return 0;
11121 infoPtr
->dwStyle
= lpss
->styleNew
;
11123 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
11124 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
11125 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
11127 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
11128 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
11129 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
11131 if (uNewView
!= uOldView
)
11135 /* LVM_SETVIEW doesn't change window style bits within LVS_TYPEMASK,
11136 changing style updates current view only when view bits change. */
11137 map_style_view(infoPtr
);
11138 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
11139 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
11141 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
11142 SetRectEmpty(&infoPtr
->rcFocus
);
11144 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
11145 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
11147 if (uNewView
== LVS_REPORT
)
11152 LISTVIEW_CreateHeader( infoPtr
);
11154 hl
.prc
= &infoPtr
->rcList
;
11156 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
11157 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
11158 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
11159 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
11162 LISTVIEW_UpdateItemSize(infoPtr
);
11165 if (uNewView
== LVS_REPORT
|| infoPtr
->dwLvExStyle
& LVS_EX_HEADERINALLVIEWS
)
11167 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
11169 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
11171 /* Turn off the header control */
11172 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
11173 TRACE("Hide header control, was 0x%08x\n", style
);
11174 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
11176 /* Turn on the header control */
11177 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
11179 TRACE("Show header control, was 0x%08x\n", style
);
11180 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
11186 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
11187 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
11188 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
11190 /* update the size of the client area */
11191 LISTVIEW_UpdateSize(infoPtr
);
11193 /* add scrollbars if needed */
11194 LISTVIEW_UpdateScroll(infoPtr
);
11196 /* invalidate client area + erase background */
11197 LISTVIEW_InvalidateList(infoPtr
);
11204 * Processes WM_STYLECHANGING messages.
11207 * [I] wStyleType : window style type (normal or extended)
11208 * [I0] lpss : window style information
11213 static INT
LISTVIEW_StyleChanging(WPARAM wStyleType
,
11216 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11217 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
11219 /* don't forward LVS_OWNERDATA only if not already set to */
11220 if ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_OWNERDATA
)
11222 if (lpss
->styleOld
& LVS_OWNERDATA
)
11223 lpss
->styleNew
|= LVS_OWNERDATA
;
11225 lpss
->styleNew
&= ~LVS_OWNERDATA
;
11233 * Processes WM_SHOWWINDOW messages.
11236 * [I] infoPtr : valid pointer to the listview structure
11237 * [I] bShown : window is being shown (FALSE when hidden)
11238 * [I] iStatus : window show status
11243 static LRESULT
LISTVIEW_ShowWindow(LISTVIEW_INFO
*infoPtr
, WPARAM bShown
, LPARAM iStatus
)
11245 /* header delayed creation */
11246 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && bShown
)
11248 LISTVIEW_CreateHeader(infoPtr
);
11250 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
))
11251 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
11254 return DefWindowProcW(infoPtr
->hwndSelf
, WM_SHOWWINDOW
, bShown
, iStatus
);
11259 * Processes CCM_GETVERSION messages.
11262 * [I] infoPtr : valid pointer to the listview structure
11267 static inline LRESULT
LISTVIEW_GetVersion(const LISTVIEW_INFO
*infoPtr
)
11269 return infoPtr
->iVersion
;
11274 * Processes CCM_SETVERSION messages.
11277 * [I] infoPtr : valid pointer to the listview structure
11278 * [I] iVersion : version to be set
11281 * -1 when requested version is greater than DLL version;
11282 * previous version otherwise
11284 static LRESULT
LISTVIEW_SetVersion(LISTVIEW_INFO
*infoPtr
, DWORD iVersion
)
11286 INT iOldVersion
= infoPtr
->iVersion
;
11288 if (iVersion
> COMCTL32_VERSION
)
11291 infoPtr
->iVersion
= iVersion
;
11293 TRACE("new version %d\n", iVersion
);
11295 return iOldVersion
;
11300 * Window procedure of the listview control.
11303 static LRESULT WINAPI
11304 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
11306 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
11308 TRACE("(hwnd=%p uMsg=%x wParam=%lx lParam=%lx)\n", hwnd
, uMsg
, wParam
, lParam
);
11310 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
11311 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11315 case LVM_APPROXIMATEVIEWRECT
:
11316 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
11317 LOWORD(lParam
), HIWORD(lParam
));
11319 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
11321 case LVM_CANCELEDITLABEL
:
11322 return LISTVIEW_CancelEditLabel(infoPtr
);
11324 case LVM_CREATEDRAGIMAGE
:
11325 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
11327 case LVM_DELETEALLITEMS
:
11328 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
11330 case LVM_DELETECOLUMN
:
11331 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
11333 case LVM_DELETEITEM
:
11334 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
11336 case LVM_EDITLABELA
:
11337 case LVM_EDITLABELW
:
11338 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
,
11339 uMsg
== LVM_EDITLABELW
);
11340 /* case LVM_ENABLEGROUPVIEW: */
11342 case LVM_ENSUREVISIBLE
:
11343 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
11345 case LVM_FINDITEMW
:
11346 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
11348 case LVM_FINDITEMA
:
11349 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
11351 case LVM_GETBKCOLOR
:
11352 return infoPtr
->clrBk
;
11354 /* case LVM_GETBKIMAGE: */
11356 case LVM_GETCALLBACKMASK
:
11357 return infoPtr
->uCallbackMask
;
11359 case LVM_GETCOLUMNA
:
11360 case LVM_GETCOLUMNW
:
11361 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11362 uMsg
== LVM_GETCOLUMNW
);
11364 case LVM_GETCOLUMNORDERARRAY
:
11365 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
11367 case LVM_GETCOLUMNWIDTH
:
11368 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
11370 case LVM_GETCOUNTPERPAGE
:
11371 return LISTVIEW_GetCountPerPage(infoPtr
);
11373 case LVM_GETEDITCONTROL
:
11374 return (LRESULT
)infoPtr
->hwndEdit
;
11376 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
11377 return infoPtr
->dwLvExStyle
;
11379 /* case LVM_GETGROUPINFO: */
11381 /* case LVM_GETGROUPMETRICS: */
11383 case LVM_GETHEADER
:
11384 return (LRESULT
)infoPtr
->hwndHeader
;
11386 case LVM_GETHOTCURSOR
:
11387 return (LRESULT
)infoPtr
->hHotCursor
;
11389 case LVM_GETHOTITEM
:
11390 return infoPtr
->nHotItem
;
11392 case LVM_GETHOVERTIME
:
11393 return infoPtr
->dwHoverTime
;
11395 case LVM_GETIMAGELIST
:
11396 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
11398 /* case LVM_GETINSERTMARK: */
11400 /* case LVM_GETINSERTMARKCOLOR: */
11402 /* case LVM_GETINSERTMARKRECT: */
11404 case LVM_GETISEARCHSTRINGA
:
11405 case LVM_GETISEARCHSTRINGW
:
11406 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
11411 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, uMsg
== LVM_GETITEMW
);
11413 case LVM_GETITEMCOUNT
:
11414 return infoPtr
->nItemCount
;
11416 case LVM_GETITEMPOSITION
:
11417 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
11419 case LVM_GETITEMRECT
:
11420 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
11422 case LVM_GETITEMSPACING
:
11423 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
11425 case LVM_GETITEMSTATE
:
11426 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
11428 case LVM_GETITEMTEXTA
:
11429 case LVM_GETITEMTEXTW
:
11430 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
,
11431 uMsg
== LVM_GETITEMTEXTW
);
11433 case LVM_GETNEXTITEM
:
11434 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
11436 case LVM_GETNUMBEROFWORKAREAS
:
11437 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
11440 case LVM_GETORIGIN
:
11441 if (!lParam
) return FALSE
;
11442 if (infoPtr
->uView
== LV_VIEW_DETAILS
||
11443 infoPtr
->uView
== LV_VIEW_LIST
) return FALSE
;
11444 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
11447 /* case LVM_GETOUTLINECOLOR: */
11449 /* case LVM_GETSELECTEDCOLUMN: */
11451 case LVM_GETSELECTEDCOUNT
:
11452 return LISTVIEW_GetSelectedCount(infoPtr
);
11454 case LVM_GETSELECTIONMARK
:
11455 return infoPtr
->nSelectionMark
;
11457 case LVM_GETSTRINGWIDTHA
:
11458 case LVM_GETSTRINGWIDTHW
:
11459 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
,
11460 uMsg
== LVM_GETSTRINGWIDTHW
);
11462 case LVM_GETSUBITEMRECT
:
11463 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
11465 case LVM_GETTEXTBKCOLOR
:
11466 return infoPtr
->clrTextBk
;
11468 case LVM_GETTEXTCOLOR
:
11469 return infoPtr
->clrText
;
11471 /* case LVM_GETTILEINFO: */
11473 /* case LVM_GETTILEVIEWINFO: */
11475 case LVM_GETTOOLTIPS
:
11476 if( !infoPtr
->hwndToolTip
)
11477 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
11478 return (LRESULT
)infoPtr
->hwndToolTip
;
11480 case LVM_GETTOPINDEX
:
11481 return LISTVIEW_GetTopIndex(infoPtr
);
11483 case LVM_GETUNICODEFORMAT
:
11484 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
11487 return infoPtr
->uView
;
11489 case LVM_GETVIEWRECT
:
11490 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
11492 case LVM_GETWORKAREAS
:
11493 FIXME("LVM_GETWORKAREAS: unimplemented\n");
11496 /* case LVM_HASGROUP: */
11499 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, TRUE
);
11501 case LVM_INSERTCOLUMNA
:
11502 case LVM_INSERTCOLUMNW
:
11503 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11504 uMsg
== LVM_INSERTCOLUMNW
);
11506 /* case LVM_INSERTGROUP: */
11508 /* case LVM_INSERTGROUPSORTED: */
11510 case LVM_INSERTITEMA
:
11511 case LVM_INSERTITEMW
:
11512 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, uMsg
== LVM_INSERTITEMW
);
11514 /* case LVM_INSERTMARKHITTEST: */
11516 /* case LVM_ISGROUPVIEWENABLED: */
11518 case LVM_ISITEMVISIBLE
:
11519 return LISTVIEW_IsItemVisible(infoPtr
, (INT
)wParam
);
11521 case LVM_MAPIDTOINDEX
:
11522 return LISTVIEW_MapIdToIndex(infoPtr
, (UINT
)wParam
);
11524 case LVM_MAPINDEXTOID
:
11525 return LISTVIEW_MapIndexToId(infoPtr
, (INT
)wParam
);
11527 /* case LVM_MOVEGROUP: */
11529 /* case LVM_MOVEITEMTOGROUP: */
11531 case LVM_REDRAWITEMS
:
11532 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
11534 /* case LVM_REMOVEALLGROUPS: */
11536 /* case LVM_REMOVEGROUP: */
11539 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
11541 case LVM_SETBKCOLOR
:
11542 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
11544 /* case LVM_SETBKIMAGE: */
11546 case LVM_SETCALLBACKMASK
:
11547 infoPtr
->uCallbackMask
= (UINT
)wParam
;
11550 case LVM_SETCOLUMNA
:
11551 case LVM_SETCOLUMNW
:
11552 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
,
11553 uMsg
== LVM_SETCOLUMNW
);
11555 case LVM_SETCOLUMNORDERARRAY
:
11556 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
11558 case LVM_SETCOLUMNWIDTH
:
11559 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
11561 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
11562 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
11564 /* case LVM_SETGROUPINFO: */
11566 /* case LVM_SETGROUPMETRICS: */
11568 case LVM_SETHOTCURSOR
:
11569 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
11571 case LVM_SETHOTITEM
:
11572 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
11574 case LVM_SETHOVERTIME
:
11575 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)lParam
);
11577 case LVM_SETICONSPACING
:
11579 return LISTVIEW_SetIconSpacing(infoPtr
, -1, -1);
11580 return LISTVIEW_SetIconSpacing(infoPtr
, LOWORD(lParam
), HIWORD(lParam
));
11582 case LVM_SETIMAGELIST
:
11583 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
11585 /* case LVM_SETINFOTIP: */
11587 /* case LVM_SETINSERTMARK: */
11589 /* case LVM_SETINSERTMARKCOLOR: */
11594 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
11595 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, (uMsg
== LVM_SETITEMW
));
11598 case LVM_SETITEMCOUNT
:
11599 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
11601 case LVM_SETITEMPOSITION
:
11604 pt
.x
= (short)LOWORD(lParam
);
11605 pt
.y
= (short)HIWORD(lParam
);
11606 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, &pt
);
11609 case LVM_SETITEMPOSITION32
:
11610 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, (POINT
*)lParam
);
11612 case LVM_SETITEMSTATE
:
11613 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
11615 case LVM_SETITEMTEXTA
:
11616 case LVM_SETITEMTEXTW
:
11617 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
,
11618 uMsg
== LVM_SETITEMTEXTW
);
11620 /* case LVM_SETOUTLINECOLOR: */
11622 /* case LVM_SETSELECTEDCOLUMN: */
11624 case LVM_SETSELECTIONMARK
:
11625 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
11627 case LVM_SETTEXTBKCOLOR
:
11628 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
11630 case LVM_SETTEXTCOLOR
:
11631 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
11633 /* case LVM_SETTILEINFO: */
11635 /* case LVM_SETTILEVIEWINFO: */
11637 /* case LVM_SETTILEWIDTH: */
11639 case LVM_SETTOOLTIPS
:
11640 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
11642 case LVM_SETUNICODEFORMAT
:
11643 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
11646 return LISTVIEW_SetView(infoPtr
, wParam
);
11648 /* case LVM_SETWORKAREAS: */
11650 /* case LVM_SORTGROUPS: */
11652 case LVM_SORTITEMS
:
11653 case LVM_SORTITEMSEX
:
11654 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, wParam
,
11655 uMsg
== LVM_SORTITEMSEX
);
11656 case LVM_SUBITEMHITTEST
:
11657 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
11660 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
11662 case CCM_GETVERSION
:
11663 return LISTVIEW_GetVersion(infoPtr
);
11665 case CCM_SETVERSION
:
11666 return LISTVIEW_SetVersion(infoPtr
, wParam
);
11669 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
11672 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
11675 return LISTVIEW_NCCreate(hwnd
, (LPCREATESTRUCTW
)lParam
);
11678 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
11681 return LISTVIEW_Destroy(infoPtr
);
11684 return LISTVIEW_Enable(infoPtr
);
11686 case WM_ERASEBKGND
:
11687 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
11689 case WM_GETDLGCODE
:
11690 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
11693 return (LRESULT
)infoPtr
->hFont
;
11696 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0);
11699 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
11702 return LISTVIEW_KillFocus(infoPtr
);
11704 case WM_LBUTTONDBLCLK
:
11705 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11707 case WM_LBUTTONDOWN
:
11708 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11711 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11714 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11716 case WM_MOUSEHOVER
:
11717 return LISTVIEW_MouseHover(infoPtr
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11720 return LISTVIEW_NCDestroy(infoPtr
);
11723 return LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
);
11726 return LISTVIEW_Notify(infoPtr
, (LPNMHDR
)lParam
);
11728 case WM_NOTIFYFORMAT
:
11729 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
11731 case WM_PRINTCLIENT
:
11732 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
11735 return LISTVIEW_WMPaint(infoPtr
, (HDC
)wParam
);
11737 case WM_RBUTTONDBLCLK
:
11738 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11740 case WM_RBUTTONDOWN
:
11741 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
11744 return LISTVIEW_SetCursor(infoPtr
, wParam
, lParam
);
11747 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
11750 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
11753 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
11755 case WM_SHOWWINDOW
:
11756 return LISTVIEW_ShowWindow(infoPtr
, wParam
, lParam
);
11758 case WM_STYLECHANGED
:
11759 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
11761 case WM_STYLECHANGING
:
11762 return LISTVIEW_StyleChanging(wParam
, (LPSTYLESTRUCT
)lParam
);
11764 case WM_SYSCOLORCHANGE
:
11765 COMCTL32_RefreshSysColors();
11767 if (infoPtr
->bDefaultBkColor
)
11769 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
11770 infoPtr
->bDefaultBkColor
= TRUE
;
11771 LISTVIEW_InvalidateList(infoPtr
);
11776 /* case WM_TIMER: */
11777 case WM_THEMECHANGED
:
11778 return LISTVIEW_ThemeChanged(infoPtr
);
11781 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0);
11783 case WM_MOUSEWHEEL
:
11784 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
11785 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11786 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
11788 case WM_WINDOWPOSCHANGED
:
11789 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
11791 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
11792 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
11794 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
11796 if (notify_measureitem(infoPtr
)) LISTVIEW_InvalidateList(infoPtr
);
11798 LISTVIEW_Size(infoPtr
, ((WINDOWPOS
*)lParam
)->cx
, ((WINDOWPOS
*)lParam
)->cy
);
11800 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11802 /* case WM_WININICHANGE: */
11805 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
11806 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
11808 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
11815 * Registers the window class.
11823 void LISTVIEW_Register(void)
11825 WNDCLASSW wndClass
;
11827 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
11828 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
11829 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
11830 wndClass
.cbClsExtra
= 0;
11831 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
11832 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
11833 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
11834 wndClass
.lpszClassName
= WC_LISTVIEWW
;
11835 RegisterClassW(&wndClass
);
11840 * Unregisters the window class.
11848 void LISTVIEW_Unregister(void)
11850 UnregisterClassW(WC_LISTVIEWW
, NULL
);
11855 * Handle any WM_COMMAND messages
11858 * [I] infoPtr : valid pointer to the listview structure
11859 * [I] wParam : the first message parameter
11860 * [I] lParam : the second message parameter
11865 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
11868 TRACE("(%p %x %x %lx)\n", infoPtr
, HIWORD(wParam
), LOWORD(wParam
), lParam
);
11870 if (!infoPtr
->hwndEdit
) return 0;
11872 switch (HIWORD(wParam
))
11877 * Adjust the edit window size
11879 WCHAR buffer
[1024];
11880 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
11881 HFONT hFont
, hOldFont
= 0;
11885 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
11886 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
11887 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
11889 /* Select font to get the right dimension of the string */
11890 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
11893 hOldFont
= SelectObject(hdc
, hFont
);
11896 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
11898 TEXTMETRICW textMetric
;
11900 /* Add Extra spacing for the next character */
11901 GetTextMetricsW(hdc
, &textMetric
);
11902 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
11904 SetWindowPos(infoPtr
->hwndEdit
, NULL
, 0, 0, sz
.cx
,
11905 rect
.bottom
- rect
.top
, SWP_DRAWFRAME
| SWP_NOMOVE
| SWP_NOZORDER
);
11908 SelectObject(hdc
, hOldFont
);
11910 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
11916 LISTVIEW_CancelEditLabel(infoPtr
);
11921 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);