4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
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_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
63 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
64 * linear in the number of items in the list, and this is
65 * unacceptable for large lists.
66 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
67 * instead of inserting in the right spot
68 * -- we should keep an ordered array of coordinates in iconic mode
69 * this would allow to frame items (iterator_frameditems),
70 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
78 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
85 * -- LVS_NOSCROLL (see Q137520)
86 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
88 * -- LVS_TYPESTYLEMASK
91 * -- LVS_EX_BORDERSELECT
93 * -- LVS_EX_HEADERDRAGDROP
96 * -- LVS_EX_MULTIWORKAREAS
98 * -- LVS_EX_SIMPLESELECT
99 * -- LVS_EX_TWOCLICKACTIVATE
100 * -- LVS_EX_UNDERLINECOLD
101 * -- LVS_EX_UNDERLINEHOT
104 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
107 * -- LVN_MARQUEEBEGIN
113 * -- LVM_CANCELEDITLABEL
114 * -- LVM_ENABLEGROUPVIEW
115 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
116 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
117 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
118 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
119 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
120 * -- LVM_GETINSERTMARKRECT
121 * -- LVM_GETNUMBEROFWORKAREAS
122 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
123 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
124 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
125 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
126 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
127 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
128 * -- LVM_GETVIEW, LVM_SETVIEW
129 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
130 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
131 * -- LVM_INSERTGROUPSORTED
132 * -- LVM_INSERTMARKHITTEST
133 * -- LVM_ISGROUPVIEWENABLED
134 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
136 * -- LVM_MOVEITEMTOGROUP
138 * -- LVM_SETTILEWIDTH
142 * -- ListView_GetCheckSate, ListView_SetCheckState
143 * -- ListView_GetHoverTime, ListView_SetHoverTime
144 * -- ListView_GetISearchString
145 * -- ListView_GetNumberOfWorkAreas
146 * -- ListView_GetOrigin
147 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
148 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
153 * Known differences in message stream from native control (not known if
154 * these differences cause problems):
155 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
156 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
157 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
158 * processing for "USEDOUBLECLICKTIME".
162 #include "wine/port.h"
177 #include "commctrl.h"
178 #include "comctl32.h"
181 #include "wine/debug.h"
182 #include "wine/unicode.h"
184 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
186 /* make sure you set this to 0 for production use! */
187 #define DEBUG_RANGES 1
189 typedef struct tagCOLUMN_INFO
191 RECT rcHeader
; /* tracks the header's rectangle */
192 int fmt
; /* same as LVCOLUMN.fmt */
195 typedef struct tagITEMHDR
199 } ITEMHDR
, *LPITEMHDR
;
201 typedef struct tagSUBITEM_INFO
207 typedef struct tagITEM_INFO
215 typedef struct tagRANGE
221 typedef struct tagRANGES
226 typedef struct tagITERATOR
235 typedef struct tagDELAYED_ITEM_EDIT
241 typedef struct tagLISTVIEW_INFO
248 HIMAGELIST himlNormal
;
249 HIMAGELIST himlSmall
;
250 HIMAGELIST himlState
;
254 POINT ptClickPos
; /* point where the user clicked */
255 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
258 RANGES selectionRanges
;
263 RECT rcList
; /* This rectangle is really the window
264 * client rectangle possibly reduced by the
265 * horizontal scroll bar and/or header - see
266 * LISTVIEW_UpdateSize. This rectangle offset
267 * by the LISTVIEW_GetOrigin value is in
268 * client coordinates */
277 INT ntmHeight
; /* Some cached metrics of the font used */
278 INT ntmMaxCharWidth
; /* by the listview to draw items */
280 BOOL bRedraw
; /* Turns on/off repaints & invalidations */
281 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
283 BOOL bDoChangeNotify
; /* send change notification messages? */
286 DWORD dwStyle
; /* the cached window GWL_STYLE */
287 DWORD dwLvExStyle
; /* extended listview style */
288 INT nItemCount
; /* the number of items in the list */
289 HDPA hdpaItems
; /* array ITEM_INFO pointers */
290 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
291 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
292 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
293 POINT currIconPos
; /* this is the position next icon will be placed */
294 PFNLVCOMPARE pfnCompare
;
302 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
304 DWORD lastKeyPressTimestamp
;
306 INT nSearchParamLength
;
307 WCHAR szSearchParam
[ MAX_PATH
];
309 INT nMeasureItemHeight
;
310 INT xTrackLine
; /* The x coefficient of the track line or -1 if none */
311 DELAYED_ITEM_EDIT itemEdit
; /* Pointer to this structure will be the timer ID */
317 /* How many we debug buffer to allocate */
318 #define DEBUG_BUFFERS 20
319 /* The size of a single debug bbuffer */
320 #define DEBUG_BUFFER_SIZE 256
322 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
323 #define SB_INTERNAL -1
325 /* maximum size of a label */
326 #define DISP_TEXT_SIZE 512
328 /* padding for items in list and small icon display modes */
329 #define WIDTH_PADDING 12
331 /* padding for items in list, report and small icon display modes */
332 #define HEIGHT_PADDING 1
334 /* offset of items in report display mode */
335 #define REPORT_MARGINX 2
337 /* padding for icon in large icon display mode
338 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
339 * that HITTEST will see.
340 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
341 * ICON_TOP_PADDING - sum of the two above.
342 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
343 * LABEL_HOR_PADDING - between text and sides of box
344 * LABEL_VERT_PADDING - between bottom of text and end of box
346 * ICON_LR_PADDING - additional width above icon size.
347 * ICON_LR_HALF - half of the above value
349 #define ICON_TOP_PADDING_NOTHITABLE 2
350 #define ICON_TOP_PADDING_HITABLE 2
351 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
352 #define ICON_BOTTOM_PADDING 4
353 #define LABEL_HOR_PADDING 5
354 #define LABEL_VERT_PADDING 7
355 #define ICON_LR_PADDING 16
356 #define ICON_LR_HALF (ICON_LR_PADDING/2)
358 /* default label width for items in list and small icon display modes */
359 #define DEFAULT_LABEL_WIDTH 40
361 /* default column width for items in list display mode */
362 #define DEFAULT_COLUMN_WIDTH 128
364 /* Size of "line" scroll for V & H scrolls */
365 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
367 /* Padding between image and label */
368 #define IMAGE_PADDING 2
370 /* Padding behind the label */
371 #define TRAILING_LABEL_PADDING 12
372 #define TRAILING_HEADER_PADDING 11
374 /* Border for the icon caption */
375 #define CAPTION_BORDER 2
377 /* Standard DrawText flags */
378 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
379 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
380 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
382 /* Image index from state */
383 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
385 /* The time in milliseconds to reset the search in the list */
386 #define KEY_DELAY 450
388 /* Dump the LISTVIEW_INFO structure to the debug channel */
389 #define LISTVIEW_DUMP(iP) do { \
390 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
391 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
392 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
393 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
394 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
395 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
396 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
397 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
398 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
399 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
402 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
405 * forward declarations
407 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
408 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*, INT
, LPRECT
);
409 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*, INT
, LPPOINT
);
410 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*, INT
, LPPOINT
);
411 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*, INT
, LPRECT
);
412 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*, INT
);
413 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*, LPPOINT
);
414 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*, LPRECT
);
415 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
416 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, LVITEMW
*, BOOL
);
417 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*);
418 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
419 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
420 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
421 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*, WPARAM
, LPARAM
);
422 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
423 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
, BOOL
);
424 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*, INT
, UINT
);
425 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
426 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
427 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
428 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*);
429 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
430 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
431 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
432 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
434 /******** Text handling functions *************************************/
436 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
437 * text string. The string may be ANSI or Unicode, in which case
438 * the boolean isW tells us the type of the string.
440 * The name of the function tell what type of strings it expects:
441 * W: Unicode, T: ANSI/Unicode - function of isW
444 static inline BOOL
is_textW(LPCWSTR text
)
446 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
449 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
451 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
452 return is_textW(text
);
455 static inline int textlenT(LPCWSTR text
, BOOL isW
)
457 return !is_textT(text
, isW
) ? 0 :
458 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
461 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
464 if (isSrcW
) lstrcpynW(dest
, src
, max
);
465 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
467 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
468 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
471 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
473 LPWSTR wstr
= (LPWSTR
)text
;
475 if (!isW
&& is_textT(text
, isW
))
477 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
478 wstr
= Alloc(len
* sizeof(WCHAR
));
479 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
481 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
485 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
487 if (!isW
&& is_textT(wstr
, isW
)) Free (wstr
);
491 * dest is a pointer to a Unicode string
492 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
494 static BOOL
textsetptrT(LPWSTR
*dest
, LPCWSTR src
, BOOL isW
)
498 if (src
== LPSTR_TEXTCALLBACKW
)
500 if (is_textW(*dest
)) Free(*dest
);
501 *dest
= LPSTR_TEXTCALLBACKW
;
505 LPWSTR pszText
= textdupTtoW(src
, isW
);
506 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
507 bResult
= Str_SetPtrW(dest
, pszText
);
508 textfreeT(pszText
, isW
);
514 * compares a Unicode to a Unicode/ANSI text string
516 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
518 if (!aw
) return bt
? -1 : 0;
519 if (!bt
) return aw
? 1 : 0;
520 if (aw
== LPSTR_TEXTCALLBACKW
)
521 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
522 if (bt
!= LPSTR_TEXTCALLBACKW
)
524 LPWSTR bw
= textdupTtoW(bt
, isW
);
525 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
533 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
537 n
= min(min(n
, lstrlenW(s1
)), lstrlenW(s2
));
538 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
539 return res
? res
- sizeof(WCHAR
) : res
;
542 /******** Debugging functions *****************************************/
544 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
546 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
547 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
550 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
552 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
553 n
= min(textlenT(text
, isW
), n
);
554 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
557 static char* debug_getbuf(void)
559 static int index
= 0;
560 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
561 return buffers
[index
++ % DEBUG_BUFFERS
];
564 static inline const char* debugrange(const RANGE
*lprng
)
566 if (!lprng
) return "(null)";
567 return wine_dbg_sprintf("[%d, %d]", lprng
->lower
, lprng
->upper
);
570 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
572 char* buf
= debug_getbuf(), *text
= buf
;
573 int len
, size
= DEBUG_BUFFER_SIZE
;
575 if (pScrollInfo
== NULL
) return "(null)";
576 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
577 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
578 if (pScrollInfo
->fMask
& SIF_RANGE
)
579 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
581 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
582 if (pScrollInfo
->fMask
& SIF_PAGE
)
583 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
585 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
586 if (pScrollInfo
->fMask
& SIF_POS
)
587 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
589 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
590 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
591 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
593 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
596 buf
= text
+ strlen(text
);
598 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
602 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
604 if (!plvnm
) return "(null)";
605 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
606 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
607 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
608 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
611 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
613 char* buf
= debug_getbuf(), *text
= buf
;
614 int len
, size
= DEBUG_BUFFER_SIZE
;
616 if (lpLVItem
== NULL
) return "(null)";
617 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
618 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
619 if (lpLVItem
->mask
& LVIF_STATE
)
620 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
622 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
623 if (lpLVItem
->mask
& LVIF_TEXT
)
624 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
626 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
627 if (lpLVItem
->mask
& LVIF_IMAGE
)
628 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
630 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
631 if (lpLVItem
->mask
& LVIF_PARAM
)
632 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
634 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
635 if (lpLVItem
->mask
& LVIF_INDENT
)
636 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
638 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
641 buf
= text
+ strlen(text
);
643 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
647 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
649 char* buf
= debug_getbuf(), *text
= buf
;
650 int len
, size
= DEBUG_BUFFER_SIZE
;
652 if (lpColumn
== NULL
) return "(null)";
653 len
= snprintf(buf
, size
, "{");
654 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
655 if (lpColumn
->mask
& LVCF_SUBITEM
)
656 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
658 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
659 if (lpColumn
->mask
& LVCF_FMT
)
660 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
662 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
663 if (lpColumn
->mask
& LVCF_WIDTH
)
664 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
666 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
667 if (lpColumn
->mask
& LVCF_TEXT
)
668 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
670 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
671 if (lpColumn
->mask
& LVCF_IMAGE
)
672 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
674 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
675 if (lpColumn
->mask
& LVCF_ORDER
)
676 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
678 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
681 buf
= text
+ strlen(text
);
683 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
687 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
689 if (!lpht
) return "(null)";
691 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
692 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
695 /* Return the corresponding text for a given scroll value */
696 static inline LPCSTR
debugscrollcode(int nScrollCode
)
700 case SB_LINELEFT
: return "SB_LINELEFT";
701 case SB_LINERIGHT
: return "SB_LINERIGHT";
702 case SB_PAGELEFT
: return "SB_PAGELEFT";
703 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
704 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
705 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
706 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
707 case SB_INTERNAL
: return "SB_INTERNAL";
708 default: return "unknown";
713 /******** Notification functions ************************************/
715 static LRESULT
notify_forward_header(const LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
717 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
718 (WPARAM
)lpnmh
->hdr
.idFrom
, (LPARAM
)lpnmh
);
721 static LRESULT
notify_hdr(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
725 TRACE("(code=%d)\n", code
);
727 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
728 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
730 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, pnmh
->idFrom
, (LPARAM
)pnmh
);
732 TRACE(" <= %ld\n", result
);
737 static inline BOOL
notify(const LISTVIEW_INFO
*infoPtr
, INT code
)
740 HWND hwnd
= infoPtr
->hwndSelf
;
741 notify_hdr(infoPtr
, code
, &nmh
);
742 return IsWindow(hwnd
);
745 static inline void notify_itemactivate(const LISTVIEW_INFO
*infoPtr
, const LVHITTESTINFO
*htInfo
)
756 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
757 item
.iItem
= htInfo
->iItem
;
759 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
760 nmia
.lParam
= item
.lParam
;
761 nmia
.uOldState
= item
.state
;
762 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
763 nmia
.uChanged
= LVIF_STATE
;
766 nmia
.iItem
= htInfo
->iItem
;
767 nmia
.iSubItem
= htInfo
->iSubItem
;
768 nmia
.ptAction
= htInfo
->pt
;
770 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
771 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
772 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
774 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
777 static inline LRESULT
notify_listview(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
779 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
780 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
783 static BOOL
notify_click(const LISTVIEW_INFO
*infoPtr
, INT code
, const LVHITTESTINFO
*lvht
)
787 HWND hwnd
= infoPtr
->hwndSelf
;
789 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
790 ZeroMemory(&nmlv
, sizeof(nmlv
));
791 nmlv
.iItem
= lvht
->iItem
;
792 nmlv
.iSubItem
= lvht
->iSubItem
;
793 nmlv
.ptAction
= lvht
->pt
;
794 item
.mask
= LVIF_PARAM
;
795 item
.iItem
= lvht
->iItem
;
797 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
798 notify_listview(infoPtr
, code
, &nmlv
);
799 return IsWindow(hwnd
);
802 static BOOL
notify_deleteitem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
806 HWND hwnd
= infoPtr
->hwndSelf
;
808 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
810 item
.mask
= LVIF_PARAM
;
813 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
814 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
815 return IsWindow(hwnd
);
818 static int get_ansi_notification(UINT unicodeNotificationCode
)
820 switch (unicodeNotificationCode
)
822 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
823 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
824 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
825 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
826 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
827 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
829 ERR("unknown notification %x\n", unicodeNotificationCode
);
835 Send notification. depends on dispinfoW having same
836 structure as dispinfoA.
837 infoPtr : listview struct
838 notificationCode : *Unicode* notification code
839 pdi : dispinfo structure (can be unicode or ansi)
840 isW : TRUE if dispinfo is Unicode
842 static BOOL
notify_dispinfoT(const LISTVIEW_INFO
*infoPtr
, UINT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
844 BOOL bResult
= FALSE
;
845 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
846 INT cchTempBufMax
= 0, savCchTextMax
= 0;
848 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
850 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
852 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
853 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
856 if (convertToAnsi
|| convertToUnicode
)
858 if (notificationCode
!= LVN_GETDISPINFOW
)
860 cchTempBufMax
= convertToUnicode
?
861 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
862 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
866 cchTempBufMax
= pdi
->item
.cchTextMax
;
867 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
870 pszTempBuf
= Alloc( (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
871 if (!pszTempBuf
) return FALSE
;
873 if (convertToUnicode
)
874 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
875 pszTempBuf
, cchTempBufMax
);
877 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
878 cchTempBufMax
, NULL
, NULL
);
880 savCchTextMax
= pdi
->item
.cchTextMax
;
881 savPszText
= pdi
->item
.pszText
;
882 pdi
->item
.pszText
= pszTempBuf
;
883 pdi
->item
.cchTextMax
= cchTempBufMax
;
886 if (infoPtr
->notifyFormat
== NFR_ANSI
)
887 realNotifCode
= get_ansi_notification(notificationCode
);
889 realNotifCode
= notificationCode
;
890 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
891 bResult
= notify_hdr(infoPtr
, realNotifCode
, &pdi
->hdr
);
893 if (convertToUnicode
|| convertToAnsi
)
895 if (convertToUnicode
) /* note : pointer can be changed by app ! */
896 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
897 savCchTextMax
, NULL
, NULL
);
899 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
900 savPszText
, savCchTextMax
);
901 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
902 pdi
->item
.cchTextMax
= savCchTextMax
;
908 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
,
909 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
911 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
912 lpnmlvcd
->nmcd
.hdc
= hdc
;
913 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
914 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
915 lpnmlvcd
->clrText
= infoPtr
->clrText
;
916 if (!lplvItem
) return;
917 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
918 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
919 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
920 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
921 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
922 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
925 static inline DWORD
notify_customdraw (const LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
927 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
930 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
931 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
932 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
933 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
934 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
935 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
939 static void prepaint_setup (const LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
, BOOL SubItem
)
941 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
942 lpnmlvcd
->clrTextBk
= comctl32_color
.clrWindow
;
943 if (lpnmlvcd
->clrText
== CLR_DEFAULT
)
944 lpnmlvcd
->clrText
= comctl32_color
.clrWindowText
;
946 /* apparently, for selected items, we have to override the returned values */
949 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
953 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
954 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
956 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
958 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
959 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
964 /* Set the text attributes */
965 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
967 SetBkMode(hdc
, OPAQUE
);
968 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
971 SetBkMode(hdc
, TRANSPARENT
);
972 SetTextColor(hdc
, lpnmlvcd
->clrText
);
975 static inline DWORD
notify_postpaint (const LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
977 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
980 /******** Item iterator functions **********************************/
982 static RANGES
ranges_create(int count
);
983 static void ranges_destroy(RANGES ranges
);
984 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
985 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
986 static void ranges_dump(RANGES ranges
);
988 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
990 RANGE range
= { nItem
, nItem
+ 1 };
992 return ranges_add(ranges
, range
);
995 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
997 RANGE range
= { nItem
, nItem
+ 1 };
999 return ranges_del(ranges
, range
);
1003 * ITERATOR DOCUMENTATION
1005 * The iterator functions allow for easy, and convenient iteration
1006 * over items of interest in the list. Typically, you create a
1007 * iterator, use it, and destroy it, as such:
1010 * iterator_xxxitems(&i, ...);
1011 * while (iterator_{prev,next}(&i)
1013 * //code which uses i.nItem
1015 * iterator_destroy(&i);
1017 * where xxx is either: framed, or visible.
1018 * Note that it is important that the code destroys the iterator
1019 * after it's done with it, as the creation of the iterator may
1020 * allocate memory, which thus needs to be freed.
1022 * You can iterate both forwards, and backwards through the list,
1023 * by using iterator_next or iterator_prev respectively.
1025 * Lower numbered items are draw on top of higher number items in
1026 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1027 * items may overlap). So, to test items, you should use
1029 * which lists the items top to bottom (in Z-order).
1030 * For drawing items, you should use
1032 * which lists the items bottom to top (in Z-order).
1033 * If you keep iterating over the items after the end-of-items
1034 * marker (-1) is returned, the iterator will start from the
1035 * beginning. Typically, you don't need to test for -1,
1036 * because iterator_{next,prev} will return TRUE if more items
1037 * are to be iterated over, or FALSE otherwise.
1039 * Note: the iterator is defined to be bidirectional. That is,
1040 * any number of prev followed by any number of next, or
1041 * five versa, should leave the iterator at the same item:
1042 * prev * n, next * n = next * n, prev * n
1044 * The iterator has a notion of an out-of-order, special item,
1045 * which sits at the start of the list. This is used in
1046 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1047 * which needs to be first, as it may overlap other items.
1049 * The code is a bit messy because we have:
1050 * - a special item to deal with
1051 * - simple range, or composite range
1053 * If you find bugs, or want to add features, please make sure you
1054 * always check/modify *both* iterator_prev, and iterator_next.
1058 * This function iterates through the items in increasing order,
1059 * but prefixed by the special item, then -1. That is:
1060 * special, 1, 2, 3, ..., n, -1.
1061 * Each item is listed only once.
1063 static inline BOOL
iterator_next(ITERATOR
* i
)
1067 i
->nItem
= i
->nSpecial
;
1068 if (i
->nItem
!= -1) return TRUE
;
1070 if (i
->nItem
== i
->nSpecial
)
1072 if (i
->ranges
) i
->index
= 0;
1078 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1079 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1084 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1085 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1088 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1090 i
->nItem
= i
->range
.lower
;
1091 if (i
->nItem
>= 0) goto testitem
;
1098 * This function iterates through the items in decreasing order,
1099 * followed by the special item, then -1. That is:
1100 * n, n-1, ..., 3, 2, 1, special, -1.
1101 * Each item is listed only once.
1103 static inline BOOL
iterator_prev(ITERATOR
* i
)
1110 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1113 if (i
->nItem
== i
->nSpecial
)
1121 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1122 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1128 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1131 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1133 i
->nItem
= i
->range
.upper
;
1134 if (i
->nItem
> 0) goto testitem
;
1136 return (i
->nItem
= i
->nSpecial
) != -1;
1139 static RANGE
iterator_range(const ITERATOR
*i
)
1143 if (!i
->ranges
) return i
->range
;
1145 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1147 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1148 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1150 else range
.lower
= range
.upper
= 0;
1156 * Releases resources associated with this ierator.
1158 static inline void iterator_destroy(const ITERATOR
*i
)
1160 ranges_destroy(i
->ranges
);
1164 * Create an empty iterator.
1166 static inline BOOL
iterator_empty(ITERATOR
* i
)
1168 ZeroMemory(i
, sizeof(*i
));
1169 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1174 * Create an iterator over a range.
1176 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1184 * Create an iterator over a bunch of ranges.
1185 * Please note that the iterator will take ownership of the ranges,
1186 * and will free them upon destruction.
1188 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1196 * Creates an iterator over the items which intersect lprc.
1198 static BOOL
iterator_frameditems(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1200 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1201 RECT frame
= *lprc
, rcItem
, rcTemp
;
1204 /* in case we fail, we want to return an empty iterator */
1205 if (!iterator_empty(i
)) return FALSE
;
1207 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1209 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1210 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1212 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
1216 if (uView
== LVS_ICON
&& infoPtr
->nFocusedItem
!= -1)
1218 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1219 if (IntersectRect(&rcTemp
, &rcItem
, lprc
))
1220 i
->nSpecial
= infoPtr
->nFocusedItem
;
1222 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1223 /* to do better here, we need to have PosX, and PosY sorted */
1224 TRACE("building icon ranges:\n");
1225 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1227 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1228 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1229 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1230 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1231 if (IntersectRect(&rcTemp
, &rcItem
, &frame
))
1232 ranges_additem(i
->ranges
, nItem
);
1236 else if (uView
== LVS_REPORT
)
1240 if (frame
.left
>= infoPtr
->nItemWidth
) return TRUE
;
1241 if (frame
.top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1243 range
.lower
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1244 range
.upper
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1245 if (range
.upper
<= range
.lower
) return TRUE
;
1246 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1247 TRACE(" report=%s\n", debugrange(&i
->range
));
1251 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1252 INT nFirstRow
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1253 INT nLastRow
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1254 INT nFirstCol
= max(frame
.left
/ infoPtr
->nItemWidth
, 0);
1255 INT nLastCol
= min((frame
.right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1256 INT lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1260 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1261 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1263 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1265 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1266 TRACE("building list ranges:\n");
1267 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1269 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1270 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1271 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1272 TRACE(" list=%s\n", debugrange(&item_range
));
1273 ranges_add(i
->ranges
, item_range
);
1281 * Creates an iterator over the items which intersect the visible region of hdc.
1283 static BOOL
iterator_visibleitems(ITERATOR
*i
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1285 POINT Origin
, Position
;
1286 RECT rcItem
, rcClip
;
1289 rgntype
= GetClipBox(hdc
, &rcClip
);
1290 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1291 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1292 if (rgntype
== SIMPLEREGION
) return TRUE
;
1294 /* first deal with the special item */
1295 if (i
->nSpecial
!= -1)
1297 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1298 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1301 /* if we can't deal with the region, we'll just go with the simple range */
1302 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1303 TRACE("building visible range:\n");
1304 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1306 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1307 if (!ranges_add(i
->ranges
, i
->range
))
1309 ranges_destroy(i
->ranges
);
1315 /* now delete the invisible items from the list */
1316 while(iterator_next(i
))
1318 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1319 rcItem
.left
= Position
.x
+ Origin
.x
;
1320 rcItem
.top
= Position
.y
+ Origin
.y
;
1321 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1322 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1323 if (!RectVisible(hdc
, &rcItem
))
1324 ranges_delitem(i
->ranges
, i
->nItem
);
1326 /* the iterator should restart on the next iterator_next */
1332 /******** Misc helper functions ************************************/
1334 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1335 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1337 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1338 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1341 static inline BOOL
is_autoarrange(const LISTVIEW_INFO
*infoPtr
)
1343 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1345 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1346 (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
);
1349 static void toggle_checkbox_state(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1351 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
1352 if(state
== 1 || state
== 2)
1356 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
1357 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
1358 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
1362 /******** Internal API functions ************************************/
1364 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1366 static COLUMN_INFO mainItem
;
1368 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1369 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1370 return DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1373 static INT
LISTVIEW_CreateHeader(LISTVIEW_INFO
*infoPtr
)
1375 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
1378 if (infoPtr
->hwndHeader
) return 0;
1380 /* setup creation flags */
1381 dFlags
|= (LVS_NOSORTHEADER
& infoPtr
->dwStyle
) ? 0 : HDS_BUTTONS
;
1382 dFlags
|= (LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) ? HDS_HIDDEN
: 0;
1384 hInst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
1387 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
1388 0, 0, 0, 0, infoPtr
->hwndSelf
, NULL
, hInst
, NULL
);
1389 if (!infoPtr
->hwndHeader
) return -1;
1391 /* set header unicode format */
1392 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
1394 /* set header font */
1395 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
1397 LISTVIEW_UpdateSize(infoPtr
);
1402 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1404 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1407 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1409 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1412 /* Listview invalidation functions: use _only_ these functions to invalidate */
1414 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1416 return infoPtr
->bRedraw
;
1419 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1421 if(!is_redrawing(infoPtr
)) return;
1422 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1423 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1426 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1430 if(!is_redrawing(infoPtr
)) return;
1431 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1432 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1435 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1437 POINT Origin
, Position
;
1440 if(!is_redrawing(infoPtr
)) return;
1441 assert ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
);
1442 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1443 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1444 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1446 rcBox
.bottom
= infoPtr
->nItemHeight
;
1447 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1448 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1451 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1453 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1456 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1460 if(!is_redrawing(infoPtr
)) return;
1461 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1462 rcCol
.top
= infoPtr
->rcList
.top
;
1463 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1464 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1469 * Retrieves the number of items that can fit vertically in the client area.
1472 * [I] infoPtr : valid pointer to the listview structure
1475 * Number of items per row.
1477 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1479 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1481 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
1486 * Retrieves the number of items that can fit horizontally in the client
1490 * [I] infoPtr : valid pointer to the listview structure
1493 * Number of items per column.
1495 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1497 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1499 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1503 /*************************************************************************
1504 * LISTVIEW_ProcessLetterKeys
1506 * Processes keyboard messages generated by pressing the letter keys
1508 * What this does is perform a case insensitive search from the
1509 * current position with the following quirks:
1510 * - If two chars or more are pressed in quick succession we search
1511 * for the corresponding string (e.g. 'abc').
1512 * - If there is a delay we wipe away the current search string and
1513 * restart with just that char.
1514 * - If the user keeps pressing the same character, whether slowly or
1515 * fast, so that the search string is entirely composed of this
1516 * character ('aaaaa' for instance), then we search for first item
1517 * that starting with that character.
1518 * - If the user types the above character in quick succession, then
1519 * we must also search for the corresponding string ('aaaaa'), and
1520 * go to that string if there is a match.
1523 * [I] hwnd : handle to the window
1524 * [I] charCode : the character code, the actual character
1525 * [I] keyData : key data
1533 * - The current implementation has a list of characters it will
1534 * accept and it ignores everything else. In particular it will
1535 * ignore accentuated characters which seems to match what
1536 * Windows does. But I'm not sure it makes sense to follow
1538 * - We don't sound a beep when the search fails.
1542 * TREEVIEW_ProcessLetterKeys
1544 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1549 WCHAR buffer
[MAX_PATH
];
1550 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1552 /* simple parameter checking */
1553 if (!charCode
|| !keyData
) return 0;
1555 /* only allow the valid WM_CHARs through */
1556 if (!isalnumW(charCode
) &&
1557 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1558 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1559 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1560 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1561 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1562 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1563 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1564 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1565 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1568 /* if there's one item or less, there is no where to go */
1569 if (infoPtr
->nItemCount
<= 1) return 0;
1571 /* update the search parameters */
1572 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1573 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1574 if (infoPtr
->nSearchParamLength
< MAX_PATH
-1)
1575 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1576 if (infoPtr
->charCode
!= charCode
)
1577 infoPtr
->charCode
= charCode
= 0;
1579 infoPtr
->charCode
=charCode
;
1580 infoPtr
->szSearchParam
[0]=charCode
;
1581 infoPtr
->nSearchParamLength
=1;
1582 /* Redundant with the 1 char string */
1586 /* and search from the current position */
1588 if (infoPtr
->nFocusedItem
>= 0) {
1589 endidx
=infoPtr
->nFocusedItem
;
1591 /* if looking for single character match,
1592 * then we must always move forward
1594 if (infoPtr
->nSearchParamLength
== 1)
1597 endidx
=infoPtr
->nItemCount
;
1601 /* Let application handle this for virtual listview */
1602 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
1607 ZeroMemory(&lvfi
, sizeof(lvfi
));
1608 lvfi
.flags
= (LVFI_WRAP
| LVFI_PARTIAL
);
1609 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
] = '\0';
1610 lvfi
.psz
= infoPtr
->szSearchParam
;
1614 nItem
= notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
1617 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1623 if (idx
== infoPtr
->nItemCount
) {
1624 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1630 item
.mask
= LVIF_TEXT
;
1633 item
.pszText
= buffer
;
1634 item
.cchTextMax
= MAX_PATH
;
1635 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1637 /* check for a match */
1638 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1641 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1642 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1643 /* This would work but we must keep looking for a longer match */
1647 } while (idx
!= endidx
);
1650 LISTVIEW_KeySelection(infoPtr
, nItem
, FALSE
);
1655 /*************************************************************************
1656 * LISTVIEW_UpdateHeaderSize [Internal]
1658 * Function to resize the header control
1661 * [I] hwnd : handle to a window
1662 * [I] nNewScrollPos : scroll pos to set
1667 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1672 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1674 if (!infoPtr
->hwndHeader
) return;
1676 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1677 point
[0].x
= winRect
.left
;
1678 point
[0].y
= winRect
.top
;
1679 point
[1].x
= winRect
.right
;
1680 point
[1].y
= winRect
.bottom
;
1682 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1683 point
[0].x
= -nNewScrollPos
;
1684 point
[1].x
+= nNewScrollPos
;
1686 SetWindowPos(infoPtr
->hwndHeader
,0,
1687 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1688 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
1689 SWP_NOZORDER
| SWP_NOACTIVATE
);
1694 * Update the scrollbars. This functions should be called whenever
1695 * the content, size or view changes.
1698 * [I] infoPtr : valid pointer to the listview structure
1703 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*infoPtr
)
1705 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1706 SCROLLINFO horzInfo
, vertInfo
;
1709 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1711 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1712 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1713 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1715 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1716 if (uView
== LVS_LIST
)
1718 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1719 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1721 /* scroll by at least one column per page */
1722 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1723 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1725 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1727 else if (uView
== LVS_REPORT
)
1729 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1731 else /* LVS_ICON, or LVS_SMALLICON */
1735 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1738 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1739 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1740 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
1741 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1742 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1744 /* Setting the horizontal scroll can change the listview size
1745 * (and potentially everything else) so we need to recompute
1746 * everything again for the vertical scroll
1749 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1750 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1751 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1753 if (uView
== LVS_REPORT
)
1755 vertInfo
.nMax
= infoPtr
->nItemCount
;
1757 /* scroll by at least one page */
1758 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1759 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1761 if (infoPtr
->nItemHeight
> 0)
1762 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1764 else if (uView
!= LVS_LIST
) /* LVS_ICON, or LVS_SMALLICON */
1768 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1771 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1772 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1773 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
1774 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1775 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1777 /* Change of the range may have changed the scroll pos. If so move the content */
1778 if (dx
!= 0 || dy
!= 0)
1781 listRect
= infoPtr
->rcList
;
1782 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
1783 SW_ERASE
| SW_INVALIDATE
);
1786 /* Update the Header Control */
1787 if (uView
== LVS_REPORT
)
1789 horzInfo
.fMask
= SIF_POS
;
1790 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1791 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1798 * Shows/hides the focus rectangle.
1801 * [I] infoPtr : valid pointer to the listview structure
1802 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1807 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1809 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1812 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1814 if (infoPtr
->nFocusedItem
< 0) return;
1816 /* we need some gymnastics in ICON mode to handle large items */
1817 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
1821 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1822 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1824 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1829 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1831 /* for some reason, owner draw should work only in report mode */
1832 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
1837 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
1838 HFONT hOldFont
= SelectObject(hdc
, hFont
);
1840 item
.iItem
= infoPtr
->nFocusedItem
;
1842 item
.mask
= LVIF_PARAM
;
1843 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1845 ZeroMemory(&dis
, sizeof(dis
));
1846 dis
.CtlType
= ODT_LISTVIEW
;
1847 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1848 dis
.itemID
= item
.iItem
;
1849 dis
.itemAction
= ODA_FOCUS
;
1850 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1851 dis
.hwndItem
= infoPtr
->hwndSelf
;
1853 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1854 dis
.itemData
= item
.lParam
;
1856 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1858 SelectObject(hdc
, hOldFont
);
1862 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1865 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1869 * Invalidates all visible selected items.
1871 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
1875 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1876 while(iterator_next(&i
))
1878 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1879 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1881 iterator_destroy(&i
);
1886 * DESCRIPTION: [INTERNAL]
1887 * Computes an item's (left,top) corner, relative to rcView.
1888 * That is, the position has NOT been made relative to the Origin.
1889 * This is deliberate, to avoid computing the Origin over, and
1890 * over again, when this function is called in a loop. Instead,
1891 * one can factor the computation of the Origin before the loop,
1892 * and offset the value returned by this function, on every iteration.
1895 * [I] infoPtr : valid pointer to the listview structure
1896 * [I] nItem : item number
1897 * [O] lpptOrig : item top, left corner
1902 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1904 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1906 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1908 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1910 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1911 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1913 else if (uView
== LVS_LIST
)
1915 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1916 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1917 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1919 else /* LVS_REPORT */
1921 lpptPosition
->x
= 0;
1922 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1927 * DESCRIPTION: [INTERNAL]
1928 * Compute the rectangles of an item. This is to localize all
1929 * the computations in one place. If you are not interested in some
1930 * of these values, simply pass in a NULL -- the function is smart
1931 * enough to compute only what's necessary. The function computes
1932 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1933 * one, the BOX rectangle. This rectangle is very cheap to compute,
1934 * and is guaranteed to contain all the other rectangles. Computing
1935 * the ICON rect is also cheap, but all the others are potentially
1936 * expensive. This gives an easy and effective optimization when
1937 * searching (like point inclusion, or rectangle intersection):
1938 * first test against the BOX, and if TRUE, test against the desired
1940 * If the function does not have all the necessary information
1941 * to computed the requested rectangles, will crash with a
1942 * failed assertion. This is done so we catch all programming
1943 * errors, given that the function is called only from our code.
1945 * We have the following 'special' meanings for a few fields:
1946 * * If LVIS_FOCUSED is set, we assume the item has the focus
1947 * This is important in ICON mode, where it might get a larger
1948 * then usual rectangle
1950 * Please note that subitem support works only in REPORT mode.
1953 * [I] infoPtr : valid pointer to the listview structure
1954 * [I] lpLVItem : item to compute the measures for
1955 * [O] lprcBox : ptr to Box rectangle
1956 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1957 * [0] lprcSelectBox : ptr to select box rectangle
1958 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1959 * [O] lprcIcon : ptr to Icon rectangle
1960 * Same as LVM_GETITEMRECT with LVIR_ICON
1961 * [O] lprcStateIcon: ptr to State Icon rectangle
1962 * [O] lprcLabel : ptr to Label rectangle
1963 * Same as LVM_GETITEMRECT with LVIR_LABEL
1968 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1969 LPRECT lprcBox
, LPRECT lprcSelectBox
,
1970 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
1972 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1973 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1974 RECT Box
, SelectBox
, Icon
, Label
;
1975 COLUMN_INFO
*lpColumnInfo
= NULL
;
1976 SIZE labelSize
= { 0, 0 };
1978 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
1980 /* Be smart and try to figure out the minimum we have to do */
1981 if (lpLVItem
->iSubItem
) assert(uView
== LVS_REPORT
);
1982 if (uView
== LVS_ICON
&& (lprcBox
|| lprcLabel
))
1984 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
1985 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
1987 if (lprcSelectBox
) doSelectBox
= TRUE
;
1988 if (lprcLabel
) doLabel
= TRUE
;
1989 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
1996 /************************************************************/
1997 /* compute the box rectangle (it should be cheap to do) */
1998 /************************************************************/
1999 if (lpLVItem
->iSubItem
|| uView
== LVS_REPORT
)
2000 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
2002 if (lpLVItem
->iSubItem
)
2004 Box
= lpColumnInfo
->rcHeader
;
2009 Box
.right
= infoPtr
->nItemWidth
;
2012 Box
.bottom
= infoPtr
->nItemHeight
;
2014 /******************************************************************/
2015 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2016 /******************************************************************/
2019 LONG state_width
= 0;
2021 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
2022 state_width
= infoPtr
->iconStateSize
.cx
;
2024 if (uView
== LVS_ICON
)
2026 Icon
.left
= Box
.left
+ state_width
;
2027 if (infoPtr
->himlNormal
)
2028 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
2029 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2030 Icon
.right
= Icon
.left
;
2031 Icon
.bottom
= Icon
.top
;
2032 if (infoPtr
->himlNormal
)
2034 Icon
.right
+= infoPtr
->iconSize
.cx
;
2035 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2038 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2040 Icon
.left
= Box
.left
+ state_width
;
2042 if (uView
== LVS_REPORT
)
2043 Icon
.left
+= REPORT_MARGINX
;
2046 Icon
.right
= Icon
.left
;
2047 if (infoPtr
->himlSmall
&&
2048 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
2049 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2050 Icon
.right
+= infoPtr
->iconSize
.cx
;
2051 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2053 if(lprcIcon
) *lprcIcon
= Icon
;
2054 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2056 /* TODO: is this correct? */
2059 lprcStateIcon
->left
= Icon
.left
- state_width
;
2060 lprcStateIcon
->right
= Icon
.left
;
2061 lprcStateIcon
->top
= Icon
.top
;
2062 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2063 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2066 else Icon
.right
= 0;
2068 /************************************************************/
2069 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2070 /************************************************************/
2073 /* calculate how far to the right can the label stretch */
2074 Label
.right
= Box
.right
;
2075 if (uView
== LVS_REPORT
)
2077 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
2080 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && uView
== LVS_REPORT
))
2082 labelSize
.cx
= infoPtr
->nItemWidth
;
2083 labelSize
.cy
= infoPtr
->nItemHeight
;
2087 /* we need the text in non owner draw mode */
2088 assert(lpLVItem
->mask
& LVIF_TEXT
);
2089 if (is_textT(lpLVItem
->pszText
, TRUE
))
2091 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2092 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2093 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2097 /* compute rough rectangle where the label will go */
2098 SetRectEmpty(&rcText
);
2099 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2100 rcText
.bottom
= infoPtr
->nItemHeight
;
2101 if (uView
== LVS_ICON
)
2102 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2104 /* now figure out the flags */
2105 if (uView
== LVS_ICON
)
2106 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2108 uFormat
= LV_SL_DT_FLAGS
;
2110 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2112 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2113 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2115 SelectObject(hdc
, hOldFont
);
2116 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2120 if (uView
== LVS_ICON
)
2122 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2123 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2124 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2125 Label
.right
= Label
.left
+ labelSize
.cx
;
2126 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2127 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2129 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2130 labelSize
.cy
/= infoPtr
->ntmHeight
;
2131 labelSize
.cy
= max(labelSize
.cy
, 1);
2132 labelSize
.cy
*= infoPtr
->ntmHeight
;
2134 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2136 else if (uView
== LVS_REPORT
)
2138 Label
.left
= Icon
.right
;
2139 Label
.top
= Box
.top
;
2140 Label
.right
= lpColumnInfo
->rcHeader
.right
;
2141 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2143 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2145 Label
.left
= Icon
.right
;
2146 Label
.top
= Box
.top
;
2147 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2148 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2151 if (lprcLabel
) *lprcLabel
= Label
;
2152 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2155 /************************************************************/
2156 /* compute STATEICON bounding box */
2157 /************************************************************/
2160 if (uView
== LVS_REPORT
)
2162 SelectBox
.left
= Icon
.right
; /* FIXME: should be Icon.left */
2163 SelectBox
.top
= Box
.top
;
2164 SelectBox
.bottom
= Box
.bottom
;
2165 if (lpLVItem
->iSubItem
== 0)
2167 /* we need the indent in report mode */
2168 assert(lpLVItem
->mask
& LVIF_INDENT
);
2169 SelectBox
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
;
2171 SelectBox
.right
= min(SelectBox
.left
+ labelSize
.cx
, Label
.right
);
2175 UnionRect(&SelectBox
, &Icon
, &Label
);
2177 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2178 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2181 /* Fix the Box if necessary */
2184 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2185 else *lprcBox
= Box
;
2187 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2191 * DESCRIPTION: [INTERNAL]
2194 * [I] infoPtr : valid pointer to the listview structure
2195 * [I] nItem : item number
2196 * [O] lprcBox : ptr to Box rectangle
2201 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2203 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2204 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2205 POINT Position
, Origin
;
2208 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2209 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2211 /* Be smart and try to figure out the minimum we have to do */
2213 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2214 lvItem
.mask
|= LVIF_TEXT
;
2215 lvItem
.iItem
= nItem
;
2216 lvItem
.iSubItem
= 0;
2217 lvItem
.pszText
= szDispText
;
2218 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2219 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2220 if (uView
== LVS_ICON
)
2222 lvItem
.mask
|= LVIF_STATE
;
2223 lvItem
.stateMask
= LVIS_FOCUSED
;
2224 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2226 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2228 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2234 * Returns the current icon position, and advances it along the top.
2235 * The returned position is not offset by Origin.
2238 * [I] infoPtr : valid pointer to the listview structure
2239 * [O] lpPos : will get the current icon position
2244 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2246 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2248 *lpPos
= infoPtr
->currIconPos
;
2250 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2251 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2253 infoPtr
->currIconPos
.x
= 0;
2254 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2260 * Returns the current icon position, and advances it down the left edge.
2261 * The returned position is not offset by Origin.
2264 * [I] infoPtr : valid pointer to the listview structure
2265 * [O] lpPos : will get the current icon position
2270 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2272 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2274 *lpPos
= infoPtr
->currIconPos
;
2276 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2277 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2279 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2280 infoPtr
->currIconPos
.y
= 0;
2286 * Moves an icon to the specified position.
2287 * It takes care of invalidating the item, etc.
2290 * [I] infoPtr : valid pointer to the listview structure
2291 * [I] nItem : the item to move
2292 * [I] lpPos : the new icon position
2293 * [I] isNew : flags the item as being new
2299 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2305 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2306 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2308 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2309 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2312 /* Allocating a POINTER for every item is too resource intensive,
2313 * so we'll keep the (x,y) in different arrays */
2314 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2315 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2317 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2324 * Arranges listview items in icon display mode.
2327 * [I] infoPtr : valid pointer to the listview structure
2328 * [I] nAlignCode : alignment code
2334 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2336 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2337 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2341 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
) return FALSE
;
2343 TRACE("nAlignCode=%d\n", nAlignCode
);
2345 if (nAlignCode
== LVA_DEFAULT
)
2347 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2348 else nAlignCode
= LVA_ALIGNTOP
;
2353 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2354 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2355 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2356 default: return FALSE
;
2359 infoPtr
->bAutoarrange
= TRUE
;
2360 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2361 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2363 next_pos(infoPtr
, &pos
);
2364 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2372 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2375 * [I] infoPtr : valid pointer to the listview structure
2376 * [O] lprcView : bounding rectangle
2382 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2386 SetRectEmpty(lprcView
);
2388 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
2392 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2394 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2395 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2396 lprcView
->right
= max(lprcView
->right
, x
);
2397 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2399 if (infoPtr
->nItemCount
> 0)
2401 lprcView
->right
+= infoPtr
->nItemWidth
;
2402 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2407 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2408 x
= infoPtr
->nItemCount
/ y
;
2409 if (infoPtr
->nItemCount
% y
) x
++;
2410 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2411 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2415 lprcView
->right
= infoPtr
->nItemWidth
;
2416 lprcView
->bottom
= infoPtr
->nItemCount
* infoPtr
->nItemHeight
;
2423 * Retrieves the bounding rectangle of all the items.
2426 * [I] infoPtr : valid pointer to the listview structure
2427 * [O] lprcView : bounding rectangle
2433 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2437 TRACE("(lprcView=%p)\n", lprcView
);
2439 if (!lprcView
) return FALSE
;
2441 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2442 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2443 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2445 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2452 * Retrieves the subitem pointer associated with the subitem index.
2455 * [I] hdpaSubItems : DPA handle for a specific item
2456 * [I] nSubItem : index of subitem
2459 * SUCCESS : subitem pointer
2462 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2464 SUBITEM_INFO
*lpSubItem
;
2467 /* we should binary search here if need be */
2468 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2470 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
2471 if (lpSubItem
->iSubItem
== nSubItem
)
2481 * Calculates the desired item width.
2484 * [I] infoPtr : valid pointer to the listview structure
2487 * The desired item width.
2489 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2491 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2494 TRACE("uView=%d\n", uView
);
2496 if (uView
== LVS_ICON
)
2497 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2498 else if (uView
== LVS_REPORT
)
2502 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2504 LISTVIEW_GetHeaderRect(infoPtr
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, &rcHeader
);
2505 nItemWidth
= rcHeader
.right
;
2508 else /* LVS_SMALLICON, or LVS_LIST */
2512 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2513 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2515 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2516 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2518 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2521 return max(nItemWidth
, 1);
2526 * Calculates the desired item height.
2529 * [I] infoPtr : valid pointer to the listview structure
2532 * The desired item height.
2534 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
2536 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2539 TRACE("uView=%d\n", uView
);
2541 if (uView
== LVS_ICON
)
2542 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2545 nItemHeight
= infoPtr
->ntmHeight
;
2546 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
2548 if (infoPtr
->himlState
)
2549 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2550 if (infoPtr
->himlSmall
)
2551 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2552 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2553 nItemHeight
+= HEIGHT_PADDING
;
2554 if (infoPtr
->nMeasureItemHeight
> 0)
2555 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2558 return max(nItemHeight
, 1);
2563 * Updates the width, and height of an item.
2566 * [I] infoPtr : valid pointer to the listview structure
2571 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2573 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2574 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2580 * Retrieves and saves important text metrics info for the current
2584 * [I] infoPtr : valid pointer to the listview structure
2587 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2589 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2590 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2591 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2595 if (GetTextMetricsW(hdc
, &tm
))
2597 infoPtr
->ntmHeight
= tm
.tmHeight
;
2598 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
2601 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
2602 infoPtr
->nEllipsisWidth
= sz
.cx
;
2604 SelectObject(hdc
, hOldFont
);
2605 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2607 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2612 * A compare function for ranges
2615 * [I] range1 : pointer to range 1;
2616 * [I] range2 : pointer to range 2;
2620 * > 0 : if range 1 > range 2
2621 * < 0 : if range 2 > range 1
2622 * = 0 : if range intersects range 2
2624 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2628 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2630 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2635 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1
), debugrange(range2
), cmp
);
2641 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2643 #define ranges_check(ranges, desc) do { } while(0)
2646 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2651 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2653 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2654 ranges_dump(ranges
);
2655 prev
= DPA_GetPtr(ranges
->hdpa
, 0);
2656 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2657 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2658 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2660 curr
= DPA_GetPtr(ranges
->hdpa
, i
);
2661 assert (prev
->upper
<= curr
->lower
);
2662 assert (curr
->lower
< curr
->upper
);
2665 TRACE("--- Done checking---\n");
2668 static RANGES
ranges_create(int count
)
2670 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
2671 if (!ranges
) return NULL
;
2672 ranges
->hdpa
= DPA_Create(count
);
2673 if (ranges
->hdpa
) return ranges
;
2678 static void ranges_clear(RANGES ranges
)
2682 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2683 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2684 DPA_DeleteAllPtrs(ranges
->hdpa
);
2688 static void ranges_destroy(RANGES ranges
)
2690 if (!ranges
) return;
2691 ranges_clear(ranges
);
2692 DPA_Destroy(ranges
->hdpa
);
2696 static RANGES
ranges_clone(RANGES ranges
)
2701 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2703 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2705 RANGE
*newrng
= Alloc(sizeof(RANGE
));
2706 if (!newrng
) goto fail
;
2707 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2708 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2713 TRACE ("clone failed\n");
2714 ranges_destroy(clone
);
2718 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2722 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2723 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2728 static void ranges_dump(RANGES ranges
)
2732 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2733 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2736 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2738 RANGE srchrng
= { nItem
, nItem
+ 1 };
2740 TRACE("(nItem=%d)\n", nItem
);
2741 ranges_check(ranges
, "before contain");
2742 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2745 static INT
ranges_itemcount(RANGES ranges
)
2749 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2751 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2752 count
+= sel
->upper
- sel
->lower
;
2758 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2760 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2763 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2764 if (index
== -1) return TRUE
;
2766 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2768 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2769 if (chkrng
->lower
>= nItem
)
2770 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2771 if (chkrng
->upper
> nItem
)
2772 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2777 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2782 TRACE("(%s)\n", debugrange(&range
));
2783 ranges_check(ranges
, "before add");
2785 /* try find overlapping regions first */
2786 srchrgn
.lower
= range
.lower
- 1;
2787 srchrgn
.upper
= range
.upper
+ 1;
2788 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2794 TRACE("Adding new range\n");
2796 /* create the brand new range to insert */
2797 newrgn
= Alloc(sizeof(RANGE
));
2798 if(!newrgn
) goto fail
;
2801 /* figure out where to insert it */
2802 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2803 TRACE("index=%d\n", index
);
2804 if (index
== -1) index
= 0;
2806 /* and get it over with */
2807 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2815 RANGE
*chkrgn
, *mrgrgn
;
2816 INT fromindex
, mergeindex
;
2818 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2819 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2821 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2822 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2824 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2826 /* merge now common ranges */
2828 srchrgn
.lower
= chkrgn
->lower
- 1;
2829 srchrgn
.upper
= chkrgn
->upper
+ 1;
2833 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2834 if (mergeindex
== -1) break;
2835 if (mergeindex
== index
)
2837 fromindex
= index
+ 1;
2841 TRACE("Merge with index %i\n", mergeindex
);
2843 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2844 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2845 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2847 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2848 if (mergeindex
< index
) index
--;
2852 ranges_check(ranges
, "after add");
2856 ranges_check(ranges
, "failed add");
2860 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2865 TRACE("(%s)\n", debugrange(&range
));
2866 ranges_check(ranges
, "before del");
2868 /* we don't use DPAS_SORTED here, since we need *
2869 * to find the first overlapping range */
2870 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2873 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2875 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2877 /* case 1: Same range */
2878 if ( (chkrgn
->upper
== range
.upper
) &&
2879 (chkrgn
->lower
== range
.lower
) )
2881 DPA_DeletePtr(ranges
->hdpa
, index
);
2884 /* case 2: engulf */
2885 else if ( (chkrgn
->upper
<= range
.upper
) &&
2886 (chkrgn
->lower
>= range
.lower
) )
2888 DPA_DeletePtr(ranges
->hdpa
, index
);
2890 /* case 3: overlap upper */
2891 else if ( (chkrgn
->upper
<= range
.upper
) &&
2892 (chkrgn
->lower
< range
.lower
) )
2894 chkrgn
->upper
= range
.lower
;
2896 /* case 4: overlap lower */
2897 else if ( (chkrgn
->upper
> range
.upper
) &&
2898 (chkrgn
->lower
>= range
.lower
) )
2900 chkrgn
->lower
= range
.upper
;
2903 /* case 5: fully internal */
2906 RANGE tmprgn
= *chkrgn
, *newrgn
;
2908 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
2909 newrgn
->lower
= chkrgn
->lower
;
2910 newrgn
->upper
= range
.lower
;
2911 chkrgn
->lower
= range
.upper
;
2912 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2921 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2924 ranges_check(ranges
, "after del");
2928 ranges_check(ranges
, "failed del");
2934 * Removes all selection ranges
2937 * [I] infoPtr : valid pointer to the listview structure
2938 * [I] toSkip : item range to skip removing the selection
2944 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2953 lvItem
.stateMask
= LVIS_SELECTED
;
2955 /* need to clone the DPA because callbacks can change it */
2956 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2957 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2958 while(iterator_next(&i
))
2959 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2960 /* note that the iterator destructor will free the cloned range */
2961 iterator_destroy(&i
);
2966 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2970 if (!(toSkip
= ranges_create(1))) return FALSE
;
2971 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2972 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
2973 ranges_destroy(toSkip
);
2977 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
2979 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
2984 * Retrieves the number of items that are marked as selected.
2987 * [I] infoPtr : valid pointer to the listview structure
2990 * Number of items selected.
2992 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
2994 INT nSelectedCount
= 0;
2996 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
2999 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
3001 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
3006 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
3008 TRACE("nSelectedCount=%d\n", nSelectedCount
);
3009 return nSelectedCount
;
3014 * Manages the item focus.
3017 * [I] infoPtr : valid pointer to the listview structure
3018 * [I] nItem : item index
3021 * TRUE : focused item changed
3022 * FALSE : focused item has NOT changed
3024 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3026 INT oldFocus
= infoPtr
->nFocusedItem
;
3029 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
3031 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
3032 lvItem
.stateMask
= LVIS_FOCUSED
;
3033 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
3035 return oldFocus
!= infoPtr
->nFocusedItem
;
3038 /* Helper function for LISTVIEW_ShiftIndices *only* */
3039 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
3041 if (nShiftItem
< nItem
) return nShiftItem
;
3043 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
3045 if (direction
> 0) return nShiftItem
+ direction
;
3047 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3052 * Updates the various indices after an item has been inserted or deleted.
3055 * [I] infoPtr : valid pointer to the listview structure
3056 * [I] nItem : item index
3057 * [I] direction : Direction of shift, +1 or -1.
3062 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3067 /* temporarily disable change notification while shifting items */
3068 bOldChange
= infoPtr
->bDoChangeNotify
;
3069 infoPtr
->bDoChangeNotify
= FALSE
;
3071 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
3073 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3075 assert(abs(direction
) == 1);
3077 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3079 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
3080 if (nNewFocus
!= infoPtr
->nFocusedItem
)
3081 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
3083 /* But we are not supposed to modify nHotItem! */
3085 infoPtr
->bDoChangeNotify
= bOldChange
;
3091 * Adds a block of selections.
3094 * [I] infoPtr : valid pointer to the listview structure
3095 * [I] nItem : item index
3098 * Whether the window is still valid.
3100 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3102 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3103 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3104 HWND hwndSelf
= infoPtr
->hwndSelf
;
3105 NMLVODSTATECHANGE nmlv
;
3110 /* Temporarily disable change notification
3111 * If the control is LVS_OWNERDATA, we need to send
3112 * only one LVN_ODSTATECHANGED notification.
3113 * See MSDN documentation for LVN_ITEMCHANGED.
3115 bOldChange
= infoPtr
->bDoChangeNotify
;
3116 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3118 if (nFirst
== -1) nFirst
= nItem
;
3120 item
.state
= LVIS_SELECTED
;
3121 item
.stateMask
= LVIS_SELECTED
;
3123 for (i
= nFirst
; i
<= nLast
; i
++)
3124 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3126 ZeroMemory(&nmlv
, sizeof(nmlv
));
3127 nmlv
.iFrom
= nFirst
;
3130 nmlv
.uOldState
= item
.state
;
3132 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3133 if (!IsWindow(hwndSelf
))
3135 infoPtr
->bDoChangeNotify
= bOldChange
;
3142 * Sets a single group selection.
3145 * [I] infoPtr : valid pointer to the listview structure
3146 * [I] nItem : item index
3151 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3153 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3159 if (!(selection
= ranges_create(100))) return;
3161 item
.state
= LVIS_SELECTED
;
3162 item
.stateMask
= LVIS_SELECTED
;
3164 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
3166 if (infoPtr
->nSelectionMark
== -1)
3168 infoPtr
->nSelectionMark
= nItem
;
3169 ranges_additem(selection
, nItem
);
3175 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3176 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3177 ranges_add(selection
, sel
);
3182 RECT rcItem
, rcSel
, rcSelMark
;
3185 rcItem
.left
= LVIR_BOUNDS
;
3186 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3187 rcSelMark
.left
= LVIR_BOUNDS
;
3188 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3189 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3190 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3191 while(iterator_next(&i
))
3193 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3194 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3196 iterator_destroy(&i
);
3199 /* disable per item notifications on LVS_OWNERDATA style
3200 FIXME: single LVN_ODSTATECHANGED should be used */
3201 bOldChange
= infoPtr
->bDoChangeNotify
;
3202 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3204 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3207 iterator_rangesitems(&i
, selection
);
3208 while(iterator_next(&i
))
3209 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3210 /* this will also destroy the selection */
3211 iterator_destroy(&i
);
3213 infoPtr
->bDoChangeNotify
= bOldChange
;
3215 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3220 * Sets a single selection.
3223 * [I] infoPtr : valid pointer to the listview structure
3224 * [I] nItem : item index
3229 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3233 TRACE("nItem=%d\n", nItem
);
3235 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3237 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3238 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3239 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3241 infoPtr
->nSelectionMark
= nItem
;
3246 * Set selection(s) with keyboard.
3249 * [I] infoPtr : valid pointer to the listview structure
3250 * [I] nItem : item index
3251 * [I] space : VK_SPACE code sent
3254 * SUCCESS : TRUE (needs to be repainted)
3255 * FAILURE : FALSE (nothing has changed)
3257 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL space
)
3259 /* FIXME: pass in the state */
3260 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3261 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3262 BOOL bResult
= FALSE
;
3264 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3265 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3267 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3270 LISTVIEW_SetSelection(infoPtr
, nItem
);
3277 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3282 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3283 lvItem
.stateMask
= LVIS_SELECTED
;
3286 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3287 if (lvItem
.state
& LVIS_SELECTED
)
3288 infoPtr
->nSelectionMark
= nItem
;
3290 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3295 LISTVIEW_SetSelection(infoPtr
, nItem
);
3298 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3301 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3305 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3307 LVHITTESTINFO lvHitTestInfo
;
3309 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3310 lvHitTestInfo
.pt
.x
= pt
.x
;
3311 lvHitTestInfo
.pt
.y
= pt
.y
;
3313 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3315 lpLVItem
->mask
= LVIF_PARAM
;
3316 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3317 lpLVItem
->iSubItem
= 0;
3319 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3322 static inline BOOL
LISTVIEW_isHotTracking(const LISTVIEW_INFO
*infoPtr
)
3324 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3325 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3326 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3331 * Called when the mouse is being actively tracked and has hovered for a specified
3335 * [I] infoPtr : valid pointer to the listview structure
3336 * [I] fwKeys : key indicator
3337 * [I] x,y : mouse position
3340 * 0 if the message was processed, non-zero if there was an error
3343 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3344 * over the item for a certain period of time.
3347 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3349 if (LISTVIEW_isHotTracking(infoPtr
))
3357 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3358 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3366 * Called whenever WM_MOUSEMOVE is received.
3369 * [I] infoPtr : valid pointer to the listview structure
3370 * [I] fwKeys : key indicator
3371 * [I] x,y : mouse position
3374 * 0 if the message is processed, non-zero if there was an error
3376 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3378 TRACKMOUSEEVENT trackinfo
;
3380 if (!(fwKeys
& MK_LBUTTON
))
3381 infoPtr
->bLButtonDown
= FALSE
;
3383 if (infoPtr
->bLButtonDown
)
3387 WORD wDragWidth
= GetSystemMetrics(SM_CXDRAG
);
3388 WORD wDragHeight
= GetSystemMetrics(SM_CYDRAG
);
3390 rect
.left
= infoPtr
->ptClickPos
.x
- wDragWidth
;
3391 rect
.right
= infoPtr
->ptClickPos
.x
+ wDragWidth
;
3392 rect
.top
= infoPtr
->ptClickPos
.y
- wDragHeight
;
3393 rect
.bottom
= infoPtr
->ptClickPos
.y
+ wDragHeight
;
3398 if (!PtInRect(&rect
, tmp
))
3400 LVHITTESTINFO lvHitTestInfo
;
3403 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3404 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3406 ZeroMemory(&nmlv
, sizeof(nmlv
));
3407 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3408 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3410 if (!infoPtr
->bDragging
)
3412 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3413 infoPtr
->bDragging
= TRUE
;
3420 infoPtr
->bLButtonDown
= FALSE
;
3422 /* see if we are supposed to be tracking mouse hovering */
3423 if (LISTVIEW_isHotTracking(infoPtr
)) {
3424 /* fill in the trackinfo struct */
3425 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3426 trackinfo
.dwFlags
= TME_QUERY
;
3427 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3428 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3430 /* see if we are already tracking this hwnd */
3431 _TrackMouseEvent(&trackinfo
);
3433 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3434 trackinfo
.dwFlags
= TME_HOVER
;
3436 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3437 _TrackMouseEvent(&trackinfo
);
3446 * Tests whether the item is assignable to a list with style lStyle
3448 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3450 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3451 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3452 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3460 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3463 * [I] infoPtr : valid pointer to the listview structure
3464 * [I] lpLVItem : valid pointer to new item attributes
3465 * [I] isNew : the item being set is being inserted
3466 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3467 * [O] bChanged : will be set to TRUE if the item really changed
3473 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3475 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3480 /* stateMask is ignored for LVM_INSERTITEM */
3481 UINT stateMask
= isNew
? ~0 : lpLVItem
->stateMask
;
3485 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3487 if (lpLVItem
->mask
== 0) return TRUE
;
3489 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3491 /* a virtual listview only stores selection and focus */
3492 if (lpLVItem
->mask
& ~LVIF_STATE
)
3498 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3499 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
3503 /* we need to get the lParam and state of the item */
3504 item
.iItem
= lpLVItem
->iItem
;
3505 item
.iSubItem
= lpLVItem
->iSubItem
;
3506 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3507 item
.stateMask
= ~0;
3510 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3512 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3513 /* determine what fields will change */
3514 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & stateMask
& ~infoPtr
->uCallbackMask
))
3515 uChanged
|= LVIF_STATE
;
3517 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3518 uChanged
|= LVIF_IMAGE
;
3520 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3521 uChanged
|= LVIF_PARAM
;
3523 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3524 uChanged
|= LVIF_INDENT
;
3526 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3527 uChanged
|= LVIF_TEXT
;
3529 TRACE("uChanged=0x%x\n", uChanged
);
3530 if (!uChanged
) return TRUE
;
3533 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3534 nmlv
.iItem
= lpLVItem
->iItem
;
3535 nmlv
.uNewState
= (item
.state
& ~stateMask
) | (lpLVItem
->state
& stateMask
);
3536 nmlv
.uOldState
= item
.state
;
3537 nmlv
.uChanged
= uChanged
;
3538 nmlv
.lParam
= item
.lParam
;
3540 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3541 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3543 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
3545 HWND hwndSelf
= infoPtr
->hwndSelf
;
3547 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3549 if (!IsWindow(hwndSelf
))
3553 /* copy information */
3554 if (lpLVItem
->mask
& LVIF_TEXT
)
3555 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3557 if (lpLVItem
->mask
& LVIF_IMAGE
)
3558 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3560 if (lpLVItem
->mask
& LVIF_PARAM
)
3561 lpItem
->lParam
= lpLVItem
->lParam
;
3563 if (lpLVItem
->mask
& LVIF_INDENT
)
3564 lpItem
->iIndent
= lpLVItem
->iIndent
;
3566 if (uChanged
& LVIF_STATE
)
3568 if (lpItem
&& (stateMask
& ~infoPtr
->uCallbackMask
))
3570 lpItem
->state
&= ~stateMask
;
3571 lpItem
->state
|= (lpLVItem
->state
& stateMask
);
3573 if (lpLVItem
->state
& stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3575 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3576 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3578 else if (stateMask
& LVIS_SELECTED
)
3580 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3582 /* if we are asked to change focus, and we manage it, do it */
3583 if (stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3585 if (lpLVItem
->state
& LVIS_FOCUSED
)
3587 if (infoPtr
->nFocusedItem
!= -1)
3589 /* remove current focus */
3590 item
.mask
= LVIF_STATE
;
3592 item
.stateMask
= LVIS_FOCUSED
;
3594 /* recurse with redrawing an item */
3595 LISTVIEW_SetItemState(infoPtr
, infoPtr
->nFocusedItem
, &item
);
3598 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3599 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3601 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3603 infoPtr
->nFocusedItem
= -1;
3608 /* if we're inserting the item, we're done */
3609 if (isNew
) return TRUE
;
3611 /* send LVN_ITEMCHANGED notification */
3612 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3613 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3620 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3623 * [I] infoPtr : valid pointer to the listview structure
3624 * [I] lpLVItem : valid pointer to new subitem attributes
3625 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3626 * [O] bChanged : will be set to TRUE if the item really changed
3632 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3635 SUBITEM_INFO
*lpSubItem
;
3637 /* we do not support subitems for virtual listviews */
3638 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3640 /* set subitem only if column is present */
3641 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3643 /* First do some sanity checks */
3644 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3645 particularly useful. We currently do not actually do anything with
3646 the flag on subitems.
3648 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
)) return FALSE
;
3649 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
3651 /* get the subitem structure, and create it if not there */
3652 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3653 assert (hdpaSubItems
);
3655 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3658 SUBITEM_INFO
*tmpSubItem
;
3661 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
3662 if (!lpSubItem
) return FALSE
;
3663 /* we could binary search here, if need be...*/
3664 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3666 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
3667 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3669 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3674 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3675 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3679 if (lpLVItem
->mask
& LVIF_IMAGE
)
3680 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3682 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3686 if (lpLVItem
->mask
& LVIF_TEXT
)
3687 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3689 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3698 * Sets item attributes.
3701 * [I] infoPtr : valid pointer to the listview structure
3702 * [I] lpLVItem : new item attributes
3703 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3709 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
3711 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3712 HWND hwndSelf
= infoPtr
->hwndSelf
;
3713 LPWSTR pszText
= NULL
;
3714 BOOL bResult
, bChanged
= FALSE
;
3716 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3718 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3721 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3722 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3724 pszText
= lpLVItem
->pszText
;
3725 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3728 /* actually set the fields */
3729 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3731 if (lpLVItem
->iSubItem
)
3732 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3734 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3735 if (!IsWindow(hwndSelf
))
3738 /* redraw item, if necessary */
3739 if (bChanged
&& !infoPtr
->bIsDrawing
)
3741 /* this little optimization eliminates some nasty flicker */
3742 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3743 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
3744 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
3745 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3747 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3752 textfreeT(lpLVItem
->pszText
, isW
);
3753 lpLVItem
->pszText
= pszText
;
3761 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3764 * [I] infoPtr : valid pointer to the listview structure
3769 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
3771 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3773 SCROLLINFO scrollInfo
;
3775 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3776 scrollInfo
.fMask
= SIF_POS
;
3778 if (uView
== LVS_LIST
)
3780 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3781 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3783 else if (uView
== LVS_REPORT
)
3785 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3786 nItem
= scrollInfo
.nPos
;
3790 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3791 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3794 TRACE("nItem=%d\n", nItem
);
3802 * Erases the background of the given rectangle
3805 * [I] infoPtr : valid pointer to the listview structure
3806 * [I] hdc : device context handle
3807 * [I] lprcBox : clipping rectangle
3813 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3815 if (!infoPtr
->hBkBrush
) return FALSE
;
3817 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
3819 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3827 * [I] infoPtr : valid pointer to the listview structure
3828 * [I] hdc : device context handle
3829 * [I] nItem : item index
3830 * [I] nSubItem : subitem index
3831 * [I] pos : item position in client coordinates
3832 * [I] cdmode : custom draw mode
3838 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3840 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3841 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3842 static WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3843 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3845 RECT rcSelect
, rcBox
, rcIcon
, rcLabel
, rcStateIcon
;
3846 NMLVCUSTOMDRAW nmlvcd
;
3851 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, wine_dbgstr_point(&pos
));
3853 /* get information needed for drawing the item */
3854 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
;
3855 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
;
3856 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3857 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3858 lvItem
.iItem
= nItem
;
3859 lvItem
.iSubItem
= nSubItem
;
3862 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3863 lvItem
.pszText
= szDispText
;
3864 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3865 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3866 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3867 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= szCallback
;
3868 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3870 /* now check if we need to update the focus rectangle */
3871 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3873 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3874 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
3875 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3876 OffsetRect(&rcSelect
, pos
.x
, pos
.y
);
3877 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3878 OffsetRect(&rcStateIcon
, pos
.x
, pos
.y
);
3879 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3880 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3881 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
3882 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
3884 /* fill in the custom draw structure */
3885 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3887 hOldFont
= GetCurrentObject(hdc
, OBJ_FONT
);
3888 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3889 if (cdmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3890 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3891 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3892 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3893 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3894 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3895 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3897 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3898 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3900 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3901 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
3902 else if ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) == FALSE
)
3903 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
3905 /* in full row select, subitems, will just use main item's colors */
3906 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3907 nmlvcd
.clrTextBk
= CLR_NONE
;
3910 if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && (nSubItem
== 0))
3912 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
3915 TRACE("uStateImage=%d\n", uStateImage
);
3916 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
,
3917 rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
3922 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3923 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3925 TRACE("iImage=%d\n", lvItem
.iImage
);
3926 ImageList_DrawEx(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3927 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
, CLR_DEFAULT
,
3928 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3931 /* Don't bother painting item being edited */
3932 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3934 /* FIXME: temporary hack */
3935 rcSelect
.left
= rcLabel
.left
;
3937 /* draw the selection background, if we're drawing the main item */
3940 /* in icon mode, the label rect is really what we want to draw the
3942 if (uView
== LVS_ICON
)
3945 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3946 rcSelect
.right
= rcBox
.right
;
3948 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3949 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3950 if(lprcFocus
) *lprcFocus
= rcSelect
;
3953 /* figure out the text drawing flags */
3954 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3955 if (uView
== LVS_ICON
)
3956 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3959 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3961 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3962 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3963 default: uFormat
|= DT_LEFT
;
3966 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3968 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3969 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3971 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3973 /* for GRIDLINES reduce the bottom so the text formats correctly */
3974 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
3977 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3980 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3981 notify_postpaint(infoPtr
, &nmlvcd
);
3982 if (cdsubitemmode
& CDRF_NEWFONT
)
3983 SelectObject(hdc
, hOldFont
);
3989 * Draws listview items when in owner draw mode.
3992 * [I] infoPtr : valid pointer to the listview structure
3993 * [I] hdc : device context handle
3998 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4000 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
4001 DWORD cditemmode
= CDRF_DODEFAULT
;
4002 NMLVCUSTOMDRAW nmlvcd
;
4003 POINT Origin
, Position
;
4009 ZeroMemory(&dis
, sizeof(dis
));
4011 /* Get scroll info once before loop */
4012 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4014 /* iterate through the invalidated rows */
4015 while(iterator_next(i
))
4017 item
.iItem
= i
->nItem
;
4019 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4020 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4021 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
4023 dis
.CtlType
= ODT_LISTVIEW
;
4025 dis
.itemID
= item
.iItem
;
4026 dis
.itemAction
= ODA_DRAWENTIRE
;
4028 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
4029 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
4030 dis
.hwndItem
= infoPtr
->hwndSelf
;
4032 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
4033 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
4034 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
4035 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
4036 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
4037 dis
.itemData
= item
.lParam
;
4039 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
4042 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4043 * structure for the rest. of the paint cycle
4045 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4046 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4047 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4049 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4051 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4052 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4055 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4056 notify_postpaint(infoPtr
, &nmlvcd
);
4062 * Draws listview items when in report display mode.
4065 * [I] infoPtr : valid pointer to the listview structure
4066 * [I] hdc : device context handle
4067 * [I] cdmode : custom draw mode
4072 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4075 RECT rcClip
, rcItem
;
4076 POINT Origin
, Position
;
4082 /* figure out what to draw */
4083 rgntype
= GetClipBox(hdc
, &rcClip
);
4084 if (rgntype
== NULLREGION
) return;
4086 /* Get scroll info once before loop */
4087 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4089 /* narrow down the columns we need to paint */
4090 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4092 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4093 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4095 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4097 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4098 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4100 iterator_rangeitems(&j
, colRange
);
4102 /* in full row select, we _have_ to draw the main item */
4103 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4106 /* iterate through the invalidated rows */
4107 while(iterator_next(i
))
4109 /* iterate through the invalidated columns */
4110 while(iterator_next(&j
))
4112 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4113 Position
.x
+= Origin
.x
;
4114 Position
.y
+= Origin
.y
;
4116 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4118 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4120 rcItem
.bottom
= infoPtr
->nItemHeight
;
4121 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
4122 if (!RectVisible(hdc
, &rcItem
)) continue;
4125 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
4128 iterator_destroy(&j
);
4133 * Draws the gridlines if necessary when in report display mode.
4136 * [I] infoPtr : valid pointer to the listview structure
4137 * [I] hdc : device context handle
4142 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
4147 RECT rcClip
, rcItem
;
4154 /* figure out what to draw */
4155 rgntype
= GetClipBox(hdc
, &rcClip
);
4156 if (rgntype
== NULLREGION
) return;
4158 /* Get scroll info once before loop */
4159 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4161 /* narrow down the columns we need to paint */
4162 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4164 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4165 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4167 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4169 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4170 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4172 iterator_rangeitems(&j
, colRange
);
4174 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
4176 hOldPen
= SelectObject ( hdc
, hPen
);
4178 /* draw the vertical lines for the columns */
4179 iterator_rangeitems(&j
, colRange
);
4180 while(iterator_next(&j
))
4182 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4183 if (rcItem
.left
== 0) continue; /* skip first column */
4184 rcItem
.left
+= Origin
.x
;
4185 rcItem
.right
+= Origin
.x
;
4186 rcItem
.top
= infoPtr
->rcList
.top
;
4187 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
4188 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
4189 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4190 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
4192 iterator_destroy(&j
);
4194 /* draw the horizontial lines for the rows */
4195 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
4196 rcItem
.left
= infoPtr
->rcList
.left
+ Origin
.x
;
4197 rcItem
.right
= infoPtr
->rcList
.right
+ Origin
.x
;
4198 rcItem
.bottom
= rcItem
.top
= Origin
.y
- 1;
4199 MoveToEx(hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4200 LineTo(hdc
, rcItem
.right
, rcItem
.top
);
4201 for(y
=itemheight
-1+Origin
.y
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
4203 rcItem
.bottom
= rcItem
.top
= y
;
4204 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
4205 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4206 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
4209 SelectObject( hdc
, hOldPen
);
4210 DeleteObject( hPen
);
4216 * Draws listview items when in list display mode.
4219 * [I] infoPtr : valid pointer to the listview structure
4220 * [I] hdc : device context handle
4221 * [I] cdmode : custom draw mode
4226 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4228 POINT Origin
, Position
;
4230 /* Get scroll info once before loop */
4231 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4233 while(iterator_prev(i
))
4235 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4236 Position
.x
+= Origin
.x
;
4237 Position
.y
+= Origin
.y
;
4239 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
4246 * Draws listview items.
4249 * [I] infoPtr : valid pointer to the listview structure
4250 * [I] hdc : device context handle
4251 * [I] prcErase : rect to be erased before refresh (may be NULL)
4256 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
4258 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4259 COLORREF oldTextColor
= 0, oldBkColor
= 0, oldClrTextBk
, oldClrText
;
4260 NMLVCUSTOMDRAW nmlvcd
;
4267 HBITMAP hbmp
= NULL
;
4269 LISTVIEW_DUMP(infoPtr
);
4271 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4272 TRACE("double buffering\n");
4274 hdc
= CreateCompatibleDC(hdcOrig
);
4276 ERR("Failed to create DC for backbuffer\n");
4279 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
4280 infoPtr
->rcList
.bottom
);
4282 ERR("Failed to create bitmap for backbuffer\n");
4287 SelectObject(hdc
, hbmp
);
4288 SelectObject(hdc
, infoPtr
->hFont
);
4290 /* Save dc values we're gonna trash while drawing
4291 * FIXME: Should be done in LISTVIEW_DrawItem() */
4292 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
4293 oldBkMode
= GetBkMode(hdc
);
4294 oldBkColor
= GetBkColor(hdc
);
4295 oldTextColor
= GetTextColor(hdc
);
4298 infoPtr
->bIsDrawing
= TRUE
;
4301 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
4302 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4303 /* If no erasing was done (usually because RedrawWindow was called
4304 * with RDW_INVALIDATE only) we need to copy the old contents into
4305 * the backbuffer before continuing. */
4306 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4307 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4308 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4309 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4312 /* FIXME: Shouldn't need to do this */
4313 oldClrTextBk
= infoPtr
->clrTextBk
;
4314 oldClrText
= infoPtr
->clrText
;
4316 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
4318 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
4319 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
4320 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4321 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
4322 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4324 /* Use these colors to draw the items */
4325 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
4326 infoPtr
->clrText
= nmlvcd
.clrText
;
4328 /* nothing to draw */
4329 if(infoPtr
->nItemCount
== 0) goto enddraw
;
4331 /* figure out what we need to draw */
4332 iterator_visibleitems(&i
, infoPtr
, hdc
);
4334 /* send cache hint notification */
4335 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4337 RANGE range
= iterator_range(&i
);
4340 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4341 nmlv
.iFrom
= range
.lower
;
4342 nmlv
.iTo
= range
.upper
- 1;
4343 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4346 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
4347 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4350 if (uView
== LVS_REPORT
)
4351 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4352 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4353 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4355 /* if we have a focus rect, draw it */
4356 if (infoPtr
->bFocus
)
4357 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
4359 iterator_destroy(&i
);
4362 /* For LVS_EX_GRIDLINES go and draw lines */
4363 /* This includes the case where there were *no* items */
4364 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
&&
4365 infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4366 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
4368 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4369 notify_postpaint(infoPtr
, &nmlvcd
);
4371 infoPtr
->clrTextBk
= oldClrTextBk
;
4372 infoPtr
->clrText
= oldClrText
;
4375 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4376 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4377 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4378 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4383 SelectObject(hdc
, hOldFont
);
4384 SetBkMode(hdc
, oldBkMode
);
4385 SetBkColor(hdc
, oldBkColor
);
4386 SetTextColor(hdc
, oldTextColor
);
4389 infoPtr
->bIsDrawing
= FALSE
;
4395 * Calculates the approximate width and height of a given number of items.
4398 * [I] infoPtr : valid pointer to the listview structure
4399 * [I] nItemCount : number of items
4400 * [I] wWidth : width
4401 * [I] wHeight : height
4404 * Returns a DWORD. The width in the low word and the height in high word.
4406 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4407 WORD wWidth
, WORD wHeight
)
4409 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4410 INT nItemCountPerColumn
= 1;
4411 INT nColumnCount
= 0;
4412 DWORD dwViewRect
= 0;
4414 if (nItemCount
== -1)
4415 nItemCount
= infoPtr
->nItemCount
;
4417 if (uView
== LVS_LIST
)
4419 if (wHeight
== 0xFFFF)
4421 /* use current height */
4422 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4425 if (wHeight
< infoPtr
->nItemHeight
)
4426 wHeight
= infoPtr
->nItemHeight
;
4430 if (infoPtr
->nItemHeight
> 0)
4432 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4433 if (nItemCountPerColumn
== 0)
4434 nItemCountPerColumn
= 1;
4436 if (nItemCount
% nItemCountPerColumn
!= 0)
4437 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4439 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4443 /* Microsoft padding magic */
4444 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4445 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4447 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4449 else if (uView
== LVS_REPORT
)
4453 if (infoPtr
->nItemCount
> 0)
4455 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4456 wWidth
= rcBox
.right
- rcBox
.left
;
4457 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4461 /* use current height and width */
4462 if (wHeight
== 0xffff)
4463 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4464 if (wWidth
== 0xffff)
4465 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4468 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4470 else if (uView
== LVS_SMALLICON
)
4471 FIXME("uView == LVS_SMALLICON: not implemented\n");
4472 else if (uView
== LVS_ICON
)
4473 FIXME("uView == LVS_ICON: not implemented\n");
4481 * Create a drag image list for the specified item.
4484 * [I] infoPtr : valid pointer to the listview structure
4485 * [I] iItem : index of item
4486 * [O] lppt : Upper-left corner of the image
4489 * Returns a handle to the image list if successful, NULL otherwise.
4491 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4497 HBITMAP hbmp
, hOldbmp
;
4498 HIMAGELIST dragList
= 0;
4499 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
4501 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4504 rcItem
.left
= LVIR_BOUNDS
;
4505 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4508 lppt
->x
= rcItem
.left
;
4509 lppt
->y
= rcItem
.top
;
4511 size
.cx
= rcItem
.right
- rcItem
.left
;
4512 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4514 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4515 hdc
= CreateCompatibleDC(hdcOrig
);
4516 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4517 hOldbmp
= SelectObject(hdc
, hbmp
);
4519 rcItem
.left
= rcItem
.top
= 0;
4520 rcItem
.right
= size
.cx
;
4521 rcItem
.bottom
= size
.cy
;
4522 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4525 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4527 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4528 SelectObject(hdc
, hOldbmp
);
4529 ImageList_Add(dragList
, hbmp
, 0);
4532 SelectObject(hdc
, hOldbmp
);
4536 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4538 TRACE("ret=%p\n", dragList
);
4546 * Removes all listview items and subitems.
4549 * [I] infoPtr : valid pointer to the listview structure
4555 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
4558 HDPA hdpaSubItems
= NULL
;
4565 /* we do it directly, to avoid notifications */
4566 ranges_clear(infoPtr
->selectionRanges
);
4567 infoPtr
->nSelectionMark
= -1;
4568 infoPtr
->nFocusedItem
= -1;
4569 SetRectEmpty(&infoPtr
->rcFocus
);
4570 /* But we are supposed to leave nHotItem as is! */
4573 /* send LVN_DELETEALLITEMS notification */
4574 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4576 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4578 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4580 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4582 /* send LVN_DELETEITEM notification, if not suppressed
4583 and if it is not a virtual listview */
4584 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4585 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4586 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4588 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
4589 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4592 DPA_Destroy(hdpaSubItems
);
4593 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4595 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4596 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4597 infoPtr
->nItemCount
--;
4602 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4603 LISTVIEW_UpdateScroll(infoPtr
);
4605 LISTVIEW_InvalidateList(infoPtr
);
4612 * Scrolls, and updates the columns, when a column is changing width.
4615 * [I] infoPtr : valid pointer to the listview structure
4616 * [I] nColumn : column to scroll
4617 * [I] dx : amount of scroll, in pixels
4622 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4624 COLUMN_INFO
*lpColumnInfo
;
4629 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4630 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4631 rcCol
= lpColumnInfo
->rcHeader
;
4632 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4633 rcCol
.left
= rcCol
.right
;
4635 /* adjust the other columns */
4636 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4638 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4639 lpColumnInfo
->rcHeader
.left
+= dx
;
4640 lpColumnInfo
->rcHeader
.right
+= dx
;
4643 /* do not update screen if not in report mode */
4644 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4646 /* if we have a focus, we must first erase the focus rect */
4647 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4649 /* Need to reset the item width when inserting a new column */
4650 infoPtr
->nItemWidth
+= dx
;
4652 LISTVIEW_UpdateScroll(infoPtr
);
4653 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
4655 /* scroll to cover the deleted column, and invalidate for redraw */
4656 rcOld
= infoPtr
->rcList
;
4657 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
4658 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4660 /* we can restore focus now */
4661 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4666 * Removes a column from the listview control.
4669 * [I] infoPtr : valid pointer to the listview structure
4670 * [I] nColumn : column index
4676 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4680 TRACE("nColumn=%d\n", nColumn
);
4682 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4683 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4685 /* While the MSDN specifically says that column zero should not be deleted,
4686 what actually happens is that the column itself is deleted but no items or subitems
4690 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4692 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4695 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4696 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4698 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4700 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4702 INT nItem
, nSubItem
, i
;
4704 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4706 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4709 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4711 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4712 if (lpSubItem
->iSubItem
== nColumn
)
4715 lpDelItem
= lpSubItem
;
4717 else if (lpSubItem
->iSubItem
> nColumn
)
4719 lpSubItem
->iSubItem
--;
4723 /* if we found our subitem, zapp it */
4727 if (is_textW(lpDelItem
->hdr
.pszText
))
4728 Free(lpDelItem
->hdr
.pszText
);
4733 /* free dpa memory */
4734 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4739 /* update the other column info */
4740 LISTVIEW_UpdateItemSize(infoPtr
);
4741 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4742 LISTVIEW_InvalidateList(infoPtr
);
4744 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4751 * Invalidates the listview after an item's insertion or deletion.
4754 * [I] infoPtr : valid pointer to the listview structure
4755 * [I] nItem : item index
4756 * [I] dir : -1 if deleting, 1 if inserting
4761 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4763 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4764 INT nPerCol
, nItemCol
, nItemRow
;
4768 /* if we don't refresh, what's the point of scrolling? */
4769 if (!is_redrawing(infoPtr
)) return;
4771 assert (abs(dir
) == 1);
4773 /* arrange icons if autoarrange is on */
4774 if (is_autoarrange(infoPtr
))
4776 BOOL arrange
= TRUE
;
4777 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4778 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4779 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4782 /* scrollbars need updating */
4783 LISTVIEW_UpdateScroll(infoPtr
);
4785 /* figure out the item's position */
4786 if (uView
== LVS_REPORT
)
4787 nPerCol
= infoPtr
->nItemCount
+ 1;
4788 else if (uView
== LVS_LIST
)
4789 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4790 else /* LVS_ICON, or LVS_SMALLICON */
4793 nItemCol
= nItem
/ nPerCol
;
4794 nItemRow
= nItem
% nPerCol
;
4795 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4797 /* move the items below up a slot */
4798 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4799 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4800 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4801 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4802 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4803 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4804 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4806 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
4807 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4808 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4811 /* report has only that column, so we're done */
4812 if (uView
== LVS_REPORT
) return;
4814 /* now for LISTs, we have to deal with the columns to the right */
4815 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4817 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4818 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4819 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4820 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4821 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4822 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4827 * Removes an item from the listview control.
4830 * [I] infoPtr : valid pointer to the listview structure
4831 * [I] nItem : item index
4837 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4840 const UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4841 const BOOL is_icon
= (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
);
4843 TRACE("(nItem=%d)\n", nItem
);
4845 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4847 /* remove selection, and focus */
4849 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4850 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4852 /* send LVN_DELETEITEM notification. */
4853 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
4855 /* we need to do this here, because we'll be deleting stuff */
4857 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4859 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4865 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4866 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4868 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
4869 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4872 DPA_Destroy(hdpaSubItems
);
4877 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4878 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4881 infoPtr
->nItemCount
--;
4882 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4884 /* now is the invalidation fun */
4886 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4893 * Callback implementation for editlabel control
4896 * [I] infoPtr : valid pointer to the listview structure
4897 * [I] pszText : modified text
4898 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4904 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4906 HWND hwndSelf
= infoPtr
->hwndSelf
;
4907 NMLVDISPINFOW dispInfo
;
4908 INT editedItem
= infoPtr
->nEditLabelItem
;
4911 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4913 infoPtr
->nEditLabelItem
= -1;
4915 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4916 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4917 dispInfo
.item
.iItem
= editedItem
;
4918 dispInfo
.item
.iSubItem
= 0;
4919 dispInfo
.item
.stateMask
= ~0;
4920 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4923 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
4926 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
4927 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
4928 textfreeT(tmp
, FALSE
);
4930 if (bSame
) return TRUE
;
4932 /* add the text from the edit in */
4933 dispInfo
.item
.mask
|= LVIF_TEXT
;
4934 dispInfo
.item
.pszText
= pszText
;
4935 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4937 /* Do we need to update the Item Text */
4938 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4939 if (!IsWindow(hwndSelf
))
4941 if (!pszText
) return TRUE
;
4943 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4945 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
4946 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
4947 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
4949 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
4954 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4955 dispInfo
.item
.mask
= LVIF_TEXT
;
4956 dispInfo
.item
.iItem
= editedItem
;
4957 dispInfo
.item
.iSubItem
= 0;
4958 dispInfo
.item
.pszText
= pszText
;
4959 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4960 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4965 * Begin in place editing of specified list view item
4968 * [I] infoPtr : valid pointer to the listview structure
4969 * [I] nItem : item index
4970 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4976 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4978 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4979 NMLVDISPINFOW dispInfo
;
4981 HWND hwndSelf
= infoPtr
->hwndSelf
;
4983 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4985 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4986 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4988 infoPtr
->nEditLabelItem
= nItem
;
4990 /* Is the EditBox still there, if so remove it */
4991 if(infoPtr
->hwndEdit
!= 0)
4993 SetFocus(infoPtr
->hwndSelf
);
4994 infoPtr
->hwndEdit
= 0;
4997 LISTVIEW_SetSelection(infoPtr
, nItem
);
4998 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4999 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
5001 rect
.left
= LVIR_LABEL
;
5002 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
5004 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5005 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
5006 dispInfo
.item
.iItem
= nItem
;
5007 dispInfo
.item
.iSubItem
= 0;
5008 dispInfo
.item
.stateMask
= ~0;
5009 dispInfo
.item
.pszText
= szDispText
;
5010 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
5011 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
5013 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
5014 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
5015 if (!infoPtr
->hwndEdit
) return 0;
5017 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
5019 if (!IsWindow(hwndSelf
))
5021 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
5022 infoPtr
->hwndEdit
= 0;
5026 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
5027 SetFocus(infoPtr
->hwndEdit
);
5028 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
5029 return infoPtr
->hwndEdit
;
5035 * Ensures the specified item is visible, scrolling into view if necessary.
5038 * [I] infoPtr : valid pointer to the listview structure
5039 * [I] nItem : item index
5040 * [I] bPartial : partially or entirely visible
5046 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
5048 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5049 INT nScrollPosHeight
= 0;
5050 INT nScrollPosWidth
= 0;
5051 INT nHorzAdjust
= 0;
5052 INT nVertAdjust
= 0;
5055 RECT rcItem
, rcTemp
;
5057 rcItem
.left
= LVIR_BOUNDS
;
5058 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
5060 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
5062 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
5064 /* scroll left/right, but in LVS_REPORT mode */
5065 if (uView
== LVS_LIST
)
5066 nScrollPosWidth
= infoPtr
->nItemWidth
;
5067 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5068 nScrollPosWidth
= 1;
5070 if (rcItem
.left
< infoPtr
->rcList
.left
)
5073 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
5078 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
5082 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
5084 /* scroll up/down, but not in LVS_LIST mode */
5085 if (uView
== LVS_REPORT
)
5086 nScrollPosHeight
= infoPtr
->nItemHeight
;
5087 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
5088 nScrollPosHeight
= 1;
5090 if (rcItem
.top
< infoPtr
->rcList
.top
)
5093 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
5098 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
5102 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
5104 if (nScrollPosWidth
)
5106 INT diff
= nHorzDiff
/ nScrollPosWidth
;
5107 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
5108 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5111 if (nScrollPosHeight
)
5113 INT diff
= nVertDiff
/ nScrollPosHeight
;
5114 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
5115 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5123 * Searches for an item with specific characteristics.
5126 * [I] hwnd : window handle
5127 * [I] nStart : base item index
5128 * [I] lpFindInfo : item information to look for
5131 * SUCCESS : index of item
5134 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5135 const LVFINDINFOW
*lpFindInfo
)
5137 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5138 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5139 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
5140 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
5141 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
5142 POINT Position
, Destination
;
5145 /* Search in virtual listviews should be done by application, not by
5146 listview control, so we just send LVN_ODFINDITEMW and return the result */
5147 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5151 nmlv
.iStart
= nStart
;
5152 nmlv
.lvfi
= *lpFindInfo
;
5153 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
5156 if (!lpFindInfo
|| nItem
< 0) return -1;
5159 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
5161 lvItem
.mask
|= LVIF_TEXT
;
5162 lvItem
.pszText
= szDispText
;
5163 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5166 if (lpFindInfo
->flags
& LVFI_WRAP
)
5169 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
5170 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
5175 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5176 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
5177 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
5178 switch(lpFindInfo
->vkDirection
)
5180 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
5181 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
5182 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
5183 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
5184 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
5185 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5186 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5188 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
5189 Destination
.x
= rcArea
.right
;
5190 Destination
.y
= rcArea
.bottom
;
5192 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
5196 else Destination
.x
= Destination
.y
= 0;
5198 /* if LVFI_PARAM is specified, all other flags are ignored */
5199 if (lpFindInfo
->flags
& LVFI_PARAM
)
5201 lvItem
.mask
|= LVIF_PARAM
;
5203 lvItem
.mask
&= ~LVIF_TEXT
;
5207 for (; nItem
< nLast
; nItem
++)
5209 lvItem
.iItem
= nItem
;
5210 lvItem
.iSubItem
= 0;
5211 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
5213 if (lvItem
.mask
& LVIF_PARAM
)
5215 if (lpFindInfo
->lParam
== lvItem
.lParam
)
5221 if (lvItem
.mask
& LVIF_TEXT
)
5223 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
5225 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
5229 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
5233 if (!bNearest
) return nItem
;
5235 /* This is very inefficient. To do a good job here,
5236 * we need a sorted array of (x,y) item positions */
5237 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5239 /* compute the distance^2 to the destination */
5240 xdist
= Destination
.x
- Position
.x
;
5241 ydist
= Destination
.y
- Position
.y
;
5242 dist
= xdist
* xdist
+ ydist
* ydist
;
5244 /* remember the distance, and item if it's closer */
5248 nNearestItem
= nItem
;
5255 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
5260 return nNearestItem
;
5265 * Searches for an item with specific characteristics.
5268 * [I] hwnd : window handle
5269 * [I] nStart : base item index
5270 * [I] lpFindInfo : item information to look for
5273 * SUCCESS : index of item
5276 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5277 const LVFINDINFOA
*lpFindInfo
)
5279 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
5284 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
5285 if (hasText
) fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
5286 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
5287 textfreeT(strW
, FALSE
);
5293 * Retrieves the background image of the listview control.
5296 * [I] infoPtr : valid pointer to the listview structure
5297 * [O] lpBkImage : background image attributes
5303 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5305 /* FIXME (listview, "empty stub!\n"); */
5311 * Retrieves column attributes.
5314 * [I] infoPtr : valid pointer to the listview structure
5315 * [I] nColumn : column index
5316 * [IO] lpColumn : column information
5317 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5318 * otherwise it is in fact a LPLVCOLUMNA
5324 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
5326 COLUMN_INFO
*lpColumnInfo
;
5329 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5330 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
5332 /* initialize memory */
5333 ZeroMemory(&hdi
, sizeof(hdi
));
5335 if (lpColumn
->mask
& LVCF_TEXT
)
5337 hdi
.mask
|= HDI_TEXT
;
5338 hdi
.pszText
= lpColumn
->pszText
;
5339 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
5342 if (lpColumn
->mask
& LVCF_IMAGE
)
5343 hdi
.mask
|= HDI_IMAGE
;
5345 if (lpColumn
->mask
& LVCF_ORDER
)
5346 hdi
.mask
|= HDI_ORDER
;
5348 if (lpColumn
->mask
& LVCF_SUBITEM
)
5349 hdi
.mask
|= HDI_LPARAM
;
5351 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
5353 if (lpColumn
->mask
& LVCF_FMT
)
5354 lpColumn
->fmt
= lpColumnInfo
->fmt
;
5356 if (lpColumn
->mask
& LVCF_WIDTH
)
5357 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
5359 if (lpColumn
->mask
& LVCF_IMAGE
)
5360 lpColumn
->iImage
= hdi
.iImage
;
5362 if (lpColumn
->mask
& LVCF_ORDER
)
5363 lpColumn
->iOrder
= hdi
.iOrder
;
5365 if (lpColumn
->mask
& LVCF_SUBITEM
)
5366 lpColumn
->iSubItem
= hdi
.lParam
;
5372 static BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
5379 /* FIXME: little hack */
5380 for (i
= 0; i
< iCount
; i
++)
5388 * Retrieves the column width.
5391 * [I] infoPtr : valid pointer to the listview structure
5392 * [I] int : column index
5395 * SUCCESS : column width
5398 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5400 INT nColumnWidth
= 0;
5403 TRACE("nColumn=%d\n", nColumn
);
5405 /* we have a 'column' in LIST and REPORT mode only */
5406 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
5409 nColumnWidth
= infoPtr
->nItemWidth
;
5412 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5413 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5414 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5416 * TODO: should we do the same in LVM_GETCOLUMN?
5418 hdItem
.mask
= HDI_WIDTH
;
5419 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
5421 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
5424 nColumnWidth
= hdItem
.cxy
;
5428 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5429 return nColumnWidth
;
5434 * In list or report display mode, retrieves the number of items that can fit
5435 * vertically in the visible area. In icon or small icon display mode,
5436 * retrieves the total number of visible items.
5439 * [I] infoPtr : valid pointer to the listview structure
5442 * Number of fully visible items.
5444 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
5446 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
5450 return infoPtr
->nItemCount
;
5452 return LISTVIEW_GetCountPerColumn(infoPtr
);
5454 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5462 * Retrieves an image list handle.
5465 * [I] infoPtr : valid pointer to the listview structure
5466 * [I] nImageList : image list identifier
5469 * SUCCESS : image list handle
5472 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5476 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5477 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5478 case LVSIL_STATE
: return infoPtr
->himlState
;
5483 /* LISTVIEW_GetISearchString */
5487 * Retrieves item attributes.
5490 * [I] hwnd : window handle
5491 * [IO] lpLVItem : item info
5492 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5493 * if FALSE, then lpLVItem is a LPLVITEMA.
5496 * This is the internal 'GetItem' interface -- it tries to
5497 * be smart and avoid text copies, if possible, by modifying
5498 * lpLVItem->pszText to point to the text string. Please note
5499 * that this is not always possible (e.g. OWNERDATA), so on
5500 * entry you *must* supply valid values for pszText, and cchTextMax.
5501 * The only difference to the documented interface is that upon
5502 * return, you should use *only* the lpLVItem->pszText, rather than
5503 * the buffer pointer you provided on input. Most code already does
5504 * that, so it's not a problem.
5505 * For the two cases when the text must be copied (that is,
5506 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5512 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5514 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5515 NMLVDISPINFOW dispInfo
;
5521 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5523 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5526 if (lpLVItem
->mask
== 0) return TRUE
;
5528 /* make a local copy */
5529 isubitem
= lpLVItem
->iSubItem
;
5531 /* a quick optimization if all we're asked is the focus state
5532 * these queries are worth optimising since they are common,
5533 * and can be answered in constant time, without the heavy accesses */
5534 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5535 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5537 lpLVItem
->state
= 0;
5538 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5539 lpLVItem
->state
|= LVIS_FOCUSED
;
5543 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5545 /* if the app stores all the data, handle it separately */
5546 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5548 dispInfo
.item
.state
= 0;
5550 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5551 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5553 /* NOTE: copy only fields which we _know_ are initialized, some apps
5554 * depend on the uninitialized fields being 0 */
5555 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5556 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5557 dispInfo
.item
.iSubItem
= isubitem
;
5558 if (lpLVItem
->mask
& LVIF_TEXT
)
5560 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5561 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5563 if (lpLVItem
->mask
& LVIF_STATE
)
5564 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5565 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5566 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5567 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5569 /* full size structure expected - _WIN32IE >= 0x560 */
5570 *lpLVItem
= dispInfo
.item
;
5572 else if (lpLVItem
->mask
& LVIF_INDENT
)
5574 /* indent member expected - _WIN32IE >= 0x300 */
5575 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5579 /* minimal structure expected */
5580 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5582 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5585 /* make sure lParam is zeroed out */
5586 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5588 /* we store only a little state, so if we're not asked, we're done */
5589 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5591 /* if focus is handled by us, report it */
5592 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5594 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5595 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5596 lpLVItem
->state
|= LVIS_FOCUSED
;
5599 /* and do the same for selection, if we handle it */
5600 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5602 lpLVItem
->state
&= ~LVIS_SELECTED
;
5603 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5604 lpLVItem
->state
|= LVIS_SELECTED
;
5610 /* find the item and subitem structures before we proceed */
5611 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5612 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5617 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5618 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5621 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5626 pItemHdr
= &lpItem
->hdr
;
5628 /* Do we need to query the state from the app? */
5629 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5631 dispInfo
.item
.mask
|= LVIF_STATE
;
5632 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5635 /* Do we need to enquire about the image? */
5636 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5637 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5639 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5640 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5643 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5644 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5646 dispInfo
.item
.mask
|= LVIF_TEXT
;
5647 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5648 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5649 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5650 *dispInfo
.item
.pszText
= '\0';
5653 /* If we don't have all the requested info, query the application */
5654 if (dispInfo
.item
.mask
!= 0)
5656 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5657 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5658 dispInfo
.item
.lParam
= lpItem
->lParam
;
5659 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5660 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5663 /* we should not store values for subitems */
5664 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5666 /* Now, handle the iImage field */
5667 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5669 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5670 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5671 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5673 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5675 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5676 lpLVItem
->iImage
= pItemHdr
->iImage
;
5678 lpLVItem
->iImage
= 0;
5681 /* The pszText field */
5682 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5684 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5685 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5687 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5689 else if (lpLVItem
->mask
& LVIF_TEXT
)
5691 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5692 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5695 /* Next is the lParam field */
5696 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5698 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5699 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5700 lpItem
->lParam
= dispInfo
.item
.lParam
;
5702 else if (lpLVItem
->mask
& LVIF_PARAM
)
5703 lpLVItem
->lParam
= lpItem
->lParam
;
5705 /* if this is a subitem, we're done */
5706 if (isubitem
) return TRUE
;
5708 /* ... the state field (this one is different due to uCallbackmask) */
5709 if (lpLVItem
->mask
& LVIF_STATE
)
5711 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
5712 if (dispInfo
.item
.mask
& LVIF_STATE
)
5714 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5715 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5717 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5719 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5720 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5721 lpLVItem
->state
|= LVIS_FOCUSED
;
5723 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5725 lpLVItem
->state
&= ~LVIS_SELECTED
;
5726 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5727 lpLVItem
->state
|= LVIS_SELECTED
;
5731 /* and last, but not least, the indent field */
5732 if (lpLVItem
->mask
& LVIF_INDENT
)
5733 lpLVItem
->iIndent
= lpItem
->iIndent
;
5740 * Retrieves item attributes.
5743 * [I] hwnd : window handle
5744 * [IO] lpLVItem : item info
5745 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5746 * if FALSE, then lpLVItem is a LPLVITEMA.
5749 * This is the external 'GetItem' interface -- it properly copies
5750 * the text in the provided buffer.
5756 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5761 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5764 pszText
= lpLVItem
->pszText
;
5765 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5766 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5767 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5768 lpLVItem
->pszText
= pszText
;
5776 * Retrieves the position (upper-left) of the listview control item.
5777 * Note that for LVS_ICON style, the upper-left is that of the icon
5778 * and not the bounding box.
5781 * [I] infoPtr : valid pointer to the listview structure
5782 * [I] nItem : item index
5783 * [O] lpptPosition : coordinate information
5789 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5791 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5794 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5796 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5798 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5799 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5801 if (uView
== LVS_ICON
)
5803 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5804 lpptPosition
->y
+= ICON_TOP_PADDING
;
5806 lpptPosition
->x
+= Origin
.x
;
5807 lpptPosition
->y
+= Origin
.y
;
5809 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
5816 * Retrieves the bounding rectangle for a listview control item.
5819 * [I] infoPtr : valid pointer to the listview structure
5820 * [I] nItem : item index
5821 * [IO] lprc : bounding rectangle coordinates
5822 * lprc->left specifies the portion of the item for which the bounding
5823 * rectangle will be retrieved.
5825 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5826 * including the icon and label.
5829 * * Experiment shows that native control returns:
5830 * * width = min (48, length of text line)
5831 * * .left = position.x - (width - iconsize.cx)/2
5832 * * .right = .left + width
5833 * * height = #lines of text * ntmHeight + icon height + 8
5834 * * .top = position.y - 2
5835 * * .bottom = .top + height
5836 * * separation between items .y = itemSpacing.cy - height
5837 * * .x = itemSpacing.cx - width
5838 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5841 * * Experiment shows that native control returns:
5842 * * width = iconSize.cx + 16
5843 * * .left = position.x - (width - iconsize.cx)/2
5844 * * .right = .left + width
5845 * * height = iconSize.cy + 4
5846 * * .top = position.y - 2
5847 * * .bottom = .top + height
5848 * * separation between items .y = itemSpacing.cy - height
5849 * * .x = itemSpacing.cx - width
5850 * LVIR_LABEL Returns the bounding rectangle of the item text.
5853 * * Experiment shows that native control returns:
5854 * * width = text length
5855 * * .left = position.x - width/2
5856 * * .right = .left + width
5857 * * height = ntmH * linecount + 2
5858 * * .top = position.y + iconSize.cy + 6
5859 * * .bottom = .top + height
5860 * * separation between items .y = itemSpacing.cy - height
5861 * * .x = itemSpacing.cx - width
5862 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5863 * rectangles, but excludes columns in report view.
5870 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5871 * upon whether the window has the focus currently and on whether the item
5872 * is the one with the focus. Ensure that the control's record of which
5873 * item has the focus agrees with the items' records.
5875 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5877 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5878 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5879 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5880 POINT Position
, Origin
;
5883 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5885 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5887 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5888 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5890 /* Be smart and try to figure out the minimum we have to do */
5891 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5892 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5893 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5894 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5895 oversizedBox
= TRUE
;
5897 /* get what we need from the item before hand, so we make
5898 * only one request. This can speed up things, if data
5899 * is stored on the app side */
5901 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5902 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5903 lvItem
.iItem
= nItem
;
5904 lvItem
.iSubItem
= 0;
5905 lvItem
.pszText
= szDispText
;
5906 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5907 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5908 /* we got the state already up, simulate it here, to avoid a reget */
5909 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5911 lvItem
.mask
|= LVIF_STATE
;
5912 lvItem
.stateMask
= LVIS_FOCUSED
;
5913 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5916 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5917 lprc
->left
= LVIR_BOUNDS
;
5921 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5925 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
5929 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5932 case LVIR_SELECTBOUNDS
:
5933 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
5937 WARN("Unknown value: %d\n", lprc
->left
);
5941 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5943 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
5950 * Retrieves the spacing between listview control items.
5953 * [I] infoPtr : valid pointer to the listview structure
5954 * [IO] lprc : rectangle to receive the output
5955 * on input, lprc->top = nSubItem
5956 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5958 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5959 * not only those of the first column.
5960 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5966 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5972 if (!lprc
) return FALSE
;
5974 nColumn
= lprc
->top
;
5976 TRACE("(nItem=%d, nSubItem=%d)\n", nItem
, lprc
->top
);
5977 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5979 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5981 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5983 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5985 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5988 lvItem
.iItem
= nItem
;
5989 lvItem
.iSubItem
= nColumn
;
5991 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5995 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
6000 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
6004 ERR("Unknown bounds=%d\n", lprc
->left
);
6008 OffsetRect(lprc
, Position
.x
, Position
.y
);
6015 * Retrieves the width of a label.
6018 * [I] infoPtr : valid pointer to the listview structure
6021 * SUCCESS : string width (in pixels)
6024 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
6026 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6029 TRACE("(nItem=%d)\n", nItem
);
6031 lvItem
.mask
= LVIF_TEXT
;
6032 lvItem
.iItem
= nItem
;
6033 lvItem
.iSubItem
= 0;
6034 lvItem
.pszText
= szDispText
;
6035 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6036 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
6038 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
6043 * Retrieves the spacing between listview control items.
6046 * [I] infoPtr : valid pointer to the listview structure
6047 * [I] bSmall : flag for small or large icon
6050 * Horizontal + vertical spacing
6052 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
6058 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6062 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
6063 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
6065 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
6072 * Retrieves the state of a listview control item.
6075 * [I] infoPtr : valid pointer to the listview structure
6076 * [I] nItem : item index
6077 * [I] uMask : state mask
6080 * State specified by the mask.
6082 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
6086 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6088 lvItem
.iItem
= nItem
;
6089 lvItem
.iSubItem
= 0;
6090 lvItem
.mask
= LVIF_STATE
;
6091 lvItem
.stateMask
= uMask
;
6092 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
6094 return lvItem
.state
& uMask
;
6099 * Retrieves the text of a listview control item or subitem.
6102 * [I] hwnd : window handle
6103 * [I] nItem : item index
6104 * [IO] lpLVItem : item information
6105 * [I] isW : TRUE if lpLVItem is Unicode
6108 * SUCCESS : string length
6111 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
6113 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6115 lpLVItem
->mask
= LVIF_TEXT
;
6116 lpLVItem
->iItem
= nItem
;
6117 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
6119 return textlenT(lpLVItem
->pszText
, isW
);
6124 * Searches for an item based on properties + relationships.
6127 * [I] infoPtr : valid pointer to the listview structure
6128 * [I] nItem : item index
6129 * [I] uFlags : relationship flag
6132 * SUCCESS : item index
6135 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
6137 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6139 LVFINDINFOW lvFindInfo
;
6140 INT nCountPerColumn
;
6144 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
6145 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
6147 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
6149 if (uFlags
& LVNI_CUT
)
6152 if (uFlags
& LVNI_DROPHILITED
)
6153 uMask
|= LVIS_DROPHILITED
;
6155 if (uFlags
& LVNI_FOCUSED
)
6156 uMask
|= LVIS_FOCUSED
;
6158 if (uFlags
& LVNI_SELECTED
)
6159 uMask
|= LVIS_SELECTED
;
6161 /* if we're asked for the focused item, that's only one,
6162 * so it's worth optimizing */
6163 if (uFlags
& LVNI_FOCUSED
)
6165 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
6166 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
6169 if (uFlags
& LVNI_ABOVE
)
6171 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6176 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6182 /* Special case for autoarrange - move 'til the top of a list */
6183 if (is_autoarrange(infoPtr
))
6185 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6186 while (nItem
- nCountPerRow
>= 0)
6188 nItem
-= nCountPerRow
;
6189 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6194 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6195 lvFindInfo
.vkDirection
= VK_UP
;
6196 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6197 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6199 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6204 else if (uFlags
& LVNI_BELOW
)
6206 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6208 while (nItem
< infoPtr
->nItemCount
)
6211 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6217 /* Special case for autoarrange - move 'til the bottom of a list */
6218 if (is_autoarrange(infoPtr
))
6220 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6221 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
6223 nItem
+= nCountPerRow
;
6224 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6229 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6230 lvFindInfo
.vkDirection
= VK_DOWN
;
6231 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6232 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6234 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6239 else if (uFlags
& LVNI_TOLEFT
)
6241 if (uView
== LVS_LIST
)
6243 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6244 while (nItem
- nCountPerColumn
>= 0)
6246 nItem
-= nCountPerColumn
;
6247 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6251 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6253 /* Special case for autoarrange - move 'til the beginning of a row */
6254 if (is_autoarrange(infoPtr
))
6256 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6257 while (nItem
% nCountPerRow
> 0)
6260 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6265 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6266 lvFindInfo
.vkDirection
= VK_LEFT
;
6267 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6268 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6270 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6275 else if (uFlags
& LVNI_TORIGHT
)
6277 if (uView
== LVS_LIST
)
6279 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6280 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
6282 nItem
+= nCountPerColumn
;
6283 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6287 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6289 /* Special case for autoarrange - move 'til the end of a row */
6290 if (is_autoarrange(infoPtr
))
6292 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6293 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
6296 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6301 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6302 lvFindInfo
.vkDirection
= VK_RIGHT
;
6303 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6304 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6306 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6315 /* search by index */
6316 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
6318 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
6326 /* LISTVIEW_GetNumberOfWorkAreas */
6330 * Retrieves the origin coordinates when in icon or small icon display mode.
6333 * [I] infoPtr : valid pointer to the listview structure
6334 * [O] lpptOrigin : coordinate information
6339 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
6341 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6342 INT nHorzPos
= 0, nVertPos
= 0;
6343 SCROLLINFO scrollInfo
;
6345 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
6346 scrollInfo
.fMask
= SIF_POS
;
6348 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
6349 nHorzPos
= scrollInfo
.nPos
;
6350 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
6351 nVertPos
= scrollInfo
.nPos
;
6353 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
6355 lpptOrigin
->x
= infoPtr
->rcList
.left
;
6356 lpptOrigin
->y
= infoPtr
->rcList
.top
;
6357 if (uView
== LVS_LIST
)
6358 nHorzPos
*= infoPtr
->nItemWidth
;
6359 else if (uView
== LVS_REPORT
)
6360 nVertPos
*= infoPtr
->nItemHeight
;
6362 lpptOrigin
->x
-= nHorzPos
;
6363 lpptOrigin
->y
-= nVertPos
;
6365 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
6370 * Retrieves the width of a string.
6373 * [I] hwnd : window handle
6374 * [I] lpszText : text string to process
6375 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6378 * SUCCESS : string width (in pixels)
6381 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
6386 if (is_textT(lpszText
, isW
))
6388 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6389 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6390 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6393 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6395 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6396 SelectObject(hdc
, hOldFont
);
6397 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6399 return stringSize
.cx
;
6404 * Determines which listview item is located at the specified position.
6407 * [I] infoPtr : valid pointer to the listview structure
6408 * [IO] lpht : hit test information
6409 * [I] subitem : fill out iSubItem.
6410 * [I] select : return the index only if the hit selects the item
6413 * (mm 20001022): We must not allow iSubItem to be touched, for
6414 * an app might pass only a structure with space up to iItem!
6415 * (MS Office 97 does that for instance in the file open dialog)
6418 * SUCCESS : item index
6421 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
6423 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6424 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6425 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6426 POINT Origin
, Position
, opt
;
6431 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
6435 if (subitem
) lpht
->iSubItem
= 0;
6437 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6438 lpht
->flags
|= LVHT_TOLEFT
;
6439 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6440 lpht
->flags
|= LVHT_TORIGHT
;
6442 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6443 lpht
->flags
|= LVHT_ABOVE
;
6444 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6445 lpht
->flags
|= LVHT_BELOW
;
6447 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6448 if (lpht
->flags
) return -1;
6450 lpht
->flags
|= LVHT_NOWHERE
;
6452 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6454 /* first deal with the large items */
6455 rcSearch
.left
= lpht
->pt
.x
;
6456 rcSearch
.top
= lpht
->pt
.y
;
6457 rcSearch
.right
= rcSearch
.left
+ 1;
6458 rcSearch
.bottom
= rcSearch
.top
+ 1;
6460 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6461 iterator_next(&i
); /* go to first item in the sequence */
6463 iterator_destroy(&i
);
6465 TRACE("lpht->iItem=%d\n", iItem
);
6466 if (iItem
== -1) return -1;
6468 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6469 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
6470 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6471 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6472 lvItem
.iItem
= iItem
;
6473 lvItem
.iSubItem
= 0;
6474 lvItem
.pszText
= szDispText
;
6475 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6476 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6477 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6479 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
6480 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6481 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6482 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6484 if (uView
== LVS_REPORT
)
6488 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6489 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6491 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
6492 if (!PtInRect(&rcBounds
, opt
)) return -1;
6494 if (PtInRect(&rcIcon
, opt
))
6495 lpht
->flags
|= LVHT_ONITEMICON
;
6496 else if (PtInRect(&rcLabel
, opt
))
6497 lpht
->flags
|= LVHT_ONITEMLABEL
;
6498 else if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && PtInRect(&rcState
, opt
))
6499 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6500 if (lpht
->flags
& LVHT_ONITEM
)
6501 lpht
->flags
&= ~LVHT_NOWHERE
;
6503 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6504 if (uView
== LVS_REPORT
&& subitem
)
6508 rcBounds
.right
= rcBounds
.left
;
6509 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6511 rcBounds
.left
= rcBounds
.right
;
6512 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6513 if (PtInRect(&rcBounds
, opt
))
6521 if (select
&& !(uView
== LVS_REPORT
&&
6522 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6523 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6525 if (uView
== LVS_REPORT
)
6527 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6528 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6530 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6532 return lpht
->iItem
= iItem
;
6536 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6537 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6538 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6539 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6540 their own sort proc. when sending LVM_SORTITEMS.
6543 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6545 LVS_SORTXXX must be specified,
6546 LVS_OWNERDRAW is not set,
6547 <item>.pszText is not LPSTR_TEXTCALLBACK.
6549 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6550 are sorted based on item text..."
6552 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6554 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
6555 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
6556 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6558 /* if we're sorting descending, negate the return value */
6559 return (((const LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6564 * Inserts a new item in the listview control.
6567 * [I] infoPtr : valid pointer to the listview structure
6568 * [I] lpLVItem : item information
6569 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6572 * SUCCESS : new item index
6575 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6577 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6582 BOOL is_sorted
, has_changed
;
6584 HWND hwndSelf
= infoPtr
->hwndSelf
;
6586 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6588 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6590 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6591 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
6593 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6595 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
6597 /* insert item in listview control data structure */
6598 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6599 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6601 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6602 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6604 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
6606 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6607 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6608 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6609 if (nItem
== -1) goto fail
;
6610 infoPtr
->nItemCount
++;
6612 /* shift indices first so they don't get tangled */
6613 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6615 /* set the item attributes */
6616 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6618 /* full size structure expected - _WIN32IE >= 0x560 */
6621 else if (lpLVItem
->mask
& LVIF_INDENT
)
6623 /* indent member expected - _WIN32IE >= 0x300 */
6624 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6628 /* minimal structure expected */
6629 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6632 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6634 item
.mask
|= LVIF_STATE
;
6635 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
6636 item
.state
&= ~LVIS_STATEIMAGEMASK
;
6637 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
6639 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6641 /* if we're sorted, sort the list, and update the index */
6644 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6645 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6646 assert(nItem
!= -1);
6649 /* make room for the position, if we are in the right mode */
6650 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6652 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6654 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6656 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6661 /* send LVN_INSERTITEM notification */
6662 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6664 nmlv
.lParam
= lpItem
->lParam
;
6665 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6666 if (!IsWindow(hwndSelf
))
6669 /* align items (set position of each item) */
6670 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6674 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6675 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6677 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6679 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6682 /* now is the invalidation fun */
6683 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6687 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6688 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6689 infoPtr
->nItemCount
--;
6691 DPA_DeletePtr(hdpaSubItems
, 0);
6692 DPA_Destroy (hdpaSubItems
);
6699 * Redraws a range of items.
6702 * [I] infoPtr : valid pointer to the listview structure
6703 * [I] nFirst : first item
6704 * [I] nLast : last item
6710 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6714 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6715 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6718 for (i
= nFirst
; i
<= nLast
; i
++)
6719 LISTVIEW_InvalidateItem(infoPtr
, i
);
6726 * Scroll the content of a listview.
6729 * [I] infoPtr : valid pointer to the listview structure
6730 * [I] dx : horizontal scroll amount in pixels
6731 * [I] dy : vertical scroll amount in pixels
6738 * If the control is in report mode (LVS_REPORT) the control can
6739 * be scrolled only in line increments. "dy" will be rounded to the
6740 * nearest number of pixels that are a whole line. Ex: if line height
6741 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6742 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6744 * For: (per experimentation with native control and CSpy ListView)
6745 * LVS_ICON dy=1 = 1 pixel (vertical only)
6747 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6749 * LVS_LIST dx=1 = 1 column (horizontal only)
6750 * but will only scroll 1 column per message
6751 * no matter what the value.
6752 * dy must be 0 or FALSE returned.
6753 * LVS_REPORT dx=1 = 1 pixel
6757 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6759 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6761 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6762 dy
/= infoPtr
->nItemHeight
;
6765 if (dy
!= 0) return FALSE
;
6772 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6773 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6780 * Sets the background color.
6783 * [I] infoPtr : valid pointer to the listview structure
6784 * [I] clrBk : background color
6790 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6792 TRACE("(clrBk=%x)\n", clrBk
);
6794 if(infoPtr
->clrBk
!= clrBk
) {
6795 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6796 infoPtr
->clrBk
= clrBk
;
6797 if (clrBk
== CLR_NONE
)
6798 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6800 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6801 LISTVIEW_InvalidateList(infoPtr
);
6807 /* LISTVIEW_SetBkImage */
6809 /*** Helper for {Insert,Set}ColumnT *only* */
6810 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
6811 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6813 if (lpColumn
->mask
& LVCF_FMT
)
6815 /* format member is valid */
6816 lphdi
->mask
|= HDI_FORMAT
;
6818 /* set text alignment (leftmost column must be left-aligned) */
6819 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6820 lphdi
->fmt
|= HDF_LEFT
;
6821 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6822 lphdi
->fmt
|= HDF_RIGHT
;
6823 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6824 lphdi
->fmt
|= HDF_CENTER
;
6826 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6827 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6829 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6831 lphdi
->fmt
|= HDF_IMAGE
;
6832 lphdi
->iImage
= I_IMAGECALLBACK
;
6836 if (lpColumn
->mask
& LVCF_WIDTH
)
6838 lphdi
->mask
|= HDI_WIDTH
;
6839 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6841 /* make it fill the remainder of the controls width */
6845 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6847 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6848 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6851 /* retrieve the layout of the header */
6852 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6853 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
6855 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6858 lphdi
->cxy
= lpColumn
->cx
;
6861 if (lpColumn
->mask
& LVCF_TEXT
)
6863 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6864 lphdi
->fmt
|= HDF_STRING
;
6865 lphdi
->pszText
= lpColumn
->pszText
;
6866 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6869 if (lpColumn
->mask
& LVCF_IMAGE
)
6871 lphdi
->mask
|= HDI_IMAGE
;
6872 lphdi
->iImage
= lpColumn
->iImage
;
6875 if (lpColumn
->mask
& LVCF_ORDER
)
6877 lphdi
->mask
|= HDI_ORDER
;
6878 lphdi
->iOrder
= lpColumn
->iOrder
;
6885 * Inserts a new column.
6888 * [I] infoPtr : valid pointer to the listview structure
6889 * [I] nColumn : column index
6890 * [I] lpColumn : column information
6891 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6894 * SUCCESS : new column index
6897 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6898 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6900 COLUMN_INFO
*lpColumnInfo
;
6903 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6905 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6907 if (!lpColumn
|| nColumn
< 0) return -1;
6908 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6910 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6911 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6914 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6915 * (can be seen in SPY) otherwise column never gets added.
6917 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
6918 hdi
.mask
|= HDI_WIDTH
;
6923 * when the iSubItem is available Windows copies it to the header lParam. It seems
6924 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6926 if (lpColumn
->mask
& LVCF_SUBITEM
)
6928 hdi
.mask
|= HDI_LPARAM
;
6929 hdi
.lParam
= lpColumn
->iSubItem
;
6932 /* create header if not present */
6933 LISTVIEW_CreateHeader(infoPtr
);
6934 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
) &&
6935 (LVS_REPORT
== uView
) && (WS_VISIBLE
& infoPtr
->dwStyle
))
6937 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
6940 /* insert item in header control */
6941 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6942 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6943 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6944 if (nNewColumn
== -1) return -1;
6945 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6947 /* create our own column info */
6948 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6949 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6951 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6952 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6954 /* now we have to actually adjust the data */
6955 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6957 SUBITEM_INFO
*lpSubItem
;
6961 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6963 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6964 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6966 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
6967 if (lpSubItem
->iSubItem
>= nNewColumn
)
6968 lpSubItem
->iSubItem
++;
6973 /* make space for the new column */
6974 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6975 LISTVIEW_UpdateItemSize(infoPtr
);
6980 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6983 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6991 * Sets the attributes of a header item.
6994 * [I] infoPtr : valid pointer to the listview structure
6995 * [I] nColumn : column index
6996 * [I] lpColumn : column attributes
6997 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7003 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
7004 const LVCOLUMNW
*lpColumn
, BOOL isW
)
7006 HDITEMW hdi
, hdiget
;
7009 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
7011 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7013 ZeroMemory(&hdi
, sizeof(HDITEMW
));
7014 if (lpColumn
->mask
& LVCF_FMT
)
7016 hdi
.mask
|= HDI_FORMAT
;
7017 hdiget
.mask
= HDI_FORMAT
;
7018 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
7019 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
7021 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
7023 /* set header item attributes */
7024 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
7025 if (!bResult
) return FALSE
;
7027 if (lpColumn
->mask
& LVCF_FMT
)
7029 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
7030 int oldFmt
= lpColumnInfo
->fmt
;
7032 lpColumnInfo
->fmt
= lpColumn
->fmt
;
7033 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
7035 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7036 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
7045 * Sets the column order array
7048 * [I] infoPtr : valid pointer to the listview structure
7049 * [I] iCount : number of elements in column order array
7050 * [I] lpiArray : pointer to column order array
7056 static BOOL
LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
7058 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
7069 * Sets the width of a column
7072 * [I] infoPtr : valid pointer to the listview structure
7073 * [I] nColumn : column index
7074 * [I] cx : column width
7080 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
7082 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7083 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
7087 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
7089 /* set column width only if in report or list mode */
7090 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
7092 /* take care of invalid cx values */
7093 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
7094 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
7096 /* resize all columns if in LVS_LIST mode */
7097 if(uView
== LVS_LIST
)
7099 infoPtr
->nItemWidth
= cx
;
7100 LISTVIEW_InvalidateList(infoPtr
);
7104 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7106 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
7111 lvItem
.mask
= LVIF_TEXT
;
7113 lvItem
.iSubItem
= nColumn
;
7114 lvItem
.pszText
= szDispText
;
7115 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7116 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7118 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
7119 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
7120 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
7122 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
7123 max_cx
+= infoPtr
->iconSize
.cx
;
7124 max_cx
+= TRAILING_LABEL_PADDING
;
7127 /* autosize based on listview items width */
7128 if(cx
== LVSCW_AUTOSIZE
)
7130 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
7132 /* if iCol is the last column make it fill the remainder of the controls width */
7133 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
7138 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7139 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
7141 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
7145 /* Despite what the MS docs say, if this is not the last
7146 column, then MS resizes the column to the width of the
7147 largest text string in the column, including headers
7148 and items. This is different from LVSCW_AUTOSIZE in that
7149 LVSCW_AUTOSIZE ignores the header string length. */
7152 /* retrieve header text */
7153 hdi
.mask
= HDI_TEXT
;
7154 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
7155 hdi
.pszText
= szDispText
;
7156 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
))
7158 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7159 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
7162 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
7163 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
7164 /* FIXME: Take into account the header image, if one is present */
7165 SelectObject(hdc
, old_font
);
7166 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7168 cx
= max (cx
, max_cx
);
7172 if (cx
< 0) return FALSE
;
7174 /* call header to update the column change */
7175 hdi
.mask
= HDI_WIDTH
;
7177 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
7178 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
7182 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7185 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
7188 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
7190 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
7191 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
7194 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
7195 ILC_COLOR
| ILC_MASK
, 2, 2);
7196 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
7197 hdc
= CreateCompatibleDC(hdc_wnd
);
7198 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
7199 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
7200 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
7202 rc
.left
= rc
.top
= 0;
7203 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
7204 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
7206 hbm_orig
= SelectObject(hdc
, hbm_mask
);
7207 FillRect(hdc
, &rc
, hbr_white
);
7208 InflateRect(&rc
, -2, -2);
7209 FillRect(hdc
, &rc
, hbr_black
);
7211 SelectObject(hdc
, hbm_im
);
7212 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
7213 SelectObject(hdc
, hbm_orig
);
7214 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7216 SelectObject(hdc
, hbm_im
);
7217 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
7218 SelectObject(hdc
, hbm_orig
);
7219 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7221 DeleteObject(hbm_mask
);
7222 DeleteObject(hbm_im
);
7230 * Sets the extended listview style.
7233 * [I] infoPtr : valid pointer to the listview structure
7235 * [I] dwStyle : style
7238 * SUCCESS : previous style
7241 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwExStyle
)
7243 DWORD dwOldExStyle
= infoPtr
->dwLvExStyle
;
7247 infoPtr
->dwLvExStyle
= (dwOldExStyle
& ~dwMask
) | (dwExStyle
& dwMask
);
7249 infoPtr
->dwLvExStyle
= dwExStyle
;
7251 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_CHECKBOXES
)
7253 HIMAGELIST himl
= 0;
7254 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7257 item
.mask
= LVIF_STATE
;
7258 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7259 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7260 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
7262 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
7264 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
7267 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_HEADERDRAGDROP
)
7269 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
7270 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
7271 dwStyle
|= HDS_DRAGDROP
;
7273 dwStyle
&= ~HDS_DRAGDROP
;
7274 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, dwStyle
);
7277 /* GRIDLINES adds decoration at top so changes sizes */
7278 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_GRIDLINES
)
7280 LISTVIEW_UpdateSize(infoPtr
);
7284 LISTVIEW_InvalidateList(infoPtr
);
7285 return dwOldExStyle
;
7290 * Sets the new hot cursor used during hot tracking and hover selection.
7293 * [I] infoPtr : valid pointer to the listview structure
7294 * [I] hCursor : the new hot cursor handle
7297 * Returns the previous hot cursor
7299 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
7301 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
7303 infoPtr
->hHotCursor
= hCursor
;
7311 * Sets the hot item index.
7314 * [I] infoPtr : valid pointer to the listview structure
7315 * [I] iIndex : index
7318 * SUCCESS : previous hot item index
7319 * FAILURE : -1 (no hot item)
7321 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
7323 INT iOldIndex
= infoPtr
->nHotItem
;
7325 infoPtr
->nHotItem
= iIndex
;
7333 * Sets the amount of time the cursor must hover over an item before it is selected.
7336 * [I] infoPtr : valid pointer to the listview structure
7337 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7340 * Returns the previous hover time
7342 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
7344 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
7346 infoPtr
->dwHoverTime
= dwHoverTime
;
7348 return oldHoverTime
;
7353 * Sets spacing for icons of LVS_ICON style.
7356 * [I] infoPtr : valid pointer to the listview structure
7357 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7358 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7361 * MAKELONG(oldcx, oldcy)
7363 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
7365 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7366 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7368 TRACE("requested=(%d,%d)\n", cx
, cy
);
7370 /* this is supported only for LVS_ICON style */
7371 if (uView
!= LVS_ICON
) return oldspacing
;
7373 /* set to defaults, if instructed to */
7374 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
7375 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
7377 /* if 0 then compute width
7378 * FIXME: Should scan each item and determine max width of
7379 * icon or label, then make that the width */
7381 cx
= infoPtr
->iconSpacing
.cx
;
7383 /* if 0 then compute height */
7385 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
7386 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
7389 infoPtr
->iconSpacing
.cx
= cx
;
7390 infoPtr
->iconSpacing
.cy
= cy
;
7392 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7393 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
7394 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
7395 infoPtr
->ntmHeight
);
7397 /* these depend on the iconSpacing */
7398 LISTVIEW_UpdateItemSize(infoPtr
);
7403 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
7407 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
7414 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
7415 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
7424 * [I] infoPtr : valid pointer to the listview structure
7425 * [I] nType : image list type
7426 * [I] himl : image list handle
7429 * SUCCESS : old image list
7432 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
7434 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7435 INT oldHeight
= infoPtr
->nItemHeight
;
7436 HIMAGELIST himlOld
= 0;
7438 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
7443 himlOld
= infoPtr
->himlNormal
;
7444 infoPtr
->himlNormal
= himl
;
7445 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
7446 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
7450 himlOld
= infoPtr
->himlSmall
;
7451 infoPtr
->himlSmall
= himl
;
7452 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
7456 himlOld
= infoPtr
->himlState
;
7457 infoPtr
->himlState
= himl
;
7458 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
7459 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7463 ERR("Unknown icon type=%d\n", nType
);
7467 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
7468 if (infoPtr
->nItemHeight
!= oldHeight
)
7469 LISTVIEW_UpdateScroll(infoPtr
);
7476 * Preallocates memory (does *not* set the actual count of items !)
7479 * [I] infoPtr : valid pointer to the listview structure
7480 * [I] nItems : item count (projected number of items to allocate)
7481 * [I] dwFlags : update flags
7487 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7489 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
7491 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
7493 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7494 INT nOldCount
= infoPtr
->nItemCount
;
7496 if (nItems
< nOldCount
)
7498 RANGE range
= { nItems
, nOldCount
};
7499 ranges_del(infoPtr
->selectionRanges
, range
);
7500 if (infoPtr
->nFocusedItem
>= nItems
)
7502 LISTVIEW_SetItemFocus(infoPtr
, -1);
7503 SetRectEmpty(&infoPtr
->rcFocus
);
7507 infoPtr
->nItemCount
= nItems
;
7508 LISTVIEW_UpdateScroll(infoPtr
);
7510 /* the flags are valid only in ownerdata report and list modes */
7511 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
7513 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7514 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7516 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7517 LISTVIEW_InvalidateList(infoPtr
);
7524 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7525 nFrom
= min(nOldCount
, nItems
);
7526 nTo
= max(nOldCount
, nItems
);
7528 if (uView
== LVS_REPORT
)
7531 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7532 rcErase
.right
= infoPtr
->nItemWidth
;
7533 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7534 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7535 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7536 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7540 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7542 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7543 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7544 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7545 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7546 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7547 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7548 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7550 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7552 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7553 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7554 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7555 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7556 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7562 /* According to MSDN for non-LVS_OWNERDATA this is just
7563 * a performance issue. The control allocates its internal
7564 * data structures for the number of items specified. It
7565 * cuts down on the number of memory allocations. Therefore
7566 * we will just issue a WARN here
7568 WARN("for non-ownerdata performance option not implemented.\n");
7576 * Sets the position of an item.
7579 * [I] infoPtr : valid pointer to the listview structure
7580 * [I] nItem : item index
7581 * [I] pt : coordinate
7587 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7589 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7592 TRACE("(nItem=%d, &pt=%s\n", nItem
, wine_dbgstr_point(&pt
));
7594 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7595 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7597 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7599 /* This point value seems to be an undocumented feature.
7600 * The best guess is that it means either at the origin,
7601 * or at true beginning of the list. I will assume the origin. */
7602 if ((pt
.x
== -1) && (pt
.y
== -1))
7605 if (uView
== LVS_ICON
)
7607 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7608 pt
.y
-= ICON_TOP_PADDING
;
7613 infoPtr
->bAutoarrange
= FALSE
;
7615 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7620 * Sets the state of one or many items.
7623 * [I] infoPtr : valid pointer to the listview structure
7624 * [I] nItem : item index
7625 * [I] lpLVItem : item or subitem info
7631 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7633 BOOL bResult
= TRUE
;
7636 lvItem
.iItem
= nItem
;
7637 lvItem
.iSubItem
= 0;
7638 lvItem
.mask
= LVIF_STATE
;
7639 lvItem
.state
= lpLVItem
->state
;
7640 lvItem
.stateMask
= lpLVItem
->stateMask
;
7641 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7645 /* apply to all items */
7646 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7647 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7650 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7653 * Update selection mark
7655 * Investigation on windows 2k showed that selection mark was updated
7656 * whenever a new selection was made, but if the selected item was
7657 * unselected it was not updated.
7659 * we are probably still not 100% accurate, but this at least sets the
7660 * proper selection mark when it is needed
7663 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7664 (infoPtr
->nSelectionMark
== -1))
7667 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7669 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7671 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7673 infoPtr
->nSelectionMark
= i
;
7677 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7679 infoPtr
->nSelectionMark
= i
;
7690 * Sets the text of an item or subitem.
7693 * [I] hwnd : window handle
7694 * [I] nItem : item index
7695 * [I] lpLVItem : item or subitem info
7696 * [I] isW : TRUE if input is Unicode
7702 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7706 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7708 lvItem
.iItem
= nItem
;
7709 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7710 lvItem
.mask
= LVIF_TEXT
;
7711 lvItem
.pszText
= lpLVItem
->pszText
;
7712 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7714 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7716 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7721 * Set item index that marks the start of a multiple selection.
7724 * [I] infoPtr : valid pointer to the listview structure
7725 * [I] nIndex : index
7728 * Index number or -1 if there is no selection mark.
7730 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7732 INT nOldIndex
= infoPtr
->nSelectionMark
;
7734 TRACE("(nIndex=%d)\n", nIndex
);
7736 infoPtr
->nSelectionMark
= nIndex
;
7743 * Sets the text background color.
7746 * [I] infoPtr : valid pointer to the listview structure
7747 * [I] clrTextBk : text background color
7753 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7755 TRACE("(clrTextBk=%x)\n", clrTextBk
);
7757 if (infoPtr
->clrTextBk
!= clrTextBk
)
7759 infoPtr
->clrTextBk
= clrTextBk
;
7760 LISTVIEW_InvalidateList(infoPtr
);
7768 * Sets the text foreground color.
7771 * [I] infoPtr : valid pointer to the listview structure
7772 * [I] clrText : text color
7778 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7780 TRACE("(clrText=%x)\n", clrText
);
7782 if (infoPtr
->clrText
!= clrText
)
7784 infoPtr
->clrText
= clrText
;
7785 LISTVIEW_InvalidateList(infoPtr
);
7793 * Determines which listview item is located at the specified position.
7796 * [I] infoPtr : valid pointer to the listview structure
7797 * [I] hwndNewToolTip : handle to new ToolTip
7802 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7804 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7805 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7806 return hwndOldToolTip
;
7811 * sets the Unicode character format flag for the control
7813 * [I] infoPtr :valid pointer to the listview structure
7814 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7817 * Old Unicode Format
7819 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL fUnicode
)
7821 BOOL rc
= infoPtr
->notifyFormat
;
7822 infoPtr
->notifyFormat
= (fUnicode
)?NFR_UNICODE
:NFR_ANSI
;
7826 /* LISTVIEW_SetWorkAreas */
7830 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
7833 * [I] first : pointer to first ITEM_INFO to compare
7834 * [I] second : pointer to second ITEM_INFO to compare
7835 * [I] lParam : HWND of control
7838 * if first comes before second : negative
7839 * if first comes after second : positive
7840 * if first and second are equivalent : zero
7842 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7844 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7845 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
7846 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
7848 /* Forward the call to the client defined callback */
7849 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7854 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
7857 * [I] first : pointer to first ITEM_INFO to compare
7858 * [I] second : pointer to second ITEM_INFO to compare
7859 * [I] lParam : HWND of control
7862 * if first comes before second : negative
7863 * if first comes after second : positive
7864 * if first and second are equivalent : zero
7866 static INT WINAPI
LISTVIEW_CallBackCompareEx(LPVOID first
, LPVOID second
, LPARAM lParam
)
7868 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7869 INT first_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, first
);
7870 INT second_idx
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, second
);
7872 /* Forward the call to the client defined callback */
7873 return (infoPtr
->pfnCompare
)( first_idx
, second_idx
, infoPtr
->lParamSort
);
7878 * Sorts the listview items.
7881 * [I] infoPtr : valid pointer to the listview structure
7882 * [I] pfnCompare : application-defined value
7883 * [I] lParamSort : pointer to comparison callback
7884 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
7890 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
,
7891 LPARAM lParamSort
, BOOL IsEx
)
7893 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7896 LPVOID selectionMarkItem
= NULL
;
7897 LPVOID focusedItem
= NULL
;
7900 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7902 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7904 if (!pfnCompare
) return FALSE
;
7905 if (!infoPtr
->hdpaItems
) return FALSE
;
7907 /* if there are 0 or 1 items, there is no need to sort */
7908 if (infoPtr
->nItemCount
< 2) return TRUE
;
7910 /* clear selection */
7911 ranges_clear(infoPtr
->selectionRanges
);
7913 /* save selection mark and focused item */
7914 if (infoPtr
->nSelectionMark
>= 0)
7915 selectionMarkItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
);
7916 if (infoPtr
->nFocusedItem
>= 0)
7917 focusedItem
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7919 infoPtr
->pfnCompare
= pfnCompare
;
7920 infoPtr
->lParamSort
= lParamSort
;
7922 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompareEx
, (LPARAM
)infoPtr
);
7924 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7926 /* restore selection ranges */
7927 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7929 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7930 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
7932 if (lpItem
->state
& LVIS_SELECTED
)
7933 ranges_additem(infoPtr
->selectionRanges
, i
);
7935 /* restore selection mark and focused item */
7936 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7937 infoPtr
->nFocusedItem
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, focusedItem
);
7939 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7941 /* refresh the display */
7942 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7943 LISTVIEW_InvalidateList(infoPtr
);
7950 * Update theme handle after a theme change.
7953 * [I] infoPtr : valid pointer to the listview structure
7957 * FAILURE : something else
7959 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
7961 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7962 CloseThemeData(theme
);
7963 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
7969 * Updates an items or rearranges the listview control.
7972 * [I] infoPtr : valid pointer to the listview structure
7973 * [I] nItem : item index
7979 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7981 TRACE("(nItem=%d)\n", nItem
);
7983 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7985 /* rearrange with default alignment style */
7986 if (is_autoarrange(infoPtr
))
7987 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7989 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7996 * Draw the track line at the place defined in the infoPtr structure.
7997 * The line is drawn with a XOR pen so drawing the line for the second time
7998 * in the same place erases the line.
8001 * [I] infoPtr : valid pointer to the listview structure
8007 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
8013 if (infoPtr
->xTrackLine
== -1)
8016 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
8018 hOldPen
= SelectObject(hdc
, GetStockObject(BLACK_PEN
));
8019 oldROP
= SetROP2(hdc
, R2_XORPEN
);
8020 MoveToEx(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
, NULL
);
8021 LineTo(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.bottom
);
8022 SetROP2(hdc
, oldROP
);
8023 SelectObject(hdc
, hOldPen
);
8024 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
8030 * Called when an edit control should be displayed. This function is called after
8031 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8034 * [I] hwnd : Handle to the listview
8035 * [I] uMsg : WM_TIMER (ignored)
8036 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8037 * [I] dwTimer : The elapsed time (ignored)
8042 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
8044 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
8045 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8047 KillTimer(hwnd
, idEvent
);
8048 editItem
->fEnabled
= FALSE
;
8049 /* check if the item is still selected */
8050 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
8051 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
8056 * Creates the listview control - the WM_NCCREATE phase.
8059 * [I] hwnd : window handle
8060 * [I] lpcs : the create parameters
8066 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8068 LISTVIEW_INFO
*infoPtr
;
8071 TRACE("(lpcs=%p)\n", lpcs
);
8073 /* initialize info pointer */
8074 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
8075 if (!infoPtr
) return FALSE
;
8077 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
8079 infoPtr
->hwndSelf
= hwnd
;
8080 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
8081 /* determine the type of structures to use */
8082 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
8083 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8085 /* initialize color information */
8086 infoPtr
->clrBk
= CLR_NONE
;
8087 infoPtr
->clrText
= CLR_DEFAULT
;
8088 infoPtr
->clrTextBk
= CLR_DEFAULT
;
8089 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
8091 /* set default values */
8092 infoPtr
->nFocusedItem
= -1;
8093 infoPtr
->nSelectionMark
= -1;
8094 infoPtr
->nHotItem
= -1;
8095 infoPtr
->bRedraw
= TRUE
;
8096 infoPtr
->bNoItemMetrics
= TRUE
;
8097 infoPtr
->bDoChangeNotify
= TRUE
;
8098 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
8099 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
8100 infoPtr
->nEditLabelItem
= -1;
8101 infoPtr
->dwHoverTime
= -1; /* default system hover time */
8102 infoPtr
->nMeasureItemHeight
= 0;
8103 infoPtr
->xTrackLine
= -1; /* no track line */
8104 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8106 /* get default font (icon title) */
8107 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
8108 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
8109 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
8110 LISTVIEW_SaveTextMetrics(infoPtr
);
8112 /* allocate memory for the data structure */
8113 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
8114 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
8115 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
8116 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
8117 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
8121 DestroyWindow(infoPtr
->hwndHeader
);
8122 ranges_destroy(infoPtr
->selectionRanges
);
8123 DPA_Destroy(infoPtr
->hdpaItems
);
8124 DPA_Destroy(infoPtr
->hdpaPosX
);
8125 DPA_Destroy(infoPtr
->hdpaPosY
);
8126 DPA_Destroy(infoPtr
->hdpaColumns
);
8133 * Creates the listview control - the WM_CREATE phase. Most of the data is
8134 * already set up in LISTVIEW_NCCreate
8137 * [I] hwnd : window handle
8138 * [I] lpcs : the create parameters
8144 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8146 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8147 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
8149 TRACE("(lpcs=%p)\n", lpcs
);
8151 infoPtr
->dwStyle
= lpcs
->style
;
8152 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
8153 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
8155 if ((uView
== LVS_REPORT
) && (lpcs
->style
& WS_VISIBLE
))
8157 if (LISTVIEW_CreateHeader(infoPtr
) < 0) return -1;
8160 infoPtr
->hwndHeader
= 0;
8162 /* init item size to avoid division by 0 */
8163 LISTVIEW_UpdateItemSize (infoPtr
);
8165 if (uView
== LVS_REPORT
)
8167 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
) && (WS_VISIBLE
& lpcs
->style
))
8169 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8171 LISTVIEW_UpdateScroll(infoPtr
);
8174 OpenThemeData(hwnd
, themeClass
);
8176 /* initialize the icon sizes */
8177 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
8178 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
8184 * Destroys the listview control.
8187 * [I] infoPtr : valid pointer to the listview structure
8193 static LRESULT
LISTVIEW_Destroy(const LISTVIEW_INFO
*infoPtr
)
8195 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
8196 CloseThemeData(theme
);
8202 * Enables the listview control.
8205 * [I] infoPtr : valid pointer to the listview structure
8206 * [I] bEnable : specifies whether to enable or disable the window
8212 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
8214 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
8215 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8221 * Erases the background of the listview control.
8224 * [I] infoPtr : valid pointer to the listview structure
8225 * [I] hdc : device context handle
8231 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8235 TRACE("(hdc=%p)\n", hdc
);
8237 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
8239 /* for double buffered controls we need to do this during refresh */
8240 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
8242 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
8248 * Helper function for LISTVIEW_[HV]Scroll *only*.
8249 * Performs vertical/horizontal scrolling by a give amount.
8252 * [I] infoPtr : valid pointer to the listview structure
8253 * [I] dx : amount of horizontal scroll
8254 * [I] dy : amount of vertical scroll
8256 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8258 /* now we can scroll the list */
8259 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
8260 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
8261 /* if we have focus, adjust rect */
8262 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
8263 UpdateWindow(infoPtr
->hwndSelf
);
8268 * Performs vertical scrolling.
8271 * [I] infoPtr : valid pointer to the listview structure
8272 * [I] nScrollCode : scroll code
8273 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8274 * [I] hScrollWnd : scrollbar control window handle
8280 * SB_LINEUP/SB_LINEDOWN:
8281 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8282 * for LVS_REPORT is 1 line
8283 * for LVS_LIST cannot occur
8286 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8287 INT nScrollDiff
, HWND hScrollWnd
)
8289 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8290 INT nOldScrollPos
, nNewScrollPos
;
8291 SCROLLINFO scrollInfo
;
8294 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8295 debugscrollcode(nScrollCode
), nScrollDiff
);
8297 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8299 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8300 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8302 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
8304 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
8306 nOldScrollPos
= scrollInfo
.nPos
;
8307 switch (nScrollCode
)
8313 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
8317 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
8321 nScrollDiff
= -scrollInfo
.nPage
;
8325 nScrollDiff
= scrollInfo
.nPage
;
8328 case SB_THUMBPOSITION
:
8330 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8337 /* quit right away if pos isn't changing */
8338 if (nScrollDiff
== 0) return 0;
8340 /* calculate new position, and handle overflows */
8341 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8342 if (nScrollDiff
> 0) {
8343 if (nNewScrollPos
< nOldScrollPos
||
8344 nNewScrollPos
> scrollInfo
.nMax
)
8345 nNewScrollPos
= scrollInfo
.nMax
;
8347 if (nNewScrollPos
> nOldScrollPos
||
8348 nNewScrollPos
< scrollInfo
.nMin
)
8349 nNewScrollPos
= scrollInfo
.nMin
;
8352 /* set the new position, and reread in case it changed */
8353 scrollInfo
.fMask
= SIF_POS
;
8354 scrollInfo
.nPos
= nNewScrollPos
;
8355 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
8357 /* carry on only if it really changed */
8358 if (nNewScrollPos
== nOldScrollPos
) return 0;
8360 /* now adjust to client coordinates */
8361 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8362 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
8364 /* and scroll the window */
8365 scroll_list(infoPtr
, 0, nScrollDiff
);
8372 * Performs horizontal scrolling.
8375 * [I] infoPtr : valid pointer to the listview structure
8376 * [I] nScrollCode : scroll code
8377 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8378 * [I] hScrollWnd : scrollbar control window handle
8384 * SB_LINELEFT/SB_LINERIGHT:
8385 * for LVS_ICON, LVS_SMALLICON 1 pixel
8386 * for LVS_REPORT is 1 pixel
8387 * for LVS_LIST is 1 column --> which is a 1 because the
8388 * scroll is based on columns not pixels
8391 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8392 INT nScrollDiff
, HWND hScrollWnd
)
8394 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8395 INT nOldScrollPos
, nNewScrollPos
;
8396 SCROLLINFO scrollInfo
;
8398 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8399 debugscrollcode(nScrollCode
), nScrollDiff
);
8401 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8403 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8404 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8406 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
8408 nOldScrollPos
= scrollInfo
.nPos
;
8410 switch (nScrollCode
)
8424 nScrollDiff
= -scrollInfo
.nPage
;
8428 nScrollDiff
= scrollInfo
.nPage
;
8431 case SB_THUMBPOSITION
:
8433 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8440 /* quit right away if pos isn't changing */
8441 if (nScrollDiff
== 0) return 0;
8443 /* calculate new position, and handle overflows */
8444 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8445 if (nScrollDiff
> 0) {
8446 if (nNewScrollPos
< nOldScrollPos
||
8447 nNewScrollPos
> scrollInfo
.nMax
)
8448 nNewScrollPos
= scrollInfo
.nMax
;
8450 if (nNewScrollPos
> nOldScrollPos
||
8451 nNewScrollPos
< scrollInfo
.nMin
)
8452 nNewScrollPos
= scrollInfo
.nMin
;
8455 /* set the new position, and reread in case it changed */
8456 scrollInfo
.fMask
= SIF_POS
;
8457 scrollInfo
.nPos
= nNewScrollPos
;
8458 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
8460 /* carry on only if it really changed */
8461 if (nNewScrollPos
== nOldScrollPos
) return 0;
8463 if(uView
== LVS_REPORT
)
8464 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
8466 /* now adjust to client coordinates */
8467 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8468 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
8470 /* and scroll the window */
8471 scroll_list(infoPtr
, nScrollDiff
, 0);
8476 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
8478 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8479 INT gcWheelDelta
= 0;
8480 INT pulScrollLines
= 3;
8481 SCROLLINFO scrollInfo
;
8483 TRACE("(wheelDelta=%d)\n", wheelDelta
);
8485 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
8486 gcWheelDelta
-= wheelDelta
;
8488 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8489 scrollInfo
.fMask
= SIF_POS
;
8496 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8497 * should be fixed in the future.
8499 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
8500 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
8504 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
8506 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
8507 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
8508 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
8513 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
8524 * [I] infoPtr : valid pointer to the listview structure
8525 * [I] nVirtualKey : virtual key
8526 * [I] lKeyData : key data
8531 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
8533 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8534 HWND hwndSelf
= infoPtr
->hwndSelf
;
8536 NMLVKEYDOWN nmKeyDown
;
8538 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
8540 /* send LVN_KEYDOWN notification */
8541 nmKeyDown
.wVKey
= nVirtualKey
;
8542 nmKeyDown
.flags
= 0;
8543 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
8544 if (!IsWindow(hwndSelf
))
8547 switch (nVirtualKey
)
8550 nItem
= infoPtr
->nFocusedItem
;
8551 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8552 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
8556 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
8558 if (!notify(infoPtr
, NM_RETURN
)) return 0;
8559 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
8564 if (infoPtr
->nItemCount
> 0)
8569 if (infoPtr
->nItemCount
> 0)
8570 nItem
= infoPtr
->nItemCount
- 1;
8574 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8578 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8582 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8586 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8590 if (uView
== LVS_REPORT
)
8592 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8593 if (infoPtr
->nFocusedItem
== topidx
)
8594 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
8599 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
8600 * LISTVIEW_GetCountPerRow(infoPtr
);
8601 if(nItem
< 0) nItem
= 0;
8605 if (uView
== LVS_REPORT
)
8607 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8608 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
8609 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
8610 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
8612 nItem
= topidx
+ cnt
- 1;
8615 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
8616 * LISTVIEW_GetCountPerRow(infoPtr
);
8617 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
8621 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
8622 LISTVIEW_KeySelection(infoPtr
, nItem
, nVirtualKey
== VK_SPACE
);
8632 * [I] infoPtr : valid pointer to the listview structure
8637 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8641 /* if we did not have the focus, there's nothing to do */
8642 if (!infoPtr
->bFocus
) return 0;
8644 /* send NM_KILLFOCUS notification */
8645 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
8647 /* if we have a focus rectangle, get rid of it */
8648 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
8650 /* set window focus flag */
8651 infoPtr
->bFocus
= FALSE
;
8653 /* invalidate the selected items before resetting focus flag */
8654 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8661 * Processes double click messages (left mouse button).
8664 * [I] infoPtr : valid pointer to the listview structure
8665 * [I] wKey : key flag
8666 * [I] x,y : mouse coordinate
8671 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8673 LVHITTESTINFO htInfo
;
8675 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8677 /* Cancel the item edition if any */
8678 if (infoPtr
->itemEdit
.fEnabled
)
8680 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
8681 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8684 /* send NM_RELEASEDCAPTURE notification */
8685 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8690 /* send NM_DBLCLK notification */
8691 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8692 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
8694 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8695 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8702 * Processes mouse down messages (left mouse button).
8705 * infoPtr [I ] valid pointer to the listview structure
8706 * wKey [I ] key flag
8707 * x,y [I ] mouse coordinate
8712 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8714 LVHITTESTINFO lvHitTestInfo
;
8715 static BOOL bGroupSelect
= TRUE
;
8716 POINT pt
= { x
, y
};
8719 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8721 /* send NM_RELEASEDCAPTURE notification */
8722 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8724 /* set left button down flag and record the click position */
8725 infoPtr
->bLButtonDown
= TRUE
;
8726 infoPtr
->ptClickPos
= pt
;
8727 infoPtr
->bDragging
= FALSE
;
8729 lvHitTestInfo
.pt
.x
= x
;
8730 lvHitTestInfo
.pt
.y
= y
;
8732 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8733 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
8734 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8736 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8738 toggle_checkbox_state(infoPtr
, nItem
);
8742 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8744 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8745 infoPtr
->nEditLabelItem
= nItem
;
8747 LISTVIEW_SetSelection(infoPtr
, nItem
);
8751 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8755 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
8756 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8757 infoPtr
->nSelectionMark
= nItem
;
8763 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8764 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8766 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8767 infoPtr
->nSelectionMark
= nItem
;
8770 else if (wKey
& MK_CONTROL
)
8774 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8776 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8777 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8778 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8779 infoPtr
->nSelectionMark
= nItem
;
8781 else if (wKey
& MK_SHIFT
)
8783 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8787 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8788 infoPtr
->nEditLabelItem
= nItem
;
8790 /* set selection (clears other pre-existing selections) */
8791 LISTVIEW_SetSelection(infoPtr
, nItem
);
8795 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
8796 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
8800 /* remove all selections */
8801 LISTVIEW_DeselectAll(infoPtr
);
8810 * Processes mouse up messages (left mouse button).
8813 * infoPtr [I ] valid pointer to the listview structure
8814 * wKey [I ] key flag
8815 * x,y [I ] mouse coordinate
8820 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8822 LVHITTESTINFO lvHitTestInfo
;
8824 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8826 if (!infoPtr
->bLButtonDown
) return 0;
8828 lvHitTestInfo
.pt
.x
= x
;
8829 lvHitTestInfo
.pt
.y
= y
;
8831 /* send NM_CLICK notification */
8832 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8833 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
8835 /* set left button flag */
8836 infoPtr
->bLButtonDown
= FALSE
;
8838 if (infoPtr
->bDragging
)
8840 infoPtr
->bDragging
= FALSE
;
8844 /* if we clicked on a selected item, edit the label */
8845 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8847 /* we want to make sure the user doesn't want to do a double click. So we will
8848 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8850 infoPtr
->itemEdit
.fEnabled
= TRUE
;
8851 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
8852 SetTimer(infoPtr
->hwndSelf
,
8853 (UINT_PTR
)&infoPtr
->itemEdit
,
8854 GetDoubleClickTime(),
8855 LISTVIEW_DelayedEditItem
);
8858 if (!infoPtr
->bFocus
)
8859 SetFocus(infoPtr
->hwndSelf
);
8866 * Destroys the listview control (called after WM_DESTROY).
8869 * [I] infoPtr : valid pointer to the listview structure
8874 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8878 /* delete all items */
8879 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
8881 /* destroy data structure */
8882 DPA_Destroy(infoPtr
->hdpaItems
);
8883 DPA_Destroy(infoPtr
->hdpaPosX
);
8884 DPA_Destroy(infoPtr
->hdpaPosY
);
8885 DPA_Destroy(infoPtr
->hdpaColumns
);
8886 ranges_destroy(infoPtr
->selectionRanges
);
8888 /* destroy image lists */
8889 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8891 if (infoPtr
->himlNormal
)
8892 ImageList_Destroy(infoPtr
->himlNormal
);
8893 if (infoPtr
->himlSmall
)
8894 ImageList_Destroy(infoPtr
->himlSmall
);
8895 if (infoPtr
->himlState
)
8896 ImageList_Destroy(infoPtr
->himlState
);
8899 /* destroy font, bkgnd brush */
8901 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8902 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8904 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8906 /* free listview info pointer*/
8914 * Handles notifications from header.
8917 * [I] infoPtr : valid pointer to the listview structure
8918 * [I] nCtrlId : control identifier
8919 * [I] lpnmh : notification information
8924 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8926 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8927 HWND hwndSelf
= infoPtr
->hwndSelf
;
8929 TRACE("(lpnmh=%p)\n", lpnmh
);
8931 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8933 switch (lpnmh
->hdr
.code
)
8938 COLUMN_INFO
*lpColumnInfo
;
8942 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8945 /* remove the old line (if any) */
8946 LISTVIEW_DrawTrackLine(infoPtr
);
8948 /* compute & draw the new line */
8949 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8950 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
8951 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8952 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
8953 LISTVIEW_DrawTrackLine(infoPtr
);
8959 /* remove the track line (if any) */
8960 LISTVIEW_DrawTrackLine(infoPtr
);
8961 infoPtr
->xTrackLine
= -1;
8965 FIXME("Changing column order not implemented\n");
8968 case HDN_ITEMCHANGINGW
:
8969 case HDN_ITEMCHANGINGA
:
8970 return notify_forward_header(infoPtr
, lpnmh
);
8972 case HDN_ITEMCHANGEDW
:
8973 case HDN_ITEMCHANGEDA
:
8975 COLUMN_INFO
*lpColumnInfo
;
8978 notify_forward_header(infoPtr
, lpnmh
);
8979 if (!IsWindow(hwndSelf
))
8982 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8986 hdi
.mask
= HDI_WIDTH
;
8987 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, &hdi
)) return 0;
8991 cxy
= lpnmh
->pitem
->cxy
;
8993 /* determine how much we change since the last know position */
8994 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8995 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8998 lpColumnInfo
->rcHeader
.right
+= dx
;
8999 if (lpnmh
->iItem
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
9000 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
9003 /* only needs to update the scrolls */
9004 infoPtr
->nItemWidth
+= dx
;
9005 LISTVIEW_UpdateScroll(infoPtr
);
9007 LISTVIEW_UpdateItemSize(infoPtr
);
9008 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
9011 RECT rcCol
= lpColumnInfo
->rcHeader
;
9013 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
9014 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
9016 rcCol
.top
= infoPtr
->rcList
.top
;
9017 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
9019 /* resizing left-aligned columns leaves most of the left side untouched */
9020 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
9022 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
9025 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
9028 /* when shrinking the last column clear the now unused field */
9029 if (lpnmh
->iItem
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1 && dx
< 0)
9032 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
9038 case HDN_ITEMCLICKW
:
9039 case HDN_ITEMCLICKA
:
9041 /* Handle sorting by Header Column */
9044 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
9046 nmlv
.iSubItem
= lpnmh
->iItem
;
9047 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
9048 notify_forward_header(infoPtr
, lpnmh
);
9052 case HDN_DIVIDERDBLCLICKW
:
9053 case HDN_DIVIDERDBLCLICKA
:
9054 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
9063 * Paint non-client area of control.
9066 * [I] infoPtr : valid pointer to the listview structureof the sender
9067 * [I] region : update region
9070 * TRUE - frame was painted
9071 * FALSE - call default window proc
9073 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
9075 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
9079 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
9080 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
9082 if (!theme
) return FALSE
;
9084 GetWindowRect(infoPtr
->hwndSelf
, &r
);
9086 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
9087 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
9088 if (region
!= (HRGN
)1)
9089 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
9090 OffsetRect(&r
, -r
.left
, -r
.top
);
9092 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
9093 OffsetRect(&r
, -r
.left
, -r
.top
);
9095 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
9096 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
9097 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
9098 ReleaseDC(infoPtr
->hwndSelf
, dc
);
9100 /* Call default proc to get the scrollbars etc. painted */
9101 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
9108 * Determines the type of structure to use.
9111 * [I] infoPtr : valid pointer to the listview structureof the sender
9112 * [I] hwndFrom : listview window handle
9113 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9118 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
9120 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
9122 if (nCommand
== NF_REQUERY
)
9123 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9125 return infoPtr
->notifyFormat
;
9130 * Paints/Repaints the listview control.
9133 * [I] infoPtr : valid pointer to the listview structure
9134 * [I] hdc : device context handle
9139 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9141 TRACE("(hdc=%p)\n", hdc
);
9143 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
9145 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9147 infoPtr
->bNoItemMetrics
= FALSE
;
9148 LISTVIEW_UpdateItemSize(infoPtr
);
9149 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
9150 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9151 LISTVIEW_UpdateScroll(infoPtr
);
9154 if (infoPtr
->hwndHeader
) UpdateWindow(infoPtr
->hwndHeader
);
9157 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
9162 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
9164 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
9165 EndPaint(infoPtr
->hwndSelf
, &ps
);
9174 * Paints/Repaints the listview control.
9177 * [I] infoPtr : valid pointer to the listview structure
9178 * [I] hdc : device context handle
9179 * [I] options : drawing options
9184 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
9186 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc
, options
);
9188 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
9191 if (options
& PRF_ERASEBKGND
)
9192 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
9194 if (options
& PRF_CLIENT
)
9195 LISTVIEW_Paint(infoPtr
, hdc
);
9203 * Processes double click messages (right mouse button).
9206 * [I] infoPtr : valid pointer to the listview structure
9207 * [I] wKey : key flag
9208 * [I] x,y : mouse coordinate
9213 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9215 LVHITTESTINFO lvHitTestInfo
;
9217 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9219 /* send NM_RELEASEDCAPTURE notification */
9220 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9222 /* send NM_RDBLCLK notification */
9223 lvHitTestInfo
.pt
.x
= x
;
9224 lvHitTestInfo
.pt
.y
= y
;
9225 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9226 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
9233 * Processes mouse down messages (right mouse button).
9236 * [I] infoPtr : valid pointer to the listview structure
9237 * [I] wKey : key flag
9238 * [I] x,y : mouse coordinate
9243 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9245 LVHITTESTINFO lvHitTestInfo
;
9248 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9250 /* send NM_RELEASEDCAPTURE notification */
9251 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9253 /* make sure the listview control window has the focus */
9254 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
9256 /* set right button down flag */
9257 infoPtr
->bRButtonDown
= TRUE
;
9259 /* determine the index of the selected item */
9260 lvHitTestInfo
.pt
.x
= x
;
9261 lvHitTestInfo
.pt
.y
= y
;
9262 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
9264 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
9266 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
9267 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
9268 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
9269 LISTVIEW_SetSelection(infoPtr
, nItem
);
9273 LISTVIEW_DeselectAll(infoPtr
);
9281 * Processes mouse up messages (right mouse button).
9284 * [I] infoPtr : valid pointer to the listview structure
9285 * [I] wKey : key flag
9286 * [I] x,y : mouse coordinate
9291 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9293 LVHITTESTINFO lvHitTestInfo
;
9296 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9298 if (!infoPtr
->bRButtonDown
) return 0;
9300 /* set button flag */
9301 infoPtr
->bRButtonDown
= FALSE
;
9303 /* Send NM_RCLICK notification */
9304 lvHitTestInfo
.pt
.x
= x
;
9305 lvHitTestInfo
.pt
.y
= y
;
9306 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9307 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
9309 /* Change to screen coordinate for WM_CONTEXTMENU */
9310 pt
= lvHitTestInfo
.pt
;
9311 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
9313 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9314 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
9315 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
9326 * [I] infoPtr : valid pointer to the listview structure
9327 * [I] hwnd : window handle of window containing the cursor
9328 * [I] nHittest : hit-test code
9329 * [I] wMouseMsg : ideintifier of the mouse message
9332 * TRUE if cursor is set
9335 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
9337 LVHITTESTINFO lvHitTestInfo
;
9339 if(!(LISTVIEW_isHotTracking(infoPtr
))) return FALSE
;
9341 if(!infoPtr
->hHotCursor
) return FALSE
;
9343 GetCursorPos(&lvHitTestInfo
.pt
);
9344 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
9346 SetCursor(infoPtr
->hHotCursor
);
9356 * [I] infoPtr : valid pointer to the listview structure
9357 * [I] hwndLoseFocus : handle of previously focused window
9362 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
9364 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
9366 /* if we have the focus already, there's nothing to do */
9367 if (infoPtr
->bFocus
) return 0;
9369 /* send NM_SETFOCUS notification */
9370 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
9372 /* set window focus flag */
9373 infoPtr
->bFocus
= TRUE
;
9375 /* put the focus rect back on */
9376 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
9378 /* redraw all visible selected items */
9379 LISTVIEW_InvalidateSelectedItems(infoPtr
);
9389 * [I] infoPtr : valid pointer to the listview structure
9390 * [I] fRedraw : font handle
9391 * [I] fRedraw : redraw flag
9396 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
9398 HFONT oldFont
= infoPtr
->hFont
;
9400 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
9402 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
9403 if (infoPtr
->hFont
== oldFont
) return 0;
9405 LISTVIEW_SaveTextMetrics(infoPtr
);
9407 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
9409 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
9410 LISTVIEW_UpdateSize(infoPtr
);
9411 LISTVIEW_UpdateScroll(infoPtr
);
9414 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
9421 * Message handling for WM_SETREDRAW.
9422 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9425 * [I] infoPtr : valid pointer to the listview structure
9426 * [I] bRedraw: state of redraw flag
9429 * DefWinProc return value
9431 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
9433 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
9435 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9436 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
9438 infoPtr
->bRedraw
= bRedraw
;
9440 if(!bRedraw
) return 0;
9442 if (is_autoarrange(infoPtr
))
9443 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9444 LISTVIEW_UpdateScroll(infoPtr
);
9446 /* despite what the WM_SETREDRAW docs says, apps expect us
9447 * to invalidate the listview here... stupid! */
9448 LISTVIEW_InvalidateList(infoPtr
);
9455 * Resizes the listview control. This function processes WM_SIZE
9456 * messages. At this time, the width and height are not used.
9459 * [I] infoPtr : valid pointer to the listview structure
9460 * [I] Width : new width
9461 * [I] Height : new height
9466 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
9468 RECT rcOld
= infoPtr
->rcList
;
9470 TRACE("(width=%d, height=%d)\n", Width
, Height
);
9472 LISTVIEW_UpdateSize(infoPtr
);
9473 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
9475 /* do not bother with display related stuff if we're not redrawing */
9476 if (!is_redrawing(infoPtr
)) return 0;
9478 if (is_autoarrange(infoPtr
))
9479 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9481 LISTVIEW_UpdateScroll(infoPtr
);
9483 /* refresh all only for lists whose height changed significantly */
9484 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
9485 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
9486 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
9487 LISTVIEW_InvalidateList(infoPtr
);
9494 * Sets the size information.
9497 * [I] infoPtr : valid pointer to the listview structure
9502 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
9504 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9506 TRACE("uView=%d, rcList(old)=%s\n", uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
9508 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
9510 if (uView
== LVS_LIST
)
9512 /* Apparently the "LIST" style is supposed to have the same
9513 * number of items in a column even if there is no scroll bar.
9514 * Since if a scroll bar already exists then the bottom is already
9515 * reduced, only reduce if the scroll bar does not currently exist.
9516 * The "2" is there to mimic the native control. I think it may be
9517 * related to either padding or edges. (GLA 7/2002)
9519 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
9520 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
9521 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
9523 else if (uView
== LVS_REPORT
)
9528 hl
.prc
= &infoPtr
->rcList
;
9530 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9531 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9532 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9533 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9534 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9535 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9537 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
9538 infoPtr
->rcList
.top
+= (infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
) ? 2 : 0;
9541 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
9546 * Processes WM_STYLECHANGED messages.
9549 * [I] infoPtr : valid pointer to the listview structure
9550 * [I] wStyleType : window style type (normal or extended)
9551 * [I] lpss : window style information
9556 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
9557 const STYLESTRUCT
*lpss
)
9559 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
9560 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
9563 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9564 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9566 if (wStyleType
!= GWL_STYLE
) return 0;
9568 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9569 /* or LVS_SINGLESEL */
9570 /* or LVS_SORT{AS,DES}CENDING */
9572 infoPtr
->dwStyle
= lpss
->styleNew
;
9574 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
9575 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
9576 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
9578 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
9579 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
9580 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
9582 if (uNewView
!= uOldView
)
9584 SIZE oldIconSize
= infoPtr
->iconSize
;
9587 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9588 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9590 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9591 SetRectEmpty(&infoPtr
->rcFocus
);
9593 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9594 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
9596 if (uNewView
== LVS_ICON
)
9598 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
9600 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9601 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
9602 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
9605 else if (uNewView
== LVS_REPORT
)
9610 LISTVIEW_CreateHeader( infoPtr
);
9612 hl
.prc
= &infoPtr
->rcList
;
9614 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9615 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9616 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9617 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9620 LISTVIEW_UpdateItemSize(infoPtr
);
9623 if (uNewView
== LVS_REPORT
)
9625 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
9627 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
9629 /* Turn off the header control */
9630 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
9631 TRACE("Hide header control, was 0x%08x\n", style
);
9632 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
9634 /* Turn on the header control */
9635 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
9637 TRACE("Show header control, was 0x%08x\n", style
);
9638 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
9644 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
9645 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
9646 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9648 /* update the size of the client area */
9649 LISTVIEW_UpdateSize(infoPtr
);
9651 /* add scrollbars if needed */
9652 LISTVIEW_UpdateScroll(infoPtr
);
9654 /* invalidate client area + erase background */
9655 LISTVIEW_InvalidateList(infoPtr
);
9662 * Processes WM_STYLECHANGING messages.
9665 * [I] infoPtr : valid pointer to the listview structure
9666 * [I] wStyleType : window style type (normal or extended)
9667 * [I0] lpss : window style information
9672 static INT
LISTVIEW_StyleChanging(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
9675 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9676 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9678 /* don't forward LVS_OWNERDATA only if not already set to */
9679 if ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_OWNERDATA
)
9681 if (lpss
->styleOld
& LVS_OWNERDATA
)
9682 lpss
->styleNew
|= LVS_OWNERDATA
;
9684 lpss
->styleNew
&= ~LVS_OWNERDATA
;
9692 * Processes WM_SHOWWINDOW messages.
9695 * [I] infoPtr : valid pointer to the listview structure
9696 * [I] bShown : window is being shown (FALSE when hidden)
9697 * [I] iStatus : window show status
9702 static LRESULT
LISTVIEW_ShowWindow(LISTVIEW_INFO
*infoPtr
, BOOL bShown
, INT iStatus
)
9704 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9706 /* header delayed creation */
9707 if ((uView
== LVS_REPORT
) && bShown
)
9709 LISTVIEW_CreateHeader(infoPtr
);
9711 if (!(LVS_NOCOLUMNHEADER
& infoPtr
->dwStyle
))
9712 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
9720 * Window procedure of the listview control.
9723 static LRESULT WINAPI
9724 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9726 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9728 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg
, wParam
, lParam
);
9730 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
9731 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9735 case LVM_APPROXIMATEVIEWRECT
:
9736 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
9737 LOWORD(lParam
), HIWORD(lParam
));
9739 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
9741 /* case LVM_CANCELEDITLABEL: */
9743 case LVM_CREATEDRAGIMAGE
:
9744 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9746 case LVM_DELETEALLITEMS
:
9747 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
9749 case LVM_DELETECOLUMN
:
9750 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
9752 case LVM_DELETEITEM
:
9753 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
9755 case LVM_EDITLABELW
:
9756 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
9758 case LVM_EDITLABELA
:
9759 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
9761 /* case LVM_ENABLEGROUPVIEW: */
9763 case LVM_ENSUREVISIBLE
:
9764 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
9767 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
9770 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
9772 case LVM_GETBKCOLOR
:
9773 return infoPtr
->clrBk
;
9775 /* case LVM_GETBKIMAGE: */
9777 case LVM_GETCALLBACKMASK
:
9778 return infoPtr
->uCallbackMask
;
9780 case LVM_GETCOLUMNA
:
9781 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9783 case LVM_GETCOLUMNW
:
9784 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9786 case LVM_GETCOLUMNORDERARRAY
:
9787 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9789 case LVM_GETCOLUMNWIDTH
:
9790 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
9792 case LVM_GETCOUNTPERPAGE
:
9793 return LISTVIEW_GetCountPerPage(infoPtr
);
9795 case LVM_GETEDITCONTROL
:
9796 return (LRESULT
)infoPtr
->hwndEdit
;
9798 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
9799 return infoPtr
->dwLvExStyle
;
9801 /* case LVM_GETGROUPINFO: */
9803 /* case LVM_GETGROUPMETRICS: */
9806 return (LRESULT
)infoPtr
->hwndHeader
;
9808 case LVM_GETHOTCURSOR
:
9809 return (LRESULT
)infoPtr
->hHotCursor
;
9811 case LVM_GETHOTITEM
:
9812 return infoPtr
->nHotItem
;
9814 case LVM_GETHOVERTIME
:
9815 return infoPtr
->dwHoverTime
;
9817 case LVM_GETIMAGELIST
:
9818 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
9820 /* case LVM_GETINSERTMARK: */
9822 /* case LVM_GETINSERTMARKCOLOR: */
9824 /* case LVM_GETINSERTMARKRECT: */
9826 case LVM_GETISEARCHSTRINGA
:
9827 case LVM_GETISEARCHSTRINGW
:
9828 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9832 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9835 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9837 case LVM_GETITEMCOUNT
:
9838 return infoPtr
->nItemCount
;
9840 case LVM_GETITEMPOSITION
:
9841 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9843 case LVM_GETITEMRECT
:
9844 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
9846 case LVM_GETITEMSPACING
:
9847 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
9849 case LVM_GETITEMSTATE
:
9850 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
9852 case LVM_GETITEMTEXTA
:
9853 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9855 case LVM_GETITEMTEXTW
:
9856 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9858 case LVM_GETNEXTITEM
:
9859 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
9861 case LVM_GETNUMBEROFWORKAREAS
:
9862 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9866 if (!lParam
) return FALSE
;
9867 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
||
9868 (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
) return FALSE
;
9869 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
9872 /* case LVM_GETOUTLINECOLOR: */
9874 /* case LVM_GETSELECTEDCOLUMN: */
9876 case LVM_GETSELECTEDCOUNT
:
9877 return LISTVIEW_GetSelectedCount(infoPtr
);
9879 case LVM_GETSELECTIONMARK
:
9880 return infoPtr
->nSelectionMark
;
9882 case LVM_GETSTRINGWIDTHA
:
9883 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9885 case LVM_GETSTRINGWIDTHW
:
9886 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9888 case LVM_GETSUBITEMRECT
:
9889 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
9891 case LVM_GETTEXTBKCOLOR
:
9892 return infoPtr
->clrTextBk
;
9894 case LVM_GETTEXTCOLOR
:
9895 return infoPtr
->clrText
;
9897 /* case LVM_GETTILEINFO: */
9899 /* case LVM_GETTILEVIEWINFO: */
9901 case LVM_GETTOOLTIPS
:
9902 if( !infoPtr
->hwndToolTip
)
9903 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9904 return (LRESULT
)infoPtr
->hwndToolTip
;
9906 case LVM_GETTOPINDEX
:
9907 return LISTVIEW_GetTopIndex(infoPtr
);
9909 case LVM_GETUNICODEFORMAT
:
9910 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
9912 /* case LVM_GETVIEW: */
9914 case LVM_GETVIEWRECT
:
9915 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9917 case LVM_GETWORKAREAS
:
9918 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9921 /* case LVM_HASGROUP: */
9924 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9926 case LVM_INSERTCOLUMNA
:
9927 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9929 case LVM_INSERTCOLUMNW
:
9930 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9932 /* case LVM_INSERTGROUP: */
9934 /* case LVM_INSERTGROUPSORTED: */
9936 case LVM_INSERTITEMA
:
9937 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9939 case LVM_INSERTITEMW
:
9940 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9942 /* case LVM_INSERTMARKHITTEST: */
9944 /* case LVM_ISGROUPVIEWENABLED: */
9946 /* case LVM_MAPIDTOINDEX: */
9948 /* case LVM_MAPINDEXTOID: */
9950 /* case LVM_MOVEGROUP: */
9952 /* case LVM_MOVEITEMTOGROUP: */
9954 case LVM_REDRAWITEMS
:
9955 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9957 /* case LVM_REMOVEALLGROUPS: */
9959 /* case LVM_REMOVEGROUP: */
9962 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9964 case LVM_SETBKCOLOR
:
9965 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9967 /* case LVM_SETBKIMAGE: */
9969 case LVM_SETCALLBACKMASK
:
9970 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9973 case LVM_SETCOLUMNA
:
9974 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9976 case LVM_SETCOLUMNW
:
9977 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9979 case LVM_SETCOLUMNORDERARRAY
:
9980 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9982 case LVM_SETCOLUMNWIDTH
:
9983 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9985 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9986 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9988 /* case LVM_SETGROUPINFO: */
9990 /* case LVM_SETGROUPMETRICS: */
9992 case LVM_SETHOTCURSOR
:
9993 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9995 case LVM_SETHOTITEM
:
9996 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9998 case LVM_SETHOVERTIME
:
9999 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
10001 case LVM_SETICONSPACING
:
10002 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
10004 case LVM_SETIMAGELIST
:
10005 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
10007 /* case LVM_SETINFOTIP: */
10009 /* case LVM_SETINSERTMARK: */
10011 /* case LVM_SETINSERTMARKCOLOR: */
10014 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
10017 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
10019 case LVM_SETITEMCOUNT
:
10020 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
10022 case LVM_SETITEMPOSITION
:
10025 pt
.x
= (short)LOWORD(lParam
);
10026 pt
.y
= (short)HIWORD(lParam
);
10027 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
10030 case LVM_SETITEMPOSITION32
:
10031 if (lParam
== 0) return FALSE
;
10032 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
10034 case LVM_SETITEMSTATE
:
10035 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
10037 case LVM_SETITEMTEXTA
:
10038 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
10040 case LVM_SETITEMTEXTW
:
10041 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
10043 /* case LVM_SETOUTLINECOLOR: */
10045 /* case LVM_SETSELECTEDCOLUMN: */
10047 case LVM_SETSELECTIONMARK
:
10048 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
10050 case LVM_SETTEXTBKCOLOR
:
10051 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
10053 case LVM_SETTEXTCOLOR
:
10054 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
10056 /* case LVM_SETTILEINFO: */
10058 /* case LVM_SETTILEVIEWINFO: */
10060 /* case LVM_SETTILEWIDTH: */
10062 case LVM_SETTOOLTIPS
:
10063 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
10065 case LVM_SETUNICODEFORMAT
:
10066 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
10068 /* case LVM_SETVIEW: */
10070 /* case LVM_SETWORKAREAS: */
10072 /* case LVM_SORTGROUPS: */
10074 case LVM_SORTITEMS
:
10075 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
, FALSE
);
10077 case LVM_SORTITEMSEX
:
10078 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
, TRUE
);
10080 case LVM_SUBITEMHITTEST
:
10081 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
10084 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
10087 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
10090 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
10093 return LISTVIEW_NCCreate(hwnd
, (LPCREATESTRUCTW
)lParam
);
10096 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
10099 return LISTVIEW_Destroy(infoPtr
);
10102 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
10104 case WM_ERASEBKGND
:
10105 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
10107 case WM_GETDLGCODE
:
10108 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
10111 return (LRESULT
)infoPtr
->hFont
;
10114 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10117 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
10120 return LISTVIEW_KillFocus(infoPtr
);
10122 case WM_LBUTTONDBLCLK
:
10123 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10125 case WM_LBUTTONDOWN
:
10126 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10129 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10132 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10134 case WM_MOUSEHOVER
:
10135 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10138 return LISTVIEW_NCDestroy(infoPtr
);
10141 if (LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
))
10146 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
10147 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
10150 case WM_NOTIFYFORMAT
:
10151 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
10153 case WM_PRINTCLIENT
:
10154 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
10157 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
10159 case WM_RBUTTONDBLCLK
:
10160 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10162 case WM_RBUTTONDOWN
:
10163 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10166 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10169 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
10174 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
10177 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
10180 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
10182 case WM_SHOWWINDOW
:
10183 LISTVIEW_ShowWindow(infoPtr
, (BOOL
)wParam
, (INT
)lParam
);
10184 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10187 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
10189 case WM_STYLECHANGED
:
10190 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
10192 case WM_STYLECHANGING
:
10193 return LISTVIEW_StyleChanging(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
10195 case WM_SYSCOLORCHANGE
:
10196 COMCTL32_RefreshSysColors();
10199 /* case WM_TIMER: */
10200 case WM_THEMECHANGED
:
10201 return LISTVIEW_ThemeChanged(infoPtr
);
10204 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10206 case WM_MOUSEWHEEL
:
10207 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
10208 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10209 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
10211 case WM_WINDOWPOSCHANGED
:
10212 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
10214 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
10215 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
10216 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
10218 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
10220 MEASUREITEMSTRUCT mis
;
10221 mis
.CtlType
= ODT_LISTVIEW
;
10222 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
10226 mis
.itemHeight
= infoPtr
->nItemHeight
;
10227 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
10228 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
10229 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
10232 LISTVIEW_UpdateSize(infoPtr
);
10233 LISTVIEW_UpdateScroll(infoPtr
);
10235 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10237 /* case WM_WININICHANGE: */
10240 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
10241 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
10244 /* call default window procedure */
10245 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10252 * Registers the window class.
10260 void LISTVIEW_Register(void)
10262 WNDCLASSW wndClass
;
10264 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
10265 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
10266 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
10267 wndClass
.cbClsExtra
= 0;
10268 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
10269 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
10270 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
10271 wndClass
.lpszClassName
= WC_LISTVIEWW
;
10272 RegisterClassW(&wndClass
);
10277 * Unregisters the window class.
10285 void LISTVIEW_Unregister(void)
10287 UnregisterClassW(WC_LISTVIEWW
, NULL
);
10292 * Handle any WM_COMMAND messages
10295 * [I] infoPtr : valid pointer to the listview structure
10296 * [I] wParam : the first message parameter
10297 * [I] lParam : the second message parameter
10302 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10304 switch (HIWORD(wParam
))
10309 * Adjust the edit window size
10311 WCHAR buffer
[1024];
10312 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
10313 HFONT hFont
, hOldFont
= 0;
10317 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
10318 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
10319 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
10321 /* Select font to get the right dimension of the string */
10322 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
10325 hOldFont
= SelectObject(hdc
, hFont
);
10328 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
10330 TEXTMETRICW textMetric
;
10332 /* Add Extra spacing for the next character */
10333 GetTextMetricsW(hdc
, &textMetric
);
10334 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10342 rect
.bottom
- rect
.top
,
10343 SWP_DRAWFRAME
|SWP_NOMOVE
);
10346 SelectObject(hdc
, hOldFont
);
10348 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
10354 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
10363 * Subclassed edit control windproc function
10366 * [I] hwnd : the edit window handle
10367 * [I] uMsg : the message that is to be processed
10368 * [I] wParam : first message parameter
10369 * [I] lParam : second message parameter
10370 * [I] isW : TRUE if input is Unicode
10375 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
10377 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
10378 BOOL cancel
= FALSE
;
10380 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10381 hwnd
, uMsg
, wParam
, lParam
, isW
);
10385 case WM_GETDLGCODE
:
10386 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
10393 WNDPROC editProc
= infoPtr
->EditWndProc
;
10394 infoPtr
->EditWndProc
= 0;
10395 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
10396 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10400 if (VK_ESCAPE
== (INT
)wParam
)
10405 else if (VK_RETURN
== (INT
)wParam
)
10409 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10412 /* kill the edit */
10413 if (infoPtr
->hwndEdit
)
10415 LPWSTR buffer
= NULL
;
10417 infoPtr
->hwndEdit
= 0;
10420 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
10424 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
10426 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
10427 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
10431 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
10436 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
10442 * Subclassed edit control Unicode windproc function
10445 * [I] hwnd : the edit window handle
10446 * [I] uMsg : the message that is to be processed
10447 * [I] wParam : first message parameter
10448 * [I] lParam : second message parameter
10452 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10454 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
10459 * Subclassed edit control ANSI windproc function
10462 * [I] hwnd : the edit window handle
10463 * [I] uMsg : the message that is to be processed
10464 * [I] wParam : first message parameter
10465 * [I] lParam : second message parameter
10469 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10471 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
10476 * Creates a subclassed edit control
10479 * [I] infoPtr : valid pointer to the listview structure
10480 * [I] text : initial text for the edit
10481 * [I] style : the window style
10482 * [I] isW : TRUE if input is Unicode
10486 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
10487 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
10489 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
10494 TEXTMETRICW textMetric
;
10495 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
10497 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
10499 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
10500 hdc
= GetDC(infoPtr
->hwndSelf
);
10502 /* Select the font to get appropriate metric dimensions */
10503 if(infoPtr
->hFont
!= 0)
10504 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
10506 /*Get String Length in pixels */
10507 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
10509 /*Add Extra spacing for the next character */
10510 GetTextMetricsW(hdc
, &textMetric
);
10511 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10513 if(infoPtr
->hFont
!= 0)
10514 SelectObject(hdc
, hOldFont
);
10516 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
10518 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10520 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10522 if (!hedit
) return 0;
10524 infoPtr
->EditWndProc
= (WNDPROC
)
10525 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
10526 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
10528 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);