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_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
94 * -- LVS_EX_HEADERDRAGDROP
97 * -- LVS_EX_MULTIWORKAREAS
98 * -- LVS_EX_ONECLICKACTIVATE
100 * -- LVS_EX_SIMPLESELECT
101 * -- LVS_EX_TRACKSELECT
102 * -- LVS_EX_TWOCLICKACTIVATE
103 * -- LVS_EX_UNDERLINECOLD
104 * -- LVS_EX_UNDERLINEHOT
107 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
110 * -- LVN_MARQUEEBEGIN
117 * -- LVM_CANCELEDITLABEL
118 * -- LVM_ENABLEGROUPVIEW
119 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
120 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
121 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
122 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
123 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
124 * -- LVM_GETINSERTMARKRECT
125 * -- LVM_GETNUMBEROFWORKAREAS
126 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
127 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
128 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
129 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
130 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
131 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
132 * -- LVM_GETVIEW, LVM_SETVIEW
133 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
134 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
135 * -- LVM_INSERTGROUPSORTED
136 * -- LVM_INSERTMARKHITTEST
137 * -- LVM_ISGROUPVIEWENABLED
138 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
140 * -- LVM_MOVEITEMTOGROUP
142 * -- LVM_SETTILEWIDTH
147 * -- ListView_GetCheckSate, ListView_SetCheckState
148 * -- ListView_GetHoverTime, ListView_SetHoverTime
149 * -- ListView_GetISearchString
150 * -- ListView_GetNumberOfWorkAreas
151 * -- ListView_GetOrigin
152 * -- ListView_GetTextBkColor
153 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
154 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
155 * -- ListView_SortItemsEx
160 * Known differences in message stream from native control (not known if
161 * these differences cause problems):
162 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
163 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
164 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
165 * processing for "USEDOUBLECLICKTIME".
169 #include "wine/port.h"
184 #include "commctrl.h"
185 #include "comctl32.h"
188 #include "wine/debug.h"
189 #include "wine/unicode.h"
191 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
193 /* make sure you set this to 0 for production use! */
194 #define DEBUG_RANGES 1
196 typedef struct tagCOLUMN_INFO
198 RECT rcHeader
; /* tracks the header's rectangle */
199 int fmt
; /* same as LVCOLUMN.fmt */
202 typedef struct tagITEMHDR
206 } ITEMHDR
, *LPITEMHDR
;
208 typedef struct tagSUBITEM_INFO
214 typedef struct tagITEM_INFO
222 typedef struct tagRANGE
228 typedef struct tagRANGES
233 typedef struct tagITERATOR
242 typedef struct tagDELAYED_ITEM_EDIT
248 typedef struct tagLISTVIEW_INFO
255 HIMAGELIST himlNormal
;
256 HIMAGELIST himlSmall
;
257 HIMAGELIST himlState
;
261 POINT ptClickPos
; /* point where the user clicked */
262 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
265 RANGES selectionRanges
;
270 RECT rcList
; /* This rectangle is really the window
271 * client rectangle possibly reduced by the
272 * horizontal scroll bar and/or header - see
273 * LISTVIEW_UpdateSize. This rectangle offset
274 * by the LISTVIEW_GetOrigin value is in
275 * client coordinates */
284 INT ntmHeight
; /* Some cached metrics of the font used */
285 INT ntmMaxCharWidth
; /* by the listview to draw items */
287 BOOL bRedraw
; /* Turns on/off repaints & invalidations */
288 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
290 BOOL bDoChangeNotify
; /* send change notification messages? */
293 DWORD dwStyle
; /* the cached window GWL_STYLE */
294 DWORD dwLvExStyle
; /* extended listview style */
295 INT nItemCount
; /* the number of items in the list */
296 HDPA hdpaItems
; /* array ITEM_INFO pointers */
297 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
298 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
299 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
300 POINT currIconPos
; /* this is the position next icon will be placed */
301 PFNLVCOMPARE pfnCompare
;
309 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
311 DWORD lastKeyPressTimestamp
;
313 INT nSearchParamLength
;
314 WCHAR szSearchParam
[ MAX_PATH
];
316 INT nMeasureItemHeight
;
317 INT xTrackLine
; /* The x coefficient of the track line or -1 if none */
318 DELAYED_ITEM_EDIT itemEdit
; /* Pointer to this structure will be the timer ID */
324 /* How many we debug buffer to allocate */
325 #define DEBUG_BUFFERS 20
326 /* The size of a single debug bbuffer */
327 #define DEBUG_BUFFER_SIZE 256
329 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
330 #define SB_INTERNAL -1
332 /* maximum size of a label */
333 #define DISP_TEXT_SIZE 512
335 /* padding for items in list and small icon display modes */
336 #define WIDTH_PADDING 12
338 /* padding for items in list, report and small icon display modes */
339 #define HEIGHT_PADDING 1
341 /* offset of items in report display mode */
342 #define REPORT_MARGINX 2
344 /* padding for icon in large icon display mode
345 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
346 * that HITTEST will see.
347 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
348 * ICON_TOP_PADDING - sum of the two above.
349 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
350 * LABEL_HOR_PADDING - between text and sides of box
351 * LABEL_VERT_PADDING - between bottom of text and end of box
353 * ICON_LR_PADDING - additional width above icon size.
354 * ICON_LR_HALF - half of the above value
356 #define ICON_TOP_PADDING_NOTHITABLE 2
357 #define ICON_TOP_PADDING_HITABLE 2
358 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
359 #define ICON_BOTTOM_PADDING 4
360 #define LABEL_HOR_PADDING 5
361 #define LABEL_VERT_PADDING 7
362 #define ICON_LR_PADDING 16
363 #define ICON_LR_HALF (ICON_LR_PADDING/2)
365 /* default label width for items in list and small icon display modes */
366 #define DEFAULT_LABEL_WIDTH 40
368 /* default column width for items in list display mode */
369 #define DEFAULT_COLUMN_WIDTH 128
371 /* Size of "line" scroll for V & H scrolls */
372 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
374 /* Padding between image and label */
375 #define IMAGE_PADDING 2
377 /* Padding behind the label */
378 #define TRAILING_LABEL_PADDING 12
379 #define TRAILING_HEADER_PADDING 11
381 /* Border for the icon caption */
382 #define CAPTION_BORDER 2
384 /* Standard DrawText flags */
385 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
386 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
387 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
389 /* Image index from state */
390 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
392 /* The time in milliseconds to reset the search in the list */
393 #define KEY_DELAY 450
395 /* Dump the LISTVIEW_INFO structure to the debug channel */
396 #define LISTVIEW_DUMP(iP) do { \
397 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
398 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
399 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
400 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
401 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
402 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
403 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
404 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
405 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
406 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
409 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
412 * forward declarations
414 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
415 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*, INT
, LPRECT
);
416 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*, INT
, LPPOINT
);
417 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*, INT
, LPPOINT
);
418 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*, INT
, LPRECT
);
419 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*, INT
);
420 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*, LPPOINT
);
421 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*, LPRECT
);
422 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
423 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, LVITEMW
*, BOOL
);
424 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*);
425 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
426 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
427 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
428 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*, WPARAM
, LPARAM
);
429 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*, PFNLVCOMPARE
, LPARAM
);
430 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
431 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
);
432 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*, INT
, UINT
);
433 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
434 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
435 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
436 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*);
437 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
438 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
439 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
440 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
442 /******** Text handling functions *************************************/
444 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
445 * text string. The string may be ANSI or Unicode, in which case
446 * the boolean isW tells us the type of the string.
448 * The name of the function tell what type of strings it expects:
449 * W: Unicode, T: ANSI/Unicode - function of isW
452 static inline BOOL
is_textW(LPCWSTR text
)
454 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
457 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
459 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
460 return is_textW(text
);
463 static inline int textlenT(LPCWSTR text
, BOOL isW
)
465 return !is_textT(text
, isW
) ? 0 :
466 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
469 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
472 if (isSrcW
) lstrcpynW(dest
, src
, max
);
473 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
475 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
476 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
479 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
481 LPWSTR wstr
= (LPWSTR
)text
;
483 if (!isW
&& is_textT(text
, isW
))
485 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
486 wstr
= Alloc(len
* sizeof(WCHAR
));
487 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
489 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
493 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
495 if (!isW
&& is_textT(wstr
, isW
)) Free (wstr
);
499 * dest is a pointer to a Unicode string
500 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
502 static BOOL
textsetptrT(LPWSTR
*dest
, LPCWSTR src
, BOOL isW
)
506 if (src
== LPSTR_TEXTCALLBACKW
)
508 if (is_textW(*dest
)) Free(*dest
);
509 *dest
= LPSTR_TEXTCALLBACKW
;
513 LPWSTR pszText
= textdupTtoW(src
, isW
);
514 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
515 bResult
= Str_SetPtrW(dest
, pszText
);
516 textfreeT(pszText
, isW
);
522 * compares a Unicode to a Unicode/ANSI text string
524 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
526 if (!aw
) return bt
? -1 : 0;
527 if (!bt
) return aw
? 1 : 0;
528 if (aw
== LPSTR_TEXTCALLBACKW
)
529 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
530 if (bt
!= LPSTR_TEXTCALLBACKW
)
532 LPWSTR bw
= textdupTtoW(bt
, isW
);
533 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
541 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
545 n
= min(min(n
, lstrlenW(s1
)), lstrlenW(s2
));
546 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
547 return res
? res
- sizeof(WCHAR
) : res
;
550 /******** Debugging functions *****************************************/
552 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
554 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
555 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
558 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
560 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
561 n
= min(textlenT(text
, isW
), n
);
562 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
565 static char* debug_getbuf(void)
567 static int index
= 0;
568 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
569 return buffers
[index
++ % DEBUG_BUFFERS
];
572 static inline const char* debugrange(const RANGE
*lprng
)
574 if (!lprng
) return "(null)";
575 return wine_dbg_sprintf("[%d, %d]", lprng
->lower
, lprng
->upper
);
578 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
580 char* buf
= debug_getbuf(), *text
= buf
;
581 int len
, size
= DEBUG_BUFFER_SIZE
;
583 if (pScrollInfo
== NULL
) return "(null)";
584 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
585 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
586 if (pScrollInfo
->fMask
& SIF_RANGE
)
587 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
589 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
590 if (pScrollInfo
->fMask
& SIF_PAGE
)
591 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
593 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
594 if (pScrollInfo
->fMask
& SIF_POS
)
595 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
597 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
598 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
599 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
601 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
604 buf
= text
+ strlen(text
);
606 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
610 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
612 if (!plvnm
) return "(null)";
613 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
614 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
615 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
616 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
619 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
621 char* buf
= debug_getbuf(), *text
= buf
;
622 int len
, size
= DEBUG_BUFFER_SIZE
;
624 if (lpLVItem
== NULL
) return "(null)";
625 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
626 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
627 if (lpLVItem
->mask
& LVIF_STATE
)
628 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
630 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
631 if (lpLVItem
->mask
& LVIF_TEXT
)
632 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
634 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
635 if (lpLVItem
->mask
& LVIF_IMAGE
)
636 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
638 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
639 if (lpLVItem
->mask
& LVIF_PARAM
)
640 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
642 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
643 if (lpLVItem
->mask
& LVIF_INDENT
)
644 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
646 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
649 buf
= text
+ strlen(text
);
651 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
655 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
657 char* buf
= debug_getbuf(), *text
= buf
;
658 int len
, size
= DEBUG_BUFFER_SIZE
;
660 if (lpColumn
== NULL
) return "(null)";
661 len
= snprintf(buf
, size
, "{");
662 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
663 if (lpColumn
->mask
& LVCF_SUBITEM
)
664 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
666 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
667 if (lpColumn
->mask
& LVCF_FMT
)
668 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
670 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
671 if (lpColumn
->mask
& LVCF_WIDTH
)
672 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
674 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
675 if (lpColumn
->mask
& LVCF_TEXT
)
676 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
678 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
679 if (lpColumn
->mask
& LVCF_IMAGE
)
680 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
682 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
683 if (lpColumn
->mask
& LVCF_ORDER
)
684 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
686 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
689 buf
= text
+ strlen(text
);
691 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
695 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
697 if (!lpht
) return "(null)";
699 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
700 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
703 /* Return the corresponding text for a given scroll value */
704 static inline LPCSTR
debugscrollcode(int nScrollCode
)
708 case SB_LINELEFT
: return "SB_LINELEFT";
709 case SB_LINERIGHT
: return "SB_LINERIGHT";
710 case SB_PAGELEFT
: return "SB_PAGELEFT";
711 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
712 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
713 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
714 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
715 case SB_INTERNAL
: return "SB_INTERNAL";
716 default: return "unknown";
721 /******** Notification functions i************************************/
723 static LRESULT
notify_forward_header(const LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
725 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
726 (WPARAM
)lpnmh
->hdr
.idFrom
, (LPARAM
)lpnmh
);
729 static LRESULT
notify_hdr(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
733 TRACE("(code=%d)\n", code
);
735 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
736 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
738 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, pnmh
->idFrom
, (LPARAM
)pnmh
);
740 TRACE(" <= %ld\n", result
);
745 static inline BOOL
notify(const LISTVIEW_INFO
*infoPtr
, INT code
)
748 HWND hwnd
= infoPtr
->hwndSelf
;
749 notify_hdr(infoPtr
, code
, &nmh
);
750 return IsWindow(hwnd
);
753 static inline void notify_itemactivate(const LISTVIEW_INFO
*infoPtr
, const LVHITTESTINFO
*htInfo
)
764 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
765 item
.iItem
= htInfo
->iItem
;
767 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
768 nmia
.lParam
= item
.lParam
;
769 nmia
.uOldState
= item
.state
;
770 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
771 nmia
.uChanged
= LVIF_STATE
;
774 nmia
.iItem
= htInfo
->iItem
;
775 nmia
.iSubItem
= htInfo
->iSubItem
;
776 nmia
.ptAction
= htInfo
->pt
;
778 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
779 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
780 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
782 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
785 static inline LRESULT
notify_listview(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
787 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
788 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
791 static BOOL
notify_click(const LISTVIEW_INFO
*infoPtr
, INT code
, const LVHITTESTINFO
*lvht
)
795 HWND hwnd
= infoPtr
->hwndSelf
;
797 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
798 ZeroMemory(&nmlv
, sizeof(nmlv
));
799 nmlv
.iItem
= lvht
->iItem
;
800 nmlv
.iSubItem
= lvht
->iSubItem
;
801 nmlv
.ptAction
= lvht
->pt
;
802 item
.mask
= LVIF_PARAM
;
803 item
.iItem
= lvht
->iItem
;
805 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
806 notify_listview(infoPtr
, code
, &nmlv
);
807 return IsWindow(hwnd
);
810 static BOOL
notify_deleteitem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
814 HWND hwnd
= infoPtr
->hwndSelf
;
816 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
818 item
.mask
= LVIF_PARAM
;
821 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
822 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
823 return IsWindow(hwnd
);
826 static int get_ansi_notification(UINT unicodeNotificationCode
)
828 switch (unicodeNotificationCode
)
830 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
831 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
832 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
833 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
834 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
835 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
837 ERR("unknown notification %x\n", unicodeNotificationCode
);
843 Send notification. depends on dispinfoW having same
844 structure as dispinfoA.
845 infoPtr : listview struct
846 notificationCode : *Unicode* notification code
847 pdi : dispinfo structure (can be unicode or ansi)
848 isW : TRUE if dispinfo is Unicode
850 static BOOL
notify_dispinfoT(const LISTVIEW_INFO
*infoPtr
, UINT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
852 BOOL bResult
= FALSE
;
853 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
854 INT cchTempBufMax
= 0, savCchTextMax
= 0;
856 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
858 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
860 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
861 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
864 if (convertToAnsi
|| convertToUnicode
)
866 if (notificationCode
!= LVN_GETDISPINFOW
)
868 cchTempBufMax
= convertToUnicode
?
869 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
870 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
874 cchTempBufMax
= pdi
->item
.cchTextMax
;
875 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
878 pszTempBuf
= Alloc( (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
879 if (!pszTempBuf
) return FALSE
;
881 if (convertToUnicode
)
882 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
883 pszTempBuf
, cchTempBufMax
);
885 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
886 cchTempBufMax
, NULL
, NULL
);
888 savCchTextMax
= pdi
->item
.cchTextMax
;
889 savPszText
= pdi
->item
.pszText
;
890 pdi
->item
.pszText
= pszTempBuf
;
891 pdi
->item
.cchTextMax
= cchTempBufMax
;
894 if (infoPtr
->notifyFormat
== NFR_ANSI
)
895 realNotifCode
= get_ansi_notification(notificationCode
);
897 realNotifCode
= notificationCode
;
898 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
899 bResult
= notify_hdr(infoPtr
, realNotifCode
, &pdi
->hdr
);
901 if (convertToUnicode
|| convertToAnsi
)
903 if (convertToUnicode
) /* note : pointer can be changed by app ! */
904 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
905 savCchTextMax
, NULL
, NULL
);
907 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
908 savPszText
, savCchTextMax
);
909 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
910 pdi
->item
.cchTextMax
= savCchTextMax
;
916 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
,
917 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
919 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
920 lpnmlvcd
->nmcd
.hdc
= hdc
;
921 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
922 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
923 lpnmlvcd
->clrText
= infoPtr
->clrText
;
924 if (!lplvItem
) return;
925 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
926 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
927 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
928 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
929 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
930 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
933 static inline DWORD
notify_customdraw (const LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
935 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
938 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
939 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
940 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
941 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
942 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
943 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
947 static void prepaint_setup (const LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
, BOOL SubItem
)
949 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
950 lpnmlvcd
->clrTextBk
= comctl32_color
.clrWindow
;
951 if (lpnmlvcd
->clrText
== CLR_DEFAULT
)
952 lpnmlvcd
->clrText
= comctl32_color
.clrWindowText
;
954 /* apparently, for selected items, we have to override the returned values */
957 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
961 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
962 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
964 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
966 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
967 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
972 /* Set the text attributes */
973 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
975 SetBkMode(hdc
, OPAQUE
);
976 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
979 SetBkMode(hdc
, TRANSPARENT
);
980 SetTextColor(hdc
, lpnmlvcd
->clrText
);
983 static inline DWORD
notify_postpaint (const LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
985 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
988 /******** Item iterator functions **********************************/
990 static RANGES
ranges_create(int count
);
991 static void ranges_destroy(RANGES ranges
);
992 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
993 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
994 static void ranges_dump(RANGES ranges
);
996 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
998 RANGE range
= { nItem
, nItem
+ 1 };
1000 return ranges_add(ranges
, range
);
1003 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
1005 RANGE range
= { nItem
, nItem
+ 1 };
1007 return ranges_del(ranges
, range
);
1011 * ITERATOR DOCUMENTATION
1013 * The iterator functions allow for easy, and convenient iteration
1014 * over items of interest in the list. Typically, you create a
1015 * iterator, use it, and destroy it, as such:
1018 * iterator_xxxitems(&i, ...);
1019 * while (iterator_{prev,next}(&i)
1021 * //code which uses i.nItem
1023 * iterator_destroy(&i);
1025 * where xxx is either: framed, or visible.
1026 * Note that it is important that the code destroys the iterator
1027 * after it's done with it, as the creation of the iterator may
1028 * allocate memory, which thus needs to be freed.
1030 * You can iterate both forwards, and backwards through the list,
1031 * by using iterator_next or iterator_prev respectively.
1033 * Lower numbered items are draw on top of higher number items in
1034 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1035 * items may overlap). So, to test items, you should use
1037 * which lists the items top to bottom (in Z-order).
1038 * For drawing items, you should use
1040 * which lists the items bottom to top (in Z-order).
1041 * If you keep iterating over the items after the end-of-items
1042 * marker (-1) is returned, the iterator will start from the
1043 * beginning. Typically, you don't need to test for -1,
1044 * because iterator_{next,prev} will return TRUE if more items
1045 * are to be iterated over, or FALSE otherwise.
1047 * Note: the iterator is defined to be bidirectional. That is,
1048 * any number of prev followed by any number of next, or
1049 * five versa, should leave the iterator at the same item:
1050 * prev * n, next * n = next * n, prev * n
1052 * The iterator has a notion of an out-of-order, special item,
1053 * which sits at the start of the list. This is used in
1054 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1055 * which needs to be first, as it may overlap other items.
1057 * The code is a bit messy because we have:
1058 * - a special item to deal with
1059 * - simple range, or composite range
1061 * If you find bugs, or want to add features, please make sure you
1062 * always check/modify *both* iterator_prev, and iterator_next.
1066 * This function iterates through the items in increasing order,
1067 * but prefixed by the special item, then -1. That is:
1068 * special, 1, 2, 3, ..., n, -1.
1069 * Each item is listed only once.
1071 static inline BOOL
iterator_next(ITERATOR
* i
)
1075 i
->nItem
= i
->nSpecial
;
1076 if (i
->nItem
!= -1) return TRUE
;
1078 if (i
->nItem
== i
->nSpecial
)
1080 if (i
->ranges
) i
->index
= 0;
1086 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1087 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1092 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1093 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1096 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1098 i
->nItem
= i
->range
.lower
;
1099 if (i
->nItem
>= 0) goto testitem
;
1106 * This function iterates through the items in decreasing order,
1107 * followed by the special item, then -1. That is:
1108 * n, n-1, ..., 3, 2, 1, special, -1.
1109 * Each item is listed only once.
1111 static inline BOOL
iterator_prev(ITERATOR
* i
)
1118 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1121 if (i
->nItem
== i
->nSpecial
)
1129 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1130 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1136 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1139 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1141 i
->nItem
= i
->range
.upper
;
1142 if (i
->nItem
> 0) goto testitem
;
1144 return (i
->nItem
= i
->nSpecial
) != -1;
1147 static RANGE
iterator_range(const ITERATOR
*i
)
1151 if (!i
->ranges
) return i
->range
;
1153 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1155 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1156 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1158 else range
.lower
= range
.upper
= 0;
1164 * Releases resources associated with this ierator.
1166 static inline void iterator_destroy(const ITERATOR
*i
)
1168 ranges_destroy(i
->ranges
);
1172 * Create an empty iterator.
1174 static inline BOOL
iterator_empty(ITERATOR
* i
)
1176 ZeroMemory(i
, sizeof(*i
));
1177 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1182 * Create an iterator over a range.
1184 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1192 * Create an iterator over a bunch of ranges.
1193 * Please note that the iterator will take ownership of the ranges,
1194 * and will free them upon destruction.
1196 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1204 * Creates an iterator over the items which intersect lprc.
1206 static BOOL
iterator_frameditems(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1208 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1209 RECT frame
= *lprc
, rcItem
, rcTemp
;
1212 /* in case we fail, we want to return an empty iterator */
1213 if (!iterator_empty(i
)) return FALSE
;
1215 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1217 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1218 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1220 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
1224 if (uView
== LVS_ICON
&& infoPtr
->nFocusedItem
!= -1)
1226 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1227 if (IntersectRect(&rcTemp
, &rcItem
, lprc
))
1228 i
->nSpecial
= infoPtr
->nFocusedItem
;
1230 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1231 /* to do better here, we need to have PosX, and PosY sorted */
1232 TRACE("building icon ranges:\n");
1233 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1235 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1236 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1237 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1238 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1239 if (IntersectRect(&rcTemp
, &rcItem
, &frame
))
1240 ranges_additem(i
->ranges
, nItem
);
1244 else if (uView
== LVS_REPORT
)
1248 if (frame
.left
>= infoPtr
->nItemWidth
) return TRUE
;
1249 if (frame
.top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1251 range
.lower
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1252 range
.upper
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1253 if (range
.upper
<= range
.lower
) return TRUE
;
1254 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1255 TRACE(" report=%s\n", debugrange(&i
->range
));
1259 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1260 INT nFirstRow
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1261 INT nLastRow
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1262 INT nFirstCol
= max(frame
.left
/ infoPtr
->nItemWidth
, 0);
1263 INT nLastCol
= min((frame
.right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1264 INT lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1268 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1269 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1271 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1273 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1274 TRACE("building list ranges:\n");
1275 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1277 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1278 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1279 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1280 TRACE(" list=%s\n", debugrange(&item_range
));
1281 ranges_add(i
->ranges
, item_range
);
1289 * Creates an iterator over the items which intersect the visible region of hdc.
1291 static BOOL
iterator_visibleitems(ITERATOR
*i
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1293 POINT Origin
, Position
;
1294 RECT rcItem
, rcClip
;
1297 rgntype
= GetClipBox(hdc
, &rcClip
);
1298 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1299 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1300 if (rgntype
== SIMPLEREGION
) return TRUE
;
1302 /* first deal with the special item */
1303 if (i
->nSpecial
!= -1)
1305 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1306 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1309 /* if we can't deal with the region, we'll just go with the simple range */
1310 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1311 TRACE("building visible range:\n");
1312 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1314 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1315 if (!ranges_add(i
->ranges
, i
->range
))
1317 ranges_destroy(i
->ranges
);
1323 /* now delete the invisible items from the list */
1324 while(iterator_next(i
))
1326 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1327 rcItem
.left
= Position
.x
+ Origin
.x
;
1328 rcItem
.top
= Position
.y
+ Origin
.y
;
1329 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1330 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1331 if (!RectVisible(hdc
, &rcItem
))
1332 ranges_delitem(i
->ranges
, i
->nItem
);
1334 /* the iterator should restart on the next iterator_next */
1340 /******** Misc helper functions ************************************/
1342 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1343 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1345 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1346 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1349 static inline BOOL
is_autoarrange(const LISTVIEW_INFO
*infoPtr
)
1351 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1353 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1354 (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
);
1357 static void toggle_checkbox_state(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1359 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
1360 if(state
== 1 || state
== 2)
1364 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
1365 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
1366 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
1370 /******** Internal API functions ************************************/
1372 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1374 static COLUMN_INFO mainItem
;
1376 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1377 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1378 return (COLUMN_INFO
*)DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1381 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1383 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1386 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1388 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1391 /* Listview invalidation functions: use _only_ these functions to invalidate */
1393 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1395 return infoPtr
->bRedraw
;
1398 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1400 if(!is_redrawing(infoPtr
)) return;
1401 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1402 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1405 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1409 if(!is_redrawing(infoPtr
)) return;
1410 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1411 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1414 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1416 POINT Origin
, Position
;
1419 if(!is_redrawing(infoPtr
)) return;
1420 assert ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
);
1421 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1422 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1423 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1425 rcBox
.bottom
= infoPtr
->nItemHeight
;
1426 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1427 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1430 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1432 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1435 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1439 if(!is_redrawing(infoPtr
)) return;
1440 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1441 rcCol
.top
= infoPtr
->rcList
.top
;
1442 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1443 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1448 * Retrieves the number of items that can fit vertically in the client area.
1451 * [I] infoPtr : valid pointer to the listview structure
1454 * Number of items per row.
1456 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1458 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1460 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
1465 * Retrieves the number of items that can fit horizontally in the client
1469 * [I] infoPtr : valid pointer to the listview structure
1472 * Number of items per column.
1474 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1476 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1478 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1482 /*************************************************************************
1483 * LISTVIEW_ProcessLetterKeys
1485 * Processes keyboard messages generated by pressing the letter keys
1487 * What this does is perform a case insensitive search from the
1488 * current position with the following quirks:
1489 * - If two chars or more are pressed in quick succession we search
1490 * for the corresponding string (e.g. 'abc').
1491 * - If there is a delay we wipe away the current search string and
1492 * restart with just that char.
1493 * - If the user keeps pressing the same character, whether slowly or
1494 * fast, so that the search string is entirely composed of this
1495 * character ('aaaaa' for instance), then we search for first item
1496 * that starting with that character.
1497 * - If the user types the above character in quick succession, then
1498 * we must also search for the corresponding string ('aaaaa'), and
1499 * go to that string if there is a match.
1502 * [I] hwnd : handle to the window
1503 * [I] charCode : the character code, the actual character
1504 * [I] keyData : key data
1512 * - The current implementation has a list of characters it will
1513 * accept and it ignores everything else. In particular it will
1514 * ignore accentuated characters which seems to match what
1515 * Windows does. But I'm not sure it makes sense to follow
1517 * - We don't sound a beep when the search fails.
1521 * TREEVIEW_ProcessLetterKeys
1523 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1528 WCHAR buffer
[MAX_PATH
];
1529 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1531 /* simple parameter checking */
1532 if (!charCode
|| !keyData
) return 0;
1534 /* only allow the valid WM_CHARs through */
1535 if (!isalnum(charCode
) &&
1536 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1537 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1538 charCode
!= '%' && charCode
!= '^' && charCode
!= '&' &&
1539 charCode
!= '*' && charCode
!= '(' && charCode
!= ')' &&
1540 charCode
!= '-' && charCode
!= '_' && charCode
!= '+' &&
1541 charCode
!= '=' && charCode
!= '\\'&& charCode
!= ']' &&
1542 charCode
!= '}' && charCode
!= '[' && charCode
!= '{' &&
1543 charCode
!= '/' && charCode
!= '?' && charCode
!= '>' &&
1544 charCode
!= '<' && charCode
!= ',' && charCode
!= '~')
1547 /* if there's one item or less, there is no where to go */
1548 if (infoPtr
->nItemCount
<= 1) return 0;
1550 /* update the search parameters */
1551 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1552 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1553 if (infoPtr
->nSearchParamLength
< MAX_PATH
)
1554 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1555 if (infoPtr
->charCode
!= charCode
)
1556 infoPtr
->charCode
= charCode
= 0;
1558 infoPtr
->charCode
=charCode
;
1559 infoPtr
->szSearchParam
[0]=charCode
;
1560 infoPtr
->nSearchParamLength
=1;
1561 /* Redundant with the 1 char string */
1565 /* and search from the current position */
1567 if (infoPtr
->nFocusedItem
>= 0) {
1568 endidx
=infoPtr
->nFocusedItem
;
1570 /* if looking for single character match,
1571 * then we must always move forward
1573 if (infoPtr
->nSearchParamLength
== 1)
1576 endidx
=infoPtr
->nItemCount
;
1580 if (idx
== infoPtr
->nItemCount
) {
1581 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1587 item
.mask
= LVIF_TEXT
;
1590 item
.pszText
= buffer
;
1591 item
.cchTextMax
= MAX_PATH
;
1592 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1594 /* check for a match */
1595 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1598 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1599 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1600 /* This would work but we must keep looking for a longer match */
1604 } while (idx
!= endidx
);
1607 LISTVIEW_KeySelection(infoPtr
, nItem
);
1612 /*************************************************************************
1613 * LISTVIEW_UpdateHeaderSize [Internal]
1615 * Function to resize the header control
1618 * [I] hwnd : handle to a window
1619 * [I] nNewScrollPos : scroll pos to set
1624 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1629 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1631 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1632 point
[0].x
= winRect
.left
;
1633 point
[0].y
= winRect
.top
;
1634 point
[1].x
= winRect
.right
;
1635 point
[1].y
= winRect
.bottom
;
1637 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1638 point
[0].x
= -nNewScrollPos
;
1639 point
[1].x
+= nNewScrollPos
;
1641 SetWindowPos(infoPtr
->hwndHeader
,0,
1642 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1643 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
1644 SWP_NOZORDER
| SWP_NOACTIVATE
);
1649 * Update the scrollbars. This functions should be called whenever
1650 * the content, size or view changes.
1653 * [I] infoPtr : valid pointer to the listview structure
1658 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*infoPtr
)
1660 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1661 SCROLLINFO horzInfo
, vertInfo
;
1664 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1666 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1667 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1668 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1670 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1671 if (uView
== LVS_LIST
)
1673 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1674 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1676 /* scroll by at least one column per page */
1677 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1678 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1680 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1682 else if (uView
== LVS_REPORT
)
1684 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1686 else /* LVS_ICON, or LVS_SMALLICON */
1690 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1693 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1694 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1695 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
1696 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1697 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1699 /* Setting the horizontal scroll can change the listview size
1700 * (and potentially everything else) so we need to recompute
1701 * everything again for the vertical scroll
1704 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1705 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1706 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1708 if (uView
== LVS_REPORT
)
1710 vertInfo
.nMax
= infoPtr
->nItemCount
;
1712 /* scroll by at least one page */
1713 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1714 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1716 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1718 else if (uView
!= LVS_LIST
) /* LVS_ICON, or LVS_SMALLICON */
1722 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1725 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1726 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1727 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
1728 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1729 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1731 /* Change of the range may have changed the scroll pos. If so move the content */
1732 if (dx
!= 0 || dy
!= 0)
1735 listRect
= infoPtr
->rcList
;
1736 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
1737 SW_ERASE
| SW_INVALIDATE
);
1740 /* Update the Header Control */
1741 if (uView
== LVS_REPORT
)
1743 horzInfo
.fMask
= SIF_POS
;
1744 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1745 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1752 * Shows/hides the focus rectangle.
1755 * [I] infoPtr : valid pointer to the listview structure
1756 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1761 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1763 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1766 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1768 if (infoPtr
->nFocusedItem
< 0) return;
1770 /* we need some gymnastics in ICON mode to handle large items */
1771 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
1775 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1776 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1778 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1783 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1785 /* for some reason, owner draw should work only in report mode */
1786 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
1791 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
1792 HFONT hOldFont
= SelectObject(hdc
, hFont
);
1794 item
.iItem
= infoPtr
->nFocusedItem
;
1796 item
.mask
= LVIF_PARAM
;
1797 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1799 ZeroMemory(&dis
, sizeof(dis
));
1800 dis
.CtlType
= ODT_LISTVIEW
;
1801 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1802 dis
.itemID
= item
.iItem
;
1803 dis
.itemAction
= ODA_FOCUS
;
1804 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1805 dis
.hwndItem
= infoPtr
->hwndSelf
;
1807 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1808 dis
.itemData
= item
.lParam
;
1810 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1812 SelectObject(hdc
, hOldFont
);
1816 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1819 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1823 * Invalidates all visible selected items.
1825 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
1829 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1830 while(iterator_next(&i
))
1832 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1833 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1835 iterator_destroy(&i
);
1840 * DESCRIPTION: [INTERNAL]
1841 * Computes an item's (left,top) corner, relative to rcView.
1842 * That is, the position has NOT been made relative to the Origin.
1843 * This is deliberate, to avoid computing the Origin over, and
1844 * over again, when this function is called in a loop. Instead,
1845 * one can factor the computation of the Origin before the loop,
1846 * and offset the value returned by this function, on every iteration.
1849 * [I] infoPtr : valid pointer to the listview structure
1850 * [I] nItem : item number
1851 * [O] lpptOrig : item top, left corner
1856 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1858 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1860 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1862 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1864 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1865 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1867 else if (uView
== LVS_LIST
)
1869 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1870 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1871 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1873 else /* LVS_REPORT */
1875 lpptPosition
->x
= 0;
1876 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1881 * DESCRIPTION: [INTERNAL]
1882 * Compute the rectangles of an item. This is to localize all
1883 * the computations in one place. If you are not interested in some
1884 * of these values, simply pass in a NULL -- the function is smart
1885 * enough to compute only what's necessary. The function computes
1886 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1887 * one, the BOX rectangle. This rectangle is very cheap to compute,
1888 * and is guaranteed to contain all the other rectangles. Computing
1889 * the ICON rect is also cheap, but all the others are potentially
1890 * expensive. This gives an easy and effective optimization when
1891 * searching (like point inclusion, or rectangle intersection):
1892 * first test against the BOX, and if TRUE, test against the desired
1894 * If the function does not have all the necessary information
1895 * to computed the requested rectangles, will crash with a
1896 * failed assertion. This is done so we catch all programming
1897 * errors, given that the function is called only from our code.
1899 * We have the following 'special' meanings for a few fields:
1900 * * If LVIS_FOCUSED is set, we assume the item has the focus
1901 * This is important in ICON mode, where it might get a larger
1902 * then usual rectangle
1904 * Please note that subitem support works only in REPORT mode.
1907 * [I] infoPtr : valid pointer to the listview structure
1908 * [I] lpLVItem : item to compute the measures for
1909 * [O] lprcBox : ptr to Box rectangle
1910 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1911 * [0] lprcSelectBox : ptr to select box rectangle
1912 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1913 * [O] lprcIcon : ptr to Icon rectangle
1914 * Same as LVM_GETITEMRECT with LVIR_ICON
1915 * [O] lprcStateIcon: ptr to State Icon rectangle
1916 * [O] lprcLabel : ptr to Label rectangle
1917 * Same as LVM_GETITEMRECT with LVIR_LABEL
1922 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1923 LPRECT lprcBox
, LPRECT lprcSelectBox
,
1924 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
1926 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1927 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1928 RECT Box
, SelectBox
, Icon
, Label
;
1929 COLUMN_INFO
*lpColumnInfo
= NULL
;
1930 SIZE labelSize
= { 0, 0 };
1932 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
1934 /* Be smart and try to figure out the minimum we have to do */
1935 if (lpLVItem
->iSubItem
) assert(uView
== LVS_REPORT
);
1936 if (uView
== LVS_ICON
&& (lprcBox
|| lprcLabel
))
1938 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
1939 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
1941 if (lprcSelectBox
) doSelectBox
= TRUE
;
1942 if (lprcLabel
) doLabel
= TRUE
;
1943 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
1950 /************************************************************/
1951 /* compute the box rectangle (it should be cheap to do) */
1952 /************************************************************/
1953 if (lpLVItem
->iSubItem
|| uView
== LVS_REPORT
)
1954 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
1956 if (lpLVItem
->iSubItem
)
1958 Box
= lpColumnInfo
->rcHeader
;
1963 Box
.right
= infoPtr
->nItemWidth
;
1966 Box
.bottom
= infoPtr
->nItemHeight
;
1968 /******************************************************************/
1969 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
1970 /******************************************************************/
1973 LONG state_width
= 0;
1975 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
1976 state_width
= infoPtr
->iconStateSize
.cx
;
1978 if (uView
== LVS_ICON
)
1980 Icon
.left
= Box
.left
+ state_width
;
1981 if (infoPtr
->himlNormal
)
1982 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
1983 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
1984 Icon
.right
= Icon
.left
;
1985 Icon
.bottom
= Icon
.top
;
1986 if (infoPtr
->himlNormal
)
1988 Icon
.right
+= infoPtr
->iconSize
.cx
;
1989 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
1992 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1994 Icon
.left
= Box
.left
+ state_width
;
1996 if (uView
== LVS_REPORT
)
1997 Icon
.left
+= REPORT_MARGINX
;
2000 Icon
.right
= Icon
.left
;
2001 if (infoPtr
->himlSmall
&&
2002 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
2003 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2004 Icon
.right
+= infoPtr
->iconSize
.cx
;
2005 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2007 if(lprcIcon
) *lprcIcon
= Icon
;
2008 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2010 /* TODO: is this correct? */
2013 lprcStateIcon
->left
= Icon
.left
- state_width
;
2014 lprcStateIcon
->right
= Icon
.left
;
2015 lprcStateIcon
->top
= Icon
.top
;
2016 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2017 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2020 else Icon
.right
= 0;
2022 /************************************************************/
2023 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2024 /************************************************************/
2027 /* calculate how far to the right can the label stretch */
2028 Label
.right
= Box
.right
;
2029 if (uView
== LVS_REPORT
)
2031 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
2034 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && uView
== LVS_REPORT
))
2036 labelSize
.cx
= infoPtr
->nItemWidth
;
2037 labelSize
.cy
= infoPtr
->nItemHeight
;
2041 /* we need the text in non owner draw mode */
2042 assert(lpLVItem
->mask
& LVIF_TEXT
);
2043 if (is_textT(lpLVItem
->pszText
, TRUE
))
2045 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2046 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2047 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2051 /* compute rough rectangle where the label will go */
2052 SetRectEmpty(&rcText
);
2053 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2054 rcText
.bottom
= infoPtr
->nItemHeight
;
2055 if (uView
== LVS_ICON
)
2056 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2058 /* now figure out the flags */
2059 if (uView
== LVS_ICON
)
2060 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2062 uFormat
= LV_SL_DT_FLAGS
;
2064 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2066 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2067 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2069 SelectObject(hdc
, hOldFont
);
2070 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2074 if (uView
== LVS_ICON
)
2076 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2077 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2078 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2079 Label
.right
= Label
.left
+ labelSize
.cx
;
2080 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2081 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2083 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2084 labelSize
.cy
/= infoPtr
->ntmHeight
;
2085 labelSize
.cy
= max(labelSize
.cy
, 1);
2086 labelSize
.cy
*= infoPtr
->ntmHeight
;
2088 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2090 else if (uView
== LVS_REPORT
)
2092 Label
.left
= Icon
.right
;
2093 Label
.top
= Box
.top
;
2094 Label
.right
= lpColumnInfo
->rcHeader
.right
;
2095 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2097 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2099 Label
.left
= Icon
.right
;
2100 Label
.top
= Box
.top
;
2101 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2102 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2105 if (lprcLabel
) *lprcLabel
= Label
;
2106 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2109 /************************************************************/
2110 /* compute STATEICON bounding box */
2111 /************************************************************/
2114 if (uView
== LVS_REPORT
)
2116 SelectBox
.left
= Icon
.right
; /* FIXME: should be Icon.left */
2117 SelectBox
.top
= Box
.top
;
2118 SelectBox
.bottom
= Box
.bottom
;
2119 if (lpLVItem
->iSubItem
== 0)
2121 /* we need the indent in report mode */
2122 assert(lpLVItem
->mask
& LVIF_INDENT
);
2123 SelectBox
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
;
2125 SelectBox
.right
= min(SelectBox
.left
+ labelSize
.cx
, Label
.right
);
2129 UnionRect(&SelectBox
, &Icon
, &Label
);
2131 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2132 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2135 /* Fix the Box if necessary */
2138 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2139 else *lprcBox
= Box
;
2141 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2145 * DESCRIPTION: [INTERNAL]
2148 * [I] infoPtr : valid pointer to the listview structure
2149 * [I] nItem : item number
2150 * [O] lprcBox : ptr to Box rectangle
2155 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2157 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2158 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2159 POINT Position
, Origin
;
2162 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2163 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2165 /* Be smart and try to figure out the minimum we have to do */
2167 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2168 lvItem
.mask
|= LVIF_TEXT
;
2169 lvItem
.iItem
= nItem
;
2170 lvItem
.iSubItem
= 0;
2171 lvItem
.pszText
= szDispText
;
2172 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2173 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2174 if (uView
== LVS_ICON
)
2176 lvItem
.mask
|= LVIF_STATE
;
2177 lvItem
.stateMask
= LVIS_FOCUSED
;
2178 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2180 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2182 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2188 * Returns the current icon position, and advances it along the top.
2189 * The returned position is not offset by Origin.
2192 * [I] infoPtr : valid pointer to the listview structure
2193 * [O] lpPos : will get the current icon position
2198 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2200 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2202 *lpPos
= infoPtr
->currIconPos
;
2204 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2205 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2207 infoPtr
->currIconPos
.x
= 0;
2208 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2214 * Returns the current icon position, and advances it down the left edge.
2215 * The returned position is not offset by Origin.
2218 * [I] infoPtr : valid pointer to the listview structure
2219 * [O] lpPos : will get the current icon position
2224 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2226 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2228 *lpPos
= infoPtr
->currIconPos
;
2230 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2231 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2233 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2234 infoPtr
->currIconPos
.y
= 0;
2240 * Moves an icon to the specified position.
2241 * It takes care of invalidating the item, etc.
2244 * [I] infoPtr : valid pointer to the listview structure
2245 * [I] nItem : the item to move
2246 * [I] lpPos : the new icon position
2247 * [I] isNew : flags the item as being new
2253 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2259 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2260 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2262 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2263 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2266 /* Allocating a POINTER for every item is too resource intensive,
2267 * so we'll keep the (x,y) in different arrays */
2268 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2269 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2271 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2278 * Arranges listview items in icon display mode.
2281 * [I] infoPtr : valid pointer to the listview structure
2282 * [I] nAlignCode : alignment code
2288 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2290 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2291 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2295 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
) return FALSE
;
2297 TRACE("nAlignCode=%d\n", nAlignCode
);
2299 if (nAlignCode
== LVA_DEFAULT
)
2301 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2302 else nAlignCode
= LVA_ALIGNTOP
;
2307 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2308 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2309 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2310 default: return FALSE
;
2313 infoPtr
->bAutoarrange
= TRUE
;
2314 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2315 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2317 next_pos(infoPtr
, &pos
);
2318 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2326 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2329 * [I] infoPtr : valid pointer to the listview structure
2330 * [O] lprcView : bounding rectangle
2336 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2340 SetRectEmpty(lprcView
);
2342 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
2346 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2348 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2349 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2350 lprcView
->right
= max(lprcView
->right
, x
);
2351 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2353 if (infoPtr
->nItemCount
> 0)
2355 lprcView
->right
+= infoPtr
->nItemWidth
;
2356 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2361 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2362 x
= infoPtr
->nItemCount
/ y
;
2363 if (infoPtr
->nItemCount
% y
) x
++;
2364 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2365 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2369 lprcView
->right
= infoPtr
->nItemWidth
;
2370 lprcView
->bottom
= infoPtr
->nItemCount
* infoPtr
->nItemHeight
;
2377 * Retrieves the bounding rectangle of all the items.
2380 * [I] infoPtr : valid pointer to the listview structure
2381 * [O] lprcView : bounding rectangle
2387 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2391 TRACE("(lprcView=%p)\n", lprcView
);
2393 if (!lprcView
) return FALSE
;
2395 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2396 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2397 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2399 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2406 * Retrieves the subitem pointer associated with the subitem index.
2409 * [I] hdpaSubItems : DPA handle for a specific item
2410 * [I] nSubItem : index of subitem
2413 * SUCCESS : subitem pointer
2416 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2418 SUBITEM_INFO
*lpSubItem
;
2421 /* we should binary search here if need be */
2422 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2424 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
2425 if (lpSubItem
->iSubItem
== nSubItem
)
2435 * Calculates the desired item width.
2438 * [I] infoPtr : valid pointer to the listview structure
2441 * The desired item width.
2443 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2445 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2448 TRACE("uView=%d\n", uView
);
2450 if (uView
== LVS_ICON
)
2451 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2452 else if (uView
== LVS_REPORT
)
2456 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2458 LISTVIEW_GetHeaderRect(infoPtr
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, &rcHeader
);
2459 nItemWidth
= rcHeader
.right
;
2462 else /* LVS_SMALLICON, or LVS_LIST */
2466 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2467 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2469 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2470 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2472 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2475 return max(nItemWidth
, 1);
2480 * Calculates the desired item height.
2483 * [I] infoPtr : valid pointer to the listview structure
2486 * The desired item height.
2488 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
2490 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2493 TRACE("uView=%d\n", uView
);
2495 if (uView
== LVS_ICON
)
2496 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2499 nItemHeight
= infoPtr
->ntmHeight
;
2500 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
2502 if (infoPtr
->himlState
)
2503 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2504 if (infoPtr
->himlSmall
)
2505 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2506 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2507 nItemHeight
+= HEIGHT_PADDING
;
2508 if (infoPtr
->nMeasureItemHeight
> 0)
2509 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2512 return max(nItemHeight
, 1);
2517 * Updates the width, and height of an item.
2520 * [I] infoPtr : valid pointer to the listview structure
2525 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2527 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2528 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2534 * Retrieves and saves important text metrics info for the current
2538 * [I] infoPtr : valid pointer to the listview structure
2541 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2543 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2544 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2545 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2549 if (GetTextMetricsW(hdc
, &tm
))
2551 infoPtr
->ntmHeight
= tm
.tmHeight
;
2552 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
2555 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
2556 infoPtr
->nEllipsisWidth
= sz
.cx
;
2558 SelectObject(hdc
, hOldFont
);
2559 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2561 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2566 * A compare function for ranges
2569 * [I] range1 : pointer to range 1;
2570 * [I] range2 : pointer to range 2;
2574 * > 0 : if range 1 > range 2
2575 * < 0 : if range 2 > range 1
2576 * = 0 : if range intersects range 2
2578 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2582 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2584 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2589 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE
*)range1
), debugrange((RANGE
*)range2
), cmp
);
2595 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2597 #define ranges_check(ranges, desc) do { } while(0)
2600 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2605 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2607 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2608 ranges_dump(ranges
);
2609 prev
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, 0);
2610 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2611 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2612 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2614 curr
= (RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
);
2615 assert (prev
->upper
<= curr
->lower
);
2616 assert (curr
->lower
< curr
->upper
);
2619 TRACE("--- Done checking---\n");
2622 static RANGES
ranges_create(int count
)
2624 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
2625 if (!ranges
) return NULL
;
2626 ranges
->hdpa
= DPA_Create(count
);
2627 if (ranges
->hdpa
) return ranges
;
2632 static void ranges_clear(RANGES ranges
)
2636 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2637 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2638 DPA_DeleteAllPtrs(ranges
->hdpa
);
2642 static void ranges_destroy(RANGES ranges
)
2644 if (!ranges
) return;
2645 ranges_clear(ranges
);
2646 DPA_Destroy(ranges
->hdpa
);
2650 static RANGES
ranges_clone(RANGES ranges
)
2655 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2657 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2659 RANGE
*newrng
= Alloc(sizeof(RANGE
));
2660 if (!newrng
) goto fail
;
2661 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2662 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2667 TRACE ("clone failed\n");
2668 ranges_destroy(clone
);
2672 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2676 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2677 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2682 static void ranges_dump(RANGES ranges
)
2686 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2687 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2690 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2692 RANGE srchrng
= { nItem
, nItem
+ 1 };
2694 TRACE("(nItem=%d)\n", nItem
);
2695 ranges_check(ranges
, "before contain");
2696 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2699 static INT
ranges_itemcount(RANGES ranges
)
2703 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2705 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2706 count
+= sel
->upper
- sel
->lower
;
2712 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2714 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2717 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2718 if (index
== -1) return TRUE
;
2720 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2722 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2723 if (chkrng
->lower
>= nItem
)
2724 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2725 if (chkrng
->upper
> nItem
)
2726 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2731 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2736 TRACE("(%s)\n", debugrange(&range
));
2737 ranges_check(ranges
, "before add");
2739 /* try find overlapping regions first */
2740 srchrgn
.lower
= range
.lower
- 1;
2741 srchrgn
.upper
= range
.upper
+ 1;
2742 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2748 TRACE("Adding new range\n");
2750 /* create the brand new range to insert */
2751 newrgn
= Alloc(sizeof(RANGE
));
2752 if(!newrgn
) goto fail
;
2755 /* figure out where to insert it */
2756 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2757 TRACE("index=%d\n", index
);
2758 if (index
== -1) index
= 0;
2760 /* and get it over with */
2761 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2769 RANGE
*chkrgn
, *mrgrgn
;
2770 INT fromindex
, mergeindex
;
2772 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2773 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2775 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2776 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2778 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2780 /* merge now common ranges */
2782 srchrgn
.lower
= chkrgn
->lower
- 1;
2783 srchrgn
.upper
= chkrgn
->upper
+ 1;
2787 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2788 if (mergeindex
== -1) break;
2789 if (mergeindex
== index
)
2791 fromindex
= index
+ 1;
2795 TRACE("Merge with index %i\n", mergeindex
);
2797 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2798 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2799 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2801 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2802 if (mergeindex
< index
) index
--;
2806 ranges_check(ranges
, "after add");
2810 ranges_check(ranges
, "failed add");
2814 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2819 TRACE("(%s)\n", debugrange(&range
));
2820 ranges_check(ranges
, "before del");
2822 /* we don't use DPAS_SORTED here, since we need *
2823 * to find the first overlapping range */
2824 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2827 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2829 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2831 /* case 1: Same range */
2832 if ( (chkrgn
->upper
== range
.upper
) &&
2833 (chkrgn
->lower
== range
.lower
) )
2835 DPA_DeletePtr(ranges
->hdpa
, index
);
2838 /* case 2: engulf */
2839 else if ( (chkrgn
->upper
<= range
.upper
) &&
2840 (chkrgn
->lower
>= range
.lower
) )
2842 DPA_DeletePtr(ranges
->hdpa
, index
);
2844 /* case 3: overlap upper */
2845 else if ( (chkrgn
->upper
<= range
.upper
) &&
2846 (chkrgn
->lower
< range
.lower
) )
2848 chkrgn
->upper
= range
.lower
;
2850 /* case 4: overlap lower */
2851 else if ( (chkrgn
->upper
> range
.upper
) &&
2852 (chkrgn
->lower
>= range
.lower
) )
2854 chkrgn
->lower
= range
.upper
;
2857 /* case 5: fully internal */
2860 RANGE tmprgn
= *chkrgn
, *newrgn
;
2862 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
2863 newrgn
->lower
= chkrgn
->lower
;
2864 newrgn
->upper
= range
.lower
;
2865 chkrgn
->lower
= range
.upper
;
2866 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2875 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2878 ranges_check(ranges
, "after del");
2882 ranges_check(ranges
, "failed del");
2888 * Removes all selection ranges
2891 * [I] infoPtr : valid pointer to the listview structure
2892 * [I] toSkip : item range to skip removing the selection
2898 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2907 lvItem
.stateMask
= LVIS_SELECTED
;
2909 /* need to clone the DPA because callbacks can change it */
2910 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2911 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2912 while(iterator_next(&i
))
2913 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2914 /* note that the iterator destructor will free the cloned range */
2915 iterator_destroy(&i
);
2920 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2924 if (!(toSkip
= ranges_create(1))) return FALSE
;
2925 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2926 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
2927 ranges_destroy(toSkip
);
2931 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
2933 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
2938 * Retrieves the number of items that are marked as selected.
2941 * [I] infoPtr : valid pointer to the listview structure
2944 * Number of items selected.
2946 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
2948 INT nSelectedCount
= 0;
2950 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
2953 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2955 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
2960 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
2962 TRACE("nSelectedCount=%d\n", nSelectedCount
);
2963 return nSelectedCount
;
2968 * Manages the item focus.
2971 * [I] infoPtr : valid pointer to the listview structure
2972 * [I] nItem : item index
2975 * TRUE : focused item changed
2976 * FALSE : focused item has NOT changed
2978 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2980 INT oldFocus
= infoPtr
->nFocusedItem
;
2983 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
2985 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
2986 lvItem
.stateMask
= LVIS_FOCUSED
;
2987 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
2989 return oldFocus
!= infoPtr
->nFocusedItem
;
2992 /* Helper function for LISTVIEW_ShiftIndices *only* */
2993 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
2995 if (nShiftItem
< nItem
) return nShiftItem
;
2997 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
2999 if (direction
> 0) return nShiftItem
+ direction
;
3001 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3006 * Updates the various indices after an item has been inserted or deleted.
3009 * [I] infoPtr : valid pointer to the listview structure
3010 * [I] nItem : item index
3011 * [I] direction : Direction of shift, +1 or -1.
3016 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3021 /* temporarily disable change notification while shifting items */
3022 bOldChange
= infoPtr
->bDoChangeNotify
;
3023 infoPtr
->bDoChangeNotify
= FALSE
;
3025 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
3027 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3029 assert(abs(direction
) == 1);
3031 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3033 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
3034 if (nNewFocus
!= infoPtr
->nFocusedItem
)
3035 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
3037 /* But we are not supposed to modify nHotItem! */
3039 infoPtr
->bDoChangeNotify
= bOldChange
;
3045 * Adds a block of selections.
3048 * [I] infoPtr : valid pointer to the listview structure
3049 * [I] nItem : item index
3052 * Whether the window is still valid.
3054 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3056 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3057 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3058 HWND hwndSelf
= infoPtr
->hwndSelf
;
3059 NMLVODSTATECHANGE nmlv
;
3064 /* Temporarily disable change notification
3065 * If the control is LVS_OWNERDATA, we need to send
3066 * only one LVN_ODSTATECHANGED notification.
3067 * See MSDN documentation for LVN_ITEMCHANGED.
3069 bOldChange
= infoPtr
->bDoChangeNotify
;
3070 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3072 if (nFirst
== -1) nFirst
= nItem
;
3074 item
.state
= LVIS_SELECTED
;
3075 item
.stateMask
= LVIS_SELECTED
;
3077 for (i
= nFirst
; i
<= nLast
; i
++)
3078 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3080 ZeroMemory(&nmlv
, sizeof(nmlv
));
3081 nmlv
.iFrom
= nFirst
;
3084 nmlv
.uOldState
= item
.state
;
3086 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3087 if (!IsWindow(hwndSelf
))
3089 infoPtr
->bDoChangeNotify
= bOldChange
;
3096 * Sets a single group selection.
3099 * [I] infoPtr : valid pointer to the listview structure
3100 * [I] nItem : item index
3105 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3107 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3113 if (!(selection
= ranges_create(100))) return;
3115 item
.state
= LVIS_SELECTED
;
3116 item
.stateMask
= LVIS_SELECTED
;
3118 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
3120 if (infoPtr
->nSelectionMark
== -1)
3122 infoPtr
->nSelectionMark
= nItem
;
3123 ranges_additem(selection
, nItem
);
3129 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3130 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3131 ranges_add(selection
, sel
);
3136 RECT rcItem
, rcSel
, rcSelMark
;
3139 rcItem
.left
= LVIR_BOUNDS
;
3140 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3141 rcSelMark
.left
= LVIR_BOUNDS
;
3142 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3143 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3144 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3145 while(iterator_next(&i
))
3147 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3148 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3150 iterator_destroy(&i
);
3153 bOldChange
= infoPtr
->bDoChangeNotify
;
3154 infoPtr
->bDoChangeNotify
= FALSE
;
3156 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3159 iterator_rangesitems(&i
, selection
);
3160 while(iterator_next(&i
))
3161 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3162 /* this will also destroy the selection */
3163 iterator_destroy(&i
);
3165 infoPtr
->bDoChangeNotify
= bOldChange
;
3167 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3172 * Sets a single selection.
3175 * [I] infoPtr : valid pointer to the listview structure
3176 * [I] nItem : item index
3181 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3185 TRACE("nItem=%d\n", nItem
);
3187 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3189 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3190 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3191 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3193 infoPtr
->nSelectionMark
= nItem
;
3198 * Set selection(s) with keyboard.
3201 * [I] infoPtr : valid pointer to the listview structure
3202 * [I] nItem : item index
3205 * SUCCESS : TRUE (needs to be repainted)
3206 * FAILURE : FALSE (nothing has changed)
3208 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3210 /* FIXME: pass in the state */
3211 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3212 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3213 BOOL bResult
= FALSE
;
3215 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3216 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3218 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3221 LISTVIEW_SetSelection(infoPtr
, nItem
);
3228 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3233 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3234 lvItem
.stateMask
= LVIS_SELECTED
;
3235 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3237 if (lvItem
.state
& LVIS_SELECTED
)
3238 infoPtr
->nSelectionMark
= nItem
;
3240 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3245 LISTVIEW_SetSelection(infoPtr
, nItem
);
3248 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3251 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3255 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3257 LVHITTESTINFO lvHitTestInfo
;
3259 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3260 lvHitTestInfo
.pt
.x
= pt
.x
;
3261 lvHitTestInfo
.pt
.y
= pt
.y
;
3263 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3265 lpLVItem
->mask
= LVIF_PARAM
;
3266 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3267 lpLVItem
->iSubItem
= 0;
3269 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3274 * Called when the mouse is being actively tracked and has hovered for a specified
3278 * [I] infoPtr : valid pointer to the listview structure
3279 * [I] fwKeys : key indicator
3280 * [I] x,y : mouse position
3283 * 0 if the message was processed, non-zero if there was an error
3286 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3287 * over the item for a certain period of time.
3290 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3292 if (infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)
3300 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3301 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3309 * Called whenever WM_MOUSEMOVE is received.
3312 * [I] infoPtr : valid pointer to the listview structure
3313 * [I] fwKeys : key indicator
3314 * [I] x,y : mouse position
3317 * 0 if the message is processed, non-zero if there was an error
3319 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3321 TRACKMOUSEEVENT trackinfo
;
3323 if (!(fwKeys
& MK_LBUTTON
))
3324 infoPtr
->bLButtonDown
= FALSE
;
3326 if (infoPtr
->bLButtonDown
)
3330 /* Check to see if we got a WM_LBUTTONUP, and skip the DragDetect.
3331 * Otherwise, DragDetect will eat it.
3333 if (PeekMessageW(&msg
, 0, WM_MOUSEFIRST
, WM_MOUSELAST
, PM_NOREMOVE
))
3334 if (msg
.message
== WM_LBUTTONUP
)
3337 if (!skip
&& DragDetect(infoPtr
->hwndSelf
, infoPtr
->ptClickPos
))
3339 LVHITTESTINFO lvHitTestInfo
;
3342 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3343 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3345 ZeroMemory(&nmlv
, sizeof(nmlv
));
3346 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3347 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3349 if (!infoPtr
->bDragging
)
3351 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3352 infoPtr
->bDragging
= TRUE
;
3359 infoPtr
->bLButtonDown
= FALSE
;
3361 /* see if we are supposed to be tracking mouse hovering */
3362 if(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) {
3363 /* fill in the trackinfo struct */
3364 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3365 trackinfo
.dwFlags
= TME_QUERY
;
3366 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3367 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3369 /* see if we are already tracking this hwnd */
3370 _TrackMouseEvent(&trackinfo
);
3372 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3373 trackinfo
.dwFlags
= TME_HOVER
;
3375 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3376 _TrackMouseEvent(&trackinfo
);
3385 * Tests whether the item is assignable to a list with style lStyle
3387 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3389 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3390 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3391 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3399 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3402 * [I] infoPtr : valid pointer to the listview structure
3403 * [I] lpLVItem : valid pointer to new item attributes
3404 * [I] isNew : the item being set is being inserted
3405 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3406 * [O] bChanged : will be set to TRUE if the item really changed
3412 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3414 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3422 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3424 if (lpLVItem
->mask
== 0) return TRUE
;
3426 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3428 /* a virtual listview only stores selection and focus */
3429 if (lpLVItem
->mask
& ~LVIF_STATE
)
3435 HDPA hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3436 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
3440 /* we need to get the lParam and state of the item */
3441 item
.iItem
= lpLVItem
->iItem
;
3442 item
.iSubItem
= lpLVItem
->iSubItem
;
3443 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3444 item
.stateMask
= ~0;
3447 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3449 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3450 /* determine what fields will change */
3451 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
))
3452 uChanged
|= LVIF_STATE
;
3454 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3455 uChanged
|= LVIF_IMAGE
;
3457 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3458 uChanged
|= LVIF_PARAM
;
3460 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3461 uChanged
|= LVIF_INDENT
;
3463 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3464 uChanged
|= LVIF_TEXT
;
3466 TRACE("uChanged=0x%x\n", uChanged
);
3467 if (!uChanged
) return TRUE
;
3470 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3471 nmlv
.iItem
= lpLVItem
->iItem
;
3472 nmlv
.uNewState
= (item
.state
& ~lpLVItem
->stateMask
) | (lpLVItem
->state
& lpLVItem
->stateMask
);
3473 nmlv
.uOldState
= item
.state
;
3474 nmlv
.uChanged
= uChanged
;
3475 nmlv
.lParam
= item
.lParam
;
3477 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3478 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3480 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
3482 HWND hwndSelf
= infoPtr
->hwndSelf
;
3484 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3486 if (!IsWindow(hwndSelf
))
3490 /* copy information */
3491 if (lpLVItem
->mask
& LVIF_TEXT
)
3492 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3494 if (lpLVItem
->mask
& LVIF_IMAGE
)
3495 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3497 if (lpLVItem
->mask
& LVIF_PARAM
)
3498 lpItem
->lParam
= lpLVItem
->lParam
;
3500 if (lpLVItem
->mask
& LVIF_INDENT
)
3501 lpItem
->iIndent
= lpLVItem
->iIndent
;
3503 if (uChanged
& LVIF_STATE
)
3505 if (lpItem
&& (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& ~(LVIS_FOCUSED
| LVIS_SELECTED
)))
3507 lpItem
->state
&= ~lpLVItem
->stateMask
;
3508 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
3510 if (lpLVItem
->state
& lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3512 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3513 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3515 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
3516 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3518 /* if we are asked to change focus, and we manage it, do it */
3519 if (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3521 if (lpLVItem
->state
& LVIS_FOCUSED
)
3523 LISTVIEW_SetItemFocus(infoPtr
, -1);
3524 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3525 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3527 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3528 infoPtr
->nFocusedItem
= -1;
3532 /* if we're inserting the item, we're done */
3533 if (isNew
) return TRUE
;
3535 /* send LVN_ITEMCHANGED notification */
3536 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3537 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3544 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3547 * [I] infoPtr : valid pointer to the listview structure
3548 * [I] lpLVItem : valid pointer to new subitem attributes
3549 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3550 * [O] bChanged : will be set to TRUE if the item really changed
3556 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3559 SUBITEM_INFO
*lpSubItem
;
3561 /* we do not support subitems for virtual listviews */
3562 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3564 /* set subitem only if column is present */
3565 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3567 /* First do some sanity checks */
3568 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3569 particularly useful. We currently do not actually do anything with
3570 the flag on subitems.
3572 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
)) return FALSE
;
3573 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
3575 /* get the subitem structure, and create it if not there */
3576 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3577 assert (hdpaSubItems
);
3579 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3582 SUBITEM_INFO
*tmpSubItem
;
3585 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
3586 if (!lpSubItem
) return FALSE
;
3587 /* we could binary search here, if need be...*/
3588 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3590 tmpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
3591 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3593 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3598 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3599 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3603 if (lpLVItem
->mask
& LVIF_IMAGE
)
3604 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3606 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3610 if (lpLVItem
->mask
& LVIF_TEXT
)
3611 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3613 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3622 * Sets item attributes.
3625 * [I] infoPtr : valid pointer to the listview structure
3626 * [I] lpLVItem : new item attributes
3627 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3633 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
3635 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3636 HWND hwndSelf
= infoPtr
->hwndSelf
;
3637 LPWSTR pszText
= NULL
;
3638 BOOL bResult
, bChanged
= FALSE
;
3640 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3642 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3645 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3646 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3648 pszText
= lpLVItem
->pszText
;
3649 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3652 /* actually set the fields */
3653 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3655 if (lpLVItem
->iSubItem
)
3656 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3658 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3659 if (!IsWindow(hwndSelf
))
3662 /* redraw item, if necessary */
3663 if (bChanged
&& !infoPtr
->bIsDrawing
)
3665 /* this little optimization eliminates some nasty flicker */
3666 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3667 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
3668 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
3669 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3671 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3676 textfreeT(lpLVItem
->pszText
, isW
);
3677 lpLVItem
->pszText
= pszText
;
3685 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3688 * [I] infoPtr : valid pointer to the listview structure
3693 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
3695 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3697 SCROLLINFO scrollInfo
;
3699 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3700 scrollInfo
.fMask
= SIF_POS
;
3702 if (uView
== LVS_LIST
)
3704 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3705 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3707 else if (uView
== LVS_REPORT
)
3709 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3710 nItem
= scrollInfo
.nPos
;
3714 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3715 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3718 TRACE("nItem=%d\n", nItem
);
3726 * Erases the background of the given rectangle
3729 * [I] infoPtr : valid pointer to the listview structure
3730 * [I] hdc : device context handle
3731 * [I] lprcBox : clipping rectangle
3737 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3739 if (!infoPtr
->hBkBrush
) return FALSE
;
3741 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
3743 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3751 * [I] infoPtr : valid pointer to the listview structure
3752 * [I] hdc : device context handle
3753 * [I] nItem : item index
3754 * [I] nSubItem : subitem index
3755 * [I] pos : item position in client coordinates
3756 * [I] cdmode : custom draw mode
3762 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3764 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3765 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3766 static WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3767 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3769 RECT rcSelect
, rcBox
, rcIcon
, rcLabel
, rcStateIcon
;
3770 NMLVCUSTOMDRAW nmlvcd
;
3775 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, wine_dbgstr_point(&pos
));
3777 /* get information needed for drawing the item */
3778 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
;
3779 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
;
3780 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3781 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3782 lvItem
.iItem
= nItem
;
3783 lvItem
.iSubItem
= nSubItem
;
3786 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3787 lvItem
.pszText
= szDispText
;
3788 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3789 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3790 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3791 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= szCallback
;
3792 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3794 /* now check if we need to update the focus rectangle */
3795 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3797 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3798 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
3799 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3800 OffsetRect(&rcSelect
, pos
.x
, pos
.y
);
3801 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3802 OffsetRect(&rcStateIcon
, pos
.x
, pos
.y
);
3803 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3804 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3805 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
3806 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
3808 /* fill in the custom draw structure */
3809 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3811 hOldFont
= GetCurrentObject(hdc
, OBJ_FONT
);
3812 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3813 if (cdmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3814 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3815 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3816 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3817 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3818 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3819 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3821 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3822 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3824 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3825 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
3826 else if ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) == FALSE
)
3827 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
3829 /* in full row select, subitems, will just use main item's colors */
3830 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3831 nmlvcd
.clrTextBk
= CLR_NONE
;
3834 if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && (nSubItem
== 0))
3836 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
3839 TRACE("uStateImage=%d\n", uStateImage
);
3840 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
,
3841 rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
3846 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3847 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3849 TRACE("iImage=%d\n", lvItem
.iImage
);
3850 ImageList_DrawEx(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3851 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
, CLR_DEFAULT
,
3852 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3855 /* Don't bother painting item being edited */
3856 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3858 /* FIXME: temporary hack */
3859 rcSelect
.left
= rcLabel
.left
;
3861 /* draw the selection background, if we're drawing the main item */
3864 /* in icon mode, the label rect is really what we want to draw the
3866 if (uView
== LVS_ICON
)
3869 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3870 rcSelect
.right
= rcBox
.right
;
3872 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3873 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3874 if(lprcFocus
) *lprcFocus
= rcSelect
;
3877 /* figure out the text drawing flags */
3878 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3879 if (uView
== LVS_ICON
)
3880 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3883 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3885 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3886 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3887 default: uFormat
|= DT_LEFT
;
3890 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3892 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3893 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3895 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3897 /* for GRIDLINES reduce the bottom so the text formats correctly */
3898 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
3901 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3904 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3905 notify_postpaint(infoPtr
, &nmlvcd
);
3906 if (cdsubitemmode
& CDRF_NEWFONT
)
3907 SelectObject(hdc
, hOldFont
);
3913 * Draws listview items when in owner draw mode.
3916 * [I] infoPtr : valid pointer to the listview structure
3917 * [I] hdc : device context handle
3922 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3924 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
3925 DWORD cditemmode
= CDRF_DODEFAULT
;
3926 NMLVCUSTOMDRAW nmlvcd
;
3927 POINT Origin
, Position
;
3933 ZeroMemory(&dis
, sizeof(dis
));
3935 /* Get scroll info once before loop */
3936 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3938 /* iterate through the invalidated rows */
3939 while(iterator_next(i
))
3941 item
.iItem
= i
->nItem
;
3943 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3944 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3945 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3947 dis
.CtlType
= ODT_LISTVIEW
;
3949 dis
.itemID
= item
.iItem
;
3950 dis
.itemAction
= ODA_DRAWENTIRE
;
3952 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
3953 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
3954 dis
.hwndItem
= infoPtr
->hwndSelf
;
3956 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
3957 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
3958 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
3959 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
3960 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3961 dis
.itemData
= item
.lParam
;
3963 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
3966 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3967 * structure for the rest. of the paint cycle
3969 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
3970 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3971 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3973 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
3975 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
3976 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
3979 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
3980 notify_postpaint(infoPtr
, &nmlvcd
);
3986 * Draws listview items when in report display mode.
3989 * [I] infoPtr : valid pointer to the listview structure
3990 * [I] hdc : device context handle
3991 * [I] cdmode : custom draw mode
3996 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3999 RECT rcClip
, rcItem
;
4000 POINT Origin
, Position
;
4006 /* figure out what to draw */
4007 rgntype
= GetClipBox(hdc
, &rcClip
);
4008 if (rgntype
== NULLREGION
) return;
4010 /* Get scroll info once before loop */
4011 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4013 /* narrow down the columns we need to paint */
4014 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4016 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4017 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4019 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4021 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4022 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4024 iterator_rangeitems(&j
, colRange
);
4026 /* in full row select, we _have_ to draw the main item */
4027 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4030 /* iterate through the invalidated rows */
4031 while(iterator_next(i
))
4033 /* iterate through the invalidated columns */
4034 while(iterator_next(&j
))
4036 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4037 Position
.x
+= Origin
.x
;
4038 Position
.y
+= Origin
.y
;
4040 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4042 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4044 rcItem
.bottom
= infoPtr
->nItemHeight
;
4045 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
4046 if (!RectVisible(hdc
, &rcItem
)) continue;
4049 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
4052 iterator_destroy(&j
);
4057 * Draws the gridlines if necessary when in report display mode.
4060 * [I] infoPtr : valid pointer to the listview structure
4061 * [I] hdc : device context handle
4066 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
4071 RECT rcClip
, rcItem
;
4078 /* figure out what to draw */
4079 rgntype
= GetClipBox(hdc
, &rcClip
);
4080 if (rgntype
== NULLREGION
) return;
4082 /* Get scroll info once before loop */
4083 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4085 /* narrow down the columns we need to paint */
4086 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4088 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4089 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4091 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4093 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4094 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4096 iterator_rangeitems(&j
, colRange
);
4098 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
4100 hOldPen
= SelectObject ( hdc
, hPen
);
4102 /* draw the vertical lines for the columns */
4103 iterator_rangeitems(&j
, colRange
);
4104 while(iterator_next(&j
))
4106 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4107 if (rcItem
.left
== 0) continue; /* skip first column */
4108 rcItem
.left
+= Origin
.x
;
4109 rcItem
.right
+= Origin
.x
;
4110 rcItem
.top
= infoPtr
->rcList
.top
;
4111 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
4112 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
4113 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4114 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
4116 iterator_destroy(&j
);
4118 /* draw the horizontial lines for the rows */
4119 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
4120 rcItem
.left
= infoPtr
->rcList
.left
+ Origin
.x
;
4121 rcItem
.right
= infoPtr
->rcList
.right
+ Origin
.x
;
4122 rcItem
.bottom
= rcItem
.top
= Origin
.y
- 1;
4123 MoveToEx(hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4124 LineTo(hdc
, rcItem
.right
, rcItem
.top
);
4125 for(y
=itemheight
-1+Origin
.y
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
4127 rcItem
.bottom
= rcItem
.top
= y
;
4128 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
4129 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4130 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
4133 SelectObject( hdc
, hOldPen
);
4134 DeleteObject( hPen
);
4140 * Draws listview items when in list display mode.
4143 * [I] infoPtr : valid pointer to the listview structure
4144 * [I] hdc : device context handle
4145 * [I] cdmode : custom draw mode
4150 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4152 POINT Origin
, Position
;
4154 /* Get scroll info once before loop */
4155 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4157 while(iterator_prev(i
))
4159 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4160 Position
.x
+= Origin
.x
;
4161 Position
.y
+= Origin
.y
;
4163 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
4170 * Draws listview items.
4173 * [I] infoPtr : valid pointer to the listview structure
4174 * [I] hdc : device context handle
4175 * [I] prcErase : rect to be erased before refresh (may be NULL)
4180 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
4182 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4183 COLORREF oldTextColor
= 0, oldBkColor
= 0, oldClrTextBk
, oldClrText
;
4184 NMLVCUSTOMDRAW nmlvcd
;
4191 HBITMAP hbmp
= NULL
;
4193 LISTVIEW_DUMP(infoPtr
);
4195 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4196 TRACE("double buffering\n");
4198 hdc
= CreateCompatibleDC(hdcOrig
);
4200 ERR("Failed to create DC for backbuffer\n");
4203 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
4204 infoPtr
->rcList
.bottom
);
4206 ERR("Failed to create bitmap for backbuffer\n");
4211 SelectObject(hdc
, hbmp
);
4212 SelectObject(hdc
, infoPtr
->hFont
);
4214 /* Save dc values we're gonna trash while drawing
4215 * FIXME: Should be done in LISTVIEW_DrawItem() */
4216 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
4217 oldBkMode
= GetBkMode(hdc
);
4218 oldBkColor
= GetBkColor(hdc
);
4219 oldTextColor
= GetTextColor(hdc
);
4222 infoPtr
->bIsDrawing
= TRUE
;
4225 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
4226 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4227 /* If no erasing was done (usually because RedrawWindow was called
4228 * with RDW_INVALIDATE only) we need to copy the old contents into
4229 * the backbuffer before continuing. */
4230 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4231 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4232 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4233 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4236 /* FIXME: Shouldn't need to do this */
4237 oldClrTextBk
= infoPtr
->clrTextBk
;
4238 oldClrText
= infoPtr
->clrText
;
4240 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
4242 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
4243 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
4244 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4245 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
4246 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4248 /* Use these colors to draw the items */
4249 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
4250 infoPtr
->clrText
= nmlvcd
.clrText
;
4252 /* nothing to draw */
4253 if(infoPtr
->nItemCount
== 0) goto enddraw
;
4255 /* figure out what we need to draw */
4256 iterator_visibleitems(&i
, infoPtr
, hdc
);
4258 /* send cache hint notification */
4259 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4261 RANGE range
= iterator_range(&i
);
4264 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4265 nmlv
.iFrom
= range
.lower
;
4266 nmlv
.iTo
= range
.upper
- 1;
4267 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4270 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
4271 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4274 if (uView
== LVS_REPORT
)
4275 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4276 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4277 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4279 /* if we have a focus rect, draw it */
4280 if (infoPtr
->bFocus
)
4281 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
4283 iterator_destroy(&i
);
4286 /* For LVS_EX_GRIDLINES go and draw lines */
4287 /* This includes the case where there were *no* items */
4288 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
&&
4289 infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4290 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
4292 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4293 notify_postpaint(infoPtr
, &nmlvcd
);
4295 infoPtr
->clrTextBk
= oldClrTextBk
;
4296 infoPtr
->clrText
= oldClrText
;
4299 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4300 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4301 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4302 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4307 SelectObject(hdc
, hOldFont
);
4308 SetBkMode(hdc
, oldBkMode
);
4309 SetBkColor(hdc
, oldBkColor
);
4310 SetTextColor(hdc
, oldTextColor
);
4313 infoPtr
->bIsDrawing
= FALSE
;
4319 * Calculates the approximate width and height of a given number of items.
4322 * [I] infoPtr : valid pointer to the listview structure
4323 * [I] nItemCount : number of items
4324 * [I] wWidth : width
4325 * [I] wHeight : height
4328 * Returns a DWORD. The width in the low word and the height in high word.
4330 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4331 WORD wWidth
, WORD wHeight
)
4333 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4334 INT nItemCountPerColumn
= 1;
4335 INT nColumnCount
= 0;
4336 DWORD dwViewRect
= 0;
4338 if (nItemCount
== -1)
4339 nItemCount
= infoPtr
->nItemCount
;
4341 if (uView
== LVS_LIST
)
4343 if (wHeight
== 0xFFFF)
4345 /* use current height */
4346 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4349 if (wHeight
< infoPtr
->nItemHeight
)
4350 wHeight
= infoPtr
->nItemHeight
;
4354 if (infoPtr
->nItemHeight
> 0)
4356 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4357 if (nItemCountPerColumn
== 0)
4358 nItemCountPerColumn
= 1;
4360 if (nItemCount
% nItemCountPerColumn
!= 0)
4361 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4363 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4367 /* Microsoft padding magic */
4368 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4369 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4371 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4373 else if (uView
== LVS_REPORT
)
4377 if (infoPtr
->nItemCount
> 0)
4379 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4380 wWidth
= rcBox
.right
- rcBox
.left
;
4381 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4385 /* use current height and width */
4386 if (wHeight
== 0xffff)
4387 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4388 if (wWidth
== 0xffff)
4389 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4392 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4394 else if (uView
== LVS_SMALLICON
)
4395 FIXME("uView == LVS_SMALLICON: not implemented\n");
4396 else if (uView
== LVS_ICON
)
4397 FIXME("uView == LVS_ICON: not implemented\n");
4405 * Create a drag image list for the specified item.
4408 * [I] infoPtr : valid pointer to the listview structure
4409 * [I] iItem : index of item
4410 * [O] lppt : Upperr-left corner of the image
4413 * Returns a handle to the image list if successful, NULL otherwise.
4415 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4421 HBITMAP hbmp
, hOldbmp
;
4422 HIMAGELIST dragList
= 0;
4423 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
4425 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4428 rcItem
.left
= LVIR_BOUNDS
;
4429 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4432 lppt
->x
= rcItem
.left
;
4433 lppt
->y
= rcItem
.top
;
4435 size
.cx
= rcItem
.right
- rcItem
.left
;
4436 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4438 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4439 hdc
= CreateCompatibleDC(hdcOrig
);
4440 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4441 hOldbmp
= SelectObject(hdc
, hbmp
);
4443 rcItem
.left
= rcItem
.top
= 0;
4444 rcItem
.right
= size
.cx
;
4445 rcItem
.bottom
= size
.cy
;
4446 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4449 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4451 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4452 SelectObject(hdc
, hOldbmp
);
4453 ImageList_Add(dragList
, hbmp
, 0);
4456 SelectObject(hdc
, hOldbmp
);
4460 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4462 TRACE("ret=%p\n", dragList
);
4470 * Removes all listview items and subitems.
4473 * [I] infoPtr : valid pointer to the listview structure
4479 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
4482 HDPA hdpaSubItems
= NULL
;
4489 /* we do it directly, to avoid notifications */
4490 ranges_clear(infoPtr
->selectionRanges
);
4491 infoPtr
->nSelectionMark
= -1;
4492 infoPtr
->nFocusedItem
= -1;
4493 SetRectEmpty(&infoPtr
->rcFocus
);
4494 /* But we are supposed to leave nHotItem as is! */
4497 /* send LVN_DELETEALLITEMS notification */
4498 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4500 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4502 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4504 /* send LVN_DELETEITEM notification, if not suppressed */
4505 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4506 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4508 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4509 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4511 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, j
);
4512 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4515 DPA_Destroy(hdpaSubItems
);
4516 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4518 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4519 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4520 infoPtr
->nItemCount
--;
4525 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4526 LISTVIEW_UpdateScroll(infoPtr
);
4528 LISTVIEW_InvalidateList(infoPtr
);
4535 * Scrolls, and updates the columns, when a column is changing width.
4538 * [I] infoPtr : valid pointer to the listview structure
4539 * [I] nColumn : column to scroll
4540 * [I] dx : amount of scroll, in pixels
4545 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4547 COLUMN_INFO
*lpColumnInfo
;
4552 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4553 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4554 rcCol
= lpColumnInfo
->rcHeader
;
4555 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4556 rcCol
.left
= rcCol
.right
;
4558 /* adjust the other columns */
4559 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4561 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4562 lpColumnInfo
->rcHeader
.left
+= dx
;
4563 lpColumnInfo
->rcHeader
.right
+= dx
;
4566 /* do not update screen if not in report mode */
4567 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4569 /* if we have a focus, we must first erase the focus rect */
4570 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4572 /* Need to reset the item width when inserting a new column */
4573 infoPtr
->nItemWidth
+= dx
;
4575 LISTVIEW_UpdateScroll(infoPtr
);
4576 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
4578 /* scroll to cover the deleted column, and invalidate for redraw */
4579 rcOld
= infoPtr
->rcList
;
4580 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
4581 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4583 /* we can restore focus now */
4584 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4589 * Removes a column from the listview control.
4592 * [I] infoPtr : valid pointer to the listview structure
4593 * [I] nColumn : column index
4599 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4603 TRACE("nColumn=%d\n", nColumn
);
4605 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4606 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4608 /* While the MSDN specifically says that column zero should not be deleted,
4609 what actually happens is that the column itself is deleted but no items or subitems
4613 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4615 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4618 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4619 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4621 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4623 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4625 INT nItem
, nSubItem
, i
;
4627 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4629 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4632 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4634 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
4635 if (lpSubItem
->iSubItem
== nColumn
)
4638 lpDelItem
= lpSubItem
;
4640 else if (lpSubItem
->iSubItem
> nColumn
)
4642 lpSubItem
->iSubItem
--;
4646 /* if we found our subitem, zapp it */
4650 if (is_textW(lpDelItem
->hdr
.pszText
))
4651 Free(lpDelItem
->hdr
.pszText
);
4656 /* free dpa memory */
4657 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4662 /* update the other column info */
4663 LISTVIEW_UpdateItemSize(infoPtr
);
4664 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4665 LISTVIEW_InvalidateList(infoPtr
);
4667 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4674 * Invalidates the listview after an item's insertion or deletion.
4677 * [I] infoPtr : valid pointer to the listview structure
4678 * [I] nItem : item index
4679 * [I] dir : -1 if deleting, 1 if inserting
4684 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4686 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4687 INT nPerCol
, nItemCol
, nItemRow
;
4691 /* if we don't refresh, what's the point of scrolling? */
4692 if (!is_redrawing(infoPtr
)) return;
4694 assert (abs(dir
) == 1);
4696 /* arrange icons if autoarrange is on */
4697 if (is_autoarrange(infoPtr
))
4699 BOOL arrange
= TRUE
;
4700 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4701 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4702 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4705 /* scrollbars need updating */
4706 LISTVIEW_UpdateScroll(infoPtr
);
4708 /* figure out the item's position */
4709 if (uView
== LVS_REPORT
)
4710 nPerCol
= infoPtr
->nItemCount
+ 1;
4711 else if (uView
== LVS_LIST
)
4712 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4713 else /* LVS_ICON, or LVS_SMALLICON */
4716 nItemCol
= nItem
/ nPerCol
;
4717 nItemRow
= nItem
% nPerCol
;
4718 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4720 /* move the items below up a slot */
4721 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4722 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4723 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4724 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4725 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4726 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4727 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4729 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
4730 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4731 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4734 /* report has only that column, so we're done */
4735 if (uView
== LVS_REPORT
) return;
4737 /* now for LISTs, we have to deal with the columns to the right */
4738 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4740 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4741 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4742 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4743 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4744 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4745 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4750 * Removes an item from the listview control.
4753 * [I] infoPtr : valid pointer to the listview structure
4754 * [I] nItem : item index
4760 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4763 const UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4764 const BOOL is_icon
= (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
);
4766 TRACE("(nItem=%d)\n", nItem
);
4768 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4770 /* remove selection, and focus */
4772 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4773 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4775 /* send LVN_DELETEITEM notification. */
4776 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
4778 /* we need to do this here, because we'll be deleting stuff */
4780 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4782 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4788 hdpaSubItems
= (HDPA
)DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4789 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4791 hdrItem
= (ITEMHDR
*)DPA_GetPtr(hdpaSubItems
, i
);
4792 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4795 DPA_Destroy(hdpaSubItems
);
4800 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4801 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4804 infoPtr
->nItemCount
--;
4805 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4807 /* now is the invalidation fun */
4809 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4816 * Callback implementation for editlabel control
4819 * [I] infoPtr : valid pointer to the listview structure
4820 * [I] pszText : modified text
4821 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4827 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4829 HWND hwndSelf
= infoPtr
->hwndSelf
;
4830 NMLVDISPINFOW dispInfo
;
4832 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4834 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4835 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
;
4836 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4837 dispInfo
.item
.iSubItem
= 0;
4838 dispInfo
.item
.stateMask
= ~0;
4839 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4840 /* add the text from the edit in */
4841 dispInfo
.item
.mask
|= LVIF_TEXT
;
4842 dispInfo
.item
.pszText
= pszText
;
4843 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4845 /* Do we need to update the Item Text */
4846 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4847 if (!IsWindow(hwndSelf
))
4849 if (!pszText
) return TRUE
;
4851 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4853 HDPA hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nEditLabelItem
);
4854 ITEM_INFO
* lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
4855 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
4857 LISTVIEW_InvalidateItem(infoPtr
, infoPtr
->nEditLabelItem
);
4862 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4863 dispInfo
.item
.mask
= LVIF_TEXT
;
4864 dispInfo
.item
.iItem
= infoPtr
->nEditLabelItem
;
4865 dispInfo
.item
.iSubItem
= 0;
4866 dispInfo
.item
.pszText
= pszText
;
4867 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4868 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4873 * Begin in place editing of specified list view item
4876 * [I] infoPtr : valid pointer to the listview structure
4877 * [I] nItem : item index
4878 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4884 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4886 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4887 NMLVDISPINFOW dispInfo
;
4889 HWND hwndSelf
= infoPtr
->hwndSelf
;
4891 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4893 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4894 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4896 infoPtr
->nEditLabelItem
= nItem
;
4898 /* Is the EditBox still there, if so remove it */
4899 if(infoPtr
->hwndEdit
!= 0)
4901 SetFocus(infoPtr
->hwndSelf
);
4902 infoPtr
->hwndEdit
= 0;
4905 LISTVIEW_SetSelection(infoPtr
, nItem
);
4906 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4907 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4909 rect
.left
= LVIR_LABEL
;
4910 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4912 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4913 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4914 dispInfo
.item
.iItem
= nItem
;
4915 dispInfo
.item
.iSubItem
= 0;
4916 dispInfo
.item
.stateMask
= ~0;
4917 dispInfo
.item
.pszText
= szDispText
;
4918 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4919 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
4921 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
4922 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
4923 if (!infoPtr
->hwndEdit
) return 0;
4925 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4927 if (!IsWindow(hwndSelf
))
4929 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
4930 infoPtr
->hwndEdit
= 0;
4934 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
4935 SetFocus(infoPtr
->hwndEdit
);
4936 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
4937 return infoPtr
->hwndEdit
;
4943 * Ensures the specified item is visible, scrolling into view if necessary.
4946 * [I] infoPtr : valid pointer to the listview structure
4947 * [I] nItem : item index
4948 * [I] bPartial : partially or entirely visible
4954 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
4956 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4957 INT nScrollPosHeight
= 0;
4958 INT nScrollPosWidth
= 0;
4959 INT nHorzAdjust
= 0;
4960 INT nVertAdjust
= 0;
4963 RECT rcItem
, rcTemp
;
4965 rcItem
.left
= LVIR_BOUNDS
;
4966 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
4968 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
4970 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
4972 /* scroll left/right, but in LVS_REPORT mode */
4973 if (uView
== LVS_LIST
)
4974 nScrollPosWidth
= infoPtr
->nItemWidth
;
4975 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
4976 nScrollPosWidth
= 1;
4978 if (rcItem
.left
< infoPtr
->rcList
.left
)
4981 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
4986 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
4990 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
4992 /* scroll up/down, but not in LVS_LIST mode */
4993 if (uView
== LVS_REPORT
)
4994 nScrollPosHeight
= infoPtr
->nItemHeight
;
4995 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
4996 nScrollPosHeight
= 1;
4998 if (rcItem
.top
< infoPtr
->rcList
.top
)
5001 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
5006 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
5010 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
5012 if (nScrollPosWidth
)
5014 INT diff
= nHorzDiff
/ nScrollPosWidth
;
5015 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
5016 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5019 if (nScrollPosHeight
)
5021 INT diff
= nVertDiff
/ nScrollPosHeight
;
5022 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
5023 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5031 * Searches for an item with specific characteristics.
5034 * [I] hwnd : window handle
5035 * [I] nStart : base item index
5036 * [I] lpFindInfo : item information to look for
5039 * SUCCESS : index of item
5042 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5043 const LVFINDINFOW
*lpFindInfo
)
5045 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5046 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5047 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
5048 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
5049 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
5050 POINT Position
, Destination
;
5053 if (!lpFindInfo
|| nItem
< 0) return -1;
5056 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
5058 lvItem
.mask
|= LVIF_TEXT
;
5059 lvItem
.pszText
= szDispText
;
5060 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5063 if (lpFindInfo
->flags
& LVFI_WRAP
)
5066 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
5067 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
5072 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5073 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
5074 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
5075 switch(lpFindInfo
->vkDirection
)
5077 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
5078 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
5079 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
5080 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
5081 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
5082 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5083 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5085 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
5086 Destination
.x
= rcArea
.right
;
5087 Destination
.y
= rcArea
.bottom
;
5089 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
5093 else Destination
.x
= Destination
.y
= 0;
5095 /* if LVFI_PARAM is specified, all other flags are ignored */
5096 if (lpFindInfo
->flags
& LVFI_PARAM
)
5098 lvItem
.mask
|= LVIF_PARAM
;
5100 lvItem
.mask
&= ~LVIF_TEXT
;
5104 for (; nItem
< nLast
; nItem
++)
5106 lvItem
.iItem
= nItem
;
5107 lvItem
.iSubItem
= 0;
5108 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
5110 if (lvItem
.mask
& LVIF_PARAM
)
5112 if (lpFindInfo
->lParam
== lvItem
.lParam
)
5118 if (lvItem
.mask
& LVIF_TEXT
)
5120 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
5122 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
5126 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
5130 if (!bNearest
) return nItem
;
5132 /* This is very inefficient. To do a good job here,
5133 * we need a sorted array of (x,y) item positions */
5134 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5136 /* compute the distance^2 to the destination */
5137 xdist
= Destination
.x
- Position
.x
;
5138 ydist
= Destination
.y
- Position
.y
;
5139 dist
= xdist
* xdist
+ ydist
* ydist
;
5141 /* remember the distance, and item if it's closer */
5145 nNearestItem
= nItem
;
5152 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
5157 return nNearestItem
;
5162 * Searches for an item with specific characteristics.
5165 * [I] hwnd : window handle
5166 * [I] nStart : base item index
5167 * [I] lpFindInfo : item information to look for
5170 * SUCCESS : index of item
5173 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5174 const LVFINDINFOA
*lpFindInfo
)
5176 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
5181 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
5182 if (hasText
) fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
5183 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
5184 textfreeT(strW
, FALSE
);
5190 * Retrieves the background image of the listview control.
5193 * [I] infoPtr : valid pointer to the listview structure
5194 * [O] lpBkImage : background image attributes
5200 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5202 /* FIXME (listview, "empty stub!\n"); */
5208 * Retrieves column attributes.
5211 * [I] infoPtr : valid pointer to the listview structure
5212 * [I] nColumn : column index
5213 * [IO] lpColumn : column information
5214 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5215 * otherwise it is in fact a LPLVCOLUMNA
5221 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
5223 COLUMN_INFO
*lpColumnInfo
;
5226 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5227 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
5229 /* initialize memory */
5230 ZeroMemory(&hdi
, sizeof(hdi
));
5232 if (lpColumn
->mask
& LVCF_TEXT
)
5234 hdi
.mask
|= HDI_TEXT
;
5235 hdi
.pszText
= lpColumn
->pszText
;
5236 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
5239 if (lpColumn
->mask
& LVCF_IMAGE
)
5240 hdi
.mask
|= HDI_IMAGE
;
5242 if (lpColumn
->mask
& LVCF_ORDER
)
5243 hdi
.mask
|= HDI_ORDER
;
5245 if (lpColumn
->mask
& LVCF_SUBITEM
)
5246 hdi
.mask
|= HDI_LPARAM
;
5248 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
5250 if (lpColumn
->mask
& LVCF_FMT
)
5251 lpColumn
->fmt
= lpColumnInfo
->fmt
;
5253 if (lpColumn
->mask
& LVCF_WIDTH
)
5254 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
5256 if (lpColumn
->mask
& LVCF_IMAGE
)
5257 lpColumn
->iImage
= hdi
.iImage
;
5259 if (lpColumn
->mask
& LVCF_ORDER
)
5260 lpColumn
->iOrder
= hdi
.iOrder
;
5262 if (lpColumn
->mask
& LVCF_SUBITEM
)
5263 lpColumn
->iSubItem
= hdi
.lParam
;
5269 static BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
5276 /* FIXME: little hack */
5277 for (i
= 0; i
< iCount
; i
++)
5285 * Retrieves the column width.
5288 * [I] infoPtr : valid pointer to the listview structure
5289 * [I] int : column index
5292 * SUCCESS : column width
5295 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5297 INT nColumnWidth
= 0;
5300 TRACE("nColumn=%d\n", nColumn
);
5302 /* we have a 'column' in LIST and REPORT mode only */
5303 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
5306 nColumnWidth
= infoPtr
->nItemWidth
;
5309 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5310 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5311 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5313 * TODO: should we do the same in LVM_GETCOLUMN?
5315 hdItem
.mask
= HDI_WIDTH
;
5316 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
5318 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
5321 nColumnWidth
= hdItem
.cxy
;
5325 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5326 return nColumnWidth
;
5331 * In list or report display mode, retrieves the number of items that can fit
5332 * vertically in the visible area. In icon or small icon display mode,
5333 * retrieves the total number of visible items.
5336 * [I] infoPtr : valid pointer to the listview structure
5339 * Number of fully visible items.
5341 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
5343 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
5347 return infoPtr
->nItemCount
;
5349 return LISTVIEW_GetCountPerColumn(infoPtr
);
5351 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5359 * Retrieves an image list handle.
5362 * [I] infoPtr : valid pointer to the listview structure
5363 * [I] nImageList : image list identifier
5366 * SUCCESS : image list handle
5369 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5373 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5374 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5375 case LVSIL_STATE
: return infoPtr
->himlState
;
5380 /* LISTVIEW_GetISearchString */
5384 * Retrieves item attributes.
5387 * [I] hwnd : window handle
5388 * [IO] lpLVItem : item info
5389 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5390 * if FALSE, then lpLVItem is a LPLVITEMA.
5393 * This is the internal 'GetItem' interface -- it tries to
5394 * be smart and avoid text copies, if possible, by modifying
5395 * lpLVItem->pszText to point to the text string. Please note
5396 * that this is not always possible (e.g. OWNERDATA), so on
5397 * entry you *must* supply valid values for pszText, and cchTextMax.
5398 * The only difference to the documented interface is that upon
5399 * return, you should use *only* the lpLVItem->pszText, rather than
5400 * the buffer pointer you provided on input. Most code already does
5401 * that, so it's not a problem.
5402 * For the two cases when the text must be copied (that is,
5403 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5409 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5411 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5412 NMLVDISPINFOW dispInfo
;
5418 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5420 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5423 if (lpLVItem
->mask
== 0) return TRUE
;
5425 /* make a local copy */
5426 isubitem
= lpLVItem
->iSubItem
;
5428 /* a quick optimization if all we're asked is the focus state
5429 * these queries are worth optimising since they are common,
5430 * and can be answered in constant time, without the heavy accesses */
5431 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5432 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5434 lpLVItem
->state
= 0;
5435 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5436 lpLVItem
->state
|= LVIS_FOCUSED
;
5440 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5442 /* if the app stores all the data, handle it separately */
5443 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5445 dispInfo
.item
.state
= 0;
5447 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5448 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5450 /* NOTE: copy only fields which we _know_ are initialized, some apps
5451 * depend on the uninitialized fields being 0 */
5452 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5453 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5454 dispInfo
.item
.iSubItem
= isubitem
;
5455 if (lpLVItem
->mask
& LVIF_TEXT
)
5457 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5458 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5460 if (lpLVItem
->mask
& LVIF_STATE
)
5461 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5462 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5463 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5464 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5466 /* full size structure expected - _WIN32IE >= 0x560 */
5467 *lpLVItem
= dispInfo
.item
;
5469 else if (lpLVItem
->mask
& LVIF_INDENT
)
5471 /* indent member expected - _WIN32IE >= 0x300 */
5472 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5476 /* minimal structure expected */
5477 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5479 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5482 /* make sure lParam is zeroed out */
5483 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5485 /* we store only a little state, so if we're not asked, we're done */
5486 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5488 /* if focus is handled by us, report it */
5489 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5491 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5492 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5493 lpLVItem
->state
|= LVIS_FOCUSED
;
5496 /* and do the same for selection, if we handle it */
5497 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5499 lpLVItem
->state
&= ~LVIS_SELECTED
;
5500 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5501 lpLVItem
->state
|= LVIS_SELECTED
;
5507 /* find the item and subitem structures before we proceed */
5508 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5509 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
5514 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5515 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5518 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5523 pItemHdr
= &lpItem
->hdr
;
5525 /* Do we need to query the state from the app? */
5526 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5528 dispInfo
.item
.mask
|= LVIF_STATE
;
5529 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5532 /* Do we need to enquire about the image? */
5533 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5534 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5536 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5537 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5540 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5541 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5543 dispInfo
.item
.mask
|= LVIF_TEXT
;
5544 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5545 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5546 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5547 *dispInfo
.item
.pszText
= '\0';
5550 /* If we don't have all the requested info, query the application */
5551 if (dispInfo
.item
.mask
!= 0)
5553 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5554 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5555 dispInfo
.item
.lParam
= lpItem
->lParam
;
5556 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5557 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5560 /* we should not store values for subitems */
5561 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5563 /* Now, handle the iImage field */
5564 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5566 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5567 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5568 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5570 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5572 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5573 lpLVItem
->iImage
= pItemHdr
->iImage
;
5575 lpLVItem
->iImage
= 0;
5578 /* The pszText field */
5579 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5581 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5582 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5584 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5586 else if (lpLVItem
->mask
& LVIF_TEXT
)
5588 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5589 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5592 /* Next is the lParam field */
5593 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5595 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5596 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5597 lpItem
->lParam
= dispInfo
.item
.lParam
;
5599 else if (lpLVItem
->mask
& LVIF_PARAM
)
5600 lpLVItem
->lParam
= lpItem
->lParam
;
5602 /* if this is a subitem, we're done */
5603 if (isubitem
) return TRUE
;
5605 /* ... the state field (this one is different due to uCallbackmask) */
5606 if (lpLVItem
->mask
& LVIF_STATE
)
5608 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
5609 if (dispInfo
.item
.mask
& LVIF_STATE
)
5611 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5612 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5614 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5616 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5617 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5618 lpLVItem
->state
|= LVIS_FOCUSED
;
5620 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5622 lpLVItem
->state
&= ~LVIS_SELECTED
;
5623 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5624 lpLVItem
->state
|= LVIS_SELECTED
;
5628 /* and last, but not least, the indent field */
5629 if (lpLVItem
->mask
& LVIF_INDENT
)
5630 lpLVItem
->iIndent
= lpItem
->iIndent
;
5637 * Retrieves item attributes.
5640 * [I] hwnd : window handle
5641 * [IO] lpLVItem : item info
5642 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5643 * if FALSE, then lpLVItem is a LPLVITEMA.
5646 * This is the external 'GetItem' interface -- it properly copies
5647 * the text in the provided buffer.
5653 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5658 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5661 pszText
= lpLVItem
->pszText
;
5662 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5663 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5664 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5665 lpLVItem
->pszText
= pszText
;
5673 * Retrieves the position (upper-left) of the listview control item.
5674 * Note that for LVS_ICON style, the upper-left is that of the icon
5675 * and not the bounding box.
5678 * [I] infoPtr : valid pointer to the listview structure
5679 * [I] nItem : item index
5680 * [O] lpptPosition : coordinate information
5686 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5688 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5691 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5693 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5695 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5696 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5698 if (uView
== LVS_ICON
)
5700 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5701 lpptPosition
->y
+= ICON_TOP_PADDING
;
5703 lpptPosition
->x
+= Origin
.x
;
5704 lpptPosition
->y
+= Origin
.y
;
5706 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
5713 * Retrieves the bounding rectangle for a listview control item.
5716 * [I] infoPtr : valid pointer to the listview structure
5717 * [I] nItem : item index
5718 * [IO] lprc : bounding rectangle coordinates
5719 * lprc->left specifies the portion of the item for which the bounding
5720 * rectangle will be retrieved.
5722 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5723 * including the icon and label.
5726 * * Experiment shows that native control returns:
5727 * * width = min (48, length of text line)
5728 * * .left = position.x - (width - iconsize.cx)/2
5729 * * .right = .left + width
5730 * * height = #lines of text * ntmHeight + icon height + 8
5731 * * .top = position.y - 2
5732 * * .bottom = .top + height
5733 * * separation between items .y = itemSpacing.cy - height
5734 * * .x = itemSpacing.cx - width
5735 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5738 * * Experiment shows that native control returns:
5739 * * width = iconSize.cx + 16
5740 * * .left = position.x - (width - iconsize.cx)/2
5741 * * .right = .left + width
5742 * * height = iconSize.cy + 4
5743 * * .top = position.y - 2
5744 * * .bottom = .top + height
5745 * * separation between items .y = itemSpacing.cy - height
5746 * * .x = itemSpacing.cx - width
5747 * LVIR_LABEL Returns the bounding rectangle of the item text.
5750 * * Experiment shows that native control returns:
5751 * * width = text length
5752 * * .left = position.x - width/2
5753 * * .right = .left + width
5754 * * height = ntmH * linecount + 2
5755 * * .top = position.y + iconSize.cy + 6
5756 * * .bottom = .top + height
5757 * * separation between items .y = itemSpacing.cy - height
5758 * * .x = itemSpacing.cx - width
5759 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5760 * rectangles, but excludes columns in report view.
5767 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5768 * upon whether the window has the focus currently and on whether the item
5769 * is the one with the focus. Ensure that the control's record of which
5770 * item has the focus agrees with the items' records.
5772 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5774 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5775 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5776 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5777 POINT Position
, Origin
;
5780 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5782 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5784 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5785 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5787 /* Be smart and try to figure out the minimum we have to do */
5788 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5789 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5790 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5791 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5792 oversizedBox
= TRUE
;
5794 /* get what we need from the item before hand, so we make
5795 * only one request. This can speed up things, if data
5796 * is stored on the app side */
5798 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5799 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5800 lvItem
.iItem
= nItem
;
5801 lvItem
.iSubItem
= 0;
5802 lvItem
.pszText
= szDispText
;
5803 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5804 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5805 /* we got the state already up, simulate it here, to avoid a reget */
5806 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5808 lvItem
.mask
|= LVIF_STATE
;
5809 lvItem
.stateMask
= LVIS_FOCUSED
;
5810 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5813 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5814 lprc
->left
= LVIR_BOUNDS
;
5818 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5822 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
5826 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5829 case LVIR_SELECTBOUNDS
:
5830 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
5834 WARN("Unknown value: %d\n", lprc
->left
);
5838 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5840 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
5847 * Retrieves the spacing between listview control items.
5850 * [I] infoPtr : valid pointer to the listview structure
5851 * [IO] lprc : rectangle to receive the output
5852 * on input, lprc->top = nSubItem
5853 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5855 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5856 * not only those of the first column.
5857 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5863 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5869 if (!lprc
) return FALSE
;
5871 nColumn
= lprc
->top
;
5873 TRACE("(nItem=%d, nSubItem=%d)\n", nItem
, lprc
->top
);
5874 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5876 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5878 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5880 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5882 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5885 lvItem
.iItem
= nItem
;
5886 lvItem
.iSubItem
= nColumn
;
5888 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5892 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5897 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5901 ERR("Unknown bounds=%d\n", lprc
->left
);
5905 OffsetRect(lprc
, Position
.x
, Position
.y
);
5912 * Retrieves the width of a label.
5915 * [I] infoPtr : valid pointer to the listview structure
5918 * SUCCESS : string width (in pixels)
5921 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
5923 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5926 TRACE("(nItem=%d)\n", nItem
);
5928 lvItem
.mask
= LVIF_TEXT
;
5929 lvItem
.iItem
= nItem
;
5930 lvItem
.iSubItem
= 0;
5931 lvItem
.pszText
= szDispText
;
5932 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5933 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5935 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
5940 * Retrieves the spacing between listview control items.
5943 * [I] infoPtr : valid pointer to the listview structure
5944 * [I] bSmall : flag for small or large icon
5947 * Horizontal + vertical spacing
5949 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
5955 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
5959 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
5960 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
5962 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
5969 * Retrieves the state of a listview control item.
5972 * [I] infoPtr : valid pointer to the listview structure
5973 * [I] nItem : item index
5974 * [I] uMask : state mask
5977 * State specified by the mask.
5979 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
5983 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
5985 lvItem
.iItem
= nItem
;
5986 lvItem
.iSubItem
= 0;
5987 lvItem
.mask
= LVIF_STATE
;
5988 lvItem
.stateMask
= uMask
;
5989 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5991 return lvItem
.state
& uMask
;
5996 * Retrieves the text of a listview control item or subitem.
5999 * [I] hwnd : window handle
6000 * [I] nItem : item index
6001 * [IO] lpLVItem : item information
6002 * [I] isW : TRUE if lpLVItem is Unicode
6005 * SUCCESS : string length
6008 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
6010 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6012 lpLVItem
->mask
= LVIF_TEXT
;
6013 lpLVItem
->iItem
= nItem
;
6014 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
6016 return textlenT(lpLVItem
->pszText
, isW
);
6021 * Searches for an item based on properties + relationships.
6024 * [I] infoPtr : valid pointer to the listview structure
6025 * [I] nItem : item index
6026 * [I] uFlags : relationship flag
6029 * SUCCESS : item index
6032 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
6034 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6036 LVFINDINFOW lvFindInfo
;
6037 INT nCountPerColumn
;
6041 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
6042 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
6044 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
6046 if (uFlags
& LVNI_CUT
)
6049 if (uFlags
& LVNI_DROPHILITED
)
6050 uMask
|= LVIS_DROPHILITED
;
6052 if (uFlags
& LVNI_FOCUSED
)
6053 uMask
|= LVIS_FOCUSED
;
6055 if (uFlags
& LVNI_SELECTED
)
6056 uMask
|= LVIS_SELECTED
;
6058 /* if we're asked for the focused item, that's only one,
6059 * so it's worth optimizing */
6060 if (uFlags
& LVNI_FOCUSED
)
6062 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
6063 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
6066 if (uFlags
& LVNI_ABOVE
)
6068 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6073 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6079 /* Special case for autoarrange - move 'til the top of a list */
6080 if (is_autoarrange(infoPtr
))
6082 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6083 while (nItem
- nCountPerRow
>= 0)
6085 nItem
-= nCountPerRow
;
6086 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6091 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6092 lvFindInfo
.vkDirection
= VK_UP
;
6093 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6094 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6096 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6101 else if (uFlags
& LVNI_BELOW
)
6103 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6105 while (nItem
< infoPtr
->nItemCount
)
6108 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6114 /* Special case for autoarrange - move 'til the bottom of a list */
6115 if (is_autoarrange(infoPtr
))
6117 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6118 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
6120 nItem
+= nCountPerRow
;
6121 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6126 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6127 lvFindInfo
.vkDirection
= VK_DOWN
;
6128 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6129 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6131 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6136 else if (uFlags
& LVNI_TOLEFT
)
6138 if (uView
== LVS_LIST
)
6140 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6141 while (nItem
- nCountPerColumn
>= 0)
6143 nItem
-= nCountPerColumn
;
6144 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6148 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6150 /* Special case for autoarrange - move 'ti the beginning of a row */
6151 if (is_autoarrange(infoPtr
))
6153 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6154 while (nItem
% nCountPerRow
> 0)
6157 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6162 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6163 lvFindInfo
.vkDirection
= VK_LEFT
;
6164 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6165 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6167 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6172 else if (uFlags
& LVNI_TORIGHT
)
6174 if (uView
== LVS_LIST
)
6176 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6177 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
6179 nItem
+= nCountPerColumn
;
6180 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6184 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6186 /* Special case for autoarrange - move 'til the end of a row */
6187 if (is_autoarrange(infoPtr
))
6189 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6190 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
6193 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6198 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6199 lvFindInfo
.vkDirection
= VK_RIGHT
;
6200 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6201 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6203 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6212 /* search by index */
6213 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
6215 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
6223 /* LISTVIEW_GetNumberOfWorkAreas */
6227 * Retrieves the origin coordinates when in icon or small icon display mode.
6230 * [I] infoPtr : valid pointer to the listview structure
6231 * [O] lpptOrigin : coordinate information
6236 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
6238 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6239 INT nHorzPos
= 0, nVertPos
= 0;
6240 SCROLLINFO scrollInfo
;
6242 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
6243 scrollInfo
.fMask
= SIF_POS
;
6245 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
6246 nHorzPos
= scrollInfo
.nPos
;
6247 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
6248 nVertPos
= scrollInfo
.nPos
;
6250 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
6252 lpptOrigin
->x
= infoPtr
->rcList
.left
;
6253 lpptOrigin
->y
= infoPtr
->rcList
.top
;
6254 if (uView
== LVS_LIST
)
6255 nHorzPos
*= infoPtr
->nItemWidth
;
6256 else if (uView
== LVS_REPORT
)
6257 nVertPos
*= infoPtr
->nItemHeight
;
6259 lpptOrigin
->x
-= nHorzPos
;
6260 lpptOrigin
->y
-= nVertPos
;
6262 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
6267 * Retrieves the width of a string.
6270 * [I] hwnd : window handle
6271 * [I] lpszText : text string to process
6272 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6275 * SUCCESS : string width (in pixels)
6278 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
6283 if (is_textT(lpszText
, isW
))
6285 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6286 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6287 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6290 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6292 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6293 SelectObject(hdc
, hOldFont
);
6294 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6296 return stringSize
.cx
;
6301 * Determines which listview item is located at the specified position.
6304 * [I] infoPtr : valid pointer to the listview structure
6305 * [IO] lpht : hit test information
6306 * [I] subitem : fill out iSubItem.
6307 * [I] select : return the index only if the hit selects the item
6310 * (mm 20001022): We must not allow iSubItem to be touched, for
6311 * an app might pass only a structure with space up to iItem!
6312 * (MS Office 97 does that for instance in the file open dialog)
6315 * SUCCESS : item index
6318 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
6320 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6321 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6322 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6323 POINT Origin
, Position
, opt
;
6328 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
6332 if (subitem
) lpht
->iSubItem
= 0;
6334 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6335 lpht
->flags
|= LVHT_TOLEFT
;
6336 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6337 lpht
->flags
|= LVHT_TORIGHT
;
6339 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6340 lpht
->flags
|= LVHT_ABOVE
;
6341 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6342 lpht
->flags
|= LVHT_BELOW
;
6344 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6345 if (lpht
->flags
) return -1;
6347 lpht
->flags
|= LVHT_NOWHERE
;
6349 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6351 /* first deal with the large items */
6352 rcSearch
.left
= lpht
->pt
.x
;
6353 rcSearch
.top
= lpht
->pt
.y
;
6354 rcSearch
.right
= rcSearch
.left
+ 1;
6355 rcSearch
.bottom
= rcSearch
.top
+ 1;
6357 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6358 iterator_next(&i
); /* go to first item in the sequence */
6360 iterator_destroy(&i
);
6362 TRACE("lpht->iItem=%d\n", iItem
);
6363 if (iItem
== -1) return -1;
6365 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6366 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
6367 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6368 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6369 lvItem
.iItem
= iItem
;
6370 lvItem
.iSubItem
= 0;
6371 lvItem
.pszText
= szDispText
;
6372 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6373 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6374 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6376 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
6377 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6378 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6379 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6381 if (uView
== LVS_REPORT
)
6385 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6386 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6388 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
6389 if (!PtInRect(&rcBounds
, opt
)) return -1;
6391 if (PtInRect(&rcIcon
, opt
))
6392 lpht
->flags
|= LVHT_ONITEMICON
;
6393 else if (PtInRect(&rcLabel
, opt
))
6394 lpht
->flags
|= LVHT_ONITEMLABEL
;
6395 else if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && PtInRect(&rcState
, opt
))
6396 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6397 if (lpht
->flags
& LVHT_ONITEM
)
6398 lpht
->flags
&= ~LVHT_NOWHERE
;
6400 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6401 if (uView
== LVS_REPORT
&& subitem
)
6405 rcBounds
.right
= rcBounds
.left
;
6406 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6408 rcBounds
.left
= rcBounds
.right
;
6409 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6410 if (PtInRect(&rcBounds
, opt
))
6418 if (select
&& !(uView
== LVS_REPORT
&&
6419 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6420 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6422 if (uView
== LVS_REPORT
)
6424 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6425 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6427 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6429 return lpht
->iItem
= iItem
;
6433 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6434 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6435 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6436 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6437 their own sort proc. when sending LVM_SORTITEMS.
6440 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6442 LVS_SORTXXX must be specified,
6443 LVS_OWNERDRAW is not set,
6444 <item>.pszText is not LPSTR_TEXTCALLBACK.
6446 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6447 are sorted based on item text..."
6449 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6451 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
6452 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
6453 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6455 /* if we're sorting descending, negate the return value */
6456 return (((const LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6461 * Inserts a new item in the listview control.
6464 * [I] infoPtr : valid pointer to the listview structure
6465 * [I] lpLVItem : item information
6466 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6469 * SUCCESS : new item index
6472 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6474 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6479 BOOL is_sorted
, has_changed
;
6481 HWND hwndSelf
= infoPtr
->hwndSelf
;
6483 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6485 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6487 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6488 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
6490 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6492 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
6494 /* insert item in listview control data structure */
6495 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6496 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6498 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6499 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6501 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
6503 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6504 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6505 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6506 if (nItem
== -1) goto fail
;
6507 infoPtr
->nItemCount
++;
6509 /* shift indices first so they don't get tangled */
6510 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6512 /* set the item attributes */
6513 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6515 /* full size structure expected - _WIN32IE >= 0x560 */
6518 else if (lpLVItem
->mask
& LVIF_INDENT
)
6520 /* indent member expected - _WIN32IE >= 0x300 */
6521 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6525 /* minimal structure expected */
6526 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6529 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6531 item
.mask
|= LVIF_STATE
;
6532 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
6533 item
.state
&= ~LVIS_STATEIMAGEMASK
;
6534 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
6536 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6538 /* if we're sorted, sort the list, and update the index */
6541 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6542 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6543 assert(nItem
!= -1);
6546 /* make room for the position, if we are in the right mode */
6547 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6549 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6551 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6553 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6558 /* send LVN_INSERTITEM notification */
6559 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6561 nmlv
.lParam
= lpItem
->lParam
;
6562 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6563 if (!IsWindow(hwndSelf
))
6566 /* align items (set position of each item) */
6567 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6571 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6572 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6574 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6576 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6579 /* now is the invalidation fun */
6580 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6584 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6585 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6586 infoPtr
->nItemCount
--;
6588 DPA_DeletePtr(hdpaSubItems
, 0);
6589 DPA_Destroy (hdpaSubItems
);
6596 * Redraws a range of items.
6599 * [I] infoPtr : valid pointer to the listview structure
6600 * [I] nFirst : first item
6601 * [I] nLast : last item
6607 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6611 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6612 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6615 for (i
= nFirst
; i
<= nLast
; i
++)
6616 LISTVIEW_InvalidateItem(infoPtr
, i
);
6623 * Scroll the content of a listview.
6626 * [I] infoPtr : valid pointer to the listview structure
6627 * [I] dx : horizontal scroll amount in pixels
6628 * [I] dy : vertical scroll amount in pixels
6635 * If the control is in report mode (LVS_REPORT) the control can
6636 * be scrolled only in line increments. "dy" will be rounded to the
6637 * nearest number of pixels that are a whole line. Ex: if line height
6638 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6639 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6641 * For: (per experimentation with native control and CSpy ListView)
6642 * LVS_ICON dy=1 = 1 pixel (vertical only)
6644 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6646 * LVS_LIST dx=1 = 1 column (horizontal only)
6647 * but will only scroll 1 column per message
6648 * no matter what the value.
6649 * dy must be 0 or FALSE returned.
6650 * LVS_REPORT dx=1 = 1 pixel
6654 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6656 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6658 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6659 dy
/= infoPtr
->nItemHeight
;
6662 if (dy
!= 0) return FALSE
;
6669 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6670 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6677 * Sets the background color.
6680 * [I] infoPtr : valid pointer to the listview structure
6681 * [I] clrBk : background color
6687 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6689 TRACE("(clrBk=%x)\n", clrBk
);
6691 if(infoPtr
->clrBk
!= clrBk
) {
6692 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6693 infoPtr
->clrBk
= clrBk
;
6694 if (clrBk
== CLR_NONE
)
6695 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6697 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6698 LISTVIEW_InvalidateList(infoPtr
);
6704 /* LISTVIEW_SetBkImage */
6706 /*** Helper for {Insert,Set}ColumnT *only* */
6707 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
6708 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6710 if (lpColumn
->mask
& LVCF_FMT
)
6712 /* format member is valid */
6713 lphdi
->mask
|= HDI_FORMAT
;
6715 /* set text alignment (leftmost column must be left-aligned) */
6716 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6717 lphdi
->fmt
|= HDF_LEFT
;
6718 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6719 lphdi
->fmt
|= HDF_RIGHT
;
6720 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6721 lphdi
->fmt
|= HDF_CENTER
;
6723 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6724 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6726 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6728 lphdi
->fmt
|= HDF_IMAGE
;
6729 lphdi
->iImage
= I_IMAGECALLBACK
;
6733 if (lpColumn
->mask
& LVCF_WIDTH
)
6735 lphdi
->mask
|= HDI_WIDTH
;
6736 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6738 /* make it fill the remainder of the controls width */
6742 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6744 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6745 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6748 /* retrieve the layout of the header */
6749 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6750 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
6752 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6755 lphdi
->cxy
= lpColumn
->cx
;
6758 if (lpColumn
->mask
& LVCF_TEXT
)
6760 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6761 lphdi
->fmt
|= HDF_STRING
;
6762 lphdi
->pszText
= lpColumn
->pszText
;
6763 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6766 if (lpColumn
->mask
& LVCF_IMAGE
)
6768 lphdi
->mask
|= HDI_IMAGE
;
6769 lphdi
->iImage
= lpColumn
->iImage
;
6772 if (lpColumn
->mask
& LVCF_ORDER
)
6774 lphdi
->mask
|= HDI_ORDER
;
6775 lphdi
->iOrder
= lpColumn
->iOrder
;
6782 * Inserts a new column.
6785 * [I] infoPtr : valid pointer to the listview structure
6786 * [I] nColumn : column index
6787 * [I] lpColumn : column information
6788 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6791 * SUCCESS : new column index
6794 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6795 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6797 COLUMN_INFO
*lpColumnInfo
;
6801 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6803 if (!lpColumn
|| nColumn
< 0) return -1;
6804 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6806 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6807 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6810 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6811 * (can be seen in SPY) otherwise column never gets added.
6813 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
6814 hdi
.mask
|= HDI_WIDTH
;
6819 * when the iSubItem is available Windows copies it to the header lParam. It seems
6820 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6822 if (lpColumn
->mask
& LVCF_SUBITEM
)
6824 hdi
.mask
|= HDI_LPARAM
;
6825 hdi
.lParam
= lpColumn
->iSubItem
;
6828 /* insert item in header control */
6829 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6830 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6831 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6832 if (nNewColumn
== -1) return -1;
6833 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6835 /* create our own column info */
6836 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6837 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6839 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6840 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6842 /* now we have to actually adjust the data */
6843 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6845 SUBITEM_INFO
*lpSubItem
;
6849 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6851 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6852 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6854 lpSubItem
= (SUBITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, i
);
6855 if (lpSubItem
->iSubItem
>= nNewColumn
)
6856 lpSubItem
->iSubItem
++;
6861 /* make space for the new column */
6862 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6863 LISTVIEW_UpdateItemSize(infoPtr
);
6868 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6871 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6879 * Sets the attributes of a header item.
6882 * [I] infoPtr : valid pointer to the listview structure
6883 * [I] nColumn : column index
6884 * [I] lpColumn : column attributes
6885 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6891 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6892 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6894 HDITEMW hdi
, hdiget
;
6897 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6899 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6901 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6902 if (lpColumn
->mask
& LVCF_FMT
)
6904 hdi
.mask
|= HDI_FORMAT
;
6905 hdiget
.mask
= HDI_FORMAT
;
6906 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6907 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6909 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6911 /* set header item attributes */
6912 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6913 if (!bResult
) return FALSE
;
6915 if (lpColumn
->mask
& LVCF_FMT
)
6917 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6918 int oldFmt
= lpColumnInfo
->fmt
;
6920 lpColumnInfo
->fmt
= lpColumn
->fmt
;
6921 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
6923 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6924 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
6933 * Sets the column order array
6936 * [I] infoPtr : valid pointer to the listview structure
6937 * [I] iCount : number of elements in column order array
6938 * [I] lpiArray : pointer to column order array
6944 static BOOL
LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
6946 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
6957 * Sets the width of a column
6960 * [I] infoPtr : valid pointer to the listview structure
6961 * [I] nColumn : column index
6962 * [I] cx : column width
6968 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
6970 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6971 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
6975 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
6977 /* set column width only if in report or list mode */
6978 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
6980 /* take care of invalid cx values */
6981 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
6982 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
6984 /* resize all columns if in LVS_LIST mode */
6985 if(uView
== LVS_LIST
)
6987 infoPtr
->nItemWidth
= cx
;
6988 LISTVIEW_InvalidateList(infoPtr
);
6992 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6994 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
6999 lvItem
.mask
= LVIF_TEXT
;
7001 lvItem
.iSubItem
= nColumn
;
7002 lvItem
.pszText
= szDispText
;
7003 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7004 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7006 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
7007 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
7008 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
7010 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
7011 max_cx
+= infoPtr
->iconSize
.cx
;
7012 max_cx
+= TRAILING_LABEL_PADDING
;
7015 /* autosize based on listview items width */
7016 if(cx
== LVSCW_AUTOSIZE
)
7018 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
7020 /* if iCol is the last column make it fill the remainder of the controls width */
7021 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
7026 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7027 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
7029 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
7033 /* Despite what the MS docs say, if this is not the last
7034 column, then MS resizes the column to the width of the
7035 largest text string in the column, including headers
7036 and items. This is different from LVSCW_AUTOSIZE in that
7037 LVSCW_AUTOSIZE ignores the header string length. */
7040 /* retrieve header text */
7041 hdi
.mask
= HDI_TEXT
;
7042 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
7043 hdi
.pszText
= szDispText
;
7044 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
))
7046 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7047 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
7050 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
7051 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
7052 /* FIXME: Take into account the header image, if one is present */
7053 SelectObject(hdc
, old_font
);
7054 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7056 cx
= max (cx
, max_cx
);
7060 if (cx
< 0) return FALSE
;
7062 /* call header to update the column change */
7063 hdi
.mask
= HDI_WIDTH
;
7065 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
7066 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
7070 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7073 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
7076 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
7078 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
7079 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
7082 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
7083 ILC_COLOR
| ILC_MASK
, 2, 2);
7084 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
7085 hdc
= CreateCompatibleDC(hdc_wnd
);
7086 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
7087 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
7088 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
7090 rc
.left
= rc
.top
= 0;
7091 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
7092 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
7094 hbm_orig
= SelectObject(hdc
, hbm_mask
);
7095 FillRect(hdc
, &rc
, hbr_white
);
7096 InflateRect(&rc
, -3, -3);
7097 FillRect(hdc
, &rc
, hbr_black
);
7099 SelectObject(hdc
, hbm_im
);
7100 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
7101 SelectObject(hdc
, hbm_orig
);
7102 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7104 SelectObject(hdc
, hbm_im
);
7105 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
7106 SelectObject(hdc
, hbm_orig
);
7107 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7109 DeleteObject(hbm_mask
);
7110 DeleteObject(hbm_im
);
7118 * Sets the extended listview style.
7121 * [I] infoPtr : valid pointer to the listview structure
7123 * [I] dwStyle : style
7126 * SUCCESS : previous style
7129 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwExStyle
)
7131 DWORD dwOldExStyle
= infoPtr
->dwLvExStyle
;
7135 infoPtr
->dwLvExStyle
= (dwOldExStyle
& ~dwMask
) | (dwExStyle
& dwMask
);
7137 infoPtr
->dwLvExStyle
= dwExStyle
;
7139 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_CHECKBOXES
)
7141 HIMAGELIST himl
= 0;
7142 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7145 item
.mask
= LVIF_STATE
;
7146 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7147 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7148 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
7150 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
7152 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
7155 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_HEADERDRAGDROP
)
7157 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
7158 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
7159 dwStyle
|= HDS_DRAGDROP
;
7161 dwStyle
&= ~HDS_DRAGDROP
;
7162 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, dwStyle
);
7165 /* GRIDLINES adds decoration at top so changes sizes */
7166 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_GRIDLINES
)
7168 LISTVIEW_UpdateSize(infoPtr
);
7172 LISTVIEW_InvalidateList(infoPtr
);
7173 return dwOldExStyle
;
7178 * Sets the new hot cursor used during hot tracking and hover selection.
7181 * [I] infoPtr : valid pointer to the listview structure
7182 * [I] hCursor : the new hot cursor handle
7185 * Returns the previous hot cursor
7187 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
7189 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
7191 infoPtr
->hHotCursor
= hCursor
;
7199 * Sets the hot item index.
7202 * [I] infoPtr : valid pointer to the listview structure
7203 * [I] iIndex : index
7206 * SUCCESS : previous hot item index
7207 * FAILURE : -1 (no hot item)
7209 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
7211 INT iOldIndex
= infoPtr
->nHotItem
;
7213 infoPtr
->nHotItem
= iIndex
;
7221 * Sets the amount of time the cursor must hover over an item before it is selected.
7224 * [I] infoPtr : valid pointer to the listview structure
7225 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7228 * Returns the previous hover time
7230 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
7232 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
7234 infoPtr
->dwHoverTime
= dwHoverTime
;
7236 return oldHoverTime
;
7241 * Sets spacing for icons of LVS_ICON style.
7244 * [I] infoPtr : valid pointer to the listview structure
7245 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7246 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7249 * MAKELONG(oldcx, oldcy)
7251 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
7253 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7254 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7256 TRACE("requested=(%d,%d)\n", cx
, cy
);
7258 /* this is supported only for LVS_ICON style */
7259 if (uView
!= LVS_ICON
) return oldspacing
;
7261 /* set to defaults, if instructed to */
7262 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
7263 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
7265 /* if 0 then compute width
7266 * FIXME: Should scan each item and determine max width of
7267 * icon or label, then make that the width */
7269 cx
= infoPtr
->iconSpacing
.cx
;
7271 /* if 0 then compute height */
7273 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
7274 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
7277 infoPtr
->iconSpacing
.cx
= cx
;
7278 infoPtr
->iconSpacing
.cy
= cy
;
7280 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7281 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
7282 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
7283 infoPtr
->ntmHeight
);
7285 /* these depend on the iconSpacing */
7286 LISTVIEW_UpdateItemSize(infoPtr
);
7291 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
7295 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
7302 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
7303 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
7312 * [I] infoPtr : valid pointer to the listview structure
7313 * [I] nType : image list type
7314 * [I] himl : image list handle
7317 * SUCCESS : old image list
7320 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
7322 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7323 INT oldHeight
= infoPtr
->nItemHeight
;
7324 HIMAGELIST himlOld
= 0;
7326 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
7331 himlOld
= infoPtr
->himlNormal
;
7332 infoPtr
->himlNormal
= himl
;
7333 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
7334 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
7338 himlOld
= infoPtr
->himlSmall
;
7339 infoPtr
->himlSmall
= himl
;
7340 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
7344 himlOld
= infoPtr
->himlState
;
7345 infoPtr
->himlState
= himl
;
7346 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
7347 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7351 ERR("Unknown icon type=%d\n", nType
);
7355 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
7356 if (infoPtr
->nItemHeight
!= oldHeight
)
7357 LISTVIEW_UpdateScroll(infoPtr
);
7364 * Preallocates memory (does *not* set the actual count of items !)
7367 * [I] infoPtr : valid pointer to the listview structure
7368 * [I] nItems : item count (projected number of items to allocate)
7369 * [I] dwFlags : update flags
7375 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7377 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
7379 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
7381 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7382 INT nOldCount
= infoPtr
->nItemCount
;
7384 if (nItems
< nOldCount
)
7386 RANGE range
= { nItems
, nOldCount
};
7387 ranges_del(infoPtr
->selectionRanges
, range
);
7388 if (infoPtr
->nFocusedItem
>= nItems
)
7390 infoPtr
->nFocusedItem
= -1;
7391 SetRectEmpty(&infoPtr
->rcFocus
);
7395 infoPtr
->nItemCount
= nItems
;
7396 LISTVIEW_UpdateScroll(infoPtr
);
7398 /* the flags are valid only in ownerdata report and list modes */
7399 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
7401 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7402 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7404 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7405 LISTVIEW_InvalidateList(infoPtr
);
7412 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7413 nFrom
= min(nOldCount
, nItems
);
7414 nTo
= max(nOldCount
, nItems
);
7416 if (uView
== LVS_REPORT
)
7419 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7420 rcErase
.right
= infoPtr
->nItemWidth
;
7421 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7422 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7423 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7424 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7428 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7430 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7431 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7432 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7433 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7434 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7435 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7436 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7438 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7440 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7441 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7442 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7443 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7444 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7450 /* According to MSDN for non-LVS_OWNERDATA this is just
7451 * a performance issue. The control allocates its internal
7452 * data structures for the number of items specified. It
7453 * cuts down on the number of memory allocations. Therefore
7454 * we will just issue a WARN here
7456 WARN("for non-ownerdata performance option not implemented.\n");
7464 * Sets the position of an item.
7467 * [I] infoPtr : valid pointer to the listview structure
7468 * [I] nItem : item index
7469 * [I] pt : coordinate
7475 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7477 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7480 TRACE("(nItem=%d, &pt=%s\n", nItem
, wine_dbgstr_point(&pt
));
7482 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7483 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7485 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7487 /* This point value seems to be an undocumented feature.
7488 * The best guess is that it means either at the origin,
7489 * or at true beginning of the list. I will assume the origin. */
7490 if ((pt
.x
== -1) && (pt
.y
== -1))
7493 if (uView
== LVS_ICON
)
7495 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7496 pt
.y
-= ICON_TOP_PADDING
;
7501 infoPtr
->bAutoarrange
= FALSE
;
7503 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7508 * Sets the state of one or many items.
7511 * [I] infoPtr : valid pointer to the listview structure
7512 * [I] nItem : item index
7513 * [I] lpLVItem : item or subitem info
7519 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7521 BOOL bResult
= TRUE
;
7524 lvItem
.iItem
= nItem
;
7525 lvItem
.iSubItem
= 0;
7526 lvItem
.mask
= LVIF_STATE
;
7527 lvItem
.state
= lpLVItem
->state
;
7528 lvItem
.stateMask
= lpLVItem
->stateMask
;
7529 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7533 /* apply to all items */
7534 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7535 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7538 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7541 * Update selection mark
7543 * Investigation on windows 2k showed that selection mark was updated
7544 * whenever a new selection was made, but if the selected item was
7545 * unselected it was not updated.
7547 * we are probably still not 100% accurate, but this at least sets the
7548 * proper selection mark when it is needed
7551 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7552 (infoPtr
->nSelectionMark
== -1))
7555 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7557 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7559 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7561 infoPtr
->nSelectionMark
= i
;
7565 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7567 infoPtr
->nSelectionMark
= i
;
7578 * Sets the text of an item or subitem.
7581 * [I] hwnd : window handle
7582 * [I] nItem : item index
7583 * [I] lpLVItem : item or subitem info
7584 * [I] isW : TRUE if input is Unicode
7590 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7594 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7596 lvItem
.iItem
= nItem
;
7597 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7598 lvItem
.mask
= LVIF_TEXT
;
7599 lvItem
.pszText
= lpLVItem
->pszText
;
7600 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7602 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7604 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7609 * Set item index that marks the start of a multiple selection.
7612 * [I] infoPtr : valid pointer to the listview structure
7613 * [I] nIndex : index
7616 * Index number or -1 if there is no selection mark.
7618 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7620 INT nOldIndex
= infoPtr
->nSelectionMark
;
7622 TRACE("(nIndex=%d)\n", nIndex
);
7624 infoPtr
->nSelectionMark
= nIndex
;
7631 * Sets the text background color.
7634 * [I] infoPtr : valid pointer to the listview structure
7635 * [I] clrTextBk : text background color
7641 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7643 TRACE("(clrTextBk=%x)\n", clrTextBk
);
7645 if (infoPtr
->clrTextBk
!= clrTextBk
)
7647 infoPtr
->clrTextBk
= clrTextBk
;
7648 LISTVIEW_InvalidateList(infoPtr
);
7656 * Sets the text foreground color.
7659 * [I] infoPtr : valid pointer to the listview structure
7660 * [I] clrText : text color
7666 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7668 TRACE("(clrText=%x)\n", clrText
);
7670 if (infoPtr
->clrText
!= clrText
)
7672 infoPtr
->clrText
= clrText
;
7673 LISTVIEW_InvalidateList(infoPtr
);
7681 * Determines which listview item is located at the specified position.
7684 * [I] infoPtr : valid pointer to the listview structure
7685 * [I] hwndNewToolTip : handle to new ToolTip
7690 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7692 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7693 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7694 return hwndOldToolTip
;
7699 * sets the Unicode character format flag for the control
7701 * [I] infoPtr :valid pointer to the listview structure
7702 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7705 * Old Unicode Format
7707 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL fUnicode
)
7709 BOOL rc
= infoPtr
->notifyFormat
;
7710 infoPtr
->notifyFormat
= (fUnicode
)?NFR_UNICODE
:NFR_ANSI
;
7714 /* LISTVIEW_SetWorkAreas */
7718 * Callback internally used by LISTVIEW_SortItems()
7721 * [I] first : pointer to first ITEM_INFO to compare
7722 * [I] second : pointer to second ITEM_INFO to compare
7723 * [I] lParam : HWND of control
7726 * if first comes before second : negative
7727 * if first comes after second : positive
7728 * if first and second are equivalent : zero
7730 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7732 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7733 ITEM_INFO
* lv_first
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)first
, 0 );
7734 ITEM_INFO
* lv_second
= (ITEM_INFO
*) DPA_GetPtr( (HDPA
)second
, 0 );
7736 /* Forward the call to the client defined callback */
7737 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7742 * Sorts the listview items.
7745 * [I] infoPtr : valid pointer to the listview structure
7746 * [I] pfnCompare : application-defined value
7747 * [I] lParamSort : pointer to comparison callback
7753 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7755 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7758 LPVOID selectionMarkItem
;
7762 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7764 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7766 if (!pfnCompare
) return FALSE
;
7767 if (!infoPtr
->hdpaItems
) return FALSE
;
7769 /* if there are 0 or 1 items, there is no need to sort */
7770 if (infoPtr
->nItemCount
< 2) return TRUE
;
7772 if (infoPtr
->nFocusedItem
>= 0)
7774 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7775 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7776 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7778 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7779 /* clear the lpItem->state for non-selected ones */
7780 /* remove the selection ranges */
7782 infoPtr
->pfnCompare
= pfnCompare
;
7783 infoPtr
->lParamSort
= lParamSort
;
7784 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7786 /* Adjust selections and indices so that they are the way they should
7787 * be after the sort (otherwise, the list items move around, but
7788 * whatever is at the item's previous original position will be
7791 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7792 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7794 hdpaSubItems
= (HDPA
)DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7795 lpItem
= (ITEM_INFO
*)DPA_GetPtr(hdpaSubItems
, 0);
7797 if (lpItem
->state
& LVIS_SELECTED
)
7799 item
.state
= LVIS_SELECTED
;
7800 item
.stateMask
= LVIS_SELECTED
;
7801 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
7803 if (lpItem
->state
& LVIS_FOCUSED
)
7805 infoPtr
->nFocusedItem
= i
;
7806 lpItem
->state
&= ~LVIS_FOCUSED
;
7809 if (selectionMarkItem
!= NULL
)
7810 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7811 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7813 /* refresh the display */
7814 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7815 LISTVIEW_InvalidateList(infoPtr
);
7822 * Update theme handle after a theme change.
7825 * [I] infoPtr : valid pointer to the listview structure
7829 * FAILURE : something else
7831 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
7833 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7834 CloseThemeData(theme
);
7835 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
7841 * Updates an items or rearranges the listview control.
7844 * [I] infoPtr : valid pointer to the listview structure
7845 * [I] nItem : item index
7851 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7853 TRACE("(nItem=%d)\n", nItem
);
7855 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7857 /* rearrange with default alignment style */
7858 if (is_autoarrange(infoPtr
))
7859 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7861 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7868 * Draw the track line at the place defined in the infoPtr structure.
7869 * The line is drawn with a XOR pen so drawing the line for the second time
7870 * in the same place erases the line.
7873 * [I] infoPtr : valid pointer to the listview structure
7879 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
7885 if (infoPtr
->xTrackLine
== -1)
7888 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
7890 hOldPen
= SelectObject(hdc
, GetStockObject(BLACK_PEN
));
7891 oldROP
= SetROP2(hdc
, R2_XORPEN
);
7892 MoveToEx(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
, NULL
);
7893 LineTo(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.bottom
);
7894 SetROP2(hdc
, oldROP
);
7895 SelectObject(hdc
, hOldPen
);
7896 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7902 * Called when an edit control should be displayed. This function is called after
7903 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7906 * [I] hwnd : Handle to the listview
7907 * [I] uMsg : WM_TIMER (ignored)
7908 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7909 * [I] dwTimer : The elapsed time (ignored)
7914 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
7916 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
7917 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
7919 KillTimer(hwnd
, idEvent
);
7920 editItem
->fEnabled
= FALSE
;
7921 /* check if the item is still selected */
7922 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
7923 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
7928 * Creates the listview control - the WM_NCCREATE phase.
7931 * [I] hwnd : window handle
7932 * [I] lpcs : the create parameters
7938 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
7940 LISTVIEW_INFO
*infoPtr
;
7943 TRACE("(lpcs=%p)\n", lpcs
);
7945 /* initialize info pointer */
7946 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
7947 if (!infoPtr
) return FALSE
;
7949 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
7951 infoPtr
->hwndSelf
= hwnd
;
7952 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
7953 /* determine the type of structures to use */
7954 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
7955 /* infoPtr->notifyFormat will be filled in WM_CREATE */
7957 /* initialize color information */
7958 infoPtr
->clrBk
= CLR_NONE
;
7959 infoPtr
->clrText
= CLR_DEFAULT
;
7960 infoPtr
->clrTextBk
= CLR_DEFAULT
;
7961 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
7963 /* set default values */
7964 infoPtr
->nFocusedItem
= -1;
7965 infoPtr
->nSelectionMark
= -1;
7966 infoPtr
->nHotItem
= -1;
7967 infoPtr
->bRedraw
= TRUE
;
7968 infoPtr
->bNoItemMetrics
= TRUE
;
7969 infoPtr
->bDoChangeNotify
= TRUE
;
7970 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
7971 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
7972 infoPtr
->nEditLabelItem
= -1;
7973 infoPtr
->dwHoverTime
= -1; /* default system hover time */
7974 infoPtr
->nMeasureItemHeight
= 0;
7975 infoPtr
->xTrackLine
= -1; /* no track line */
7976 infoPtr
->itemEdit
.fEnabled
= FALSE
;
7978 /* get default font (icon title) */
7979 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
7980 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
7981 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
7982 LISTVIEW_SaveTextMetrics(infoPtr
);
7984 /* allocate memory for the data structure */
7985 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
7986 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
7987 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
7988 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
7989 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
7993 DestroyWindow(infoPtr
->hwndHeader
);
7994 ranges_destroy(infoPtr
->selectionRanges
);
7995 DPA_Destroy(infoPtr
->hdpaItems
);
7996 DPA_Destroy(infoPtr
->hdpaPosX
);
7997 DPA_Destroy(infoPtr
->hdpaPosY
);
7998 DPA_Destroy(infoPtr
->hdpaColumns
);
8005 * Creates the listview control - the WM_CREATE phase. Most of the data is
8006 * already set up in LISTVIEW_NCCreate
8009 * [I] hwnd : window handle
8010 * [I] lpcs : the create parameters
8016 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8018 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8019 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
8020 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
8022 TRACE("(lpcs=%p)\n", lpcs
);
8024 infoPtr
->dwStyle
= lpcs
->style
;
8025 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
8026 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
8028 /* setup creation flags */
8029 dFlags
|= (LVS_NOSORTHEADER
& lpcs
->style
) ? 0 : HDS_BUTTONS
;
8030 dFlags
|= (LVS_NOCOLUMNHEADER
& lpcs
->style
) ? HDS_HIDDEN
: 0;
8033 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
8034 0, 0, 0, 0, hwnd
, NULL
,
8035 lpcs
->hInstance
, NULL
);
8036 if (!infoPtr
->hwndHeader
) return -1;
8038 /* set header unicode format */
8039 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, (WPARAM
)TRUE
, (LPARAM
)NULL
);
8041 /* set header font */
8042 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
8044 /* init item size to avoid division by 0 */
8045 LISTVIEW_UpdateItemSize (infoPtr
);
8047 if (uView
== LVS_REPORT
)
8049 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
8051 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8053 LISTVIEW_UpdateSize(infoPtr
);
8054 LISTVIEW_UpdateScroll(infoPtr
);
8057 OpenThemeData(hwnd
, themeClass
);
8059 /* initialize the icon sizes */
8060 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
8061 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
8067 * Destroys the listview control.
8070 * [I] infoPtr : valid pointer to the listview structure
8076 static LRESULT
LISTVIEW_Destroy(const LISTVIEW_INFO
*infoPtr
)
8078 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
8079 CloseThemeData(theme
);
8085 * Enables the listview control.
8088 * [I] infoPtr : valid pointer to the listview structure
8089 * [I] bEnable : specifies whether to enable or disable the window
8095 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
8097 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
8098 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8104 * Erases the background of the listview control.
8107 * [I] infoPtr : valid pointer to the listview structure
8108 * [I] hdc : device context handle
8114 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8118 TRACE("(hdc=%p)\n", hdc
);
8120 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
8122 /* for double buffered controls we need to do this during refresh */
8123 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
8125 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
8131 * Helper function for LISTVIEW_[HV]Scroll *only*.
8132 * Performs vertical/horizontal scrolling by a give amount.
8135 * [I] infoPtr : valid pointer to the listview structure
8136 * [I] dx : amount of horizontal scroll
8137 * [I] dy : amount of vertical scroll
8139 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8141 /* now we can scroll the list */
8142 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
8143 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
8144 /* if we have focus, adjust rect */
8145 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
8146 UpdateWindow(infoPtr
->hwndSelf
);
8151 * Performs vertical scrolling.
8154 * [I] infoPtr : valid pointer to the listview structure
8155 * [I] nScrollCode : scroll code
8156 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8157 * [I] hScrollWnd : scrollbar control window handle
8163 * SB_LINEUP/SB_LINEDOWN:
8164 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8165 * for LVS_REPORT is 1 line
8166 * for LVS_LIST cannot occur
8169 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8170 INT nScrollDiff
, HWND hScrollWnd
)
8172 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8173 INT nOldScrollPos
, nNewScrollPos
;
8174 SCROLLINFO scrollInfo
;
8177 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8178 debugscrollcode(nScrollCode
), nScrollDiff
);
8180 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8182 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8183 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8185 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
8187 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
8189 nOldScrollPos
= scrollInfo
.nPos
;
8190 switch (nScrollCode
)
8196 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
8200 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
8204 nScrollDiff
= -scrollInfo
.nPage
;
8208 nScrollDiff
= scrollInfo
.nPage
;
8211 case SB_THUMBPOSITION
:
8213 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8220 /* quit right away if pos isn't changing */
8221 if (nScrollDiff
== 0) return 0;
8223 /* calculate new position, and handle overflows */
8224 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8225 if (nScrollDiff
> 0) {
8226 if (nNewScrollPos
< nOldScrollPos
||
8227 nNewScrollPos
> scrollInfo
.nMax
)
8228 nNewScrollPos
= scrollInfo
.nMax
;
8230 if (nNewScrollPos
> nOldScrollPos
||
8231 nNewScrollPos
< scrollInfo
.nMin
)
8232 nNewScrollPos
= scrollInfo
.nMin
;
8235 /* set the new position, and reread in case it changed */
8236 scrollInfo
.fMask
= SIF_POS
;
8237 scrollInfo
.nPos
= nNewScrollPos
;
8238 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
8240 /* carry on only if it really changed */
8241 if (nNewScrollPos
== nOldScrollPos
) return 0;
8243 /* now adjust to client coordinates */
8244 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8245 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
8247 /* and scroll the window */
8248 scroll_list(infoPtr
, 0, nScrollDiff
);
8255 * Performs horizontal scrolling.
8258 * [I] infoPtr : valid pointer to the listview structure
8259 * [I] nScrollCode : scroll code
8260 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8261 * [I] hScrollWnd : scrollbar control window handle
8267 * SB_LINELEFT/SB_LINERIGHT:
8268 * for LVS_ICON, LVS_SMALLICON 1 pixel
8269 * for LVS_REPORT is 1 pixel
8270 * for LVS_LIST is 1 column --> which is a 1 because the
8271 * scroll is based on columns not pixels
8274 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8275 INT nScrollDiff
, HWND hScrollWnd
)
8277 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8278 INT nOldScrollPos
, nNewScrollPos
;
8279 SCROLLINFO scrollInfo
;
8281 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8282 debugscrollcode(nScrollCode
), nScrollDiff
);
8284 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8286 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8287 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8289 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
8291 nOldScrollPos
= scrollInfo
.nPos
;
8293 switch (nScrollCode
)
8307 nScrollDiff
= -scrollInfo
.nPage
;
8311 nScrollDiff
= scrollInfo
.nPage
;
8314 case SB_THUMBPOSITION
:
8316 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8323 /* quit right away if pos isn't changing */
8324 if (nScrollDiff
== 0) return 0;
8326 /* calculate new position, and handle overflows */
8327 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8328 if (nScrollDiff
> 0) {
8329 if (nNewScrollPos
< nOldScrollPos
||
8330 nNewScrollPos
> scrollInfo
.nMax
)
8331 nNewScrollPos
= scrollInfo
.nMax
;
8333 if (nNewScrollPos
> nOldScrollPos
||
8334 nNewScrollPos
< scrollInfo
.nMin
)
8335 nNewScrollPos
= scrollInfo
.nMin
;
8338 /* set the new position, and reread in case it changed */
8339 scrollInfo
.fMask
= SIF_POS
;
8340 scrollInfo
.nPos
= nNewScrollPos
;
8341 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
8343 /* carry on only if it really changed */
8344 if (nNewScrollPos
== nOldScrollPos
) return 0;
8346 if(uView
== LVS_REPORT
)
8347 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
8349 /* now adjust to client coordinates */
8350 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8351 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
8353 /* and scroll the window */
8354 scroll_list(infoPtr
, nScrollDiff
, 0);
8359 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
8361 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8362 INT gcWheelDelta
= 0;
8363 INT pulScrollLines
= 3;
8364 SCROLLINFO scrollInfo
;
8366 TRACE("(wheelDelta=%d)\n", wheelDelta
);
8368 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
8369 gcWheelDelta
-= wheelDelta
;
8371 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8372 scrollInfo
.fMask
= SIF_POS
;
8379 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8380 * should be fixed in the future.
8382 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
8383 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
8387 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
8389 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
8390 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
8391 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
8396 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
8407 * [I] infoPtr : valid pointer to the listview structure
8408 * [I] nVirtualKey : virtual key
8409 * [I] lKeyData : key data
8414 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
8416 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8417 HWND hwndSelf
= infoPtr
->hwndSelf
;
8419 NMLVKEYDOWN nmKeyDown
;
8421 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
8423 /* send LVN_KEYDOWN notification */
8424 nmKeyDown
.wVKey
= nVirtualKey
;
8425 nmKeyDown
.flags
= 0;
8426 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
8427 if (!IsWindow(hwndSelf
))
8430 switch (nVirtualKey
)
8433 nItem
= infoPtr
->nFocusedItem
;
8434 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8435 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
8439 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
8441 if (!notify(infoPtr
, NM_RETURN
)) return 0;
8442 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
8447 if (infoPtr
->nItemCount
> 0)
8452 if (infoPtr
->nItemCount
> 0)
8453 nItem
= infoPtr
->nItemCount
- 1;
8457 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8461 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8465 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8469 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8473 if (uView
== LVS_REPORT
)
8475 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8476 if (infoPtr
->nFocusedItem
== topidx
)
8477 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
8482 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
8483 * LISTVIEW_GetCountPerRow(infoPtr
);
8484 if(nItem
< 0) nItem
= 0;
8488 if (uView
== LVS_REPORT
)
8490 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8491 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
8492 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
8493 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
8495 nItem
= topidx
+ cnt
- 1;
8498 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
8499 * LISTVIEW_GetCountPerRow(infoPtr
);
8500 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
8504 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
8505 LISTVIEW_KeySelection(infoPtr
, nItem
);
8515 * [I] infoPtr : valid pointer to the listview structure
8520 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8524 /* if we did not have the focus, there's nothing to do */
8525 if (!infoPtr
->bFocus
) return 0;
8527 /* send NM_KILLFOCUS notification */
8528 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
8530 /* if we have a focus rectagle, get rid of it */
8531 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
8533 /* set window focus flag */
8534 infoPtr
->bFocus
= FALSE
;
8536 /* invalidate the selected items before resetting focus flag */
8537 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8544 * Processes double click messages (left mouse button).
8547 * [I] infoPtr : valid pointer to the listview structure
8548 * [I] wKey : key flag
8549 * [I] x,y : mouse coordinate
8554 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8556 LVHITTESTINFO htInfo
;
8558 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8560 /* Cancel the item edition if any */
8561 if (infoPtr
->itemEdit
.fEnabled
)
8563 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
8564 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8567 /* send NM_RELEASEDCAPTURE notification */
8568 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8573 /* send NM_DBLCLK notification */
8574 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8575 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
8577 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8578 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8585 * Processes mouse down messages (left mouse button).
8588 * infoPtr [I ] valid pointer to the listview structure
8589 * wKey [I ] key flag
8590 * x,y [I ] mouse coordinate
8595 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8597 LVHITTESTINFO lvHitTestInfo
;
8598 static BOOL bGroupSelect
= TRUE
;
8599 BOOL bReceivedFocus
= FALSE
;
8600 POINT pt
= { x
, y
};
8603 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8605 /* send NM_RELEASEDCAPTURE notification */
8606 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8608 if (!infoPtr
->bFocus
)
8609 bReceivedFocus
= TRUE
;
8611 /* set left button down flag and record the click position */
8612 infoPtr
->bLButtonDown
= TRUE
;
8613 infoPtr
->ptClickPos
= pt
;
8614 infoPtr
->bDragging
= FALSE
;
8616 lvHitTestInfo
.pt
.x
= x
;
8617 lvHitTestInfo
.pt
.y
= y
;
8619 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8620 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
8621 infoPtr
->nEditLabelItem
= -1;
8622 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8624 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8626 toggle_checkbox_state(infoPtr
, nItem
);
8630 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8632 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8633 infoPtr
->nEditLabelItem
= nItem
;
8635 LISTVIEW_SetSelection(infoPtr
, nItem
);
8639 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8643 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
8644 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8645 infoPtr
->nSelectionMark
= nItem
;
8651 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8652 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8654 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8655 infoPtr
->nSelectionMark
= nItem
;
8658 else if (wKey
& MK_CONTROL
)
8662 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8664 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8665 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8666 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8667 infoPtr
->nSelectionMark
= nItem
;
8669 else if (wKey
& MK_SHIFT
)
8671 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8675 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8676 infoPtr
->nEditLabelItem
= nItem
;
8678 /* set selection (clears other pre-existing selections) */
8679 LISTVIEW_SetSelection(infoPtr
, nItem
);
8685 /* remove all selections */
8686 LISTVIEW_DeselectAll(infoPtr
);
8691 infoPtr
->nEditLabelItem
= -1;
8698 * Processes mouse up messages (left mouse button).
8701 * infoPtr [I ] valid pointer to the listview structure
8702 * wKey [I ] key flag
8703 * x,y [I ] mouse coordinate
8708 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8710 LVHITTESTINFO lvHitTestInfo
;
8712 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8714 if (!infoPtr
->bLButtonDown
) return 0;
8716 lvHitTestInfo
.pt
.x
= x
;
8717 lvHitTestInfo
.pt
.y
= y
;
8719 /* send NM_CLICK notification */
8720 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8721 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
8723 /* set left button flag */
8724 infoPtr
->bLButtonDown
= FALSE
;
8726 if (infoPtr
->bDragging
)
8728 infoPtr
->bDragging
= FALSE
;
8732 /* if we clicked on a selected item, edit the label */
8733 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8735 /* we want to make sure the user doesn't want to do a double click. So we will
8736 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8738 infoPtr
->itemEdit
.fEnabled
= TRUE
;
8739 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
8740 SetTimer(infoPtr
->hwndSelf
,
8741 (UINT_PTR
)&infoPtr
->itemEdit
,
8742 GetDoubleClickTime(),
8743 LISTVIEW_DelayedEditItem
);
8746 if (!infoPtr
->bFocus
)
8747 SetFocus(infoPtr
->hwndSelf
);
8754 * Destroys the listview control (called after WM_DESTROY).
8757 * [I] infoPtr : valid pointer to the listview structure
8762 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8766 /* delete all items */
8767 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
8769 /* destroy data structure */
8770 DPA_Destroy(infoPtr
->hdpaItems
);
8771 DPA_Destroy(infoPtr
->hdpaPosX
);
8772 DPA_Destroy(infoPtr
->hdpaPosY
);
8773 DPA_Destroy(infoPtr
->hdpaColumns
);
8774 ranges_destroy(infoPtr
->selectionRanges
);
8776 /* destroy image lists */
8777 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8779 if (infoPtr
->himlNormal
)
8780 ImageList_Destroy(infoPtr
->himlNormal
);
8781 if (infoPtr
->himlSmall
)
8782 ImageList_Destroy(infoPtr
->himlSmall
);
8783 if (infoPtr
->himlState
)
8784 ImageList_Destroy(infoPtr
->himlState
);
8787 /* destroy font, bkgnd brush */
8789 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8790 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8792 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8794 /* free listview info pointer*/
8802 * Handles notifications from header.
8805 * [I] infoPtr : valid pointer to the listview structure
8806 * [I] nCtrlId : control identifier
8807 * [I] lpnmh : notification information
8812 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8814 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8815 HWND hwndSelf
= infoPtr
->hwndSelf
;
8817 TRACE("(lpnmh=%p)\n", lpnmh
);
8819 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8821 switch (lpnmh
->hdr
.code
)
8826 COLUMN_INFO
*lpColumnInfo
;
8830 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8833 /* remove the old line (if any) */
8834 LISTVIEW_DrawTrackLine(infoPtr
);
8836 /* compute & draw the new line */
8837 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8838 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
8839 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8840 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
8841 LISTVIEW_DrawTrackLine(infoPtr
);
8847 /* remove the track line (if any) */
8848 LISTVIEW_DrawTrackLine(infoPtr
);
8849 infoPtr
->xTrackLine
= -1;
8853 FIXME("Changing column order not implemented\n");
8856 case HDN_ITEMCHANGINGW
:
8857 case HDN_ITEMCHANGINGA
:
8858 return notify_forward_header(infoPtr
, lpnmh
);
8860 case HDN_ITEMCHANGEDW
:
8861 case HDN_ITEMCHANGEDA
:
8863 COLUMN_INFO
*lpColumnInfo
;
8866 notify_forward_header(infoPtr
, lpnmh
);
8867 if (!IsWindow(hwndSelf
))
8870 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8874 hdi
.mask
= HDI_WIDTH
;
8875 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, &hdi
)) return 0;
8879 cxy
= lpnmh
->pitem
->cxy
;
8881 /* determine how much we change since the last know position */
8882 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8883 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8886 lpColumnInfo
->rcHeader
.right
+= dx
;
8887 if (lpnmh
->iItem
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
8888 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
8891 /* only needs to update the scrolls */
8892 infoPtr
->nItemWidth
+= dx
;
8893 LISTVIEW_UpdateScroll(infoPtr
);
8895 LISTVIEW_UpdateItemSize(infoPtr
);
8896 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
8899 RECT rcCol
= lpColumnInfo
->rcHeader
;
8901 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8902 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
8904 rcCol
.top
= infoPtr
->rcList
.top
;
8905 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
8907 /* resizing left-aligned columns leaves most of the left side untouched */
8908 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8910 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
8913 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
8916 /* when shrinking the last column clear the now unused field */
8917 if (lpnmh
->iItem
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1 && dx
< 0)
8920 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
8926 case HDN_ITEMCLICKW
:
8927 case HDN_ITEMCLICKA
:
8929 /* Handle sorting by Header Column */
8932 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8934 nmlv
.iSubItem
= lpnmh
->iItem
;
8935 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
8939 case HDN_DIVIDERDBLCLICKW
:
8940 case HDN_DIVIDERDBLCLICKA
:
8941 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
8950 * Paint non-client area of control.
8953 * [I] infoPtr : valid pointer to the listview structureof the sender
8954 * [I] region : update region
8957 * TRUE - frame was painted
8958 * FALSE - call default window proc
8960 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
8962 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
8966 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
8967 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
8969 if (!theme
) return FALSE
;
8971 GetWindowRect(infoPtr
->hwndSelf
, &r
);
8973 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
8974 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
8975 if (region
!= (HRGN
)1)
8976 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
8977 OffsetRect(&r
, -r
.left
, -r
.top
);
8979 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
8980 OffsetRect(&r
, -r
.left
, -r
.top
);
8982 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
8983 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
8984 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
8985 ReleaseDC(infoPtr
->hwndSelf
, dc
);
8987 /* Call default proc to get the scrollbars etc. painted */
8988 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
8995 * Determines the type of structure to use.
8998 * [I] infoPtr : valid pointer to the listview structureof the sender
8999 * [I] hwndFrom : listview window handle
9000 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9005 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
9007 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
9009 if (nCommand
== NF_REQUERY
)
9010 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9012 return infoPtr
->notifyFormat
;
9017 * Paints/Repaints the listview control.
9020 * [I] infoPtr : valid pointer to the listview structure
9021 * [I] hdc : device context handle
9026 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9028 TRACE("(hdc=%p)\n", hdc
);
9030 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
9032 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9034 infoPtr
->bNoItemMetrics
= FALSE
;
9035 LISTVIEW_UpdateItemSize(infoPtr
);
9036 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
9037 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9038 LISTVIEW_UpdateScroll(infoPtr
);
9041 UpdateWindow(infoPtr
->hwndHeader
);
9044 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
9049 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
9051 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
9052 EndPaint(infoPtr
->hwndSelf
, &ps
);
9061 * Paints/Repaints the listview control.
9064 * [I] infoPtr : valid pointer to the listview structure
9065 * [I] hdc : device context handle
9066 * [I] options : drawing options
9071 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
9073 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc
, options
);
9075 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
9078 if (options
& PRF_ERASEBKGND
)
9079 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
9081 if (options
& PRF_CLIENT
)
9082 LISTVIEW_Paint(infoPtr
, hdc
);
9090 * Processes double click messages (right mouse button).
9093 * [I] infoPtr : valid pointer to the listview structure
9094 * [I] wKey : key flag
9095 * [I] x,y : mouse coordinate
9100 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9102 LVHITTESTINFO lvHitTestInfo
;
9104 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9106 /* send NM_RELEASEDCAPTURE notification */
9107 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9109 /* send NM_RDBLCLK notification */
9110 lvHitTestInfo
.pt
.x
= x
;
9111 lvHitTestInfo
.pt
.y
= y
;
9112 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9113 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
9120 * Processes mouse down messages (right mouse button).
9123 * [I] infoPtr : valid pointer to the listview structure
9124 * [I] wKey : key flag
9125 * [I] x,y : mouse coordinate
9130 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9132 LVHITTESTINFO lvHitTestInfo
;
9135 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9137 /* send NM_RELEASEDCAPTURE notification */
9138 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9140 /* make sure the listview control window has the focus */
9141 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
9143 /* set right button down flag */
9144 infoPtr
->bRButtonDown
= TRUE
;
9146 /* determine the index of the selected item */
9147 lvHitTestInfo
.pt
.x
= x
;
9148 lvHitTestInfo
.pt
.y
= y
;
9149 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
9151 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
9153 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
9154 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
9155 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
9156 LISTVIEW_SetSelection(infoPtr
, nItem
);
9160 LISTVIEW_DeselectAll(infoPtr
);
9168 * Processes mouse up messages (right mouse button).
9171 * [I] infoPtr : valid pointer to the listview structure
9172 * [I] wKey : key flag
9173 * [I] x,y : mouse coordinate
9178 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9180 LVHITTESTINFO lvHitTestInfo
;
9183 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9185 if (!infoPtr
->bRButtonDown
) return 0;
9187 /* set button flag */
9188 infoPtr
->bRButtonDown
= FALSE
;
9190 /* Send NM_RClICK notification */
9191 lvHitTestInfo
.pt
.x
= x
;
9192 lvHitTestInfo
.pt
.y
= y
;
9193 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9194 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
9196 /* Change to screen coordinate for WM_CONTEXTMENU */
9197 pt
= lvHitTestInfo
.pt
;
9198 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
9200 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9201 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
9202 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
9213 * [I] infoPtr : valid pointer to the listview structure
9214 * [I] hwnd : window handle of window containing the cursor
9215 * [I] nHittest : hit-test code
9216 * [I] wMouseMsg : ideintifier of the mouse message
9219 * TRUE if cursor is set
9222 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
9224 LVHITTESTINFO lvHitTestInfo
;
9226 if(!(infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
)) return FALSE
;
9228 if(!infoPtr
->hHotCursor
) return FALSE
;
9230 GetCursorPos(&lvHitTestInfo
.pt
);
9231 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
9233 SetCursor(infoPtr
->hHotCursor
);
9243 * [I] infoPtr : valid pointer to the listview structure
9244 * [I] hwndLoseFocus : handle of previously focused window
9249 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
9251 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
9253 /* if we have the focus already, there's nothing to do */
9254 if (infoPtr
->bFocus
) return 0;
9256 /* send NM_SETFOCUS notification */
9257 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
9259 /* set window focus flag */
9260 infoPtr
->bFocus
= TRUE
;
9262 /* put the focus rect back on */
9263 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
9265 /* redraw all visible selected items */
9266 LISTVIEW_InvalidateSelectedItems(infoPtr
);
9276 * [I] infoPtr : valid pointer to the listview structure
9277 * [I] fRedraw : font handle
9278 * [I] fRedraw : redraw flag
9283 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
9285 HFONT oldFont
= infoPtr
->hFont
;
9287 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
9289 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
9290 if (infoPtr
->hFont
== oldFont
) return 0;
9292 LISTVIEW_SaveTextMetrics(infoPtr
);
9294 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
9296 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
9297 LISTVIEW_UpdateSize(infoPtr
);
9298 LISTVIEW_UpdateScroll(infoPtr
);
9301 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
9308 * Message handling for WM_SETREDRAW.
9309 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9312 * [I] infoPtr : valid pointer to the listview structure
9313 * [I] bRedraw: state of redraw flag
9316 * DefWinProc return value
9318 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
9320 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
9322 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9323 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
9325 infoPtr
->bRedraw
= bRedraw
;
9327 if(!bRedraw
) return 0;
9329 if (is_autoarrange(infoPtr
))
9330 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9331 LISTVIEW_UpdateScroll(infoPtr
);
9333 /* despite what the WM_SETREDRAW docs says, apps expect us
9334 * to invalidate the listview here... stupid! */
9335 LISTVIEW_InvalidateList(infoPtr
);
9342 * Resizes the listview control. This function processes WM_SIZE
9343 * messages. At this time, the width and height are not used.
9346 * [I] infoPtr : valid pointer to the listview structure
9347 * [I] Width : new width
9348 * [I] Height : new height
9353 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
9355 RECT rcOld
= infoPtr
->rcList
;
9357 TRACE("(width=%d, height=%d)\n", Width
, Height
);
9359 LISTVIEW_UpdateSize(infoPtr
);
9360 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
9362 /* do not bother with display related stuff if we're not redrawing */
9363 if (!is_redrawing(infoPtr
)) return 0;
9365 if (is_autoarrange(infoPtr
))
9366 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9368 LISTVIEW_UpdateScroll(infoPtr
);
9370 /* refresh all only for lists whose height changed significantly */
9371 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
9372 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
9373 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
9374 LISTVIEW_InvalidateList(infoPtr
);
9381 * Sets the size information.
9384 * [I] infoPtr : valid pointer to the listview structure
9389 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
9391 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9393 TRACE("uView=%d, rcList(old)=%s\n", uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
9395 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
9397 if (uView
== LVS_LIST
)
9399 /* Apparently the "LIST" style is supposed to have the same
9400 * number of items in a column even if there is no scroll bar.
9401 * Since if a scroll bar already exists then the bottom is already
9402 * reduced, only reduce if the scroll bar does not currently exist.
9403 * The "2" is there to mimic the native control. I think it may be
9404 * related to either padding or edges. (GLA 7/2002)
9406 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
9407 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
9408 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
9410 else if (uView
== LVS_REPORT
)
9415 hl
.prc
= &infoPtr
->rcList
;
9417 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9418 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9419 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9420 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9421 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9422 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9424 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
9425 infoPtr
->rcList
.top
+= (infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
) ? 2 : 0;
9428 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
9433 * Processes WM_STYLECHANGED messages.
9436 * [I] infoPtr : valid pointer to the listview structure
9437 * [I] wStyleType : window style type (normal or extended)
9438 * [I] lpss : window style information
9443 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
9444 const STYLESTRUCT
*lpss
)
9446 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
9447 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
9450 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9451 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9453 if (wStyleType
!= GWL_STYLE
) return 0;
9455 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9456 /* what if LVS_OWNERDATA changed? */
9457 /* or LVS_SINGLESEL */
9458 /* or LVS_SORT{AS,DES}CENDING */
9460 infoPtr
->dwStyle
= lpss
->styleNew
;
9462 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
9463 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
9464 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
9466 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
9467 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
9468 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
9470 if (uNewView
!= uOldView
)
9472 SIZE oldIconSize
= infoPtr
->iconSize
;
9475 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9476 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9478 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9479 SetRectEmpty(&infoPtr
->rcFocus
);
9481 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9482 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
9484 if (uNewView
== LVS_ICON
)
9486 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
9488 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9489 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
9490 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
9493 else if (uNewView
== LVS_REPORT
)
9498 hl
.prc
= &infoPtr
->rcList
;
9500 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9501 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9502 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9503 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9506 LISTVIEW_UpdateItemSize(infoPtr
);
9509 if (uNewView
== LVS_REPORT
)
9511 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
9513 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
9515 /* Turn off the header control */
9516 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
9517 TRACE("Hide header control, was 0x%08x\n", style
);
9518 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
9520 /* Turn on the header control */
9521 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
9523 TRACE("Show header control, was 0x%08x\n", style
);
9524 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
9530 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
9531 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
9532 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9534 /* update the size of the client area */
9535 LISTVIEW_UpdateSize(infoPtr
);
9537 /* add scrollbars if needed */
9538 LISTVIEW_UpdateScroll(infoPtr
);
9540 /* invalidate client area + erase background */
9541 LISTVIEW_InvalidateList(infoPtr
);
9548 * Window procedure of the listview control.
9551 static LRESULT WINAPI
9552 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9554 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9556 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg
, wParam
, lParam
);
9558 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
9559 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9563 case LVM_APPROXIMATEVIEWRECT
:
9564 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
9565 LOWORD(lParam
), HIWORD(lParam
));
9567 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
9569 /* case LVM_CANCELEDITLABEL: */
9571 case LVM_CREATEDRAGIMAGE
:
9572 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9574 case LVM_DELETEALLITEMS
:
9575 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
9577 case LVM_DELETECOLUMN
:
9578 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
9580 case LVM_DELETEITEM
:
9581 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
9583 case LVM_EDITLABELW
:
9584 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
9586 case LVM_EDITLABELA
:
9587 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
9589 /* case LVM_ENABLEGROUPVIEW: */
9591 case LVM_ENSUREVISIBLE
:
9592 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
9595 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
9598 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
9600 case LVM_GETBKCOLOR
:
9601 return infoPtr
->clrBk
;
9603 /* case LVM_GETBKIMAGE: */
9605 case LVM_GETCALLBACKMASK
:
9606 return infoPtr
->uCallbackMask
;
9608 case LVM_GETCOLUMNA
:
9609 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9611 case LVM_GETCOLUMNW
:
9612 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9614 case LVM_GETCOLUMNORDERARRAY
:
9615 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9617 case LVM_GETCOLUMNWIDTH
:
9618 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
9620 case LVM_GETCOUNTPERPAGE
:
9621 return LISTVIEW_GetCountPerPage(infoPtr
);
9623 case LVM_GETEDITCONTROL
:
9624 return (LRESULT
)infoPtr
->hwndEdit
;
9626 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
9627 return infoPtr
->dwLvExStyle
;
9629 /* case LVM_GETGROUPINFO: */
9631 /* case LVM_GETGROUPMETRICS: */
9634 return (LRESULT
)infoPtr
->hwndHeader
;
9636 case LVM_GETHOTCURSOR
:
9637 return (LRESULT
)infoPtr
->hHotCursor
;
9639 case LVM_GETHOTITEM
:
9640 return infoPtr
->nHotItem
;
9642 case LVM_GETHOVERTIME
:
9643 return infoPtr
->dwHoverTime
;
9645 case LVM_GETIMAGELIST
:
9646 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
9648 /* case LVM_GETINSERTMARK: */
9650 /* case LVM_GETINSERTMARKCOLOR: */
9652 /* case LVM_GETINSERTMARKRECT: */
9654 case LVM_GETISEARCHSTRINGA
:
9655 case LVM_GETISEARCHSTRINGW
:
9656 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9660 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9663 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9665 case LVM_GETITEMCOUNT
:
9666 return infoPtr
->nItemCount
;
9668 case LVM_GETITEMPOSITION
:
9669 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9671 case LVM_GETITEMRECT
:
9672 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
9674 case LVM_GETITEMSPACING
:
9675 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
9677 case LVM_GETITEMSTATE
:
9678 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
9680 case LVM_GETITEMTEXTA
:
9681 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9683 case LVM_GETITEMTEXTW
:
9684 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9686 case LVM_GETNEXTITEM
:
9687 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
9689 case LVM_GETNUMBEROFWORKAREAS
:
9690 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9694 if (!lParam
) return FALSE
;
9695 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
||
9696 (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
) return FALSE
;
9697 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
9700 /* case LVM_GETOUTLINECOLOR: */
9702 /* case LVM_GETSELECTEDCOLUMN: */
9704 case LVM_GETSELECTEDCOUNT
:
9705 return LISTVIEW_GetSelectedCount(infoPtr
);
9707 case LVM_GETSELECTIONMARK
:
9708 return infoPtr
->nSelectionMark
;
9710 case LVM_GETSTRINGWIDTHA
:
9711 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9713 case LVM_GETSTRINGWIDTHW
:
9714 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9716 case LVM_GETSUBITEMRECT
:
9717 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
9719 case LVM_GETTEXTBKCOLOR
:
9720 return infoPtr
->clrTextBk
;
9722 case LVM_GETTEXTCOLOR
:
9723 return infoPtr
->clrText
;
9725 /* case LVM_GETTILEINFO: */
9727 /* case LVM_GETTILEVIEWINFO: */
9729 case LVM_GETTOOLTIPS
:
9730 if( !infoPtr
->hwndToolTip
)
9731 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9732 return (LRESULT
)infoPtr
->hwndToolTip
;
9734 case LVM_GETTOPINDEX
:
9735 return LISTVIEW_GetTopIndex(infoPtr
);
9737 case LVM_GETUNICODEFORMAT
:
9738 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
9740 /* case LVM_GETVIEW: */
9742 case LVM_GETVIEWRECT
:
9743 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9745 case LVM_GETWORKAREAS
:
9746 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9749 /* case LVM_HASGROUP: */
9752 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9754 case LVM_INSERTCOLUMNA
:
9755 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9757 case LVM_INSERTCOLUMNW
:
9758 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9760 /* case LVM_INSERTGROUP: */
9762 /* case LVM_INSERTGROUPSORTED: */
9764 case LVM_INSERTITEMA
:
9765 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9767 case LVM_INSERTITEMW
:
9768 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9770 /* case LVM_INSERTMARKHITTEST: */
9772 /* case LVM_ISGROUPVIEWENABLED: */
9774 /* case LVM_MAPIDTOINDEX: */
9776 /* case LVM_MAPINDEXTOID: */
9778 /* case LVM_MOVEGROUP: */
9780 /* case LVM_MOVEITEMTOGROUP: */
9782 case LVM_REDRAWITEMS
:
9783 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9785 /* case LVM_REMOVEALLGROUPS: */
9787 /* case LVM_REMOVEGROUP: */
9790 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9792 case LVM_SETBKCOLOR
:
9793 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9795 /* case LVM_SETBKIMAGE: */
9797 case LVM_SETCALLBACKMASK
:
9798 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9801 case LVM_SETCOLUMNA
:
9802 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9804 case LVM_SETCOLUMNW
:
9805 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9807 case LVM_SETCOLUMNORDERARRAY
:
9808 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9810 case LVM_SETCOLUMNWIDTH
:
9811 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9813 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9814 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9816 /* case LVM_SETGROUPINFO: */
9818 /* case LVM_SETGROUPMETRICS: */
9820 case LVM_SETHOTCURSOR
:
9821 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9823 case LVM_SETHOTITEM
:
9824 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9826 case LVM_SETHOVERTIME
:
9827 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9829 case LVM_SETICONSPACING
:
9830 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9832 case LVM_SETIMAGELIST
:
9833 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9835 /* case LVM_SETINFOTIP: */
9837 /* case LVM_SETINSERTMARK: */
9839 /* case LVM_SETINSERTMARKCOLOR: */
9842 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9845 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9847 case LVM_SETITEMCOUNT
:
9848 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9850 case LVM_SETITEMPOSITION
:
9853 pt
.x
= (short)LOWORD(lParam
);
9854 pt
.y
= (short)HIWORD(lParam
);
9855 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
9858 case LVM_SETITEMPOSITION32
:
9859 if (lParam
== 0) return FALSE
;
9860 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
9862 case LVM_SETITEMSTATE
:
9863 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9865 case LVM_SETITEMTEXTA
:
9866 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9868 case LVM_SETITEMTEXTW
:
9869 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9871 /* case LVM_SETOUTLINECOLOR: */
9873 /* case LVM_SETSELECTEDCOLUMN: */
9875 case LVM_SETSELECTIONMARK
:
9876 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9878 case LVM_SETTEXTBKCOLOR
:
9879 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9881 case LVM_SETTEXTCOLOR
:
9882 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
9884 /* case LVM_SETTILEINFO: */
9886 /* case LVM_SETTILEVIEWINFO: */
9888 /* case LVM_SETTILEWIDTH: */
9890 case LVM_SETTOOLTIPS
:
9891 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
9893 case LVM_SETUNICODEFORMAT
:
9894 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
9896 /* case LVM_SETVIEW: */
9898 /* case LVM_SETWORKAREAS: */
9900 /* case LVM_SORTGROUPS: */
9903 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9905 /* LVM_SORTITEMSEX: */
9907 case LVM_SUBITEMHITTEST
:
9908 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
9911 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
9914 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
9917 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
9920 return LISTVIEW_NCCreate(hwnd
, (LPCREATESTRUCTW
)lParam
);
9923 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9926 return LISTVIEW_Destroy(infoPtr
);
9929 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
9932 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
9935 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9938 return (LRESULT
)infoPtr
->hFont
;
9941 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9944 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
9947 return LISTVIEW_KillFocus(infoPtr
);
9949 case WM_LBUTTONDBLCLK
:
9950 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9952 case WM_LBUTTONDOWN
:
9953 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9956 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9959 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9962 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9965 return LISTVIEW_NCDestroy(infoPtr
);
9968 if (LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
))
9973 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
9974 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
9977 case WM_NOTIFYFORMAT
:
9978 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
9980 case WM_PRINTCLIENT
:
9981 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
9984 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
9986 case WM_RBUTTONDBLCLK
:
9987 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9989 case WM_RBUTTONDOWN
:
9990 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9993 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
9996 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
10001 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
10004 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
10007 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
10010 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
10012 case WM_STYLECHANGED
:
10013 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
10015 case WM_SYSCOLORCHANGE
:
10016 COMCTL32_RefreshSysColors();
10019 /* case WM_TIMER: */
10020 case WM_THEMECHANGED
:
10021 return LISTVIEW_ThemeChanged(infoPtr
);
10024 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10026 case WM_MOUSEWHEEL
:
10027 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
10028 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10029 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
10031 case WM_WINDOWPOSCHANGED
:
10032 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
10034 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
10035 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
10036 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
10038 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
10040 MEASUREITEMSTRUCT mis
;
10041 mis
.CtlType
= ODT_LISTVIEW
;
10042 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
10046 mis
.itemHeight
= infoPtr
->nItemHeight
;
10047 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
10048 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
10049 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
10052 LISTVIEW_UpdateSize(infoPtr
);
10053 LISTVIEW_UpdateScroll(infoPtr
);
10055 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10057 /* case WM_WININICHANGE: */
10060 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
10061 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
10064 /* call default window procedure */
10065 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10072 * Registers the window class.
10080 void LISTVIEW_Register(void)
10082 WNDCLASSW wndClass
;
10084 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
10085 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
10086 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
10087 wndClass
.cbClsExtra
= 0;
10088 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
10089 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
10090 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
10091 wndClass
.lpszClassName
= WC_LISTVIEWW
;
10092 RegisterClassW(&wndClass
);
10097 * Unregisters the window class.
10105 void LISTVIEW_Unregister(void)
10107 UnregisterClassW(WC_LISTVIEWW
, NULL
);
10112 * Handle any WM_COMMAND messages
10115 * [I] infoPtr : valid pointer to the listview structure
10116 * [I] wParam : the first message parameter
10117 * [I] lParam : the second message parameter
10122 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10124 switch (HIWORD(wParam
))
10129 * Adjust the edit window size
10131 WCHAR buffer
[1024];
10132 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
10133 HFONT hFont
, hOldFont
= 0;
10137 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
10138 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
10139 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
10141 /* Select font to get the right dimension of the string */
10142 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
10145 hOldFont
= SelectObject(hdc
, hFont
);
10148 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
10150 TEXTMETRICW textMetric
;
10152 /* Add Extra spacing for the next character */
10153 GetTextMetricsW(hdc
, &textMetric
);
10154 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10162 rect
.bottom
- rect
.top
,
10163 SWP_DRAWFRAME
|SWP_NOMOVE
);
10166 SelectObject(hdc
, hOldFont
);
10168 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
10174 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
10183 * Subclassed edit control windproc function
10186 * [I] hwnd : the edit window handle
10187 * [I] uMsg : the message that is to be processed
10188 * [I] wParam : first message parameter
10189 * [I] lParam : second message parameter
10190 * [I] isW : TRUE if input is Unicode
10195 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
10197 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
10198 BOOL cancel
= FALSE
;
10200 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10201 hwnd
, uMsg
, wParam
, lParam
, isW
);
10205 case WM_GETDLGCODE
:
10206 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
10213 WNDPROC editProc
= infoPtr
->EditWndProc
;
10214 infoPtr
->EditWndProc
= 0;
10215 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
10216 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10220 if (VK_ESCAPE
== (INT
)wParam
)
10225 else if (VK_RETURN
== (INT
)wParam
)
10229 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10232 /* kill the edit */
10233 if (infoPtr
->hwndEdit
)
10235 LPWSTR buffer
= NULL
;
10237 infoPtr
->hwndEdit
= 0;
10240 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
10244 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
10246 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
10247 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
10251 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
10256 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
10262 * Subclassed edit control Unicode windproc function
10265 * [I] hwnd : the edit window handle
10266 * [I] uMsg : the message that is to be processed
10267 * [I] wParam : first message parameter
10268 * [I] lParam : second message parameter
10272 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10274 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
10279 * Subclassed edit control ANSI windproc function
10282 * [I] hwnd : the edit window handle
10283 * [I] uMsg : the message that is to be processed
10284 * [I] wParam : first message parameter
10285 * [I] lParam : second message parameter
10289 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10291 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
10296 * Creates a subclassed edit control
10299 * [I] infoPtr : valid pointer to the listview structure
10300 * [I] text : initial text for the edit
10301 * [I] style : the window style
10302 * [I] isW : TRUE if input is Unicode
10306 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
10307 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
10309 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
10314 TEXTMETRICW textMetric
;
10315 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
10317 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
10319 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
10320 hdc
= GetDC(infoPtr
->hwndSelf
);
10322 /* Select the font to get appropriate metric dimensions */
10323 if(infoPtr
->hFont
!= 0)
10324 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
10326 /*Get String Length in pixels */
10327 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
10329 /*Add Extra spacing for the next character */
10330 GetTextMetricsW(hdc
, &textMetric
);
10331 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10333 if(infoPtr
->hFont
!= 0)
10334 SelectObject(hdc
, hOldFont
);
10336 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
10338 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10340 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10342 if (!hedit
) return 0;
10344 infoPtr
->EditWndProc
= (WNDPROC
)
10345 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
10346 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
10348 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);