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 Nikolay Sivov
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * This code was audited for completeness against the documented features
28 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
30 * Unless otherwise noted, we believe this code to be complete, as per
31 * the specification mentioned above.
32 * If you discover missing features, or bugs, please note them below.
36 * Default Message Processing
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
40 * or small icon and the LVS_AUTOARRANGE style is specified.
45 * -- Hot item handling, mouse hovering
46 * -- Workareas support
51 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
52 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
53 * -- LVA_SNAPTOGRID not implemented
54 * -- LISTVIEW_ApproximateViewRect partially implemented
55 * -- LISTVIEW_[GS]etColumnOrderArray stubs
56 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
57 * -- LISTVIEW_SetIconSpacing is incomplete
58 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
61 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
62 * linear in the number of items in the list, and this is
63 * unacceptable for large lists.
64 * -- if list is sorted by item text LISTVIEW_InsertItemT could use
65 * binary search to calculate item index (e.g. DPA_Search()).
66 * This requires sorted state to be reliably tracked in item modifiers.
67 * -- we should keep an ordered array of coordinates in iconic mode
68 * this would allow to frame items (iterator_frameditems),
69 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
76 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
83 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_EX_BORDERSELECT
89 * -- LVS_EX_HEADERDRAGDROP
92 * -- LVS_EX_MULTIWORKAREAS
94 * -- LVS_EX_SIMPLESELECT
95 * -- LVS_EX_TWOCLICKACTIVATE
96 * -- LVS_EX_UNDERLINECOLD
97 * -- LVS_EX_UNDERLINEHOT
100 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
103 * -- LVN_MARQUEEBEGIN
109 * -- LVM_CANCELEDITLABEL
110 * -- LVM_ENABLEGROUPVIEW
111 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
112 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
113 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
114 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
115 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
116 * -- LVM_GETINSERTMARKRECT
117 * -- LVM_GETNUMBEROFWORKAREAS
118 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
119 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
120 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
121 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
122 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
123 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
124 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
125 * -- LVM_INSERTGROUPSORTED
126 * -- LVM_INSERTMARKHITTEST
127 * -- LVM_ISGROUPVIEWENABLED
128 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
130 * -- LVM_MOVEITEMTOGROUP
132 * -- LVM_SETTILEWIDTH
136 * -- ListView_GetHoverTime, ListView_SetHoverTime
137 * -- ListView_GetISearchString
138 * -- ListView_GetNumberOfWorkAreas
139 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
144 * Known differences in message stream from native control (not known if
145 * these differences cause problems):
146 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
147 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
148 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
149 * processing for "USEDOUBLECLICKTIME".
153 #include "wine/port.h"
168 #include "commctrl.h"
169 #include "comctl32.h"
172 #include "wine/debug.h"
173 #include "wine/unicode.h"
175 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
177 /* make sure you set this to 0 for production use! */
178 #define DEBUG_RANGES 1
180 typedef struct tagCOLUMN_INFO
182 RECT rcHeader
; /* tracks the header's rectangle */
183 int fmt
; /* same as LVCOLUMN.fmt */
186 typedef struct tagITEMHDR
190 } ITEMHDR
, *LPITEMHDR
;
192 typedef struct tagSUBITEM_INFO
198 typedef struct tagITEM_INFO
206 typedef struct tagRANGE
212 typedef struct tagRANGES
217 typedef struct tagITERATOR
226 typedef struct tagDELAYED_ITEM_EDIT
232 typedef struct tagLISTVIEW_INFO
239 HIMAGELIST himlNormal
;
240 HIMAGELIST himlSmall
;
241 HIMAGELIST himlState
;
245 POINT ptClickPos
; /* point where the user clicked */
246 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
249 RANGES selectionRanges
;
254 RECT rcList
; /* This rectangle is really the window
255 * client rectangle possibly reduced by the
256 * horizontal scroll bar and/or header - see
257 * LISTVIEW_UpdateSize. This rectangle offset
258 * by the LISTVIEW_GetOrigin value is in
259 * client coordinates */
268 INT ntmHeight
; /* Some cached metrics of the font used */
269 INT ntmMaxCharWidth
; /* by the listview to draw items */
271 BOOL bRedraw
; /* Turns on/off repaints & invalidations */
272 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
274 BOOL bDoChangeNotify
; /* send change notification messages? */
277 DWORD dwStyle
; /* the cached window GWL_STYLE */
278 DWORD dwLvExStyle
; /* extended listview style */
279 DWORD uView
; /* current view available through LVM_[G,S]ETVIEW */
280 INT nItemCount
; /* the number of items in the list */
281 HDPA hdpaItems
; /* array ITEM_INFO pointers */
282 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
283 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
284 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
285 POINT currIconPos
; /* this is the position next icon will be placed */
286 PFNLVCOMPARE pfnCompare
;
291 INT nLButtonDownItem
; /* tracks item to reset multiselection on WM_LBUTTONUP */
295 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
297 DWORD lastKeyPressTimestamp
;
299 INT nSearchParamLength
;
300 WCHAR szSearchParam
[ MAX_PATH
];
302 INT nMeasureItemHeight
;
303 INT xTrackLine
; /* The x coefficient of the track line or -1 if none */
304 DELAYED_ITEM_EDIT itemEdit
; /* Pointer to this structure will be the timer ID */
306 DWORD iVersion
; /* CCM_[G,S]ETVERSION */
312 /* How many we debug buffer to allocate */
313 #define DEBUG_BUFFERS 20
314 /* The size of a single debug bbuffer */
315 #define DEBUG_BUFFER_SIZE 256
317 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
318 #define SB_INTERNAL -1
320 /* maximum size of a label */
321 #define DISP_TEXT_SIZE 512
323 /* padding for items in list and small icon display modes */
324 #define WIDTH_PADDING 12
326 /* padding for items in list, report and small icon display modes */
327 #define HEIGHT_PADDING 1
329 /* offset of items in report display mode */
330 #define REPORT_MARGINX 2
332 /* padding for icon in large icon display mode
333 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
334 * that HITTEST will see.
335 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
336 * ICON_TOP_PADDING - sum of the two above.
337 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
338 * LABEL_HOR_PADDING - between text and sides of box
339 * LABEL_VERT_PADDING - between bottom of text and end of box
341 * ICON_LR_PADDING - additional width above icon size.
342 * ICON_LR_HALF - half of the above value
344 #define ICON_TOP_PADDING_NOTHITABLE 2
345 #define ICON_TOP_PADDING_HITABLE 2
346 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
347 #define ICON_BOTTOM_PADDING 4
348 #define LABEL_HOR_PADDING 5
349 #define LABEL_VERT_PADDING 7
350 #define ICON_LR_PADDING 16
351 #define ICON_LR_HALF (ICON_LR_PADDING/2)
353 /* default label width for items in list and small icon display modes */
354 #define DEFAULT_LABEL_WIDTH 40
356 /* default column width for items in list display mode */
357 #define DEFAULT_COLUMN_WIDTH 128
359 /* Size of "line" scroll for V & H scrolls */
360 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
362 /* Padding between image and label */
363 #define IMAGE_PADDING 2
365 /* Padding behind the label */
366 #define TRAILING_LABEL_PADDING 12
367 #define TRAILING_HEADER_PADDING 11
369 /* Border for the icon caption */
370 #define CAPTION_BORDER 2
372 /* Standard DrawText flags */
373 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
374 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
375 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
377 /* Image index from state */
378 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
380 /* The time in milliseconds to reset the search in the list */
381 #define KEY_DELAY 450
383 /* Dump the LISTVIEW_INFO structure to the debug channel */
384 #define LISTVIEW_DUMP(iP) do { \
385 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
386 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
387 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
388 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
389 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
390 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
391 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
392 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
393 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
394 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
397 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
400 * forward declarations
402 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
403 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*, INT
, LPRECT
);
404 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*, INT
, LPPOINT
);
405 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*, INT
, LPPOINT
);
406 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*, INT
, LPRECT
);
407 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*, INT
);
408 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*, LPPOINT
);
409 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*, LPRECT
);
410 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
411 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*, WPARAM
, LPARAM
);
412 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
413 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
, BOOL
);
414 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*, INT
, UINT
);
415 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
416 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
417 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
418 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
419 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, BOOL
);
420 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
421 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
423 /******** Text handling functions *************************************/
425 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
426 * text string. The string may be ANSI or Unicode, in which case
427 * the boolean isW tells us the type of the string.
429 * The name of the function tell what type of strings it expects:
430 * W: Unicode, T: ANSI/Unicode - function of isW
433 static inline BOOL
is_textW(LPCWSTR text
)
435 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
438 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
440 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
441 return is_textW(text
);
444 static inline int textlenT(LPCWSTR text
, BOOL isW
)
446 return !is_textT(text
, isW
) ? 0 :
447 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
450 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
453 if (isSrcW
) lstrcpynW(dest
, src
, max
);
454 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
456 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
457 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
460 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
462 LPWSTR wstr
= (LPWSTR
)text
;
464 if (!isW
&& is_textT(text
, isW
))
466 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
467 wstr
= Alloc(len
* sizeof(WCHAR
));
468 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
470 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
474 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
476 if (!isW
&& is_textT(wstr
, isW
)) Free (wstr
);
480 * dest is a pointer to a Unicode string
481 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
483 static BOOL
textsetptrT(LPWSTR
*dest
, LPCWSTR src
, BOOL isW
)
487 if (src
== LPSTR_TEXTCALLBACKW
)
489 if (is_textW(*dest
)) Free(*dest
);
490 *dest
= LPSTR_TEXTCALLBACKW
;
494 LPWSTR pszText
= textdupTtoW(src
, isW
);
495 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
496 bResult
= Str_SetPtrW(dest
, pszText
);
497 textfreeT(pszText
, isW
);
503 * compares a Unicode to a Unicode/ANSI text string
505 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
507 if (!aw
) return bt
? -1 : 0;
508 if (!bt
) return aw
? 1 : 0;
509 if (aw
== LPSTR_TEXTCALLBACKW
)
510 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
511 if (bt
!= LPSTR_TEXTCALLBACKW
)
513 LPWSTR bw
= textdupTtoW(bt
, isW
);
514 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
522 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
526 n
= min(min(n
, lstrlenW(s1
)), lstrlenW(s2
));
527 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
528 return res
? res
- sizeof(WCHAR
) : res
;
531 /******** Debugging functions *****************************************/
533 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
535 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
536 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
539 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
541 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
542 n
= min(textlenT(text
, isW
), n
);
543 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
546 static char* debug_getbuf(void)
548 static int index
= 0;
549 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
550 return buffers
[index
++ % DEBUG_BUFFERS
];
553 static inline const char* debugrange(const RANGE
*lprng
)
555 if (!lprng
) return "(null)";
556 return wine_dbg_sprintf("[%d, %d]", lprng
->lower
, lprng
->upper
);
559 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
561 char* buf
= debug_getbuf(), *text
= buf
;
562 int len
, size
= DEBUG_BUFFER_SIZE
;
564 if (pScrollInfo
== NULL
) return "(null)";
565 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
566 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
567 if (pScrollInfo
->fMask
& SIF_RANGE
)
568 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
570 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
571 if (pScrollInfo
->fMask
& SIF_PAGE
)
572 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
574 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
575 if (pScrollInfo
->fMask
& SIF_POS
)
576 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
578 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
579 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
580 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
582 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
585 buf
= text
+ strlen(text
);
587 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
591 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
593 if (!plvnm
) return "(null)";
594 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
595 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
596 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
597 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
600 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
602 char* buf
= debug_getbuf(), *text
= buf
;
603 int len
, size
= DEBUG_BUFFER_SIZE
;
605 if (lpLVItem
== NULL
) return "(null)";
606 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
607 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
608 if (lpLVItem
->mask
& LVIF_STATE
)
609 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
611 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
612 if (lpLVItem
->mask
& LVIF_TEXT
)
613 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
615 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
616 if (lpLVItem
->mask
& LVIF_IMAGE
)
617 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
619 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
620 if (lpLVItem
->mask
& LVIF_PARAM
)
621 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
623 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
624 if (lpLVItem
->mask
& LVIF_INDENT
)
625 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
627 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
630 buf
= text
+ strlen(text
);
632 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
636 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
638 char* buf
= debug_getbuf(), *text
= buf
;
639 int len
, size
= DEBUG_BUFFER_SIZE
;
641 if (lpColumn
== NULL
) return "(null)";
642 len
= snprintf(buf
, size
, "{");
643 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
644 if (lpColumn
->mask
& LVCF_SUBITEM
)
645 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
647 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
648 if (lpColumn
->mask
& LVCF_FMT
)
649 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
651 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
652 if (lpColumn
->mask
& LVCF_WIDTH
)
653 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
655 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
656 if (lpColumn
->mask
& LVCF_TEXT
)
657 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
659 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
660 if (lpColumn
->mask
& LVCF_IMAGE
)
661 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
663 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
664 if (lpColumn
->mask
& LVCF_ORDER
)
665 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
667 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
670 buf
= text
+ strlen(text
);
672 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
676 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
678 if (!lpht
) return "(null)";
680 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
681 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
684 /* Return the corresponding text for a given scroll value */
685 static inline LPCSTR
debugscrollcode(int nScrollCode
)
689 case SB_LINELEFT
: return "SB_LINELEFT";
690 case SB_LINERIGHT
: return "SB_LINERIGHT";
691 case SB_PAGELEFT
: return "SB_PAGELEFT";
692 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
693 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
694 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
695 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
696 case SB_INTERNAL
: return "SB_INTERNAL";
697 default: return "unknown";
702 /******** Notification functions ************************************/
704 static LRESULT
notify_forward_header(const LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
706 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
707 (WPARAM
)lpnmh
->hdr
.idFrom
, (LPARAM
)lpnmh
);
710 static LRESULT
notify_hdr(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
714 TRACE("(code=%d)\n", code
);
716 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
717 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
719 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, pnmh
->idFrom
, (LPARAM
)pnmh
);
721 TRACE(" <= %ld\n", result
);
726 static inline BOOL
notify(const LISTVIEW_INFO
*infoPtr
, INT code
)
729 HWND hwnd
= infoPtr
->hwndSelf
;
730 notify_hdr(infoPtr
, code
, &nmh
);
731 return IsWindow(hwnd
);
734 static inline void notify_itemactivate(const LISTVIEW_INFO
*infoPtr
, const LVHITTESTINFO
*htInfo
)
745 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
746 item
.iItem
= htInfo
->iItem
;
748 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
749 nmia
.lParam
= item
.lParam
;
750 nmia
.uOldState
= item
.state
;
751 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
752 nmia
.uChanged
= LVIF_STATE
;
755 nmia
.iItem
= htInfo
->iItem
;
756 nmia
.iSubItem
= htInfo
->iSubItem
;
757 nmia
.ptAction
= htInfo
->pt
;
759 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
760 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
761 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
763 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
766 static inline LRESULT
notify_listview(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
768 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
769 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
772 static BOOL
notify_click(const LISTVIEW_INFO
*infoPtr
, INT code
, const LVHITTESTINFO
*lvht
)
776 HWND hwnd
= infoPtr
->hwndSelf
;
778 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
779 ZeroMemory(&nmia
, sizeof(nmia
));
780 nmia
.iItem
= lvht
->iItem
;
781 nmia
.iSubItem
= lvht
->iSubItem
;
782 nmia
.ptAction
= lvht
->pt
;
783 item
.mask
= LVIF_PARAM
;
784 item
.iItem
= lvht
->iItem
;
786 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmia
.lParam
= item
.lParam
;
787 notify_hdr(infoPtr
, code
, (LPNMHDR
)&nmia
);
788 return IsWindow(hwnd
);
791 static BOOL
notify_deleteitem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
795 HWND hwnd
= infoPtr
->hwndSelf
;
797 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
799 item
.mask
= LVIF_PARAM
;
802 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
803 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
804 return IsWindow(hwnd
);
807 static int get_ansi_notification(UINT unicodeNotificationCode
)
809 switch (unicodeNotificationCode
)
811 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
812 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
813 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
814 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
815 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
816 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
818 ERR("unknown notification %x\n", unicodeNotificationCode
);
824 Send notification. depends on dispinfoW having same
825 structure as dispinfoA.
826 infoPtr : listview struct
827 notificationCode : *Unicode* notification code
828 pdi : dispinfo structure (can be unicode or ansi)
829 isW : TRUE if dispinfo is Unicode
831 static BOOL
notify_dispinfoT(const LISTVIEW_INFO
*infoPtr
, UINT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
833 BOOL bResult
= FALSE
;
834 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
835 INT cchTempBufMax
= 0, savCchTextMax
= 0;
837 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
839 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
841 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
842 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
845 if (convertToAnsi
|| convertToUnicode
)
847 if (notificationCode
!= LVN_GETDISPINFOW
)
849 cchTempBufMax
= convertToUnicode
?
850 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
851 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
855 cchTempBufMax
= pdi
->item
.cchTextMax
;
856 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
859 pszTempBuf
= Alloc( (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
860 if (!pszTempBuf
) return FALSE
;
862 if (convertToUnicode
)
863 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
864 pszTempBuf
, cchTempBufMax
);
866 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
867 cchTempBufMax
, NULL
, NULL
);
869 savCchTextMax
= pdi
->item
.cchTextMax
;
870 savPszText
= pdi
->item
.pszText
;
871 pdi
->item
.pszText
= pszTempBuf
;
872 pdi
->item
.cchTextMax
= cchTempBufMax
;
875 if (infoPtr
->notifyFormat
== NFR_ANSI
)
876 realNotifCode
= get_ansi_notification(notificationCode
);
878 realNotifCode
= notificationCode
;
879 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
880 bResult
= notify_hdr(infoPtr
, realNotifCode
, &pdi
->hdr
);
882 if (convertToUnicode
|| convertToAnsi
)
884 if (convertToUnicode
) /* note : pointer can be changed by app ! */
885 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
886 savCchTextMax
, NULL
, NULL
);
888 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
889 savPszText
, savCchTextMax
);
890 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
891 pdi
->item
.cchTextMax
= savCchTextMax
;
897 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
,
898 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
900 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
901 lpnmlvcd
->nmcd
.hdc
= hdc
;
902 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
903 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
904 lpnmlvcd
->clrText
= infoPtr
->clrText
;
905 if (!lplvItem
) return;
906 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
907 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
908 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
909 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
910 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
911 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
914 static inline DWORD
notify_customdraw (const LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
916 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
919 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
920 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
921 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
922 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
923 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
924 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
928 static void prepaint_setup (const LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
, BOOL SubItem
)
930 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
931 lpnmlvcd
->clrTextBk
= comctl32_color
.clrWindow
;
932 if (lpnmlvcd
->clrText
== CLR_DEFAULT
)
933 lpnmlvcd
->clrText
= comctl32_color
.clrWindowText
;
935 /* apparently, for selected items, we have to override the returned values */
938 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
942 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
943 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
945 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
947 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
948 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
953 /* Set the text attributes */
954 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
956 SetBkMode(hdc
, OPAQUE
);
957 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
960 SetBkMode(hdc
, TRANSPARENT
);
961 SetTextColor(hdc
, lpnmlvcd
->clrText
);
964 static inline DWORD
notify_postpaint (const LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
966 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
969 /******** Item iterator functions **********************************/
971 static RANGES
ranges_create(int count
);
972 static void ranges_destroy(RANGES ranges
);
973 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
974 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
975 static void ranges_dump(RANGES ranges
);
977 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
979 RANGE range
= { nItem
, nItem
+ 1 };
981 return ranges_add(ranges
, range
);
984 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
986 RANGE range
= { nItem
, nItem
+ 1 };
988 return ranges_del(ranges
, range
);
992 * ITERATOR DOCUMENTATION
994 * The iterator functions allow for easy, and convenient iteration
995 * over items of interest in the list. Typically, you create a
996 * iterator, use it, and destroy it, as such:
999 * iterator_xxxitems(&i, ...);
1000 * while (iterator_{prev,next}(&i)
1002 * //code which uses i.nItem
1004 * iterator_destroy(&i);
1006 * where xxx is either: framed, or visible.
1007 * Note that it is important that the code destroys the iterator
1008 * after it's done with it, as the creation of the iterator may
1009 * allocate memory, which thus needs to be freed.
1011 * You can iterate both forwards, and backwards through the list,
1012 * by using iterator_next or iterator_prev respectively.
1014 * Lower numbered items are draw on top of higher number items in
1015 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1016 * items may overlap). So, to test items, you should use
1018 * which lists the items top to bottom (in Z-order).
1019 * For drawing items, you should use
1021 * which lists the items bottom to top (in Z-order).
1022 * If you keep iterating over the items after the end-of-items
1023 * marker (-1) is returned, the iterator will start from the
1024 * beginning. Typically, you don't need to test for -1,
1025 * because iterator_{next,prev} will return TRUE if more items
1026 * are to be iterated over, or FALSE otherwise.
1028 * Note: the iterator is defined to be bidirectional. That is,
1029 * any number of prev followed by any number of next, or
1030 * five versa, should leave the iterator at the same item:
1031 * prev * n, next * n = next * n, prev * n
1033 * The iterator has a notion of an out-of-order, special item,
1034 * which sits at the start of the list. This is used in
1035 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1036 * which needs to be first, as it may overlap other items.
1038 * The code is a bit messy because we have:
1039 * - a special item to deal with
1040 * - simple range, or composite range
1042 * If you find bugs, or want to add features, please make sure you
1043 * always check/modify *both* iterator_prev, and iterator_next.
1047 * This function iterates through the items in increasing order,
1048 * but prefixed by the special item, then -1. That is:
1049 * special, 1, 2, 3, ..., n, -1.
1050 * Each item is listed only once.
1052 static inline BOOL
iterator_next(ITERATOR
* i
)
1056 i
->nItem
= i
->nSpecial
;
1057 if (i
->nItem
!= -1) return TRUE
;
1059 if (i
->nItem
== i
->nSpecial
)
1061 if (i
->ranges
) i
->index
= 0;
1067 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1068 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1073 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1074 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1077 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1079 i
->nItem
= i
->range
.lower
;
1080 if (i
->nItem
>= 0) goto testitem
;
1087 * This function iterates through the items in decreasing order,
1088 * followed by the special item, then -1. That is:
1089 * n, n-1, ..., 3, 2, 1, special, -1.
1090 * Each item is listed only once.
1092 static inline BOOL
iterator_prev(ITERATOR
* i
)
1099 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1102 if (i
->nItem
== i
->nSpecial
)
1110 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1111 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1117 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1120 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1122 i
->nItem
= i
->range
.upper
;
1123 if (i
->nItem
> 0) goto testitem
;
1125 return (i
->nItem
= i
->nSpecial
) != -1;
1128 static RANGE
iterator_range(const ITERATOR
*i
)
1132 if (!i
->ranges
) return i
->range
;
1134 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1136 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1137 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1139 else range
.lower
= range
.upper
= 0;
1145 * Releases resources associated with this ierator.
1147 static inline void iterator_destroy(const ITERATOR
*i
)
1149 ranges_destroy(i
->ranges
);
1153 * Create an empty iterator.
1155 static inline BOOL
iterator_empty(ITERATOR
* i
)
1157 ZeroMemory(i
, sizeof(*i
));
1158 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1163 * Create an iterator over a range.
1165 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1173 * Create an iterator over a bunch of ranges.
1174 * Please note that the iterator will take ownership of the ranges,
1175 * and will free them upon destruction.
1177 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1185 * Creates an iterator over the items which intersect lprc.
1187 static BOOL
iterator_frameditems(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1189 RECT frame
= *lprc
, rcItem
, rcTemp
;
1192 /* in case we fail, we want to return an empty iterator */
1193 if (!iterator_empty(i
)) return FALSE
;
1195 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1197 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1198 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1200 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
1204 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->nFocusedItem
!= -1)
1206 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1207 if (IntersectRect(&rcTemp
, &rcItem
, lprc
))
1208 i
->nSpecial
= infoPtr
->nFocusedItem
;
1210 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1211 /* to do better here, we need to have PosX, and PosY sorted */
1212 TRACE("building icon ranges:\n");
1213 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1215 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1216 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1217 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1218 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1219 if (IntersectRect(&rcTemp
, &rcItem
, &frame
))
1220 ranges_additem(i
->ranges
, nItem
);
1224 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
1228 if (frame
.left
>= infoPtr
->nItemWidth
) return TRUE
;
1229 if (frame
.top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1231 range
.lower
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1232 range
.upper
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1233 if (range
.upper
<= range
.lower
) return TRUE
;
1234 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1235 TRACE(" report=%s\n", debugrange(&i
->range
));
1239 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1240 INT nFirstRow
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1241 INT nLastRow
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1248 if (infoPtr
->nItemWidth
)
1250 nFirstCol
= max(frame
.left
/ infoPtr
->nItemWidth
, 0);
1251 nLastCol
= min((frame
.right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1255 nFirstCol
= max(frame
.left
, 0);
1256 nLastCol
= min(frame
.right
- 1, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1259 lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1261 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1262 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1264 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1266 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1267 TRACE("building list ranges:\n");
1268 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1270 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1271 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1272 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1273 TRACE(" list=%s\n", debugrange(&item_range
));
1274 ranges_add(i
->ranges
, item_range
);
1282 * Creates an iterator over the items which intersect the visible region of hdc.
1284 static BOOL
iterator_visibleitems(ITERATOR
*i
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1286 POINT Origin
, Position
;
1287 RECT rcItem
, rcClip
;
1290 rgntype
= GetClipBox(hdc
, &rcClip
);
1291 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1292 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1293 if (rgntype
== SIMPLEREGION
) return TRUE
;
1295 /* first deal with the special item */
1296 if (i
->nSpecial
!= -1)
1298 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1299 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1302 /* if we can't deal with the region, we'll just go with the simple range */
1303 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1304 TRACE("building visible range:\n");
1305 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1307 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1308 if (!ranges_add(i
->ranges
, i
->range
))
1310 ranges_destroy(i
->ranges
);
1316 /* now delete the invisible items from the list */
1317 while(iterator_next(i
))
1319 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1320 rcItem
.left
= Position
.x
+ Origin
.x
;
1321 rcItem
.top
= Position
.y
+ Origin
.y
;
1322 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1323 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1324 if (!RectVisible(hdc
, &rcItem
))
1325 ranges_delitem(i
->ranges
, i
->nItem
);
1327 /* the iterator should restart on the next iterator_next */
1333 /******** Misc helper functions ************************************/
1335 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1336 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1338 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1339 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1342 static inline BOOL
is_autoarrange(const LISTVIEW_INFO
*infoPtr
)
1344 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1345 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
);
1348 static void toggle_checkbox_state(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1350 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
1351 if(state
== 1 || state
== 2)
1355 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
1356 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
1357 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
1361 /* this should be called after window style got updated,
1362 it used to reset view state to match current window style */
1363 static inline void map_style_view(LISTVIEW_INFO
*infoPtr
)
1365 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
1368 infoPtr
->uView
= LV_VIEW_ICON
;
1371 infoPtr
->uView
= LV_VIEW_DETAILS
;
1374 infoPtr
->uView
= LV_VIEW_SMALLICON
;
1377 infoPtr
->uView
= LV_VIEW_LIST
;
1381 /******** Internal API functions ************************************/
1383 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1385 static COLUMN_INFO mainItem
;
1387 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1388 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1389 return DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1392 static INT
LISTVIEW_CreateHeader(LISTVIEW_INFO
*infoPtr
)
1394 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
1397 if (infoPtr
->hwndHeader
) return 0;
1399 TRACE("Creating header for list %p\n", infoPtr
->hwndSelf
);
1401 /* setup creation flags */
1402 dFlags
|= (LVS_NOSORTHEADER
& infoPtr
->dwStyle
) ? 0 : HDS_BUTTONS
;
1403 dFlags
|= (LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) ? HDS_HIDDEN
: 0;
1405 hInst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
1408 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
1409 0, 0, 0, 0, infoPtr
->hwndSelf
, NULL
, hInst
, NULL
);
1410 if (!infoPtr
->hwndHeader
) return -1;
1412 /* set header unicode format */
1413 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
1415 /* set header font */
1416 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
1418 LISTVIEW_UpdateSize(infoPtr
);
1423 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1425 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1428 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1430 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1433 /* used to handle collapse main item column case */
1434 static inline BOOL
LISTVIEW_DrawFocusRect(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1436 return (infoPtr
->rcFocus
.left
< infoPtr
->rcFocus
.right
) ?
1437 DrawFocusRect(hdc
, &infoPtr
->rcFocus
) : FALSE
;
1440 /* Listview invalidation functions: use _only_ these functions to invalidate */
1442 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1444 return infoPtr
->bRedraw
;
1447 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1449 if(!is_redrawing(infoPtr
)) return;
1450 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1451 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1454 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1458 if(!is_redrawing(infoPtr
)) return;
1459 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1460 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1463 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1465 POINT Origin
, Position
;
1468 if(!is_redrawing(infoPtr
)) return;
1469 assert (infoPtr
->uView
== LV_VIEW_DETAILS
);
1470 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1471 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1472 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1474 rcBox
.bottom
= infoPtr
->nItemHeight
;
1475 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1476 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1479 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1481 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1484 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1488 if(!is_redrawing(infoPtr
)) return;
1489 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1490 rcCol
.top
= infoPtr
->rcList
.top
;
1491 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1492 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1497 * Retrieves the number of items that can fit vertically in the client area.
1500 * [I] infoPtr : valid pointer to the listview structure
1503 * Number of items per row.
1505 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1507 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1509 return max(nListWidth
/(infoPtr
->nItemWidth
? infoPtr
->nItemWidth
: 1), 1);
1514 * Retrieves the number of items that can fit horizontally in the client
1518 * [I] infoPtr : valid pointer to the listview structure
1521 * Number of items per column.
1523 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1525 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1527 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1531 /*************************************************************************
1532 * LISTVIEW_ProcessLetterKeys
1534 * Processes keyboard messages generated by pressing the letter keys
1536 * What this does is perform a case insensitive search from the
1537 * current position with the following quirks:
1538 * - If two chars or more are pressed in quick succession we search
1539 * for the corresponding string (e.g. 'abc').
1540 * - If there is a delay we wipe away the current search string and
1541 * restart with just that char.
1542 * - If the user keeps pressing the same character, whether slowly or
1543 * fast, so that the search string is entirely composed of this
1544 * character ('aaaaa' for instance), then we search for first item
1545 * that starting with that character.
1546 * - If the user types the above character in quick succession, then
1547 * we must also search for the corresponding string ('aaaaa'), and
1548 * go to that string if there is a match.
1551 * [I] hwnd : handle to the window
1552 * [I] charCode : the character code, the actual character
1553 * [I] keyData : key data
1561 * - The current implementation has a list of characters it will
1562 * accept and it ignores everything else. In particular it will
1563 * ignore accentuated characters which seems to match what
1564 * Windows does. But I'm not sure it makes sense to follow
1566 * - We don't sound a beep when the search fails.
1570 * TREEVIEW_ProcessLetterKeys
1572 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1577 WCHAR buffer
[MAX_PATH
];
1578 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1580 /* simple parameter checking */
1581 if (!charCode
|| !keyData
) return 0;
1583 /* only allow the valid WM_CHARs through */
1584 if (!isalnumW(charCode
) &&
1585 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1586 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1587 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1588 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1589 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1590 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1591 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1592 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1593 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1596 /* if there's one item or less, there is no where to go */
1597 if (infoPtr
->nItemCount
<= 1) return 0;
1599 /* update the search parameters */
1600 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1601 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1602 if (infoPtr
->nSearchParamLength
< MAX_PATH
-1)
1603 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1604 if (infoPtr
->charCode
!= charCode
)
1605 infoPtr
->charCode
= charCode
= 0;
1607 infoPtr
->charCode
=charCode
;
1608 infoPtr
->szSearchParam
[0]=charCode
;
1609 infoPtr
->nSearchParamLength
=1;
1610 /* Redundant with the 1 char string */
1614 /* and search from the current position */
1616 if (infoPtr
->nFocusedItem
>= 0) {
1617 endidx
=infoPtr
->nFocusedItem
;
1619 /* if looking for single character match,
1620 * then we must always move forward
1622 if (infoPtr
->nSearchParamLength
== 1)
1625 endidx
=infoPtr
->nItemCount
;
1629 /* Let application handle this for virtual listview */
1630 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
1635 ZeroMemory(&lvfi
, sizeof(lvfi
));
1636 lvfi
.flags
= (LVFI_WRAP
| LVFI_PARTIAL
);
1637 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
] = '\0';
1638 lvfi
.psz
= infoPtr
->szSearchParam
;
1642 nItem
= notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
1645 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1651 if (idx
== infoPtr
->nItemCount
) {
1652 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1658 item
.mask
= LVIF_TEXT
;
1661 item
.pszText
= buffer
;
1662 item
.cchTextMax
= MAX_PATH
;
1663 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1665 /* check for a match */
1666 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1669 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1670 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1671 /* This would work but we must keep looking for a longer match */
1675 } while (idx
!= endidx
);
1678 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1683 /*************************************************************************
1684 * LISTVIEW_UpdateHeaderSize [Internal]
1686 * Function to resize the header control
1689 * [I] hwnd : handle to a window
1690 * [I] nNewScrollPos : scroll pos to set
1695 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1700 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1702 if (!infoPtr
->hwndHeader
) return;
1704 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1705 point
[0].x
= winRect
.left
;
1706 point
[0].y
= winRect
.top
;
1707 point
[1].x
= winRect
.right
;
1708 point
[1].y
= winRect
.bottom
;
1710 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1711 point
[0].x
= -nNewScrollPos
;
1712 point
[1].x
+= nNewScrollPos
;
1714 SetWindowPos(infoPtr
->hwndHeader
,0,
1715 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1716 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
1717 SWP_NOZORDER
| SWP_NOACTIVATE
);
1722 * Update the scrollbars. This functions should be called whenever
1723 * the content, size or view changes.
1726 * [I] infoPtr : valid pointer to the listview structure
1731 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*infoPtr
)
1733 SCROLLINFO horzInfo
, vertInfo
;
1736 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1738 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1739 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1740 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1742 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1743 if (infoPtr
->uView
== LV_VIEW_LIST
)
1745 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1746 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1748 /* scroll by at least one column per page */
1749 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1750 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1752 if (infoPtr
->nItemWidth
)
1753 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1755 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
1757 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1759 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1763 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1766 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1767 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1768 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
1769 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1770 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1772 /* Setting the horizontal scroll can change the listview size
1773 * (and potentially everything else) so we need to recompute
1774 * everything again for the vertical scroll
1777 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1778 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1779 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1781 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
1783 vertInfo
.nMax
= infoPtr
->nItemCount
;
1785 /* scroll by at least one page */
1786 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1787 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1789 if (infoPtr
->nItemHeight
> 0)
1790 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1792 else if (infoPtr
->uView
!= LV_VIEW_LIST
) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1796 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1799 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1800 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1801 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
1802 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1803 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1805 /* Change of the range may have changed the scroll pos. If so move the content */
1806 if (dx
!= 0 || dy
!= 0)
1809 listRect
= infoPtr
->rcList
;
1810 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
1811 SW_ERASE
| SW_INVALIDATE
);
1814 /* Update the Header Control */
1815 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
1817 horzInfo
.fMask
= SIF_POS
;
1818 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1819 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1826 * Shows/hides the focus rectangle.
1829 * [I] infoPtr : valid pointer to the listview structure
1830 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1835 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1839 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1841 if (infoPtr
->nFocusedItem
< 0) return;
1843 /* we need some gymnastics in ICON mode to handle large items */
1844 if (infoPtr
->uView
== LV_VIEW_ICON
)
1848 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1849 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1851 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1856 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1858 /* for some reason, owner draw should work only in report mode */
1859 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
1864 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
1865 HFONT hOldFont
= SelectObject(hdc
, hFont
);
1867 item
.iItem
= infoPtr
->nFocusedItem
;
1869 item
.mask
= LVIF_PARAM
;
1870 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1872 ZeroMemory(&dis
, sizeof(dis
));
1873 dis
.CtlType
= ODT_LISTVIEW
;
1874 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1875 dis
.itemID
= item
.iItem
;
1876 dis
.itemAction
= ODA_FOCUS
;
1877 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1878 dis
.hwndItem
= infoPtr
->hwndSelf
;
1880 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1881 dis
.itemData
= item
.lParam
;
1883 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1885 SelectObject(hdc
, hOldFont
);
1889 LISTVIEW_DrawFocusRect(infoPtr
, hdc
);
1892 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1896 * Invalidates all visible selected items.
1898 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
1902 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1903 while(iterator_next(&i
))
1905 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1906 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1908 iterator_destroy(&i
);
1913 * DESCRIPTION: [INTERNAL]
1914 * Computes an item's (left,top) corner, relative to rcView.
1915 * That is, the position has NOT been made relative to the Origin.
1916 * This is deliberate, to avoid computing the Origin over, and
1917 * over again, when this function is called in a loop. Instead,
1918 * one can factor the computation of the Origin before the loop,
1919 * and offset the value returned by this function, on every iteration.
1922 * [I] infoPtr : valid pointer to the listview structure
1923 * [I] nItem : item number
1924 * [O] lpptOrig : item top, left corner
1929 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1931 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1933 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
1935 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1936 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1938 else if (infoPtr
->uView
== LV_VIEW_LIST
)
1940 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1941 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1942 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1944 else /* LV_VIEW_DETAILS */
1946 lpptPosition
->x
= REPORT_MARGINX
;
1947 /* item is always at zero indexed column */
1948 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
1949 lpptPosition
->x
+= LISTVIEW_GetColumnInfo(infoPtr
, 0)->rcHeader
.left
;
1950 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1955 * DESCRIPTION: [INTERNAL]
1956 * Compute the rectangles of an item. This is to localize all
1957 * the computations in one place. If you are not interested in some
1958 * of these values, simply pass in a NULL -- the function is smart
1959 * enough to compute only what's necessary. The function computes
1960 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1961 * one, the BOX rectangle. This rectangle is very cheap to compute,
1962 * and is guaranteed to contain all the other rectangles. Computing
1963 * the ICON rect is also cheap, but all the others are potentially
1964 * expensive. This gives an easy and effective optimization when
1965 * searching (like point inclusion, or rectangle intersection):
1966 * first test against the BOX, and if TRUE, test against the desired
1968 * If the function does not have all the necessary information
1969 * to computed the requested rectangles, will crash with a
1970 * failed assertion. This is done so we catch all programming
1971 * errors, given that the function is called only from our code.
1973 * We have the following 'special' meanings for a few fields:
1974 * * If LVIS_FOCUSED is set, we assume the item has the focus
1975 * This is important in ICON mode, where it might get a larger
1976 * then usual rectangle
1978 * Please note that subitem support works only in REPORT mode.
1981 * [I] infoPtr : valid pointer to the listview structure
1982 * [I] lpLVItem : item to compute the measures for
1983 * [O] lprcBox : ptr to Box rectangle
1984 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1985 * [0] lprcSelectBox : ptr to select box rectangle
1986 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1987 * [O] lprcIcon : ptr to Icon rectangle
1988 * Same as LVM_GETITEMRECT with LVIR_ICON
1989 * [O] lprcStateIcon: ptr to State Icon rectangle
1990 * [O] lprcLabel : ptr to Label rectangle
1991 * Same as LVM_GETITEMRECT with LVIR_LABEL
1996 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1997 LPRECT lprcBox
, LPRECT lprcSelectBox
,
1998 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
2000 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
2001 RECT Box
, SelectBox
, Icon
, Label
;
2002 COLUMN_INFO
*lpColumnInfo
= NULL
;
2003 SIZE labelSize
= { 0, 0 };
2005 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
2007 /* Be smart and try to figure out the minimum we have to do */
2008 if (lpLVItem
->iSubItem
) assert(infoPtr
->uView
== LV_VIEW_DETAILS
);
2009 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprcBox
|| lprcLabel
))
2011 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
2012 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
2014 if (lprcSelectBox
) doSelectBox
= TRUE
;
2015 if (lprcLabel
) doLabel
= TRUE
;
2016 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
2023 /************************************************************/
2024 /* compute the box rectangle (it should be cheap to do) */
2025 /************************************************************/
2026 if (lpLVItem
->iSubItem
|| infoPtr
->uView
== LV_VIEW_DETAILS
)
2027 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
2029 if (lpLVItem
->iSubItem
)
2031 Box
= lpColumnInfo
->rcHeader
;
2036 Box
.right
= infoPtr
->nItemWidth
;
2039 Box
.bottom
= infoPtr
->nItemHeight
;
2041 /******************************************************************/
2042 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2043 /******************************************************************/
2046 LONG state_width
= 0;
2048 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
2049 state_width
= infoPtr
->iconStateSize
.cx
;
2051 if (infoPtr
->uView
== LV_VIEW_ICON
)
2053 Icon
.left
= Box
.left
+ state_width
;
2054 if (infoPtr
->himlNormal
)
2055 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
2056 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2057 Icon
.right
= Icon
.left
;
2058 Icon
.bottom
= Icon
.top
;
2059 if (infoPtr
->himlNormal
)
2061 Icon
.right
+= infoPtr
->iconSize
.cx
;
2062 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2065 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2067 Icon
.left
= Box
.left
+ state_width
;
2069 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lpLVItem
->iSubItem
== 0)
2071 /* we need the indent in report mode */
2072 assert(lpLVItem
->mask
& LVIF_INDENT
);
2073 Icon
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
+ REPORT_MARGINX
;
2077 Icon
.right
= Icon
.left
;
2078 if (infoPtr
->himlSmall
&&
2079 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
2080 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2081 Icon
.right
+= infoPtr
->iconSize
.cx
;
2082 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2084 if(lprcIcon
) *lprcIcon
= Icon
;
2085 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2087 /* TODO: is this correct? */
2090 lprcStateIcon
->left
= Icon
.left
- state_width
;
2091 lprcStateIcon
->right
= Icon
.left
;
2092 lprcStateIcon
->top
= Icon
.top
;
2093 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2094 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2097 else Icon
.right
= 0;
2099 /************************************************************/
2100 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2101 /************************************************************/
2104 /* calculate how far to the right can the label stretch */
2105 Label
.right
= Box
.right
;
2106 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2108 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
2111 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && infoPtr
->uView
== LV_VIEW_DETAILS
))
2113 labelSize
.cx
= infoPtr
->nItemWidth
;
2114 labelSize
.cy
= infoPtr
->nItemHeight
;
2118 /* we need the text in non owner draw mode */
2119 assert(lpLVItem
->mask
& LVIF_TEXT
);
2120 if (is_textT(lpLVItem
->pszText
, TRUE
))
2122 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2123 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2124 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2128 /* compute rough rectangle where the label will go */
2129 SetRectEmpty(&rcText
);
2130 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2131 rcText
.bottom
= infoPtr
->nItemHeight
;
2132 if (infoPtr
->uView
== LV_VIEW_ICON
)
2133 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2135 /* now figure out the flags */
2136 if (infoPtr
->uView
== LV_VIEW_ICON
)
2137 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2139 uFormat
= LV_SL_DT_FLAGS
;
2141 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2143 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2144 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2146 SelectObject(hdc
, hOldFont
);
2147 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2151 if (infoPtr
->uView
== LV_VIEW_ICON
)
2153 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2154 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2155 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2156 Label
.right
= Label
.left
+ labelSize
.cx
;
2157 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2158 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2160 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2161 labelSize
.cy
/= infoPtr
->ntmHeight
;
2162 labelSize
.cy
= max(labelSize
.cy
, 1);
2163 labelSize
.cy
*= infoPtr
->ntmHeight
;
2165 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2167 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2169 Label
.left
= Icon
.right
;
2170 Label
.top
= Box
.top
;
2171 Label
.right
= lpColumnInfo
->rcHeader
.right
;
2172 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2174 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2176 Label
.left
= Icon
.right
;
2177 Label
.top
= Box
.top
;
2178 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2179 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2182 if (lprcLabel
) *lprcLabel
= Label
;
2183 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2186 /************************************************************/
2187 /* compute SELECT bounding box */
2188 /************************************************************/
2191 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2193 SelectBox
.left
= Icon
.left
;
2194 SelectBox
.top
= Box
.top
;
2195 SelectBox
.bottom
= Box
.bottom
;
2196 SelectBox
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2200 UnionRect(&SelectBox
, &Icon
, &Label
);
2202 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2203 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2206 /* Fix the Box if necessary */
2209 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2210 else *lprcBox
= Box
;
2212 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2216 * DESCRIPTION: [INTERNAL]
2219 * [I] infoPtr : valid pointer to the listview structure
2220 * [I] nItem : item number
2221 * [O] lprcBox : ptr to Box rectangle
2226 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2228 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2229 POINT Position
, Origin
;
2232 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2233 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2235 /* Be smart and try to figure out the minimum we have to do */
2237 if (infoPtr
->uView
== LV_VIEW_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2238 lvItem
.mask
|= LVIF_TEXT
;
2239 lvItem
.iItem
= nItem
;
2240 lvItem
.iSubItem
= 0;
2241 lvItem
.pszText
= szDispText
;
2242 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2243 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2244 if (infoPtr
->uView
== LV_VIEW_ICON
)
2246 lvItem
.mask
|= LVIF_STATE
;
2247 lvItem
.stateMask
= LVIS_FOCUSED
;
2248 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2250 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2252 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2258 * Returns the current icon position, and advances it along the top.
2259 * The returned position is not offset by Origin.
2262 * [I] infoPtr : valid pointer to the listview structure
2263 * [O] lpPos : will get the current icon position
2268 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2270 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2272 *lpPos
= infoPtr
->currIconPos
;
2274 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2275 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2277 infoPtr
->currIconPos
.x
= 0;
2278 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2284 * Returns the current icon position, and advances it down the left edge.
2285 * The returned position is not offset by Origin.
2288 * [I] infoPtr : valid pointer to the listview structure
2289 * [O] lpPos : will get the current icon position
2294 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2296 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2298 *lpPos
= infoPtr
->currIconPos
;
2300 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2301 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2303 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2304 infoPtr
->currIconPos
.y
= 0;
2310 * Moves an icon to the specified position.
2311 * It takes care of invalidating the item, etc.
2314 * [I] infoPtr : valid pointer to the listview structure
2315 * [I] nItem : the item to move
2316 * [I] lpPos : the new icon position
2317 * [I] isNew : flags the item as being new
2323 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2329 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2330 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2332 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2333 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2336 /* Allocating a POINTER for every item is too resource intensive,
2337 * so we'll keep the (x,y) in different arrays */
2338 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2339 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2341 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2348 * Arranges listview items in icon display mode.
2351 * [I] infoPtr : valid pointer to the listview structure
2352 * [I] nAlignCode : alignment code
2358 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2360 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2364 if (infoPtr
->uView
!= LV_VIEW_ICON
&& infoPtr
->uView
!= LV_VIEW_SMALLICON
) return FALSE
;
2366 TRACE("nAlignCode=%d\n", nAlignCode
);
2368 if (nAlignCode
== LVA_DEFAULT
)
2370 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2371 else nAlignCode
= LVA_ALIGNTOP
;
2376 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2377 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2378 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2379 default: return FALSE
;
2382 infoPtr
->bAutoarrange
= TRUE
;
2383 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2384 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2386 next_pos(infoPtr
, &pos
);
2387 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2395 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2396 * For LVS_REPORT always returns empty rectangle.
2399 * [I] infoPtr : valid pointer to the listview structure
2400 * [O] lprcView : bounding rectangle
2406 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2410 SetRectEmpty(lprcView
);
2412 switch (infoPtr
->uView
)
2415 case LV_VIEW_SMALLICON
:
2416 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2418 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2419 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2420 lprcView
->right
= max(lprcView
->right
, x
);
2421 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2423 if (infoPtr
->nItemCount
> 0)
2425 lprcView
->right
+= infoPtr
->nItemWidth
;
2426 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2431 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2432 x
= infoPtr
->nItemCount
/ y
;
2433 if (infoPtr
->nItemCount
% y
) x
++;
2434 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2435 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2442 * Retrieves the bounding rectangle of all the items.
2445 * [I] infoPtr : valid pointer to the listview structure
2446 * [O] lprcView : bounding rectangle
2452 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2456 TRACE("(lprcView=%p)\n", lprcView
);
2458 if (!lprcView
) return FALSE
;
2460 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2462 if (infoPtr
->uView
!= LV_VIEW_DETAILS
)
2464 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2465 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2468 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2475 * Retrieves the subitem pointer associated with the subitem index.
2478 * [I] hdpaSubItems : DPA handle for a specific item
2479 * [I] nSubItem : index of subitem
2482 * SUCCESS : subitem pointer
2485 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2487 SUBITEM_INFO
*lpSubItem
;
2490 /* we should binary search here if need be */
2491 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2493 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
2494 if (lpSubItem
->iSubItem
== nSubItem
)
2504 * Calculates the desired item width.
2507 * [I] infoPtr : valid pointer to the listview structure
2510 * The desired item width.
2512 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2516 TRACE("uView=%d\n", infoPtr
->uView
);
2518 if (infoPtr
->uView
== LV_VIEW_ICON
)
2519 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2520 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
2522 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2527 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
2528 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
2530 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcHeader
);
2531 nItemWidth
= rcHeader
.right
;
2534 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2538 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2539 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2541 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2542 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2544 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2552 * Calculates the desired item height.
2555 * [I] infoPtr : valid pointer to the listview structure
2558 * The desired item height.
2560 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
2564 TRACE("uView=%d\n", infoPtr
->uView
);
2566 if (infoPtr
->uView
== LV_VIEW_ICON
)
2567 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2570 nItemHeight
= infoPtr
->ntmHeight
;
2571 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
2573 if (infoPtr
->himlState
)
2574 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2575 if (infoPtr
->himlSmall
)
2576 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2577 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2578 nItemHeight
+= HEIGHT_PADDING
;
2579 if (infoPtr
->nMeasureItemHeight
> 0)
2580 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2583 return max(nItemHeight
, 1);
2588 * Updates the width, and height of an item.
2591 * [I] infoPtr : valid pointer to the listview structure
2596 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2598 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2599 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2605 * Retrieves and saves important text metrics info for the current
2609 * [I] infoPtr : valid pointer to the listview structure
2612 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2614 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2615 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2616 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2620 if (GetTextMetricsW(hdc
, &tm
))
2622 infoPtr
->ntmHeight
= tm
.tmHeight
;
2623 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
2626 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
2627 infoPtr
->nEllipsisWidth
= sz
.cx
;
2629 SelectObject(hdc
, hOldFont
);
2630 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2632 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2637 * A compare function for ranges
2640 * [I] range1 : pointer to range 1;
2641 * [I] range2 : pointer to range 2;
2645 * > 0 : if range 1 > range 2
2646 * < 0 : if range 2 > range 1
2647 * = 0 : if range intersects range 2
2649 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2653 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2655 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2660 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1
), debugrange(range2
), cmp
);
2666 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2668 #define ranges_check(ranges, desc) do { } while(0)
2671 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2676 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2678 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2679 ranges_dump(ranges
);
2680 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2682 prev
= DPA_GetPtr(ranges
->hdpa
, 0);
2683 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2684 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2686 curr
= DPA_GetPtr(ranges
->hdpa
, i
);
2687 assert (prev
->upper
<= curr
->lower
);
2688 assert (curr
->lower
< curr
->upper
);
2692 TRACE("--- Done checking---\n");
2695 static RANGES
ranges_create(int count
)
2697 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
2698 if (!ranges
) return NULL
;
2699 ranges
->hdpa
= DPA_Create(count
);
2700 if (ranges
->hdpa
) return ranges
;
2705 static void ranges_clear(RANGES ranges
)
2709 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2710 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2711 DPA_DeleteAllPtrs(ranges
->hdpa
);
2715 static void ranges_destroy(RANGES ranges
)
2717 if (!ranges
) return;
2718 ranges_clear(ranges
);
2719 DPA_Destroy(ranges
->hdpa
);
2723 static RANGES
ranges_clone(RANGES ranges
)
2728 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2730 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2732 RANGE
*newrng
= Alloc(sizeof(RANGE
));
2733 if (!newrng
) goto fail
;
2734 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2735 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2740 TRACE ("clone failed\n");
2741 ranges_destroy(clone
);
2745 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2749 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2750 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2755 static void ranges_dump(RANGES ranges
)
2759 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2760 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2763 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2765 RANGE srchrng
= { nItem
, nItem
+ 1 };
2767 TRACE("(nItem=%d)\n", nItem
);
2768 ranges_check(ranges
, "before contain");
2769 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2772 static INT
ranges_itemcount(RANGES ranges
)
2776 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2778 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2779 count
+= sel
->upper
- sel
->lower
;
2785 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2787 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2790 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2791 if (index
== -1) return TRUE
;
2793 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2795 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2796 if (chkrng
->lower
>= nItem
)
2797 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2798 if (chkrng
->upper
> nItem
)
2799 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2804 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2809 TRACE("(%s)\n", debugrange(&range
));
2810 ranges_check(ranges
, "before add");
2812 /* try find overlapping regions first */
2813 srchrgn
.lower
= range
.lower
- 1;
2814 srchrgn
.upper
= range
.upper
+ 1;
2815 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2821 TRACE("Adding new range\n");
2823 /* create the brand new range to insert */
2824 newrgn
= Alloc(sizeof(RANGE
));
2825 if(!newrgn
) goto fail
;
2828 /* figure out where to insert it */
2829 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2830 TRACE("index=%d\n", index
);
2831 if (index
== -1) index
= 0;
2833 /* and get it over with */
2834 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2842 RANGE
*chkrgn
, *mrgrgn
;
2843 INT fromindex
, mergeindex
;
2845 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2846 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2848 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2849 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2851 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2853 /* merge now common ranges */
2855 srchrgn
.lower
= chkrgn
->lower
- 1;
2856 srchrgn
.upper
= chkrgn
->upper
+ 1;
2860 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2861 if (mergeindex
== -1) break;
2862 if (mergeindex
== index
)
2864 fromindex
= index
+ 1;
2868 TRACE("Merge with index %i\n", mergeindex
);
2870 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2871 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2872 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2874 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2875 if (mergeindex
< index
) index
--;
2879 ranges_check(ranges
, "after add");
2883 ranges_check(ranges
, "failed add");
2887 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2892 TRACE("(%s)\n", debugrange(&range
));
2893 ranges_check(ranges
, "before del");
2895 /* we don't use DPAS_SORTED here, since we need *
2896 * to find the first overlapping range */
2897 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2900 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2902 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2904 /* case 1: Same range */
2905 if ( (chkrgn
->upper
== range
.upper
) &&
2906 (chkrgn
->lower
== range
.lower
) )
2908 DPA_DeletePtr(ranges
->hdpa
, index
);
2911 /* case 2: engulf */
2912 else if ( (chkrgn
->upper
<= range
.upper
) &&
2913 (chkrgn
->lower
>= range
.lower
) )
2915 DPA_DeletePtr(ranges
->hdpa
, index
);
2917 /* case 3: overlap upper */
2918 else if ( (chkrgn
->upper
<= range
.upper
) &&
2919 (chkrgn
->lower
< range
.lower
) )
2921 chkrgn
->upper
= range
.lower
;
2923 /* case 4: overlap lower */
2924 else if ( (chkrgn
->upper
> range
.upper
) &&
2925 (chkrgn
->lower
>= range
.lower
) )
2927 chkrgn
->lower
= range
.upper
;
2930 /* case 5: fully internal */
2933 RANGE tmprgn
= *chkrgn
, *newrgn
;
2935 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
2936 newrgn
->lower
= chkrgn
->lower
;
2937 newrgn
->upper
= range
.lower
;
2938 chkrgn
->lower
= range
.upper
;
2939 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2948 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2951 ranges_check(ranges
, "after del");
2955 ranges_check(ranges
, "failed del");
2961 * Removes all selection ranges
2964 * [I] infoPtr : valid pointer to the listview structure
2965 * [I] toSkip : item range to skip removing the selection
2971 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2980 lvItem
.stateMask
= LVIS_SELECTED
;
2982 /* need to clone the DPA because callbacks can change it */
2983 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2984 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2985 while(iterator_next(&i
))
2986 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2987 /* note that the iterator destructor will free the cloned range */
2988 iterator_destroy(&i
);
2993 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2997 if (!(toSkip
= ranges_create(1))) return FALSE
;
2998 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2999 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
3000 ranges_destroy(toSkip
);
3004 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
3006 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
3011 * Retrieves the number of items that are marked as selected.
3014 * [I] infoPtr : valid pointer to the listview structure
3017 * Number of items selected.
3019 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
3021 INT nSelectedCount
= 0;
3023 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3026 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3028 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
3033 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
3035 TRACE("nSelectedCount=%d\n", nSelectedCount
);
3036 return nSelectedCount
;
3041 * Manages the item focus.
3044 * [I] infoPtr : valid pointer to the listview structure
3045 * [I] nItem : item index
3048 * TRUE : focused item changed
3049 * FALSE : focused item has NOT changed
3051 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3053 INT oldFocus
= infoPtr
->nFocusedItem
;
3056 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
3058 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
3059 lvItem
.stateMask
= LVIS_FOCUSED
;
3060 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
3062 return oldFocus
!= infoPtr
->nFocusedItem
;
3065 /* Helper function for LISTVIEW_ShiftIndices *only* */
3066 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
3068 if (nShiftItem
< nItem
) return nShiftItem
;
3070 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
3072 if (direction
> 0) return nShiftItem
+ direction
;
3074 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3079 * Updates the various indices after an item has been inserted or deleted.
3082 * [I] infoPtr : valid pointer to the listview structure
3083 * [I] nItem : item index
3084 * [I] direction : Direction of shift, +1 or -1.
3089 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3094 /* temporarily disable change notification while shifting items */
3095 bOldChange
= infoPtr
->bDoChangeNotify
;
3096 infoPtr
->bDoChangeNotify
= FALSE
;
3098 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
3100 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3102 assert(abs(direction
) == 1);
3104 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3106 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
3107 if (nNewFocus
!= infoPtr
->nFocusedItem
)
3108 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
3110 /* But we are not supposed to modify nHotItem! */
3112 infoPtr
->bDoChangeNotify
= bOldChange
;
3118 * Adds a block of selections.
3121 * [I] infoPtr : valid pointer to the listview structure
3122 * [I] nItem : item index
3125 * Whether the window is still valid.
3127 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3129 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3130 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3131 HWND hwndSelf
= infoPtr
->hwndSelf
;
3132 NMLVODSTATECHANGE nmlv
;
3137 /* Temporarily disable change notification
3138 * If the control is LVS_OWNERDATA, we need to send
3139 * only one LVN_ODSTATECHANGED notification.
3140 * See MSDN documentation for LVN_ITEMCHANGED.
3142 bOldChange
= infoPtr
->bDoChangeNotify
;
3143 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3145 if (nFirst
== -1) nFirst
= nItem
;
3147 item
.state
= LVIS_SELECTED
;
3148 item
.stateMask
= LVIS_SELECTED
;
3150 for (i
= nFirst
; i
<= nLast
; i
++)
3151 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3153 ZeroMemory(&nmlv
, sizeof(nmlv
));
3154 nmlv
.iFrom
= nFirst
;
3157 nmlv
.uOldState
= item
.state
;
3159 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3160 if (!IsWindow(hwndSelf
))
3162 infoPtr
->bDoChangeNotify
= bOldChange
;
3169 * Sets a single group selection.
3172 * [I] infoPtr : valid pointer to the listview structure
3173 * [I] nItem : item index
3178 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3185 if (!(selection
= ranges_create(100))) return;
3187 item
.state
= LVIS_SELECTED
;
3188 item
.stateMask
= LVIS_SELECTED
;
3190 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
3192 if (infoPtr
->nSelectionMark
== -1)
3194 infoPtr
->nSelectionMark
= nItem
;
3195 ranges_additem(selection
, nItem
);
3201 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3202 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3203 ranges_add(selection
, sel
);
3208 RECT rcItem
, rcSel
, rcSelMark
;
3211 rcItem
.left
= LVIR_BOUNDS
;
3212 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3213 rcSelMark
.left
= LVIR_BOUNDS
;
3214 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3215 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3216 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3217 while(iterator_next(&i
))
3219 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3220 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3222 iterator_destroy(&i
);
3225 /* disable per item notifications on LVS_OWNERDATA style
3226 FIXME: single LVN_ODSTATECHANGED should be used */
3227 bOldChange
= infoPtr
->bDoChangeNotify
;
3228 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3230 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3233 iterator_rangesitems(&i
, selection
);
3234 while(iterator_next(&i
))
3235 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3236 /* this will also destroy the selection */
3237 iterator_destroy(&i
);
3239 infoPtr
->bDoChangeNotify
= bOldChange
;
3241 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3246 * Sets a single selection.
3249 * [I] infoPtr : valid pointer to the listview structure
3250 * [I] nItem : item index
3255 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3259 TRACE("nItem=%d\n", nItem
);
3261 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3263 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3264 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3265 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3267 infoPtr
->nSelectionMark
= nItem
;
3272 * Set selection(s) with keyboard.
3275 * [I] infoPtr : valid pointer to the listview structure
3276 * [I] nItem : item index
3277 * [I] space : VK_SPACE code sent
3280 * SUCCESS : TRUE (needs to be repainted)
3281 * FAILURE : FALSE (nothing has changed)
3283 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL space
)
3285 /* FIXME: pass in the state */
3286 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3287 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3288 BOOL bResult
= FALSE
;
3290 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3291 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3295 if (infoPtr
->dwStyle
& LVS_SINGLESEL
|| (wShift
== 0 && wCtrl
== 0))
3296 LISTVIEW_SetSelection(infoPtr
, nItem
);
3300 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3304 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3305 lvItem
.stateMask
= LVIS_SELECTED
;
3308 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3309 if (lvItem
.state
& LVIS_SELECTED
)
3310 infoPtr
->nSelectionMark
= nItem
;
3312 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3315 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3318 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3322 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3324 LVHITTESTINFO lvHitTestInfo
;
3326 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3327 lvHitTestInfo
.pt
.x
= pt
.x
;
3328 lvHitTestInfo
.pt
.y
= pt
.y
;
3330 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3332 lpLVItem
->mask
= LVIF_PARAM
;
3333 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3334 lpLVItem
->iSubItem
= 0;
3336 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3339 static inline BOOL
LISTVIEW_isHotTracking(const LISTVIEW_INFO
*infoPtr
)
3341 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3342 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3343 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3348 * Called when the mouse is being actively tracked and has hovered for a specified
3352 * [I] infoPtr : valid pointer to the listview structure
3353 * [I] fwKeys : key indicator
3354 * [I] x,y : mouse position
3357 * 0 if the message was processed, non-zero if there was an error
3360 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3361 * over the item for a certain period of time.
3364 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3366 if (LISTVIEW_isHotTracking(infoPtr
))
3374 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3375 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3383 * Called whenever WM_MOUSEMOVE is received.
3386 * [I] infoPtr : valid pointer to the listview structure
3387 * [I] fwKeys : key indicator
3388 * [I] x,y : mouse position
3391 * 0 if the message is processed, non-zero if there was an error
3393 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3395 TRACKMOUSEEVENT trackinfo
;
3397 if (!(fwKeys
& MK_LBUTTON
))
3398 infoPtr
->bLButtonDown
= FALSE
;
3400 if (infoPtr
->bLButtonDown
)
3404 LVHITTESTINFO lvHitTestInfo
;
3405 WORD wDragWidth
= GetSystemMetrics(SM_CXDRAG
);
3406 WORD wDragHeight
= GetSystemMetrics(SM_CYDRAG
);
3408 rect
.left
= infoPtr
->ptClickPos
.x
- wDragWidth
;
3409 rect
.right
= infoPtr
->ptClickPos
.x
+ wDragWidth
;
3410 rect
.top
= infoPtr
->ptClickPos
.y
- wDragHeight
;
3411 rect
.bottom
= infoPtr
->ptClickPos
.y
+ wDragHeight
;
3416 lvHitTestInfo
.pt
= tmp
;
3417 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3419 /* reset item marker */
3420 if (infoPtr
->nLButtonDownItem
!= lvHitTestInfo
.iItem
)
3421 infoPtr
->nLButtonDownItem
= -1;
3423 if (!PtInRect(&rect
, tmp
))
3425 /* this path covers the following:
3426 1. WM_LBUTTONDOWN over selected item (sets focus on it)
3427 2. change focus with keys
3428 3. move mouse over item from step 1 selects it and moves focus on it */
3429 if (infoPtr
->nLButtonDownItem
!= -1 &&
3430 !LISTVIEW_GetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, LVIS_SELECTED
))
3434 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3435 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3437 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nLButtonDownItem
, &lvItem
);
3438 infoPtr
->nLButtonDownItem
= -1;
3441 if (!infoPtr
->bDragging
)
3445 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3446 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3448 ZeroMemory(&nmlv
, sizeof(nmlv
));
3449 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3450 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3452 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3453 infoPtr
->bDragging
= TRUE
;
3460 /* see if we are supposed to be tracking mouse hovering */
3461 if (LISTVIEW_isHotTracking(infoPtr
)) {
3462 /* fill in the trackinfo struct */
3463 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3464 trackinfo
.dwFlags
= TME_QUERY
;
3465 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3466 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3468 /* see if we are already tracking this hwnd */
3469 _TrackMouseEvent(&trackinfo
);
3471 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3472 trackinfo
.dwFlags
= TME_HOVER
;
3474 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3475 _TrackMouseEvent(&trackinfo
);
3484 * Tests whether the item is assignable to a list with style lStyle
3486 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3488 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3489 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3490 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3498 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3501 * [I] infoPtr : valid pointer to the listview structure
3502 * [I] lpLVItem : valid pointer to new item attributes
3503 * [I] isNew : the item being set is being inserted
3504 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3505 * [O] bChanged : will be set to TRUE if the item really changed
3511 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3517 /* stateMask is ignored for LVM_INSERTITEM */
3518 UINT stateMask
= isNew
? ~0 : lpLVItem
->stateMask
;
3522 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3524 if (lpLVItem
->mask
== 0) return TRUE
;
3526 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3528 /* a virtual listview only stores selection and focus */
3529 if (lpLVItem
->mask
& ~LVIF_STATE
)
3535 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3536 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
3540 /* we need to get the lParam and state of the item */
3541 item
.iItem
= lpLVItem
->iItem
;
3542 item
.iSubItem
= lpLVItem
->iSubItem
;
3543 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3544 item
.stateMask
= (infoPtr
->dwStyle
& LVS_OWNERDATA
) ? LVIS_FOCUSED
| LVIS_SELECTED
: ~0;
3548 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3550 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3551 /* determine what fields will change */
3552 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & stateMask
& ~infoPtr
->uCallbackMask
))
3553 uChanged
|= LVIF_STATE
;
3555 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3556 uChanged
|= LVIF_IMAGE
;
3558 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3559 uChanged
|= LVIF_PARAM
;
3561 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3562 uChanged
|= LVIF_INDENT
;
3564 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3565 uChanged
|= LVIF_TEXT
;
3567 TRACE("uChanged=0x%x\n", uChanged
);
3568 if (!uChanged
) return TRUE
;
3571 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3572 nmlv
.iItem
= lpLVItem
->iItem
;
3573 nmlv
.uNewState
= (item
.state
& ~stateMask
) | (lpLVItem
->state
& stateMask
);
3574 nmlv
.uOldState
= item
.state
;
3575 nmlv
.uChanged
= uChanged
;
3576 nmlv
.lParam
= item
.lParam
;
3578 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3579 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3581 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
3583 HWND hwndSelf
= infoPtr
->hwndSelf
;
3585 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3587 if (!IsWindow(hwndSelf
))
3591 /* copy information */
3592 if (lpLVItem
->mask
& LVIF_TEXT
)
3593 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3595 if (lpLVItem
->mask
& LVIF_IMAGE
)
3596 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3598 if (lpLVItem
->mask
& LVIF_PARAM
)
3599 lpItem
->lParam
= lpLVItem
->lParam
;
3601 if (lpLVItem
->mask
& LVIF_INDENT
)
3602 lpItem
->iIndent
= lpLVItem
->iIndent
;
3604 if (uChanged
& LVIF_STATE
)
3606 if (lpItem
&& (stateMask
& ~infoPtr
->uCallbackMask
))
3608 lpItem
->state
&= ~stateMask
;
3609 lpItem
->state
|= (lpLVItem
->state
& stateMask
);
3611 if (lpLVItem
->state
& stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3613 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3614 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3616 else if (stateMask
& LVIS_SELECTED
)
3618 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3620 /* if we are asked to change focus, and we manage it, do it */
3621 if (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3623 if (lpLVItem
->state
& LVIS_FOCUSED
)
3625 if (infoPtr
->nFocusedItem
!= -1)
3627 /* remove current focus */
3628 item
.mask
= LVIF_STATE
;
3630 item
.stateMask
= LVIS_FOCUSED
;
3632 /* recurse with redrawing an item */
3633 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nFocusedItem
, &item
);
3636 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3637 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, infoPtr
->uView
== LV_VIEW_LIST
);
3639 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3641 infoPtr
->nFocusedItem
= -1;
3646 /* if we're inserting the item, we're done */
3647 if (isNew
) return TRUE
;
3649 /* send LVN_ITEMCHANGED notification */
3650 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3651 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3658 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3661 * [I] infoPtr : valid pointer to the listview structure
3662 * [I] lpLVItem : valid pointer to new subitem attributes
3663 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3664 * [O] bChanged : will be set to TRUE if the item really changed
3670 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3673 SUBITEM_INFO
*lpSubItem
;
3675 /* we do not support subitems for virtual listviews */
3676 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3678 /* set subitem only if column is present */
3679 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3681 /* First do some sanity checks */
3682 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3683 particularly useful. We currently do not actually do anything with
3684 the flag on subitems.
3686 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
)) return FALSE
;
3687 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
3689 /* get the subitem structure, and create it if not there */
3690 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3691 assert (hdpaSubItems
);
3693 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3696 SUBITEM_INFO
*tmpSubItem
;
3699 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
3700 if (!lpSubItem
) return FALSE
;
3701 /* we could binary search here, if need be...*/
3702 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3704 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
3705 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3707 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3712 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3713 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3717 if (lpLVItem
->mask
& LVIF_IMAGE
)
3718 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3720 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3724 if (lpLVItem
->mask
& LVIF_TEXT
)
3725 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3727 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3736 * Sets item attributes.
3739 * [I] infoPtr : valid pointer to the listview structure
3740 * [I] lpLVItem : new item attributes
3741 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3747 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
3749 HWND hwndSelf
= infoPtr
->hwndSelf
;
3750 LPWSTR pszText
= NULL
;
3751 BOOL bResult
, bChanged
= FALSE
;
3753 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3755 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3758 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3759 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3761 pszText
= lpLVItem
->pszText
;
3762 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3765 /* actually set the fields */
3766 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3768 if (lpLVItem
->iSubItem
)
3769 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3771 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3772 if (!IsWindow(hwndSelf
))
3775 /* redraw item, if necessary */
3776 if (bChanged
&& !infoPtr
->bIsDrawing
)
3778 /* this little optimization eliminates some nasty flicker */
3779 if ( infoPtr
->uView
== LV_VIEW_DETAILS
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3780 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
3781 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
3782 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3784 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3789 textfreeT(lpLVItem
->pszText
, isW
);
3790 lpLVItem
->pszText
= pszText
;
3798 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3801 * [I] infoPtr : valid pointer to the listview structure
3806 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
3809 SCROLLINFO scrollInfo
;
3811 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3812 scrollInfo
.fMask
= SIF_POS
;
3814 if (infoPtr
->uView
== LV_VIEW_LIST
)
3816 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3817 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3819 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
3821 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3822 nItem
= scrollInfo
.nPos
;
3826 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3827 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3830 TRACE("nItem=%d\n", nItem
);
3838 * Erases the background of the given rectangle
3841 * [I] infoPtr : valid pointer to the listview structure
3842 * [I] hdc : device context handle
3843 * [I] lprcBox : clipping rectangle
3849 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3851 if (!infoPtr
->hBkBrush
) return FALSE
;
3853 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
3855 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3863 * [I] infoPtr : valid pointer to the listview structure
3864 * [I] hdc : device context handle
3865 * [I] nItem : item index
3866 * [I] nSubItem : subitem index
3867 * [I] pos : item position in client coordinates
3868 * [I] cdmode : custom draw mode
3874 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3877 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3878 static WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3879 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3881 RECT rcSelect
, rcBox
, rcIcon
, rcLabel
, rcStateIcon
;
3882 NMLVCUSTOMDRAW nmlvcd
;
3887 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, wine_dbgstr_point(&pos
));
3889 /* get information needed for drawing the item */
3890 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
;
3891 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
;
3892 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
3893 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3894 lvItem
.iItem
= nItem
;
3895 lvItem
.iSubItem
= nSubItem
;
3898 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3899 lvItem
.pszText
= szDispText
;
3900 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3901 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3902 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3903 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= szCallback
;
3904 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3906 /* now check if we need to update the focus rectangle */
3907 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3909 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3910 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
3911 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3912 OffsetRect(&rcSelect
, pos
.x
, pos
.y
);
3913 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3914 OffsetRect(&rcStateIcon
, pos
.x
, pos
.y
);
3915 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3916 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3917 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
3918 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
3920 /* fill in the custom draw structure */
3921 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3923 hOldFont
= GetCurrentObject(hdc
, OBJ_FONT
);
3924 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3925 if (cdmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3926 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3927 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3928 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3929 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3930 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3931 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3933 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3934 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3936 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3937 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
3938 else if ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) == FALSE
)
3939 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
3941 /* in full row select, subitems, will just use main item's colors */
3942 if (nSubItem
&& infoPtr
->uView
== LV_VIEW_DETAILS
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3943 nmlvcd
.clrTextBk
= CLR_NONE
;
3946 if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && (nSubItem
== 0))
3948 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
3951 TRACE("uStateImage=%d\n", uStateImage
);
3952 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
,
3953 rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
3958 himl
= (infoPtr
->uView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3959 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3961 TRACE("iImage=%d\n", lvItem
.iImage
);
3962 ImageList_DrawEx(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3963 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
, CLR_DEFAULT
,
3964 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3967 /* Don't bother painting item being edited */
3968 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3970 /* FIXME: temporary hack */
3971 rcSelect
.left
= rcLabel
.left
;
3973 /* draw the selection background, if we're drawing the main item */
3976 /* in icon mode, the label rect is really what we want to draw the
3978 if (infoPtr
->uView
== LV_VIEW_ICON
)
3981 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3982 rcSelect
.right
= rcBox
.right
;
3984 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3985 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3986 /* store new focus rectangle */
3987 if (infoPtr
->nFocusedItem
== nItem
) infoPtr
->rcFocus
= rcSelect
;
3990 /* figure out the text drawing flags */
3991 uFormat
= (infoPtr
->uView
== LV_VIEW_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3992 if (infoPtr
->uView
== LV_VIEW_ICON
)
3993 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3996 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3998 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3999 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
4000 default: uFormat
|= DT_LEFT
;
4003 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
4005 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
4006 else rcLabel
.left
+= LABEL_HOR_PADDING
;
4008 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
4010 /* for GRIDLINES reduce the bottom so the text formats correctly */
4011 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4014 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
4017 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
4018 notify_postpaint(infoPtr
, &nmlvcd
);
4019 if (cdsubitemmode
& CDRF_NEWFONT
)
4020 SelectObject(hdc
, hOldFont
);
4026 * Draws listview items when in owner draw mode.
4029 * [I] infoPtr : valid pointer to the listview structure
4030 * [I] hdc : device context handle
4035 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4037 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
4038 DWORD cditemmode
= CDRF_DODEFAULT
;
4039 NMLVCUSTOMDRAW nmlvcd
;
4040 POINT Origin
, Position
;
4046 ZeroMemory(&dis
, sizeof(dis
));
4048 /* Get scroll info once before loop */
4049 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4051 /* iterate through the invalidated rows */
4052 while(iterator_next(i
))
4054 item
.iItem
= i
->nItem
;
4056 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4057 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4058 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
4060 dis
.CtlType
= ODT_LISTVIEW
;
4062 dis
.itemID
= item
.iItem
;
4063 dis
.itemAction
= ODA_DRAWENTIRE
;
4065 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
4066 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
4067 dis
.hwndItem
= infoPtr
->hwndSelf
;
4069 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
4070 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
4071 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
4072 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
4073 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
4074 dis
.itemData
= item
.lParam
;
4076 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
4079 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4080 * structure for the rest. of the paint cycle
4082 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4083 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4084 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4086 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4088 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4089 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4092 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4093 notify_postpaint(infoPtr
, &nmlvcd
);
4099 * Draws listview items when in report display mode.
4102 * [I] infoPtr : valid pointer to the listview structure
4103 * [I] hdc : device context handle
4104 * [I] cdmode : custom draw mode
4109 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4112 RECT rcClip
, rcItem
;
4113 POINT Origin
, Position
;
4120 /* figure out what to draw */
4121 rgntype
= GetClipBox(hdc
, &rcClip
);
4122 if (rgntype
== NULLREGION
) return;
4124 /* Get scroll info once before loop */
4125 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4127 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4129 /* narrow down the columns we need to paint */
4130 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
4132 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
4134 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4135 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
4136 ranges_additem(colRanges
, index
);
4138 iterator_rangesitems(&j
, colRanges
);
4140 /* in full row select, we _have_ to draw the main item */
4141 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4144 /* iterate through the invalidated rows */
4145 while(iterator_next(i
))
4147 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4148 Position
.x
+= Origin
.x
;
4149 Position
.y
+= Origin
.y
;
4151 /* iterate through the invalidated columns */
4152 while(iterator_next(&j
))
4154 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4156 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4158 rcItem
.bottom
= infoPtr
->nItemHeight
;
4159 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
4160 if (!RectVisible(hdc
, &rcItem
)) continue;
4163 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
4166 iterator_destroy(&j
);
4171 * Draws the gridlines if necessary when in report display mode.
4174 * [I] infoPtr : valid pointer to the listview structure
4175 * [I] hdc : device context handle
4180 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
4186 RECT rcClip
, rcItem
= {0};
4194 /* figure out what to draw */
4195 rgntype
= GetClipBox(hdc
, &rcClip
);
4196 if (rgntype
== NULLREGION
) return;
4198 /* Get scroll info once before loop */
4199 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4201 colRanges
= ranges_create(DPA_GetPtrCount(infoPtr
->hdpaColumns
));
4203 /* narrow down the columns we need to paint */
4204 for(col
= 0; col
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); col
++)
4206 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, col
, 0);
4208 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4209 if ((rcItem
.right
+ Origin
.x
>= rcClip
.left
) && (rcItem
.left
+ Origin
.x
< rcClip
.right
))
4210 ranges_additem(colRanges
, index
);
4213 /* is right most vertical line visible? */
4214 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
4216 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
4217 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4218 rmost
= (rcItem
.right
+ Origin
.x
< rcClip
.right
);
4221 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
4223 hOldPen
= SelectObject ( hdc
, hPen
);
4225 /* draw the vertical lines for the columns */
4226 iterator_rangesitems(&j
, colRanges
);
4227 while(iterator_next(&j
))
4229 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4230 if (rcItem
.left
== 0) continue; /* skip leftmost column */
4231 rcItem
.left
+= Origin
.x
;
4232 rcItem
.right
+= Origin
.x
;
4233 rcItem
.top
= infoPtr
->rcList
.top
;
4234 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
4235 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
4236 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4237 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
4239 iterator_destroy(&j
);
4240 /* draw rightmost grid line if visible */
4243 index
= SendMessageW(infoPtr
->hwndHeader
, HDM_ORDERTOINDEX
,
4244 DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, 0);
4245 LISTVIEW_GetHeaderRect(infoPtr
, index
, &rcItem
);
4247 rcItem
.right
+= Origin
.x
;
4249 MoveToEx (hdc
, rcItem
.right
, infoPtr
->rcList
.top
, NULL
);
4250 LineTo (hdc
, rcItem
.right
, infoPtr
->rcList
.bottom
);
4253 /* draw the horizontial lines for the rows */
4254 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
4255 rcItem
.left
= infoPtr
->rcList
.left
;
4256 rcItem
.right
= infoPtr
->rcList
.right
;
4257 rcItem
.bottom
= rcItem
.top
= Origin
.y
- 1;
4258 MoveToEx(hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4259 LineTo(hdc
, rcItem
.right
, rcItem
.top
);
4260 for(y
=itemheight
-1+Origin
.y
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
4262 rcItem
.bottom
= rcItem
.top
= y
;
4263 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
4264 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4265 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
4268 SelectObject( hdc
, hOldPen
);
4269 DeleteObject( hPen
);
4275 * Draws listview items when in list display mode.
4278 * [I] infoPtr : valid pointer to the listview structure
4279 * [I] hdc : device context handle
4280 * [I] cdmode : custom draw mode
4285 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4287 POINT Origin
, Position
;
4289 /* Get scroll info once before loop */
4290 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4292 while(iterator_prev(i
))
4294 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4295 Position
.x
+= Origin
.x
;
4296 Position
.y
+= Origin
.y
;
4298 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
4305 * Draws listview items.
4308 * [I] infoPtr : valid pointer to the listview structure
4309 * [I] hdc : device context handle
4310 * [I] prcErase : rect to be erased before refresh (may be NULL)
4315 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
4317 COLORREF oldTextColor
= 0, oldBkColor
= 0, oldClrTextBk
, oldClrText
;
4318 NMLVCUSTOMDRAW nmlvcd
;
4325 HBITMAP hbmp
= NULL
;
4328 LISTVIEW_DUMP(infoPtr
);
4330 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4331 TRACE("double buffering\n");
4333 hdc
= CreateCompatibleDC(hdcOrig
);
4335 ERR("Failed to create DC for backbuffer\n");
4338 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
4339 infoPtr
->rcList
.bottom
);
4341 ERR("Failed to create bitmap for backbuffer\n");
4346 SelectObject(hdc
, hbmp
);
4347 SelectObject(hdc
, infoPtr
->hFont
);
4349 /* Save dc values we're gonna trash while drawing
4350 * FIXME: Should be done in LISTVIEW_DrawItem() */
4351 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
4352 oldBkMode
= GetBkMode(hdc
);
4353 oldBkColor
= GetBkColor(hdc
);
4354 oldTextColor
= GetTextColor(hdc
);
4357 infoPtr
->bIsDrawing
= TRUE
;
4360 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
4361 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4362 /* If no erasing was done (usually because RedrawWindow was called
4363 * with RDW_INVALIDATE only) we need to copy the old contents into
4364 * the backbuffer before continuing. */
4365 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4366 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4367 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4368 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4371 /* FIXME: Shouldn't need to do this */
4372 oldClrTextBk
= infoPtr
->clrTextBk
;
4373 oldClrText
= infoPtr
->clrText
;
4375 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
4377 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
4378 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
4379 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4380 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
4381 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4383 /* Use these colors to draw the items */
4384 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
4385 infoPtr
->clrText
= nmlvcd
.clrText
;
4387 /* nothing to draw */
4388 if(infoPtr
->nItemCount
== 0) goto enddraw
;
4390 /* figure out what we need to draw */
4391 iterator_visibleitems(&i
, infoPtr
, hdc
);
4392 range
= iterator_range(&i
);
4394 /* send cache hint notification */
4395 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4399 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4400 nmlv
.iFrom
= range
.lower
;
4401 nmlv
.iTo
= range
.upper
- 1;
4402 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4405 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
4406 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4409 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4410 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4411 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
4412 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4414 /* if we have a focus rect and it's visible, draw it */
4415 if (infoPtr
->bFocus
&& range
.lower
<= infoPtr
->nFocusedItem
&&
4416 (range
.upper
- 1) >= infoPtr
->nFocusedItem
)
4417 LISTVIEW_DrawFocusRect(infoPtr
, hdc
);
4419 iterator_destroy(&i
);
4422 /* For LVS_EX_GRIDLINES go and draw lines */
4423 /* This includes the case where there were *no* items */
4424 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4425 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
4427 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4428 notify_postpaint(infoPtr
, &nmlvcd
);
4430 infoPtr
->clrTextBk
= oldClrTextBk
;
4431 infoPtr
->clrText
= oldClrText
;
4434 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4435 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4436 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4437 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4442 SelectObject(hdc
, hOldFont
);
4443 SetBkMode(hdc
, oldBkMode
);
4444 SetBkColor(hdc
, oldBkColor
);
4445 SetTextColor(hdc
, oldTextColor
);
4448 infoPtr
->bIsDrawing
= FALSE
;
4454 * Calculates the approximate width and height of a given number of items.
4457 * [I] infoPtr : valid pointer to the listview structure
4458 * [I] nItemCount : number of items
4459 * [I] wWidth : width
4460 * [I] wHeight : height
4463 * Returns a DWORD. The width in the low word and the height in high word.
4465 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4466 WORD wWidth
, WORD wHeight
)
4468 INT nItemCountPerColumn
= 1;
4469 INT nColumnCount
= 0;
4470 DWORD dwViewRect
= 0;
4472 if (nItemCount
== -1)
4473 nItemCount
= infoPtr
->nItemCount
;
4475 if (infoPtr
->uView
== LV_VIEW_LIST
)
4477 if (wHeight
== 0xFFFF)
4479 /* use current height */
4480 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4483 if (wHeight
< infoPtr
->nItemHeight
)
4484 wHeight
= infoPtr
->nItemHeight
;
4488 if (infoPtr
->nItemHeight
> 0)
4490 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4491 if (nItemCountPerColumn
== 0)
4492 nItemCountPerColumn
= 1;
4494 if (nItemCount
% nItemCountPerColumn
!= 0)
4495 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4497 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4501 /* Microsoft padding magic */
4502 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4503 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4505 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4507 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4511 if (infoPtr
->nItemCount
> 0)
4513 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4514 wWidth
= rcBox
.right
- rcBox
.left
;
4515 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4519 /* use current height and width */
4520 if (wHeight
== 0xffff)
4521 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4522 if (wWidth
== 0xffff)
4523 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4526 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4528 else if (infoPtr
->uView
== LV_VIEW_SMALLICON
)
4529 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
4530 else if (infoPtr
->uView
== LV_VIEW_ICON
)
4531 FIXME("uView == LV_VIEW_ICON: not implemented\n");
4539 * Create a drag image list for the specified item.
4542 * [I] infoPtr : valid pointer to the listview structure
4543 * [I] iItem : index of item
4544 * [O] lppt : Upper-left corner of the image
4547 * Returns a handle to the image list if successful, NULL otherwise.
4549 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4555 HBITMAP hbmp
, hOldbmp
;
4556 HIMAGELIST dragList
= 0;
4557 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
4559 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4562 rcItem
.left
= LVIR_BOUNDS
;
4563 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4566 lppt
->x
= rcItem
.left
;
4567 lppt
->y
= rcItem
.top
;
4569 size
.cx
= rcItem
.right
- rcItem
.left
;
4570 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4572 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4573 hdc
= CreateCompatibleDC(hdcOrig
);
4574 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4575 hOldbmp
= SelectObject(hdc
, hbmp
);
4577 rcItem
.left
= rcItem
.top
= 0;
4578 rcItem
.right
= size
.cx
;
4579 rcItem
.bottom
= size
.cy
;
4580 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4583 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4585 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4586 SelectObject(hdc
, hOldbmp
);
4587 ImageList_Add(dragList
, hbmp
, 0);
4590 SelectObject(hdc
, hOldbmp
);
4594 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4596 TRACE("ret=%p\n", dragList
);
4604 * Removes all listview items and subitems.
4607 * [I] infoPtr : valid pointer to the listview structure
4613 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
4616 HDPA hdpaSubItems
= NULL
;
4623 /* we do it directly, to avoid notifications */
4624 ranges_clear(infoPtr
->selectionRanges
);
4625 infoPtr
->nSelectionMark
= -1;
4626 infoPtr
->nFocusedItem
= -1;
4627 SetRectEmpty(&infoPtr
->rcFocus
);
4628 /* But we are supposed to leave nHotItem as is! */
4631 /* send LVN_DELETEALLITEMS notification */
4632 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4634 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4636 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4638 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4640 /* send LVN_DELETEITEM notification, if not suppressed
4641 and if it is not a virtual listview */
4642 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4643 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4644 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4646 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
4647 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4650 DPA_Destroy(hdpaSubItems
);
4651 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4653 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4654 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4655 infoPtr
->nItemCount
--;
4660 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4661 LISTVIEW_UpdateScroll(infoPtr
);
4663 LISTVIEW_InvalidateList(infoPtr
);
4670 * Scrolls, and updates the columns, when a column is changing width.
4673 * [I] infoPtr : valid pointer to the listview structure
4674 * [I] nColumn : column to scroll
4675 * [I] dx : amount of scroll, in pixels
4680 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4682 COLUMN_INFO
*lpColumnInfo
;
4687 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4688 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4689 rcCol
= lpColumnInfo
->rcHeader
;
4690 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4691 rcCol
.left
= rcCol
.right
;
4693 /* adjust the other columns */
4694 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4696 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4697 lpColumnInfo
->rcHeader
.left
+= dx
;
4698 lpColumnInfo
->rcHeader
.right
+= dx
;
4701 /* do not update screen if not in report mode */
4702 if (!is_redrawing(infoPtr
) || infoPtr
->uView
!= LV_VIEW_DETAILS
) return;
4704 /* Need to reset the item width when inserting a new column */
4705 infoPtr
->nItemWidth
+= dx
;
4707 LISTVIEW_UpdateScroll(infoPtr
);
4708 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
4710 /* scroll to cover the deleted column, and invalidate for redraw */
4711 rcOld
= infoPtr
->rcList
;
4712 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
4713 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4718 * Removes a column from the listview control.
4721 * [I] infoPtr : valid pointer to the listview structure
4722 * [I] nColumn : column index
4728 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4732 TRACE("nColumn=%d\n", nColumn
);
4734 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4735 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4737 /* While the MSDN specifically says that column zero should not be deleted,
4738 what actually happens is that the column itself is deleted but no items or subitems
4742 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4744 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nColumn
, 0))
4747 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4748 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4750 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4752 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4754 INT nItem
, nSubItem
, i
;
4756 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4758 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4761 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4763 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4764 if (lpSubItem
->iSubItem
== nColumn
)
4767 lpDelItem
= lpSubItem
;
4769 else if (lpSubItem
->iSubItem
> nColumn
)
4771 lpSubItem
->iSubItem
--;
4775 /* if we found our subitem, zapp it */
4779 if (is_textW(lpDelItem
->hdr
.pszText
))
4780 Free(lpDelItem
->hdr
.pszText
);
4785 /* free dpa memory */
4786 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4791 /* update the other column info */
4792 LISTVIEW_UpdateItemSize(infoPtr
);
4793 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4794 LISTVIEW_InvalidateList(infoPtr
);
4796 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4803 * Invalidates the listview after an item's insertion or deletion.
4806 * [I] infoPtr : valid pointer to the listview structure
4807 * [I] nItem : item index
4808 * [I] dir : -1 if deleting, 1 if inserting
4813 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4815 INT nPerCol
, nItemCol
, nItemRow
;
4819 /* if we don't refresh, what's the point of scrolling? */
4820 if (!is_redrawing(infoPtr
)) return;
4822 assert (abs(dir
) == 1);
4824 /* arrange icons if autoarrange is on */
4825 if (is_autoarrange(infoPtr
))
4827 BOOL arrange
= TRUE
;
4828 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4829 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4830 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4833 /* scrollbars need updating */
4834 LISTVIEW_UpdateScroll(infoPtr
);
4836 /* figure out the item's position */
4837 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
4838 nPerCol
= infoPtr
->nItemCount
+ 1;
4839 else if (infoPtr
->uView
== LV_VIEW_LIST
)
4840 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4841 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
4844 nItemCol
= nItem
/ nPerCol
;
4845 nItemRow
= nItem
% nPerCol
;
4846 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4848 /* move the items below up a slot */
4849 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4850 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4851 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4852 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4853 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4854 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4855 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4857 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
4858 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4859 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4862 /* report has only that column, so we're done */
4863 if (infoPtr
->uView
== LV_VIEW_DETAILS
) return;
4865 /* now for LISTs, we have to deal with the columns to the right */
4866 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4868 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4869 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4870 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4871 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4872 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4873 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4878 * Removes an item from the listview control.
4881 * [I] infoPtr : valid pointer to the listview structure
4882 * [I] nItem : item index
4888 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4891 const BOOL is_icon
= (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
);
4893 TRACE("(nItem=%d)\n", nItem
);
4895 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4897 /* remove selection, and focus */
4899 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4900 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4902 /* send LVN_DELETEITEM notification. */
4903 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
4905 /* we need to do this here, because we'll be deleting stuff */
4907 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4909 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4915 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4916 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4918 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
4919 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4922 DPA_Destroy(hdpaSubItems
);
4927 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4928 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4931 infoPtr
->nItemCount
--;
4932 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4934 /* now is the invalidation fun */
4936 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4943 * Callback implementation for editlabel control
4946 * [I] infoPtr : valid pointer to the listview structure
4947 * [I] storeText : store edit box text as item text
4948 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4954 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, BOOL storeText
, BOOL isW
)
4956 HWND hwndSelf
= infoPtr
->hwndSelf
;
4957 NMLVDISPINFOW dispInfo
;
4958 INT editedItem
= infoPtr
->nEditLabelItem
;
4960 WCHAR
*pszText
= NULL
;
4965 DWORD len
= isW
? GetWindowTextLengthW(infoPtr
->hwndEdit
) : GetWindowTextLengthA(infoPtr
->hwndEdit
);
4969 if ((pszText
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))))
4971 if (isW
) GetWindowTextW(infoPtr
->hwndEdit
, pszText
, len
+1);
4972 else GetWindowTextA(infoPtr
->hwndEdit
, (CHAR
*)pszText
, len
+1);
4977 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4979 infoPtr
->nEditLabelItem
= -1;
4980 infoPtr
->hwndEdit
= 0;
4982 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4983 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4984 dispInfo
.item
.iItem
= editedItem
;
4985 dispInfo
.item
.iSubItem
= 0;
4986 dispInfo
.item
.stateMask
= ~0;
4987 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
))
4994 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
4997 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
4998 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
4999 textfreeT(tmp
, FALSE
);
5007 /* add the text from the edit in */
5008 dispInfo
.item
.mask
|= LVIF_TEXT
;
5009 dispInfo
.item
.pszText
= pszText
;
5010 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
5012 /* Do we need to update the Item Text */
5013 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
))
5018 if (!IsWindow(hwndSelf
))
5023 if (!pszText
) return TRUE
;
5025 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
5027 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
5028 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5029 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
5031 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
5037 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5038 dispInfo
.item
.mask
= LVIF_TEXT
;
5039 dispInfo
.item
.iItem
= editedItem
;
5040 dispInfo
.item
.iSubItem
= 0;
5041 dispInfo
.item
.pszText
= pszText
;
5042 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
5043 res
= LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
5053 * Begin in place editing of specified list view item
5056 * [I] infoPtr : valid pointer to the listview structure
5057 * [I] nItem : item index
5058 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5064 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
5066 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
5067 NMLVDISPINFOW dispInfo
;
5070 HWND hwndSelf
= infoPtr
->hwndSelf
;
5072 HFONT hOldFont
= NULL
;
5073 TEXTMETRICW textMetric
;
5075 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
5077 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
5079 /* Is the EditBox still there, if so remove it */
5080 if(infoPtr
->hwndEdit
!= 0)
5082 SetFocus(infoPtr
->hwndSelf
);
5083 infoPtr
->hwndEdit
= 0;
5086 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5088 infoPtr
->nEditLabelItem
= nItem
;
5090 LISTVIEW_SetSelection(infoPtr
, nItem
);
5091 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
5092 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
5094 rect
.left
= LVIR_LABEL
;
5095 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
5097 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5098 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
5099 dispInfo
.item
.iItem
= nItem
;
5100 dispInfo
.item
.iSubItem
= 0;
5101 dispInfo
.item
.stateMask
= ~0;
5102 dispInfo
.item
.pszText
= szDispText
;
5103 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
5104 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
5106 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
, isW
);
5107 if (!infoPtr
->hwndEdit
) return 0;
5109 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
5111 if (!IsWindow(hwndSelf
))
5113 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
5114 infoPtr
->hwndEdit
= 0;
5118 /* Now position and display edit box */
5119 hdc
= GetDC(infoPtr
->hwndSelf
);
5121 /* Select the font to get appropriate metric dimensions */
5122 if(infoPtr
->hFont
!= 0)
5123 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
5125 /* Get String Length in pixels */
5126 GetTextExtentPoint32W(hdc
, dispInfo
.item
.pszText
, lstrlenW(dispInfo
.item
.pszText
), &sz
);
5128 /* Add Extra spacing for the next character */
5129 GetTextMetricsW(hdc
, &textMetric
);
5130 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
5132 if(infoPtr
->hFont
!= 0)
5133 SelectObject(hdc
, hOldFont
);
5135 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
5137 MoveWindow(infoPtr
->hwndEdit
, rect
.left
- 2, rect
.top
- 1, sz
.cx
,
5138 rect
.bottom
- rect
.top
+ 2, FALSE
);
5139 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
5140 SetFocus(infoPtr
->hwndEdit
);
5141 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
5142 return infoPtr
->hwndEdit
;
5148 * Ensures the specified item is visible, scrolling into view if necessary.
5151 * [I] infoPtr : valid pointer to the listview structure
5152 * [I] nItem : item index
5153 * [I] bPartial : partially or entirely visible
5159 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
5161 INT nScrollPosHeight
= 0;
5162 INT nScrollPosWidth
= 0;
5163 INT nHorzAdjust
= 0;
5164 INT nVertAdjust
= 0;
5167 RECT rcItem
, rcTemp
;
5169 rcItem
.left
= LVIR_BOUNDS
;
5170 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
5172 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
5174 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
5176 /* scroll left/right, but in LV_VIEW_DETAILS mode */
5177 if (infoPtr
->uView
== LV_VIEW_LIST
)
5178 nScrollPosWidth
= infoPtr
->nItemWidth
;
5179 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
5180 nScrollPosWidth
= 1;
5182 if (rcItem
.left
< infoPtr
->rcList
.left
)
5185 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
5190 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
5194 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
5196 /* scroll up/down, but not in LVS_LIST mode */
5197 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
5198 nScrollPosHeight
= infoPtr
->nItemHeight
;
5199 else if ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
))
5200 nScrollPosHeight
= 1;
5202 if (rcItem
.top
< infoPtr
->rcList
.top
)
5205 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
5210 if (infoPtr
->uView
!= LV_VIEW_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
5214 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
5216 if (nScrollPosWidth
)
5218 INT diff
= nHorzDiff
/ nScrollPosWidth
;
5219 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
5220 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5223 if (nScrollPosHeight
)
5225 INT diff
= nVertDiff
/ nScrollPosHeight
;
5226 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
5227 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5235 * Searches for an item with specific characteristics.
5238 * [I] hwnd : window handle
5239 * [I] nStart : base item index
5240 * [I] lpFindInfo : item information to look for
5243 * SUCCESS : index of item
5246 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5247 const LVFINDINFOW
*lpFindInfo
)
5249 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5250 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
5251 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
5252 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
5253 POINT Position
, Destination
;
5256 /* Search in virtual listviews should be done by application, not by
5257 listview control, so we just send LVN_ODFINDITEMW and return the result */
5258 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5262 nmlv
.iStart
= nStart
;
5263 nmlv
.lvfi
= *lpFindInfo
;
5264 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
5267 if (!lpFindInfo
|| nItem
< 0) return -1;
5270 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
5272 lvItem
.mask
|= LVIF_TEXT
;
5273 lvItem
.pszText
= szDispText
;
5274 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5277 if (lpFindInfo
->flags
& LVFI_WRAP
)
5280 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
5281 (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
))
5286 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5287 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
5288 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
5289 switch(lpFindInfo
->vkDirection
)
5291 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
5292 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
5293 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
5294 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
5295 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
5296 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5297 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5299 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
5300 Destination
.x
= rcArea
.right
;
5301 Destination
.y
= rcArea
.bottom
;
5303 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
5307 else Destination
.x
= Destination
.y
= 0;
5309 /* if LVFI_PARAM is specified, all other flags are ignored */
5310 if (lpFindInfo
->flags
& LVFI_PARAM
)
5312 lvItem
.mask
|= LVIF_PARAM
;
5314 lvItem
.mask
&= ~LVIF_TEXT
;
5318 for (; nItem
< nLast
; nItem
++)
5320 lvItem
.iItem
= nItem
;
5321 lvItem
.iSubItem
= 0;
5322 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
5324 if (lvItem
.mask
& LVIF_PARAM
)
5326 if (lpFindInfo
->lParam
== lvItem
.lParam
)
5332 if (lvItem
.mask
& LVIF_TEXT
)
5334 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
5336 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
5340 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
5344 if (!bNearest
) return nItem
;
5346 /* This is very inefficient. To do a good job here,
5347 * we need a sorted array of (x,y) item positions */
5348 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5350 /* compute the distance^2 to the destination */
5351 xdist
= Destination
.x
- Position
.x
;
5352 ydist
= Destination
.y
- Position
.y
;
5353 dist
= xdist
* xdist
+ ydist
* ydist
;
5355 /* remember the distance, and item if it's closer */
5359 nNearestItem
= nItem
;
5366 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
5371 return nNearestItem
;
5376 * Searches for an item with specific characteristics.
5379 * [I] hwnd : window handle
5380 * [I] nStart : base item index
5381 * [I] lpFindInfo : item information to look for
5384 * SUCCESS : index of item
5387 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5388 const LVFINDINFOA
*lpFindInfo
)
5390 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
5395 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
5396 if (hasText
) fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
5397 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
5398 textfreeT(strW
, FALSE
);
5404 * Retrieves the background image of the listview control.
5407 * [I] infoPtr : valid pointer to the listview structure
5408 * [O] lpBkImage : background image attributes
5414 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5416 /* FIXME (listview, "empty stub!\n"); */
5422 * Retrieves column attributes.
5425 * [I] infoPtr : valid pointer to the listview structure
5426 * [I] nColumn : column index
5427 * [IO] lpColumn : column information
5428 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5429 * otherwise it is in fact a LPLVCOLUMNA
5435 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
5437 COLUMN_INFO
*lpColumnInfo
;
5440 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5441 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
5443 /* initialize memory */
5444 ZeroMemory(&hdi
, sizeof(hdi
));
5446 if (lpColumn
->mask
& LVCF_TEXT
)
5448 hdi
.mask
|= HDI_TEXT
;
5449 hdi
.pszText
= lpColumn
->pszText
;
5450 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
5453 if (lpColumn
->mask
& LVCF_IMAGE
)
5454 hdi
.mask
|= HDI_IMAGE
;
5456 if (lpColumn
->mask
& LVCF_ORDER
)
5457 hdi
.mask
|= HDI_ORDER
;
5459 if (lpColumn
->mask
& LVCF_SUBITEM
)
5460 hdi
.mask
|= HDI_LPARAM
;
5462 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
5464 if (lpColumn
->mask
& LVCF_FMT
)
5465 lpColumn
->fmt
= lpColumnInfo
->fmt
;
5467 if (lpColumn
->mask
& LVCF_WIDTH
)
5468 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
5470 if (lpColumn
->mask
& LVCF_IMAGE
)
5471 lpColumn
->iImage
= hdi
.iImage
;
5473 if (lpColumn
->mask
& LVCF_ORDER
)
5474 lpColumn
->iOrder
= hdi
.iOrder
;
5476 if (lpColumn
->mask
& LVCF_SUBITEM
)
5477 lpColumn
->iSubItem
= hdi
.lParam
;
5483 static BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
5485 TRACE("iCount=%d, lpiArray=%p\n", iCount
, lpiArray
);
5490 return SendMessageW(infoPtr
->hwndHeader
, HDM_GETORDERARRAY
, iCount
, (LPARAM
)lpiArray
);
5495 * Retrieves the column width.
5498 * [I] infoPtr : valid pointer to the listview structure
5499 * [I] int : column index
5502 * SUCCESS : column width
5505 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5507 INT nColumnWidth
= 0;
5510 TRACE("nColumn=%d\n", nColumn
);
5512 /* we have a 'column' in LIST and REPORT mode only */
5513 switch(infoPtr
->uView
)
5516 nColumnWidth
= infoPtr
->nItemWidth
;
5518 case LV_VIEW_DETAILS
:
5519 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5520 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5521 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5523 * TODO: should we do the same in LVM_GETCOLUMN?
5525 hdItem
.mask
= HDI_WIDTH
;
5526 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
5528 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
5531 nColumnWidth
= hdItem
.cxy
;
5535 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5536 return nColumnWidth
;
5541 * In list or report display mode, retrieves the number of items that can fit
5542 * vertically in the visible area. In icon or small icon display mode,
5543 * retrieves the total number of visible items.
5546 * [I] infoPtr : valid pointer to the listview structure
5549 * Number of fully visible items.
5551 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
5553 switch (infoPtr
->uView
)
5556 case LV_VIEW_SMALLICON
:
5557 return infoPtr
->nItemCount
;
5558 case LV_VIEW_DETAILS
:
5559 return LISTVIEW_GetCountPerColumn(infoPtr
);
5561 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5569 * Retrieves an image list handle.
5572 * [I] infoPtr : valid pointer to the listview structure
5573 * [I] nImageList : image list identifier
5576 * SUCCESS : image list handle
5579 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5583 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5584 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5585 case LVSIL_STATE
: return infoPtr
->himlState
;
5590 /* LISTVIEW_GetISearchString */
5594 * Retrieves item attributes.
5597 * [I] hwnd : window handle
5598 * [IO] lpLVItem : item info
5599 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5600 * if FALSE, then lpLVItem is a LPLVITEMA.
5603 * This is the internal 'GetItem' interface -- it tries to
5604 * be smart and avoid text copies, if possible, by modifying
5605 * lpLVItem->pszText to point to the text string. Please note
5606 * that this is not always possible (e.g. OWNERDATA), so on
5607 * entry you *must* supply valid values for pszText, and cchTextMax.
5608 * The only difference to the documented interface is that upon
5609 * return, you should use *only* the lpLVItem->pszText, rather than
5610 * the buffer pointer you provided on input. Most code already does
5611 * that, so it's not a problem.
5612 * For the two cases when the text must be copied (that is,
5613 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5619 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5621 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5622 NMLVDISPINFOW dispInfo
;
5628 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5630 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5633 if (lpLVItem
->mask
== 0) return TRUE
;
5635 /* make a local copy */
5636 isubitem
= lpLVItem
->iSubItem
;
5638 /* a quick optimization if all we're asked is the focus state
5639 * these queries are worth optimising since they are common,
5640 * and can be answered in constant time, without the heavy accesses */
5641 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5642 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5644 lpLVItem
->state
= 0;
5645 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5646 lpLVItem
->state
|= LVIS_FOCUSED
;
5650 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5652 /* if the app stores all the data, handle it separately */
5653 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5655 dispInfo
.item
.state
= 0;
5657 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5658 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) ||
5659 ((lpLVItem
->mask
& LVIF_STATE
) && (infoPtr
->uCallbackMask
& lpLVItem
->stateMask
)))
5661 UINT mask
= lpLVItem
->mask
;
5663 /* NOTE: copy only fields which we _know_ are initialized, some apps
5664 * depend on the uninitialized fields being 0 */
5665 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5666 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5667 dispInfo
.item
.iSubItem
= isubitem
;
5668 if (lpLVItem
->mask
& LVIF_TEXT
)
5670 if (lpLVItem
->mask
& LVIF_NORECOMPUTE
)
5672 dispInfo
.item
.mask
&= ~(LVIF_TEXT
| LVIF_NORECOMPUTE
);
5675 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5676 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5679 if (lpLVItem
->mask
& LVIF_STATE
)
5680 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5681 /* could be zeroed on LVIF_NORECOMPUTE case */
5682 if (dispInfo
.item
.mask
!= 0)
5684 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5685 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5686 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5688 /* full size structure expected - _WIN32IE >= 0x560 */
5689 *lpLVItem
= dispInfo
.item
;
5691 else if (lpLVItem
->mask
& LVIF_INDENT
)
5693 /* indent member expected - _WIN32IE >= 0x300 */
5694 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5698 /* minimal structure expected */
5699 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5701 lpLVItem
->mask
= mask
;
5702 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5706 /* make sure lParam is zeroed out */
5707 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5709 /* callback marked pointer required here */
5710 if ((lpLVItem
->mask
& LVIF_TEXT
) && (lpLVItem
->mask
& LVIF_NORECOMPUTE
))
5711 lpLVItem
->pszText
= LPSTR_TEXTCALLBACKW
;
5713 /* we store only a little state, so if we're not asked, we're done */
5714 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5716 /* if focus is handled by us, report it */
5717 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5719 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5720 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5721 lpLVItem
->state
|= LVIS_FOCUSED
;
5724 /* and do the same for selection, if we handle it */
5725 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5727 lpLVItem
->state
&= ~LVIS_SELECTED
;
5728 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5729 lpLVItem
->state
|= LVIS_SELECTED
;
5735 /* find the item and subitem structures before we proceed */
5736 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5737 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5742 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5743 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5746 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5751 pItemHdr
= &lpItem
->hdr
;
5753 /* Do we need to query the state from the app? */
5754 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5756 dispInfo
.item
.mask
|= LVIF_STATE
;
5757 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5760 /* Do we need to enquire about the image? */
5761 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5762 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5764 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5765 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5768 /* Only items support indentation */
5769 if ((lpLVItem
->mask
& LVIF_INDENT
) && lpItem
->iIndent
== I_INDENTCALLBACK
&&
5772 dispInfo
.item
.mask
|= LVIF_INDENT
;
5773 dispInfo
.item
.iIndent
= I_INDENTCALLBACK
;
5776 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5777 if ((lpLVItem
->mask
& LVIF_TEXT
) && !(lpLVItem
->mask
& LVIF_NORECOMPUTE
) &&
5778 !is_textW(pItemHdr
->pszText
))
5780 dispInfo
.item
.mask
|= LVIF_TEXT
;
5781 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5782 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5783 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5784 *dispInfo
.item
.pszText
= '\0';
5787 /* If we don't have all the requested info, query the application */
5788 if (dispInfo
.item
.mask
!= 0)
5790 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5791 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5792 dispInfo
.item
.lParam
= lpItem
->lParam
;
5793 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5794 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5797 /* we should not store values for subitems */
5798 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5800 /* Now, handle the iImage field */
5801 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5803 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5804 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5805 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5807 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5809 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5810 lpLVItem
->iImage
= pItemHdr
->iImage
;
5812 lpLVItem
->iImage
= 0;
5815 /* The pszText field */
5816 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5818 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5819 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5821 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5823 else if (lpLVItem
->mask
& LVIF_TEXT
)
5825 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
5826 if (isW
|| !is_textW(pItemHdr
->pszText
)) lpLVItem
->pszText
= pItemHdr
->pszText
;
5827 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5830 /* Next is the lParam field */
5831 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5833 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5834 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5835 lpItem
->lParam
= dispInfo
.item
.lParam
;
5837 else if (lpLVItem
->mask
& LVIF_PARAM
)
5838 lpLVItem
->lParam
= lpItem
->lParam
;
5840 /* if this is a subitem, we're done */
5841 if (isubitem
) return TRUE
;
5843 /* ... the state field (this one is different due to uCallbackmask) */
5844 if (lpLVItem
->mask
& LVIF_STATE
)
5846 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
5847 if (dispInfo
.item
.mask
& LVIF_STATE
)
5849 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5850 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5852 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5854 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5855 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5856 lpLVItem
->state
|= LVIS_FOCUSED
;
5858 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5860 lpLVItem
->state
&= ~LVIS_SELECTED
;
5861 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5862 lpLVItem
->state
|= LVIS_SELECTED
;
5866 /* and last, but not least, the indent field */
5867 if (dispInfo
.item
.mask
& LVIF_INDENT
)
5869 lpLVItem
->iIndent
= dispInfo
.item
.iIndent
;
5870 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && lpItem
->iIndent
== I_INDENTCALLBACK
)
5871 lpItem
->iIndent
= dispInfo
.item
.iIndent
;
5873 else if (lpLVItem
->mask
& LVIF_INDENT
)
5875 lpLVItem
->iIndent
= lpItem
->iIndent
;
5883 * Retrieves item attributes.
5886 * [I] hwnd : window handle
5887 * [IO] lpLVItem : item info
5888 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5889 * if FALSE, then lpLVItem is a LPLVITEMA.
5892 * This is the external 'GetItem' interface -- it properly copies
5893 * the text in the provided buffer.
5899 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5904 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5907 pszText
= lpLVItem
->pszText
;
5908 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5909 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5911 if (lpLVItem
->pszText
!= LPSTR_TEXTCALLBACKW
)
5912 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5914 pszText
= LPSTR_TEXTCALLBACKW
;
5916 lpLVItem
->pszText
= pszText
;
5924 * Retrieves the position (upper-left) of the listview control item.
5925 * Note that for LVS_ICON style, the upper-left is that of the icon
5926 * and not the bounding box.
5929 * [I] infoPtr : valid pointer to the listview structure
5930 * [I] nItem : item index
5931 * [O] lpptPosition : coordinate information
5937 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5941 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5943 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5945 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5946 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5948 if (infoPtr
->uView
== LV_VIEW_ICON
)
5950 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5951 lpptPosition
->y
+= ICON_TOP_PADDING
;
5953 lpptPosition
->x
+= Origin
.x
;
5954 lpptPosition
->y
+= Origin
.y
;
5956 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
5963 * Retrieves the bounding rectangle for a listview control item.
5966 * [I] infoPtr : valid pointer to the listview structure
5967 * [I] nItem : item index
5968 * [IO] lprc : bounding rectangle coordinates
5969 * lprc->left specifies the portion of the item for which the bounding
5970 * rectangle will be retrieved.
5972 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5973 * including the icon and label.
5976 * * Experiment shows that native control returns:
5977 * * width = min (48, length of text line)
5978 * * .left = position.x - (width - iconsize.cx)/2
5979 * * .right = .left + width
5980 * * height = #lines of text * ntmHeight + icon height + 8
5981 * * .top = position.y - 2
5982 * * .bottom = .top + height
5983 * * separation between items .y = itemSpacing.cy - height
5984 * * .x = itemSpacing.cx - width
5985 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5988 * * Experiment shows that native control returns:
5989 * * width = iconSize.cx + 16
5990 * * .left = position.x - (width - iconsize.cx)/2
5991 * * .right = .left + width
5992 * * height = iconSize.cy + 4
5993 * * .top = position.y - 2
5994 * * .bottom = .top + height
5995 * * separation between items .y = itemSpacing.cy - height
5996 * * .x = itemSpacing.cx - width
5997 * LVIR_LABEL Returns the bounding rectangle of the item text.
6000 * * Experiment shows that native control returns:
6001 * * width = text length
6002 * * .left = position.x - width/2
6003 * * .right = .left + width
6004 * * height = ntmH * linecount + 2
6005 * * .top = position.y + iconSize.cy + 6
6006 * * .bottom = .top + height
6007 * * separation between items .y = itemSpacing.cy - height
6008 * * .x = itemSpacing.cx - width
6009 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
6010 * rectangles, but excludes columns in report view.
6017 * Note that the bounding rectangle of the label in the LVS_ICON view depends
6018 * upon whether the window has the focus currently and on whether the item
6019 * is the one with the focus. Ensure that the control's record of which
6020 * item has the focus agrees with the items' records.
6022 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
6024 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6025 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
6026 POINT Position
, Origin
;
6029 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
6031 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
6033 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6034 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
6036 /* Be smart and try to figure out the minimum we have to do */
6037 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
6038 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
6039 if (infoPtr
->uView
== LV_VIEW_ICON
&& lprc
->left
!= LVIR_ICON
&&
6040 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
6041 oversizedBox
= TRUE
;
6043 /* get what we need from the item before hand, so we make
6044 * only one request. This can speed up things, if data
6045 * is stored on the app side */
6047 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
6048 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
6049 lvItem
.iItem
= nItem
;
6050 lvItem
.iSubItem
= 0;
6051 lvItem
.pszText
= szDispText
;
6052 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6053 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
6054 /* we got the state already up, simulate it here, to avoid a reget */
6055 if (infoPtr
->uView
== LV_VIEW_ICON
&& (lprc
->left
!= LVIR_ICON
))
6057 lvItem
.mask
|= LVIF_STATE
;
6058 lvItem
.stateMask
= LVIS_FOCUSED
;
6059 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
6062 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
6063 lprc
->left
= LVIR_BOUNDS
;
6067 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
6071 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
6075 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
6078 case LVIR_SELECTBOUNDS
:
6079 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
6083 WARN("Unknown value: %d\n", lprc
->left
);
6087 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
6088 OffsetRect(lprc
, Origin
.x
, Position
.y
+ Origin
.y
);
6090 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
6092 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
6099 * Retrieves the spacing between listview control items.
6102 * [I] infoPtr : valid pointer to the listview structure
6103 * [IO] lprc : rectangle to receive the output
6104 * on input, lprc->top = nSubItem
6105 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6107 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6108 * not only those of the first column.
6109 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6115 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
6121 if (!lprc
) return FALSE
;
6123 nColumn
= lprc
->top
;
6125 TRACE("(nItem=%d, nSubItem=%d)\n", nItem
, lprc
->top
);
6126 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6128 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
6130 if (infoPtr
->uView
!= LV_VIEW_DETAILS
) return FALSE
;
6132 /* special case for header items */
6135 if (lprc
->left
!= LVIR_BOUNDS
)
6137 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc
->left
);
6141 if (infoPtr
->hwndHeader
)
6142 return SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, lprc
->top
, (LPARAM
)lprc
);
6145 memset(lprc
, 0, sizeof(RECT
));
6150 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
6152 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6155 lvItem
.iItem
= nItem
;
6156 lvItem
.iSubItem
= nColumn
;
6158 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
6162 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
6167 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
6171 ERR("Unknown bounds=%d\n", lprc
->left
);
6175 OffsetRect(lprc
, 0, Position
.y
);
6182 * Retrieves the width of a label.
6185 * [I] infoPtr : valid pointer to the listview structure
6188 * SUCCESS : string width (in pixels)
6191 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
6193 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6196 TRACE("(nItem=%d)\n", nItem
);
6198 lvItem
.mask
= LVIF_TEXT
;
6199 lvItem
.iItem
= nItem
;
6200 lvItem
.iSubItem
= 0;
6201 lvItem
.pszText
= szDispText
;
6202 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6203 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
6205 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6210 * Retrieves the spacing between listview control items.
6213 * [I] infoPtr : valid pointer to the listview structure
6214 * [I] bSmall : flag for small or large icon
6217 * Horizontal + vertical spacing
6219 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
6225 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6229 if (infoPtr
->uView
== LV_VIEW_ICON
)
6230 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
6232 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
6239 * Retrieves the state of a listview control item.
6242 * [I] infoPtr : valid pointer to the listview structure
6243 * [I] nItem : item index
6244 * [I] uMask : state mask
6247 * State specified by the mask.
6249 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
6253 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6255 lvItem
.iItem
= nItem
;
6256 lvItem
.iSubItem
= 0;
6257 lvItem
.mask
= LVIF_STATE
;
6258 lvItem
.stateMask
= uMask
;
6259 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
6261 return lvItem
.state
& uMask
;
6266 * Retrieves the text of a listview control item or subitem.
6269 * [I] hwnd : window handle
6270 * [I] nItem : item index
6271 * [IO] lpLVItem : item information
6272 * [I] isW : TRUE if lpLVItem is Unicode
6275 * SUCCESS : string length
6278 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
6280 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6282 lpLVItem
->mask
= LVIF_TEXT
;
6283 lpLVItem
->iItem
= nItem
;
6284 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
6286 return textlenT(lpLVItem
->pszText
, isW
);
6291 * Searches for an item based on properties + relationships.
6294 * [I] infoPtr : valid pointer to the listview structure
6295 * [I] nItem : item index
6296 * [I] uFlags : relationship flag
6299 * SUCCESS : item index
6302 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
6305 LVFINDINFOW lvFindInfo
;
6306 INT nCountPerColumn
;
6310 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
6311 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
6313 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
6315 if (uFlags
& LVNI_CUT
)
6318 if (uFlags
& LVNI_DROPHILITED
)
6319 uMask
|= LVIS_DROPHILITED
;
6321 if (uFlags
& LVNI_FOCUSED
)
6322 uMask
|= LVIS_FOCUSED
;
6324 if (uFlags
& LVNI_SELECTED
)
6325 uMask
|= LVIS_SELECTED
;
6327 /* if we're asked for the focused item, that's only one,
6328 * so it's worth optimizing */
6329 if (uFlags
& LVNI_FOCUSED
)
6331 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
6332 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
6335 if (uFlags
& LVNI_ABOVE
)
6337 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
6342 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6348 /* Special case for autoarrange - move 'til the top of a list */
6349 if (is_autoarrange(infoPtr
))
6351 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6352 while (nItem
- nCountPerRow
>= 0)
6354 nItem
-= nCountPerRow
;
6355 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6360 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6361 lvFindInfo
.vkDirection
= VK_UP
;
6362 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
6363 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
6365 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6370 else if (uFlags
& LVNI_BELOW
)
6372 if ((infoPtr
->uView
== LV_VIEW_LIST
) || (infoPtr
->uView
== LV_VIEW_DETAILS
))
6374 while (nItem
< infoPtr
->nItemCount
)
6377 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6383 /* Special case for autoarrange - move 'til the bottom of a list */
6384 if (is_autoarrange(infoPtr
))
6386 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6387 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
6389 nItem
+= nCountPerRow
;
6390 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6395 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6396 lvFindInfo
.vkDirection
= VK_DOWN
;
6397 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
6398 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
6400 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6405 else if (uFlags
& LVNI_TOLEFT
)
6407 if (infoPtr
->uView
== LV_VIEW_LIST
)
6409 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6410 while (nItem
- nCountPerColumn
>= 0)
6412 nItem
-= nCountPerColumn
;
6413 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6417 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
6419 /* Special case for autoarrange - move 'til the beginning of a row */
6420 if (is_autoarrange(infoPtr
))
6422 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6423 while (nItem
% nCountPerRow
> 0)
6426 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6431 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6432 lvFindInfo
.vkDirection
= VK_LEFT
;
6433 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
6434 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
6436 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6441 else if (uFlags
& LVNI_TORIGHT
)
6443 if (infoPtr
->uView
== LV_VIEW_LIST
)
6445 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6446 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
6448 nItem
+= nCountPerColumn
;
6449 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6453 else if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
6455 /* Special case for autoarrange - move 'til the end of a row */
6456 if (is_autoarrange(infoPtr
))
6458 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6459 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
6462 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6467 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6468 lvFindInfo
.vkDirection
= VK_RIGHT
;
6469 LISTVIEW_GetItemPosition(infoPtr
, nItem
, &lvFindInfo
.pt
);
6470 while ((nItem
= LISTVIEW_FindItemW(infoPtr
, nItem
, &lvFindInfo
)) != -1)
6472 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6481 /* search by index */
6482 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
6484 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
6492 /* LISTVIEW_GetNumberOfWorkAreas */
6496 * Retrieves the origin coordinates when in icon or small icon display mode.
6499 * [I] infoPtr : valid pointer to the listview structure
6500 * [O] lpptOrigin : coordinate information
6505 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
6507 INT nHorzPos
= 0, nVertPos
= 0;
6508 SCROLLINFO scrollInfo
;
6510 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
6511 scrollInfo
.fMask
= SIF_POS
;
6513 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
6514 nHorzPos
= scrollInfo
.nPos
;
6515 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
6516 nVertPos
= scrollInfo
.nPos
;
6518 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
6520 lpptOrigin
->x
= infoPtr
->rcList
.left
;
6521 lpptOrigin
->y
= infoPtr
->rcList
.top
;
6522 if (infoPtr
->uView
== LV_VIEW_LIST
)
6523 nHorzPos
*= infoPtr
->nItemWidth
;
6524 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
6525 nVertPos
*= infoPtr
->nItemHeight
;
6527 lpptOrigin
->x
-= nHorzPos
;
6528 lpptOrigin
->y
-= nVertPos
;
6530 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
6535 * Retrieves the width of a string.
6538 * [I] hwnd : window handle
6539 * [I] lpszText : text string to process
6540 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6543 * SUCCESS : string width (in pixels)
6546 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
6551 if (is_textT(lpszText
, isW
))
6553 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6554 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6555 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6558 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6560 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6561 SelectObject(hdc
, hOldFont
);
6562 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6564 return stringSize
.cx
;
6569 * Determines which listview item is located at the specified position.
6572 * [I] infoPtr : valid pointer to the listview structure
6573 * [IO] lpht : hit test information
6574 * [I] subitem : fill out iSubItem.
6575 * [I] select : return the index only if the hit selects the item
6578 * (mm 20001022): We must not allow iSubItem to be touched, for
6579 * an app might pass only a structure with space up to iItem!
6580 * (MS Office 97 does that for instance in the file open dialog)
6583 * SUCCESS : item index
6586 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
6588 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6589 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6590 POINT Origin
, Position
, opt
;
6595 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
6599 if (subitem
) lpht
->iSubItem
= 0;
6601 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6602 lpht
->flags
|= LVHT_TOLEFT
;
6603 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6604 lpht
->flags
|= LVHT_TORIGHT
;
6606 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6607 lpht
->flags
|= LVHT_ABOVE
;
6608 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6609 lpht
->flags
|= LVHT_BELOW
;
6611 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6612 if (lpht
->flags
) return -1;
6614 lpht
->flags
|= LVHT_NOWHERE
;
6616 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6618 /* first deal with the large items */
6619 rcSearch
.left
= lpht
->pt
.x
;
6620 rcSearch
.top
= lpht
->pt
.y
;
6621 rcSearch
.right
= rcSearch
.left
+ 1;
6622 rcSearch
.bottom
= rcSearch
.top
+ 1;
6624 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6625 iterator_next(&i
); /* go to first item in the sequence */
6627 iterator_destroy(&i
);
6629 TRACE("lpht->iItem=%d\n", iItem
);
6630 if (iItem
== -1) return -1;
6632 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& subitem
)
6634 RECT bounds
, *pRect
;
6637 /* for top/bottom only */
6638 bounds
.left
= LVIR_BOUNDS
;
6639 LISTVIEW_GetItemRect(infoPtr
, iItem
, &bounds
);
6641 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6643 pRect
= &LISTVIEW_GetColumnInfo(infoPtr
, j
)->rcHeader
;
6644 bounds
.left
= pRect
->left
;
6645 bounds
.right
= pRect
->right
;
6647 if (PtInRect(&bounds
, lpht
->pt
))
6653 TRACE("lpht->iSubItem=%d\n", lpht
->iSubItem
);
6656 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6657 if (infoPtr
->uView
== LV_VIEW_DETAILS
) lvItem
.mask
|= LVIF_INDENT
;
6658 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6659 if (infoPtr
->uView
== LV_VIEW_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6660 lvItem
.iItem
= iItem
;
6661 lvItem
.iSubItem
= subitem
? lpht
->iSubItem
: 0;
6662 lvItem
.pszText
= szDispText
;
6663 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6664 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6665 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6667 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
6668 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6669 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6670 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6672 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
6676 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6677 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6679 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
6680 if (!PtInRect(&rcBounds
, opt
)) return -1;
6682 if (PtInRect(&rcIcon
, opt
))
6683 lpht
->flags
|= LVHT_ONITEMICON
;
6684 else if (PtInRect(&rcLabel
, opt
))
6685 lpht
->flags
|= LVHT_ONITEMLABEL
;
6686 else if (infoPtr
->himlState
&& PtInRect(&rcState
, opt
))
6687 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6688 /* special case for LVS_EX_FULLROWSELECT */
6689 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
&&
6690 !(lpht
->flags
& LVHT_ONITEM
))
6692 lpht
->flags
= LVHT_ONITEM
| LVHT_ABOVE
;
6694 if (lpht
->flags
& LVHT_ONITEM
)
6695 lpht
->flags
&= ~LVHT_NOWHERE
;
6696 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6698 if (select
&& !(infoPtr
->uView
== LV_VIEW_DETAILS
&&
6699 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6700 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6702 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
6704 /* get main item bounds */
6705 lvItem
.iSubItem
= 0;
6706 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
6707 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6708 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6710 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6712 return lpht
->iItem
= iItem
;
6717 * Inserts a new item in the listview control.
6720 * [I] infoPtr : valid pointer to the listview structure
6721 * [I] lpLVItem : item information
6722 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6725 * SUCCESS : new item index
6728 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6734 BOOL is_sorted
, has_changed
;
6736 HWND hwndSelf
= infoPtr
->hwndSelf
;
6738 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6740 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6742 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6743 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
6745 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6747 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
6749 /* insert item in listview control data structure */
6750 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6751 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6753 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6754 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6756 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
6758 /* calculate new item index */
6765 while (i
< infoPtr
->nItemCount
)
6767 hItem
= DPA_GetPtr( infoPtr
->hdpaItems
, i
);
6768 item_s
= (ITEM_INFO
*)DPA_GetPtr(hItem
, 0);
6770 cmpv
= textcmpWT(item_s
->hdr
.pszText
, lpLVItem
->pszText
, TRUE
);
6771 if (infoPtr
->dwStyle
& LVS_SORTDESCENDING
) cmpv
*= -1;
6773 if (cmpv
>= 0) break;
6779 nItem
= min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6781 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6782 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6783 if (nItem
== -1) goto fail
;
6784 infoPtr
->nItemCount
++;
6786 /* shift indices first so they don't get tangled */
6787 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6789 /* set the item attributes */
6790 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6792 /* full size structure expected - _WIN32IE >= 0x560 */
6795 else if (lpLVItem
->mask
& LVIF_INDENT
)
6797 /* indent member expected - _WIN32IE >= 0x300 */
6798 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6802 /* minimal structure expected */
6803 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6806 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6808 item
.mask
|= LVIF_STATE
;
6809 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
6810 item
.state
&= ~LVIS_STATEIMAGEMASK
;
6811 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
6813 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6815 /* make room for the position, if we are in the right mode */
6816 if ((infoPtr
->uView
== LV_VIEW_SMALLICON
) || (infoPtr
->uView
== LV_VIEW_ICON
))
6818 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6820 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6822 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6827 /* send LVN_INSERTITEM notification */
6828 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6830 nmlv
.lParam
= lpItem
->lParam
;
6831 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6832 if (!IsWindow(hwndSelf
))
6835 /* align items (set position of each item) */
6836 if (infoPtr
->uView
== LV_VIEW_SMALLICON
|| infoPtr
->uView
== LV_VIEW_ICON
)
6840 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6841 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6843 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6845 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6848 /* now is the invalidation fun */
6849 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6853 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6854 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6855 infoPtr
->nItemCount
--;
6857 DPA_DeletePtr(hdpaSubItems
, 0);
6858 DPA_Destroy (hdpaSubItems
);
6865 * Redraws a range of items.
6868 * [I] infoPtr : valid pointer to the listview structure
6869 * [I] nFirst : first item
6870 * [I] nLast : last item
6876 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6880 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6881 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6884 for (i
= nFirst
; i
<= nLast
; i
++)
6885 LISTVIEW_InvalidateItem(infoPtr
, i
);
6892 * Scroll the content of a listview.
6895 * [I] infoPtr : valid pointer to the listview structure
6896 * [I] dx : horizontal scroll amount in pixels
6897 * [I] dy : vertical scroll amount in pixels
6904 * If the control is in report view (LV_VIEW_DETAILS) the control can
6905 * be scrolled only in line increments. "dy" will be rounded to the
6906 * nearest number of pixels that are a whole line. Ex: if line height
6907 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6908 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6910 * For: (per experimentation with native control and CSpy ListView)
6911 * LV_VIEW_ICON dy=1 = 1 pixel (vertical only)
6913 * LV_VIEW_SMALLICON dy=1 = 1 pixel (vertical only)
6915 * LV_VIEW_LIST dx=1 = 1 column (horizontal only)
6916 * but will only scroll 1 column per message
6917 * no matter what the value.
6918 * dy must be 0 or FALSE returned.
6919 * LV_VIEW_DETAILS dx=1 = 1 pixel
6923 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6925 switch(infoPtr
->uView
) {
6926 case LV_VIEW_DETAILS
:
6927 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6928 dy
/= infoPtr
->nItemHeight
;
6931 if (dy
!= 0) return FALSE
;
6938 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6939 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6946 * Sets the background color.
6949 * [I] infoPtr : valid pointer to the listview structure
6950 * [I] clrBk : background color
6956 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6958 TRACE("(clrBk=%x)\n", clrBk
);
6960 if(infoPtr
->clrBk
!= clrBk
) {
6961 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6962 infoPtr
->clrBk
= clrBk
;
6963 if (clrBk
== CLR_NONE
)
6964 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6966 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6967 LISTVIEW_InvalidateList(infoPtr
);
6973 /* LISTVIEW_SetBkImage */
6975 /*** Helper for {Insert,Set}ColumnT *only* */
6976 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
6977 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6979 if (lpColumn
->mask
& LVCF_FMT
)
6981 /* format member is valid */
6982 lphdi
->mask
|= HDI_FORMAT
;
6984 /* set text alignment (leftmost column must be left-aligned) */
6985 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6986 lphdi
->fmt
|= HDF_LEFT
;
6987 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6988 lphdi
->fmt
|= HDF_RIGHT
;
6989 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6990 lphdi
->fmt
|= HDF_CENTER
;
6992 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6993 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6995 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6997 lphdi
->fmt
|= HDF_IMAGE
;
6998 lphdi
->iImage
= I_IMAGECALLBACK
;
7002 if (lpColumn
->mask
& LVCF_WIDTH
)
7004 lphdi
->mask
|= HDI_WIDTH
;
7005 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
7007 /* make it fill the remainder of the controls width */
7011 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
7013 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
7014 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
7017 /* retrieve the layout of the header */
7018 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
7019 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
7021 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
7024 lphdi
->cxy
= lpColumn
->cx
;
7027 if (lpColumn
->mask
& LVCF_TEXT
)
7029 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
7030 lphdi
->fmt
|= HDF_STRING
;
7031 lphdi
->pszText
= lpColumn
->pszText
;
7032 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
7035 if (lpColumn
->mask
& LVCF_IMAGE
)
7037 lphdi
->mask
|= HDI_IMAGE
;
7038 lphdi
->iImage
= lpColumn
->iImage
;
7041 if (lpColumn
->mask
& LVCF_ORDER
)
7043 lphdi
->mask
|= HDI_ORDER
;
7044 lphdi
->iOrder
= lpColumn
->iOrder
;
7051 * Inserts a new column.
7054 * [I] infoPtr : valid pointer to the listview structure
7055 * [I] nColumn : column index
7056 * [I] lpColumn : column information
7057 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
7060 * SUCCESS : new column index
7063 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
7064 const LVCOLUMNW
*lpColumn
, BOOL isW
)
7066 COLUMN_INFO
*lpColumnInfo
;
7070 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
7072 if (!lpColumn
|| nColumn
< 0) return -1;
7073 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
7075 ZeroMemory(&hdi
, sizeof(HDITEMW
));
7076 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
7079 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
7080 * (can be seen in SPY) otherwise column never gets added.
7082 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
7083 hdi
.mask
|= HDI_WIDTH
;
7088 * when the iSubItem is available Windows copies it to the header lParam. It seems
7089 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7091 if (lpColumn
->mask
& LVCF_SUBITEM
)
7093 hdi
.mask
|= HDI_LPARAM
;
7094 hdi
.lParam
= lpColumn
->iSubItem
;
7097 /* create header if not present */
7098 LISTVIEW_CreateHeader(infoPtr
);
7099 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) &&
7100 (infoPtr
->uView
== LV_VIEW_DETAILS
) && (WS_VISIBLE
& infoPtr
->dwStyle
))
7102 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
7105 /* insert item in header control */
7106 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
7107 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
7108 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
7109 if (nNewColumn
== -1) return -1;
7110 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
7112 /* create our own column info */
7113 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
7114 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
7116 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
7117 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMRECT
, nNewColumn
, (LPARAM
)&lpColumnInfo
->rcHeader
))
7120 /* now we have to actually adjust the data */
7121 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
7123 SUBITEM_INFO
*lpSubItem
;
7127 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
7129 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
7130 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
7132 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
7133 if (lpSubItem
->iSubItem
>= nNewColumn
)
7134 lpSubItem
->iSubItem
++;
7139 /* make space for the new column */
7140 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
7141 LISTVIEW_UpdateItemSize(infoPtr
);
7146 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
7149 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
7157 * Sets the attributes of a header item.
7160 * [I] infoPtr : valid pointer to the listview structure
7161 * [I] nColumn : column index
7162 * [I] lpColumn : column attributes
7163 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7169 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
7170 const LVCOLUMNW
*lpColumn
, BOOL isW
)
7172 HDITEMW hdi
, hdiget
;
7175 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
7177 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7179 ZeroMemory(&hdi
, sizeof(HDITEMW
));
7180 if (lpColumn
->mask
& LVCF_FMT
)
7182 hdi
.mask
|= HDI_FORMAT
;
7183 hdiget
.mask
= HDI_FORMAT
;
7184 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
7185 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
7187 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
7189 /* set header item attributes */
7190 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
7191 if (!bResult
) return FALSE
;
7193 if (lpColumn
->mask
& LVCF_FMT
)
7195 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
7196 int oldFmt
= lpColumnInfo
->fmt
;
7198 lpColumnInfo
->fmt
= lpColumn
->fmt
;
7199 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
7201 if (infoPtr
->uView
== LV_VIEW_DETAILS
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
7210 * Sets the column order array
7213 * [I] infoPtr : valid pointer to the listview structure
7214 * [I] iCount : number of elements in column order array
7215 * [I] lpiArray : pointer to column order array
7221 static BOOL
LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
7223 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
7234 * Sets the width of a column
7237 * [I] infoPtr : valid pointer to the listview structure
7238 * [I] nColumn : column index
7239 * [I] cx : column width
7245 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
7247 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
7251 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
7253 /* set column width only if in report or list mode */
7254 if (infoPtr
->uView
!= LV_VIEW_DETAILS
&& infoPtr
->uView
!= LV_VIEW_LIST
) return FALSE
;
7256 /* take care of invalid cx values */
7257 if(infoPtr
->uView
== LV_VIEW_DETAILS
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
7258 else if (infoPtr
->uView
== LV_VIEW_LIST
&& cx
< 1) return FALSE
;
7260 /* resize all columns if in LV_VIEW_LIST mode */
7261 if(infoPtr
->uView
== LV_VIEW_LIST
)
7263 infoPtr
->nItemWidth
= cx
;
7264 LISTVIEW_InvalidateList(infoPtr
);
7268 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7270 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
7275 lvItem
.mask
= LVIF_TEXT
;
7277 lvItem
.iSubItem
= nColumn
;
7278 lvItem
.pszText
= szDispText
;
7279 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7280 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7282 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
7283 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
7284 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
7286 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
7287 max_cx
+= infoPtr
->iconSize
.cx
;
7288 max_cx
+= TRAILING_LABEL_PADDING
;
7291 /* autosize based on listview items width */
7292 if(cx
== LVSCW_AUTOSIZE
)
7294 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
7296 /* if iCol is the last column make it fill the remainder of the controls width */
7297 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
7302 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7303 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
7305 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
7309 /* Despite what the MS docs say, if this is not the last
7310 column, then MS resizes the column to the width of the
7311 largest text string in the column, including headers
7312 and items. This is different from LVSCW_AUTOSIZE in that
7313 LVSCW_AUTOSIZE ignores the header string length. */
7316 /* retrieve header text */
7317 hdi
.mask
= HDI_TEXT
;
7318 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
7319 hdi
.pszText
= szDispText
;
7320 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
))
7322 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7323 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
7326 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
7327 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
7328 /* FIXME: Take into account the header image, if one is present */
7329 SelectObject(hdc
, old_font
);
7330 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7332 cx
= max (cx
, max_cx
);
7336 if (cx
< 0) return FALSE
;
7338 /* call header to update the column change */
7339 hdi
.mask
= HDI_WIDTH
;
7341 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
7342 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
7346 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7349 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
7352 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
7354 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
7355 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
7358 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
7359 ILC_COLOR
| ILC_MASK
, 2, 2);
7360 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
7361 hdc
= CreateCompatibleDC(hdc_wnd
);
7362 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
7363 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
7364 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
7366 rc
.left
= rc
.top
= 0;
7367 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
7368 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
7370 hbm_orig
= SelectObject(hdc
, hbm_mask
);
7371 FillRect(hdc
, &rc
, hbr_white
);
7372 InflateRect(&rc
, -2, -2);
7373 FillRect(hdc
, &rc
, hbr_black
);
7375 SelectObject(hdc
, hbm_im
);
7376 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
7377 SelectObject(hdc
, hbm_orig
);
7378 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7380 SelectObject(hdc
, hbm_im
);
7381 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
7382 SelectObject(hdc
, hbm_orig
);
7383 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7385 DeleteObject(hbm_mask
);
7386 DeleteObject(hbm_im
);
7394 * Sets the extended listview style.
7397 * [I] infoPtr : valid pointer to the listview structure
7399 * [I] dwStyle : style
7402 * SUCCESS : previous style
7405 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwExStyle
)
7407 DWORD dwOldExStyle
= infoPtr
->dwLvExStyle
;
7411 infoPtr
->dwLvExStyle
= (dwOldExStyle
& ~dwMask
) | (dwExStyle
& dwMask
);
7413 infoPtr
->dwLvExStyle
= dwExStyle
;
7415 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_CHECKBOXES
)
7417 HIMAGELIST himl
= 0;
7418 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7421 item
.mask
= LVIF_STATE
;
7422 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7423 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7424 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
7426 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
7428 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
7431 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_HEADERDRAGDROP
)
7435 /* if not already created */
7436 LISTVIEW_CreateHeader(infoPtr
);
7438 dwStyle
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
7439 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
7440 dwStyle
|= HDS_DRAGDROP
;
7442 dwStyle
&= ~HDS_DRAGDROP
;
7443 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, dwStyle
);
7446 /* GRIDLINES adds decoration at top so changes sizes */
7447 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_GRIDLINES
)
7449 LISTVIEW_UpdateSize(infoPtr
);
7453 LISTVIEW_InvalidateList(infoPtr
);
7454 return dwOldExStyle
;
7459 * Sets the new hot cursor used during hot tracking and hover selection.
7462 * [I] infoPtr : valid pointer to the listview structure
7463 * [I] hCursor : the new hot cursor handle
7466 * Returns the previous hot cursor
7468 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
7470 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
7472 infoPtr
->hHotCursor
= hCursor
;
7480 * Sets the hot item index.
7483 * [I] infoPtr : valid pointer to the listview structure
7484 * [I] iIndex : index
7487 * SUCCESS : previous hot item index
7488 * FAILURE : -1 (no hot item)
7490 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
7492 INT iOldIndex
= infoPtr
->nHotItem
;
7494 infoPtr
->nHotItem
= iIndex
;
7502 * Sets the amount of time the cursor must hover over an item before it is selected.
7505 * [I] infoPtr : valid pointer to the listview structure
7506 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7509 * Returns the previous hover time
7511 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
7513 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
7515 infoPtr
->dwHoverTime
= dwHoverTime
;
7517 return oldHoverTime
;
7522 * Sets spacing for icons of LVS_ICON style.
7525 * [I] infoPtr : valid pointer to the listview structure
7526 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7527 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7530 * MAKELONG(oldcx, oldcy)
7532 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
7534 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7536 TRACE("requested=(%d,%d)\n", cx
, cy
);
7538 /* this is supported only for LVS_ICON style */
7539 if (infoPtr
->uView
!= LV_VIEW_ICON
) return oldspacing
;
7541 /* set to defaults, if instructed to */
7542 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
7543 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
7545 /* if 0 then compute width
7546 * FIXME: Should scan each item and determine max width of
7547 * icon or label, then make that the width */
7549 cx
= infoPtr
->iconSpacing
.cx
;
7551 /* if 0 then compute height */
7553 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
7554 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
7557 infoPtr
->iconSpacing
.cx
= cx
;
7558 infoPtr
->iconSpacing
.cy
= cy
;
7560 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7561 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
7562 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
7563 infoPtr
->ntmHeight
);
7565 /* these depend on the iconSpacing */
7566 LISTVIEW_UpdateItemSize(infoPtr
);
7571 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
7575 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
7582 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
7583 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
7592 * [I] infoPtr : valid pointer to the listview structure
7593 * [I] nType : image list type
7594 * [I] himl : image list handle
7597 * SUCCESS : old image list
7600 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
7602 INT oldHeight
= infoPtr
->nItemHeight
;
7603 HIMAGELIST himlOld
= 0;
7605 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
7610 himlOld
= infoPtr
->himlNormal
;
7611 infoPtr
->himlNormal
= himl
;
7612 if (infoPtr
->uView
== LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
7613 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
7617 himlOld
= infoPtr
->himlSmall
;
7618 infoPtr
->himlSmall
= himl
;
7619 if (infoPtr
->uView
!= LV_VIEW_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
7623 himlOld
= infoPtr
->himlState
;
7624 infoPtr
->himlState
= himl
;
7625 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
7626 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7630 ERR("Unknown icon type=%d\n", nType
);
7634 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
7635 if (infoPtr
->nItemHeight
!= oldHeight
)
7636 LISTVIEW_UpdateScroll(infoPtr
);
7643 * Preallocates memory (does *not* set the actual count of items !)
7646 * [I] infoPtr : valid pointer to the listview structure
7647 * [I] nItems : item count (projected number of items to allocate)
7648 * [I] dwFlags : update flags
7654 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7656 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
7658 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
7660 INT nOldCount
= infoPtr
->nItemCount
;
7662 if (nItems
< nOldCount
)
7664 RANGE range
= { nItems
, nOldCount
};
7665 ranges_del(infoPtr
->selectionRanges
, range
);
7666 if (infoPtr
->nFocusedItem
>= nItems
)
7668 LISTVIEW_SetItemFocus(infoPtr
, -1);
7669 SetRectEmpty(&infoPtr
->rcFocus
);
7673 infoPtr
->nItemCount
= nItems
;
7674 LISTVIEW_UpdateScroll(infoPtr
);
7676 /* the flags are valid only in ownerdata report and list modes */
7677 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
) dwFlags
= 0;
7679 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7680 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7682 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7683 LISTVIEW_InvalidateList(infoPtr
);
7690 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7691 nFrom
= min(nOldCount
, nItems
);
7692 nTo
= max(nOldCount
, nItems
);
7694 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
7697 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7698 rcErase
.right
= infoPtr
->nItemWidth
;
7699 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7700 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7701 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7702 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7704 else /* LV_VIEW_LIST */
7706 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7708 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7709 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7710 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7711 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7712 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7713 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7714 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7716 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7718 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7719 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7720 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7721 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7722 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7728 /* According to MSDN for non-LVS_OWNERDATA this is just
7729 * a performance issue. The control allocates its internal
7730 * data structures for the number of items specified. It
7731 * cuts down on the number of memory allocations. Therefore
7732 * we will just issue a WARN here
7734 WARN("for non-ownerdata performance option not implemented.\n");
7742 * Sets the position of an item.
7745 * [I] infoPtr : valid pointer to the listview structure
7746 * [I] nItem : item index
7747 * [I] pt : coordinate
7753 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7757 TRACE("(nItem=%d, &pt=%s\n", nItem
, wine_dbgstr_point(&pt
));
7759 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7760 !(infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)) return FALSE
;
7762 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7764 /* This point value seems to be an undocumented feature.
7765 * The best guess is that it means either at the origin,
7766 * or at true beginning of the list. I will assume the origin. */
7767 if ((pt
.x
== -1) && (pt
.y
== -1))
7770 if (infoPtr
->uView
== LV_VIEW_ICON
)
7772 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7773 pt
.y
-= ICON_TOP_PADDING
;
7778 infoPtr
->bAutoarrange
= FALSE
;
7780 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7785 * Sets the state of one or many items.
7788 * [I] infoPtr : valid pointer to the listview structure
7789 * [I] nItem : item index
7790 * [I] lpLVItem : item or subitem info
7796 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7798 BOOL bResult
= TRUE
;
7801 lvItem
.iItem
= nItem
;
7802 lvItem
.iSubItem
= 0;
7803 lvItem
.mask
= LVIF_STATE
;
7804 lvItem
.state
= lpLVItem
->state
;
7805 lvItem
.stateMask
= lpLVItem
->stateMask
;
7806 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7810 /* select all isn't allowed in LVS_SINGLESEL */
7811 if ((lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) && (infoPtr
->dwStyle
& LVS_SINGLESEL
))
7814 /* apply to all items */
7815 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7816 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7819 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7822 * Update selection mark
7824 * Investigation on windows 2k showed that selection mark was updated
7825 * whenever a new selection was made, but if the selected item was
7826 * unselected it was not updated.
7828 * we are probably still not 100% accurate, but this at least sets the
7829 * proper selection mark when it is needed
7832 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7833 (infoPtr
->nSelectionMark
== -1))
7836 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7838 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7840 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7842 infoPtr
->nSelectionMark
= i
;
7846 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7848 infoPtr
->nSelectionMark
= i
;
7859 * Sets the text of an item or subitem.
7862 * [I] hwnd : window handle
7863 * [I] nItem : item index
7864 * [I] lpLVItem : item or subitem info
7865 * [I] isW : TRUE if input is Unicode
7871 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7875 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7877 lvItem
.iItem
= nItem
;
7878 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7879 lvItem
.mask
= LVIF_TEXT
;
7880 lvItem
.pszText
= lpLVItem
->pszText
;
7881 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7883 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7885 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7890 * Set item index that marks the start of a multiple selection.
7893 * [I] infoPtr : valid pointer to the listview structure
7894 * [I] nIndex : index
7897 * Index number or -1 if there is no selection mark.
7899 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7901 INT nOldIndex
= infoPtr
->nSelectionMark
;
7903 TRACE("(nIndex=%d)\n", nIndex
);
7905 infoPtr
->nSelectionMark
= nIndex
;
7912 * Sets the text background color.
7915 * [I] infoPtr : valid pointer to the listview structure
7916 * [I] clrTextBk : text background color
7922 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7924 TRACE("(clrTextBk=%x)\n", clrTextBk
);
7926 if (infoPtr
->clrTextBk
!= clrTextBk
)
7928 infoPtr
->clrTextBk
= clrTextBk
;
7929 LISTVIEW_InvalidateList(infoPtr
);
7937 * Sets the text foreground color.
7940 * [I] infoPtr : valid pointer to the listview structure
7941 * [I] clrText : text color
7947 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7949 TRACE("(clrText=%x)\n", clrText
);
7951 if (infoPtr
->clrText
!= clrText
)
7953 infoPtr
->clrText
= clrText
;
7954 LISTVIEW_InvalidateList(infoPtr
);
7962 * Sets new ToolTip window to ListView control.
7965 * [I] infoPtr : valid pointer to the listview structure
7966 * [I] hwndNewToolTip : handle to new ToolTip
7971 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7973 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7974 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7975 return hwndOldToolTip
;
7980 * sets the Unicode character format flag for the control
7982 * [I] infoPtr :valid pointer to the listview structure
7983 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7986 * Old Unicode Format
7988 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL fUnicode
)
7990 SHORT rc
= infoPtr
->notifyFormat
;
7991 infoPtr
->notifyFormat
= (fUnicode
) ? NFR_UNICODE
: NFR_ANSI
;
7992 return rc
== NFR_UNICODE
;
7997 * sets the control view mode
7999 * [I] infoPtr :valid pointer to the listview structure
8000 * [I] nView :new view mode value
8006 static INT
LISTVIEW_SetView(LISTVIEW_INFO
*infoPtr
, DWORD nView
)
8008 SIZE oldIconSize
= infoPtr
->iconSize
;
8011 if (infoPtr
->uView
== nView
) return 1;
8013 if ((INT
)nView
< 0 || nView
> LV_VIEW_MAX
) return -1;
8014 if (nView
== LV_VIEW_TILE
)
8016 FIXME("View LV_VIEW_TILE unimplemented\n");
8020 infoPtr
->uView
= nView
;
8022 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8023 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
8025 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
8026 SetRectEmpty(&infoPtr
->rcFocus
);
8028 himl
= (nView
== LV_VIEW_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
8029 set_icon_size(&infoPtr
->iconSize
, himl
, nView
!= LV_VIEW_ICON
);
8034 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
8036 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
8037 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
8038 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
8040 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8042 case LV_VIEW_SMALLICON
:
8043 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8045 case LV_VIEW_DETAILS
:
8050 LISTVIEW_CreateHeader( infoPtr
);
8052 hl
.prc
= &infoPtr
->rcList
;
8054 SendMessageW(infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
8055 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
8056 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
8063 LISTVIEW_UpdateItemSize(infoPtr
);
8064 LISTVIEW_UpdateSize(infoPtr
);
8065 LISTVIEW_UpdateScroll(infoPtr
);
8066 LISTVIEW_InvalidateList(infoPtr
);
8068 TRACE("nView=%d\n", nView
);
8073 /* LISTVIEW_SetWorkAreas */
8077 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
8080 * [I] first : pointer to first ITEM_INFO to compare
8081 * [I] second : pointer to second ITEM_INFO to compare
8082 * [I] lParam : HWND of control
8085 * if first comes before second : negative
8086 * if first comes after second : positive
8087 * if first and second are equivalent : zero
8089 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
8091 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
8092 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
8093 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
8095 /* Forward the call to the client defined callback */
8096 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
8101 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
8104 * [I] first : pointer to first ITEM_INFO to compare
8105 * [I] second : pointer to second ITEM_INFO to compare
8106 * [I] lParam : HWND of control
8109 * if first comes before second : negative
8110 * if first comes after second : positive
8111 * if first and second are equivalent : zero
8113 static INT WINAPI
LISTVIEW_CallBackCompareEx(LPVOID first
, LPVOID second
, LPARAM lParam
)
8115 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
8116 INT first_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, first
);
8117 INT second_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, second
);
8119 /* Forward the call to the client defined callback */
8120 return (infoPtr
->pfnCompare
)( first_idx
, second_idx
, infoPtr
->lParamSort
);
8125 * Sorts the listview items.
8128 * [I] infoPtr : valid pointer to the listview structure
8129 * [I] pfnCompare : application-defined value
8130 * [I] lParamSort : pointer to comparison callback
8131 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
8137 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
,
8138 LPARAM lParamSort
, BOOL IsEx
)
8142 LPVOID selectionMarkItem
= NULL
;
8143 LPVOID focusedItem
= NULL
;
8146 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
8148 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
8150 if (!pfnCompare
) return FALSE
;
8151 if (!infoPtr
->hdpaItems
) return FALSE
;
8153 /* if there are 0 or 1 items, there is no need to sort */
8154 if (infoPtr
->nItemCount
< 2) return TRUE
;
8156 /* clear selection */
8157 ranges_clear(infoPtr
->selectionRanges
);
8159 /* save selection mark and focused item */
8160 if (infoPtr
->nSelectionMark
>= 0)
8161 selectionMarkItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
);
8162 if (infoPtr
->nFocusedItem
>= 0)
8163 focusedItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
8165 infoPtr
->pfnCompare
= pfnCompare
;
8166 infoPtr
->lParamSort
= lParamSort
;
8168 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompareEx
, (LPARAM
)infoPtr
);
8170 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
8172 /* restore selection ranges */
8173 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
8175 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
8176 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
8178 if (lpItem
->state
& LVIS_SELECTED
)
8179 ranges_additem(infoPtr
->selectionRanges
, i
);
8181 /* restore selection mark and focused item */
8182 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
8183 infoPtr
->nFocusedItem
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, focusedItem
);
8185 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8187 /* refresh the display */
8188 if (infoPtr
->uView
!= LV_VIEW_ICON
&& infoPtr
->uView
!= LV_VIEW_SMALLICON
)
8189 LISTVIEW_InvalidateList(infoPtr
);
8196 * Update theme handle after a theme change.
8199 * [I] infoPtr : valid pointer to the listview structure
8203 * FAILURE : something else
8205 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
8207 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
8208 CloseThemeData(theme
);
8209 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
8215 * Updates an items or rearranges the listview control.
8218 * [I] infoPtr : valid pointer to the listview structure
8219 * [I] nItem : item index
8225 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
8227 TRACE("(nItem=%d)\n", nItem
);
8229 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
8231 /* rearrange with default alignment style */
8232 if (is_autoarrange(infoPtr
))
8233 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
8235 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
8242 * Draw the track line at the place defined in the infoPtr structure.
8243 * The line is drawn with a XOR pen so drawing the line for the second time
8244 * in the same place erases the line.
8247 * [I] infoPtr : valid pointer to the listview structure
8253 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
8259 if (infoPtr
->xTrackLine
== -1)
8262 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
8264 hOldPen
= SelectObject(hdc
, GetStockObject(BLACK_PEN
));
8265 oldROP
= SetROP2(hdc
, R2_XORPEN
);
8266 MoveToEx(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
, NULL
);
8267 LineTo(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.bottom
);
8268 SetROP2(hdc
, oldROP
);
8269 SelectObject(hdc
, hOldPen
);
8270 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
8276 * Called when an edit control should be displayed. This function is called after
8277 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8280 * [I] hwnd : Handle to the listview
8281 * [I] uMsg : WM_TIMER (ignored)
8282 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8283 * [I] dwTimer : The elapsed time (ignored)
8288 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
8290 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
8291 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8293 KillTimer(hwnd
, idEvent
);
8294 editItem
->fEnabled
= FALSE
;
8295 /* check if the item is still selected */
8296 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
8297 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
8302 * Creates the listview control - the WM_NCCREATE phase.
8305 * [I] hwnd : window handle
8306 * [I] lpcs : the create parameters
8312 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8314 LISTVIEW_INFO
*infoPtr
;
8317 TRACE("(lpcs=%p)\n", lpcs
);
8319 /* initialize info pointer */
8320 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
8321 if (!infoPtr
) return FALSE
;
8323 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
8325 infoPtr
->hwndSelf
= hwnd
;
8326 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
8327 map_style_view(infoPtr
);
8328 /* determine the type of structures to use */
8329 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
8330 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8332 /* initialize color information */
8333 infoPtr
->clrBk
= CLR_NONE
;
8334 infoPtr
->clrText
= CLR_DEFAULT
;
8335 infoPtr
->clrTextBk
= CLR_DEFAULT
;
8336 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
8338 /* set default values */
8339 infoPtr
->nFocusedItem
= -1;
8340 infoPtr
->nSelectionMark
= -1;
8341 infoPtr
->nHotItem
= -1;
8342 infoPtr
->bRedraw
= TRUE
;
8343 infoPtr
->bNoItemMetrics
= TRUE
;
8344 infoPtr
->bDoChangeNotify
= TRUE
;
8345 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
8346 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
8347 infoPtr
->nEditLabelItem
= -1;
8348 infoPtr
->nLButtonDownItem
= -1;
8349 infoPtr
->dwHoverTime
= -1; /* default system hover time */
8350 infoPtr
->nMeasureItemHeight
= 0;
8351 infoPtr
->xTrackLine
= -1; /* no track line */
8352 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8353 infoPtr
->iVersion
= COMCTL32_VERSION
;
8355 /* get default font (icon title) */
8356 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
8357 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
8358 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
8359 LISTVIEW_SaveTextMetrics(infoPtr
);
8361 /* allocate memory for the data structure */
8362 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
8363 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
8364 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
8365 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
8366 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
8370 DestroyWindow(infoPtr
->hwndHeader
);
8371 ranges_destroy(infoPtr
->selectionRanges
);
8372 DPA_Destroy(infoPtr
->hdpaItems
);
8373 DPA_Destroy(infoPtr
->hdpaPosX
);
8374 DPA_Destroy(infoPtr
->hdpaPosY
);
8375 DPA_Destroy(infoPtr
->hdpaColumns
);
8382 * Creates the listview control - the WM_CREATE phase. Most of the data is
8383 * already set up in LISTVIEW_NCCreate
8386 * [I] hwnd : window handle
8387 * [I] lpcs : the create parameters
8393 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8395 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8397 TRACE("(lpcs=%p)\n", lpcs
);
8399 infoPtr
->dwStyle
= lpcs
->style
;
8400 map_style_view(infoPtr
);
8402 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
8403 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
8404 /* on error defaulting to ANSI notifications */
8405 if (infoPtr
->notifyFormat
== 0) infoPtr
->notifyFormat
= NFR_ANSI
;
8407 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && (lpcs
->style
& WS_VISIBLE
))
8409 if (LISTVIEW_CreateHeader(infoPtr
) < 0) return -1;
8412 infoPtr
->hwndHeader
= 0;
8414 /* init item size to avoid division by 0 */
8415 LISTVIEW_UpdateItemSize (infoPtr
);
8417 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
8419 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
) && (WS_VISIBLE
& lpcs
->style
))
8421 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8423 LISTVIEW_UpdateScroll(infoPtr
);
8426 OpenThemeData(hwnd
, themeClass
);
8428 /* initialize the icon sizes */
8429 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, infoPtr
->uView
!= LV_VIEW_ICON
);
8430 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
8436 * Destroys the listview control.
8439 * [I] infoPtr : valid pointer to the listview structure
8445 static LRESULT
LISTVIEW_Destroy(const LISTVIEW_INFO
*infoPtr
)
8447 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
8448 CloseThemeData(theme
);
8454 * Enables the listview control.
8457 * [I] infoPtr : valid pointer to the listview structure
8458 * [I] bEnable : specifies whether to enable or disable the window
8464 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
8466 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
8467 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8473 * Erases the background of the listview control.
8476 * [I] infoPtr : valid pointer to the listview structure
8477 * [I] hdc : device context handle
8483 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8487 TRACE("(hdc=%p)\n", hdc
);
8489 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
8491 if (infoPtr
->clrBk
== CLR_NONE
)
8492 return SendMessageW(infoPtr
->hwndNotify
, WM_ERASEBKGND
, (WPARAM
)hdc
, 0);
8494 /* for double buffered controls we need to do this during refresh */
8495 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
8497 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
8503 * Helper function for LISTVIEW_[HV]Scroll *only*.
8504 * Performs vertical/horizontal scrolling by a give amount.
8507 * [I] infoPtr : valid pointer to the listview structure
8508 * [I] dx : amount of horizontal scroll
8509 * [I] dy : amount of vertical scroll
8511 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8513 /* now we can scroll the list */
8514 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
8515 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
8516 /* if we have focus, adjust rect */
8517 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
8518 UpdateWindow(infoPtr
->hwndSelf
);
8523 * Performs vertical scrolling.
8526 * [I] infoPtr : valid pointer to the listview structure
8527 * [I] nScrollCode : scroll code
8528 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8529 * [I] hScrollWnd : scrollbar control window handle
8535 * SB_LINEUP/SB_LINEDOWN:
8536 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8537 * for LVS_REPORT is 1 line
8538 * for LVS_LIST cannot occur
8541 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8542 INT nScrollDiff
, HWND hScrollWnd
)
8544 INT nOldScrollPos
, nNewScrollPos
;
8545 SCROLLINFO scrollInfo
;
8548 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8549 debugscrollcode(nScrollCode
), nScrollDiff
);
8551 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8553 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8554 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8556 is_an_icon
= ((infoPtr
->uView
== LV_VIEW_ICON
) || (infoPtr
->uView
== LV_VIEW_SMALLICON
));
8558 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
8560 nOldScrollPos
= scrollInfo
.nPos
;
8561 switch (nScrollCode
)
8567 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
8571 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
8575 nScrollDiff
= -scrollInfo
.nPage
;
8579 nScrollDiff
= scrollInfo
.nPage
;
8582 case SB_THUMBPOSITION
:
8584 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8591 /* quit right away if pos isn't changing */
8592 if (nScrollDiff
== 0) return 0;
8594 /* calculate new position, and handle overflows */
8595 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8596 if (nScrollDiff
> 0) {
8597 if (nNewScrollPos
< nOldScrollPos
||
8598 nNewScrollPos
> scrollInfo
.nMax
)
8599 nNewScrollPos
= scrollInfo
.nMax
;
8601 if (nNewScrollPos
> nOldScrollPos
||
8602 nNewScrollPos
< scrollInfo
.nMin
)
8603 nNewScrollPos
= scrollInfo
.nMin
;
8606 /* set the new position, and reread in case it changed */
8607 scrollInfo
.fMask
= SIF_POS
;
8608 scrollInfo
.nPos
= nNewScrollPos
;
8609 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
8611 /* carry on only if it really changed */
8612 if (nNewScrollPos
== nOldScrollPos
) return 0;
8614 /* now adjust to client coordinates */
8615 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8616 if (infoPtr
->uView
== LV_VIEW_DETAILS
) nScrollDiff
*= infoPtr
->nItemHeight
;
8618 /* and scroll the window */
8619 scroll_list(infoPtr
, 0, nScrollDiff
);
8626 * Performs horizontal scrolling.
8629 * [I] infoPtr : valid pointer to the listview structure
8630 * [I] nScrollCode : scroll code
8631 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8632 * [I] hScrollWnd : scrollbar control window handle
8638 * SB_LINELEFT/SB_LINERIGHT:
8639 * for LVS_ICON, LVS_SMALLICON 1 pixel
8640 * for LVS_REPORT is 1 pixel
8641 * for LVS_LIST is 1 column --> which is a 1 because the
8642 * scroll is based on columns not pixels
8645 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8646 INT nScrollDiff
, HWND hScrollWnd
)
8648 INT nOldScrollPos
, nNewScrollPos
;
8649 SCROLLINFO scrollInfo
;
8651 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8652 debugscrollcode(nScrollCode
), nScrollDiff
);
8654 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8656 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8657 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8659 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
8661 nOldScrollPos
= scrollInfo
.nPos
;
8663 switch (nScrollCode
)
8677 nScrollDiff
= -scrollInfo
.nPage
;
8681 nScrollDiff
= scrollInfo
.nPage
;
8684 case SB_THUMBPOSITION
:
8686 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8693 /* quit right away if pos isn't changing */
8694 if (nScrollDiff
== 0) return 0;
8696 /* calculate new position, and handle overflows */
8697 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8698 if (nScrollDiff
> 0) {
8699 if (nNewScrollPos
< nOldScrollPos
||
8700 nNewScrollPos
> scrollInfo
.nMax
)
8701 nNewScrollPos
= scrollInfo
.nMax
;
8703 if (nNewScrollPos
> nOldScrollPos
||
8704 nNewScrollPos
< scrollInfo
.nMin
)
8705 nNewScrollPos
= scrollInfo
.nMin
;
8708 /* set the new position, and reread in case it changed */
8709 scrollInfo
.fMask
= SIF_POS
;
8710 scrollInfo
.nPos
= nNewScrollPos
;
8711 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
8713 /* carry on only if it really changed */
8714 if (nNewScrollPos
== nOldScrollPos
) return 0;
8716 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
8717 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
8719 /* now adjust to client coordinates */
8720 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8721 if (infoPtr
->uView
== LV_VIEW_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
8723 /* and scroll the window */
8724 scroll_list(infoPtr
, nScrollDiff
, 0);
8729 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
8731 INT gcWheelDelta
= 0;
8732 INT pulScrollLines
= 3;
8733 SCROLLINFO scrollInfo
;
8735 TRACE("(wheelDelta=%d)\n", wheelDelta
);
8737 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
8738 gcWheelDelta
-= wheelDelta
;
8740 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8741 scrollInfo
.fMask
= SIF_POS
;
8743 switch(infoPtr
->uView
)
8746 case LV_VIEW_SMALLICON
:
8748 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8749 * should be fixed in the future.
8751 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
8752 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
8755 case LV_VIEW_DETAILS
:
8756 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
8758 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
8759 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
8760 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
8765 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
8776 * [I] infoPtr : valid pointer to the listview structure
8777 * [I] nVirtualKey : virtual key
8778 * [I] lKeyData : key data
8783 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
8785 HWND hwndSelf
= infoPtr
->hwndSelf
;
8787 NMLVKEYDOWN nmKeyDown
;
8789 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
8791 /* send LVN_KEYDOWN notification */
8792 nmKeyDown
.wVKey
= nVirtualKey
;
8793 nmKeyDown
.flags
= 0;
8794 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
8795 if (!IsWindow(hwndSelf
))
8798 switch (nVirtualKey
)
8801 nItem
= infoPtr
->nFocusedItem
;
8802 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8803 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
8807 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
8809 if (!notify(infoPtr
, NM_RETURN
)) return 0;
8810 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
8815 if (infoPtr
->nItemCount
> 0)
8820 if (infoPtr
->nItemCount
> 0)
8821 nItem
= infoPtr
->nItemCount
- 1;
8825 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8829 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8833 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8837 nItem
= LISTVIEW_GetNextItem(infoPtr
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8841 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
8843 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8844 if (infoPtr
->nFocusedItem
== topidx
)
8845 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
8850 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
8851 * LISTVIEW_GetCountPerRow(infoPtr
);
8852 if(nItem
< 0) nItem
= 0;
8856 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
8858 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8859 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
8860 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
8861 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
8863 nItem
= topidx
+ cnt
- 1;
8866 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
8867 * LISTVIEW_GetCountPerRow(infoPtr
);
8868 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
8872 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
8873 LISTVIEW_KeySelection(infoPtr
, nItem
, nVirtualKey
== VK_SPACE
);
8883 * [I] infoPtr : valid pointer to the listview structure
8888 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8892 /* if we did not have the focus, there's nothing to do */
8893 if (!infoPtr
->bFocus
) return 0;
8895 /* send NM_KILLFOCUS notification */
8896 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
8898 /* if we have a focus rectangle, get rid of it */
8899 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
8901 /* set window focus flag */
8902 infoPtr
->bFocus
= FALSE
;
8904 /* invalidate the selected items before resetting focus flag */
8905 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8912 * Processes double click messages (left mouse button).
8915 * [I] infoPtr : valid pointer to the listview structure
8916 * [I] wKey : key flag
8917 * [I] x,y : mouse coordinate
8922 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8924 LVHITTESTINFO htInfo
;
8926 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8928 /* Cancel the item edition if any */
8929 if (infoPtr
->itemEdit
.fEnabled
)
8931 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
8932 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8935 /* send NM_RELEASEDCAPTURE notification */
8936 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8941 /* send NM_DBLCLK notification */
8942 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8943 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
8945 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8946 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8953 * Processes mouse down messages (left mouse button).
8956 * infoPtr [I ] valid pointer to the listview structure
8957 * wKey [I ] key flag
8958 * x,y [I ] mouse coordinate
8963 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8965 LVHITTESTINFO lvHitTestInfo
;
8966 static BOOL bGroupSelect
= TRUE
;
8967 POINT pt
= { x
, y
};
8970 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8972 /* send NM_RELEASEDCAPTURE notification */
8973 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8975 /* set left button down flag and record the click position */
8976 infoPtr
->bLButtonDown
= TRUE
;
8977 infoPtr
->ptClickPos
= pt
;
8978 infoPtr
->bDragging
= FALSE
;
8980 lvHitTestInfo
.pt
.x
= x
;
8981 lvHitTestInfo
.pt
.y
= y
;
8983 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8984 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
8985 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8987 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8989 toggle_checkbox_state(infoPtr
, nItem
);
8993 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8995 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8996 infoPtr
->nEditLabelItem
= nItem
;
8998 LISTVIEW_SetSelection(infoPtr
, nItem
);
9002 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
9006 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
9007 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
9008 infoPtr
->nSelectionMark
= nItem
;
9014 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
9015 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
9017 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
9018 infoPtr
->nSelectionMark
= nItem
;
9021 else if (wKey
& MK_CONTROL
)
9025 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
9027 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
9028 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
9029 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
9030 infoPtr
->nSelectionMark
= nItem
;
9032 else if (wKey
& MK_SHIFT
)
9034 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
9038 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
9040 infoPtr
->nEditLabelItem
= nItem
;
9041 infoPtr
->nLButtonDownItem
= nItem
;
9043 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
9046 /* set selection (clears other pre-existing selections) */
9047 LISTVIEW_SetSelection(infoPtr
, nItem
);
9051 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
9052 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
9056 /* remove all selections */
9057 if (!(wKey
& MK_CONTROL
) && !(wKey
& MK_SHIFT
))
9058 LISTVIEW_DeselectAll(infoPtr
);
9067 * Processes mouse up messages (left mouse button).
9070 * infoPtr [I ] valid pointer to the listview structure
9071 * wKey [I ] key flag
9072 * x,y [I ] mouse coordinate
9077 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9079 LVHITTESTINFO lvHitTestInfo
;
9081 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
9083 if (!infoPtr
->bLButtonDown
) return 0;
9085 lvHitTestInfo
.pt
.x
= x
;
9086 lvHitTestInfo
.pt
.y
= y
;
9088 /* send NM_CLICK notification */
9089 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9090 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
9092 /* set left button flag */
9093 infoPtr
->bLButtonDown
= FALSE
;
9095 /* set a single selection, reset others */
9096 if(lvHitTestInfo
.iItem
== infoPtr
->nLButtonDownItem
&& lvHitTestInfo
.iItem
!= -1)
9097 LISTVIEW_SetSelection(infoPtr
, infoPtr
->nLButtonDownItem
);
9098 infoPtr
->nLButtonDownItem
= -1;
9100 if (infoPtr
->bDragging
)
9102 infoPtr
->bDragging
= FALSE
;
9106 /* if we clicked on a selected item, edit the label */
9107 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
9109 /* we want to make sure the user doesn't want to do a double click. So we will
9110 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
9112 infoPtr
->itemEdit
.fEnabled
= TRUE
;
9113 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
9114 SetTimer(infoPtr
->hwndSelf
,
9115 (UINT_PTR
)&infoPtr
->itemEdit
,
9116 GetDoubleClickTime(),
9117 LISTVIEW_DelayedEditItem
);
9120 if (!infoPtr
->bFocus
)
9121 SetFocus(infoPtr
->hwndSelf
);
9128 * Destroys the listview control (called after WM_DESTROY).
9131 * [I] infoPtr : valid pointer to the listview structure
9136 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
9140 /* delete all items */
9141 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
9143 /* destroy data structure */
9144 DPA_Destroy(infoPtr
->hdpaItems
);
9145 DPA_Destroy(infoPtr
->hdpaPosX
);
9146 DPA_Destroy(infoPtr
->hdpaPosY
);
9147 DPA_Destroy(infoPtr
->hdpaColumns
);
9148 ranges_destroy(infoPtr
->selectionRanges
);
9150 /* destroy image lists */
9151 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
9153 if (infoPtr
->himlNormal
)
9154 ImageList_Destroy(infoPtr
->himlNormal
);
9155 if (infoPtr
->himlSmall
)
9156 ImageList_Destroy(infoPtr
->himlSmall
);
9157 if (infoPtr
->himlState
)
9158 ImageList_Destroy(infoPtr
->himlState
);
9161 /* destroy font, bkgnd brush */
9163 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
9164 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
9166 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
9168 /* free listview info pointer*/
9176 * Handles notifications from header.
9179 * [I] infoPtr : valid pointer to the listview structure
9180 * [I] nCtrlId : control identifier
9181 * [I] lpnmh : notification information
9186 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
9188 HWND hwndSelf
= infoPtr
->hwndSelf
;
9190 TRACE("(lpnmh=%p)\n", lpnmh
);
9192 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
9194 switch (lpnmh
->hdr
.code
)
9199 COLUMN_INFO
*lpColumnInfo
;
9203 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
9206 /* remove the old line (if any) */
9207 LISTVIEW_DrawTrackLine(infoPtr
);
9209 /* compute & draw the new line */
9210 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
9211 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
9212 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
9213 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
9214 LISTVIEW_DrawTrackLine(infoPtr
);
9220 /* remove the track line (if any) */
9221 LISTVIEW_DrawTrackLine(infoPtr
);
9222 infoPtr
->xTrackLine
= -1;
9226 FIXME("Changing column order not implemented\n");
9229 case HDN_ITEMCHANGINGW
:
9230 case HDN_ITEMCHANGINGA
:
9231 return notify_forward_header(infoPtr
, lpnmh
);
9233 case HDN_ITEMCHANGEDW
:
9234 case HDN_ITEMCHANGEDA
:
9236 COLUMN_INFO
*lpColumnInfo
;
9239 notify_forward_header(infoPtr
, lpnmh
);
9240 if (!IsWindow(hwndSelf
))
9243 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
9247 hdi
.mask
= HDI_WIDTH
;
9248 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, &hdi
)) return 0;
9252 cxy
= lpnmh
->pitem
->cxy
;
9254 /* determine how much we change since the last know position */
9255 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
9256 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
9259 lpColumnInfo
->rcHeader
.right
+= dx
;
9260 if (lpnmh
->iItem
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
9261 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
9264 /* only needs to update the scrolls */
9265 infoPtr
->nItemWidth
+= dx
;
9266 LISTVIEW_UpdateScroll(infoPtr
);
9268 LISTVIEW_UpdateItemSize(infoPtr
);
9269 if (infoPtr
->uView
== LV_VIEW_DETAILS
&& is_redrawing(infoPtr
))
9272 RECT rcCol
= lpColumnInfo
->rcHeader
;
9274 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
9275 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
9277 rcCol
.top
= infoPtr
->rcList
.top
;
9278 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
9280 /* resizing left-aligned columns leaves most of the left side untouched */
9281 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
9283 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
9286 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
9289 /* when shrinking the last column clear the now unused field */
9290 if (lpnmh
->iItem
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
9296 /* deal with right from rightmost column area */
9297 right
.left
= rcCol
.right
;
9298 right
.top
= rcCol
.top
;
9299 right
.bottom
= rcCol
.bottom
;
9300 right
.right
= infoPtr
->rcList
.right
;
9302 LISTVIEW_InvalidateRect(infoPtr
, &right
);
9305 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
9311 case HDN_ITEMCLICKW
:
9312 case HDN_ITEMCLICKA
:
9314 /* Handle sorting by Header Column */
9317 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
9319 nmlv
.iSubItem
= lpnmh
->iItem
;
9320 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
9321 notify_forward_header(infoPtr
, lpnmh
);
9325 case HDN_DIVIDERDBLCLICKW
:
9326 case HDN_DIVIDERDBLCLICKA
:
9327 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
9336 * Paint non-client area of control.
9339 * [I] infoPtr : valid pointer to the listview structureof the sender
9340 * [I] region : update region
9343 * TRUE - frame was painted
9344 * FALSE - call default window proc
9346 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
9348 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
9352 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
9353 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
9355 if (!theme
) return FALSE
;
9357 GetWindowRect(infoPtr
->hwndSelf
, &r
);
9359 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
9360 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
9361 if (region
!= (HRGN
)1)
9362 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
9363 OffsetRect(&r
, -r
.left
, -r
.top
);
9365 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
9366 OffsetRect(&r
, -r
.left
, -r
.top
);
9368 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
9369 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
9370 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
9371 ReleaseDC(infoPtr
->hwndSelf
, dc
);
9373 /* Call default proc to get the scrollbars etc. painted */
9374 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
9381 * Determines the type of structure to use.
9384 * [I] infoPtr : valid pointer to the listview structureof the sender
9385 * [I] hwndFrom : listview window handle
9386 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9391 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
9393 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
9395 if (nCommand
== NF_REQUERY
)
9396 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9398 return infoPtr
->notifyFormat
;
9403 * Paints/Repaints the listview control.
9406 * [I] infoPtr : valid pointer to the listview structure
9407 * [I] hdc : device context handle
9412 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9414 TRACE("(hdc=%p)\n", hdc
);
9416 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
9418 infoPtr
->bNoItemMetrics
= FALSE
;
9419 LISTVIEW_UpdateItemSize(infoPtr
);
9420 if (infoPtr
->uView
== LV_VIEW_ICON
|| infoPtr
->uView
== LV_VIEW_SMALLICON
)
9421 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9422 LISTVIEW_UpdateScroll(infoPtr
);
9425 if (infoPtr
->hwndHeader
) UpdateWindow(infoPtr
->hwndHeader
);
9428 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
9433 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
9435 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
9436 EndPaint(infoPtr
->hwndSelf
, &ps
);
9445 * Paints/Repaints the listview control.
9448 * [I] infoPtr : valid pointer to the listview structure
9449 * [I] hdc : device context handle
9450 * [I] options : drawing options
9455 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
9457 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc
, options
);
9459 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
9462 if (options
& PRF_ERASEBKGND
)
9463 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
9465 if (options
& PRF_CLIENT
)
9466 LISTVIEW_Paint(infoPtr
, hdc
);
9474 * Processes double click messages (right mouse button).
9477 * [I] infoPtr : valid pointer to the listview structure
9478 * [I] wKey : key flag
9479 * [I] x,y : mouse coordinate
9484 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9486 LVHITTESTINFO lvHitTestInfo
;
9488 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9490 /* send NM_RELEASEDCAPTURE notification */
9491 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9493 /* send NM_RDBLCLK notification */
9494 lvHitTestInfo
.pt
.x
= x
;
9495 lvHitTestInfo
.pt
.y
= y
;
9496 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9497 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
9504 * Processes mouse down messages (right mouse button).
9507 * [I] infoPtr : valid pointer to the listview structure
9508 * [I] wKey : key flag
9509 * [I] x,y : mouse coordinate
9514 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9516 LVHITTESTINFO lvHitTestInfo
;
9519 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9521 /* send NM_RELEASEDCAPTURE notification */
9522 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9524 /* make sure the listview control window has the focus */
9525 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
9527 /* set right button down flag */
9528 infoPtr
->bRButtonDown
= TRUE
;
9530 /* determine the index of the selected item */
9531 lvHitTestInfo
.pt
.x
= x
;
9532 lvHitTestInfo
.pt
.y
= y
;
9533 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
9535 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
9537 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
9538 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
9539 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
9540 LISTVIEW_SetSelection(infoPtr
, nItem
);
9544 LISTVIEW_DeselectAll(infoPtr
);
9552 * Processes mouse up messages (right mouse button).
9555 * [I] infoPtr : valid pointer to the listview structure
9556 * [I] wKey : key flag
9557 * [I] x,y : mouse coordinate
9562 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9564 LVHITTESTINFO lvHitTestInfo
;
9567 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9569 if (!infoPtr
->bRButtonDown
) return 0;
9571 /* set button flag */
9572 infoPtr
->bRButtonDown
= FALSE
;
9574 /* Send NM_RCLICK notification */
9575 lvHitTestInfo
.pt
.x
= x
;
9576 lvHitTestInfo
.pt
.y
= y
;
9577 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9578 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
9580 /* Change to screen coordinate for WM_CONTEXTMENU */
9581 pt
= lvHitTestInfo
.pt
;
9582 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
9584 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9585 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
9586 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
9597 * [I] infoPtr : valid pointer to the listview structure
9598 * [I] hwnd : window handle of window containing the cursor
9599 * [I] nHittest : hit-test code
9600 * [I] wMouseMsg : ideintifier of the mouse message
9603 * TRUE if cursor is set
9606 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
9608 LVHITTESTINFO lvHitTestInfo
;
9610 if(!(LISTVIEW_isHotTracking(infoPtr
))) return FALSE
;
9612 if(!infoPtr
->hHotCursor
) return FALSE
;
9614 GetCursorPos(&lvHitTestInfo
.pt
);
9615 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
9617 SetCursor(infoPtr
->hHotCursor
);
9627 * [I] infoPtr : valid pointer to the listview structure
9628 * [I] hwndLoseFocus : handle of previously focused window
9633 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
9635 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
9637 /* if we have the focus already, there's nothing to do */
9638 if (infoPtr
->bFocus
) return 0;
9640 /* send NM_SETFOCUS notification */
9641 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
9643 /* set window focus flag */
9644 infoPtr
->bFocus
= TRUE
;
9646 /* put the focus rect back on */
9647 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
9649 /* redraw all visible selected items */
9650 LISTVIEW_InvalidateSelectedItems(infoPtr
);
9660 * [I] infoPtr : valid pointer to the listview structure
9661 * [I] fRedraw : font handle
9662 * [I] fRedraw : redraw flag
9667 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
9669 HFONT oldFont
= infoPtr
->hFont
;
9671 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
9673 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
9674 if (infoPtr
->hFont
== oldFont
) return 0;
9676 LISTVIEW_SaveTextMetrics(infoPtr
);
9678 if (infoPtr
->uView
== LV_VIEW_DETAILS
)
9680 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
9681 LISTVIEW_UpdateSize(infoPtr
);
9682 LISTVIEW_UpdateScroll(infoPtr
);
9685 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
9692 * Message handling for WM_SETREDRAW.
9693 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9696 * [I] infoPtr : valid pointer to the listview structure
9697 * [I] bRedraw: state of redraw flag
9700 * DefWinProc return value
9702 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
9704 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
9706 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9707 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
9709 infoPtr
->bRedraw
= bRedraw
;
9711 if(!bRedraw
) return 0;
9713 if (is_autoarrange(infoPtr
))
9714 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9715 LISTVIEW_UpdateScroll(infoPtr
);
9717 /* despite what the WM_SETREDRAW docs says, apps expect us
9718 * to invalidate the listview here... stupid! */
9719 LISTVIEW_InvalidateList(infoPtr
);
9726 * Resizes the listview control. This function processes WM_SIZE
9727 * messages. At this time, the width and height are not used.
9730 * [I] infoPtr : valid pointer to the listview structure
9731 * [I] Width : new width
9732 * [I] Height : new height
9737 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
9739 RECT rcOld
= infoPtr
->rcList
;
9741 TRACE("(width=%d, height=%d)\n", Width
, Height
);
9743 LISTVIEW_UpdateSize(infoPtr
);
9744 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
9746 /* do not bother with display related stuff if we're not redrawing */
9747 if (!is_redrawing(infoPtr
)) return 0;
9749 if (is_autoarrange(infoPtr
))
9750 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9752 LISTVIEW_UpdateScroll(infoPtr
);
9754 /* refresh all only for lists whose height changed significantly */
9755 if ((infoPtr
->uView
== LV_VIEW_LIST
) &&
9756 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
9757 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
9758 LISTVIEW_InvalidateList(infoPtr
);
9765 * Sets the size information.
9768 * [I] infoPtr : valid pointer to the listview structure
9773 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
9775 TRACE("uView=%d, rcList(old)=%s\n", infoPtr
->uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
9777 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
9779 if (infoPtr
->uView
== LV_VIEW_LIST
)
9781 /* Apparently the "LIST" style is supposed to have the same
9782 * number of items in a column even if there is no scroll bar.
9783 * Since if a scroll bar already exists then the bottom is already
9784 * reduced, only reduce if the scroll bar does not currently exist.
9785 * The "2" is there to mimic the native control. I think it may be
9786 * related to either padding or edges. (GLA 7/2002)
9788 if (!(infoPtr
->dwStyle
& WS_HSCROLL
))
9789 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
9790 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
9792 else if (infoPtr
->uView
== LV_VIEW_DETAILS
)
9797 hl
.prc
= &infoPtr
->rcList
;
9799 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9800 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9801 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9802 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9803 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9804 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9806 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
9807 infoPtr
->rcList
.top
+= (infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
) ? 2 : 0;
9810 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
9815 * Processes WM_STYLECHANGED messages.
9818 * [I] infoPtr : valid pointer to the listview structure
9819 * [I] wStyleType : window style type (normal or extended)
9820 * [I] lpss : window style information
9825 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
9826 const STYLESTRUCT
*lpss
)
9828 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
9829 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
9832 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9833 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9835 if (wStyleType
!= GWL_STYLE
) return 0;
9837 infoPtr
->dwStyle
= lpss
->styleNew
;
9838 map_style_view(infoPtr
);
9840 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
9841 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
9842 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
9844 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
9845 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
9846 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
9848 if (uNewView
!= uOldView
)
9850 SIZE oldIconSize
= infoPtr
->iconSize
;
9853 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9854 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9856 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9857 SetRectEmpty(&infoPtr
->rcFocus
);
9859 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9860 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
9862 if (uNewView
== LVS_ICON
)
9864 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
9866 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9867 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
9868 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
9871 else if (uNewView
== LVS_REPORT
)
9876 LISTVIEW_CreateHeader( infoPtr
);
9878 hl
.prc
= &infoPtr
->rcList
;
9880 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9881 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9882 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9883 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9886 LISTVIEW_UpdateItemSize(infoPtr
);
9889 if (uNewView
== LVS_REPORT
)
9891 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
9893 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
9895 /* Turn off the header control */
9896 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
9897 TRACE("Hide header control, was 0x%08x\n", style
);
9898 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
9900 /* Turn on the header control */
9901 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
9903 TRACE("Show header control, was 0x%08x\n", style
);
9904 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
9910 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
9911 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
9912 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9914 /* update the size of the client area */
9915 LISTVIEW_UpdateSize(infoPtr
);
9917 /* add scrollbars if needed */
9918 LISTVIEW_UpdateScroll(infoPtr
);
9920 /* invalidate client area + erase background */
9921 LISTVIEW_InvalidateList(infoPtr
);
9928 * Processes WM_STYLECHANGING messages.
9931 * [I] infoPtr : valid pointer to the listview structure
9932 * [I] wStyleType : window style type (normal or extended)
9933 * [I0] lpss : window style information
9938 static INT
LISTVIEW_StyleChanging(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
9941 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9942 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9944 /* don't forward LVS_OWNERDATA only if not already set to */
9945 if ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_OWNERDATA
)
9947 if (lpss
->styleOld
& LVS_OWNERDATA
)
9948 lpss
->styleNew
|= LVS_OWNERDATA
;
9950 lpss
->styleNew
&= ~LVS_OWNERDATA
;
9958 * Processes WM_SHOWWINDOW messages.
9961 * [I] infoPtr : valid pointer to the listview structure
9962 * [I] bShown : window is being shown (FALSE when hidden)
9963 * [I] iStatus : window show status
9968 static LRESULT
LISTVIEW_ShowWindow(LISTVIEW_INFO
*infoPtr
, BOOL bShown
, INT iStatus
)
9970 /* header delayed creation */
9971 if ((infoPtr
->uView
== LV_VIEW_DETAILS
) && bShown
)
9973 LISTVIEW_CreateHeader(infoPtr
);
9975 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
))
9976 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
9984 * Processes CCM_GETVERSION messages.
9987 * [I] infoPtr : valid pointer to the listview structure
9992 static inline LRESULT
LISTVIEW_GetVersion(const LISTVIEW_INFO
*infoPtr
)
9994 return infoPtr
->iVersion
;
9999 * Processes CCM_SETVERSION messages.
10002 * [I] infoPtr : valid pointer to the listview structure
10003 * [I] iVersion : version to be set
10006 * -1 when requested version is greater than DLL version;
10007 * previous version otherwise
10009 static LRESULT
LISTVIEW_SetVersion(LISTVIEW_INFO
*infoPtr
, DWORD iVersion
)
10011 INT iOldVersion
= infoPtr
->iVersion
;
10013 if (iVersion
> COMCTL32_VERSION
)
10016 infoPtr
->iVersion
= iVersion
;
10018 TRACE("new version %d\n", iVersion
);
10020 return iOldVersion
;
10025 * Window procedure of the listview control.
10028 static LRESULT WINAPI
10029 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10031 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
10033 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg
, wParam
, lParam
);
10035 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
10036 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10040 case LVM_APPROXIMATEVIEWRECT
:
10041 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
10042 LOWORD(lParam
), HIWORD(lParam
));
10044 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
10046 /* case LVM_CANCELEDITLABEL: */
10048 case LVM_CREATEDRAGIMAGE
:
10049 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
10051 case LVM_DELETEALLITEMS
:
10052 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
10054 case LVM_DELETECOLUMN
:
10055 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
10057 case LVM_DELETEITEM
:
10058 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
10060 case LVM_EDITLABELW
:
10061 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
10063 case LVM_EDITLABELA
:
10064 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
10066 /* case LVM_ENABLEGROUPVIEW: */
10068 case LVM_ENSUREVISIBLE
:
10069 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
10071 case LVM_FINDITEMW
:
10072 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
10074 case LVM_FINDITEMA
:
10075 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
10077 case LVM_GETBKCOLOR
:
10078 return infoPtr
->clrBk
;
10080 /* case LVM_GETBKIMAGE: */
10082 case LVM_GETCALLBACKMASK
:
10083 return infoPtr
->uCallbackMask
;
10085 case LVM_GETCOLUMNA
:
10086 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
10088 case LVM_GETCOLUMNW
:
10089 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
10091 case LVM_GETCOLUMNORDERARRAY
:
10092 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
10094 case LVM_GETCOLUMNWIDTH
:
10095 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
10097 case LVM_GETCOUNTPERPAGE
:
10098 return LISTVIEW_GetCountPerPage(infoPtr
);
10100 case LVM_GETEDITCONTROL
:
10101 return (LRESULT
)infoPtr
->hwndEdit
;
10103 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
10104 return infoPtr
->dwLvExStyle
;
10106 /* case LVM_GETGROUPINFO: */
10108 /* case LVM_GETGROUPMETRICS: */
10110 case LVM_GETHEADER
:
10111 return (LRESULT
)infoPtr
->hwndHeader
;
10113 case LVM_GETHOTCURSOR
:
10114 return (LRESULT
)infoPtr
->hHotCursor
;
10116 case LVM_GETHOTITEM
:
10117 return infoPtr
->nHotItem
;
10119 case LVM_GETHOVERTIME
:
10120 return infoPtr
->dwHoverTime
;
10122 case LVM_GETIMAGELIST
:
10123 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
10125 /* case LVM_GETINSERTMARK: */
10127 /* case LVM_GETINSERTMARKCOLOR: */
10129 /* case LVM_GETINSERTMARKRECT: */
10131 case LVM_GETISEARCHSTRINGA
:
10132 case LVM_GETISEARCHSTRINGW
:
10133 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
10137 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
10140 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
10142 case LVM_GETITEMCOUNT
:
10143 return infoPtr
->nItemCount
;
10145 case LVM_GETITEMPOSITION
:
10146 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
10148 case LVM_GETITEMRECT
:
10149 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
10151 case LVM_GETITEMSPACING
:
10152 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
10154 case LVM_GETITEMSTATE
:
10155 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
10157 case LVM_GETITEMTEXTA
:
10158 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
10160 case LVM_GETITEMTEXTW
:
10161 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
10163 case LVM_GETNEXTITEM
:
10164 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
10166 case LVM_GETNUMBEROFWORKAREAS
:
10167 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
10170 case LVM_GETORIGIN
:
10171 if (!lParam
) return FALSE
;
10172 if (infoPtr
->uView
== LV_VIEW_DETAILS
||
10173 infoPtr
->uView
== LV_VIEW_LIST
) return FALSE
;
10174 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
10177 /* case LVM_GETOUTLINECOLOR: */
10179 /* case LVM_GETSELECTEDCOLUMN: */
10181 case LVM_GETSELECTEDCOUNT
:
10182 return LISTVIEW_GetSelectedCount(infoPtr
);
10184 case LVM_GETSELECTIONMARK
:
10185 return infoPtr
->nSelectionMark
;
10187 case LVM_GETSTRINGWIDTHA
:
10188 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
10190 case LVM_GETSTRINGWIDTHW
:
10191 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
10193 case LVM_GETSUBITEMRECT
:
10194 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
10196 case LVM_GETTEXTBKCOLOR
:
10197 return infoPtr
->clrTextBk
;
10199 case LVM_GETTEXTCOLOR
:
10200 return infoPtr
->clrText
;
10202 /* case LVM_GETTILEINFO: */
10204 /* case LVM_GETTILEVIEWINFO: */
10206 case LVM_GETTOOLTIPS
:
10207 if( !infoPtr
->hwndToolTip
)
10208 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
10209 return (LRESULT
)infoPtr
->hwndToolTip
;
10211 case LVM_GETTOPINDEX
:
10212 return LISTVIEW_GetTopIndex(infoPtr
);
10214 case LVM_GETUNICODEFORMAT
:
10215 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
10218 return infoPtr
->uView
;
10220 case LVM_GETVIEWRECT
:
10221 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
10223 case LVM_GETWORKAREAS
:
10224 FIXME("LVM_GETWORKAREAS: unimplemented\n");
10227 /* case LVM_HASGROUP: */
10230 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, TRUE
);
10232 case LVM_INSERTCOLUMNA
:
10233 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
10235 case LVM_INSERTCOLUMNW
:
10236 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
10238 /* case LVM_INSERTGROUP: */
10240 /* case LVM_INSERTGROUPSORTED: */
10242 case LVM_INSERTITEMA
:
10243 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
10245 case LVM_INSERTITEMW
:
10246 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
10248 /* case LVM_INSERTMARKHITTEST: */
10250 /* case LVM_ISGROUPVIEWENABLED: */
10252 /* case LVM_MAPIDTOINDEX: */
10254 /* case LVM_MAPINDEXTOID: */
10256 /* case LVM_MOVEGROUP: */
10258 /* case LVM_MOVEITEMTOGROUP: */
10260 case LVM_REDRAWITEMS
:
10261 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
10263 /* case LVM_REMOVEALLGROUPS: */
10265 /* case LVM_REMOVEGROUP: */
10268 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
10270 case LVM_SETBKCOLOR
:
10271 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
10273 /* case LVM_SETBKIMAGE: */
10275 case LVM_SETCALLBACKMASK
:
10276 infoPtr
->uCallbackMask
= (UINT
)wParam
;
10279 case LVM_SETCOLUMNA
:
10280 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
10282 case LVM_SETCOLUMNW
:
10283 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
10285 case LVM_SETCOLUMNORDERARRAY
:
10286 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
10288 case LVM_SETCOLUMNWIDTH
:
10289 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
10291 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
10292 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
10294 /* case LVM_SETGROUPINFO: */
10296 /* case LVM_SETGROUPMETRICS: */
10298 case LVM_SETHOTCURSOR
:
10299 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
10301 case LVM_SETHOTITEM
:
10302 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
10304 case LVM_SETHOVERTIME
:
10305 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
10307 case LVM_SETICONSPACING
:
10308 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
10310 case LVM_SETIMAGELIST
:
10311 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
10313 /* case LVM_SETINFOTIP: */
10315 /* case LVM_SETINSERTMARK: */
10317 /* case LVM_SETINSERTMARKCOLOR: */
10322 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
10323 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, (uMsg
== LVM_SETITEMW
));
10326 case LVM_SETITEMCOUNT
:
10327 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
10329 case LVM_SETITEMPOSITION
:
10332 pt
.x
= (short)LOWORD(lParam
);
10333 pt
.y
= (short)HIWORD(lParam
);
10334 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
10337 case LVM_SETITEMPOSITION32
:
10338 if (lParam
== 0) return FALSE
;
10339 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
10341 case LVM_SETITEMSTATE
:
10342 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
10344 case LVM_SETITEMTEXTA
:
10345 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
10347 case LVM_SETITEMTEXTW
:
10348 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
10350 /* case LVM_SETOUTLINECOLOR: */
10352 /* case LVM_SETSELECTEDCOLUMN: */
10354 case LVM_SETSELECTIONMARK
:
10355 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
10357 case LVM_SETTEXTBKCOLOR
:
10358 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
10360 case LVM_SETTEXTCOLOR
:
10361 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
10363 /* case LVM_SETTILEINFO: */
10365 /* case LVM_SETTILEVIEWINFO: */
10367 /* case LVM_SETTILEWIDTH: */
10369 case LVM_SETTOOLTIPS
:
10370 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
10372 case LVM_SETUNICODEFORMAT
:
10373 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
10376 return LISTVIEW_SetView(infoPtr
, wParam
);
10378 /* case LVM_SETWORKAREAS: */
10380 /* case LVM_SORTGROUPS: */
10382 case LVM_SORTITEMS
:
10383 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
, FALSE
);
10385 case LVM_SORTITEMSEX
:
10386 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
, TRUE
);
10388 case LVM_SUBITEMHITTEST
:
10389 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
10392 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
10394 case CCM_GETVERSION
:
10395 return LISTVIEW_GetVersion(infoPtr
);
10397 case CCM_SETVERSION
:
10398 return LISTVIEW_SetVersion(infoPtr
, wParam
);
10401 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
10404 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
10407 return LISTVIEW_NCCreate(hwnd
, (LPCREATESTRUCTW
)lParam
);
10410 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
10413 return LISTVIEW_Destroy(infoPtr
);
10416 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
10418 case WM_ERASEBKGND
:
10419 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
10421 case WM_GETDLGCODE
:
10422 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
10425 return (LRESULT
)infoPtr
->hFont
;
10428 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10431 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
10434 return LISTVIEW_KillFocus(infoPtr
);
10436 case WM_LBUTTONDBLCLK
:
10437 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10439 case WM_LBUTTONDOWN
:
10440 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10443 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10446 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10448 case WM_MOUSEHOVER
:
10449 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10452 return LISTVIEW_NCDestroy(infoPtr
);
10455 if (LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
))
10460 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
10461 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
10464 case WM_NOTIFYFORMAT
:
10465 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
10467 case WM_PRINTCLIENT
:
10468 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
10471 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
10473 case WM_RBUTTONDBLCLK
:
10474 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10476 case WM_RBUTTONDOWN
:
10477 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10480 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10483 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
10488 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
10491 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
10494 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
10496 case WM_SHOWWINDOW
:
10497 LISTVIEW_ShowWindow(infoPtr
, (BOOL
)wParam
, (INT
)lParam
);
10498 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10501 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
10503 case WM_STYLECHANGED
:
10504 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
10506 case WM_STYLECHANGING
:
10507 return LISTVIEW_StyleChanging(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
10509 case WM_SYSCOLORCHANGE
:
10510 COMCTL32_RefreshSysColors();
10513 /* case WM_TIMER: */
10514 case WM_THEMECHANGED
:
10515 return LISTVIEW_ThemeChanged(infoPtr
);
10518 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10520 case WM_MOUSEWHEEL
:
10521 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
10522 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10523 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
10525 case WM_WINDOWPOSCHANGED
:
10526 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
10528 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
10529 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
10531 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (infoPtr
->uView
== LV_VIEW_DETAILS
))
10533 MEASUREITEMSTRUCT mis
;
10534 mis
.CtlType
= ODT_LISTVIEW
;
10535 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
10539 mis
.itemHeight
= infoPtr
->nItemHeight
;
10540 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
10541 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
10542 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
10545 LISTVIEW_UpdateSize(infoPtr
);
10546 LISTVIEW_UpdateScroll(infoPtr
);
10548 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10550 /* case WM_WININICHANGE: */
10553 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
10554 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
10557 /* call default window procedure */
10558 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10565 * Registers the window class.
10573 void LISTVIEW_Register(void)
10575 WNDCLASSW wndClass
;
10577 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
10578 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
10579 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
10580 wndClass
.cbClsExtra
= 0;
10581 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
10582 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
10583 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
10584 wndClass
.lpszClassName
= WC_LISTVIEWW
;
10585 RegisterClassW(&wndClass
);
10590 * Unregisters the window class.
10598 void LISTVIEW_Unregister(void)
10600 UnregisterClassW(WC_LISTVIEWW
, NULL
);
10605 * Handle any WM_COMMAND messages
10608 * [I] infoPtr : valid pointer to the listview structure
10609 * [I] wParam : the first message parameter
10610 * [I] lParam : the second message parameter
10615 static LRESULT
LISTVIEW_Command(LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10617 switch (HIWORD(wParam
))
10622 * Adjust the edit window size
10624 WCHAR buffer
[1024];
10625 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
10626 HFONT hFont
, hOldFont
= 0;
10630 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
10631 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
10632 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
10634 /* Select font to get the right dimension of the string */
10635 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
10638 hOldFont
= SelectObject(hdc
, hFont
);
10641 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
10643 TEXTMETRICW textMetric
;
10645 /* Add Extra spacing for the next character */
10646 GetTextMetricsW(hdc
, &textMetric
);
10647 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10655 rect
.bottom
- rect
.top
,
10656 SWP_DRAWFRAME
|SWP_NOMOVE
);
10659 SelectObject(hdc
, hOldFont
);
10661 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
10667 /* handle value will be lost after LISTVIEW_EndEditLabelT */
10668 HWND edit
= infoPtr
->hwndEdit
;
10670 LISTVIEW_EndEditLabelT(infoPtr
, TRUE
, IsWindowUnicode(infoPtr
->hwndEdit
));
10671 SendMessageW(edit
, WM_CLOSE
, 0, 0);
10675 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
10684 * Subclassed edit control windproc function
10687 * [I] hwnd : the edit window handle
10688 * [I] uMsg : the message that is to be processed
10689 * [I] wParam : first message parameter
10690 * [I] lParam : second message parameter
10691 * [I] isW : TRUE if input is Unicode
10696 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
10698 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
10701 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10702 hwnd
, uMsg
, wParam
, lParam
, isW
);
10706 case WM_GETDLGCODE
:
10707 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
10711 WNDPROC editProc
= infoPtr
->EditWndProc
;
10712 infoPtr
->EditWndProc
= 0;
10713 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
10714 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10718 if (VK_ESCAPE
== (INT
)wParam
)
10723 else if (VK_RETURN
== (INT
)wParam
)
10727 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10730 /* kill the edit */
10731 if (infoPtr
->hwndEdit
)
10732 LISTVIEW_EndEditLabelT(infoPtr
, save
, isW
);
10734 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
10740 * Subclassed edit control Unicode windproc function
10743 * [I] hwnd : the edit window handle
10744 * [I] uMsg : the message that is to be processed
10745 * [I] wParam : first message parameter
10746 * [I] lParam : second message parameter
10750 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10752 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
10757 * Subclassed edit control ANSI windproc function
10760 * [I] hwnd : the edit window handle
10761 * [I] uMsg : the message that is to be processed
10762 * [I] wParam : first message parameter
10763 * [I] lParam : second message parameter
10767 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10769 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
10774 * Creates a subclassed edit control
10777 * [I] infoPtr : valid pointer to the listview structure
10778 * [I] text : initial text for the edit
10779 * [I] style : the window style
10780 * [I] isW : TRUE if input is Unicode
10784 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
, BOOL isW
)
10786 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
10788 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
10790 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
10792 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
10794 /* Window will be resized and positioned after LVN_BEGINLABELEDIT */
10796 hedit
= CreateWindowW(editName
, text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
10798 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, 0, 0, 0, 0, infoPtr
->hwndSelf
, 0, hinst
, 0);
10800 if (!hedit
) return 0;
10802 infoPtr
->EditWndProc
= (WNDPROC
)
10803 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
10804 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
10806 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);