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
99 * -- LVS_EX_SIMPLESELECT
100 * -- LVS_EX_TWOCLICKACTIVATE
101 * -- LVS_EX_UNDERLINECOLD
102 * -- LVS_EX_UNDERLINEHOT
105 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
108 * -- LVN_MARQUEEBEGIN
114 * -- LVM_CANCELEDITLABEL
115 * -- LVM_ENABLEGROUPVIEW
116 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
117 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
118 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
119 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
120 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
121 * -- LVM_GETINSERTMARKRECT
122 * -- LVM_GETNUMBEROFWORKAREAS
123 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
124 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
125 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
126 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
127 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
128 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
129 * -- LVM_GETVIEW, LVM_SETVIEW
130 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
131 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
132 * -- LVM_INSERTGROUPSORTED
133 * -- LVM_INSERTMARKHITTEST
134 * -- LVM_ISGROUPVIEWENABLED
135 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
137 * -- LVM_MOVEITEMTOGROUP
139 * -- LVM_SETTILEWIDTH
144 * -- ListView_GetCheckSate, ListView_SetCheckState
145 * -- ListView_GetHoverTime, ListView_SetHoverTime
146 * -- ListView_GetISearchString
147 * -- ListView_GetNumberOfWorkAreas
148 * -- ListView_GetOrigin
149 * -- ListView_GetTextBkColor
150 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
151 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
152 * -- ListView_SortItemsEx
157 * Known differences in message stream from native control (not known if
158 * these differences cause problems):
159 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
160 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
161 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
162 * processing for "USEDOUBLECLICKTIME".
166 #include "wine/port.h"
181 #include "commctrl.h"
182 #include "comctl32.h"
185 #include "wine/debug.h"
186 #include "wine/unicode.h"
188 WINE_DEFAULT_DEBUG_CHANNEL(listview
);
190 /* make sure you set this to 0 for production use! */
191 #define DEBUG_RANGES 1
193 typedef struct tagCOLUMN_INFO
195 RECT rcHeader
; /* tracks the header's rectangle */
196 int fmt
; /* same as LVCOLUMN.fmt */
199 typedef struct tagITEMHDR
203 } ITEMHDR
, *LPITEMHDR
;
205 typedef struct tagSUBITEM_INFO
211 typedef struct tagITEM_INFO
219 typedef struct tagRANGE
225 typedef struct tagRANGES
230 typedef struct tagITERATOR
239 typedef struct tagDELAYED_ITEM_EDIT
245 typedef struct tagLISTVIEW_INFO
252 HIMAGELIST himlNormal
;
253 HIMAGELIST himlSmall
;
254 HIMAGELIST himlState
;
258 POINT ptClickPos
; /* point where the user clicked */
259 BOOL bNoItemMetrics
; /* flags if item metrics are not yet computed */
262 RANGES selectionRanges
;
267 RECT rcList
; /* This rectangle is really the window
268 * client rectangle possibly reduced by the
269 * horizontal scroll bar and/or header - see
270 * LISTVIEW_UpdateSize. This rectangle offset
271 * by the LISTVIEW_GetOrigin value is in
272 * client coordinates */
281 INT ntmHeight
; /* Some cached metrics of the font used */
282 INT ntmMaxCharWidth
; /* by the listview to draw items */
284 BOOL bRedraw
; /* Turns on/off repaints & invalidations */
285 BOOL bAutoarrange
; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
287 BOOL bDoChangeNotify
; /* send change notification messages? */
290 DWORD dwStyle
; /* the cached window GWL_STYLE */
291 DWORD dwLvExStyle
; /* extended listview style */
292 INT nItemCount
; /* the number of items in the list */
293 HDPA hdpaItems
; /* array ITEM_INFO pointers */
294 HDPA hdpaPosX
; /* maintains the (X, Y) coordinates of the */
295 HDPA hdpaPosY
; /* items in LVS_ICON, and LVS_SMALLICON modes */
296 HDPA hdpaColumns
; /* array of COLUMN_INFO pointers */
297 POINT currIconPos
; /* this is the position next icon will be placed */
298 PFNLVCOMPARE pfnCompare
;
306 DWORD cditemmode
; /* Keep the custom draw flags for an item/row */
308 DWORD lastKeyPressTimestamp
;
310 INT nSearchParamLength
;
311 WCHAR szSearchParam
[ MAX_PATH
];
313 INT nMeasureItemHeight
;
314 INT xTrackLine
; /* The x coefficient of the track line or -1 if none */
315 DELAYED_ITEM_EDIT itemEdit
; /* Pointer to this structure will be the timer ID */
321 /* How many we debug buffer to allocate */
322 #define DEBUG_BUFFERS 20
323 /* The size of a single debug bbuffer */
324 #define DEBUG_BUFFER_SIZE 256
326 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
327 #define SB_INTERNAL -1
329 /* maximum size of a label */
330 #define DISP_TEXT_SIZE 512
332 /* padding for items in list and small icon display modes */
333 #define WIDTH_PADDING 12
335 /* padding for items in list, report and small icon display modes */
336 #define HEIGHT_PADDING 1
338 /* offset of items in report display mode */
339 #define REPORT_MARGINX 2
341 /* padding for icon in large icon display mode
342 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
343 * that HITTEST will see.
344 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
345 * ICON_TOP_PADDING - sum of the two above.
346 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
347 * LABEL_HOR_PADDING - between text and sides of box
348 * LABEL_VERT_PADDING - between bottom of text and end of box
350 * ICON_LR_PADDING - additional width above icon size.
351 * ICON_LR_HALF - half of the above value
353 #define ICON_TOP_PADDING_NOTHITABLE 2
354 #define ICON_TOP_PADDING_HITABLE 2
355 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
356 #define ICON_BOTTOM_PADDING 4
357 #define LABEL_HOR_PADDING 5
358 #define LABEL_VERT_PADDING 7
359 #define ICON_LR_PADDING 16
360 #define ICON_LR_HALF (ICON_LR_PADDING/2)
362 /* default label width for items in list and small icon display modes */
363 #define DEFAULT_LABEL_WIDTH 40
365 /* default column width for items in list display mode */
366 #define DEFAULT_COLUMN_WIDTH 128
368 /* Size of "line" scroll for V & H scrolls */
369 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
371 /* Padding between image and label */
372 #define IMAGE_PADDING 2
374 /* Padding behind the label */
375 #define TRAILING_LABEL_PADDING 12
376 #define TRAILING_HEADER_PADDING 11
378 /* Border for the icon caption */
379 #define CAPTION_BORDER 2
381 /* Standard DrawText flags */
382 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
384 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
386 /* Image index from state */
387 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
389 /* The time in milliseconds to reset the search in the list */
390 #define KEY_DELAY 450
392 /* Dump the LISTVIEW_INFO structure to the debug channel */
393 #define LISTVIEW_DUMP(iP) do { \
394 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
395 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
396 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
397 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
398 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
399 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
400 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
401 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
402 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
403 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
406 static const WCHAR themeClass
[] = {'L','i','s','t','V','i','e','w',0};
409 * forward declarations
411 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*, LPLVITEMW
, BOOL
);
412 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*, INT
, LPRECT
);
413 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*, INT
, LPPOINT
);
414 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*, INT
, LPPOINT
);
415 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*, INT
, LPRECT
);
416 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*, INT
);
417 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*, LPPOINT
);
418 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*, LPRECT
);
419 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*, INT
);
420 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*, LVITEMW
*, BOOL
);
421 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*);
422 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*, INT
);
423 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*);
424 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*, INT
, BOOL
);
425 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*, WPARAM
, LPARAM
);
426 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*, PFNLVCOMPARE
, LPARAM
);
427 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*, LPCWSTR
, BOOL
);
428 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*, INT
);
429 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*, INT
, UINT
);
430 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*, INT
, const LVITEMW
*);
431 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
432 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*, INT
, INT
, HWND
);
433 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*);
434 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*, INT
, BOOL
);
435 static HWND
CreateEditLabelT(LISTVIEW_INFO
*, LPCWSTR
, DWORD
, INT
, INT
, INT
, INT
, BOOL
);
436 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*, INT
, HIMAGELIST
);
437 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*, LPLVHITTESTINFO
, BOOL
, BOOL
);
439 /******** Text handling functions *************************************/
441 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
442 * text string. The string may be ANSI or Unicode, in which case
443 * the boolean isW tells us the type of the string.
445 * The name of the function tell what type of strings it expects:
446 * W: Unicode, T: ANSI/Unicode - function of isW
449 static inline BOOL
is_textW(LPCWSTR text
)
451 return text
!= NULL
&& text
!= LPSTR_TEXTCALLBACKW
;
454 static inline BOOL
is_textT(LPCWSTR text
, BOOL isW
)
456 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
457 return is_textW(text
);
460 static inline int textlenT(LPCWSTR text
, BOOL isW
)
462 return !is_textT(text
, isW
) ? 0 :
463 isW
? lstrlenW(text
) : lstrlenA((LPCSTR
)text
);
466 static inline void textcpynT(LPWSTR dest
, BOOL isDestW
, LPCWSTR src
, BOOL isSrcW
, INT max
)
469 if (isSrcW
) lstrcpynW(dest
, src
, max
);
470 else MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)src
, -1, dest
, max
);
472 if (isSrcW
) WideCharToMultiByte(CP_ACP
, 0, src
, -1, (LPSTR
)dest
, max
, NULL
, NULL
);
473 else lstrcpynA((LPSTR
)dest
, (LPCSTR
)src
, max
);
476 static inline LPWSTR
textdupTtoW(LPCWSTR text
, BOOL isW
)
478 LPWSTR wstr
= (LPWSTR
)text
;
480 if (!isW
&& is_textT(text
, isW
))
482 INT len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, NULL
, 0);
483 wstr
= Alloc(len
* sizeof(WCHAR
));
484 if (wstr
) MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)text
, -1, wstr
, len
);
486 TRACE(" wstr=%s\n", text
== LPSTR_TEXTCALLBACKW
? "(callback)" : debugstr_w(wstr
));
490 static inline void textfreeT(LPWSTR wstr
, BOOL isW
)
492 if (!isW
&& is_textT(wstr
, isW
)) Free (wstr
);
496 * dest is a pointer to a Unicode string
497 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
499 static BOOL
textsetptrT(LPWSTR
*dest
, LPCWSTR src
, BOOL isW
)
503 if (src
== LPSTR_TEXTCALLBACKW
)
505 if (is_textW(*dest
)) Free(*dest
);
506 *dest
= LPSTR_TEXTCALLBACKW
;
510 LPWSTR pszText
= textdupTtoW(src
, isW
);
511 if (*dest
== LPSTR_TEXTCALLBACKW
) *dest
= NULL
;
512 bResult
= Str_SetPtrW(dest
, pszText
);
513 textfreeT(pszText
, isW
);
519 * compares a Unicode to a Unicode/ANSI text string
521 static inline int textcmpWT(LPCWSTR aw
, LPCWSTR bt
, BOOL isW
)
523 if (!aw
) return bt
? -1 : 0;
524 if (!bt
) return aw
? 1 : 0;
525 if (aw
== LPSTR_TEXTCALLBACKW
)
526 return bt
== LPSTR_TEXTCALLBACKW
? 0 : -1;
527 if (bt
!= LPSTR_TEXTCALLBACKW
)
529 LPWSTR bw
= textdupTtoW(bt
, isW
);
530 int r
= bw
? lstrcmpW(aw
, bw
) : 1;
538 static inline int lstrncmpiW(LPCWSTR s1
, LPCWSTR s2
, int n
)
542 n
= min(min(n
, lstrlenW(s1
)), lstrlenW(s2
));
543 res
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, s1
, n
, s2
, n
);
544 return res
? res
- sizeof(WCHAR
) : res
;
547 /******** Debugging functions *****************************************/
549 static inline LPCSTR
debugtext_t(LPCWSTR text
, BOOL isW
)
551 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
552 return isW
? debugstr_w(text
) : debugstr_a((LPCSTR
)text
);
555 static inline LPCSTR
debugtext_tn(LPCWSTR text
, BOOL isW
, INT n
)
557 if (text
== LPSTR_TEXTCALLBACKW
) return "(callback)";
558 n
= min(textlenT(text
, isW
), n
);
559 return isW
? debugstr_wn(text
, n
) : debugstr_an((LPCSTR
)text
, n
);
562 static char* debug_getbuf(void)
564 static int index
= 0;
565 static char buffers
[DEBUG_BUFFERS
][DEBUG_BUFFER_SIZE
];
566 return buffers
[index
++ % DEBUG_BUFFERS
];
569 static inline const char* debugrange(const RANGE
*lprng
)
571 if (!lprng
) return "(null)";
572 return wine_dbg_sprintf("[%d, %d]", lprng
->lower
, lprng
->upper
);
575 static const char* debugscrollinfo(const SCROLLINFO
*pScrollInfo
)
577 char* buf
= debug_getbuf(), *text
= buf
;
578 int len
, size
= DEBUG_BUFFER_SIZE
;
580 if (pScrollInfo
== NULL
) return "(null)";
581 len
= snprintf(buf
, size
, "{cbSize=%d, ", pScrollInfo
->cbSize
);
582 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
583 if (pScrollInfo
->fMask
& SIF_RANGE
)
584 len
= snprintf(buf
, size
, "nMin=%d, nMax=%d, ", pScrollInfo
->nMin
, pScrollInfo
->nMax
);
586 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
587 if (pScrollInfo
->fMask
& SIF_PAGE
)
588 len
= snprintf(buf
, size
, "nPage=%u, ", pScrollInfo
->nPage
);
590 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
591 if (pScrollInfo
->fMask
& SIF_POS
)
592 len
= snprintf(buf
, size
, "nPos=%d, ", pScrollInfo
->nPos
);
594 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
595 if (pScrollInfo
->fMask
& SIF_TRACKPOS
)
596 len
= snprintf(buf
, size
, "nTrackPos=%d, ", pScrollInfo
->nTrackPos
);
598 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
601 buf
= text
+ strlen(text
);
603 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
607 static const char* debugnmlistview(const NMLISTVIEW
*plvnm
)
609 if (!plvnm
) return "(null)";
610 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
611 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
612 plvnm
->iItem
, plvnm
->iSubItem
, plvnm
->uNewState
, plvnm
->uOldState
,
613 plvnm
->uChanged
, wine_dbgstr_point(&plvnm
->ptAction
), plvnm
->lParam
);
616 static const char* debuglvitem_t(const LVITEMW
*lpLVItem
, BOOL isW
)
618 char* buf
= debug_getbuf(), *text
= buf
;
619 int len
, size
= DEBUG_BUFFER_SIZE
;
621 if (lpLVItem
== NULL
) return "(null)";
622 len
= snprintf(buf
, size
, "{iItem=%d, iSubItem=%d, ", lpLVItem
->iItem
, lpLVItem
->iSubItem
);
623 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
624 if (lpLVItem
->mask
& LVIF_STATE
)
625 len
= snprintf(buf
, size
, "state=%x, stateMask=%x, ", lpLVItem
->state
, lpLVItem
->stateMask
);
627 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
628 if (lpLVItem
->mask
& LVIF_TEXT
)
629 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem
->pszText
, isW
, 80), lpLVItem
->cchTextMax
);
631 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
632 if (lpLVItem
->mask
& LVIF_IMAGE
)
633 len
= snprintf(buf
, size
, "iImage=%d, ", lpLVItem
->iImage
);
635 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
636 if (lpLVItem
->mask
& LVIF_PARAM
)
637 len
= snprintf(buf
, size
, "lParam=%lx, ", lpLVItem
->lParam
);
639 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
640 if (lpLVItem
->mask
& LVIF_INDENT
)
641 len
= snprintf(buf
, size
, "iIndent=%d, ", lpLVItem
->iIndent
);
643 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
646 buf
= text
+ strlen(text
);
648 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
652 static const char* debuglvcolumn_t(const LVCOLUMNW
*lpColumn
, BOOL isW
)
654 char* buf
= debug_getbuf(), *text
= buf
;
655 int len
, size
= DEBUG_BUFFER_SIZE
;
657 if (lpColumn
== NULL
) return "(null)";
658 len
= snprintf(buf
, size
, "{");
659 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
660 if (lpColumn
->mask
& LVCF_SUBITEM
)
661 len
= snprintf(buf
, size
, "iSubItem=%d, ", lpColumn
->iSubItem
);
663 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
664 if (lpColumn
->mask
& LVCF_FMT
)
665 len
= snprintf(buf
, size
, "fmt=%x, ", lpColumn
->fmt
);
667 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
668 if (lpColumn
->mask
& LVCF_WIDTH
)
669 len
= snprintf(buf
, size
, "cx=%d, ", lpColumn
->cx
);
671 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
672 if (lpColumn
->mask
& LVCF_TEXT
)
673 len
= snprintf(buf
, size
, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn
->pszText
, isW
, 80), lpColumn
->cchTextMax
);
675 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
676 if (lpColumn
->mask
& LVCF_IMAGE
)
677 len
= snprintf(buf
, size
, "iImage=%d, ", lpColumn
->iImage
);
679 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
680 if (lpColumn
->mask
& LVCF_ORDER
)
681 len
= snprintf(buf
, size
, "iOrder=%d, ", lpColumn
->iOrder
);
683 if (len
== -1) goto end
; buf
+= len
; size
-= len
;
686 buf
= text
+ strlen(text
);
688 if (buf
- text
> 2) { buf
[-2] = '}'; buf
[-1] = 0; }
692 static const char* debuglvhittestinfo(const LVHITTESTINFO
*lpht
)
694 if (!lpht
) return "(null)";
696 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
697 wine_dbgstr_point(&lpht
->pt
), lpht
->flags
, lpht
->iItem
, lpht
->iSubItem
);
700 /* Return the corresponding text for a given scroll value */
701 static inline LPCSTR
debugscrollcode(int nScrollCode
)
705 case SB_LINELEFT
: return "SB_LINELEFT";
706 case SB_LINERIGHT
: return "SB_LINERIGHT";
707 case SB_PAGELEFT
: return "SB_PAGELEFT";
708 case SB_PAGERIGHT
: return "SB_PAGERIGHT";
709 case SB_THUMBPOSITION
: return "SB_THUMBPOSITION";
710 case SB_THUMBTRACK
: return "SB_THUMBTRACK";
711 case SB_ENDSCROLL
: return "SB_ENDSCROLL";
712 case SB_INTERNAL
: return "SB_INTERNAL";
713 default: return "unknown";
718 /******** Notification functions i************************************/
720 static LRESULT
notify_forward_header(const LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
722 return SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
,
723 (WPARAM
)lpnmh
->hdr
.idFrom
, (LPARAM
)lpnmh
);
726 static LRESULT
notify_hdr(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMHDR pnmh
)
730 TRACE("(code=%d)\n", code
);
732 pnmh
->hwndFrom
= infoPtr
->hwndSelf
;
733 pnmh
->idFrom
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
735 result
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFY
, pnmh
->idFrom
, (LPARAM
)pnmh
);
737 TRACE(" <= %ld\n", result
);
742 static inline BOOL
notify(const LISTVIEW_INFO
*infoPtr
, INT code
)
745 HWND hwnd
= infoPtr
->hwndSelf
;
746 notify_hdr(infoPtr
, code
, &nmh
);
747 return IsWindow(hwnd
);
750 static inline void notify_itemactivate(const LISTVIEW_INFO
*infoPtr
, const LVHITTESTINFO
*htInfo
)
761 item
.mask
= LVIF_PARAM
|LVIF_STATE
;
762 item
.iItem
= htInfo
->iItem
;
764 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) {
765 nmia
.lParam
= item
.lParam
;
766 nmia
.uOldState
= item
.state
;
767 nmia
.uNewState
= item
.state
| LVIS_ACTIVATING
;
768 nmia
.uChanged
= LVIF_STATE
;
771 nmia
.iItem
= htInfo
->iItem
;
772 nmia
.iSubItem
= htInfo
->iSubItem
;
773 nmia
.ptAction
= htInfo
->pt
;
775 if (GetKeyState(VK_SHIFT
) & 0x8000) nmia
.uKeyFlags
|= LVKF_SHIFT
;
776 if (GetKeyState(VK_CONTROL
) & 0x8000) nmia
.uKeyFlags
|= LVKF_CONTROL
;
777 if (GetKeyState(VK_MENU
) & 0x8000) nmia
.uKeyFlags
|= LVKF_ALT
;
779 notify_hdr(infoPtr
, LVN_ITEMACTIVATE
, (LPNMHDR
)&nmia
);
782 static inline LRESULT
notify_listview(const LISTVIEW_INFO
*infoPtr
, INT code
, LPNMLISTVIEW plvnm
)
784 TRACE("(code=%d, plvnm=%s)\n", code
, debugnmlistview(plvnm
));
785 return notify_hdr(infoPtr
, code
, (LPNMHDR
)plvnm
);
788 static BOOL
notify_click(const LISTVIEW_INFO
*infoPtr
, INT code
, const LVHITTESTINFO
*lvht
)
792 HWND hwnd
= infoPtr
->hwndSelf
;
794 TRACE("code=%d, lvht=%s\n", code
, debuglvhittestinfo(lvht
));
795 ZeroMemory(&nmlv
, sizeof(nmlv
));
796 nmlv
.iItem
= lvht
->iItem
;
797 nmlv
.iSubItem
= lvht
->iSubItem
;
798 nmlv
.ptAction
= lvht
->pt
;
799 item
.mask
= LVIF_PARAM
;
800 item
.iItem
= lvht
->iItem
;
802 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
803 notify_listview(infoPtr
, code
, &nmlv
);
804 return IsWindow(hwnd
);
807 static BOOL
notify_deleteitem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
811 HWND hwnd
= infoPtr
->hwndSelf
;
813 ZeroMemory(&nmlv
, sizeof (NMLISTVIEW
));
815 item
.mask
= LVIF_PARAM
;
818 if (LISTVIEW_GetItemT(infoPtr
, &item
, TRUE
)) nmlv
.lParam
= item
.lParam
;
819 notify_listview(infoPtr
, LVN_DELETEITEM
, &nmlv
);
820 return IsWindow(hwnd
);
823 static int get_ansi_notification(UINT unicodeNotificationCode
)
825 switch (unicodeNotificationCode
)
827 case LVN_BEGINLABELEDITW
: return LVN_BEGINLABELEDITA
;
828 case LVN_ENDLABELEDITW
: return LVN_ENDLABELEDITA
;
829 case LVN_GETDISPINFOW
: return LVN_GETDISPINFOA
;
830 case LVN_SETDISPINFOW
: return LVN_SETDISPINFOA
;
831 case LVN_ODFINDITEMW
: return LVN_ODFINDITEMA
;
832 case LVN_GETINFOTIPW
: return LVN_GETINFOTIPA
;
834 ERR("unknown notification %x\n", unicodeNotificationCode
);
840 Send notification. depends on dispinfoW having same
841 structure as dispinfoA.
842 infoPtr : listview struct
843 notificationCode : *Unicode* notification code
844 pdi : dispinfo structure (can be unicode or ansi)
845 isW : TRUE if dispinfo is Unicode
847 static BOOL
notify_dispinfoT(const LISTVIEW_INFO
*infoPtr
, UINT notificationCode
, LPNMLVDISPINFOW pdi
, BOOL isW
)
849 BOOL bResult
= FALSE
;
850 BOOL convertToAnsi
= FALSE
, convertToUnicode
= FALSE
;
851 INT cchTempBufMax
= 0, savCchTextMax
= 0;
853 LPWSTR pszTempBuf
= NULL
, savPszText
= NULL
;
855 if ((pdi
->item
.mask
& LVIF_TEXT
) && is_textT(pdi
->item
.pszText
, isW
))
857 convertToAnsi
= (isW
&& infoPtr
->notifyFormat
== NFR_ANSI
);
858 convertToUnicode
= (!isW
&& infoPtr
->notifyFormat
== NFR_UNICODE
);
861 if (convertToAnsi
|| convertToUnicode
)
863 if (notificationCode
!= LVN_GETDISPINFOW
)
865 cchTempBufMax
= convertToUnicode
?
866 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1, NULL
, 0):
867 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, NULL
, 0, NULL
, NULL
);
871 cchTempBufMax
= pdi
->item
.cchTextMax
;
872 *pdi
->item
.pszText
= 0; /* make sure we don't process garbage */
875 pszTempBuf
= Alloc( (convertToUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) * cchTempBufMax
);
876 if (!pszTempBuf
) return FALSE
;
878 if (convertToUnicode
)
879 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pdi
->item
.pszText
, -1,
880 pszTempBuf
, cchTempBufMax
);
882 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) pszTempBuf
,
883 cchTempBufMax
, NULL
, NULL
);
885 savCchTextMax
= pdi
->item
.cchTextMax
;
886 savPszText
= pdi
->item
.pszText
;
887 pdi
->item
.pszText
= pszTempBuf
;
888 pdi
->item
.cchTextMax
= cchTempBufMax
;
891 if (infoPtr
->notifyFormat
== NFR_ANSI
)
892 realNotifCode
= get_ansi_notification(notificationCode
);
894 realNotifCode
= notificationCode
;
895 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi
->item
, infoPtr
->notifyFormat
!= NFR_ANSI
));
896 bResult
= notify_hdr(infoPtr
, realNotifCode
, &pdi
->hdr
);
898 if (convertToUnicode
|| convertToAnsi
)
900 if (convertToUnicode
) /* note : pointer can be changed by app ! */
901 WideCharToMultiByte(CP_ACP
, 0, pdi
->item
.pszText
, -1, (LPSTR
) savPszText
,
902 savCchTextMax
, NULL
, NULL
);
904 MultiByteToWideChar(CP_ACP
, 0, (LPSTR
) pdi
->item
.pszText
, -1,
905 savPszText
, savCchTextMax
);
906 pdi
->item
.pszText
= savPszText
; /* restores our buffer */
907 pdi
->item
.cchTextMax
= savCchTextMax
;
913 static void customdraw_fill(NMLVCUSTOMDRAW
*lpnmlvcd
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
,
914 const RECT
*rcBounds
, const LVITEMW
*lplvItem
)
916 ZeroMemory(lpnmlvcd
, sizeof(NMLVCUSTOMDRAW
));
917 lpnmlvcd
->nmcd
.hdc
= hdc
;
918 lpnmlvcd
->nmcd
.rc
= *rcBounds
;
919 lpnmlvcd
->clrTextBk
= infoPtr
->clrTextBk
;
920 lpnmlvcd
->clrText
= infoPtr
->clrText
;
921 if (!lplvItem
) return;
922 lpnmlvcd
->nmcd
.dwItemSpec
= lplvItem
->iItem
+ 1;
923 lpnmlvcd
->iSubItem
= lplvItem
->iSubItem
;
924 if (lplvItem
->state
& LVIS_SELECTED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_SELECTED
;
925 if (lplvItem
->state
& LVIS_FOCUSED
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_FOCUS
;
926 if (lplvItem
->iItem
== infoPtr
->nHotItem
) lpnmlvcd
->nmcd
.uItemState
|= CDIS_HOT
;
927 lpnmlvcd
->nmcd
.lItemlParam
= lplvItem
->lParam
;
930 static inline DWORD
notify_customdraw (const LISTVIEW_INFO
*infoPtr
, DWORD dwDrawStage
, NMLVCUSTOMDRAW
*lpnmlvcd
)
932 BOOL isForItem
= (lpnmlvcd
->nmcd
.dwItemSpec
!= 0);
935 lpnmlvcd
->nmcd
.dwDrawStage
= dwDrawStage
;
936 if (isForItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_ITEM
;
937 if (lpnmlvcd
->iSubItem
) lpnmlvcd
->nmcd
.dwDrawStage
|= CDDS_SUBITEM
;
938 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
--;
939 result
= notify_hdr(infoPtr
, NM_CUSTOMDRAW
, &lpnmlvcd
->nmcd
.hdr
);
940 if (isForItem
) lpnmlvcd
->nmcd
.dwItemSpec
++;
944 static void prepaint_setup (const LISTVIEW_INFO
*infoPtr
, HDC hdc
, NMLVCUSTOMDRAW
*lpnmlvcd
, BOOL SubItem
)
946 if (lpnmlvcd
->clrTextBk
== CLR_DEFAULT
)
947 lpnmlvcd
->clrTextBk
= comctl32_color
.clrWindow
;
948 if (lpnmlvcd
->clrText
== CLR_DEFAULT
)
949 lpnmlvcd
->clrText
= comctl32_color
.clrWindowText
;
951 /* apparently, for selected items, we have to override the returned values */
954 if (lpnmlvcd
->nmcd
.uItemState
& CDIS_SELECTED
)
958 lpnmlvcd
->clrTextBk
= comctl32_color
.clrHighlight
;
959 lpnmlvcd
->clrText
= comctl32_color
.clrHighlightText
;
961 else if (infoPtr
->dwStyle
& LVS_SHOWSELALWAYS
)
963 lpnmlvcd
->clrTextBk
= comctl32_color
.clr3dFace
;
964 lpnmlvcd
->clrText
= comctl32_color
.clrBtnText
;
969 /* Set the text attributes */
970 if (lpnmlvcd
->clrTextBk
!= CLR_NONE
)
972 SetBkMode(hdc
, OPAQUE
);
973 SetBkColor(hdc
,lpnmlvcd
->clrTextBk
);
976 SetBkMode(hdc
, TRANSPARENT
);
977 SetTextColor(hdc
, lpnmlvcd
->clrText
);
980 static inline DWORD
notify_postpaint (const LISTVIEW_INFO
*infoPtr
, NMLVCUSTOMDRAW
*lpnmlvcd
)
982 return notify_customdraw(infoPtr
, CDDS_POSTPAINT
, lpnmlvcd
);
985 /******** Item iterator functions **********************************/
987 static RANGES
ranges_create(int count
);
988 static void ranges_destroy(RANGES ranges
);
989 static BOOL
ranges_add(RANGES ranges
, RANGE range
);
990 static BOOL
ranges_del(RANGES ranges
, RANGE range
);
991 static void ranges_dump(RANGES ranges
);
993 static inline BOOL
ranges_additem(RANGES ranges
, INT nItem
)
995 RANGE range
= { nItem
, nItem
+ 1 };
997 return ranges_add(ranges
, range
);
1000 static inline BOOL
ranges_delitem(RANGES ranges
, INT nItem
)
1002 RANGE range
= { nItem
, nItem
+ 1 };
1004 return ranges_del(ranges
, range
);
1008 * ITERATOR DOCUMENTATION
1010 * The iterator functions allow for easy, and convenient iteration
1011 * over items of interest in the list. Typically, you create a
1012 * iterator, use it, and destroy it, as such:
1015 * iterator_xxxitems(&i, ...);
1016 * while (iterator_{prev,next}(&i)
1018 * //code which uses i.nItem
1020 * iterator_destroy(&i);
1022 * where xxx is either: framed, or visible.
1023 * Note that it is important that the code destroys the iterator
1024 * after it's done with it, as the creation of the iterator may
1025 * allocate memory, which thus needs to be freed.
1027 * You can iterate both forwards, and backwards through the list,
1028 * by using iterator_next or iterator_prev respectively.
1030 * Lower numbered items are draw on top of higher number items in
1031 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1032 * items may overlap). So, to test items, you should use
1034 * which lists the items top to bottom (in Z-order).
1035 * For drawing items, you should use
1037 * which lists the items bottom to top (in Z-order).
1038 * If you keep iterating over the items after the end-of-items
1039 * marker (-1) is returned, the iterator will start from the
1040 * beginning. Typically, you don't need to test for -1,
1041 * because iterator_{next,prev} will return TRUE if more items
1042 * are to be iterated over, or FALSE otherwise.
1044 * Note: the iterator is defined to be bidirectional. That is,
1045 * any number of prev followed by any number of next, or
1046 * five versa, should leave the iterator at the same item:
1047 * prev * n, next * n = next * n, prev * n
1049 * The iterator has a notion of an out-of-order, special item,
1050 * which sits at the start of the list. This is used in
1051 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1052 * which needs to be first, as it may overlap other items.
1054 * The code is a bit messy because we have:
1055 * - a special item to deal with
1056 * - simple range, or composite range
1058 * If you find bugs, or want to add features, please make sure you
1059 * always check/modify *both* iterator_prev, and iterator_next.
1063 * This function iterates through the items in increasing order,
1064 * but prefixed by the special item, then -1. That is:
1065 * special, 1, 2, 3, ..., n, -1.
1066 * Each item is listed only once.
1068 static inline BOOL
iterator_next(ITERATOR
* i
)
1072 i
->nItem
= i
->nSpecial
;
1073 if (i
->nItem
!= -1) return TRUE
;
1075 if (i
->nItem
== i
->nSpecial
)
1077 if (i
->ranges
) i
->index
= 0;
1083 if (i
->nItem
== i
->nSpecial
) i
->nItem
++;
1084 if (i
->nItem
< i
->range
.upper
) return TRUE
;
1089 if (i
->index
< DPA_GetPtrCount(i
->ranges
->hdpa
))
1090 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, i
->index
++);
1093 else if (i
->nItem
>= i
->range
.upper
) goto end
;
1095 i
->nItem
= i
->range
.lower
;
1096 if (i
->nItem
>= 0) goto testitem
;
1103 * This function iterates through the items in decreasing order,
1104 * followed by the special item, then -1. That is:
1105 * n, n-1, ..., 3, 2, 1, special, -1.
1106 * Each item is listed only once.
1108 static inline BOOL
iterator_prev(ITERATOR
* i
)
1115 if (i
->ranges
) i
->index
= DPA_GetPtrCount(i
->ranges
->hdpa
);
1118 if (i
->nItem
== i
->nSpecial
)
1126 if (i
->nItem
== i
->nSpecial
) i
->nItem
--;
1127 if (i
->nItem
>= i
->range
.lower
) return TRUE
;
1133 i
->range
= *(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, --i
->index
);
1136 else if (!start
&& i
->nItem
< i
->range
.lower
) goto end
;
1138 i
->nItem
= i
->range
.upper
;
1139 if (i
->nItem
> 0) goto testitem
;
1141 return (i
->nItem
= i
->nSpecial
) != -1;
1144 static RANGE
iterator_range(const ITERATOR
*i
)
1148 if (!i
->ranges
) return i
->range
;
1150 if (DPA_GetPtrCount(i
->ranges
->hdpa
) > 0)
1152 range
.lower
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, 0)).lower
;
1153 range
.upper
= (*(RANGE
*)DPA_GetPtr(i
->ranges
->hdpa
, DPA_GetPtrCount(i
->ranges
->hdpa
) - 1)).upper
;
1155 else range
.lower
= range
.upper
= 0;
1161 * Releases resources associated with this ierator.
1163 static inline void iterator_destroy(const ITERATOR
*i
)
1165 ranges_destroy(i
->ranges
);
1169 * Create an empty iterator.
1171 static inline BOOL
iterator_empty(ITERATOR
* i
)
1173 ZeroMemory(i
, sizeof(*i
));
1174 i
->nItem
= i
->nSpecial
= i
->range
.lower
= i
->range
.upper
= -1;
1179 * Create an iterator over a range.
1181 static inline BOOL
iterator_rangeitems(ITERATOR
* i
, RANGE range
)
1189 * Create an iterator over a bunch of ranges.
1190 * Please note that the iterator will take ownership of the ranges,
1191 * and will free them upon destruction.
1193 static inline BOOL
iterator_rangesitems(ITERATOR
* i
, RANGES ranges
)
1201 * Creates an iterator over the items which intersect lprc.
1203 static BOOL
iterator_frameditems(ITERATOR
* i
, const LISTVIEW_INFO
* infoPtr
, const RECT
*lprc
)
1205 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1206 RECT frame
= *lprc
, rcItem
, rcTemp
;
1209 /* in case we fail, we want to return an empty iterator */
1210 if (!iterator_empty(i
)) return FALSE
;
1212 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1214 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc
));
1215 OffsetRect(&frame
, -Origin
.x
, -Origin
.y
);
1217 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
1221 if (uView
== LVS_ICON
&& infoPtr
->nFocusedItem
!= -1)
1223 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcItem
);
1224 if (IntersectRect(&rcTemp
, &rcItem
, lprc
))
1225 i
->nSpecial
= infoPtr
->nFocusedItem
;
1227 if (!(iterator_rangesitems(i
, ranges_create(50)))) return FALSE
;
1228 /* to do better here, we need to have PosX, and PosY sorted */
1229 TRACE("building icon ranges:\n");
1230 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
1232 rcItem
.left
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1233 rcItem
.top
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1234 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1235 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1236 if (IntersectRect(&rcTemp
, &rcItem
, &frame
))
1237 ranges_additem(i
->ranges
, nItem
);
1241 else if (uView
== LVS_REPORT
)
1245 if (frame
.left
>= infoPtr
->nItemWidth
) return TRUE
;
1246 if (frame
.top
>= infoPtr
->nItemHeight
* infoPtr
->nItemCount
) return TRUE
;
1248 range
.lower
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1249 range
.upper
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, infoPtr
->nItemCount
- 1) + 1;
1250 if (range
.upper
<= range
.lower
) return TRUE
;
1251 if (!iterator_rangeitems(i
, range
)) return FALSE
;
1252 TRACE(" report=%s\n", debugrange(&i
->range
));
1256 INT nPerCol
= max((infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
, 1);
1257 INT nFirstRow
= max(frame
.top
/ infoPtr
->nItemHeight
, 0);
1258 INT nLastRow
= min((frame
.bottom
- 1) / infoPtr
->nItemHeight
, nPerCol
- 1);
1259 INT nFirstCol
= max(frame
.left
/ infoPtr
->nItemWidth
, 0);
1260 INT nLastCol
= min((frame
.right
- 1) / infoPtr
->nItemWidth
, (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
);
1261 INT lower
= nFirstCol
* nPerCol
+ nFirstRow
;
1265 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1266 nPerCol
, nFirstRow
, nLastRow
, nFirstCol
, nLastCol
, lower
);
1268 if (nLastCol
< nFirstCol
|| nLastRow
< nFirstRow
) return TRUE
;
1270 if (!(iterator_rangesitems(i
, ranges_create(nLastCol
- nFirstCol
+ 1)))) return FALSE
;
1271 TRACE("building list ranges:\n");
1272 for (nCol
= nFirstCol
; nCol
<= nLastCol
; nCol
++)
1274 item_range
.lower
= nCol
* nPerCol
+ nFirstRow
;
1275 if(item_range
.lower
>= infoPtr
->nItemCount
) break;
1276 item_range
.upper
= min(nCol
* nPerCol
+ nLastRow
+ 1, infoPtr
->nItemCount
);
1277 TRACE(" list=%s\n", debugrange(&item_range
));
1278 ranges_add(i
->ranges
, item_range
);
1286 * Creates an iterator over the items which intersect the visible region of hdc.
1288 static BOOL
iterator_visibleitems(ITERATOR
*i
, const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
1290 POINT Origin
, Position
;
1291 RECT rcItem
, rcClip
;
1294 rgntype
= GetClipBox(hdc
, &rcClip
);
1295 if (rgntype
== NULLREGION
) return iterator_empty(i
);
1296 if (!iterator_frameditems(i
, infoPtr
, &rcClip
)) return FALSE
;
1297 if (rgntype
== SIMPLEREGION
) return TRUE
;
1299 /* first deal with the special item */
1300 if (i
->nSpecial
!= -1)
1302 LISTVIEW_GetItemBox(infoPtr
, i
->nSpecial
, &rcItem
);
1303 if (!RectVisible(hdc
, &rcItem
)) i
->nSpecial
= -1;
1306 /* if we can't deal with the region, we'll just go with the simple range */
1307 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1308 TRACE("building visible range:\n");
1309 if (!i
->ranges
&& i
->range
.lower
< i
->range
.upper
)
1311 if (!(i
->ranges
= ranges_create(50))) return TRUE
;
1312 if (!ranges_add(i
->ranges
, i
->range
))
1314 ranges_destroy(i
->ranges
);
1320 /* now delete the invisible items from the list */
1321 while(iterator_next(i
))
1323 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
1324 rcItem
.left
= Position
.x
+ Origin
.x
;
1325 rcItem
.top
= Position
.y
+ Origin
.y
;
1326 rcItem
.right
= rcItem
.left
+ infoPtr
->nItemWidth
;
1327 rcItem
.bottom
= rcItem
.top
+ infoPtr
->nItemHeight
;
1328 if (!RectVisible(hdc
, &rcItem
))
1329 ranges_delitem(i
->ranges
, i
->nItem
);
1331 /* the iterator should restart on the next iterator_next */
1337 /******** Misc helper functions ************************************/
1339 static inline LRESULT
CallWindowProcT(WNDPROC proc
, HWND hwnd
, UINT uMsg
,
1340 WPARAM wParam
, LPARAM lParam
, BOOL isW
)
1342 if (isW
) return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
1343 else return CallWindowProcA(proc
, hwnd
, uMsg
, wParam
, lParam
);
1346 static inline BOOL
is_autoarrange(const LISTVIEW_INFO
*infoPtr
)
1348 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1350 return ((infoPtr
->dwStyle
& LVS_AUTOARRANGE
) || infoPtr
->bAutoarrange
) &&
1351 (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
);
1354 static void toggle_checkbox_state(LISTVIEW_INFO
*infoPtr
, INT nItem
)
1356 DWORD state
= STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_STATEIMAGEMASK
));
1357 if(state
== 1 || state
== 2)
1361 lvitem
.state
= INDEXTOSTATEIMAGEMASK(state
);
1362 lvitem
.stateMask
= LVIS_STATEIMAGEMASK
;
1363 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvitem
);
1367 /******** Internal API functions ************************************/
1369 static inline COLUMN_INFO
* LISTVIEW_GetColumnInfo(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
)
1371 static COLUMN_INFO mainItem
;
1373 if (nSubItem
== 0 && DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0) return &mainItem
;
1374 assert (nSubItem
>= 0 && nSubItem
< DPA_GetPtrCount(infoPtr
->hdpaColumns
));
1375 return DPA_GetPtr(infoPtr
->hdpaColumns
, nSubItem
);
1378 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO
*infoPtr
, INT nSubItem
, LPRECT lprc
)
1380 *lprc
= LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->rcHeader
;
1383 static inline BOOL
LISTVIEW_GetItemW(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
)
1385 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
1388 /* Listview invalidation functions: use _only_ these functions to invalidate */
1390 static inline BOOL
is_redrawing(const LISTVIEW_INFO
*infoPtr
)
1392 return infoPtr
->bRedraw
;
1395 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO
*infoPtr
, const RECT
* rect
)
1397 if(!is_redrawing(infoPtr
)) return;
1398 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect
));
1399 InvalidateRect(infoPtr
->hwndSelf
, rect
, TRUE
);
1402 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
1406 if(!is_redrawing(infoPtr
)) return;
1407 LISTVIEW_GetItemBox(infoPtr
, nItem
, &rcBox
);
1408 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1411 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, INT nSubItem
)
1413 POINT Origin
, Position
;
1416 if(!is_redrawing(infoPtr
)) return;
1417 assert ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
);
1418 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
1419 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
1420 LISTVIEW_GetHeaderRect(infoPtr
, nSubItem
, &rcBox
);
1422 rcBox
.bottom
= infoPtr
->nItemHeight
;
1423 OffsetRect(&rcBox
, Origin
.x
+ Position
.x
, Origin
.y
+ Position
.y
);
1424 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1427 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO
*infoPtr
)
1429 LISTVIEW_InvalidateRect(infoPtr
, NULL
);
1432 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
1436 if(!is_redrawing(infoPtr
)) return;
1437 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
1438 rcCol
.top
= infoPtr
->rcList
.top
;
1439 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
1440 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
1445 * Retrieves the number of items that can fit vertically in the client area.
1448 * [I] infoPtr : valid pointer to the listview structure
1451 * Number of items per row.
1453 static inline INT
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO
*infoPtr
)
1455 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1457 return max(nListWidth
/infoPtr
->nItemWidth
, 1);
1462 * Retrieves the number of items that can fit horizontally in the client
1466 * [I] infoPtr : valid pointer to the listview structure
1469 * Number of items per column.
1471 static inline INT
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO
*infoPtr
)
1473 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1475 return max(nListHeight
/ infoPtr
->nItemHeight
, 1);
1479 /*************************************************************************
1480 * LISTVIEW_ProcessLetterKeys
1482 * Processes keyboard messages generated by pressing the letter keys
1484 * What this does is perform a case insensitive search from the
1485 * current position with the following quirks:
1486 * - If two chars or more are pressed in quick succession we search
1487 * for the corresponding string (e.g. 'abc').
1488 * - If there is a delay we wipe away the current search string and
1489 * restart with just that char.
1490 * - If the user keeps pressing the same character, whether slowly or
1491 * fast, so that the search string is entirely composed of this
1492 * character ('aaaaa' for instance), then we search for first item
1493 * that starting with that character.
1494 * - If the user types the above character in quick succession, then
1495 * we must also search for the corresponding string ('aaaaa'), and
1496 * go to that string if there is a match.
1499 * [I] hwnd : handle to the window
1500 * [I] charCode : the character code, the actual character
1501 * [I] keyData : key data
1509 * - The current implementation has a list of characters it will
1510 * accept and it ignores everything else. In particular it will
1511 * ignore accentuated characters which seems to match what
1512 * Windows does. But I'm not sure it makes sense to follow
1514 * - We don't sound a beep when the search fails.
1518 * TREEVIEW_ProcessLetterKeys
1520 static INT
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO
*infoPtr
, WPARAM charCode
, LPARAM keyData
)
1525 WCHAR buffer
[MAX_PATH
];
1526 DWORD lastKeyPressTimestamp
= infoPtr
->lastKeyPressTimestamp
;
1528 /* simple parameter checking */
1529 if (!charCode
|| !keyData
) return 0;
1531 /* only allow the valid WM_CHARs through */
1532 if (!isalnumW(charCode
) &&
1533 charCode
!= '.' && charCode
!= '`' && charCode
!= '!' &&
1534 charCode
!= '@' && charCode
!= '#' && charCode
!= '$' &&
1535 charCode
!= '%' && charCode
!= '^' && 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
!= '~')
1544 /* if there's one item or less, there is no where to go */
1545 if (infoPtr
->nItemCount
<= 1) return 0;
1547 /* update the search parameters */
1548 infoPtr
->lastKeyPressTimestamp
= GetTickCount();
1549 if (infoPtr
->lastKeyPressTimestamp
- lastKeyPressTimestamp
< KEY_DELAY
) {
1550 if (infoPtr
->nSearchParamLength
< MAX_PATH
)
1551 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
++]=charCode
;
1552 if (infoPtr
->charCode
!= charCode
)
1553 infoPtr
->charCode
= charCode
= 0;
1555 infoPtr
->charCode
=charCode
;
1556 infoPtr
->szSearchParam
[0]=charCode
;
1557 infoPtr
->nSearchParamLength
=1;
1558 /* Redundant with the 1 char string */
1562 /* and search from the current position */
1564 if (infoPtr
->nFocusedItem
>= 0) {
1565 endidx
=infoPtr
->nFocusedItem
;
1567 /* if looking for single character match,
1568 * then we must always move forward
1570 if (infoPtr
->nSearchParamLength
== 1)
1573 endidx
=infoPtr
->nItemCount
;
1577 /* Let application handle this for virtual listview */
1578 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
1583 ZeroMemory(&lvfi
, sizeof(lvfi
));
1584 lvfi
.flags
= (LVFI_WRAP
| LVFI_PARTIAL
);
1585 infoPtr
->szSearchParam
[infoPtr
->nSearchParamLength
] = '\0';
1586 lvfi
.psz
= infoPtr
->szSearchParam
;
1590 nItem
= notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
1593 LISTVIEW_KeySelection(infoPtr
, nItem
);
1599 if (idx
== infoPtr
->nItemCount
) {
1600 if (endidx
== infoPtr
->nItemCount
|| endidx
== 0)
1606 item
.mask
= LVIF_TEXT
;
1609 item
.pszText
= buffer
;
1610 item
.cchTextMax
= MAX_PATH
;
1611 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) return 0;
1613 /* check for a match */
1614 if (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,infoPtr
->nSearchParamLength
) == 0) {
1617 } else if ( (charCode
!= 0) && (nItem
== -1) && (nItem
!= infoPtr
->nFocusedItem
) &&
1618 (lstrncmpiW(item
.pszText
,infoPtr
->szSearchParam
,1) == 0) ) {
1619 /* This would work but we must keep looking for a longer match */
1623 } while (idx
!= endidx
);
1626 LISTVIEW_KeySelection(infoPtr
, nItem
);
1631 /*************************************************************************
1632 * LISTVIEW_UpdateHeaderSize [Internal]
1634 * Function to resize the header control
1637 * [I] hwnd : handle to a window
1638 * [I] nNewScrollPos : scroll pos to set
1643 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO
*infoPtr
, INT nNewScrollPos
)
1648 TRACE("nNewScrollPos=%d\n", nNewScrollPos
);
1650 GetWindowRect(infoPtr
->hwndHeader
, &winRect
);
1651 point
[0].x
= winRect
.left
;
1652 point
[0].y
= winRect
.top
;
1653 point
[1].x
= winRect
.right
;
1654 point
[1].y
= winRect
.bottom
;
1656 MapWindowPoints(HWND_DESKTOP
, infoPtr
->hwndSelf
, point
, 2);
1657 point
[0].x
= -nNewScrollPos
;
1658 point
[1].x
+= nNewScrollPos
;
1660 SetWindowPos(infoPtr
->hwndHeader
,0,
1661 point
[0].x
,point
[0].y
,point
[1].x
,point
[1].y
,
1662 (infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
) ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
|
1663 SWP_NOZORDER
| SWP_NOACTIVATE
);
1668 * Update the scrollbars. This functions should be called whenever
1669 * the content, size or view changes.
1672 * [I] infoPtr : valid pointer to the listview structure
1677 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO
*infoPtr
)
1679 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1680 SCROLLINFO horzInfo
, vertInfo
;
1683 if ((infoPtr
->dwStyle
& LVS_NOSCROLL
) || !is_redrawing(infoPtr
)) return;
1685 ZeroMemory(&horzInfo
, sizeof(SCROLLINFO
));
1686 horzInfo
.cbSize
= sizeof(SCROLLINFO
);
1687 horzInfo
.nPage
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
1689 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1690 if (uView
== LVS_LIST
)
1692 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
1693 horzInfo
.nMax
= (infoPtr
->nItemCount
+ nPerCol
- 1) / nPerCol
;
1695 /* scroll by at least one column per page */
1696 if(horzInfo
.nPage
< infoPtr
->nItemWidth
)
1697 horzInfo
.nPage
= infoPtr
->nItemWidth
;
1699 horzInfo
.nPage
/= infoPtr
->nItemWidth
;
1701 else if (uView
== LVS_REPORT
)
1703 horzInfo
.nMax
= infoPtr
->nItemWidth
;
1705 else /* LVS_ICON, or LVS_SMALLICON */
1709 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) horzInfo
.nMax
= rcView
.right
- rcView
.left
;
1712 horzInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1713 horzInfo
.nMax
= max(horzInfo
.nMax
- 1, 0);
1714 dx
= GetScrollPos(infoPtr
->hwndSelf
, SB_HORZ
);
1715 dx
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
, TRUE
);
1716 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo
));
1718 /* Setting the horizontal scroll can change the listview size
1719 * (and potentially everything else) so we need to recompute
1720 * everything again for the vertical scroll
1723 ZeroMemory(&vertInfo
, sizeof(SCROLLINFO
));
1724 vertInfo
.cbSize
= sizeof(SCROLLINFO
);
1725 vertInfo
.nPage
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
1727 if (uView
== LVS_REPORT
)
1729 vertInfo
.nMax
= infoPtr
->nItemCount
;
1731 /* scroll by at least one page */
1732 if(vertInfo
.nPage
< infoPtr
->nItemHeight
)
1733 vertInfo
.nPage
= infoPtr
->nItemHeight
;
1735 if (infoPtr
->nItemHeight
> 0)
1736 vertInfo
.nPage
/= infoPtr
->nItemHeight
;
1738 else if (uView
!= LVS_LIST
) /* LVS_ICON, or LVS_SMALLICON */
1742 if (LISTVIEW_GetViewRect(infoPtr
, &rcView
)) vertInfo
.nMax
= rcView
.bottom
- rcView
.top
;
1745 vertInfo
.fMask
= SIF_RANGE
| SIF_PAGE
;
1746 vertInfo
.nMax
= max(vertInfo
.nMax
- 1, 0);
1747 dy
= GetScrollPos(infoPtr
->hwndSelf
, SB_VERT
);
1748 dy
-= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &vertInfo
, TRUE
);
1749 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo
));
1751 /* Change of the range may have changed the scroll pos. If so move the content */
1752 if (dx
!= 0 || dy
!= 0)
1755 listRect
= infoPtr
->rcList
;
1756 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &listRect
, &listRect
, 0, 0,
1757 SW_ERASE
| SW_INVALIDATE
);
1760 /* Update the Header Control */
1761 if (uView
== LVS_REPORT
)
1763 horzInfo
.fMask
= SIF_POS
;
1764 GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &horzInfo
);
1765 LISTVIEW_UpdateHeaderSize(infoPtr
, horzInfo
.nPos
);
1772 * Shows/hides the focus rectangle.
1775 * [I] infoPtr : valid pointer to the listview structure
1776 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1781 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO
*infoPtr
, BOOL fShow
)
1783 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1786 TRACE("fShow=%d, nItem=%d\n", fShow
, infoPtr
->nFocusedItem
);
1788 if (infoPtr
->nFocusedItem
< 0) return;
1790 /* we need some gymnastics in ICON mode to handle large items */
1791 if ( (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
1795 LISTVIEW_GetItemBox(infoPtr
, infoPtr
->nFocusedItem
, &rcBox
);
1796 if ((rcBox
.bottom
- rcBox
.top
) > infoPtr
->nItemHeight
)
1798 LISTVIEW_InvalidateRect(infoPtr
, &rcBox
);
1803 if (!(hdc
= GetDC(infoPtr
->hwndSelf
))) return;
1805 /* for some reason, owner draw should work only in report mode */
1806 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
1811 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
1812 HFONT hOldFont
= SelectObject(hdc
, hFont
);
1814 item
.iItem
= infoPtr
->nFocusedItem
;
1816 item
.mask
= LVIF_PARAM
;
1817 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) goto done
;
1819 ZeroMemory(&dis
, sizeof(dis
));
1820 dis
.CtlType
= ODT_LISTVIEW
;
1821 dis
.CtlID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
1822 dis
.itemID
= item
.iItem
;
1823 dis
.itemAction
= ODA_FOCUS
;
1824 if (fShow
) dis
.itemState
|= ODS_FOCUS
;
1825 dis
.hwndItem
= infoPtr
->hwndSelf
;
1827 LISTVIEW_GetItemBox(infoPtr
, dis
.itemID
, &dis
.rcItem
);
1828 dis
.itemData
= item
.lParam
;
1830 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
1832 SelectObject(hdc
, hOldFont
);
1836 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
1839 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
1843 * Invalidates all visible selected items.
1845 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO
*infoPtr
)
1849 iterator_frameditems(&i
, infoPtr
, &infoPtr
->rcList
);
1850 while(iterator_next(&i
))
1852 if (LISTVIEW_GetItemState(infoPtr
, i
.nItem
, LVIS_SELECTED
))
1853 LISTVIEW_InvalidateItem(infoPtr
, i
.nItem
);
1855 iterator_destroy(&i
);
1860 * DESCRIPTION: [INTERNAL]
1861 * Computes an item's (left,top) corner, relative to rcView.
1862 * That is, the position has NOT been made relative to the Origin.
1863 * This is deliberate, to avoid computing the Origin over, and
1864 * over again, when this function is called in a loop. Instead,
1865 * one can factor the computation of the Origin before the loop,
1866 * and offset the value returned by this function, on every iteration.
1869 * [I] infoPtr : valid pointer to the listview structure
1870 * [I] nItem : item number
1871 * [O] lpptOrig : item top, left corner
1876 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
1878 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1880 assert(nItem
>= 0 && nItem
< infoPtr
->nItemCount
);
1882 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
1884 lpptPosition
->x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
1885 lpptPosition
->y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
1887 else if (uView
== LVS_LIST
)
1889 INT nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
1890 lpptPosition
->x
= nItem
/ nCountPerColumn
* infoPtr
->nItemWidth
;
1891 lpptPosition
->y
= nItem
% nCountPerColumn
* infoPtr
->nItemHeight
;
1893 else /* LVS_REPORT */
1895 lpptPosition
->x
= 0;
1896 lpptPosition
->y
= nItem
* infoPtr
->nItemHeight
;
1901 * DESCRIPTION: [INTERNAL]
1902 * Compute the rectangles of an item. This is to localize all
1903 * the computations in one place. If you are not interested in some
1904 * of these values, simply pass in a NULL -- the function is smart
1905 * enough to compute only what's necessary. The function computes
1906 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1907 * one, the BOX rectangle. This rectangle is very cheap to compute,
1908 * and is guaranteed to contain all the other rectangles. Computing
1909 * the ICON rect is also cheap, but all the others are potentially
1910 * expensive. This gives an easy and effective optimization when
1911 * searching (like point inclusion, or rectangle intersection):
1912 * first test against the BOX, and if TRUE, test against the desired
1914 * If the function does not have all the necessary information
1915 * to computed the requested rectangles, will crash with a
1916 * failed assertion. This is done so we catch all programming
1917 * errors, given that the function is called only from our code.
1919 * We have the following 'special' meanings for a few fields:
1920 * * If LVIS_FOCUSED is set, we assume the item has the focus
1921 * This is important in ICON mode, where it might get a larger
1922 * then usual rectangle
1924 * Please note that subitem support works only in REPORT mode.
1927 * [I] infoPtr : valid pointer to the listview structure
1928 * [I] lpLVItem : item to compute the measures for
1929 * [O] lprcBox : ptr to Box rectangle
1930 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1931 * [0] lprcSelectBox : ptr to select box rectangle
1932 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1933 * [O] lprcIcon : ptr to Icon rectangle
1934 * Same as LVM_GETITEMRECT with LVIR_ICON
1935 * [O] lprcStateIcon: ptr to State Icon rectangle
1936 * [O] lprcLabel : ptr to Label rectangle
1937 * Same as LVM_GETITEMRECT with LVIR_LABEL
1942 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
,
1943 LPRECT lprcBox
, LPRECT lprcSelectBox
,
1944 LPRECT lprcIcon
, LPRECT lprcStateIcon
, LPRECT lprcLabel
)
1946 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
1947 BOOL doSelectBox
= FALSE
, doIcon
= FALSE
, doLabel
= FALSE
, oversizedBox
= FALSE
;
1948 RECT Box
, SelectBox
, Icon
, Label
;
1949 COLUMN_INFO
*lpColumnInfo
= NULL
;
1950 SIZE labelSize
= { 0, 0 };
1952 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem
, TRUE
));
1954 /* Be smart and try to figure out the minimum we have to do */
1955 if (lpLVItem
->iSubItem
) assert(uView
== LVS_REPORT
);
1956 if (uView
== LVS_ICON
&& (lprcBox
|| lprcLabel
))
1958 assert((lpLVItem
->mask
& LVIF_STATE
) && (lpLVItem
->stateMask
& LVIS_FOCUSED
));
1959 if (lpLVItem
->state
& LVIS_FOCUSED
) oversizedBox
= doLabel
= TRUE
;
1961 if (lprcSelectBox
) doSelectBox
= TRUE
;
1962 if (lprcLabel
) doLabel
= TRUE
;
1963 if (doLabel
|| lprcIcon
|| lprcStateIcon
) doIcon
= TRUE
;
1970 /************************************************************/
1971 /* compute the box rectangle (it should be cheap to do) */
1972 /************************************************************/
1973 if (lpLVItem
->iSubItem
|| uView
== LVS_REPORT
)
1974 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpLVItem
->iSubItem
);
1976 if (lpLVItem
->iSubItem
)
1978 Box
= lpColumnInfo
->rcHeader
;
1983 Box
.right
= infoPtr
->nItemWidth
;
1986 Box
.bottom
= infoPtr
->nItemHeight
;
1988 /******************************************************************/
1989 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
1990 /******************************************************************/
1993 LONG state_width
= 0;
1995 if (infoPtr
->himlState
&& lpLVItem
->iSubItem
== 0)
1996 state_width
= infoPtr
->iconStateSize
.cx
;
1998 if (uView
== LVS_ICON
)
2000 Icon
.left
= Box
.left
+ state_width
;
2001 if (infoPtr
->himlNormal
)
2002 Icon
.left
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
- state_width
) / 2;
2003 Icon
.top
= Box
.top
+ ICON_TOP_PADDING
;
2004 Icon
.right
= Icon
.left
;
2005 Icon
.bottom
= Icon
.top
;
2006 if (infoPtr
->himlNormal
)
2008 Icon
.right
+= infoPtr
->iconSize
.cx
;
2009 Icon
.bottom
+= infoPtr
->iconSize
.cy
;
2012 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2014 Icon
.left
= Box
.left
+ state_width
;
2016 if (uView
== LVS_REPORT
)
2017 Icon
.left
+= REPORT_MARGINX
;
2020 Icon
.right
= Icon
.left
;
2021 if (infoPtr
->himlSmall
&&
2022 (!lpColumnInfo
|| lpLVItem
->iSubItem
== 0 || (lpColumnInfo
->fmt
& LVCFMT_IMAGE
) ||
2023 ((infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
) && lpLVItem
->iImage
!= I_IMAGECALLBACK
)))
2024 Icon
.right
+= infoPtr
->iconSize
.cx
;
2025 Icon
.bottom
= Icon
.top
+ infoPtr
->iconSize
.cy
;
2027 if(lprcIcon
) *lprcIcon
= Icon
;
2028 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon
));
2030 /* TODO: is this correct? */
2033 lprcStateIcon
->left
= Icon
.left
- state_width
;
2034 lprcStateIcon
->right
= Icon
.left
;
2035 lprcStateIcon
->top
= Icon
.top
;
2036 lprcStateIcon
->bottom
= lprcStateIcon
->top
+ infoPtr
->iconSize
.cy
;
2037 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon
));
2040 else Icon
.right
= 0;
2042 /************************************************************/
2043 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2044 /************************************************************/
2047 /* calculate how far to the right can the label stretch */
2048 Label
.right
= Box
.right
;
2049 if (uView
== LVS_REPORT
)
2051 if (lpLVItem
->iSubItem
== 0) Label
= lpColumnInfo
->rcHeader
;
2054 if (lpLVItem
->iSubItem
|| ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && uView
== LVS_REPORT
))
2056 labelSize
.cx
= infoPtr
->nItemWidth
;
2057 labelSize
.cy
= infoPtr
->nItemHeight
;
2061 /* we need the text in non owner draw mode */
2062 assert(lpLVItem
->mask
& LVIF_TEXT
);
2063 if (is_textT(lpLVItem
->pszText
, TRUE
))
2065 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2066 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2067 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2071 /* compute rough rectangle where the label will go */
2072 SetRectEmpty(&rcText
);
2073 rcText
.right
= infoPtr
->nItemWidth
- TRAILING_LABEL_PADDING
;
2074 rcText
.bottom
= infoPtr
->nItemHeight
;
2075 if (uView
== LVS_ICON
)
2076 rcText
.bottom
-= ICON_TOP_PADDING
+ infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2078 /* now figure out the flags */
2079 if (uView
== LVS_ICON
)
2080 uFormat
= oversizedBox
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
;
2082 uFormat
= LV_SL_DT_FLAGS
;
2084 DrawTextW (hdc
, lpLVItem
->pszText
, -1, &rcText
, uFormat
| DT_CALCRECT
);
2086 labelSize
.cx
= min(rcText
.right
- rcText
.left
+ TRAILING_LABEL_PADDING
, infoPtr
->nItemWidth
);
2087 labelSize
.cy
= rcText
.bottom
- rcText
.top
;
2089 SelectObject(hdc
, hOldFont
);
2090 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2094 if (uView
== LVS_ICON
)
2096 Label
.left
= Box
.left
+ (infoPtr
->nItemWidth
- labelSize
.cx
) / 2;
2097 Label
.top
= Box
.top
+ ICON_TOP_PADDING_HITABLE
+
2098 infoPtr
->iconSize
.cy
+ ICON_BOTTOM_PADDING
;
2099 Label
.right
= Label
.left
+ labelSize
.cx
;
2100 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2101 if (!oversizedBox
&& labelSize
.cy
> infoPtr
->ntmHeight
)
2103 labelSize
.cy
= min(Box
.bottom
- Label
.top
, labelSize
.cy
);
2104 labelSize
.cy
/= infoPtr
->ntmHeight
;
2105 labelSize
.cy
= max(labelSize
.cy
, 1);
2106 labelSize
.cy
*= infoPtr
->ntmHeight
;
2108 Label
.bottom
= Label
.top
+ labelSize
.cy
+ HEIGHT_PADDING
;
2110 else if (uView
== LVS_REPORT
)
2112 Label
.left
= Icon
.right
;
2113 Label
.top
= Box
.top
;
2114 Label
.right
= lpColumnInfo
->rcHeader
.right
;
2115 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2117 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2119 Label
.left
= Icon
.right
;
2120 Label
.top
= Box
.top
;
2121 Label
.right
= min(Label
.left
+ labelSize
.cx
, Label
.right
);
2122 Label
.bottom
= Label
.top
+ infoPtr
->nItemHeight
;
2125 if (lprcLabel
) *lprcLabel
= Label
;
2126 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label
));
2129 /************************************************************/
2130 /* compute STATEICON bounding box */
2131 /************************************************************/
2134 if (uView
== LVS_REPORT
)
2136 SelectBox
.left
= Icon
.right
; /* FIXME: should be Icon.left */
2137 SelectBox
.top
= Box
.top
;
2138 SelectBox
.bottom
= Box
.bottom
;
2139 if (lpLVItem
->iSubItem
== 0)
2141 /* we need the indent in report mode */
2142 assert(lpLVItem
->mask
& LVIF_INDENT
);
2143 SelectBox
.left
+= infoPtr
->iconSize
.cx
* lpLVItem
->iIndent
;
2145 SelectBox
.right
= min(SelectBox
.left
+ labelSize
.cx
, Label
.right
);
2149 UnionRect(&SelectBox
, &Icon
, &Label
);
2151 if (lprcSelectBox
) *lprcSelectBox
= SelectBox
;
2152 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox
));
2155 /* Fix the Box if necessary */
2158 if (oversizedBox
) UnionRect(lprcBox
, &Box
, &Label
);
2159 else *lprcBox
= Box
;
2161 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box
));
2165 * DESCRIPTION: [INTERNAL]
2168 * [I] infoPtr : valid pointer to the listview structure
2169 * [I] nItem : item number
2170 * [O] lprcBox : ptr to Box rectangle
2175 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprcBox
)
2177 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2178 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
2179 POINT Position
, Origin
;
2182 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
2183 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
2185 /* Be smart and try to figure out the minimum we have to do */
2187 if (uView
== LVS_ICON
&& infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
2188 lvItem
.mask
|= LVIF_TEXT
;
2189 lvItem
.iItem
= nItem
;
2190 lvItem
.iSubItem
= 0;
2191 lvItem
.pszText
= szDispText
;
2192 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
2193 if (lvItem
.mask
) LISTVIEW_GetItemW(infoPtr
, &lvItem
);
2194 if (uView
== LVS_ICON
)
2196 lvItem
.mask
|= LVIF_STATE
;
2197 lvItem
.stateMask
= LVIS_FOCUSED
;
2198 lvItem
.state
= (lvItem
.mask
& LVIF_TEXT
? LVIS_FOCUSED
: 0);
2200 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprcBox
, 0, 0, 0, 0);
2202 OffsetRect(lprcBox
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
2208 * Returns the current icon position, and advances it along the top.
2209 * The returned position is not offset by Origin.
2212 * [I] infoPtr : valid pointer to the listview structure
2213 * [O] lpPos : will get the current icon position
2218 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2220 INT nListWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
2222 *lpPos
= infoPtr
->currIconPos
;
2224 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2225 if (infoPtr
->currIconPos
.x
+ infoPtr
->nItemWidth
<= nListWidth
) return;
2227 infoPtr
->currIconPos
.x
= 0;
2228 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2234 * Returns the current icon position, and advances it down the left edge.
2235 * The returned position is not offset by Origin.
2238 * [I] infoPtr : valid pointer to the listview structure
2239 * [O] lpPos : will get the current icon position
2244 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO
*infoPtr
, LPPOINT lpPos
)
2246 INT nListHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
2248 *lpPos
= infoPtr
->currIconPos
;
2250 infoPtr
->currIconPos
.y
+= infoPtr
->nItemHeight
;
2251 if (infoPtr
->currIconPos
.y
+ infoPtr
->nItemHeight
<= nListHeight
) return;
2253 infoPtr
->currIconPos
.x
+= infoPtr
->nItemWidth
;
2254 infoPtr
->currIconPos
.y
= 0;
2260 * Moves an icon to the specified position.
2261 * It takes care of invalidating the item, etc.
2264 * [I] infoPtr : valid pointer to the listview structure
2265 * [I] nItem : the item to move
2266 * [I] lpPos : the new icon position
2267 * [I] isNew : flags the item as being new
2273 static BOOL
LISTVIEW_MoveIconTo(const LISTVIEW_INFO
*infoPtr
, INT nItem
, const POINT
*lppt
, BOOL isNew
)
2279 old
.x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, nItem
);
2280 old
.y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, nItem
);
2282 if (lppt
->x
== old
.x
&& lppt
->y
== old
.y
) return TRUE
;
2283 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2286 /* Allocating a POINTER for every item is too resource intensive,
2287 * so we'll keep the (x,y) in different arrays */
2288 if (!DPA_SetPtr(infoPtr
->hdpaPosX
, nItem
, (void *)(LONG_PTR
)lppt
->x
)) return FALSE
;
2289 if (!DPA_SetPtr(infoPtr
->hdpaPosY
, nItem
, (void *)(LONG_PTR
)lppt
->y
)) return FALSE
;
2291 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
2298 * Arranges listview items in icon display mode.
2301 * [I] infoPtr : valid pointer to the listview structure
2302 * [I] nAlignCode : alignment code
2308 static BOOL
LISTVIEW_Arrange(LISTVIEW_INFO
*infoPtr
, INT nAlignCode
)
2310 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2311 void (*next_pos
)(LISTVIEW_INFO
*, LPPOINT
);
2315 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
) return FALSE
;
2317 TRACE("nAlignCode=%d\n", nAlignCode
);
2319 if (nAlignCode
== LVA_DEFAULT
)
2321 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
) nAlignCode
= LVA_ALIGNLEFT
;
2322 else nAlignCode
= LVA_ALIGNTOP
;
2327 case LVA_ALIGNLEFT
: next_pos
= LISTVIEW_NextIconPosLeft
; break;
2328 case LVA_ALIGNTOP
: next_pos
= LISTVIEW_NextIconPosTop
; break;
2329 case LVA_SNAPTOGRID
: next_pos
= LISTVIEW_NextIconPosTop
; break; /* FIXME */
2330 default: return FALSE
;
2333 infoPtr
->bAutoarrange
= TRUE
;
2334 infoPtr
->currIconPos
.x
= infoPtr
->currIconPos
.y
= 0;
2335 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2337 next_pos(infoPtr
, &pos
);
2338 LISTVIEW_MoveIconTo(infoPtr
, i
, &pos
, FALSE
);
2346 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2349 * [I] infoPtr : valid pointer to the listview structure
2350 * [O] lprcView : bounding rectangle
2356 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2360 SetRectEmpty(lprcView
);
2362 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
2366 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2368 x
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosX
, i
);
2369 y
= (LONG_PTR
)DPA_GetPtr(infoPtr
->hdpaPosY
, i
);
2370 lprcView
->right
= max(lprcView
->right
, x
);
2371 lprcView
->bottom
= max(lprcView
->bottom
, y
);
2373 if (infoPtr
->nItemCount
> 0)
2375 lprcView
->right
+= infoPtr
->nItemWidth
;
2376 lprcView
->bottom
+= infoPtr
->nItemHeight
;
2381 y
= LISTVIEW_GetCountPerColumn(infoPtr
);
2382 x
= infoPtr
->nItemCount
/ y
;
2383 if (infoPtr
->nItemCount
% y
) x
++;
2384 lprcView
->right
= x
* infoPtr
->nItemWidth
;
2385 lprcView
->bottom
= y
* infoPtr
->nItemHeight
;
2389 lprcView
->right
= infoPtr
->nItemWidth
;
2390 lprcView
->bottom
= infoPtr
->nItemCount
* infoPtr
->nItemHeight
;
2397 * Retrieves the bounding rectangle of all the items.
2400 * [I] infoPtr : valid pointer to the listview structure
2401 * [O] lprcView : bounding rectangle
2407 static BOOL
LISTVIEW_GetViewRect(const LISTVIEW_INFO
*infoPtr
, LPRECT lprcView
)
2411 TRACE("(lprcView=%p)\n", lprcView
);
2413 if (!lprcView
) return FALSE
;
2415 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
2416 LISTVIEW_GetAreaRect(infoPtr
, lprcView
);
2417 OffsetRect(lprcView
, ptOrigin
.x
, ptOrigin
.y
);
2419 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView
));
2426 * Retrieves the subitem pointer associated with the subitem index.
2429 * [I] hdpaSubItems : DPA handle for a specific item
2430 * [I] nSubItem : index of subitem
2433 * SUCCESS : subitem pointer
2436 static SUBITEM_INFO
* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems
, INT nSubItem
)
2438 SUBITEM_INFO
*lpSubItem
;
2441 /* we should binary search here if need be */
2442 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
2444 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
2445 if (lpSubItem
->iSubItem
== nSubItem
)
2455 * Calculates the desired item width.
2458 * [I] infoPtr : valid pointer to the listview structure
2461 * The desired item width.
2463 static INT
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO
*infoPtr
)
2465 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2468 TRACE("uView=%d\n", uView
);
2470 if (uView
== LVS_ICON
)
2471 nItemWidth
= infoPtr
->iconSpacing
.cx
;
2472 else if (uView
== LVS_REPORT
)
2476 if (DPA_GetPtrCount(infoPtr
->hdpaColumns
) > 0)
2478 LISTVIEW_GetHeaderRect(infoPtr
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1, &rcHeader
);
2479 nItemWidth
= rcHeader
.right
;
2482 else /* LVS_SMALLICON, or LVS_LIST */
2486 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2487 nItemWidth
= max(LISTVIEW_GetLabelWidth(infoPtr
, i
), nItemWidth
);
2489 if (infoPtr
->himlSmall
) nItemWidth
+= infoPtr
->iconSize
.cx
;
2490 if (infoPtr
->himlState
) nItemWidth
+= infoPtr
->iconStateSize
.cx
;
2492 nItemWidth
= max(DEFAULT_COLUMN_WIDTH
, nItemWidth
+ WIDTH_PADDING
);
2495 return max(nItemWidth
, 1);
2500 * Calculates the desired item height.
2503 * [I] infoPtr : valid pointer to the listview structure
2506 * The desired item height.
2508 static INT
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO
*infoPtr
)
2510 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
2513 TRACE("uView=%d\n", uView
);
2515 if (uView
== LVS_ICON
)
2516 nItemHeight
= infoPtr
->iconSpacing
.cy
;
2519 nItemHeight
= infoPtr
->ntmHeight
;
2520 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
2522 if (infoPtr
->himlState
)
2523 nItemHeight
= max(nItemHeight
, infoPtr
->iconStateSize
.cy
);
2524 if (infoPtr
->himlSmall
)
2525 nItemHeight
= max(nItemHeight
, infoPtr
->iconSize
.cy
);
2526 if (infoPtr
->himlState
|| infoPtr
->himlSmall
)
2527 nItemHeight
+= HEIGHT_PADDING
;
2528 if (infoPtr
->nMeasureItemHeight
> 0)
2529 nItemHeight
= infoPtr
->nMeasureItemHeight
;
2532 return max(nItemHeight
, 1);
2537 * Updates the width, and height of an item.
2540 * [I] infoPtr : valid pointer to the listview structure
2545 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO
*infoPtr
)
2547 infoPtr
->nItemWidth
= LISTVIEW_CalculateItemWidth(infoPtr
);
2548 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
2554 * Retrieves and saves important text metrics info for the current
2558 * [I] infoPtr : valid pointer to the listview structure
2561 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO
*infoPtr
)
2563 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
2564 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
2565 HFONT hOldFont
= SelectObject(hdc
, hFont
);
2569 if (GetTextMetricsW(hdc
, &tm
))
2571 infoPtr
->ntmHeight
= tm
.tmHeight
;
2572 infoPtr
->ntmMaxCharWidth
= tm
.tmMaxCharWidth
;
2575 if (GetTextExtentPoint32A(hdc
, "...", 3, &sz
))
2576 infoPtr
->nEllipsisWidth
= sz
.cx
;
2578 SelectObject(hdc
, hOldFont
);
2579 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
2581 TRACE("tmHeight=%d\n", infoPtr
->ntmHeight
);
2586 * A compare function for ranges
2589 * [I] range1 : pointer to range 1;
2590 * [I] range2 : pointer to range 2;
2594 * > 0 : if range 1 > range 2
2595 * < 0 : if range 2 > range 1
2596 * = 0 : if range intersects range 2
2598 static INT CALLBACK
ranges_cmp(LPVOID range1
, LPVOID range2
, LPARAM flags
)
2602 if (((RANGE
*)range1
)->upper
<= ((RANGE
*)range2
)->lower
)
2604 else if (((RANGE
*)range2
)->upper
<= ((RANGE
*)range1
)->lower
)
2609 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1
), debugrange(range2
), cmp
);
2615 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2617 #define ranges_check(ranges, desc) do { } while(0)
2620 static void ranges_assert(RANGES ranges
, LPCSTR desc
, const char *func
, int line
)
2625 TRACE("*** Checking %s:%d:%s ***\n", func
, line
, desc
);
2627 assert (DPA_GetPtrCount(ranges
->hdpa
) >= 0);
2628 ranges_dump(ranges
);
2629 prev
= DPA_GetPtr(ranges
->hdpa
, 0);
2630 if (DPA_GetPtrCount(ranges
->hdpa
) > 0)
2631 assert (prev
->lower
>= 0 && prev
->lower
< prev
->upper
);
2632 for (i
= 1; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2634 curr
= DPA_GetPtr(ranges
->hdpa
, i
);
2635 assert (prev
->upper
<= curr
->lower
);
2636 assert (curr
->lower
< curr
->upper
);
2639 TRACE("--- Done checking---\n");
2642 static RANGES
ranges_create(int count
)
2644 RANGES ranges
= Alloc(sizeof(struct tagRANGES
));
2645 if (!ranges
) return NULL
;
2646 ranges
->hdpa
= DPA_Create(count
);
2647 if (ranges
->hdpa
) return ranges
;
2652 static void ranges_clear(RANGES ranges
)
2656 for(i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2657 Free(DPA_GetPtr(ranges
->hdpa
, i
));
2658 DPA_DeleteAllPtrs(ranges
->hdpa
);
2662 static void ranges_destroy(RANGES ranges
)
2664 if (!ranges
) return;
2665 ranges_clear(ranges
);
2666 DPA_Destroy(ranges
->hdpa
);
2670 static RANGES
ranges_clone(RANGES ranges
)
2675 if (!(clone
= ranges_create(DPA_GetPtrCount(ranges
->hdpa
)))) goto fail
;
2677 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2679 RANGE
*newrng
= Alloc(sizeof(RANGE
));
2680 if (!newrng
) goto fail
;
2681 *newrng
= *((RANGE
*)DPA_GetPtr(ranges
->hdpa
, i
));
2682 DPA_SetPtr(clone
->hdpa
, i
, newrng
);
2687 TRACE ("clone failed\n");
2688 ranges_destroy(clone
);
2692 static RANGES
ranges_diff(RANGES ranges
, RANGES sub
)
2696 for (i
= 0; i
< DPA_GetPtrCount(sub
->hdpa
); i
++)
2697 ranges_del(ranges
, *((RANGE
*)DPA_GetPtr(sub
->hdpa
, i
)));
2702 static void ranges_dump(RANGES ranges
)
2706 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2707 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges
->hdpa
, i
)));
2710 static inline BOOL
ranges_contain(RANGES ranges
, INT nItem
)
2712 RANGE srchrng
= { nItem
, nItem
+ 1 };
2714 TRACE("(nItem=%d)\n", nItem
);
2715 ranges_check(ranges
, "before contain");
2716 return DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
) != -1;
2719 static INT
ranges_itemcount(RANGES ranges
)
2723 for (i
= 0; i
< DPA_GetPtrCount(ranges
->hdpa
); i
++)
2725 RANGE
*sel
= DPA_GetPtr(ranges
->hdpa
, i
);
2726 count
+= sel
->upper
- sel
->lower
;
2732 static BOOL
ranges_shift(RANGES ranges
, INT nItem
, INT delta
, INT nUpper
)
2734 RANGE srchrng
= { nItem
, nItem
+ 1 }, *chkrng
;
2737 index
= DPA_Search(ranges
->hdpa
, &srchrng
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2738 if (index
== -1) return TRUE
;
2740 for (; index
< DPA_GetPtrCount(ranges
->hdpa
); index
++)
2742 chkrng
= DPA_GetPtr(ranges
->hdpa
, index
);
2743 if (chkrng
->lower
>= nItem
)
2744 chkrng
->lower
= max(min(chkrng
->lower
+ delta
, nUpper
- 1), 0);
2745 if (chkrng
->upper
> nItem
)
2746 chkrng
->upper
= max(min(chkrng
->upper
+ delta
, nUpper
), 0);
2751 static BOOL
ranges_add(RANGES ranges
, RANGE range
)
2756 TRACE("(%s)\n", debugrange(&range
));
2757 ranges_check(ranges
, "before add");
2759 /* try find overlapping regions first */
2760 srchrgn
.lower
= range
.lower
- 1;
2761 srchrgn
.upper
= range
.upper
+ 1;
2762 index
= DPA_Search(ranges
->hdpa
, &srchrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
);
2768 TRACE("Adding new range\n");
2770 /* create the brand new range to insert */
2771 newrgn
= Alloc(sizeof(RANGE
));
2772 if(!newrgn
) goto fail
;
2775 /* figure out where to insert it */
2776 index
= DPA_Search(ranges
->hdpa
, newrgn
, 0, ranges_cmp
, 0, DPAS_SORTED
| DPAS_INSERTAFTER
);
2777 TRACE("index=%d\n", index
);
2778 if (index
== -1) index
= 0;
2780 /* and get it over with */
2781 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2789 RANGE
*chkrgn
, *mrgrgn
;
2790 INT fromindex
, mergeindex
;
2792 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2793 TRACE("Merge with %s @%d\n", debugrange(chkrgn
), index
);
2795 chkrgn
->lower
= min(range
.lower
, chkrgn
->lower
);
2796 chkrgn
->upper
= max(range
.upper
, chkrgn
->upper
);
2798 TRACE("New range %s @%d\n", debugrange(chkrgn
), index
);
2800 /* merge now common ranges */
2802 srchrgn
.lower
= chkrgn
->lower
- 1;
2803 srchrgn
.upper
= chkrgn
->upper
+ 1;
2807 mergeindex
= DPA_Search(ranges
->hdpa
, &srchrgn
, fromindex
, ranges_cmp
, 0, 0);
2808 if (mergeindex
== -1) break;
2809 if (mergeindex
== index
)
2811 fromindex
= index
+ 1;
2815 TRACE("Merge with index %i\n", mergeindex
);
2817 mrgrgn
= DPA_GetPtr(ranges
->hdpa
, mergeindex
);
2818 chkrgn
->lower
= min(chkrgn
->lower
, mrgrgn
->lower
);
2819 chkrgn
->upper
= max(chkrgn
->upper
, mrgrgn
->upper
);
2821 DPA_DeletePtr(ranges
->hdpa
, mergeindex
);
2822 if (mergeindex
< index
) index
--;
2826 ranges_check(ranges
, "after add");
2830 ranges_check(ranges
, "failed add");
2834 static BOOL
ranges_del(RANGES ranges
, RANGE range
)
2839 TRACE("(%s)\n", debugrange(&range
));
2840 ranges_check(ranges
, "before del");
2842 /* we don't use DPAS_SORTED here, since we need *
2843 * to find the first overlapping range */
2844 index
= DPA_Search(ranges
->hdpa
, &range
, 0, ranges_cmp
, 0, 0);
2847 chkrgn
= DPA_GetPtr(ranges
->hdpa
, index
);
2849 TRACE("Matches range %s @%d\n", debugrange(chkrgn
), index
);
2851 /* case 1: Same range */
2852 if ( (chkrgn
->upper
== range
.upper
) &&
2853 (chkrgn
->lower
== range
.lower
) )
2855 DPA_DeletePtr(ranges
->hdpa
, index
);
2858 /* case 2: engulf */
2859 else if ( (chkrgn
->upper
<= range
.upper
) &&
2860 (chkrgn
->lower
>= range
.lower
) )
2862 DPA_DeletePtr(ranges
->hdpa
, index
);
2864 /* case 3: overlap upper */
2865 else if ( (chkrgn
->upper
<= range
.upper
) &&
2866 (chkrgn
->lower
< range
.lower
) )
2868 chkrgn
->upper
= range
.lower
;
2870 /* case 4: overlap lower */
2871 else if ( (chkrgn
->upper
> range
.upper
) &&
2872 (chkrgn
->lower
>= range
.lower
) )
2874 chkrgn
->lower
= range
.upper
;
2877 /* case 5: fully internal */
2880 RANGE tmprgn
= *chkrgn
, *newrgn
;
2882 if (!(newrgn
= Alloc(sizeof(RANGE
)))) goto fail
;
2883 newrgn
->lower
= chkrgn
->lower
;
2884 newrgn
->upper
= range
.lower
;
2885 chkrgn
->lower
= range
.upper
;
2886 if (DPA_InsertPtr(ranges
->hdpa
, index
, newrgn
) == -1)
2895 index
= DPA_Search(ranges
->hdpa
, &range
, index
, ranges_cmp
, 0, 0);
2898 ranges_check(ranges
, "after del");
2902 ranges_check(ranges
, "failed del");
2908 * Removes all selection ranges
2911 * [I] infoPtr : valid pointer to the listview structure
2912 * [I] toSkip : item range to skip removing the selection
2918 static BOOL
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO
*infoPtr
, RANGES toSkip
)
2927 lvItem
.stateMask
= LVIS_SELECTED
;
2929 /* need to clone the DPA because callbacks can change it */
2930 if (!(clone
= ranges_clone(infoPtr
->selectionRanges
))) return FALSE
;
2931 iterator_rangesitems(&i
, ranges_diff(clone
, toSkip
));
2932 while(iterator_next(&i
))
2933 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &lvItem
);
2934 /* note that the iterator destructor will free the cloned range */
2935 iterator_destroy(&i
);
2940 static inline BOOL
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
2944 if (!(toSkip
= ranges_create(1))) return FALSE
;
2945 if (nItem
!= -1) ranges_additem(toSkip
, nItem
);
2946 LISTVIEW_DeselectAllSkipItems(infoPtr
, toSkip
);
2947 ranges_destroy(toSkip
);
2951 static inline BOOL
LISTVIEW_DeselectAll(LISTVIEW_INFO
*infoPtr
)
2953 return LISTVIEW_DeselectAllSkipItem(infoPtr
, -1);
2958 * Retrieves the number of items that are marked as selected.
2961 * [I] infoPtr : valid pointer to the listview structure
2964 * Number of items selected.
2966 static INT
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO
*infoPtr
)
2968 INT nSelectedCount
= 0;
2970 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
2973 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
2975 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
2980 nSelectedCount
= ranges_itemcount(infoPtr
->selectionRanges
);
2982 TRACE("nSelectedCount=%d\n", nSelectedCount
);
2983 return nSelectedCount
;
2988 * Manages the item focus.
2991 * [I] infoPtr : valid pointer to the listview structure
2992 * [I] nItem : item index
2995 * TRUE : focused item changed
2996 * FALSE : focused item has NOT changed
2998 static inline BOOL
LISTVIEW_SetItemFocus(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3000 INT oldFocus
= infoPtr
->nFocusedItem
;
3003 if (nItem
== infoPtr
->nFocusedItem
) return FALSE
;
3005 lvItem
.state
= nItem
== -1 ? 0 : LVIS_FOCUSED
;
3006 lvItem
.stateMask
= LVIS_FOCUSED
;
3007 LISTVIEW_SetItemState(infoPtr
, nItem
== -1 ? infoPtr
->nFocusedItem
: nItem
, &lvItem
);
3009 return oldFocus
!= infoPtr
->nFocusedItem
;
3012 /* Helper function for LISTVIEW_ShiftIndices *only* */
3013 static INT
shift_item(const LISTVIEW_INFO
*infoPtr
, INT nShiftItem
, INT nItem
, INT direction
)
3015 if (nShiftItem
< nItem
) return nShiftItem
;
3017 if (nShiftItem
> nItem
) return nShiftItem
+ direction
;
3019 if (direction
> 0) return nShiftItem
+ direction
;
3021 return min(nShiftItem
, infoPtr
->nItemCount
- 1);
3026 * Updates the various indices after an item has been inserted or deleted.
3029 * [I] infoPtr : valid pointer to the listview structure
3030 * [I] nItem : item index
3031 * [I] direction : Direction of shift, +1 or -1.
3036 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT direction
)
3041 /* temporarily disable change notification while shifting items */
3042 bOldChange
= infoPtr
->bDoChangeNotify
;
3043 infoPtr
->bDoChangeNotify
= FALSE
;
3045 TRACE("Shifting %iu, %i steps\n", nItem
, direction
);
3047 ranges_shift(infoPtr
->selectionRanges
, nItem
, direction
, infoPtr
->nItemCount
);
3049 assert(abs(direction
) == 1);
3051 infoPtr
->nSelectionMark
= shift_item(infoPtr
, infoPtr
->nSelectionMark
, nItem
, direction
);
3053 nNewFocus
= shift_item(infoPtr
, infoPtr
->nFocusedItem
, nItem
, direction
);
3054 if (nNewFocus
!= infoPtr
->nFocusedItem
)
3055 LISTVIEW_SetItemFocus(infoPtr
, nNewFocus
);
3057 /* But we are not supposed to modify nHotItem! */
3059 infoPtr
->bDoChangeNotify
= bOldChange
;
3065 * Adds a block of selections.
3068 * [I] infoPtr : valid pointer to the listview structure
3069 * [I] nItem : item index
3072 * Whether the window is still valid.
3074 static BOOL
LISTVIEW_AddGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3076 INT nFirst
= min(infoPtr
->nSelectionMark
, nItem
);
3077 INT nLast
= max(infoPtr
->nSelectionMark
, nItem
);
3078 HWND hwndSelf
= infoPtr
->hwndSelf
;
3079 NMLVODSTATECHANGE nmlv
;
3084 /* Temporarily disable change notification
3085 * If the control is LVS_OWNERDATA, we need to send
3086 * only one LVN_ODSTATECHANGED notification.
3087 * See MSDN documentation for LVN_ITEMCHANGED.
3089 bOldChange
= infoPtr
->bDoChangeNotify
;
3090 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) infoPtr
->bDoChangeNotify
= FALSE
;
3092 if (nFirst
== -1) nFirst
= nItem
;
3094 item
.state
= LVIS_SELECTED
;
3095 item
.stateMask
= LVIS_SELECTED
;
3097 for (i
= nFirst
; i
<= nLast
; i
++)
3098 LISTVIEW_SetItemState(infoPtr
,i
,&item
);
3100 ZeroMemory(&nmlv
, sizeof(nmlv
));
3101 nmlv
.iFrom
= nFirst
;
3104 nmlv
.uOldState
= item
.state
;
3106 notify_hdr(infoPtr
, LVN_ODSTATECHANGED
, (LPNMHDR
)&nmlv
);
3107 if (!IsWindow(hwndSelf
))
3109 infoPtr
->bDoChangeNotify
= bOldChange
;
3116 * Sets a single group selection.
3119 * [I] infoPtr : valid pointer to the listview structure
3120 * [I] nItem : item index
3125 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3127 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3133 if (!(selection
= ranges_create(100))) return;
3135 item
.state
= LVIS_SELECTED
;
3136 item
.stateMask
= LVIS_SELECTED
;
3138 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
3140 if (infoPtr
->nSelectionMark
== -1)
3142 infoPtr
->nSelectionMark
= nItem
;
3143 ranges_additem(selection
, nItem
);
3149 sel
.lower
= min(infoPtr
->nSelectionMark
, nItem
);
3150 sel
.upper
= max(infoPtr
->nSelectionMark
, nItem
) + 1;
3151 ranges_add(selection
, sel
);
3156 RECT rcItem
, rcSel
, rcSelMark
;
3159 rcItem
.left
= LVIR_BOUNDS
;
3160 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return;
3161 rcSelMark
.left
= LVIR_BOUNDS
;
3162 if (!LISTVIEW_GetItemRect(infoPtr
, infoPtr
->nSelectionMark
, &rcSelMark
)) return;
3163 UnionRect(&rcSel
, &rcItem
, &rcSelMark
);
3164 iterator_frameditems(&i
, infoPtr
, &rcSel
);
3165 while(iterator_next(&i
))
3167 LISTVIEW_GetItemPosition(infoPtr
, i
.nItem
, &ptItem
);
3168 if (PtInRect(&rcSel
, ptItem
)) ranges_additem(selection
, i
.nItem
);
3170 iterator_destroy(&i
);
3173 bOldChange
= infoPtr
->bDoChangeNotify
;
3174 infoPtr
->bDoChangeNotify
= FALSE
;
3176 LISTVIEW_DeselectAllSkipItems(infoPtr
, selection
);
3179 iterator_rangesitems(&i
, selection
);
3180 while(iterator_next(&i
))
3181 LISTVIEW_SetItemState(infoPtr
, i
.nItem
, &item
);
3182 /* this will also destroy the selection */
3183 iterator_destroy(&i
);
3185 infoPtr
->bDoChangeNotify
= bOldChange
;
3187 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3192 * Sets a single selection.
3195 * [I] infoPtr : valid pointer to the listview structure
3196 * [I] nItem : item index
3201 static void LISTVIEW_SetSelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3205 TRACE("nItem=%d\n", nItem
);
3207 LISTVIEW_DeselectAllSkipItem(infoPtr
, nItem
);
3209 lvItem
.state
= LVIS_FOCUSED
| LVIS_SELECTED
;
3210 lvItem
.stateMask
= LVIS_FOCUSED
| LVIS_SELECTED
;
3211 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3213 infoPtr
->nSelectionMark
= nItem
;
3218 * Set selection(s) with keyboard.
3221 * [I] infoPtr : valid pointer to the listview structure
3222 * [I] nItem : item index
3225 * SUCCESS : TRUE (needs to be repainted)
3226 * FAILURE : FALSE (nothing has changed)
3228 static BOOL
LISTVIEW_KeySelection(LISTVIEW_INFO
*infoPtr
, INT nItem
)
3230 /* FIXME: pass in the state */
3231 WORD wShift
= HIWORD(GetKeyState(VK_SHIFT
));
3232 WORD wCtrl
= HIWORD(GetKeyState(VK_CONTROL
));
3233 BOOL bResult
= FALSE
;
3235 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem
, wShift
, wCtrl
);
3236 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
3238 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
3241 LISTVIEW_SetSelection(infoPtr
, nItem
);
3248 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
3253 lvItem
.state
= ~LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3254 lvItem
.stateMask
= LVIS_SELECTED
;
3255 LISTVIEW_SetItemState(infoPtr
, nItem
, &lvItem
);
3257 if (lvItem
.state
& LVIS_SELECTED
)
3258 infoPtr
->nSelectionMark
= nItem
;
3260 bResult
= LISTVIEW_SetItemFocus(infoPtr
, nItem
);
3265 LISTVIEW_SetSelection(infoPtr
, nItem
);
3268 LISTVIEW_EnsureVisible(infoPtr
, nItem
, FALSE
);
3271 UpdateWindow(infoPtr
->hwndSelf
); /* update client area */
3275 static BOOL
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, POINT pt
)
3277 LVHITTESTINFO lvHitTestInfo
;
3279 ZeroMemory(&lvHitTestInfo
, sizeof(lvHitTestInfo
));
3280 lvHitTestInfo
.pt
.x
= pt
.x
;
3281 lvHitTestInfo
.pt
.y
= pt
.y
;
3283 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
3285 lpLVItem
->mask
= LVIF_PARAM
;
3286 lpLVItem
->iItem
= lvHitTestInfo
.iItem
;
3287 lpLVItem
->iSubItem
= 0;
3289 return LISTVIEW_GetItemT(infoPtr
, lpLVItem
, TRUE
);
3292 static inline BOOL
LISTVIEW_isHotTracking(const LISTVIEW_INFO
*infoPtr
)
3294 return ((infoPtr
->dwLvExStyle
& LVS_EX_TRACKSELECT
) ||
3295 (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
) ||
3296 (infoPtr
->dwLvExStyle
& LVS_EX_TWOCLICKACTIVATE
));
3301 * Called when the mouse is being actively tracked and has hovered for a specified
3305 * [I] infoPtr : valid pointer to the listview structure
3306 * [I] fwKeys : key indicator
3307 * [I] x,y : mouse position
3310 * 0 if the message was processed, non-zero if there was an error
3313 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3314 * over the item for a certain period of time.
3317 static LRESULT
LISTVIEW_MouseHover(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3319 if (LISTVIEW_isHotTracking(infoPtr
))
3327 if (LISTVIEW_GetItemAtPt(infoPtr
, &item
, pt
))
3328 LISTVIEW_SetSelection(infoPtr
, item
.iItem
);
3336 * Called whenever WM_MOUSEMOVE is received.
3339 * [I] infoPtr : valid pointer to the listview structure
3340 * [I] fwKeys : key indicator
3341 * [I] x,y : mouse position
3344 * 0 if the message is processed, non-zero if there was an error
3346 static LRESULT
LISTVIEW_MouseMove(LISTVIEW_INFO
*infoPtr
, WORD fwKeys
, INT x
, INT y
)
3348 TRACKMOUSEEVENT trackinfo
;
3350 if (!(fwKeys
& MK_LBUTTON
))
3351 infoPtr
->bLButtonDown
= FALSE
;
3353 if (infoPtr
->bLButtonDown
)
3357 WORD wDragWidth
= GetSystemMetrics(SM_CXDRAG
);
3358 WORD wDragHeight
= GetSystemMetrics(SM_CYDRAG
);
3360 rect
.left
= infoPtr
->ptClickPos
.x
- wDragWidth
;
3361 rect
.right
= infoPtr
->ptClickPos
.x
+ wDragWidth
;
3362 rect
.top
= infoPtr
->ptClickPos
.y
- wDragHeight
;
3363 rect
.bottom
= infoPtr
->ptClickPos
.y
+ wDragHeight
;
3368 if (!PtInRect(&rect
, tmp
))
3370 LVHITTESTINFO lvHitTestInfo
;
3373 lvHitTestInfo
.pt
= infoPtr
->ptClickPos
;
3374 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
3376 ZeroMemory(&nmlv
, sizeof(nmlv
));
3377 nmlv
.iItem
= lvHitTestInfo
.iItem
;
3378 nmlv
.ptAction
= infoPtr
->ptClickPos
;
3380 if (!infoPtr
->bDragging
)
3382 notify_listview(infoPtr
, LVN_BEGINDRAG
, &nmlv
);
3383 infoPtr
->bDragging
= TRUE
;
3390 infoPtr
->bLButtonDown
= FALSE
;
3392 /* see if we are supposed to be tracking mouse hovering */
3393 if (LISTVIEW_isHotTracking(infoPtr
)) {
3394 /* fill in the trackinfo struct */
3395 trackinfo
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3396 trackinfo
.dwFlags
= TME_QUERY
;
3397 trackinfo
.hwndTrack
= infoPtr
->hwndSelf
;
3398 trackinfo
.dwHoverTime
= infoPtr
->dwHoverTime
;
3400 /* see if we are already tracking this hwnd */
3401 _TrackMouseEvent(&trackinfo
);
3403 if(!(trackinfo
.dwFlags
& TME_HOVER
)) {
3404 trackinfo
.dwFlags
= TME_HOVER
;
3406 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3407 _TrackMouseEvent(&trackinfo
);
3416 * Tests whether the item is assignable to a list with style lStyle
3418 static inline BOOL
is_assignable_item(const LVITEMW
*lpLVItem
, LONG lStyle
)
3420 if ( (lpLVItem
->mask
& LVIF_TEXT
) &&
3421 (lpLVItem
->pszText
== LPSTR_TEXTCALLBACKW
) &&
3422 (lStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) ) return FALSE
;
3430 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3433 * [I] infoPtr : valid pointer to the listview structure
3434 * [I] lpLVItem : valid pointer to new item attributes
3435 * [I] isNew : the item being set is being inserted
3436 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3437 * [O] bChanged : will be set to TRUE if the item really changed
3443 static BOOL
set_main_item(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isNew
, BOOL isW
, BOOL
*bChanged
)
3445 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3453 assert(lpLVItem
->iItem
>= 0 && lpLVItem
->iItem
< infoPtr
->nItemCount
);
3455 if (lpLVItem
->mask
== 0) return TRUE
;
3457 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
3459 /* a virtual listview only stores selection and focus */
3460 if (lpLVItem
->mask
& ~LVIF_STATE
)
3466 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3467 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
3471 /* we need to get the lParam and state of the item */
3472 item
.iItem
= lpLVItem
->iItem
;
3473 item
.iSubItem
= lpLVItem
->iSubItem
;
3474 item
.mask
= LVIF_STATE
| LVIF_PARAM
;
3475 item
.stateMask
= ~0;
3478 if (!isNew
&& !LISTVIEW_GetItemW(infoPtr
, &item
)) return FALSE
;
3480 TRACE("oldState=%x, newState=%x\n", item
.state
, lpLVItem
->state
);
3481 /* determine what fields will change */
3482 if ((lpLVItem
->mask
& LVIF_STATE
) && ((item
.state
^ lpLVItem
->state
) & lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
))
3483 uChanged
|= LVIF_STATE
;
3485 if ((lpLVItem
->mask
& LVIF_IMAGE
) && (lpItem
->hdr
.iImage
!= lpLVItem
->iImage
))
3486 uChanged
|= LVIF_IMAGE
;
3488 if ((lpLVItem
->mask
& LVIF_PARAM
) && (lpItem
->lParam
!= lpLVItem
->lParam
))
3489 uChanged
|= LVIF_PARAM
;
3491 if ((lpLVItem
->mask
& LVIF_INDENT
) && (lpItem
->iIndent
!= lpLVItem
->iIndent
))
3492 uChanged
|= LVIF_INDENT
;
3494 if ((lpLVItem
->mask
& LVIF_TEXT
) && textcmpWT(lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
))
3495 uChanged
|= LVIF_TEXT
;
3497 TRACE("uChanged=0x%x\n", uChanged
);
3498 if (!uChanged
) return TRUE
;
3501 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
3502 nmlv
.iItem
= lpLVItem
->iItem
;
3503 nmlv
.uNewState
= (item
.state
& ~lpLVItem
->stateMask
) | (lpLVItem
->state
& lpLVItem
->stateMask
);
3504 nmlv
.uOldState
= item
.state
;
3505 nmlv
.uChanged
= uChanged
;
3506 nmlv
.lParam
= item
.lParam
;
3508 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3509 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3511 if(lpItem
&& !isNew
&& infoPtr
->bDoChangeNotify
)
3513 HWND hwndSelf
= infoPtr
->hwndSelf
;
3515 if (notify_listview(infoPtr
, LVN_ITEMCHANGING
, &nmlv
))
3517 if (!IsWindow(hwndSelf
))
3521 /* copy information */
3522 if (lpLVItem
->mask
& LVIF_TEXT
)
3523 textsetptrT(&lpItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3525 if (lpLVItem
->mask
& LVIF_IMAGE
)
3526 lpItem
->hdr
.iImage
= lpLVItem
->iImage
;
3528 if (lpLVItem
->mask
& LVIF_PARAM
)
3529 lpItem
->lParam
= lpLVItem
->lParam
;
3531 if (lpLVItem
->mask
& LVIF_INDENT
)
3532 lpItem
->iIndent
= lpLVItem
->iIndent
;
3534 if (uChanged
& LVIF_STATE
)
3536 if (lpItem
&& (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& ~(LVIS_FOCUSED
| LVIS_SELECTED
)))
3538 lpItem
->state
&= ~lpLVItem
->stateMask
;
3539 lpItem
->state
|= (lpLVItem
->state
& lpLVItem
->stateMask
);
3541 if (lpLVItem
->state
& lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
3543 if (infoPtr
->dwStyle
& LVS_SINGLESEL
) LISTVIEW_DeselectAllSkipItem(infoPtr
, lpLVItem
->iItem
);
3544 ranges_additem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3546 else if (lpLVItem
->stateMask
& LVIS_SELECTED
)
3547 ranges_delitem(infoPtr
->selectionRanges
, lpLVItem
->iItem
);
3549 /* if we are asked to change focus, and we manage it, do it */
3550 if (lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
3552 if (lpLVItem
->state
& LVIS_FOCUSED
)
3554 LISTVIEW_SetItemFocus(infoPtr
, -1);
3555 infoPtr
->nFocusedItem
= lpLVItem
->iItem
;
3556 LISTVIEW_EnsureVisible(infoPtr
, lpLVItem
->iItem
, uView
== LVS_LIST
);
3558 else if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
3559 infoPtr
->nFocusedItem
= -1;
3563 /* if we're inserting the item, we're done */
3564 if (isNew
) return TRUE
;
3566 /* send LVN_ITEMCHANGED notification */
3567 if (lpLVItem
->mask
& LVIF_PARAM
) nmlv
.lParam
= lpLVItem
->lParam
;
3568 if (infoPtr
->bDoChangeNotify
) notify_listview(infoPtr
, LVN_ITEMCHANGED
, &nmlv
);
3575 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3578 * [I] infoPtr : valid pointer to the listview structure
3579 * [I] lpLVItem : valid pointer to new subitem attributes
3580 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3581 * [O] bChanged : will be set to TRUE if the item really changed
3587 static BOOL
set_sub_item(const LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
, BOOL
*bChanged
)
3590 SUBITEM_INFO
*lpSubItem
;
3592 /* we do not support subitems for virtual listviews */
3593 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
3595 /* set subitem only if column is present */
3596 if (lpLVItem
->iSubItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
3598 /* First do some sanity checks */
3599 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3600 particularly useful. We currently do not actually do anything with
3601 the flag on subitems.
3603 if (lpLVItem
->mask
& ~(LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
)) return FALSE
;
3604 if (!(lpLVItem
->mask
& (LVIF_TEXT
| LVIF_IMAGE
| LVIF_STATE
))) return TRUE
;
3606 /* get the subitem structure, and create it if not there */
3607 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
3608 assert (hdpaSubItems
);
3610 lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, lpLVItem
->iSubItem
);
3613 SUBITEM_INFO
*tmpSubItem
;
3616 lpSubItem
= Alloc(sizeof(SUBITEM_INFO
));
3617 if (!lpSubItem
) return FALSE
;
3618 /* we could binary search here, if need be...*/
3619 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
3621 tmpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
3622 if (tmpSubItem
->iSubItem
> lpLVItem
->iSubItem
) break;
3624 if (DPA_InsertPtr(hdpaSubItems
, i
, lpSubItem
) == -1)
3629 lpSubItem
->iSubItem
= lpLVItem
->iSubItem
;
3630 lpSubItem
->hdr
.iImage
= I_IMAGECALLBACK
;
3634 if (lpLVItem
->mask
& LVIF_IMAGE
)
3635 if (lpSubItem
->hdr
.iImage
!= lpLVItem
->iImage
)
3637 lpSubItem
->hdr
.iImage
= lpLVItem
->iImage
;
3641 if (lpLVItem
->mask
& LVIF_TEXT
)
3642 if (lpSubItem
->hdr
.pszText
!= lpLVItem
->pszText
)
3644 textsetptrT(&lpSubItem
->hdr
.pszText
, lpLVItem
->pszText
, isW
);
3653 * Sets item attributes.
3656 * [I] infoPtr : valid pointer to the listview structure
3657 * [I] lpLVItem : new item attributes
3658 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3664 static BOOL
LISTVIEW_SetItemT(LISTVIEW_INFO
*infoPtr
, LVITEMW
*lpLVItem
, BOOL isW
)
3666 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3667 HWND hwndSelf
= infoPtr
->hwndSelf
;
3668 LPWSTR pszText
= NULL
;
3669 BOOL bResult
, bChanged
= FALSE
;
3671 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
3673 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
3676 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3677 if ((lpLVItem
->mask
& LVIF_TEXT
) && is_textW(lpLVItem
->pszText
))
3679 pszText
= lpLVItem
->pszText
;
3680 lpLVItem
->pszText
= textdupTtoW(lpLVItem
->pszText
, isW
);
3683 /* actually set the fields */
3684 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return FALSE
;
3686 if (lpLVItem
->iSubItem
)
3687 bResult
= set_sub_item(infoPtr
, lpLVItem
, TRUE
, &bChanged
);
3689 bResult
= set_main_item(infoPtr
, lpLVItem
, FALSE
, TRUE
, &bChanged
);
3690 if (!IsWindow(hwndSelf
))
3693 /* redraw item, if necessary */
3694 if (bChanged
&& !infoPtr
->bIsDrawing
)
3696 /* this little optimization eliminates some nasty flicker */
3697 if ( uView
== LVS_REPORT
&& !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) &&
3698 !(infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) &&
3699 lpLVItem
->iSubItem
> 0 && lpLVItem
->iSubItem
<= DPA_GetPtrCount(infoPtr
->hdpaColumns
) )
3700 LISTVIEW_InvalidateSubItem(infoPtr
, lpLVItem
->iItem
, lpLVItem
->iSubItem
);
3702 LISTVIEW_InvalidateItem(infoPtr
, lpLVItem
->iItem
);
3707 textfreeT(lpLVItem
->pszText
, isW
);
3708 lpLVItem
->pszText
= pszText
;
3716 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3719 * [I] infoPtr : valid pointer to the listview structure
3724 static INT
LISTVIEW_GetTopIndex(const LISTVIEW_INFO
*infoPtr
)
3726 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3728 SCROLLINFO scrollInfo
;
3730 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
3731 scrollInfo
.fMask
= SIF_POS
;
3733 if (uView
== LVS_LIST
)
3735 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
3736 nItem
= scrollInfo
.nPos
* LISTVIEW_GetCountPerColumn(infoPtr
);
3738 else if (uView
== LVS_REPORT
)
3740 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3741 nItem
= scrollInfo
.nPos
;
3745 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
3746 nItem
= LISTVIEW_GetCountPerRow(infoPtr
) * (scrollInfo
.nPos
/ infoPtr
->nItemHeight
);
3749 TRACE("nItem=%d\n", nItem
);
3757 * Erases the background of the given rectangle
3760 * [I] infoPtr : valid pointer to the listview structure
3761 * [I] hdc : device context handle
3762 * [I] lprcBox : clipping rectangle
3768 static inline BOOL
LISTVIEW_FillBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*lprcBox
)
3770 if (!infoPtr
->hBkBrush
) return FALSE
;
3772 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc
, wine_dbgstr_rect(lprcBox
), infoPtr
->hBkBrush
);
3774 return FillRect(hdc
, lprcBox
, infoPtr
->hBkBrush
);
3782 * [I] infoPtr : valid pointer to the listview structure
3783 * [I] hdc : device context handle
3784 * [I] nItem : item index
3785 * [I] nSubItem : subitem index
3786 * [I] pos : item position in client coordinates
3787 * [I] cdmode : custom draw mode
3793 static BOOL
LISTVIEW_DrawItem(LISTVIEW_INFO
*infoPtr
, HDC hdc
, INT nItem
, INT nSubItem
, POINT pos
, DWORD cdmode
)
3795 UINT uFormat
, uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
3796 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
3797 static WCHAR szCallback
[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3798 DWORD cdsubitemmode
= CDRF_DODEFAULT
;
3800 RECT rcSelect
, rcBox
, rcIcon
, rcLabel
, rcStateIcon
;
3801 NMLVCUSTOMDRAW nmlvcd
;
3806 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc
, nItem
, nSubItem
, wine_dbgstr_point(&pos
));
3808 /* get information needed for drawing the item */
3809 lvItem
.mask
= LVIF_TEXT
| LVIF_IMAGE
| LVIF_PARAM
;
3810 if (nSubItem
== 0) lvItem
.mask
|= LVIF_STATE
;
3811 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
3812 lvItem
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
| LVIS_STATEIMAGEMASK
;
3813 lvItem
.iItem
= nItem
;
3814 lvItem
.iSubItem
= nSubItem
;
3817 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
3818 lvItem
.pszText
= szDispText
;
3819 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
3820 if (nSubItem
> 0 && (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3821 lvItem
.state
= LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
);
3822 if (lvItem
.pszText
== LPSTR_TEXTCALLBACKW
) lvItem
.pszText
= szCallback
;
3823 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
3825 /* now check if we need to update the focus rectangle */
3826 lprcFocus
= infoPtr
->bFocus
&& (lvItem
.state
& LVIS_FOCUSED
) ? &infoPtr
->rcFocus
: 0;
3828 if (!lprcFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
3829 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, &rcSelect
, &rcIcon
, &rcStateIcon
, &rcLabel
);
3830 OffsetRect(&rcBox
, pos
.x
, pos
.y
);
3831 OffsetRect(&rcSelect
, pos
.x
, pos
.y
);
3832 OffsetRect(&rcIcon
, pos
.x
, pos
.y
);
3833 OffsetRect(&rcStateIcon
, pos
.x
, pos
.y
);
3834 OffsetRect(&rcLabel
, pos
.x
, pos
.y
);
3835 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3836 wine_dbgstr_rect(&rcBox
), wine_dbgstr_rect(&rcSelect
),
3837 wine_dbgstr_rect(&rcIcon
), wine_dbgstr_rect(&rcLabel
));
3839 /* fill in the custom draw structure */
3840 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcBox
, &lvItem
);
3842 hOldFont
= GetCurrentObject(hdc
, OBJ_FONT
);
3843 if (nSubItem
> 0) cdmode
= infoPtr
->cditemmode
;
3844 if (cdmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3845 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
3846 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
3847 if (nSubItem
== 0) infoPtr
->cditemmode
= cdsubitemmode
;
3848 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3849 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3850 if (nSubItem
== 0 && cdsubitemmode
== CDRF_NOTIFYITEMDRAW
)
3852 cdsubitemmode
= notify_customdraw(infoPtr
, CDDS_SUBITEM
| CDDS_ITEMPREPAINT
, &nmlvcd
);
3853 if (cdsubitemmode
& CDRF_SKIPDEFAULT
) goto postpaint
;
3855 if (nSubItem
== 0 || (cdmode
& CDRF_NOTIFYITEMDRAW
))
3856 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
3857 else if ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) == FALSE
)
3858 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, TRUE
);
3860 /* in full row select, subitems, will just use main item's colors */
3861 if (nSubItem
&& uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3862 nmlvcd
.clrTextBk
= CLR_NONE
;
3865 if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && (nSubItem
== 0))
3867 UINT uStateImage
= STATEIMAGEINDEX(lvItem
.state
);
3870 TRACE("uStateImage=%d\n", uStateImage
);
3871 ImageList_Draw(infoPtr
->himlState
, uStateImage
- 1, hdc
,
3872 rcStateIcon
.left
, rcStateIcon
.top
, ILD_NORMAL
);
3877 himl
= (uView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
3878 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
))
3880 TRACE("iImage=%d\n", lvItem
.iImage
);
3881 ImageList_DrawEx(himl
, lvItem
.iImage
, hdc
, rcIcon
.left
, rcIcon
.top
,
3882 rcIcon
.right
- rcIcon
.left
, rcIcon
.bottom
- rcIcon
.top
, infoPtr
->clrBk
, CLR_DEFAULT
,
3883 (lvItem
.state
& LVIS_SELECTED
) && (infoPtr
->bFocus
) ? ILD_SELECTED
: ILD_NORMAL
);
3886 /* Don't bother painting item being edited */
3887 if (infoPtr
->hwndEdit
&& nItem
== infoPtr
->nEditLabelItem
&& nSubItem
== 0) goto postpaint
;
3889 /* FIXME: temporary hack */
3890 rcSelect
.left
= rcLabel
.left
;
3892 /* draw the selection background, if we're drawing the main item */
3895 /* in icon mode, the label rect is really what we want to draw the
3897 if (uView
== LVS_ICON
)
3900 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
))
3901 rcSelect
.right
= rcBox
.right
;
3903 if (nmlvcd
.clrTextBk
!= CLR_NONE
)
3904 ExtTextOutW(hdc
, rcSelect
.left
, rcSelect
.top
, ETO_OPAQUE
, &rcSelect
, 0, 0, 0);
3905 if(lprcFocus
) *lprcFocus
= rcSelect
;
3908 /* figure out the text drawing flags */
3909 uFormat
= (uView
== LVS_ICON
? (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
) : LV_SL_DT_FLAGS
);
3910 if (uView
== LVS_ICON
)
3911 uFormat
= (lprcFocus
? LV_FL_DT_FLAGS
: LV_ML_DT_FLAGS
);
3914 switch (LISTVIEW_GetColumnInfo(infoPtr
, nSubItem
)->fmt
& LVCFMT_JUSTIFYMASK
)
3916 case LVCFMT_RIGHT
: uFormat
|= DT_RIGHT
; break;
3917 case LVCFMT_CENTER
: uFormat
|= DT_CENTER
; break;
3918 default: uFormat
|= DT_LEFT
;
3921 if (!(uFormat
& (DT_RIGHT
| DT_CENTER
)))
3923 if (himl
&& lvItem
.iImage
>= 0 && !IsRectEmpty(&rcIcon
)) rcLabel
.left
+= IMAGE_PADDING
;
3924 else rcLabel
.left
+= LABEL_HOR_PADDING
;
3926 else if (uFormat
& DT_RIGHT
) rcLabel
.right
-= LABEL_HOR_PADDING
;
3928 /* for GRIDLINES reduce the bottom so the text formats correctly */
3929 if (uView
== LVS_REPORT
&& infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
3932 DrawTextW(hdc
, lvItem
.pszText
, -1, &rcLabel
, uFormat
);
3935 if (cdsubitemmode
& CDRF_NOTIFYPOSTPAINT
)
3936 notify_postpaint(infoPtr
, &nmlvcd
);
3937 if (cdsubitemmode
& CDRF_NEWFONT
)
3938 SelectObject(hdc
, hOldFont
);
3944 * Draws listview items when in owner draw mode.
3947 * [I] infoPtr : valid pointer to the listview structure
3948 * [I] hdc : device context handle
3953 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
3955 UINT uID
= (UINT
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
3956 DWORD cditemmode
= CDRF_DODEFAULT
;
3957 NMLVCUSTOMDRAW nmlvcd
;
3958 POINT Origin
, Position
;
3964 ZeroMemory(&dis
, sizeof(dis
));
3966 /* Get scroll info once before loop */
3967 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
3969 /* iterate through the invalidated rows */
3970 while(iterator_next(i
))
3972 item
.iItem
= i
->nItem
;
3974 item
.mask
= LVIF_PARAM
| LVIF_STATE
;
3975 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
3976 if (!LISTVIEW_GetItemW(infoPtr
, &item
)) continue;
3978 dis
.CtlType
= ODT_LISTVIEW
;
3980 dis
.itemID
= item
.iItem
;
3981 dis
.itemAction
= ODA_DRAWENTIRE
;
3983 if (item
.state
& LVIS_SELECTED
) dis
.itemState
|= ODS_SELECTED
;
3984 if (infoPtr
->bFocus
&& (item
.state
& LVIS_FOCUSED
)) dis
.itemState
|= ODS_FOCUS
;
3985 dis
.hwndItem
= infoPtr
->hwndSelf
;
3987 LISTVIEW_GetItemOrigin(infoPtr
, dis
.itemID
, &Position
);
3988 dis
.rcItem
.left
= Position
.x
+ Origin
.x
;
3989 dis
.rcItem
.right
= dis
.rcItem
.left
+ infoPtr
->nItemWidth
;
3990 dis
.rcItem
.top
= Position
.y
+ Origin
.y
;
3991 dis
.rcItem
.bottom
= dis
.rcItem
.top
+ infoPtr
->nItemHeight
;
3992 dis
.itemData
= item
.lParam
;
3994 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item
, TRUE
), wine_dbgstr_rect(&dis
.rcItem
));
3997 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3998 * structure for the rest. of the paint cycle
4000 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &dis
.rcItem
, &item
);
4001 if (cdmode
& CDRF_NOTIFYITEMDRAW
)
4002 cditemmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4004 if (!(cditemmode
& CDRF_SKIPDEFAULT
))
4006 prepaint_setup (infoPtr
, hdc
, &nmlvcd
, FALSE
);
4007 SendMessageW(infoPtr
->hwndNotify
, WM_DRAWITEM
, dis
.CtlID
, (LPARAM
)&dis
);
4010 if (cditemmode
& CDRF_NOTIFYPOSTPAINT
)
4011 notify_postpaint(infoPtr
, &nmlvcd
);
4017 * Draws listview items when in report display mode.
4020 * [I] infoPtr : valid pointer to the listview structure
4021 * [I] hdc : device context handle
4022 * [I] cdmode : custom draw mode
4027 static void LISTVIEW_RefreshReport(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4030 RECT rcClip
, rcItem
;
4031 POINT Origin
, Position
;
4037 /* figure out what to draw */
4038 rgntype
= GetClipBox(hdc
, &rcClip
);
4039 if (rgntype
== NULLREGION
) return;
4041 /* Get scroll info once before loop */
4042 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4044 /* narrow down the columns we need to paint */
4045 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4047 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4048 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4050 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4052 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4053 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4055 iterator_rangeitems(&j
, colRange
);
4057 /* in full row select, we _have_ to draw the main item */
4058 if (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
)
4061 /* iterate through the invalidated rows */
4062 while(iterator_next(i
))
4064 /* iterate through the invalidated columns */
4065 while(iterator_next(&j
))
4067 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4068 Position
.x
+= Origin
.x
;
4069 Position
.y
+= Origin
.y
;
4071 if (rgntype
== COMPLEXREGION
&& !((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && j
.nItem
== 0))
4073 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4075 rcItem
.bottom
= infoPtr
->nItemHeight
;
4076 OffsetRect(&rcItem
, Position
.x
, Position
.y
);
4077 if (!RectVisible(hdc
, &rcItem
)) continue;
4080 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, j
.nItem
, Position
, cdmode
);
4083 iterator_destroy(&j
);
4088 * Draws the gridlines if necessary when in report display mode.
4091 * [I] infoPtr : valid pointer to the listview structure
4092 * [I] hdc : device context handle
4097 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
4102 RECT rcClip
, rcItem
;
4109 /* figure out what to draw */
4110 rgntype
= GetClipBox(hdc
, &rcClip
);
4111 if (rgntype
== NULLREGION
) return;
4113 /* Get scroll info once before loop */
4114 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4116 /* narrow down the columns we need to paint */
4117 for(colRange
.lower
= 0; colRange
.lower
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.lower
++)
4119 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.lower
, &rcItem
);
4120 if (rcItem
.right
+ Origin
.x
>= rcClip
.left
) break;
4122 for(colRange
.upper
= DPA_GetPtrCount(infoPtr
->hdpaColumns
); colRange
.upper
> 0; colRange
.upper
--)
4124 LISTVIEW_GetHeaderRect(infoPtr
, colRange
.upper
- 1, &rcItem
);
4125 if (rcItem
.left
+ Origin
.x
< rcClip
.right
) break;
4127 iterator_rangeitems(&j
, colRange
);
4129 if ((hPen
= CreatePen( PS_SOLID
, 1, comctl32_color
.clr3dFace
)))
4131 hOldPen
= SelectObject ( hdc
, hPen
);
4133 /* draw the vertical lines for the columns */
4134 iterator_rangeitems(&j
, colRange
);
4135 while(iterator_next(&j
))
4137 LISTVIEW_GetHeaderRect(infoPtr
, j
.nItem
, &rcItem
);
4138 if (rcItem
.left
== 0) continue; /* skip first column */
4139 rcItem
.left
+= Origin
.x
;
4140 rcItem
.right
+= Origin
.x
;
4141 rcItem
.top
= infoPtr
->rcList
.top
;
4142 rcItem
.bottom
= infoPtr
->rcList
.bottom
;
4143 TRACE("vert col=%d, rcItem=%s\n", j
.nItem
, wine_dbgstr_rect(&rcItem
));
4144 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4145 LineTo (hdc
, rcItem
.left
, rcItem
.bottom
);
4147 iterator_destroy(&j
);
4149 /* draw the horizontial lines for the rows */
4150 itemheight
= LISTVIEW_CalculateItemHeight(infoPtr
);
4151 rcItem
.left
= infoPtr
->rcList
.left
+ Origin
.x
;
4152 rcItem
.right
= infoPtr
->rcList
.right
+ Origin
.x
;
4153 rcItem
.bottom
= rcItem
.top
= Origin
.y
- 1;
4154 MoveToEx(hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4155 LineTo(hdc
, rcItem
.right
, rcItem
.top
);
4156 for(y
=itemheight
-1+Origin
.y
; y
<=infoPtr
->rcList
.bottom
; y
+=itemheight
)
4158 rcItem
.bottom
= rcItem
.top
= y
;
4159 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem
));
4160 MoveToEx (hdc
, rcItem
.left
, rcItem
.top
, NULL
);
4161 LineTo (hdc
, rcItem
.right
, rcItem
.top
);
4164 SelectObject( hdc
, hOldPen
);
4165 DeleteObject( hPen
);
4171 * Draws listview items when in list display mode.
4174 * [I] infoPtr : valid pointer to the listview structure
4175 * [I] hdc : device context handle
4176 * [I] cdmode : custom draw mode
4181 static void LISTVIEW_RefreshList(LISTVIEW_INFO
*infoPtr
, ITERATOR
*i
, HDC hdc
, DWORD cdmode
)
4183 POINT Origin
, Position
;
4185 /* Get scroll info once before loop */
4186 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4188 while(iterator_prev(i
))
4190 LISTVIEW_GetItemOrigin(infoPtr
, i
->nItem
, &Position
);
4191 Position
.x
+= Origin
.x
;
4192 Position
.y
+= Origin
.y
;
4194 LISTVIEW_DrawItem(infoPtr
, hdc
, i
->nItem
, 0, Position
, cdmode
);
4201 * Draws listview items.
4204 * [I] infoPtr : valid pointer to the listview structure
4205 * [I] hdc : device context handle
4206 * [I] prcErase : rect to be erased before refresh (may be NULL)
4211 static void LISTVIEW_Refresh(LISTVIEW_INFO
*infoPtr
, HDC hdc
, const RECT
*prcErase
)
4213 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4214 COLORREF oldTextColor
= 0, oldBkColor
= 0, oldClrTextBk
, oldClrText
;
4215 NMLVCUSTOMDRAW nmlvcd
;
4222 HBITMAP hbmp
= NULL
;
4224 LISTVIEW_DUMP(infoPtr
);
4226 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4227 TRACE("double buffering\n");
4229 hdc
= CreateCompatibleDC(hdcOrig
);
4231 ERR("Failed to create DC for backbuffer\n");
4234 hbmp
= CreateCompatibleBitmap(hdcOrig
, infoPtr
->rcList
.right
,
4235 infoPtr
->rcList
.bottom
);
4237 ERR("Failed to create bitmap for backbuffer\n");
4242 SelectObject(hdc
, hbmp
);
4243 SelectObject(hdc
, infoPtr
->hFont
);
4245 /* Save dc values we're gonna trash while drawing
4246 * FIXME: Should be done in LISTVIEW_DrawItem() */
4247 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
4248 oldBkMode
= GetBkMode(hdc
);
4249 oldBkColor
= GetBkColor(hdc
);
4250 oldTextColor
= GetTextColor(hdc
);
4253 infoPtr
->bIsDrawing
= TRUE
;
4256 LISTVIEW_FillBkgnd(infoPtr
, hdc
, prcErase
);
4257 } else if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) {
4258 /* If no erasing was done (usually because RedrawWindow was called
4259 * with RDW_INVALIDATE only) we need to copy the old contents into
4260 * the backbuffer before continuing. */
4261 BitBlt(hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4262 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4263 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4264 hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4267 /* FIXME: Shouldn't need to do this */
4268 oldClrTextBk
= infoPtr
->clrTextBk
;
4269 oldClrText
= infoPtr
->clrText
;
4271 infoPtr
->cditemmode
= CDRF_DODEFAULT
;
4273 GetClientRect(infoPtr
->hwndSelf
, &rcClient
);
4274 customdraw_fill(&nmlvcd
, infoPtr
, hdc
, &rcClient
, 0);
4275 cdmode
= notify_customdraw(infoPtr
, CDDS_PREPAINT
, &nmlvcd
);
4276 if (cdmode
& CDRF_SKIPDEFAULT
) goto enddraw
;
4277 prepaint_setup(infoPtr
, hdc
, &nmlvcd
, FALSE
);
4279 /* Use these colors to draw the items */
4280 infoPtr
->clrTextBk
= nmlvcd
.clrTextBk
;
4281 infoPtr
->clrText
= nmlvcd
.clrText
;
4283 /* nothing to draw */
4284 if(infoPtr
->nItemCount
== 0) goto enddraw
;
4286 /* figure out what we need to draw */
4287 iterator_visibleitems(&i
, infoPtr
, hdc
);
4289 /* send cache hint notification */
4290 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
4292 RANGE range
= iterator_range(&i
);
4295 ZeroMemory(&nmlv
, sizeof(NMLVCACHEHINT
));
4296 nmlv
.iFrom
= range
.lower
;
4297 nmlv
.iTo
= range
.upper
- 1;
4298 notify_hdr(infoPtr
, LVN_ODCACHEHINT
, &nmlv
.hdr
);
4301 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
4302 LISTVIEW_RefreshOwnerDraw(infoPtr
, &i
, hdc
, cdmode
);
4305 if (uView
== LVS_REPORT
)
4306 LISTVIEW_RefreshReport(infoPtr
, &i
, hdc
, cdmode
);
4307 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4308 LISTVIEW_RefreshList(infoPtr
, &i
, hdc
, cdmode
);
4310 /* if we have a focus rect, draw it */
4311 if (infoPtr
->bFocus
)
4312 DrawFocusRect(hdc
, &infoPtr
->rcFocus
);
4314 iterator_destroy(&i
);
4317 /* For LVS_EX_GRIDLINES go and draw lines */
4318 /* This includes the case where there were *no* items */
4319 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
&&
4320 infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
)
4321 LISTVIEW_RefreshReportGrid(infoPtr
, hdc
);
4323 if (cdmode
& CDRF_NOTIFYPOSTPAINT
)
4324 notify_postpaint(infoPtr
, &nmlvcd
);
4326 infoPtr
->clrTextBk
= oldClrTextBk
;
4327 infoPtr
->clrText
= oldClrText
;
4330 BitBlt(hdcOrig
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
,
4331 infoPtr
->rcList
.right
- infoPtr
->rcList
.left
,
4332 infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
,
4333 hdc
, infoPtr
->rcList
.left
, infoPtr
->rcList
.top
, SRCCOPY
);
4338 SelectObject(hdc
, hOldFont
);
4339 SetBkMode(hdc
, oldBkMode
);
4340 SetBkColor(hdc
, oldBkColor
);
4341 SetTextColor(hdc
, oldTextColor
);
4344 infoPtr
->bIsDrawing
= FALSE
;
4350 * Calculates the approximate width and height of a given number of items.
4353 * [I] infoPtr : valid pointer to the listview structure
4354 * [I] nItemCount : number of items
4355 * [I] wWidth : width
4356 * [I] wHeight : height
4359 * Returns a DWORD. The width in the low word and the height in high word.
4361 static DWORD
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO
*infoPtr
, INT nItemCount
,
4362 WORD wWidth
, WORD wHeight
)
4364 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4365 INT nItemCountPerColumn
= 1;
4366 INT nColumnCount
= 0;
4367 DWORD dwViewRect
= 0;
4369 if (nItemCount
== -1)
4370 nItemCount
= infoPtr
->nItemCount
;
4372 if (uView
== LVS_LIST
)
4374 if (wHeight
== 0xFFFF)
4376 /* use current height */
4377 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4380 if (wHeight
< infoPtr
->nItemHeight
)
4381 wHeight
= infoPtr
->nItemHeight
;
4385 if (infoPtr
->nItemHeight
> 0)
4387 nItemCountPerColumn
= wHeight
/ infoPtr
->nItemHeight
;
4388 if (nItemCountPerColumn
== 0)
4389 nItemCountPerColumn
= 1;
4391 if (nItemCount
% nItemCountPerColumn
!= 0)
4392 nColumnCount
= nItemCount
/ nItemCountPerColumn
;
4394 nColumnCount
= nItemCount
/ nItemCountPerColumn
+ 1;
4398 /* Microsoft padding magic */
4399 wHeight
= nItemCountPerColumn
* infoPtr
->nItemHeight
+ 2;
4400 wWidth
= nColumnCount
* infoPtr
->nItemWidth
+ 2;
4402 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4404 else if (uView
== LVS_REPORT
)
4408 if (infoPtr
->nItemCount
> 0)
4410 LISTVIEW_GetItemBox(infoPtr
, 0, &rcBox
);
4411 wWidth
= rcBox
.right
- rcBox
.left
;
4412 wHeight
= (rcBox
.bottom
- rcBox
.top
) * nItemCount
;
4416 /* use current height and width */
4417 if (wHeight
== 0xffff)
4418 wHeight
= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
;
4419 if (wWidth
== 0xffff)
4420 wWidth
= infoPtr
->rcList
.right
- infoPtr
->rcList
.left
;
4423 dwViewRect
= MAKELONG(wWidth
, wHeight
);
4425 else if (uView
== LVS_SMALLICON
)
4426 FIXME("uView == LVS_SMALLICON: not implemented\n");
4427 else if (uView
== LVS_ICON
)
4428 FIXME("uView == LVS_ICON: not implemented\n");
4436 * Create a drag image list for the specified item.
4439 * [I] infoPtr : valid pointer to the listview structure
4440 * [I] iItem : index of item
4441 * [O] lppt : Upperr-left corner of the image
4444 * Returns a handle to the image list if successful, NULL otherwise.
4446 static HIMAGELIST
LISTVIEW_CreateDragImage(LISTVIEW_INFO
*infoPtr
, INT iItem
, LPPOINT lppt
)
4452 HBITMAP hbmp
, hOldbmp
;
4453 HIMAGELIST dragList
= 0;
4454 TRACE("iItem=%d Count=%d\n", iItem
, infoPtr
->nItemCount
);
4456 if (iItem
< 0 || iItem
>= infoPtr
->nItemCount
)
4459 rcItem
.left
= LVIR_BOUNDS
;
4460 if (!LISTVIEW_GetItemRect(infoPtr
, iItem
, &rcItem
))
4463 lppt
->x
= rcItem
.left
;
4464 lppt
->y
= rcItem
.top
;
4466 size
.cx
= rcItem
.right
- rcItem
.left
;
4467 size
.cy
= rcItem
.bottom
- rcItem
.top
;
4469 hdcOrig
= GetDC(infoPtr
->hwndSelf
);
4470 hdc
= CreateCompatibleDC(hdcOrig
);
4471 hbmp
= CreateCompatibleBitmap(hdcOrig
, size
.cx
, size
.cy
);
4472 hOldbmp
= SelectObject(hdc
, hbmp
);
4474 rcItem
.left
= rcItem
.top
= 0;
4475 rcItem
.right
= size
.cx
;
4476 rcItem
.bottom
= size
.cy
;
4477 FillRect(hdc
, &rcItem
, infoPtr
->hBkBrush
);
4480 if (LISTVIEW_DrawItem(infoPtr
, hdc
, iItem
, 0, pos
, infoPtr
->cditemmode
))
4482 dragList
= ImageList_Create(size
.cx
, size
.cy
, ILC_COLOR
, 10, 10);
4483 SelectObject(hdc
, hOldbmp
);
4484 ImageList_Add(dragList
, hbmp
, 0);
4487 SelectObject(hdc
, hOldbmp
);
4491 ReleaseDC(infoPtr
->hwndSelf
, hdcOrig
);
4493 TRACE("ret=%p\n", dragList
);
4501 * Removes all listview items and subitems.
4504 * [I] infoPtr : valid pointer to the listview structure
4510 static BOOL
LISTVIEW_DeleteAllItems(LISTVIEW_INFO
*infoPtr
, BOOL destroy
)
4513 HDPA hdpaSubItems
= NULL
;
4520 /* we do it directly, to avoid notifications */
4521 ranges_clear(infoPtr
->selectionRanges
);
4522 infoPtr
->nSelectionMark
= -1;
4523 infoPtr
->nFocusedItem
= -1;
4524 SetRectEmpty(&infoPtr
->rcFocus
);
4525 /* But we are supposed to leave nHotItem as is! */
4528 /* send LVN_DELETEALLITEMS notification */
4529 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
4531 bSuppress
= notify_listview(infoPtr
, LVN_DELETEALLITEMS
, &nmlv
);
4533 for (i
= infoPtr
->nItemCount
- 1; i
>= 0; i
--)
4535 /* send LVN_DELETEITEM notification, if not suppressed */
4536 if (!bSuppress
) notify_deleteitem(infoPtr
, i
);
4537 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4539 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
4540 for (j
= 0; j
< DPA_GetPtrCount(hdpaSubItems
); j
++)
4542 hdrItem
= DPA_GetPtr(hdpaSubItems
, j
);
4543 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4546 DPA_Destroy(hdpaSubItems
);
4547 DPA_DeletePtr(infoPtr
->hdpaItems
, i
);
4549 DPA_DeletePtr(infoPtr
->hdpaPosX
, i
);
4550 DPA_DeletePtr(infoPtr
->hdpaPosY
, i
);
4551 infoPtr
->nItemCount
--;
4556 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4557 LISTVIEW_UpdateScroll(infoPtr
);
4559 LISTVIEW_InvalidateList(infoPtr
);
4566 * Scrolls, and updates the columns, when a column is changing width.
4569 * [I] infoPtr : valid pointer to the listview structure
4570 * [I] nColumn : column to scroll
4571 * [I] dx : amount of scroll, in pixels
4576 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT dx
)
4578 COLUMN_INFO
*lpColumnInfo
;
4583 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) < 1) return;
4584 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1));
4585 rcCol
= lpColumnInfo
->rcHeader
;
4586 if (nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
))
4587 rcCol
.left
= rcCol
.right
;
4589 /* adjust the other columns */
4590 for (nCol
= nColumn
; nCol
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); nCol
++)
4592 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nCol
);
4593 lpColumnInfo
->rcHeader
.left
+= dx
;
4594 lpColumnInfo
->rcHeader
.right
+= dx
;
4597 /* do not update screen if not in report mode */
4598 if (!is_redrawing(infoPtr
) || (infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return;
4600 /* if we have a focus, we must first erase the focus rect */
4601 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
4603 /* Need to reset the item width when inserting a new column */
4604 infoPtr
->nItemWidth
+= dx
;
4606 LISTVIEW_UpdateScroll(infoPtr
);
4607 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
4609 /* scroll to cover the deleted column, and invalidate for redraw */
4610 rcOld
= infoPtr
->rcList
;
4611 rcOld
.left
= ptOrigin
.x
+ rcCol
.left
+ dx
;
4612 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, 0, &rcOld
, &rcOld
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4614 /* we can restore focus now */
4615 if (infoPtr
->bFocus
) LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
4620 * Removes a column from the listview control.
4623 * [I] infoPtr : valid pointer to the listview structure
4624 * [I] nColumn : column index
4630 static BOOL
LISTVIEW_DeleteColumn(LISTVIEW_INFO
*infoPtr
, INT nColumn
)
4634 TRACE("nColumn=%d\n", nColumn
);
4636 if (nColumn
< 0 || DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0
4637 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
4639 /* While the MSDN specifically says that column zero should not be deleted,
4640 what actually happens is that the column itself is deleted but no items or subitems
4644 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcCol
);
4646 if (!Header_DeleteItem(infoPtr
->hwndHeader
, nColumn
))
4649 Free(DPA_GetPtr(infoPtr
->hdpaColumns
, nColumn
));
4650 DPA_DeletePtr(infoPtr
->hdpaColumns
, nColumn
);
4652 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && nColumn
)
4654 SUBITEM_INFO
*lpSubItem
, *lpDelItem
;
4656 INT nItem
, nSubItem
, i
;
4658 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
4660 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
4663 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4665 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
4666 if (lpSubItem
->iSubItem
== nColumn
)
4669 lpDelItem
= lpSubItem
;
4671 else if (lpSubItem
->iSubItem
> nColumn
)
4673 lpSubItem
->iSubItem
--;
4677 /* if we found our subitem, zapp it */
4681 if (is_textW(lpDelItem
->hdr
.pszText
))
4682 Free(lpDelItem
->hdr
.pszText
);
4687 /* free dpa memory */
4688 DPA_DeletePtr(hdpaSubItems
, nSubItem
);
4693 /* update the other column info */
4694 LISTVIEW_UpdateItemSize(infoPtr
);
4695 if(DPA_GetPtrCount(infoPtr
->hdpaColumns
) == 0)
4696 LISTVIEW_InvalidateList(infoPtr
);
4698 LISTVIEW_ScrollColumns(infoPtr
, nColumn
, -(rcCol
.right
- rcCol
.left
));
4705 * Invalidates the listview after an item's insertion or deletion.
4708 * [I] infoPtr : valid pointer to the listview structure
4709 * [I] nItem : item index
4710 * [I] dir : -1 if deleting, 1 if inserting
4715 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO
*infoPtr
, INT nItem
, INT dir
)
4717 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4718 INT nPerCol
, nItemCol
, nItemRow
;
4722 /* if we don't refresh, what's the point of scrolling? */
4723 if (!is_redrawing(infoPtr
)) return;
4725 assert (abs(dir
) == 1);
4727 /* arrange icons if autoarrange is on */
4728 if (is_autoarrange(infoPtr
))
4730 BOOL arrange
= TRUE
;
4731 if (dir
< 0 && nItem
>= infoPtr
->nItemCount
) arrange
= FALSE
;
4732 if (dir
> 0 && nItem
== infoPtr
->nItemCount
- 1) arrange
= FALSE
;
4733 if (arrange
) LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
4736 /* scrollbars need updating */
4737 LISTVIEW_UpdateScroll(infoPtr
);
4739 /* figure out the item's position */
4740 if (uView
== LVS_REPORT
)
4741 nPerCol
= infoPtr
->nItemCount
+ 1;
4742 else if (uView
== LVS_LIST
)
4743 nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
4744 else /* LVS_ICON, or LVS_SMALLICON */
4747 nItemCol
= nItem
/ nPerCol
;
4748 nItemRow
= nItem
% nPerCol
;
4749 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
4751 /* move the items below up a slot */
4752 rcScroll
.left
= nItemCol
* infoPtr
->nItemWidth
;
4753 rcScroll
.top
= nItemRow
* infoPtr
->nItemHeight
;
4754 rcScroll
.right
= rcScroll
.left
+ infoPtr
->nItemWidth
;
4755 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4756 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4757 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll
), dir
* infoPtr
->nItemHeight
);
4758 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4760 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll
), wine_dbgstr_rect(&infoPtr
->rcList
));
4761 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4762 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4765 /* report has only that column, so we're done */
4766 if (uView
== LVS_REPORT
) return;
4768 /* now for LISTs, we have to deal with the columns to the right */
4769 rcScroll
.left
= (nItemCol
+ 1) * infoPtr
->nItemWidth
;
4771 rcScroll
.right
= (infoPtr
->nItemCount
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
4772 rcScroll
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
4773 OffsetRect(&rcScroll
, Origin
.x
, Origin
.y
);
4774 if (IntersectRect(&rcScroll
, &rcScroll
, &infoPtr
->rcList
))
4775 ScrollWindowEx(infoPtr
->hwndSelf
, 0, dir
* infoPtr
->nItemHeight
,
4776 &rcScroll
, &rcScroll
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
4781 * Removes an item from the listview control.
4784 * [I] infoPtr : valid pointer to the listview structure
4785 * [I] nItem : item index
4791 static BOOL
LISTVIEW_DeleteItem(LISTVIEW_INFO
*infoPtr
, INT nItem
)
4794 const UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
4795 const BOOL is_icon
= (uView
== LVS_SMALLICON
|| uView
== LVS_ICON
);
4797 TRACE("(nItem=%d)\n", nItem
);
4799 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
4801 /* remove selection, and focus */
4803 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
4804 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
4806 /* send LVN_DELETEITEM notification. */
4807 if (!notify_deleteitem(infoPtr
, nItem
)) return FALSE
;
4809 /* we need to do this here, because we'll be deleting stuff */
4811 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4813 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4819 hdpaSubItems
= DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
4820 for (i
= 0; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
4822 hdrItem
= DPA_GetPtr(hdpaSubItems
, i
);
4823 if (is_textW(hdrItem
->pszText
)) Free(hdrItem
->pszText
);
4826 DPA_Destroy(hdpaSubItems
);
4831 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
4832 DPA_DeletePtr(infoPtr
->hdpaPosY
, nItem
);
4835 infoPtr
->nItemCount
--;
4836 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
4838 /* now is the invalidation fun */
4840 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, -1);
4847 * Callback implementation for editlabel control
4850 * [I] infoPtr : valid pointer to the listview structure
4851 * [I] pszText : modified text
4852 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4858 static BOOL
LISTVIEW_EndEditLabelT(LISTVIEW_INFO
*infoPtr
, LPWSTR pszText
, BOOL isW
)
4860 HWND hwndSelf
= infoPtr
->hwndSelf
;
4861 NMLVDISPINFOW dispInfo
;
4862 INT editedItem
= infoPtr
->nEditLabelItem
;
4865 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText
, isW
), isW
);
4867 infoPtr
->nEditLabelItem
= -1;
4869 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4870 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4871 dispInfo
.item
.iItem
= editedItem
;
4872 dispInfo
.item
.iSubItem
= 0;
4873 dispInfo
.item
.stateMask
= ~0;
4874 if (!LISTVIEW_GetItemW(infoPtr
, &dispInfo
.item
)) return FALSE
;
4877 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, pszText
) == 0);
4880 LPWSTR tmp
= textdupTtoW(pszText
, FALSE
);
4881 bSame
= (lstrcmpW(dispInfo
.item
.pszText
, tmp
) == 0);
4882 textfreeT(tmp
, FALSE
);
4884 if (bSame
) return TRUE
;
4886 /* add the text from the edit in */
4887 dispInfo
.item
.mask
|= LVIF_TEXT
;
4888 dispInfo
.item
.pszText
= pszText
;
4889 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4891 /* Do we need to update the Item Text */
4892 if (!notify_dispinfoT(infoPtr
, LVN_ENDLABELEDITW
, &dispInfo
, isW
)) return FALSE
;
4893 if (!IsWindow(hwndSelf
))
4895 if (!pszText
) return TRUE
;
4897 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
))
4899 HDPA hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, editedItem
);
4900 ITEM_INFO
* lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
4901 if (lpItem
&& lpItem
->hdr
.pszText
== LPSTR_TEXTCALLBACKW
)
4903 LISTVIEW_InvalidateItem(infoPtr
, editedItem
);
4908 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4909 dispInfo
.item
.mask
= LVIF_TEXT
;
4910 dispInfo
.item
.iItem
= editedItem
;
4911 dispInfo
.item
.iSubItem
= 0;
4912 dispInfo
.item
.pszText
= pszText
;
4913 dispInfo
.item
.cchTextMax
= textlenT(pszText
, isW
);
4914 return LISTVIEW_SetItemT(infoPtr
, &dispInfo
.item
, isW
);
4919 * Begin in place editing of specified list view item
4922 * [I] infoPtr : valid pointer to the listview structure
4923 * [I] nItem : item index
4924 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4930 static HWND
LISTVIEW_EditLabelT(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL isW
)
4932 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
4933 NMLVDISPINFOW dispInfo
;
4935 HWND hwndSelf
= infoPtr
->hwndSelf
;
4937 TRACE("(nItem=%d, isW=%d)\n", nItem
, isW
);
4939 if (~infoPtr
->dwStyle
& LVS_EDITLABELS
) return 0;
4940 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
4942 infoPtr
->nEditLabelItem
= nItem
;
4944 /* Is the EditBox still there, if so remove it */
4945 if(infoPtr
->hwndEdit
!= 0)
4947 SetFocus(infoPtr
->hwndSelf
);
4948 infoPtr
->hwndEdit
= 0;
4951 LISTVIEW_SetSelection(infoPtr
, nItem
);
4952 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
4953 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
4955 rect
.left
= LVIR_LABEL
;
4956 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rect
)) return 0;
4958 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
4959 dispInfo
.item
.mask
= LVIF_PARAM
| LVIF_STATE
| LVIF_TEXT
;
4960 dispInfo
.item
.iItem
= nItem
;
4961 dispInfo
.item
.iSubItem
= 0;
4962 dispInfo
.item
.stateMask
= ~0;
4963 dispInfo
.item
.pszText
= szDispText
;
4964 dispInfo
.item
.cchTextMax
= DISP_TEXT_SIZE
;
4965 if (!LISTVIEW_GetItemT(infoPtr
, &dispInfo
.item
, isW
)) return 0;
4967 infoPtr
->hwndEdit
= CreateEditLabelT(infoPtr
, dispInfo
.item
.pszText
, WS_VISIBLE
,
4968 rect
.left
-2, rect
.top
-1, 0, rect
.bottom
- rect
.top
+2, isW
);
4969 if (!infoPtr
->hwndEdit
) return 0;
4971 if (notify_dispinfoT(infoPtr
, LVN_BEGINLABELEDITW
, &dispInfo
, isW
))
4973 if (!IsWindow(hwndSelf
))
4975 SendMessageW(infoPtr
->hwndEdit
, WM_CLOSE
, 0, 0);
4976 infoPtr
->hwndEdit
= 0;
4980 ShowWindow(infoPtr
->hwndEdit
, SW_NORMAL
);
4981 SetFocus(infoPtr
->hwndEdit
);
4982 SendMessageW(infoPtr
->hwndEdit
, EM_SETSEL
, 0, -1);
4983 return infoPtr
->hwndEdit
;
4989 * Ensures the specified item is visible, scrolling into view if necessary.
4992 * [I] infoPtr : valid pointer to the listview structure
4993 * [I] nItem : item index
4994 * [I] bPartial : partially or entirely visible
5000 static BOOL
LISTVIEW_EnsureVisible(LISTVIEW_INFO
*infoPtr
, INT nItem
, BOOL bPartial
)
5002 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5003 INT nScrollPosHeight
= 0;
5004 INT nScrollPosWidth
= 0;
5005 INT nHorzAdjust
= 0;
5006 INT nVertAdjust
= 0;
5009 RECT rcItem
, rcTemp
;
5011 rcItem
.left
= LVIR_BOUNDS
;
5012 if (!LISTVIEW_GetItemRect(infoPtr
, nItem
, &rcItem
)) return FALSE
;
5014 if (bPartial
&& IntersectRect(&rcTemp
, &infoPtr
->rcList
, &rcItem
)) return TRUE
;
5016 if (rcItem
.left
< infoPtr
->rcList
.left
|| rcItem
.right
> infoPtr
->rcList
.right
)
5018 /* scroll left/right, but in LVS_REPORT mode */
5019 if (uView
== LVS_LIST
)
5020 nScrollPosWidth
= infoPtr
->nItemWidth
;
5021 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
5022 nScrollPosWidth
= 1;
5024 if (rcItem
.left
< infoPtr
->rcList
.left
)
5027 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.left
- infoPtr
->rcList
.left
;
5032 if (uView
!= LVS_REPORT
) nHorzDiff
= rcItem
.right
- infoPtr
->rcList
.right
;
5036 if (rcItem
.top
< infoPtr
->rcList
.top
|| rcItem
.bottom
> infoPtr
->rcList
.bottom
)
5038 /* scroll up/down, but not in LVS_LIST mode */
5039 if (uView
== LVS_REPORT
)
5040 nScrollPosHeight
= infoPtr
->nItemHeight
;
5041 else if ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
))
5042 nScrollPosHeight
= 1;
5044 if (rcItem
.top
< infoPtr
->rcList
.top
)
5047 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.top
- infoPtr
->rcList
.top
;
5052 if (uView
!= LVS_LIST
) nVertDiff
= rcItem
.bottom
- infoPtr
->rcList
.bottom
;
5056 if (!nScrollPosWidth
&& !nScrollPosHeight
) return TRUE
;
5058 if (nScrollPosWidth
)
5060 INT diff
= nHorzDiff
/ nScrollPosWidth
;
5061 if (nHorzDiff
% nScrollPosWidth
) diff
+= nHorzAdjust
;
5062 LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5065 if (nScrollPosHeight
)
5067 INT diff
= nVertDiff
/ nScrollPosHeight
;
5068 if (nVertDiff
% nScrollPosHeight
) diff
+= nVertAdjust
;
5069 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, diff
, 0);
5077 * Searches for an item with specific characteristics.
5080 * [I] hwnd : window handle
5081 * [I] nStart : base item index
5082 * [I] lpFindInfo : item information to look for
5085 * SUCCESS : index of item
5088 static INT
LISTVIEW_FindItemW(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5089 const LVFINDINFOW
*lpFindInfo
)
5091 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5092 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5093 BOOL bWrap
= FALSE
, bNearest
= FALSE
;
5094 INT nItem
= nStart
+ 1, nLast
= infoPtr
->nItemCount
, nNearestItem
= -1;
5095 ULONG xdist
, ydist
, dist
, mindist
= 0x7fffffff;
5096 POINT Position
, Destination
;
5099 /* Search in virtual listviews should be done by application, not by
5100 listview control, so we just send LVN_ODFINDITEMW and return the result */
5101 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5105 nmlv
.iStart
= nStart
;
5106 nmlv
.lvfi
= *lpFindInfo
;
5107 return notify_hdr(infoPtr
, LVN_ODFINDITEMW
, (LPNMHDR
)&nmlv
.hdr
);
5110 if (!lpFindInfo
|| nItem
< 0) return -1;
5113 if (lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
))
5115 lvItem
.mask
|= LVIF_TEXT
;
5116 lvItem
.pszText
= szDispText
;
5117 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5120 if (lpFindInfo
->flags
& LVFI_WRAP
)
5123 if ((lpFindInfo
->flags
& LVFI_NEARESTXY
) &&
5124 (uView
== LVS_ICON
|| uView
==LVS_SMALLICON
))
5129 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5130 Destination
.x
= lpFindInfo
->pt
.x
- Origin
.x
;
5131 Destination
.y
= lpFindInfo
->pt
.y
- Origin
.y
;
5132 switch(lpFindInfo
->vkDirection
)
5134 case VK_DOWN
: Destination
.y
+= infoPtr
->nItemHeight
; break;
5135 case VK_UP
: Destination
.y
-= infoPtr
->nItemHeight
; break;
5136 case VK_RIGHT
: Destination
.x
+= infoPtr
->nItemWidth
; break;
5137 case VK_LEFT
: Destination
.x
-= infoPtr
->nItemWidth
; break;
5138 case VK_HOME
: Destination
.x
= Destination
.y
= 0; break;
5139 case VK_NEXT
: Destination
.y
+= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5140 case VK_PRIOR
: Destination
.y
-= infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
; break;
5142 LISTVIEW_GetAreaRect(infoPtr
, &rcArea
);
5143 Destination
.x
= rcArea
.right
;
5144 Destination
.y
= rcArea
.bottom
;
5146 default: ERR("Unknown vkDirection=%d\n", lpFindInfo
->vkDirection
);
5150 else Destination
.x
= Destination
.y
= 0;
5152 /* if LVFI_PARAM is specified, all other flags are ignored */
5153 if (lpFindInfo
->flags
& LVFI_PARAM
)
5155 lvItem
.mask
|= LVIF_PARAM
;
5157 lvItem
.mask
&= ~LVIF_TEXT
;
5161 for (; nItem
< nLast
; nItem
++)
5163 lvItem
.iItem
= nItem
;
5164 lvItem
.iSubItem
= 0;
5165 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
5167 if (lvItem
.mask
& LVIF_PARAM
)
5169 if (lpFindInfo
->lParam
== lvItem
.lParam
)
5175 if (lvItem
.mask
& LVIF_TEXT
)
5177 if (lpFindInfo
->flags
& LVFI_PARTIAL
)
5179 if (strstrW(lvItem
.pszText
, lpFindInfo
->psz
) == NULL
) continue;
5183 if (lstrcmpW(lvItem
.pszText
, lpFindInfo
->psz
) != 0) continue;
5187 if (!bNearest
) return nItem
;
5189 /* This is very inefficient. To do a good job here,
5190 * we need a sorted array of (x,y) item positions */
5191 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5193 /* compute the distance^2 to the destination */
5194 xdist
= Destination
.x
- Position
.x
;
5195 ydist
= Destination
.y
- Position
.y
;
5196 dist
= xdist
* xdist
+ ydist
* ydist
;
5198 /* remember the distance, and item if it's closer */
5202 nNearestItem
= nItem
;
5209 nLast
= min(nStart
+ 1, infoPtr
->nItemCount
);
5214 return nNearestItem
;
5219 * Searches for an item with specific characteristics.
5222 * [I] hwnd : window handle
5223 * [I] nStart : base item index
5224 * [I] lpFindInfo : item information to look for
5227 * SUCCESS : index of item
5230 static INT
LISTVIEW_FindItemA(const LISTVIEW_INFO
*infoPtr
, INT nStart
,
5231 const LVFINDINFOA
*lpFindInfo
)
5233 BOOL hasText
= lpFindInfo
->flags
& (LVFI_STRING
| LVFI_PARTIAL
);
5238 memcpy(&fiw
, lpFindInfo
, sizeof(fiw
));
5239 if (hasText
) fiw
.psz
= strW
= textdupTtoW((LPCWSTR
)lpFindInfo
->psz
, FALSE
);
5240 res
= LISTVIEW_FindItemW(infoPtr
, nStart
, &fiw
);
5241 textfreeT(strW
, FALSE
);
5247 * Retrieves the background image of the listview control.
5250 * [I] infoPtr : valid pointer to the listview structure
5251 * [O] lpBkImage : background image attributes
5257 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5259 /* FIXME (listview, "empty stub!\n"); */
5265 * Retrieves column attributes.
5268 * [I] infoPtr : valid pointer to the listview structure
5269 * [I] nColumn : column index
5270 * [IO] lpColumn : column information
5271 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5272 * otherwise it is in fact a LPLVCOLUMNA
5278 static BOOL
LISTVIEW_GetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
, LPLVCOLUMNW lpColumn
, BOOL isW
)
5280 COLUMN_INFO
*lpColumnInfo
;
5283 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5284 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
5286 /* initialize memory */
5287 ZeroMemory(&hdi
, sizeof(hdi
));
5289 if (lpColumn
->mask
& LVCF_TEXT
)
5291 hdi
.mask
|= HDI_TEXT
;
5292 hdi
.pszText
= lpColumn
->pszText
;
5293 hdi
.cchTextMax
= lpColumn
->cchTextMax
;
5296 if (lpColumn
->mask
& LVCF_IMAGE
)
5297 hdi
.mask
|= HDI_IMAGE
;
5299 if (lpColumn
->mask
& LVCF_ORDER
)
5300 hdi
.mask
|= HDI_ORDER
;
5302 if (lpColumn
->mask
& LVCF_SUBITEM
)
5303 hdi
.mask
|= HDI_LPARAM
;
5305 if (!SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_GETITEMW
: HDM_GETITEMA
, nColumn
, (LPARAM
)&hdi
)) return FALSE
;
5307 if (lpColumn
->mask
& LVCF_FMT
)
5308 lpColumn
->fmt
= lpColumnInfo
->fmt
;
5310 if (lpColumn
->mask
& LVCF_WIDTH
)
5311 lpColumn
->cx
= lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
;
5313 if (lpColumn
->mask
& LVCF_IMAGE
)
5314 lpColumn
->iImage
= hdi
.iImage
;
5316 if (lpColumn
->mask
& LVCF_ORDER
)
5317 lpColumn
->iOrder
= hdi
.iOrder
;
5319 if (lpColumn
->mask
& LVCF_SUBITEM
)
5320 lpColumn
->iSubItem
= hdi
.lParam
;
5326 static BOOL
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, LPINT lpiArray
)
5333 /* FIXME: little hack */
5334 for (i
= 0; i
< iCount
; i
++)
5342 * Retrieves the column width.
5345 * [I] infoPtr : valid pointer to the listview structure
5346 * [I] int : column index
5349 * SUCCESS : column width
5352 static INT
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO
*infoPtr
, INT nColumn
)
5354 INT nColumnWidth
= 0;
5357 TRACE("nColumn=%d\n", nColumn
);
5359 /* we have a 'column' in LIST and REPORT mode only */
5360 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
)
5363 nColumnWidth
= infoPtr
->nItemWidth
;
5366 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5367 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5368 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5370 * TODO: should we do the same in LVM_GETCOLUMN?
5372 hdItem
.mask
= HDI_WIDTH
;
5373 if (!SendMessageW(infoPtr
->hwndHeader
, HDM_GETITEMW
, nColumn
, (LPARAM
)&hdItem
))
5375 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr
->hwndSelf
, nColumn
);
5378 nColumnWidth
= hdItem
.cxy
;
5382 TRACE("nColumnWidth=%d\n", nColumnWidth
);
5383 return nColumnWidth
;
5388 * In list or report display mode, retrieves the number of items that can fit
5389 * vertically in the visible area. In icon or small icon display mode,
5390 * retrieves the total number of visible items.
5393 * [I] infoPtr : valid pointer to the listview structure
5396 * Number of fully visible items.
5398 static INT
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO
*infoPtr
)
5400 switch (infoPtr
->dwStyle
& LVS_TYPEMASK
)
5404 return infoPtr
->nItemCount
;
5406 return LISTVIEW_GetCountPerColumn(infoPtr
);
5408 return LISTVIEW_GetCountPerRow(infoPtr
) * LISTVIEW_GetCountPerColumn(infoPtr
);
5416 * Retrieves an image list handle.
5419 * [I] infoPtr : valid pointer to the listview structure
5420 * [I] nImageList : image list identifier
5423 * SUCCESS : image list handle
5426 static HIMAGELIST
LISTVIEW_GetImageList(const LISTVIEW_INFO
*infoPtr
, INT nImageList
)
5430 case LVSIL_NORMAL
: return infoPtr
->himlNormal
;
5431 case LVSIL_SMALL
: return infoPtr
->himlSmall
;
5432 case LVSIL_STATE
: return infoPtr
->himlState
;
5437 /* LISTVIEW_GetISearchString */
5441 * Retrieves item attributes.
5444 * [I] hwnd : window handle
5445 * [IO] lpLVItem : item info
5446 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5447 * if FALSE, then lpLVItem is a LPLVITEMA.
5450 * This is the internal 'GetItem' interface -- it tries to
5451 * be smart and avoid text copies, if possible, by modifying
5452 * lpLVItem->pszText to point to the text string. Please note
5453 * that this is not always possible (e.g. OWNERDATA), so on
5454 * entry you *must* supply valid values for pszText, and cchTextMax.
5455 * The only difference to the documented interface is that upon
5456 * return, you should use *only* the lpLVItem->pszText, rather than
5457 * the buffer pointer you provided on input. Most code already does
5458 * that, so it's not a problem.
5459 * For the two cases when the text must be copied (that is,
5460 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5466 static BOOL
LISTVIEW_GetItemT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5468 ITEMHDR callbackHdr
= { LPSTR_TEXTCALLBACKW
, I_IMAGECALLBACK
};
5469 NMLVDISPINFOW dispInfo
;
5475 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
5477 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5480 if (lpLVItem
->mask
== 0) return TRUE
;
5482 /* make a local copy */
5483 isubitem
= lpLVItem
->iSubItem
;
5485 /* a quick optimization if all we're asked is the focus state
5486 * these queries are worth optimising since they are common,
5487 * and can be answered in constant time, without the heavy accesses */
5488 if ( (lpLVItem
->mask
== LVIF_STATE
) && (lpLVItem
->stateMask
== LVIS_FOCUSED
) &&
5489 !(infoPtr
->uCallbackMask
& LVIS_FOCUSED
) )
5491 lpLVItem
->state
= 0;
5492 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5493 lpLVItem
->state
|= LVIS_FOCUSED
;
5497 ZeroMemory(&dispInfo
, sizeof(dispInfo
));
5499 /* if the app stores all the data, handle it separately */
5500 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
5502 dispInfo
.item
.state
= 0;
5504 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5505 if ((lpLVItem
->mask
& ~(LVIF_STATE
| LVIF_PARAM
)) || infoPtr
->uCallbackMask
)
5507 /* NOTE: copy only fields which we _know_ are initialized, some apps
5508 * depend on the uninitialized fields being 0 */
5509 dispInfo
.item
.mask
= lpLVItem
->mask
& ~LVIF_PARAM
;
5510 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5511 dispInfo
.item
.iSubItem
= isubitem
;
5512 if (lpLVItem
->mask
& LVIF_TEXT
)
5514 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5515 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5517 if (lpLVItem
->mask
& LVIF_STATE
)
5518 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
& infoPtr
->uCallbackMask
;
5519 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5520 dispInfo
.item
.stateMask
= lpLVItem
->stateMask
;
5521 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
5523 /* full size structure expected - _WIN32IE >= 0x560 */
5524 *lpLVItem
= dispInfo
.item
;
5526 else if (lpLVItem
->mask
& LVIF_INDENT
)
5528 /* indent member expected - _WIN32IE >= 0x300 */
5529 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iGroupId
));
5533 /* minimal structure expected */
5534 memcpy(lpLVItem
, &dispInfo
.item
, offsetof( LVITEMW
, iIndent
));
5536 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem
, isW
));
5539 /* make sure lParam is zeroed out */
5540 if (lpLVItem
->mask
& LVIF_PARAM
) lpLVItem
->lParam
= 0;
5542 /* we store only a little state, so if we're not asked, we're done */
5543 if (!(lpLVItem
->mask
& LVIF_STATE
) || isubitem
) return TRUE
;
5545 /* if focus is handled by us, report it */
5546 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5548 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5549 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5550 lpLVItem
->state
|= LVIS_FOCUSED
;
5553 /* and do the same for selection, if we handle it */
5554 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5556 lpLVItem
->state
&= ~LVIS_SELECTED
;
5557 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5558 lpLVItem
->state
|= LVIS_SELECTED
;
5564 /* find the item and subitem structures before we proceed */
5565 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, lpLVItem
->iItem
);
5566 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
5571 SUBITEM_INFO
*lpSubItem
= LISTVIEW_GetSubItemPtr(hdpaSubItems
, isubitem
);
5572 pItemHdr
= lpSubItem
? &lpSubItem
->hdr
: &callbackHdr
;
5575 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem
);
5580 pItemHdr
= &lpItem
->hdr
;
5582 /* Do we need to query the state from the app? */
5583 if ((lpLVItem
->mask
& LVIF_STATE
) && infoPtr
->uCallbackMask
&& isubitem
== 0)
5585 dispInfo
.item
.mask
|= LVIF_STATE
;
5586 dispInfo
.item
.stateMask
= infoPtr
->uCallbackMask
;
5589 /* Do we need to enquire about the image? */
5590 if ((lpLVItem
->mask
& LVIF_IMAGE
) && pItemHdr
->iImage
== I_IMAGECALLBACK
&&
5591 (isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
)))
5593 dispInfo
.item
.mask
|= LVIF_IMAGE
;
5594 dispInfo
.item
.iImage
= I_IMAGECALLBACK
;
5597 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5598 if ((lpLVItem
->mask
& LVIF_TEXT
) && !is_textW(pItemHdr
->pszText
))
5600 dispInfo
.item
.mask
|= LVIF_TEXT
;
5601 dispInfo
.item
.pszText
= lpLVItem
->pszText
;
5602 dispInfo
.item
.cchTextMax
= lpLVItem
->cchTextMax
;
5603 if (dispInfo
.item
.pszText
&& dispInfo
.item
.cchTextMax
> 0)
5604 *dispInfo
.item
.pszText
= '\0';
5607 /* If we don't have all the requested info, query the application */
5608 if (dispInfo
.item
.mask
!= 0)
5610 dispInfo
.item
.iItem
= lpLVItem
->iItem
;
5611 dispInfo
.item
.iSubItem
= lpLVItem
->iSubItem
; /* yes: the original subitem */
5612 dispInfo
.item
.lParam
= lpItem
->lParam
;
5613 notify_dispinfoT(infoPtr
, LVN_GETDISPINFOW
, &dispInfo
, isW
);
5614 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo
.item
, isW
));
5617 /* we should not store values for subitems */
5618 if (isubitem
) dispInfo
.item
.mask
&= ~LVIF_DI_SETITEM
;
5620 /* Now, handle the iImage field */
5621 if (dispInfo
.item
.mask
& LVIF_IMAGE
)
5623 lpLVItem
->iImage
= dispInfo
.item
.iImage
;
5624 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->iImage
== I_IMAGECALLBACK
)
5625 pItemHdr
->iImage
= dispInfo
.item
.iImage
;
5627 else if (lpLVItem
->mask
& LVIF_IMAGE
)
5629 if(isubitem
== 0 || (infoPtr
->dwLvExStyle
& LVS_EX_SUBITEMIMAGES
))
5630 lpLVItem
->iImage
= pItemHdr
->iImage
;
5632 lpLVItem
->iImage
= 0;
5635 /* The pszText field */
5636 if (dispInfo
.item
.mask
& LVIF_TEXT
)
5638 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
) && pItemHdr
->pszText
)
5639 textsetptrT(&pItemHdr
->pszText
, dispInfo
.item
.pszText
, isW
);
5641 lpLVItem
->pszText
= dispInfo
.item
.pszText
;
5643 else if (lpLVItem
->mask
& LVIF_TEXT
)
5645 if (isW
) lpLVItem
->pszText
= pItemHdr
->pszText
;
5646 else textcpynT(lpLVItem
->pszText
, isW
, pItemHdr
->pszText
, TRUE
, lpLVItem
->cchTextMax
);
5649 /* Next is the lParam field */
5650 if (dispInfo
.item
.mask
& LVIF_PARAM
)
5652 lpLVItem
->lParam
= dispInfo
.item
.lParam
;
5653 if ((dispInfo
.item
.mask
& LVIF_DI_SETITEM
))
5654 lpItem
->lParam
= dispInfo
.item
.lParam
;
5656 else if (lpLVItem
->mask
& LVIF_PARAM
)
5657 lpLVItem
->lParam
= lpItem
->lParam
;
5659 /* if this is a subitem, we're done */
5660 if (isubitem
) return TRUE
;
5662 /* ... the state field (this one is different due to uCallbackmask) */
5663 if (lpLVItem
->mask
& LVIF_STATE
)
5665 lpLVItem
->state
= lpItem
->state
& lpLVItem
->stateMask
;
5666 if (dispInfo
.item
.mask
& LVIF_STATE
)
5668 lpLVItem
->state
&= ~dispInfo
.item
.stateMask
;
5669 lpLVItem
->state
|= (dispInfo
.item
.state
& dispInfo
.item
.stateMask
);
5671 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_FOCUSED
)
5673 lpLVItem
->state
&= ~LVIS_FOCUSED
;
5674 if (infoPtr
->nFocusedItem
== lpLVItem
->iItem
)
5675 lpLVItem
->state
|= LVIS_FOCUSED
;
5677 if ( lpLVItem
->stateMask
& ~infoPtr
->uCallbackMask
& LVIS_SELECTED
)
5679 lpLVItem
->state
&= ~LVIS_SELECTED
;
5680 if (ranges_contain(infoPtr
->selectionRanges
, lpLVItem
->iItem
))
5681 lpLVItem
->state
|= LVIS_SELECTED
;
5685 /* and last, but not least, the indent field */
5686 if (lpLVItem
->mask
& LVIF_INDENT
)
5687 lpLVItem
->iIndent
= lpItem
->iIndent
;
5694 * Retrieves item attributes.
5697 * [I] hwnd : window handle
5698 * [IO] lpLVItem : item info
5699 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5700 * if FALSE, then lpLVItem is a LPLVITEMA.
5703 * This is the external 'GetItem' interface -- it properly copies
5704 * the text in the provided buffer.
5710 static BOOL
LISTVIEW_GetItemExtT(const LISTVIEW_INFO
*infoPtr
, LPLVITEMW lpLVItem
, BOOL isW
)
5715 if (!lpLVItem
|| lpLVItem
->iItem
< 0 || lpLVItem
->iItem
>= infoPtr
->nItemCount
)
5718 pszText
= lpLVItem
->pszText
;
5719 bResult
= LISTVIEW_GetItemT(infoPtr
, lpLVItem
, isW
);
5720 if (bResult
&& lpLVItem
->pszText
!= pszText
)
5721 textcpynT(pszText
, isW
, lpLVItem
->pszText
, isW
, lpLVItem
->cchTextMax
);
5722 lpLVItem
->pszText
= pszText
;
5730 * Retrieves the position (upper-left) of the listview control item.
5731 * Note that for LVS_ICON style, the upper-left is that of the icon
5732 * and not the bounding box.
5735 * [I] infoPtr : valid pointer to the listview structure
5736 * [I] nItem : item index
5737 * [O] lpptPosition : coordinate information
5743 static BOOL
LISTVIEW_GetItemPosition(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPPOINT lpptPosition
)
5745 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5748 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem
, lpptPosition
);
5750 if (!lpptPosition
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5752 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5753 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, lpptPosition
);
5755 if (uView
== LVS_ICON
)
5757 lpptPosition
->x
+= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
5758 lpptPosition
->y
+= ICON_TOP_PADDING
;
5760 lpptPosition
->x
+= Origin
.x
;
5761 lpptPosition
->y
+= Origin
.y
;
5763 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition
));
5770 * Retrieves the bounding rectangle for a listview control item.
5773 * [I] infoPtr : valid pointer to the listview structure
5774 * [I] nItem : item index
5775 * [IO] lprc : bounding rectangle coordinates
5776 * lprc->left specifies the portion of the item for which the bounding
5777 * rectangle will be retrieved.
5779 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5780 * including the icon and label.
5783 * * Experiment shows that native control returns:
5784 * * width = min (48, length of text line)
5785 * * .left = position.x - (width - iconsize.cx)/2
5786 * * .right = .left + width
5787 * * height = #lines of text * ntmHeight + icon height + 8
5788 * * .top = position.y - 2
5789 * * .bottom = .top + height
5790 * * separation between items .y = itemSpacing.cy - height
5791 * * .x = itemSpacing.cx - width
5792 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5795 * * Experiment shows that native control returns:
5796 * * width = iconSize.cx + 16
5797 * * .left = position.x - (width - iconsize.cx)/2
5798 * * .right = .left + width
5799 * * height = iconSize.cy + 4
5800 * * .top = position.y - 2
5801 * * .bottom = .top + height
5802 * * separation between items .y = itemSpacing.cy - height
5803 * * .x = itemSpacing.cx - width
5804 * LVIR_LABEL Returns the bounding rectangle of the item text.
5807 * * Experiment shows that native control returns:
5808 * * width = text length
5809 * * .left = position.x - width/2
5810 * * .right = .left + width
5811 * * height = ntmH * linecount + 2
5812 * * .top = position.y + iconSize.cy + 6
5813 * * .bottom = .top + height
5814 * * separation between items .y = itemSpacing.cy - height
5815 * * .x = itemSpacing.cx - width
5816 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5817 * rectangles, but excludes columns in report view.
5824 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5825 * upon whether the window has the focus currently and on whether the item
5826 * is the one with the focus. Ensure that the control's record of which
5827 * item has the focus agrees with the items' records.
5829 static BOOL
LISTVIEW_GetItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5831 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
5832 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5833 BOOL doLabel
= TRUE
, oversizedBox
= FALSE
;
5834 POINT Position
, Origin
;
5837 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr
->hwndSelf
, nItem
, lprc
);
5839 if (!lprc
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
5841 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
5842 LISTVIEW_GetItemOrigin(infoPtr
, nItem
, &Position
);
5844 /* Be smart and try to figure out the minimum we have to do */
5845 if (lprc
->left
== LVIR_ICON
) doLabel
= FALSE
;
5846 if (uView
== LVS_REPORT
&& lprc
->left
== LVIR_BOUNDS
) doLabel
= FALSE
;
5847 if (uView
== LVS_ICON
&& lprc
->left
!= LVIR_ICON
&&
5848 infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_FOCUSED
))
5849 oversizedBox
= TRUE
;
5851 /* get what we need from the item before hand, so we make
5852 * only one request. This can speed up things, if data
5853 * is stored on the app side */
5855 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
5856 if (doLabel
) lvItem
.mask
|= LVIF_TEXT
;
5857 lvItem
.iItem
= nItem
;
5858 lvItem
.iSubItem
= 0;
5859 lvItem
.pszText
= szDispText
;
5860 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5861 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5862 /* we got the state already up, simulate it here, to avoid a reget */
5863 if (uView
== LVS_ICON
&& (lprc
->left
!= LVIR_ICON
))
5865 lvItem
.mask
|= LVIF_STATE
;
5866 lvItem
.stateMask
= LVIS_FOCUSED
;
5867 lvItem
.state
= (oversizedBox
? LVIS_FOCUSED
: 0);
5870 if (uView
== LVS_REPORT
&& (infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) && lprc
->left
== LVIR_SELECTBOUNDS
)
5871 lprc
->left
= LVIR_BOUNDS
;
5875 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5879 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, NULL
, NULL
, lprc
);
5883 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5886 case LVIR_SELECTBOUNDS
:
5887 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, lprc
, NULL
, NULL
, NULL
);
5891 WARN("Unknown value: %d\n", lprc
->left
);
5895 OffsetRect(lprc
, Position
.x
+ Origin
.x
, Position
.y
+ Origin
.y
);
5897 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc
));
5904 * Retrieves the spacing between listview control items.
5907 * [I] infoPtr : valid pointer to the listview structure
5908 * [IO] lprc : rectangle to receive the output
5909 * on input, lprc->top = nSubItem
5910 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5912 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5913 * not only those of the first column.
5914 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5920 static BOOL
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPRECT lprc
)
5926 if (!lprc
) return FALSE
;
5928 nColumn
= lprc
->top
;
5930 TRACE("(nItem=%d, nSubItem=%d)\n", nItem
, lprc
->top
);
5931 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5933 return LISTVIEW_GetItemRect(infoPtr
, nItem
, lprc
);
5935 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) != LVS_REPORT
) return FALSE
;
5937 if (!LISTVIEW_GetItemPosition(infoPtr
, nItem
, &Position
)) return FALSE
;
5939 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
5942 lvItem
.iItem
= nItem
;
5943 lvItem
.iSubItem
= nColumn
;
5945 if (lvItem
.mask
&& !LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return FALSE
;
5949 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, NULL
, NULL
, lprc
, NULL
, NULL
);
5954 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, lprc
, NULL
, NULL
, NULL
, NULL
);
5958 ERR("Unknown bounds=%d\n", lprc
->left
);
5962 OffsetRect(lprc
, Position
.x
, Position
.y
);
5969 * Retrieves the width of a label.
5972 * [I] infoPtr : valid pointer to the listview structure
5975 * SUCCESS : string width (in pixels)
5978 static INT
LISTVIEW_GetLabelWidth(const LISTVIEW_INFO
*infoPtr
, INT nItem
)
5980 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
5983 TRACE("(nItem=%d)\n", nItem
);
5985 lvItem
.mask
= LVIF_TEXT
;
5986 lvItem
.iItem
= nItem
;
5987 lvItem
.iSubItem
= 0;
5988 lvItem
.pszText
= szDispText
;
5989 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
5990 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
5992 return LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
5997 * Retrieves the spacing between listview control items.
6000 * [I] infoPtr : valid pointer to the listview structure
6001 * [I] bSmall : flag for small or large icon
6004 * Horizontal + vertical spacing
6006 static LONG
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO
*infoPtr
, BOOL bSmall
)
6012 lResult
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
6016 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_ICON
)
6017 lResult
= MAKELONG(DEFAULT_COLUMN_WIDTH
, GetSystemMetrics(SM_CXSMICON
)+HEIGHT_PADDING
);
6019 lResult
= MAKELONG(infoPtr
->nItemWidth
, infoPtr
->nItemHeight
);
6026 * Retrieves the state of a listview control item.
6029 * [I] infoPtr : valid pointer to the listview structure
6030 * [I] nItem : item index
6031 * [I] uMask : state mask
6034 * State specified by the mask.
6036 static UINT
LISTVIEW_GetItemState(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uMask
)
6040 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6042 lvItem
.iItem
= nItem
;
6043 lvItem
.iSubItem
= 0;
6044 lvItem
.mask
= LVIF_STATE
;
6045 lvItem
.stateMask
= uMask
;
6046 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return 0;
6048 return lvItem
.state
& uMask
;
6053 * Retrieves the text of a listview control item or subitem.
6056 * [I] hwnd : window handle
6057 * [I] nItem : item index
6058 * [IO] lpLVItem : item information
6059 * [I] isW : TRUE if lpLVItem is Unicode
6062 * SUCCESS : string length
6065 static INT
LISTVIEW_GetItemTextT(const LISTVIEW_INFO
*infoPtr
, INT nItem
, LPLVITEMW lpLVItem
, BOOL isW
)
6067 if (!lpLVItem
|| nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return 0;
6069 lpLVItem
->mask
= LVIF_TEXT
;
6070 lpLVItem
->iItem
= nItem
;
6071 if (!LISTVIEW_GetItemExtT(infoPtr
, lpLVItem
, isW
)) return 0;
6073 return textlenT(lpLVItem
->pszText
, isW
);
6078 * Searches for an item based on properties + relationships.
6081 * [I] infoPtr : valid pointer to the listview structure
6082 * [I] nItem : item index
6083 * [I] uFlags : relationship flag
6086 * SUCCESS : item index
6089 static INT
LISTVIEW_GetNextItem(const LISTVIEW_INFO
*infoPtr
, INT nItem
, UINT uFlags
)
6091 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6093 LVFINDINFOW lvFindInfo
;
6094 INT nCountPerColumn
;
6098 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem
, uFlags
, infoPtr
->nItemCount
);
6099 if (nItem
< -1 || nItem
>= infoPtr
->nItemCount
) return -1;
6101 ZeroMemory(&lvFindInfo
, sizeof(lvFindInfo
));
6103 if (uFlags
& LVNI_CUT
)
6106 if (uFlags
& LVNI_DROPHILITED
)
6107 uMask
|= LVIS_DROPHILITED
;
6109 if (uFlags
& LVNI_FOCUSED
)
6110 uMask
|= LVIS_FOCUSED
;
6112 if (uFlags
& LVNI_SELECTED
)
6113 uMask
|= LVIS_SELECTED
;
6115 /* if we're asked for the focused item, that's only one,
6116 * so it's worth optimizing */
6117 if (uFlags
& LVNI_FOCUSED
)
6119 if ((LISTVIEW_GetItemState(infoPtr
, infoPtr
->nFocusedItem
, uMask
) & uMask
) != uMask
) return -1;
6120 return (infoPtr
->nFocusedItem
== nItem
) ? -1 : infoPtr
->nFocusedItem
;
6123 if (uFlags
& LVNI_ABOVE
)
6125 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6130 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6136 /* Special case for autoarrange - move 'til the top of a list */
6137 if (is_autoarrange(infoPtr
))
6139 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6140 while (nItem
- nCountPerRow
>= 0)
6142 nItem
-= nCountPerRow
;
6143 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6148 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6149 lvFindInfo
.vkDirection
= VK_UP
;
6150 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6151 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6153 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6158 else if (uFlags
& LVNI_BELOW
)
6160 if ((uView
== LVS_LIST
) || (uView
== LVS_REPORT
))
6162 while (nItem
< infoPtr
->nItemCount
)
6165 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6171 /* Special case for autoarrange - move 'til the bottom of a list */
6172 if (is_autoarrange(infoPtr
))
6174 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6175 while (nItem
+ nCountPerRow
< infoPtr
->nItemCount
)
6177 nItem
+= nCountPerRow
;
6178 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6183 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6184 lvFindInfo
.vkDirection
= VK_DOWN
;
6185 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6186 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6188 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6193 else if (uFlags
& LVNI_TOLEFT
)
6195 if (uView
== LVS_LIST
)
6197 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6198 while (nItem
- nCountPerColumn
>= 0)
6200 nItem
-= nCountPerColumn
;
6201 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6205 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6207 /* Special case for autoarrange - move 'ti the beginning of a row */
6208 if (is_autoarrange(infoPtr
))
6210 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6211 while (nItem
% nCountPerRow
> 0)
6214 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6219 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6220 lvFindInfo
.vkDirection
= VK_LEFT
;
6221 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6222 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6224 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6229 else if (uFlags
& LVNI_TORIGHT
)
6231 if (uView
== LVS_LIST
)
6233 nCountPerColumn
= LISTVIEW_GetCountPerColumn(infoPtr
);
6234 while (nItem
+ nCountPerColumn
< infoPtr
->nItemCount
)
6236 nItem
+= nCountPerColumn
;
6237 if ((ListView_GetItemState(infoPtr
->hwndSelf
, nItem
, uMask
) & uMask
) == uMask
)
6241 else if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6243 /* Special case for autoarrange - move 'til the end of a row */
6244 if (is_autoarrange(infoPtr
))
6246 nCountPerRow
= LISTVIEW_GetCountPerRow(infoPtr
);
6247 while (nItem
% nCountPerRow
< nCountPerRow
- 1 )
6250 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6255 lvFindInfo
.flags
= LVFI_NEARESTXY
;
6256 lvFindInfo
.vkDirection
= VK_RIGHT
;
6257 SendMessageW( infoPtr
->hwndSelf
, LVM_GETITEMPOSITION
, nItem
, (LPARAM
)&lvFindInfo
.pt
);
6258 while ((nItem
= ListView_FindItemW(infoPtr
->hwndSelf
, nItem
, &lvFindInfo
)) != -1)
6260 if ((LISTVIEW_GetItemState(infoPtr
, nItem
, uMask
) & uMask
) == uMask
)
6269 /* search by index */
6270 for (i
= nItem
; i
< infoPtr
->nItemCount
; i
++)
6272 if ((LISTVIEW_GetItemState(infoPtr
, i
, uMask
) & uMask
) == uMask
)
6280 /* LISTVIEW_GetNumberOfWorkAreas */
6284 * Retrieves the origin coordinates when in icon or small icon display mode.
6287 * [I] infoPtr : valid pointer to the listview structure
6288 * [O] lpptOrigin : coordinate information
6293 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO
*infoPtr
, LPPOINT lpptOrigin
)
6295 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6296 INT nHorzPos
= 0, nVertPos
= 0;
6297 SCROLLINFO scrollInfo
;
6299 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
6300 scrollInfo
.fMask
= SIF_POS
;
6302 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
))
6303 nHorzPos
= scrollInfo
.nPos
;
6304 if (GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
))
6305 nVertPos
= scrollInfo
.nPos
;
6307 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos
, nVertPos
);
6309 lpptOrigin
->x
= infoPtr
->rcList
.left
;
6310 lpptOrigin
->y
= infoPtr
->rcList
.top
;
6311 if (uView
== LVS_LIST
)
6312 nHorzPos
*= infoPtr
->nItemWidth
;
6313 else if (uView
== LVS_REPORT
)
6314 nVertPos
*= infoPtr
->nItemHeight
;
6316 lpptOrigin
->x
-= nHorzPos
;
6317 lpptOrigin
->y
-= nVertPos
;
6319 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin
));
6324 * Retrieves the width of a string.
6327 * [I] hwnd : window handle
6328 * [I] lpszText : text string to process
6329 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6332 * SUCCESS : string width (in pixels)
6335 static INT
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO
*infoPtr
, LPCWSTR lpszText
, BOOL isW
)
6340 if (is_textT(lpszText
, isW
))
6342 HFONT hFont
= infoPtr
->hFont
? infoPtr
->hFont
: infoPtr
->hDefaultFont
;
6343 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
6344 HFONT hOldFont
= SelectObject(hdc
, hFont
);
6347 GetTextExtentPointW(hdc
, lpszText
, lstrlenW(lpszText
), &stringSize
);
6349 GetTextExtentPointA(hdc
, (LPCSTR
)lpszText
, lstrlenA((LPCSTR
)lpszText
), &stringSize
);
6350 SelectObject(hdc
, hOldFont
);
6351 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
6353 return stringSize
.cx
;
6358 * Determines which listview item is located at the specified position.
6361 * [I] infoPtr : valid pointer to the listview structure
6362 * [IO] lpht : hit test information
6363 * [I] subitem : fill out iSubItem.
6364 * [I] select : return the index only if the hit selects the item
6367 * (mm 20001022): We must not allow iSubItem to be touched, for
6368 * an app might pass only a structure with space up to iItem!
6369 * (MS Office 97 does that for instance in the file open dialog)
6372 * SUCCESS : item index
6375 static INT
LISTVIEW_HitTest(const LISTVIEW_INFO
*infoPtr
, LPLVHITTESTINFO lpht
, BOOL subitem
, BOOL select
)
6377 WCHAR szDispText
[DISP_TEXT_SIZE
] = { '\0' };
6378 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6379 RECT rcBox
, rcBounds
, rcState
, rcIcon
, rcLabel
, rcSearch
;
6380 POINT Origin
, Position
, opt
;
6385 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht
->pt
), subitem
, select
);
6389 if (subitem
) lpht
->iSubItem
= 0;
6391 if (infoPtr
->rcList
.left
> lpht
->pt
.x
)
6392 lpht
->flags
|= LVHT_TOLEFT
;
6393 else if (infoPtr
->rcList
.right
< lpht
->pt
.x
)
6394 lpht
->flags
|= LVHT_TORIGHT
;
6396 if (infoPtr
->rcList
.top
> lpht
->pt
.y
)
6397 lpht
->flags
|= LVHT_ABOVE
;
6398 else if (infoPtr
->rcList
.bottom
< lpht
->pt
.y
)
6399 lpht
->flags
|= LVHT_BELOW
;
6401 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6402 if (lpht
->flags
) return -1;
6404 lpht
->flags
|= LVHT_NOWHERE
;
6406 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
6408 /* first deal with the large items */
6409 rcSearch
.left
= lpht
->pt
.x
;
6410 rcSearch
.top
= lpht
->pt
.y
;
6411 rcSearch
.right
= rcSearch
.left
+ 1;
6412 rcSearch
.bottom
= rcSearch
.top
+ 1;
6414 iterator_frameditems(&i
, infoPtr
, &rcSearch
);
6415 iterator_next(&i
); /* go to first item in the sequence */
6417 iterator_destroy(&i
);
6419 TRACE("lpht->iItem=%d\n", iItem
);
6420 if (iItem
== -1) return -1;
6422 lvItem
.mask
= LVIF_STATE
| LVIF_TEXT
;
6423 if (uView
== LVS_REPORT
) lvItem
.mask
|= LVIF_INDENT
;
6424 lvItem
.stateMask
= LVIS_STATEIMAGEMASK
;
6425 if (uView
== LVS_ICON
) lvItem
.stateMask
|= LVIS_FOCUSED
;
6426 lvItem
.iItem
= iItem
;
6427 lvItem
.iSubItem
= 0;
6428 lvItem
.pszText
= szDispText
;
6429 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
6430 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) return -1;
6431 if (!infoPtr
->bFocus
) lvItem
.state
&= ~LVIS_FOCUSED
;
6433 LISTVIEW_GetItemMetrics(infoPtr
, &lvItem
, &rcBox
, NULL
, &rcIcon
, &rcState
, &rcLabel
);
6434 LISTVIEW_GetItemOrigin(infoPtr
, iItem
, &Position
);
6435 opt
.x
= lpht
->pt
.x
- Position
.x
- Origin
.x
;
6436 opt
.y
= lpht
->pt
.y
- Position
.y
- Origin
.y
;
6438 if (uView
== LVS_REPORT
)
6442 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6443 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6445 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds
));
6446 if (!PtInRect(&rcBounds
, opt
)) return -1;
6448 if (PtInRect(&rcIcon
, opt
))
6449 lpht
->flags
|= LVHT_ONITEMICON
;
6450 else if (PtInRect(&rcLabel
, opt
))
6451 lpht
->flags
|= LVHT_ONITEMLABEL
;
6452 else if (infoPtr
->himlState
&& STATEIMAGEINDEX(lvItem
.state
) && PtInRect(&rcState
, opt
))
6453 lpht
->flags
|= LVHT_ONITEMSTATEICON
;
6454 if (lpht
->flags
& LVHT_ONITEM
)
6455 lpht
->flags
&= ~LVHT_NOWHERE
;
6457 TRACE("lpht->flags=0x%x\n", lpht
->flags
);
6458 if (uView
== LVS_REPORT
&& subitem
)
6462 rcBounds
.right
= rcBounds
.left
;
6463 for (j
= 0; j
< DPA_GetPtrCount(infoPtr
->hdpaColumns
); j
++)
6465 rcBounds
.left
= rcBounds
.right
;
6466 rcBounds
.right
+= LISTVIEW_GetColumnWidth(infoPtr
, j
);
6467 if (PtInRect(&rcBounds
, opt
))
6475 if (select
&& !(uView
== LVS_REPORT
&&
6476 ((infoPtr
->dwLvExStyle
& LVS_EX_FULLROWSELECT
) ||
6477 (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
))))
6479 if (uView
== LVS_REPORT
)
6481 UnionRect(&rcBounds
, &rcIcon
, &rcLabel
);
6482 UnionRect(&rcBounds
, &rcBounds
, &rcState
);
6484 if (!PtInRect(&rcBounds
, opt
)) iItem
= -1;
6486 return lpht
->iItem
= iItem
;
6490 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6491 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6492 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6493 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6494 their own sort proc. when sending LVM_SORTITEMS.
6497 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6499 LVS_SORTXXX must be specified,
6500 LVS_OWNERDRAW is not set,
6501 <item>.pszText is not LPSTR_TEXTCALLBACK.
6503 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6504 are sorted based on item text..."
6506 static INT WINAPI
LISTVIEW_InsertCompare( LPVOID first
, LPVOID second
, LPARAM lParam
)
6508 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
6509 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
6510 INT cmpv
= textcmpWT(lv_first
->hdr
.pszText
, lv_second
->hdr
.pszText
, TRUE
);
6512 /* if we're sorting descending, negate the return value */
6513 return (((const LISTVIEW_INFO
*)lParam
)->dwStyle
& LVS_SORTDESCENDING
) ? -cmpv
: cmpv
;
6518 * Inserts a new item in the listview control.
6521 * [I] infoPtr : valid pointer to the listview structure
6522 * [I] lpLVItem : item information
6523 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6526 * SUCCESS : new item index
6529 static INT
LISTVIEW_InsertItemT(LISTVIEW_INFO
*infoPtr
, const LVITEMW
*lpLVItem
, BOOL isW
)
6531 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6536 BOOL is_sorted
, has_changed
;
6538 HWND hwndSelf
= infoPtr
->hwndSelf
;
6540 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem
, isW
), isW
);
6542 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return infoPtr
->nItemCount
++;
6544 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6545 if (!lpLVItem
|| lpLVItem
->iSubItem
) return -1;
6547 if (!is_assignable_item(lpLVItem
, infoPtr
->dwStyle
)) return -1;
6549 if (!(lpItem
= Alloc(sizeof(ITEM_INFO
)))) return -1;
6551 /* insert item in listview control data structure */
6552 if ( !(hdpaSubItems
= DPA_Create(8)) ) goto fail
;
6553 if ( !DPA_SetPtr(hdpaSubItems
, 0, lpItem
) ) assert (FALSE
);
6555 is_sorted
= (infoPtr
->dwStyle
& (LVS_SORTASCENDING
| LVS_SORTDESCENDING
)) &&
6556 !(infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (LPSTR_TEXTCALLBACKW
!= lpLVItem
->pszText
);
6558 if (lpLVItem
->iItem
< 0 && !is_sorted
) return -1;
6560 nItem
= is_sorted
? infoPtr
->nItemCount
: min(lpLVItem
->iItem
, infoPtr
->nItemCount
);
6561 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem
, is_sorted
, infoPtr
->nItemCount
, lpLVItem
->iItem
);
6562 nItem
= DPA_InsertPtr( infoPtr
->hdpaItems
, nItem
, hdpaSubItems
);
6563 if (nItem
== -1) goto fail
;
6564 infoPtr
->nItemCount
++;
6566 /* shift indices first so they don't get tangled */
6567 LISTVIEW_ShiftIndices(infoPtr
, nItem
, 1);
6569 /* set the item attributes */
6570 if (lpLVItem
->mask
& (LVIF_GROUPID
|LVIF_COLUMNS
))
6572 /* full size structure expected - _WIN32IE >= 0x560 */
6575 else if (lpLVItem
->mask
& LVIF_INDENT
)
6577 /* indent member expected - _WIN32IE >= 0x300 */
6578 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iGroupId
));
6582 /* minimal structure expected */
6583 memcpy(&item
, lpLVItem
, offsetof( LVITEMW
, iIndent
));
6586 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
6588 item
.mask
|= LVIF_STATE
;
6589 item
.stateMask
|= LVIS_STATEIMAGEMASK
;
6590 item
.state
&= ~LVIS_STATEIMAGEMASK
;
6591 item
.state
|= INDEXTOSTATEIMAGEMASK(1);
6593 if (!set_main_item(infoPtr
, &item
, TRUE
, isW
, &has_changed
)) goto undo
;
6595 /* if we're sorted, sort the list, and update the index */
6598 DPA_Sort( infoPtr
->hdpaItems
, LISTVIEW_InsertCompare
, (LPARAM
)infoPtr
);
6599 nItem
= DPA_GetPtrIndex( infoPtr
->hdpaItems
, hdpaSubItems
);
6600 assert(nItem
!= -1);
6603 /* make room for the position, if we are in the right mode */
6604 if ((uView
== LVS_SMALLICON
) || (uView
== LVS_ICON
))
6606 if (DPA_InsertPtr(infoPtr
->hdpaPosX
, nItem
, 0) == -1)
6608 if (DPA_InsertPtr(infoPtr
->hdpaPosY
, nItem
, 0) == -1)
6610 DPA_DeletePtr(infoPtr
->hdpaPosX
, nItem
);
6615 /* send LVN_INSERTITEM notification */
6616 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
6618 nmlv
.lParam
= lpItem
->lParam
;
6619 notify_listview(infoPtr
, LVN_INSERTITEM
, &nmlv
);
6620 if (!IsWindow(hwndSelf
))
6623 /* align items (set position of each item) */
6624 if ((uView
== LVS_SMALLICON
|| uView
== LVS_ICON
))
6628 if (infoPtr
->dwStyle
& LVS_ALIGNLEFT
)
6629 LISTVIEW_NextIconPosLeft(infoPtr
, &pt
);
6631 LISTVIEW_NextIconPosTop(infoPtr
, &pt
);
6633 LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, TRUE
);
6636 /* now is the invalidation fun */
6637 LISTVIEW_ScrollOnInsert(infoPtr
, nItem
, 1);
6641 LISTVIEW_ShiftIndices(infoPtr
, nItem
, -1);
6642 DPA_DeletePtr(infoPtr
->hdpaItems
, nItem
);
6643 infoPtr
->nItemCount
--;
6645 DPA_DeletePtr(hdpaSubItems
, 0);
6646 DPA_Destroy (hdpaSubItems
);
6653 * Redraws a range of items.
6656 * [I] infoPtr : valid pointer to the listview structure
6657 * [I] nFirst : first item
6658 * [I] nLast : last item
6664 static BOOL
LISTVIEW_RedrawItems(const LISTVIEW_INFO
*infoPtr
, INT nFirst
, INT nLast
)
6668 if (nLast
< nFirst
|| min(nFirst
, nLast
) < 0 ||
6669 max(nFirst
, nLast
) >= infoPtr
->nItemCount
)
6672 for (i
= nFirst
; i
<= nLast
; i
++)
6673 LISTVIEW_InvalidateItem(infoPtr
, i
);
6680 * Scroll the content of a listview.
6683 * [I] infoPtr : valid pointer to the listview structure
6684 * [I] dx : horizontal scroll amount in pixels
6685 * [I] dy : vertical scroll amount in pixels
6692 * If the control is in report mode (LVS_REPORT) the control can
6693 * be scrolled only in line increments. "dy" will be rounded to the
6694 * nearest number of pixels that are a whole line. Ex: if line height
6695 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6696 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6698 * For: (per experimentation with native control and CSpy ListView)
6699 * LVS_ICON dy=1 = 1 pixel (vertical only)
6701 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6703 * LVS_LIST dx=1 = 1 column (horizontal only)
6704 * but will only scroll 1 column per message
6705 * no matter what the value.
6706 * dy must be 0 or FALSE returned.
6707 * LVS_REPORT dx=1 = 1 pixel
6711 static BOOL
LISTVIEW_Scroll(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
6713 switch(infoPtr
->dwStyle
& LVS_TYPEMASK
) {
6715 dy
+= (dy
< 0 ? -1 : 1) * infoPtr
->nItemHeight
/2;
6716 dy
/= infoPtr
->nItemHeight
;
6719 if (dy
!= 0) return FALSE
;
6726 if (dx
!= 0) LISTVIEW_HScroll(infoPtr
, SB_INTERNAL
, dx
, 0);
6727 if (dy
!= 0) LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, dy
, 0);
6734 * Sets the background color.
6737 * [I] infoPtr : valid pointer to the listview structure
6738 * [I] clrBk : background color
6744 static BOOL
LISTVIEW_SetBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrBk
)
6746 TRACE("(clrBk=%x)\n", clrBk
);
6748 if(infoPtr
->clrBk
!= clrBk
) {
6749 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
6750 infoPtr
->clrBk
= clrBk
;
6751 if (clrBk
== CLR_NONE
)
6752 infoPtr
->hBkBrush
= (HBRUSH
)GetClassLongPtrW(infoPtr
->hwndSelf
, GCLP_HBRBACKGROUND
);
6754 infoPtr
->hBkBrush
= CreateSolidBrush(clrBk
);
6755 LISTVIEW_InvalidateList(infoPtr
);
6761 /* LISTVIEW_SetBkImage */
6763 /*** Helper for {Insert,Set}ColumnT *only* */
6764 static void column_fill_hditem(const LISTVIEW_INFO
*infoPtr
, HDITEMW
*lphdi
, INT nColumn
,
6765 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6767 if (lpColumn
->mask
& LVCF_FMT
)
6769 /* format member is valid */
6770 lphdi
->mask
|= HDI_FORMAT
;
6772 /* set text alignment (leftmost column must be left-aligned) */
6773 if (nColumn
== 0 || (lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
6774 lphdi
->fmt
|= HDF_LEFT
;
6775 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_RIGHT
)
6776 lphdi
->fmt
|= HDF_RIGHT
;
6777 else if ((lpColumn
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_CENTER
)
6778 lphdi
->fmt
|= HDF_CENTER
;
6780 if (lpColumn
->fmt
& LVCFMT_BITMAP_ON_RIGHT
)
6781 lphdi
->fmt
|= HDF_BITMAP_ON_RIGHT
;
6783 if (lpColumn
->fmt
& LVCFMT_COL_HAS_IMAGES
)
6785 lphdi
->fmt
|= HDF_IMAGE
;
6786 lphdi
->iImage
= I_IMAGECALLBACK
;
6790 if (lpColumn
->mask
& LVCF_WIDTH
)
6792 lphdi
->mask
|= HDI_WIDTH
;
6793 if(lpColumn
->cx
== LVSCW_AUTOSIZE_USEHEADER
)
6795 /* make it fill the remainder of the controls width */
6799 for(item_index
= 0; item_index
< (nColumn
- 1); item_index
++)
6801 LISTVIEW_GetHeaderRect(infoPtr
, item_index
, &rcHeader
);
6802 lphdi
->cxy
+= rcHeader
.right
- rcHeader
.left
;
6805 /* retrieve the layout of the header */
6806 GetClientRect(infoPtr
->hwndSelf
, &rcHeader
);
6807 TRACE("start cxy=%d rcHeader=%s\n", lphdi
->cxy
, wine_dbgstr_rect(&rcHeader
));
6809 lphdi
->cxy
= (rcHeader
.right
- rcHeader
.left
) - lphdi
->cxy
;
6812 lphdi
->cxy
= lpColumn
->cx
;
6815 if (lpColumn
->mask
& LVCF_TEXT
)
6817 lphdi
->mask
|= HDI_TEXT
| HDI_FORMAT
;
6818 lphdi
->fmt
|= HDF_STRING
;
6819 lphdi
->pszText
= lpColumn
->pszText
;
6820 lphdi
->cchTextMax
= textlenT(lpColumn
->pszText
, isW
);
6823 if (lpColumn
->mask
& LVCF_IMAGE
)
6825 lphdi
->mask
|= HDI_IMAGE
;
6826 lphdi
->iImage
= lpColumn
->iImage
;
6829 if (lpColumn
->mask
& LVCF_ORDER
)
6831 lphdi
->mask
|= HDI_ORDER
;
6832 lphdi
->iOrder
= lpColumn
->iOrder
;
6839 * Inserts a new column.
6842 * [I] infoPtr : valid pointer to the listview structure
6843 * [I] nColumn : column index
6844 * [I] lpColumn : column information
6845 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6848 * SUCCESS : new column index
6851 static INT
LISTVIEW_InsertColumnT(LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6852 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6854 COLUMN_INFO
*lpColumnInfo
;
6858 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6860 if (!lpColumn
|| nColumn
< 0) return -1;
6861 nColumn
= min(nColumn
, DPA_GetPtrCount(infoPtr
->hdpaColumns
));
6863 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6864 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6867 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6868 * (can be seen in SPY) otherwise column never gets added.
6870 if (!(lpColumn
->mask
& LVCF_WIDTH
)) {
6871 hdi
.mask
|= HDI_WIDTH
;
6876 * when the iSubItem is available Windows copies it to the header lParam. It seems
6877 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6879 if (lpColumn
->mask
& LVCF_SUBITEM
)
6881 hdi
.mask
|= HDI_LPARAM
;
6882 hdi
.lParam
= lpColumn
->iSubItem
;
6885 /* insert item in header control */
6886 nNewColumn
= SendMessageW(infoPtr
->hwndHeader
,
6887 isW
? HDM_INSERTITEMW
: HDM_INSERTITEMA
,
6888 (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6889 if (nNewColumn
== -1) return -1;
6890 if (nNewColumn
!= nColumn
) ERR("nColumn=%d, nNewColumn=%d\n", nColumn
, nNewColumn
);
6892 /* create our own column info */
6893 if (!(lpColumnInfo
= Alloc(sizeof(COLUMN_INFO
)))) goto fail
;
6894 if (DPA_InsertPtr(infoPtr
->hdpaColumns
, nNewColumn
, lpColumnInfo
) == -1) goto fail
;
6896 if (lpColumn
->mask
& LVCF_FMT
) lpColumnInfo
->fmt
= lpColumn
->fmt
;
6897 if (!Header_GetItemRect(infoPtr
->hwndHeader
, nNewColumn
, &lpColumnInfo
->rcHeader
)) goto fail
;
6899 /* now we have to actually adjust the data */
6900 if (!(infoPtr
->dwStyle
& LVS_OWNERDATA
) && infoPtr
->nItemCount
> 0)
6902 SUBITEM_INFO
*lpSubItem
;
6906 for (nItem
= 0; nItem
< infoPtr
->nItemCount
; nItem
++)
6908 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, nItem
);
6909 for (i
= 1; i
< DPA_GetPtrCount(hdpaSubItems
); i
++)
6911 lpSubItem
= DPA_GetPtr(hdpaSubItems
, i
);
6912 if (lpSubItem
->iSubItem
>= nNewColumn
)
6913 lpSubItem
->iSubItem
++;
6918 /* make space for the new column */
6919 LISTVIEW_ScrollColumns(infoPtr
, nNewColumn
+ 1, lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
6920 LISTVIEW_UpdateItemSize(infoPtr
);
6925 if (nNewColumn
!= -1) SendMessageW(infoPtr
->hwndHeader
, HDM_DELETEITEM
, nNewColumn
, 0);
6928 DPA_DeletePtr(infoPtr
->hdpaColumns
, nNewColumn
);
6936 * Sets the attributes of a header item.
6939 * [I] infoPtr : valid pointer to the listview structure
6940 * [I] nColumn : column index
6941 * [I] lpColumn : column attributes
6942 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6948 static BOOL
LISTVIEW_SetColumnT(const LISTVIEW_INFO
*infoPtr
, INT nColumn
,
6949 const LVCOLUMNW
*lpColumn
, BOOL isW
)
6951 HDITEMW hdi
, hdiget
;
6954 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn
, debuglvcolumn_t(lpColumn
, isW
), isW
);
6956 if (!lpColumn
|| nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
6958 ZeroMemory(&hdi
, sizeof(HDITEMW
));
6959 if (lpColumn
->mask
& LVCF_FMT
)
6961 hdi
.mask
|= HDI_FORMAT
;
6962 hdiget
.mask
= HDI_FORMAT
;
6963 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdiget
))
6964 hdi
.fmt
= hdiget
.fmt
& HDF_STRING
;
6966 column_fill_hditem(infoPtr
, &hdi
, nColumn
, lpColumn
, isW
);
6968 /* set header item attributes */
6969 bResult
= SendMessageW(infoPtr
->hwndHeader
, isW
? HDM_SETITEMW
: HDM_SETITEMA
, (WPARAM
)nColumn
, (LPARAM
)&hdi
);
6970 if (!bResult
) return FALSE
;
6972 if (lpColumn
->mask
& LVCF_FMT
)
6974 COLUMN_INFO
*lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, nColumn
);
6975 int oldFmt
= lpColumnInfo
->fmt
;
6977 lpColumnInfo
->fmt
= lpColumn
->fmt
;
6978 if ((oldFmt
^ lpColumn
->fmt
) & (LVCFMT_JUSTIFYMASK
| LVCFMT_IMAGE
))
6980 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
6981 if (uView
== LVS_REPORT
) LISTVIEW_InvalidateColumn(infoPtr
, nColumn
);
6990 * Sets the column order array
6993 * [I] infoPtr : valid pointer to the listview structure
6994 * [I] iCount : number of elements in column order array
6995 * [I] lpiArray : pointer to column order array
7001 static BOOL
LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO
*infoPtr
, INT iCount
, const INT
*lpiArray
)
7003 FIXME("iCount %d lpiArray %p\n", iCount
, lpiArray
);
7014 * Sets the width of a column
7017 * [I] infoPtr : valid pointer to the listview structure
7018 * [I] nColumn : column index
7019 * [I] cx : column width
7025 static BOOL
LISTVIEW_SetColumnWidth(LISTVIEW_INFO
*infoPtr
, INT nColumn
, INT cx
)
7027 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7028 WCHAR szDispText
[DISP_TEXT_SIZE
] = { 0 };
7032 TRACE("(nColumn=%d, cx=%d\n", nColumn
, cx
);
7034 /* set column width only if in report or list mode */
7035 if (uView
!= LVS_REPORT
&& uView
!= LVS_LIST
) return FALSE
;
7037 /* take care of invalid cx values */
7038 if(uView
== LVS_REPORT
&& cx
< -2) cx
= LVSCW_AUTOSIZE
;
7039 else if (uView
== LVS_LIST
&& cx
< 1) return FALSE
;
7041 /* resize all columns if in LVS_LIST mode */
7042 if(uView
== LVS_LIST
)
7044 infoPtr
->nItemWidth
= cx
;
7045 LISTVIEW_InvalidateList(infoPtr
);
7049 if (nColumn
< 0 || nColumn
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return FALSE
;
7051 if (cx
== LVSCW_AUTOSIZE
|| (cx
== LVSCW_AUTOSIZE_USEHEADER
&& nColumn
< DPA_GetPtrCount(infoPtr
->hdpaColumns
) -1))
7056 lvItem
.mask
= LVIF_TEXT
;
7058 lvItem
.iSubItem
= nColumn
;
7059 lvItem
.pszText
= szDispText
;
7060 lvItem
.cchTextMax
= DISP_TEXT_SIZE
;
7061 for (; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7063 if (!LISTVIEW_GetItemW(infoPtr
, &lvItem
)) continue;
7064 nLabelWidth
= LISTVIEW_GetStringWidthT(infoPtr
, lvItem
.pszText
, TRUE
);
7065 if (max_cx
< nLabelWidth
) max_cx
= nLabelWidth
;
7067 if (infoPtr
->himlSmall
&& (nColumn
== 0 || (LISTVIEW_GetColumnInfo(infoPtr
, nColumn
)->fmt
& LVCFMT_IMAGE
)))
7068 max_cx
+= infoPtr
->iconSize
.cx
;
7069 max_cx
+= TRAILING_LABEL_PADDING
;
7072 /* autosize based on listview items width */
7073 if(cx
== LVSCW_AUTOSIZE
)
7075 else if(cx
== LVSCW_AUTOSIZE_USEHEADER
)
7077 /* if iCol is the last column make it fill the remainder of the controls width */
7078 if(nColumn
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1)
7083 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7084 LISTVIEW_GetHeaderRect(infoPtr
, nColumn
, &rcHeader
);
7086 cx
= infoPtr
->rcList
.right
- Origin
.x
- rcHeader
.left
;
7090 /* Despite what the MS docs say, if this is not the last
7091 column, then MS resizes the column to the width of the
7092 largest text string in the column, including headers
7093 and items. This is different from LVSCW_AUTOSIZE in that
7094 LVSCW_AUTOSIZE ignores the header string length. */
7097 /* retrieve header text */
7098 hdi
.mask
= HDI_TEXT
;
7099 hdi
.cchTextMax
= DISP_TEXT_SIZE
;
7100 hdi
.pszText
= szDispText
;
7101 if (Header_GetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
))
7103 HDC hdc
= GetDC(infoPtr
->hwndSelf
);
7104 HFONT old_font
= SelectObject(hdc
, (HFONT
)SendMessageW(infoPtr
->hwndHeader
, WM_GETFONT
, 0, 0));
7107 if (GetTextExtentPoint32W(hdc
, hdi
.pszText
, lstrlenW(hdi
.pszText
), &size
))
7108 cx
= size
.cx
+ TRAILING_HEADER_PADDING
;
7109 /* FIXME: Take into account the header image, if one is present */
7110 SelectObject(hdc
, old_font
);
7111 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7113 cx
= max (cx
, max_cx
);
7117 if (cx
< 0) return FALSE
;
7119 /* call header to update the column change */
7120 hdi
.mask
= HDI_WIDTH
;
7122 TRACE("hdi.cxy=%d\n", hdi
.cxy
);
7123 return Header_SetItemW(infoPtr
->hwndHeader
, nColumn
, &hdi
);
7127 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7130 static HIMAGELIST
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO
*infoPtr
)
7133 HBITMAP hbm_im
, hbm_mask
, hbm_orig
;
7135 HBRUSH hbr_white
= GetStockObject(WHITE_BRUSH
);
7136 HBRUSH hbr_black
= GetStockObject(BLACK_BRUSH
);
7139 himl
= ImageList_Create(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
),
7140 ILC_COLOR
| ILC_MASK
, 2, 2);
7141 hdc_wnd
= GetDC(infoPtr
->hwndSelf
);
7142 hdc
= CreateCompatibleDC(hdc_wnd
);
7143 hbm_im
= CreateCompatibleBitmap(hdc_wnd
, GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
));
7144 hbm_mask
= CreateBitmap(GetSystemMetrics(SM_CXSMICON
), GetSystemMetrics(SM_CYSMICON
), 1, 1, NULL
);
7145 ReleaseDC(infoPtr
->hwndSelf
, hdc_wnd
);
7147 rc
.left
= rc
.top
= 0;
7148 rc
.right
= GetSystemMetrics(SM_CXSMICON
);
7149 rc
.bottom
= GetSystemMetrics(SM_CYSMICON
);
7151 hbm_orig
= SelectObject(hdc
, hbm_mask
);
7152 FillRect(hdc
, &rc
, hbr_white
);
7153 InflateRect(&rc
, -3, -3);
7154 FillRect(hdc
, &rc
, hbr_black
);
7156 SelectObject(hdc
, hbm_im
);
7157 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
);
7158 SelectObject(hdc
, hbm_orig
);
7159 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7161 SelectObject(hdc
, hbm_im
);
7162 DrawFrameControl(hdc
, &rc
, DFC_BUTTON
, DFCS_BUTTONCHECK
| DFCS_MONO
| DFCS_CHECKED
);
7163 SelectObject(hdc
, hbm_orig
);
7164 ImageList_Add(himl
, hbm_im
, hbm_mask
);
7166 DeleteObject(hbm_mask
);
7167 DeleteObject(hbm_im
);
7175 * Sets the extended listview style.
7178 * [I] infoPtr : valid pointer to the listview structure
7180 * [I] dwStyle : style
7183 * SUCCESS : previous style
7186 static DWORD
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO
*infoPtr
, DWORD dwMask
, DWORD dwExStyle
)
7188 DWORD dwOldExStyle
= infoPtr
->dwLvExStyle
;
7192 infoPtr
->dwLvExStyle
= (dwOldExStyle
& ~dwMask
) | (dwExStyle
& dwMask
);
7194 infoPtr
->dwLvExStyle
= dwExStyle
;
7196 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_CHECKBOXES
)
7198 HIMAGELIST himl
= 0;
7199 if(infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
7202 item
.mask
= LVIF_STATE
;
7203 item
.stateMask
= LVIS_STATEIMAGEMASK
;
7204 item
.state
= INDEXTOSTATEIMAGEMASK(1);
7205 LISTVIEW_SetItemState(infoPtr
, -1, &item
);
7207 himl
= LISTVIEW_CreateCheckBoxIL(infoPtr
);
7209 LISTVIEW_SetImageList(infoPtr
, LVSIL_STATE
, himl
);
7212 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_HEADERDRAGDROP
)
7214 DWORD dwStyle
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
7215 if (infoPtr
->dwLvExStyle
& LVS_EX_HEADERDRAGDROP
)
7216 dwStyle
|= HDS_DRAGDROP
;
7218 dwStyle
&= ~HDS_DRAGDROP
;
7219 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, dwStyle
);
7222 /* GRIDLINES adds decoration at top so changes sizes */
7223 if((infoPtr
->dwLvExStyle
^ dwOldExStyle
) & LVS_EX_GRIDLINES
)
7225 LISTVIEW_UpdateSize(infoPtr
);
7229 LISTVIEW_InvalidateList(infoPtr
);
7230 return dwOldExStyle
;
7235 * Sets the new hot cursor used during hot tracking and hover selection.
7238 * [I] infoPtr : valid pointer to the listview structure
7239 * [I] hCursor : the new hot cursor handle
7242 * Returns the previous hot cursor
7244 static HCURSOR
LISTVIEW_SetHotCursor(LISTVIEW_INFO
*infoPtr
, HCURSOR hCursor
)
7246 HCURSOR oldCursor
= infoPtr
->hHotCursor
;
7248 infoPtr
->hHotCursor
= hCursor
;
7256 * Sets the hot item index.
7259 * [I] infoPtr : valid pointer to the listview structure
7260 * [I] iIndex : index
7263 * SUCCESS : previous hot item index
7264 * FAILURE : -1 (no hot item)
7266 static INT
LISTVIEW_SetHotItem(LISTVIEW_INFO
*infoPtr
, INT iIndex
)
7268 INT iOldIndex
= infoPtr
->nHotItem
;
7270 infoPtr
->nHotItem
= iIndex
;
7278 * Sets the amount of time the cursor must hover over an item before it is selected.
7281 * [I] infoPtr : valid pointer to the listview structure
7282 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7285 * Returns the previous hover time
7287 static DWORD
LISTVIEW_SetHoverTime(LISTVIEW_INFO
*infoPtr
, DWORD dwHoverTime
)
7289 DWORD oldHoverTime
= infoPtr
->dwHoverTime
;
7291 infoPtr
->dwHoverTime
= dwHoverTime
;
7293 return oldHoverTime
;
7298 * Sets spacing for icons of LVS_ICON style.
7301 * [I] infoPtr : valid pointer to the listview structure
7302 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7303 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7306 * MAKELONG(oldcx, oldcy)
7308 static DWORD
LISTVIEW_SetIconSpacing(LISTVIEW_INFO
*infoPtr
, INT cx
, INT cy
)
7310 DWORD oldspacing
= MAKELONG(infoPtr
->iconSpacing
.cx
, infoPtr
->iconSpacing
.cy
);
7311 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7313 TRACE("requested=(%d,%d)\n", cx
, cy
);
7315 /* this is supported only for LVS_ICON style */
7316 if (uView
!= LVS_ICON
) return oldspacing
;
7318 /* set to defaults, if instructed to */
7319 if (cx
== -1) cx
= GetSystemMetrics(SM_CXICONSPACING
);
7320 if (cy
== -1) cy
= GetSystemMetrics(SM_CYICONSPACING
);
7322 /* if 0 then compute width
7323 * FIXME: Should scan each item and determine max width of
7324 * icon or label, then make that the width */
7326 cx
= infoPtr
->iconSpacing
.cx
;
7328 /* if 0 then compute height */
7330 cy
= infoPtr
->iconSize
.cy
+ 2 * infoPtr
->ntmHeight
+
7331 ICON_BOTTOM_PADDING
+ ICON_TOP_PADDING
+ LABEL_VERT_PADDING
;
7334 infoPtr
->iconSpacing
.cx
= cx
;
7335 infoPtr
->iconSpacing
.cy
= cy
;
7337 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7338 LOWORD(oldspacing
), HIWORD(oldspacing
), cx
, cy
,
7339 infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
,
7340 infoPtr
->ntmHeight
);
7342 /* these depend on the iconSpacing */
7343 LISTVIEW_UpdateItemSize(infoPtr
);
7348 static inline void set_icon_size(SIZE
*size
, HIMAGELIST himl
, BOOL small
)
7352 if (himl
&& ImageList_GetIconSize(himl
, &cx
, &cy
))
7359 size
->cx
= GetSystemMetrics(small
? SM_CXSMICON
: SM_CXICON
);
7360 size
->cy
= GetSystemMetrics(small
? SM_CYSMICON
: SM_CYICON
);
7369 * [I] infoPtr : valid pointer to the listview structure
7370 * [I] nType : image list type
7371 * [I] himl : image list handle
7374 * SUCCESS : old image list
7377 static HIMAGELIST
LISTVIEW_SetImageList(LISTVIEW_INFO
*infoPtr
, INT nType
, HIMAGELIST himl
)
7379 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7380 INT oldHeight
= infoPtr
->nItemHeight
;
7381 HIMAGELIST himlOld
= 0;
7383 TRACE("(nType=%d, himl=%p\n", nType
, himl
);
7388 himlOld
= infoPtr
->himlNormal
;
7389 infoPtr
->himlNormal
= himl
;
7390 if (uView
== LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, FALSE
);
7391 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
7395 himlOld
= infoPtr
->himlSmall
;
7396 infoPtr
->himlSmall
= himl
;
7397 if (uView
!= LVS_ICON
) set_icon_size(&infoPtr
->iconSize
, himl
, TRUE
);
7401 himlOld
= infoPtr
->himlState
;
7402 infoPtr
->himlState
= himl
;
7403 set_icon_size(&infoPtr
->iconStateSize
, himl
, TRUE
);
7404 ImageList_SetBkColor(infoPtr
->himlState
, CLR_NONE
);
7408 ERR("Unknown icon type=%d\n", nType
);
7412 infoPtr
->nItemHeight
= LISTVIEW_CalculateItemHeight(infoPtr
);
7413 if (infoPtr
->nItemHeight
!= oldHeight
)
7414 LISTVIEW_UpdateScroll(infoPtr
);
7421 * Preallocates memory (does *not* set the actual count of items !)
7424 * [I] infoPtr : valid pointer to the listview structure
7425 * [I] nItems : item count (projected number of items to allocate)
7426 * [I] dwFlags : update flags
7432 static BOOL
LISTVIEW_SetItemCount(LISTVIEW_INFO
*infoPtr
, INT nItems
, DWORD dwFlags
)
7434 TRACE("(nItems=%d, dwFlags=%x)\n", nItems
, dwFlags
);
7436 if (infoPtr
->dwStyle
& LVS_OWNERDATA
)
7438 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7439 INT nOldCount
= infoPtr
->nItemCount
;
7441 if (nItems
< nOldCount
)
7443 RANGE range
= { nItems
, nOldCount
};
7444 ranges_del(infoPtr
->selectionRanges
, range
);
7445 if (infoPtr
->nFocusedItem
>= nItems
)
7447 infoPtr
->nFocusedItem
= -1;
7448 SetRectEmpty(&infoPtr
->rcFocus
);
7452 infoPtr
->nItemCount
= nItems
;
7453 LISTVIEW_UpdateScroll(infoPtr
);
7455 /* the flags are valid only in ownerdata report and list modes */
7456 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
) dwFlags
= 0;
7458 if (!(dwFlags
& LVSICF_NOSCROLL
) && infoPtr
->nFocusedItem
!= -1)
7459 LISTVIEW_EnsureVisible(infoPtr
, infoPtr
->nFocusedItem
, FALSE
);
7461 if (!(dwFlags
& LVSICF_NOINVALIDATEALL
))
7462 LISTVIEW_InvalidateList(infoPtr
);
7469 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7470 nFrom
= min(nOldCount
, nItems
);
7471 nTo
= max(nOldCount
, nItems
);
7473 if (uView
== LVS_REPORT
)
7476 rcErase
.top
= nFrom
* infoPtr
->nItemHeight
;
7477 rcErase
.right
= infoPtr
->nItemWidth
;
7478 rcErase
.bottom
= nTo
* infoPtr
->nItemHeight
;
7479 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7480 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7481 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7485 INT nPerCol
= LISTVIEW_GetCountPerColumn(infoPtr
);
7487 rcErase
.left
= (nFrom
/ nPerCol
) * infoPtr
->nItemWidth
;
7488 rcErase
.top
= (nFrom
% nPerCol
) * infoPtr
->nItemHeight
;
7489 rcErase
.right
= rcErase
.left
+ infoPtr
->nItemWidth
;
7490 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7491 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7492 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7493 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7495 rcErase
.left
= (nFrom
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7497 rcErase
.right
= (nTo
/ nPerCol
+ 1) * infoPtr
->nItemWidth
;
7498 rcErase
.bottom
= nPerCol
* infoPtr
->nItemHeight
;
7499 OffsetRect(&rcErase
, Origin
.x
, Origin
.y
);
7500 if (IntersectRect(&rcErase
, &rcErase
, &infoPtr
->rcList
))
7501 LISTVIEW_InvalidateRect(infoPtr
, &rcErase
);
7507 /* According to MSDN for non-LVS_OWNERDATA this is just
7508 * a performance issue. The control allocates its internal
7509 * data structures for the number of items specified. It
7510 * cuts down on the number of memory allocations. Therefore
7511 * we will just issue a WARN here
7513 WARN("for non-ownerdata performance option not implemented.\n");
7521 * Sets the position of an item.
7524 * [I] infoPtr : valid pointer to the listview structure
7525 * [I] nItem : item index
7526 * [I] pt : coordinate
7532 static BOOL
LISTVIEW_SetItemPosition(LISTVIEW_INFO
*infoPtr
, INT nItem
, POINT pt
)
7534 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7537 TRACE("(nItem=%d, &pt=%s\n", nItem
, wine_dbgstr_point(&pt
));
7539 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
||
7540 !(uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)) return FALSE
;
7542 LISTVIEW_GetOrigin(infoPtr
, &Origin
);
7544 /* This point value seems to be an undocumented feature.
7545 * The best guess is that it means either at the origin,
7546 * or at true beginning of the list. I will assume the origin. */
7547 if ((pt
.x
== -1) && (pt
.y
== -1))
7550 if (uView
== LVS_ICON
)
7552 pt
.x
-= (infoPtr
->nItemWidth
- infoPtr
->iconSize
.cx
) / 2;
7553 pt
.y
-= ICON_TOP_PADDING
;
7558 infoPtr
->bAutoarrange
= FALSE
;
7560 return LISTVIEW_MoveIconTo(infoPtr
, nItem
, &pt
, FALSE
);
7565 * Sets the state of one or many items.
7568 * [I] infoPtr : valid pointer to the listview structure
7569 * [I] nItem : item index
7570 * [I] lpLVItem : item or subitem info
7576 static BOOL
LISTVIEW_SetItemState(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
)
7578 BOOL bResult
= TRUE
;
7581 lvItem
.iItem
= nItem
;
7582 lvItem
.iSubItem
= 0;
7583 lvItem
.mask
= LVIF_STATE
;
7584 lvItem
.state
= lpLVItem
->state
;
7585 lvItem
.stateMask
= lpLVItem
->stateMask
;
7586 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem
, TRUE
));
7590 /* apply to all items */
7591 for (lvItem
.iItem
= 0; lvItem
.iItem
< infoPtr
->nItemCount
; lvItem
.iItem
++)
7592 if (!LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
)) bResult
= FALSE
;
7595 bResult
= LISTVIEW_SetItemT(infoPtr
, &lvItem
, TRUE
);
7598 * Update selection mark
7600 * Investigation on windows 2k showed that selection mark was updated
7601 * whenever a new selection was made, but if the selected item was
7602 * unselected it was not updated.
7604 * we are probably still not 100% accurate, but this at least sets the
7605 * proper selection mark when it is needed
7608 if (bResult
&& (lvItem
.state
& lvItem
.stateMask
& LVIS_SELECTED
) &&
7609 (infoPtr
->nSelectionMark
== -1))
7612 for (i
= 0; i
< infoPtr
->nItemCount
; i
++)
7614 if (infoPtr
->uCallbackMask
& LVIS_SELECTED
)
7616 if (LISTVIEW_GetItemState(infoPtr
, i
, LVIS_SELECTED
))
7618 infoPtr
->nSelectionMark
= i
;
7622 else if (ranges_contain(infoPtr
->selectionRanges
, i
))
7624 infoPtr
->nSelectionMark
= i
;
7635 * Sets the text of an item or subitem.
7638 * [I] hwnd : window handle
7639 * [I] nItem : item index
7640 * [I] lpLVItem : item or subitem info
7641 * [I] isW : TRUE if input is Unicode
7647 static BOOL
LISTVIEW_SetItemTextT(LISTVIEW_INFO
*infoPtr
, INT nItem
, const LVITEMW
*lpLVItem
, BOOL isW
)
7651 if (nItem
< 0 && nItem
>= infoPtr
->nItemCount
) return FALSE
;
7653 lvItem
.iItem
= nItem
;
7654 lvItem
.iSubItem
= lpLVItem
->iSubItem
;
7655 lvItem
.mask
= LVIF_TEXT
;
7656 lvItem
.pszText
= lpLVItem
->pszText
;
7657 lvItem
.cchTextMax
= lpLVItem
->cchTextMax
;
7659 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem
, debuglvitem_t(&lvItem
, isW
), isW
);
7661 return LISTVIEW_SetItemT(infoPtr
, &lvItem
, isW
);
7666 * Set item index that marks the start of a multiple selection.
7669 * [I] infoPtr : valid pointer to the listview structure
7670 * [I] nIndex : index
7673 * Index number or -1 if there is no selection mark.
7675 static INT
LISTVIEW_SetSelectionMark(LISTVIEW_INFO
*infoPtr
, INT nIndex
)
7677 INT nOldIndex
= infoPtr
->nSelectionMark
;
7679 TRACE("(nIndex=%d)\n", nIndex
);
7681 infoPtr
->nSelectionMark
= nIndex
;
7688 * Sets the text background color.
7691 * [I] infoPtr : valid pointer to the listview structure
7692 * [I] clrTextBk : text background color
7698 static BOOL
LISTVIEW_SetTextBkColor(LISTVIEW_INFO
*infoPtr
, COLORREF clrTextBk
)
7700 TRACE("(clrTextBk=%x)\n", clrTextBk
);
7702 if (infoPtr
->clrTextBk
!= clrTextBk
)
7704 infoPtr
->clrTextBk
= clrTextBk
;
7705 LISTVIEW_InvalidateList(infoPtr
);
7713 * Sets the text foreground color.
7716 * [I] infoPtr : valid pointer to the listview structure
7717 * [I] clrText : text color
7723 static BOOL
LISTVIEW_SetTextColor (LISTVIEW_INFO
*infoPtr
, COLORREF clrText
)
7725 TRACE("(clrText=%x)\n", clrText
);
7727 if (infoPtr
->clrText
!= clrText
)
7729 infoPtr
->clrText
= clrText
;
7730 LISTVIEW_InvalidateList(infoPtr
);
7738 * Determines which listview item is located at the specified position.
7741 * [I] infoPtr : valid pointer to the listview structure
7742 * [I] hwndNewToolTip : handle to new ToolTip
7747 static HWND
LISTVIEW_SetToolTips( LISTVIEW_INFO
*infoPtr
, HWND hwndNewToolTip
)
7749 HWND hwndOldToolTip
= infoPtr
->hwndToolTip
;
7750 infoPtr
->hwndToolTip
= hwndNewToolTip
;
7751 return hwndOldToolTip
;
7756 * sets the Unicode character format flag for the control
7758 * [I] infoPtr :valid pointer to the listview structure
7759 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7762 * Old Unicode Format
7764 static BOOL
LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO
*infoPtr
, BOOL fUnicode
)
7766 BOOL rc
= infoPtr
->notifyFormat
;
7767 infoPtr
->notifyFormat
= (fUnicode
)?NFR_UNICODE
:NFR_ANSI
;
7771 /* LISTVIEW_SetWorkAreas */
7775 * Callback internally used by LISTVIEW_SortItems()
7778 * [I] first : pointer to first ITEM_INFO to compare
7779 * [I] second : pointer to second ITEM_INFO to compare
7780 * [I] lParam : HWND of control
7783 * if first comes before second : negative
7784 * if first comes after second : positive
7785 * if first and second are equivalent : zero
7787 static INT WINAPI
LISTVIEW_CallBackCompare(LPVOID first
, LPVOID second
, LPARAM lParam
)
7789 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)lParam
;
7790 ITEM_INFO
* lv_first
= DPA_GetPtr( first
, 0 );
7791 ITEM_INFO
* lv_second
= DPA_GetPtr( second
, 0 );
7793 /* Forward the call to the client defined callback */
7794 return (infoPtr
->pfnCompare
)( lv_first
->lParam
, lv_second
->lParam
, infoPtr
->lParamSort
);
7799 * Sorts the listview items.
7802 * [I] infoPtr : valid pointer to the listview structure
7803 * [I] pfnCompare : application-defined value
7804 * [I] lParamSort : pointer to comparison callback
7810 static BOOL
LISTVIEW_SortItems(LISTVIEW_INFO
*infoPtr
, PFNLVCOMPARE pfnCompare
, LPARAM lParamSort
)
7812 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
7815 LPVOID selectionMarkItem
;
7819 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare
, lParamSort
);
7821 if (infoPtr
->dwStyle
& LVS_OWNERDATA
) return FALSE
;
7823 if (!pfnCompare
) return FALSE
;
7824 if (!infoPtr
->hdpaItems
) return FALSE
;
7826 /* if there are 0 or 1 items, there is no need to sort */
7827 if (infoPtr
->nItemCount
< 2) return TRUE
;
7829 if (infoPtr
->nFocusedItem
>= 0)
7831 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nFocusedItem
);
7832 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
7833 if (lpItem
) lpItem
->state
|= LVIS_FOCUSED
;
7835 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7836 /* clear the lpItem->state for non-selected ones */
7837 /* remove the selection ranges */
7839 infoPtr
->pfnCompare
= pfnCompare
;
7840 infoPtr
->lParamSort
= lParamSort
;
7841 DPA_Sort(infoPtr
->hdpaItems
, LISTVIEW_CallBackCompare
, (LPARAM
)infoPtr
);
7843 /* Adjust selections and indices so that they are the way they should
7844 * be after the sort (otherwise, the list items move around, but
7845 * whatever is at the item's previous original position will be
7848 selectionMarkItem
=(infoPtr
->nSelectionMark
>=0)?DPA_GetPtr(infoPtr
->hdpaItems
, infoPtr
->nSelectionMark
):NULL
;
7849 for (i
=0; i
< infoPtr
->nItemCount
; i
++)
7851 hdpaSubItems
= DPA_GetPtr(infoPtr
->hdpaItems
, i
);
7852 lpItem
= DPA_GetPtr(hdpaSubItems
, 0);
7854 if (lpItem
->state
& LVIS_SELECTED
)
7856 item
.state
= LVIS_SELECTED
;
7857 item
.stateMask
= LVIS_SELECTED
;
7858 LISTVIEW_SetItemState(infoPtr
, i
, &item
);
7860 if (lpItem
->state
& LVIS_FOCUSED
)
7862 infoPtr
->nFocusedItem
= i
;
7863 lpItem
->state
&= ~LVIS_FOCUSED
;
7866 if (selectionMarkItem
!= NULL
)
7867 infoPtr
->nSelectionMark
= DPA_GetPtrIndex(infoPtr
->hdpaItems
, selectionMarkItem
);
7868 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7870 /* refresh the display */
7871 if (uView
!= LVS_ICON
&& uView
!= LVS_SMALLICON
)
7872 LISTVIEW_InvalidateList(infoPtr
);
7879 * Update theme handle after a theme change.
7882 * [I] infoPtr : valid pointer to the listview structure
7886 * FAILURE : something else
7888 static LRESULT
LISTVIEW_ThemeChanged(const LISTVIEW_INFO
*infoPtr
)
7890 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
7891 CloseThemeData(theme
);
7892 OpenThemeData(infoPtr
->hwndSelf
, themeClass
);
7898 * Updates an items or rearranges the listview control.
7901 * [I] infoPtr : valid pointer to the listview structure
7902 * [I] nItem : item index
7908 static BOOL
LISTVIEW_Update(LISTVIEW_INFO
*infoPtr
, INT nItem
)
7910 TRACE("(nItem=%d)\n", nItem
);
7912 if (nItem
< 0 || nItem
>= infoPtr
->nItemCount
) return FALSE
;
7914 /* rearrange with default alignment style */
7915 if (is_autoarrange(infoPtr
))
7916 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
7918 LISTVIEW_InvalidateItem(infoPtr
, nItem
);
7925 * Draw the track line at the place defined in the infoPtr structure.
7926 * The line is drawn with a XOR pen so drawing the line for the second time
7927 * in the same place erases the line.
7930 * [I] infoPtr : valid pointer to the listview structure
7936 static BOOL
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO
*infoPtr
)
7942 if (infoPtr
->xTrackLine
== -1)
7945 if (!(hdc
= GetDC(infoPtr
->hwndSelf
)))
7947 hOldPen
= SelectObject(hdc
, GetStockObject(BLACK_PEN
));
7948 oldROP
= SetROP2(hdc
, R2_XORPEN
);
7949 MoveToEx(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.top
, NULL
);
7950 LineTo(hdc
, infoPtr
->xTrackLine
, infoPtr
->rcList
.bottom
);
7951 SetROP2(hdc
, oldROP
);
7952 SelectObject(hdc
, hOldPen
);
7953 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
7959 * Called when an edit control should be displayed. This function is called after
7960 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7963 * [I] hwnd : Handle to the listview
7964 * [I] uMsg : WM_TIMER (ignored)
7965 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7966 * [I] dwTimer : The elapsed time (ignored)
7971 static VOID CALLBACK
LISTVIEW_DelayedEditItem(HWND hwnd
, UINT uMsg
, UINT_PTR idEvent
, DWORD dwTime
)
7973 DELAYED_ITEM_EDIT
*editItem
= (DELAYED_ITEM_EDIT
*)idEvent
;
7974 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
7976 KillTimer(hwnd
, idEvent
);
7977 editItem
->fEnabled
= FALSE
;
7978 /* check if the item is still selected */
7979 if (infoPtr
->bFocus
&& LISTVIEW_GetItemState(infoPtr
, editItem
->iItem
, LVIS_SELECTED
))
7980 LISTVIEW_EditLabelT(infoPtr
, editItem
->iItem
, TRUE
);
7985 * Creates the listview control - the WM_NCCREATE phase.
7988 * [I] hwnd : window handle
7989 * [I] lpcs : the create parameters
7995 static LRESULT
LISTVIEW_NCCreate(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
7997 LISTVIEW_INFO
*infoPtr
;
8000 TRACE("(lpcs=%p)\n", lpcs
);
8002 /* initialize info pointer */
8003 infoPtr
= Alloc(sizeof(LISTVIEW_INFO
));
8004 if (!infoPtr
) return FALSE
;
8006 SetWindowLongPtrW(hwnd
, 0, (DWORD_PTR
)infoPtr
);
8008 infoPtr
->hwndSelf
= hwnd
;
8009 infoPtr
->dwStyle
= lpcs
->style
; /* Note: may be changed in WM_CREATE */
8010 /* determine the type of structures to use */
8011 infoPtr
->hwndNotify
= lpcs
->hwndParent
;
8012 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8014 /* initialize color information */
8015 infoPtr
->clrBk
= CLR_NONE
;
8016 infoPtr
->clrText
= CLR_DEFAULT
;
8017 infoPtr
->clrTextBk
= CLR_DEFAULT
;
8018 LISTVIEW_SetBkColor(infoPtr
, comctl32_color
.clrWindow
);
8020 /* set default values */
8021 infoPtr
->nFocusedItem
= -1;
8022 infoPtr
->nSelectionMark
= -1;
8023 infoPtr
->nHotItem
= -1;
8024 infoPtr
->bRedraw
= TRUE
;
8025 infoPtr
->bNoItemMetrics
= TRUE
;
8026 infoPtr
->bDoChangeNotify
= TRUE
;
8027 infoPtr
->iconSpacing
.cx
= GetSystemMetrics(SM_CXICONSPACING
);
8028 infoPtr
->iconSpacing
.cy
= GetSystemMetrics(SM_CYICONSPACING
);
8029 infoPtr
->nEditLabelItem
= -1;
8030 infoPtr
->dwHoverTime
= -1; /* default system hover time */
8031 infoPtr
->nMeasureItemHeight
= 0;
8032 infoPtr
->xTrackLine
= -1; /* no track line */
8033 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8035 /* get default font (icon title) */
8036 SystemParametersInfoW(SPI_GETICONTITLELOGFONT
, 0, &logFont
, 0);
8037 infoPtr
->hDefaultFont
= CreateFontIndirectW(&logFont
);
8038 infoPtr
->hFont
= infoPtr
->hDefaultFont
;
8039 LISTVIEW_SaveTextMetrics(infoPtr
);
8041 /* allocate memory for the data structure */
8042 if (!(infoPtr
->selectionRanges
= ranges_create(10))) goto fail
;
8043 if (!(infoPtr
->hdpaItems
= DPA_Create(10))) goto fail
;
8044 if (!(infoPtr
->hdpaPosX
= DPA_Create(10))) goto fail
;
8045 if (!(infoPtr
->hdpaPosY
= DPA_Create(10))) goto fail
;
8046 if (!(infoPtr
->hdpaColumns
= DPA_Create(10))) goto fail
;
8050 DestroyWindow(infoPtr
->hwndHeader
);
8051 ranges_destroy(infoPtr
->selectionRanges
);
8052 DPA_Destroy(infoPtr
->hdpaItems
);
8053 DPA_Destroy(infoPtr
->hdpaPosX
);
8054 DPA_Destroy(infoPtr
->hdpaPosY
);
8055 DPA_Destroy(infoPtr
->hdpaColumns
);
8062 * Creates the listview control - the WM_CREATE phase. Most of the data is
8063 * already set up in LISTVIEW_NCCreate
8066 * [I] hwnd : window handle
8067 * [I] lpcs : the create parameters
8073 static LRESULT
LISTVIEW_Create(HWND hwnd
, const CREATESTRUCTW
*lpcs
)
8075 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
8076 UINT uView
= lpcs
->style
& LVS_TYPEMASK
;
8077 DWORD dFlags
= WS_CHILD
| HDS_HORZ
| HDS_FULLDRAG
| HDS_DRAGDROP
;
8079 TRACE("(lpcs=%p)\n", lpcs
);
8081 infoPtr
->dwStyle
= lpcs
->style
;
8082 infoPtr
->notifyFormat
= SendMessageW(infoPtr
->hwndNotify
, WM_NOTIFYFORMAT
,
8083 (WPARAM
)infoPtr
->hwndSelf
, (LPARAM
)NF_QUERY
);
8085 /* setup creation flags */
8086 dFlags
|= (LVS_NOSORTHEADER
& lpcs
->style
) ? 0 : HDS_BUTTONS
;
8087 dFlags
|= (LVS_NOCOLUMNHEADER
& lpcs
->style
) ? HDS_HIDDEN
: 0;
8090 infoPtr
->hwndHeader
= CreateWindowW(WC_HEADERW
, NULL
, dFlags
,
8091 0, 0, 0, 0, hwnd
, NULL
,
8092 lpcs
->hInstance
, NULL
);
8093 if (!infoPtr
->hwndHeader
) return -1;
8095 /* set header unicode format */
8096 SendMessageW(infoPtr
->hwndHeader
, HDM_SETUNICODEFORMAT
, TRUE
, 0);
8098 /* set header font */
8099 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, (LPARAM
)TRUE
);
8101 /* init item size to avoid division by 0 */
8102 LISTVIEW_UpdateItemSize (infoPtr
);
8104 if (uView
== LVS_REPORT
)
8106 if (!(LVS_NOCOLUMNHEADER
& lpcs
->style
))
8108 ShowWindow(infoPtr
->hwndHeader
, SW_SHOWNORMAL
);
8110 LISTVIEW_UpdateSize(infoPtr
);
8111 LISTVIEW_UpdateScroll(infoPtr
);
8114 OpenThemeData(hwnd
, themeClass
);
8116 /* initialize the icon sizes */
8117 set_icon_size(&infoPtr
->iconSize
, infoPtr
->himlNormal
, uView
!= LVS_ICON
);
8118 set_icon_size(&infoPtr
->iconStateSize
, infoPtr
->himlState
, TRUE
);
8124 * Destroys the listview control.
8127 * [I] infoPtr : valid pointer to the listview structure
8133 static LRESULT
LISTVIEW_Destroy(const LISTVIEW_INFO
*infoPtr
)
8135 HTHEME theme
= GetWindowTheme(infoPtr
->hwndSelf
);
8136 CloseThemeData(theme
);
8142 * Enables the listview control.
8145 * [I] infoPtr : valid pointer to the listview structure
8146 * [I] bEnable : specifies whether to enable or disable the window
8152 static BOOL
LISTVIEW_Enable(const LISTVIEW_INFO
*infoPtr
, BOOL bEnable
)
8154 if (infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
)
8155 InvalidateRect(infoPtr
->hwndSelf
, NULL
, TRUE
);
8161 * Erases the background of the listview control.
8164 * [I] infoPtr : valid pointer to the listview structure
8165 * [I] hdc : device context handle
8171 static inline BOOL
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO
*infoPtr
, HDC hdc
)
8175 TRACE("(hdc=%p)\n", hdc
);
8177 if (!GetClipBox(hdc
, &rc
)) return FALSE
;
8179 /* for double buffered controls we need to do this during refresh */
8180 if (infoPtr
->dwLvExStyle
& LVS_EX_DOUBLEBUFFER
) return FALSE
;
8182 return LISTVIEW_FillBkgnd(infoPtr
, hdc
, &rc
);
8188 * Helper function for LISTVIEW_[HV]Scroll *only*.
8189 * Performs vertical/horizontal scrolling by a give amount.
8192 * [I] infoPtr : valid pointer to the listview structure
8193 * [I] dx : amount of horizontal scroll
8194 * [I] dy : amount of vertical scroll
8196 static void scroll_list(LISTVIEW_INFO
*infoPtr
, INT dx
, INT dy
)
8198 /* now we can scroll the list */
8199 ScrollWindowEx(infoPtr
->hwndSelf
, dx
, dy
, &infoPtr
->rcList
,
8200 &infoPtr
->rcList
, 0, 0, SW_ERASE
| SW_INVALIDATE
);
8201 /* if we have focus, adjust rect */
8202 OffsetRect(&infoPtr
->rcFocus
, dx
, dy
);
8203 UpdateWindow(infoPtr
->hwndSelf
);
8208 * Performs vertical scrolling.
8211 * [I] infoPtr : valid pointer to the listview structure
8212 * [I] nScrollCode : scroll code
8213 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8214 * [I] hScrollWnd : scrollbar control window handle
8220 * SB_LINEUP/SB_LINEDOWN:
8221 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8222 * for LVS_REPORT is 1 line
8223 * for LVS_LIST cannot occur
8226 static LRESULT
LISTVIEW_VScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8227 INT nScrollDiff
, HWND hScrollWnd
)
8229 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8230 INT nOldScrollPos
, nNewScrollPos
;
8231 SCROLLINFO scrollInfo
;
8234 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8235 debugscrollcode(nScrollCode
), nScrollDiff
);
8237 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8239 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8240 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8242 is_an_icon
= ((uView
== LVS_ICON
) || (uView
== LVS_SMALLICON
));
8244 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
)) return 1;
8246 nOldScrollPos
= scrollInfo
.nPos
;
8247 switch (nScrollCode
)
8253 nScrollDiff
= (is_an_icon
) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE
: -1;
8257 nScrollDiff
= (is_an_icon
) ? LISTVIEW_SCROLL_ICON_LINE_SIZE
: 1;
8261 nScrollDiff
= -scrollInfo
.nPage
;
8265 nScrollDiff
= scrollInfo
.nPage
;
8268 case SB_THUMBPOSITION
:
8270 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8277 /* quit right away if pos isn't changing */
8278 if (nScrollDiff
== 0) return 0;
8280 /* calculate new position, and handle overflows */
8281 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8282 if (nScrollDiff
> 0) {
8283 if (nNewScrollPos
< nOldScrollPos
||
8284 nNewScrollPos
> scrollInfo
.nMax
)
8285 nNewScrollPos
= scrollInfo
.nMax
;
8287 if (nNewScrollPos
> nOldScrollPos
||
8288 nNewScrollPos
< scrollInfo
.nMin
)
8289 nNewScrollPos
= scrollInfo
.nMin
;
8292 /* set the new position, and reread in case it changed */
8293 scrollInfo
.fMask
= SIF_POS
;
8294 scrollInfo
.nPos
= nNewScrollPos
;
8295 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_VERT
, &scrollInfo
, TRUE
);
8297 /* carry on only if it really changed */
8298 if (nNewScrollPos
== nOldScrollPos
) return 0;
8300 /* now adjust to client coordinates */
8301 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8302 if (uView
== LVS_REPORT
) nScrollDiff
*= infoPtr
->nItemHeight
;
8304 /* and scroll the window */
8305 scroll_list(infoPtr
, 0, nScrollDiff
);
8312 * Performs horizontal scrolling.
8315 * [I] infoPtr : valid pointer to the listview structure
8316 * [I] nScrollCode : scroll code
8317 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8318 * [I] hScrollWnd : scrollbar control window handle
8324 * SB_LINELEFT/SB_LINERIGHT:
8325 * for LVS_ICON, LVS_SMALLICON 1 pixel
8326 * for LVS_REPORT is 1 pixel
8327 * for LVS_LIST is 1 column --> which is a 1 because the
8328 * scroll is based on columns not pixels
8331 static LRESULT
LISTVIEW_HScroll(LISTVIEW_INFO
*infoPtr
, INT nScrollCode
,
8332 INT nScrollDiff
, HWND hScrollWnd
)
8334 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8335 INT nOldScrollPos
, nNewScrollPos
;
8336 SCROLLINFO scrollInfo
;
8338 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode
,
8339 debugscrollcode(nScrollCode
), nScrollDiff
);
8341 if (infoPtr
->hwndEdit
) SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
8343 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8344 scrollInfo
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_TRACKPOS
;
8346 if (!GetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
)) return 1;
8348 nOldScrollPos
= scrollInfo
.nPos
;
8350 switch (nScrollCode
)
8364 nScrollDiff
= -scrollInfo
.nPage
;
8368 nScrollDiff
= scrollInfo
.nPage
;
8371 case SB_THUMBPOSITION
:
8373 nScrollDiff
= scrollInfo
.nTrackPos
- scrollInfo
.nPos
;
8380 /* quit right away if pos isn't changing */
8381 if (nScrollDiff
== 0) return 0;
8383 /* calculate new position, and handle overflows */
8384 nNewScrollPos
= scrollInfo
.nPos
+ nScrollDiff
;
8385 if (nScrollDiff
> 0) {
8386 if (nNewScrollPos
< nOldScrollPos
||
8387 nNewScrollPos
> scrollInfo
.nMax
)
8388 nNewScrollPos
= scrollInfo
.nMax
;
8390 if (nNewScrollPos
> nOldScrollPos
||
8391 nNewScrollPos
< scrollInfo
.nMin
)
8392 nNewScrollPos
= scrollInfo
.nMin
;
8395 /* set the new position, and reread in case it changed */
8396 scrollInfo
.fMask
= SIF_POS
;
8397 scrollInfo
.nPos
= nNewScrollPos
;
8398 nNewScrollPos
= SetScrollInfo(infoPtr
->hwndSelf
, SB_HORZ
, &scrollInfo
, TRUE
);
8400 /* carry on only if it really changed */
8401 if (nNewScrollPos
== nOldScrollPos
) return 0;
8403 if(uView
== LVS_REPORT
)
8404 LISTVIEW_UpdateHeaderSize(infoPtr
, nNewScrollPos
);
8406 /* now adjust to client coordinates */
8407 nScrollDiff
= nOldScrollPos
- nNewScrollPos
;
8408 if (uView
== LVS_LIST
) nScrollDiff
*= infoPtr
->nItemWidth
;
8410 /* and scroll the window */
8411 scroll_list(infoPtr
, nScrollDiff
, 0);
8416 static LRESULT
LISTVIEW_MouseWheel(LISTVIEW_INFO
*infoPtr
, INT wheelDelta
)
8418 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8419 INT gcWheelDelta
= 0;
8420 INT pulScrollLines
= 3;
8421 SCROLLINFO scrollInfo
;
8423 TRACE("(wheelDelta=%d)\n", wheelDelta
);
8425 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
8426 gcWheelDelta
-= wheelDelta
;
8428 scrollInfo
.cbSize
= sizeof(SCROLLINFO
);
8429 scrollInfo
.fMask
= SIF_POS
;
8436 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8437 * should be fixed in the future.
8439 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, (gcWheelDelta
< 0) ?
8440 -LISTVIEW_SCROLL_ICON_LINE_SIZE
: LISTVIEW_SCROLL_ICON_LINE_SIZE
, 0);
8444 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
8446 int cLineScroll
= min(LISTVIEW_GetCountPerColumn(infoPtr
), pulScrollLines
);
8447 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
8448 LISTVIEW_VScroll(infoPtr
, SB_INTERNAL
, cLineScroll
, 0);
8453 LISTVIEW_HScroll(infoPtr
, (gcWheelDelta
< 0) ? SB_LINELEFT
: SB_LINERIGHT
, 0, 0);
8464 * [I] infoPtr : valid pointer to the listview structure
8465 * [I] nVirtualKey : virtual key
8466 * [I] lKeyData : key data
8471 static LRESULT
LISTVIEW_KeyDown(LISTVIEW_INFO
*infoPtr
, INT nVirtualKey
, LONG lKeyData
)
8473 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8474 HWND hwndSelf
= infoPtr
->hwndSelf
;
8476 NMLVKEYDOWN nmKeyDown
;
8478 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey
, lKeyData
);
8480 /* send LVN_KEYDOWN notification */
8481 nmKeyDown
.wVKey
= nVirtualKey
;
8482 nmKeyDown
.flags
= 0;
8483 notify_hdr(infoPtr
, LVN_KEYDOWN
, &nmKeyDown
.hdr
);
8484 if (!IsWindow(hwndSelf
))
8487 switch (nVirtualKey
)
8490 nItem
= infoPtr
->nFocusedItem
;
8491 if (infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
)
8492 toggle_checkbox_state(infoPtr
, infoPtr
->nFocusedItem
);
8496 if ((infoPtr
->nItemCount
> 0) && (infoPtr
->nFocusedItem
!= -1))
8498 if (!notify(infoPtr
, NM_RETURN
)) return 0;
8499 if (!notify(infoPtr
, LVN_ITEMACTIVATE
)) return 0;
8504 if (infoPtr
->nItemCount
> 0)
8509 if (infoPtr
->nItemCount
> 0)
8510 nItem
= infoPtr
->nItemCount
- 1;
8514 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TOLEFT
);
8518 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_ABOVE
);
8522 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_TORIGHT
);
8526 nItem
= ListView_GetNextItem(infoPtr
->hwndSelf
, infoPtr
->nFocusedItem
, LVNI_BELOW
);
8530 if (uView
== LVS_REPORT
)
8532 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8533 if (infoPtr
->nFocusedItem
== topidx
)
8534 nItem
= topidx
- LISTVIEW_GetCountPerColumn(infoPtr
) + 1;
8539 nItem
= infoPtr
->nFocusedItem
- LISTVIEW_GetCountPerColumn(infoPtr
)
8540 * LISTVIEW_GetCountPerRow(infoPtr
);
8541 if(nItem
< 0) nItem
= 0;
8545 if (uView
== LVS_REPORT
)
8547 INT topidx
= LISTVIEW_GetTopIndex(infoPtr
);
8548 INT cnt
= LISTVIEW_GetCountPerColumn(infoPtr
);
8549 if (infoPtr
->nFocusedItem
== topidx
+ cnt
- 1)
8550 nItem
= infoPtr
->nFocusedItem
+ cnt
- 1;
8552 nItem
= topidx
+ cnt
- 1;
8555 nItem
= infoPtr
->nFocusedItem
+ LISTVIEW_GetCountPerColumn(infoPtr
)
8556 * LISTVIEW_GetCountPerRow(infoPtr
);
8557 if(nItem
>= infoPtr
->nItemCount
) nItem
= infoPtr
->nItemCount
- 1;
8561 if ((nItem
!= -1) && (nItem
!= infoPtr
->nFocusedItem
|| nVirtualKey
== VK_SPACE
))
8562 LISTVIEW_KeySelection(infoPtr
, nItem
);
8572 * [I] infoPtr : valid pointer to the listview structure
8577 static LRESULT
LISTVIEW_KillFocus(LISTVIEW_INFO
*infoPtr
)
8581 /* if we did not have the focus, there's nothing to do */
8582 if (!infoPtr
->bFocus
) return 0;
8584 /* send NM_KILLFOCUS notification */
8585 if (!notify(infoPtr
, NM_KILLFOCUS
)) return 0;
8587 /* if we have a focus rectagle, get rid of it */
8588 LISTVIEW_ShowFocusRect(infoPtr
, FALSE
);
8590 /* set window focus flag */
8591 infoPtr
->bFocus
= FALSE
;
8593 /* invalidate the selected items before resetting focus flag */
8594 LISTVIEW_InvalidateSelectedItems(infoPtr
);
8601 * Processes double click messages (left mouse button).
8604 * [I] infoPtr : valid pointer to the listview structure
8605 * [I] wKey : key flag
8606 * [I] x,y : mouse coordinate
8611 static LRESULT
LISTVIEW_LButtonDblClk(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8613 LVHITTESTINFO htInfo
;
8615 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8617 /* Cancel the item edition if any */
8618 if (infoPtr
->itemEdit
.fEnabled
)
8620 KillTimer(infoPtr
->hwndSelf
, (UINT_PTR
)&infoPtr
->itemEdit
);
8621 infoPtr
->itemEdit
.fEnabled
= FALSE
;
8624 /* send NM_RELEASEDCAPTURE notification */
8625 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8630 /* send NM_DBLCLK notification */
8631 LISTVIEW_HitTest(infoPtr
, &htInfo
, TRUE
, FALSE
);
8632 if (!notify_click(infoPtr
, NM_DBLCLK
, &htInfo
)) return 0;
8634 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8635 if(htInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&htInfo
);
8642 * Processes mouse down messages (left mouse button).
8645 * infoPtr [I ] valid pointer to the listview structure
8646 * wKey [I ] key flag
8647 * x,y [I ] mouse coordinate
8652 static LRESULT
LISTVIEW_LButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8654 LVHITTESTINFO lvHitTestInfo
;
8655 static BOOL bGroupSelect
= TRUE
;
8656 POINT pt
= { x
, y
};
8659 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8661 /* send NM_RELEASEDCAPTURE notification */
8662 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
8664 /* set left button down flag and record the click position */
8665 infoPtr
->bLButtonDown
= TRUE
;
8666 infoPtr
->ptClickPos
= pt
;
8667 infoPtr
->bDragging
= FALSE
;
8669 lvHitTestInfo
.pt
.x
= x
;
8670 lvHitTestInfo
.pt
.y
= y
;
8672 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
8673 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt
), nItem
);
8674 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
8676 if ((infoPtr
->dwLvExStyle
& LVS_EX_CHECKBOXES
) && (lvHitTestInfo
.flags
& LVHT_ONITEMSTATEICON
))
8678 toggle_checkbox_state(infoPtr
, nItem
);
8682 if (infoPtr
->dwStyle
& LVS_SINGLESEL
)
8684 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8685 infoPtr
->nEditLabelItem
= nItem
;
8687 LISTVIEW_SetSelection(infoPtr
, nItem
);
8691 if ((wKey
& MK_CONTROL
) && (wKey
& MK_SHIFT
))
8695 if (!LISTVIEW_AddGroupSelection(infoPtr
, nItem
)) return 0;
8696 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
8697 infoPtr
->nSelectionMark
= nItem
;
8703 item
.state
= LVIS_SELECTED
| LVIS_FOCUSED
;
8704 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8706 LISTVIEW_SetItemState(infoPtr
,nItem
,&item
);
8707 infoPtr
->nSelectionMark
= nItem
;
8710 else if (wKey
& MK_CONTROL
)
8714 bGroupSelect
= (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
) == 0);
8716 item
.state
= (bGroupSelect
? LVIS_SELECTED
: 0) | LVIS_FOCUSED
;
8717 item
.stateMask
= LVIS_SELECTED
| LVIS_FOCUSED
;
8718 LISTVIEW_SetItemState(infoPtr
, nItem
, &item
);
8719 infoPtr
->nSelectionMark
= nItem
;
8721 else if (wKey
& MK_SHIFT
)
8723 LISTVIEW_SetGroupSelection(infoPtr
, nItem
);
8727 if (LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
8728 infoPtr
->nEditLabelItem
= nItem
;
8730 /* set selection (clears other pre-existing selections) */
8731 LISTVIEW_SetSelection(infoPtr
, nItem
);
8735 if (infoPtr
->dwLvExStyle
& LVS_EX_ONECLICKACTIVATE
)
8736 if(lvHitTestInfo
.iItem
!= -1) notify_itemactivate(infoPtr
,&lvHitTestInfo
);
8740 /* remove all selections */
8741 LISTVIEW_DeselectAll(infoPtr
);
8750 * Processes mouse up messages (left mouse button).
8753 * infoPtr [I ] valid pointer to the listview structure
8754 * wKey [I ] key flag
8755 * x,y [I ] mouse coordinate
8760 static LRESULT
LISTVIEW_LButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
8762 LVHITTESTINFO lvHitTestInfo
;
8764 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey
, x
, y
);
8766 if (!infoPtr
->bLButtonDown
) return 0;
8768 lvHitTestInfo
.pt
.x
= x
;
8769 lvHitTestInfo
.pt
.y
= y
;
8771 /* send NM_CLICK notification */
8772 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
8773 if (!notify_click(infoPtr
, NM_CLICK
, &lvHitTestInfo
)) return 0;
8775 /* set left button flag */
8776 infoPtr
->bLButtonDown
= FALSE
;
8778 if (infoPtr
->bDragging
)
8780 infoPtr
->bDragging
= FALSE
;
8784 /* if we clicked on a selected item, edit the label */
8785 if(lvHitTestInfo
.iItem
== infoPtr
->nEditLabelItem
&& (lvHitTestInfo
.flags
& LVHT_ONITEMLABEL
))
8787 /* we want to make sure the user doesn't want to do a double click. So we will
8788 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8790 infoPtr
->itemEdit
.fEnabled
= TRUE
;
8791 infoPtr
->itemEdit
.iItem
= lvHitTestInfo
.iItem
;
8792 SetTimer(infoPtr
->hwndSelf
,
8793 (UINT_PTR
)&infoPtr
->itemEdit
,
8794 GetDoubleClickTime(),
8795 LISTVIEW_DelayedEditItem
);
8798 if (!infoPtr
->bFocus
)
8799 SetFocus(infoPtr
->hwndSelf
);
8806 * Destroys the listview control (called after WM_DESTROY).
8809 * [I] infoPtr : valid pointer to the listview structure
8814 static LRESULT
LISTVIEW_NCDestroy(LISTVIEW_INFO
*infoPtr
)
8818 /* delete all items */
8819 LISTVIEW_DeleteAllItems(infoPtr
, TRUE
);
8821 /* destroy data structure */
8822 DPA_Destroy(infoPtr
->hdpaItems
);
8823 DPA_Destroy(infoPtr
->hdpaPosX
);
8824 DPA_Destroy(infoPtr
->hdpaPosY
);
8825 DPA_Destroy(infoPtr
->hdpaColumns
);
8826 ranges_destroy(infoPtr
->selectionRanges
);
8828 /* destroy image lists */
8829 if (!(infoPtr
->dwStyle
& LVS_SHAREIMAGELISTS
))
8831 if (infoPtr
->himlNormal
)
8832 ImageList_Destroy(infoPtr
->himlNormal
);
8833 if (infoPtr
->himlSmall
)
8834 ImageList_Destroy(infoPtr
->himlSmall
);
8835 if (infoPtr
->himlState
)
8836 ImageList_Destroy(infoPtr
->himlState
);
8839 /* destroy font, bkgnd brush */
8841 if (infoPtr
->hDefaultFont
) DeleteObject(infoPtr
->hDefaultFont
);
8842 if (infoPtr
->clrBk
!= CLR_NONE
) DeleteObject(infoPtr
->hBkBrush
);
8844 SetWindowLongPtrW(infoPtr
->hwndSelf
, 0, 0);
8846 /* free listview info pointer*/
8854 * Handles notifications from header.
8857 * [I] infoPtr : valid pointer to the listview structure
8858 * [I] nCtrlId : control identifier
8859 * [I] lpnmh : notification information
8864 static LRESULT
LISTVIEW_HeaderNotification(LISTVIEW_INFO
*infoPtr
, const NMHEADERW
*lpnmh
)
8866 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
8867 HWND hwndSelf
= infoPtr
->hwndSelf
;
8869 TRACE("(lpnmh=%p)\n", lpnmh
);
8871 if (!lpnmh
|| lpnmh
->iItem
< 0 || lpnmh
->iItem
>= DPA_GetPtrCount(infoPtr
->hdpaColumns
)) return 0;
8873 switch (lpnmh
->hdr
.code
)
8878 COLUMN_INFO
*lpColumnInfo
;
8882 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8885 /* remove the old line (if any) */
8886 LISTVIEW_DrawTrackLine(infoPtr
);
8888 /* compute & draw the new line */
8889 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8890 x
= lpColumnInfo
->rcHeader
.left
+ lpnmh
->pitem
->cxy
;
8891 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8892 infoPtr
->xTrackLine
= x
+ ptOrigin
.x
;
8893 LISTVIEW_DrawTrackLine(infoPtr
);
8899 /* remove the track line (if any) */
8900 LISTVIEW_DrawTrackLine(infoPtr
);
8901 infoPtr
->xTrackLine
= -1;
8905 FIXME("Changing column order not implemented\n");
8908 case HDN_ITEMCHANGINGW
:
8909 case HDN_ITEMCHANGINGA
:
8910 return notify_forward_header(infoPtr
, lpnmh
);
8912 case HDN_ITEMCHANGEDW
:
8913 case HDN_ITEMCHANGEDA
:
8915 COLUMN_INFO
*lpColumnInfo
;
8918 notify_forward_header(infoPtr
, lpnmh
);
8919 if (!IsWindow(hwndSelf
))
8922 if (!lpnmh
->pitem
|| !(lpnmh
->pitem
->mask
& HDI_WIDTH
))
8926 hdi
.mask
= HDI_WIDTH
;
8927 if (!Header_GetItemW(infoPtr
->hwndHeader
, lpnmh
->iItem
, &hdi
)) return 0;
8931 cxy
= lpnmh
->pitem
->cxy
;
8933 /* determine how much we change since the last know position */
8934 lpColumnInfo
= LISTVIEW_GetColumnInfo(infoPtr
, lpnmh
->iItem
);
8935 dx
= cxy
- (lpColumnInfo
->rcHeader
.right
- lpColumnInfo
->rcHeader
.left
);
8938 lpColumnInfo
->rcHeader
.right
+= dx
;
8939 if (lpnmh
->iItem
+ 1 < DPA_GetPtrCount(infoPtr
->hdpaColumns
))
8940 LISTVIEW_ScrollColumns(infoPtr
, lpnmh
->iItem
+ 1, dx
);
8943 /* only needs to update the scrolls */
8944 infoPtr
->nItemWidth
+= dx
;
8945 LISTVIEW_UpdateScroll(infoPtr
);
8947 LISTVIEW_UpdateItemSize(infoPtr
);
8948 if (uView
== LVS_REPORT
&& is_redrawing(infoPtr
))
8951 RECT rcCol
= lpColumnInfo
->rcHeader
;
8953 LISTVIEW_GetOrigin(infoPtr
, &ptOrigin
);
8954 OffsetRect(&rcCol
, ptOrigin
.x
, 0);
8956 rcCol
.top
= infoPtr
->rcList
.top
;
8957 rcCol
.bottom
= infoPtr
->rcList
.bottom
;
8959 /* resizing left-aligned columns leaves most of the left side untouched */
8960 if ((lpColumnInfo
->fmt
& LVCFMT_JUSTIFYMASK
) == LVCFMT_LEFT
)
8962 INT nMaxDirty
= infoPtr
->nEllipsisWidth
+ infoPtr
->ntmMaxCharWidth
;
8965 rcCol
.left
= max (rcCol
.left
, rcCol
.right
- nMaxDirty
);
8968 /* when shrinking the last column clear the now unused field */
8969 if (lpnmh
->iItem
== DPA_GetPtrCount(infoPtr
->hdpaColumns
) - 1 && dx
< 0)
8972 LISTVIEW_InvalidateRect(infoPtr
, &rcCol
);
8978 case HDN_ITEMCLICKW
:
8979 case HDN_ITEMCLICKA
:
8981 /* Handle sorting by Header Column */
8984 ZeroMemory(&nmlv
, sizeof(NMLISTVIEW
));
8986 nmlv
.iSubItem
= lpnmh
->iItem
;
8987 notify_listview(infoPtr
, LVN_COLUMNCLICK
, &nmlv
);
8991 case HDN_DIVIDERDBLCLICKW
:
8992 case HDN_DIVIDERDBLCLICKA
:
8993 LISTVIEW_SetColumnWidth(infoPtr
, lpnmh
->iItem
, LVSCW_AUTOSIZE
);
9002 * Paint non-client area of control.
9005 * [I] infoPtr : valid pointer to the listview structureof the sender
9006 * [I] region : update region
9009 * TRUE - frame was painted
9010 * FALSE - call default window proc
9012 static BOOL
LISTVIEW_NCPaint(const LISTVIEW_INFO
*infoPtr
, HRGN region
)
9014 HTHEME theme
= GetWindowTheme (infoPtr
->hwndSelf
);
9018 int cxEdge
= GetSystemMetrics (SM_CXEDGE
),
9019 cyEdge
= GetSystemMetrics (SM_CYEDGE
);
9021 if (!theme
) return FALSE
;
9023 GetWindowRect(infoPtr
->hwndSelf
, &r
);
9025 cliprgn
= CreateRectRgn (r
.left
+ cxEdge
, r
.top
+ cyEdge
,
9026 r
.right
- cxEdge
, r
.bottom
- cyEdge
);
9027 if (region
!= (HRGN
)1)
9028 CombineRgn (cliprgn
, cliprgn
, region
, RGN_AND
);
9029 OffsetRect(&r
, -r
.left
, -r
.top
);
9031 dc
= GetDCEx(infoPtr
->hwndSelf
, region
, DCX_WINDOW
|DCX_INTERSECTRGN
);
9032 OffsetRect(&r
, -r
.left
, -r
.top
);
9034 if (IsThemeBackgroundPartiallyTransparent (theme
, 0, 0))
9035 DrawThemeParentBackground(infoPtr
->hwndSelf
, dc
, &r
);
9036 DrawThemeBackground (theme
, dc
, 0, 0, &r
, 0);
9037 ReleaseDC(infoPtr
->hwndSelf
, dc
);
9039 /* Call default proc to get the scrollbars etc. painted */
9040 DefWindowProcW (infoPtr
->hwndSelf
, WM_NCPAINT
, (WPARAM
)cliprgn
, 0);
9047 * Determines the type of structure to use.
9050 * [I] infoPtr : valid pointer to the listview structureof the sender
9051 * [I] hwndFrom : listview window handle
9052 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9057 static LRESULT
LISTVIEW_NotifyFormat(LISTVIEW_INFO
*infoPtr
, HWND hwndFrom
, INT nCommand
)
9059 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom
, nCommand
);
9061 if (nCommand
== NF_REQUERY
)
9062 infoPtr
->notifyFormat
= SendMessageW(hwndFrom
, WM_NOTIFYFORMAT
, (WPARAM
)infoPtr
->hwndSelf
, NF_QUERY
);
9064 return infoPtr
->notifyFormat
;
9069 * Paints/Repaints the listview control.
9072 * [I] infoPtr : valid pointer to the listview structure
9073 * [I] hdc : device context handle
9078 static LRESULT
LISTVIEW_Paint(LISTVIEW_INFO
*infoPtr
, HDC hdc
)
9080 TRACE("(hdc=%p)\n", hdc
);
9082 if (infoPtr
->bNoItemMetrics
&& infoPtr
->nItemCount
)
9084 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9086 infoPtr
->bNoItemMetrics
= FALSE
;
9087 LISTVIEW_UpdateItemSize(infoPtr
);
9088 if (uView
== LVS_ICON
|| uView
== LVS_SMALLICON
)
9089 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9090 LISTVIEW_UpdateScroll(infoPtr
);
9093 UpdateWindow(infoPtr
->hwndHeader
);
9096 LISTVIEW_Refresh(infoPtr
, hdc
, NULL
);
9101 hdc
= BeginPaint(infoPtr
->hwndSelf
, &ps
);
9103 LISTVIEW_Refresh(infoPtr
, hdc
, ps
.fErase
? &ps
.rcPaint
: NULL
);
9104 EndPaint(infoPtr
->hwndSelf
, &ps
);
9113 * Paints/Repaints the listview control.
9116 * [I] infoPtr : valid pointer to the listview structure
9117 * [I] hdc : device context handle
9118 * [I] options : drawing options
9123 static LRESULT
LISTVIEW_PrintClient(LISTVIEW_INFO
*infoPtr
, HDC hdc
, DWORD options
)
9125 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc
, options
);
9127 if ((options
& PRF_CHECKVISIBLE
) && !IsWindowVisible(infoPtr
->hwndSelf
))
9130 if (options
& PRF_ERASEBKGND
)
9131 LISTVIEW_EraseBkgnd(infoPtr
, hdc
);
9133 if (options
& PRF_CLIENT
)
9134 LISTVIEW_Paint(infoPtr
, hdc
);
9142 * Processes double click messages (right mouse button).
9145 * [I] infoPtr : valid pointer to the listview structure
9146 * [I] wKey : key flag
9147 * [I] x,y : mouse coordinate
9152 static LRESULT
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9154 LVHITTESTINFO lvHitTestInfo
;
9156 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9158 /* send NM_RELEASEDCAPTURE notification */
9159 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9161 /* send NM_RDBLCLK notification */
9162 lvHitTestInfo
.pt
.x
= x
;
9163 lvHitTestInfo
.pt
.y
= y
;
9164 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9165 notify_click(infoPtr
, NM_RDBLCLK
, &lvHitTestInfo
);
9172 * Processes mouse down messages (right mouse button).
9175 * [I] infoPtr : valid pointer to the listview structure
9176 * [I] wKey : key flag
9177 * [I] x,y : mouse coordinate
9182 static LRESULT
LISTVIEW_RButtonDown(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9184 LVHITTESTINFO lvHitTestInfo
;
9187 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9189 /* send NM_RELEASEDCAPTURE notification */
9190 if (!notify(infoPtr
, NM_RELEASEDCAPTURE
)) return 0;
9192 /* make sure the listview control window has the focus */
9193 if (!infoPtr
->bFocus
) SetFocus(infoPtr
->hwndSelf
);
9195 /* set right button down flag */
9196 infoPtr
->bRButtonDown
= TRUE
;
9198 /* determine the index of the selected item */
9199 lvHitTestInfo
.pt
.x
= x
;
9200 lvHitTestInfo
.pt
.y
= y
;
9201 nItem
= LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, TRUE
);
9203 if ((nItem
>= 0) && (nItem
< infoPtr
->nItemCount
))
9205 LISTVIEW_SetItemFocus(infoPtr
, nItem
);
9206 if (!((wKey
& MK_SHIFT
) || (wKey
& MK_CONTROL
)) &&
9207 !LISTVIEW_GetItemState(infoPtr
, nItem
, LVIS_SELECTED
))
9208 LISTVIEW_SetSelection(infoPtr
, nItem
);
9212 LISTVIEW_DeselectAll(infoPtr
);
9220 * Processes mouse up messages (right mouse button).
9223 * [I] infoPtr : valid pointer to the listview structure
9224 * [I] wKey : key flag
9225 * [I] x,y : mouse coordinate
9230 static LRESULT
LISTVIEW_RButtonUp(LISTVIEW_INFO
*infoPtr
, WORD wKey
, INT x
, INT y
)
9232 LVHITTESTINFO lvHitTestInfo
;
9235 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey
, x
, y
);
9237 if (!infoPtr
->bRButtonDown
) return 0;
9239 /* set button flag */
9240 infoPtr
->bRButtonDown
= FALSE
;
9242 /* Send NM_RClICK notification */
9243 lvHitTestInfo
.pt
.x
= x
;
9244 lvHitTestInfo
.pt
.y
= y
;
9245 LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, TRUE
, FALSE
);
9246 if (!notify_click(infoPtr
, NM_RCLICK
, &lvHitTestInfo
)) return 0;
9248 /* Change to screen coordinate for WM_CONTEXTMENU */
9249 pt
= lvHitTestInfo
.pt
;
9250 ClientToScreen(infoPtr
->hwndSelf
, &pt
);
9252 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9253 SendMessageW(infoPtr
->hwndSelf
, WM_CONTEXTMENU
,
9254 (WPARAM
)infoPtr
->hwndSelf
, MAKELPARAM(pt
.x
, pt
.y
));
9265 * [I] infoPtr : valid pointer to the listview structure
9266 * [I] hwnd : window handle of window containing the cursor
9267 * [I] nHittest : hit-test code
9268 * [I] wMouseMsg : ideintifier of the mouse message
9271 * TRUE if cursor is set
9274 static BOOL
LISTVIEW_SetCursor(const LISTVIEW_INFO
*infoPtr
, HWND hwnd
, UINT nHittest
, UINT wMouseMsg
)
9276 LVHITTESTINFO lvHitTestInfo
;
9278 if(!(LISTVIEW_isHotTracking(infoPtr
))) return FALSE
;
9280 if(!infoPtr
->hHotCursor
) return FALSE
;
9282 GetCursorPos(&lvHitTestInfo
.pt
);
9283 if (LISTVIEW_HitTest(infoPtr
, &lvHitTestInfo
, FALSE
, FALSE
) < 0) return FALSE
;
9285 SetCursor(infoPtr
->hHotCursor
);
9295 * [I] infoPtr : valid pointer to the listview structure
9296 * [I] hwndLoseFocus : handle of previously focused window
9301 static LRESULT
LISTVIEW_SetFocus(LISTVIEW_INFO
*infoPtr
, HWND hwndLoseFocus
)
9303 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus
);
9305 /* if we have the focus already, there's nothing to do */
9306 if (infoPtr
->bFocus
) return 0;
9308 /* send NM_SETFOCUS notification */
9309 if (!notify(infoPtr
, NM_SETFOCUS
)) return 0;
9311 /* set window focus flag */
9312 infoPtr
->bFocus
= TRUE
;
9314 /* put the focus rect back on */
9315 LISTVIEW_ShowFocusRect(infoPtr
, TRUE
);
9317 /* redraw all visible selected items */
9318 LISTVIEW_InvalidateSelectedItems(infoPtr
);
9328 * [I] infoPtr : valid pointer to the listview structure
9329 * [I] fRedraw : font handle
9330 * [I] fRedraw : redraw flag
9335 static LRESULT
LISTVIEW_SetFont(LISTVIEW_INFO
*infoPtr
, HFONT hFont
, WORD fRedraw
)
9337 HFONT oldFont
= infoPtr
->hFont
;
9339 TRACE("(hfont=%p,redraw=%hu)\n", hFont
, fRedraw
);
9341 infoPtr
->hFont
= hFont
? hFont
: infoPtr
->hDefaultFont
;
9342 if (infoPtr
->hFont
== oldFont
) return 0;
9344 LISTVIEW_SaveTextMetrics(infoPtr
);
9346 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
)
9348 SendMessageW(infoPtr
->hwndHeader
, WM_SETFONT
, (WPARAM
)hFont
, MAKELPARAM(fRedraw
, 0));
9349 LISTVIEW_UpdateSize(infoPtr
);
9350 LISTVIEW_UpdateScroll(infoPtr
);
9353 if (fRedraw
) LISTVIEW_InvalidateList(infoPtr
);
9360 * Message handling for WM_SETREDRAW.
9361 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9364 * [I] infoPtr : valid pointer to the listview structure
9365 * [I] bRedraw: state of redraw flag
9368 * DefWinProc return value
9370 static LRESULT
LISTVIEW_SetRedraw(LISTVIEW_INFO
*infoPtr
, BOOL bRedraw
)
9372 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr
->bRedraw
, bRedraw
);
9374 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9375 if ((infoPtr
->bRedraw
&& bRedraw
) || (!infoPtr
->bRedraw
&& !bRedraw
)) return 0;
9377 infoPtr
->bRedraw
= bRedraw
;
9379 if(!bRedraw
) return 0;
9381 if (is_autoarrange(infoPtr
))
9382 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9383 LISTVIEW_UpdateScroll(infoPtr
);
9385 /* despite what the WM_SETREDRAW docs says, apps expect us
9386 * to invalidate the listview here... stupid! */
9387 LISTVIEW_InvalidateList(infoPtr
);
9394 * Resizes the listview control. This function processes WM_SIZE
9395 * messages. At this time, the width and height are not used.
9398 * [I] infoPtr : valid pointer to the listview structure
9399 * [I] Width : new width
9400 * [I] Height : new height
9405 static LRESULT
LISTVIEW_Size(LISTVIEW_INFO
*infoPtr
, int Width
, int Height
)
9407 RECT rcOld
= infoPtr
->rcList
;
9409 TRACE("(width=%d, height=%d)\n", Width
, Height
);
9411 LISTVIEW_UpdateSize(infoPtr
);
9412 if (EqualRect(&rcOld
, &infoPtr
->rcList
)) return 0;
9414 /* do not bother with display related stuff if we're not redrawing */
9415 if (!is_redrawing(infoPtr
)) return 0;
9417 if (is_autoarrange(infoPtr
))
9418 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9420 LISTVIEW_UpdateScroll(infoPtr
);
9422 /* refresh all only for lists whose height changed significantly */
9423 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
&&
9424 (rcOld
.bottom
- rcOld
.top
) / infoPtr
->nItemHeight
!=
9425 (infoPtr
->rcList
.bottom
- infoPtr
->rcList
.top
) / infoPtr
->nItemHeight
)
9426 LISTVIEW_InvalidateList(infoPtr
);
9433 * Sets the size information.
9436 * [I] infoPtr : valid pointer to the listview structure
9441 static void LISTVIEW_UpdateSize(LISTVIEW_INFO
*infoPtr
)
9443 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
9445 TRACE("uView=%d, rcList(old)=%s\n", uView
, wine_dbgstr_rect(&infoPtr
->rcList
));
9447 GetClientRect(infoPtr
->hwndSelf
, &infoPtr
->rcList
);
9449 if (uView
== LVS_LIST
)
9451 /* Apparently the "LIST" style is supposed to have the same
9452 * number of items in a column even if there is no scroll bar.
9453 * Since if a scroll bar already exists then the bottom is already
9454 * reduced, only reduce if the scroll bar does not currently exist.
9455 * The "2" is there to mimic the native control. I think it may be
9456 * related to either padding or edges. (GLA 7/2002)
9458 if (!(GetWindowLongW(infoPtr
->hwndSelf
, GWL_STYLE
) & WS_HSCROLL
))
9459 infoPtr
->rcList
.bottom
-= GetSystemMetrics(SM_CYHSCROLL
);
9460 infoPtr
->rcList
.bottom
= max (infoPtr
->rcList
.bottom
- 2, 0);
9462 else if (uView
== LVS_REPORT
)
9467 hl
.prc
= &infoPtr
->rcList
;
9469 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9470 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp
.flags
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9471 SetWindowPos(wp
.hwnd
, wp
.hwndInsertAfter
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9472 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9473 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9474 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp
.x
, wp
.y
, wp
.cx
, wp
.cy
);
9476 infoPtr
->rcList
.top
= max(wp
.cy
, 0);
9477 infoPtr
->rcList
.top
+= (infoPtr
->dwLvExStyle
& LVS_EX_GRIDLINES
) ? 2 : 0;
9480 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr
->rcList
));
9485 * Processes WM_STYLECHANGED messages.
9488 * [I] infoPtr : valid pointer to the listview structure
9489 * [I] wStyleType : window style type (normal or extended)
9490 * [I] lpss : window style information
9495 static INT
LISTVIEW_StyleChanged(LISTVIEW_INFO
*infoPtr
, WPARAM wStyleType
,
9496 const STYLESTRUCT
*lpss
)
9498 UINT uNewView
= lpss
->styleNew
& LVS_TYPEMASK
;
9499 UINT uOldView
= lpss
->styleOld
& LVS_TYPEMASK
;
9502 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9503 wStyleType
, lpss
->styleOld
, lpss
->styleNew
);
9505 if (wStyleType
!= GWL_STYLE
) return 0;
9507 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9508 /* what if LVS_OWNERDATA changed? */
9509 /* or LVS_SINGLESEL */
9510 /* or LVS_SORT{AS,DES}CENDING */
9512 infoPtr
->dwStyle
= lpss
->styleNew
;
9514 if (((lpss
->styleOld
& WS_HSCROLL
) != 0)&&
9515 ((lpss
->styleNew
& WS_HSCROLL
) == 0))
9516 ShowScrollBar(infoPtr
->hwndSelf
, SB_HORZ
, FALSE
);
9518 if (((lpss
->styleOld
& WS_VSCROLL
) != 0)&&
9519 ((lpss
->styleNew
& WS_VSCROLL
) == 0))
9520 ShowScrollBar(infoPtr
->hwndSelf
, SB_VERT
, FALSE
);
9522 if (uNewView
!= uOldView
)
9524 SIZE oldIconSize
= infoPtr
->iconSize
;
9527 SendMessageW(infoPtr
->hwndEdit
, WM_KILLFOCUS
, 0, 0);
9528 ShowWindow(infoPtr
->hwndHeader
, SW_HIDE
);
9530 ShowScrollBar(infoPtr
->hwndSelf
, SB_BOTH
, FALSE
);
9531 SetRectEmpty(&infoPtr
->rcFocus
);
9533 himl
= (uNewView
== LVS_ICON
? infoPtr
->himlNormal
: infoPtr
->himlSmall
);
9534 set_icon_size(&infoPtr
->iconSize
, himl
, uNewView
!= LVS_ICON
);
9536 if (uNewView
== LVS_ICON
)
9538 if ((infoPtr
->iconSize
.cx
!= oldIconSize
.cx
) || (infoPtr
->iconSize
.cy
!= oldIconSize
.cy
))
9540 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9541 oldIconSize
.cx
, oldIconSize
.cy
, infoPtr
->iconSize
.cx
, infoPtr
->iconSize
.cy
);
9542 LISTVIEW_SetIconSpacing(infoPtr
, 0, 0);
9545 else if (uNewView
== LVS_REPORT
)
9550 hl
.prc
= &infoPtr
->rcList
;
9552 SendMessageW( infoPtr
->hwndHeader
, HDM_LAYOUT
, 0, (LPARAM
)&hl
);
9553 SetWindowPos(infoPtr
->hwndHeader
, infoPtr
->hwndSelf
, wp
.x
, wp
.y
, wp
.cx
, wp
.cy
,
9554 wp
.flags
| ((infoPtr
->dwStyle
& LVS_NOCOLUMNHEADER
)
9555 ? SWP_HIDEWINDOW
: SWP_SHOWWINDOW
));
9558 LISTVIEW_UpdateItemSize(infoPtr
);
9561 if (uNewView
== LVS_REPORT
)
9563 if ((lpss
->styleOld
^ lpss
->styleNew
) & LVS_NOCOLUMNHEADER
)
9565 if (lpss
->styleNew
& LVS_NOCOLUMNHEADER
)
9567 /* Turn off the header control */
9568 style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
);
9569 TRACE("Hide header control, was 0x%08x\n", style
);
9570 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, style
| HDS_HIDDEN
);
9572 /* Turn on the header control */
9573 if ((style
= GetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
)) & HDS_HIDDEN
)
9575 TRACE("Show header control, was 0x%08x\n", style
);
9576 SetWindowLongW(infoPtr
->hwndHeader
, GWL_STYLE
, (style
& ~HDS_HIDDEN
) | WS_VISIBLE
);
9582 if ( (uNewView
== LVS_ICON
|| uNewView
== LVS_SMALLICON
) &&
9583 (uNewView
!= uOldView
|| ((lpss
->styleNew
^ lpss
->styleOld
) & LVS_ALIGNMASK
)) )
9584 LISTVIEW_Arrange(infoPtr
, LVA_DEFAULT
);
9586 /* update the size of the client area */
9587 LISTVIEW_UpdateSize(infoPtr
);
9589 /* add scrollbars if needed */
9590 LISTVIEW_UpdateScroll(infoPtr
);
9592 /* invalidate client area + erase background */
9593 LISTVIEW_InvalidateList(infoPtr
);
9600 * Window procedure of the listview control.
9603 static LRESULT WINAPI
9604 LISTVIEW_WindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
9606 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(hwnd
, 0);
9608 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg
, wParam
, lParam
);
9610 if (!infoPtr
&& (uMsg
!= WM_NCCREATE
))
9611 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
9615 case LVM_APPROXIMATEVIEWRECT
:
9616 return LISTVIEW_ApproximateViewRect(infoPtr
, (INT
)wParam
,
9617 LOWORD(lParam
), HIWORD(lParam
));
9619 return LISTVIEW_Arrange(infoPtr
, (INT
)wParam
);
9621 /* case LVM_CANCELEDITLABEL: */
9623 case LVM_CREATEDRAGIMAGE
:
9624 return (LRESULT
)LISTVIEW_CreateDragImage(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9626 case LVM_DELETEALLITEMS
:
9627 return LISTVIEW_DeleteAllItems(infoPtr
, FALSE
);
9629 case LVM_DELETECOLUMN
:
9630 return LISTVIEW_DeleteColumn(infoPtr
, (INT
)wParam
);
9632 case LVM_DELETEITEM
:
9633 return LISTVIEW_DeleteItem(infoPtr
, (INT
)wParam
);
9635 case LVM_EDITLABELW
:
9636 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, TRUE
);
9638 case LVM_EDITLABELA
:
9639 return (LRESULT
)LISTVIEW_EditLabelT(infoPtr
, (INT
)wParam
, FALSE
);
9641 /* case LVM_ENABLEGROUPVIEW: */
9643 case LVM_ENSUREVISIBLE
:
9644 return LISTVIEW_EnsureVisible(infoPtr
, (INT
)wParam
, (BOOL
)lParam
);
9647 return LISTVIEW_FindItemW(infoPtr
, (INT
)wParam
, (LPLVFINDINFOW
)lParam
);
9650 return LISTVIEW_FindItemA(infoPtr
, (INT
)wParam
, (LPLVFINDINFOA
)lParam
);
9652 case LVM_GETBKCOLOR
:
9653 return infoPtr
->clrBk
;
9655 /* case LVM_GETBKIMAGE: */
9657 case LVM_GETCALLBACKMASK
:
9658 return infoPtr
->uCallbackMask
;
9660 case LVM_GETCOLUMNA
:
9661 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9663 case LVM_GETCOLUMNW
:
9664 return LISTVIEW_GetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9666 case LVM_GETCOLUMNORDERARRAY
:
9667 return LISTVIEW_GetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9669 case LVM_GETCOLUMNWIDTH
:
9670 return LISTVIEW_GetColumnWidth(infoPtr
, (INT
)wParam
);
9672 case LVM_GETCOUNTPERPAGE
:
9673 return LISTVIEW_GetCountPerPage(infoPtr
);
9675 case LVM_GETEDITCONTROL
:
9676 return (LRESULT
)infoPtr
->hwndEdit
;
9678 case LVM_GETEXTENDEDLISTVIEWSTYLE
:
9679 return infoPtr
->dwLvExStyle
;
9681 /* case LVM_GETGROUPINFO: */
9683 /* case LVM_GETGROUPMETRICS: */
9686 return (LRESULT
)infoPtr
->hwndHeader
;
9688 case LVM_GETHOTCURSOR
:
9689 return (LRESULT
)infoPtr
->hHotCursor
;
9691 case LVM_GETHOTITEM
:
9692 return infoPtr
->nHotItem
;
9694 case LVM_GETHOVERTIME
:
9695 return infoPtr
->dwHoverTime
;
9697 case LVM_GETIMAGELIST
:
9698 return (LRESULT
)LISTVIEW_GetImageList(infoPtr
, (INT
)wParam
);
9700 /* case LVM_GETINSERTMARK: */
9702 /* case LVM_GETINSERTMARKCOLOR: */
9704 /* case LVM_GETINSERTMARKRECT: */
9706 case LVM_GETISEARCHSTRINGA
:
9707 case LVM_GETISEARCHSTRINGW
:
9708 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9712 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9715 return LISTVIEW_GetItemExtT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9717 case LVM_GETITEMCOUNT
:
9718 return infoPtr
->nItemCount
;
9720 case LVM_GETITEMPOSITION
:
9721 return LISTVIEW_GetItemPosition(infoPtr
, (INT
)wParam
, (LPPOINT
)lParam
);
9723 case LVM_GETITEMRECT
:
9724 return LISTVIEW_GetItemRect(infoPtr
, (INT
)wParam
, (LPRECT
)lParam
);
9726 case LVM_GETITEMSPACING
:
9727 return LISTVIEW_GetItemSpacing(infoPtr
, (BOOL
)wParam
);
9729 case LVM_GETITEMSTATE
:
9730 return LISTVIEW_GetItemState(infoPtr
, (INT
)wParam
, (UINT
)lParam
);
9732 case LVM_GETITEMTEXTA
:
9733 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9735 case LVM_GETITEMTEXTW
:
9736 return LISTVIEW_GetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9738 case LVM_GETNEXTITEM
:
9739 return LISTVIEW_GetNextItem(infoPtr
, (INT
)wParam
, LOWORD(lParam
));
9741 case LVM_GETNUMBEROFWORKAREAS
:
9742 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9746 if (!lParam
) return FALSE
;
9747 if ((infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_REPORT
||
9748 (infoPtr
->dwStyle
& LVS_TYPEMASK
) == LVS_LIST
) return FALSE
;
9749 LISTVIEW_GetOrigin(infoPtr
, (LPPOINT
)lParam
);
9752 /* case LVM_GETOUTLINECOLOR: */
9754 /* case LVM_GETSELECTEDCOLUMN: */
9756 case LVM_GETSELECTEDCOUNT
:
9757 return LISTVIEW_GetSelectedCount(infoPtr
);
9759 case LVM_GETSELECTIONMARK
:
9760 return infoPtr
->nSelectionMark
;
9762 case LVM_GETSTRINGWIDTHA
:
9763 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, FALSE
);
9765 case LVM_GETSTRINGWIDTHW
:
9766 return LISTVIEW_GetStringWidthT(infoPtr
, (LPCWSTR
)lParam
, TRUE
);
9768 case LVM_GETSUBITEMRECT
:
9769 return LISTVIEW_GetSubItemRect(infoPtr
, (UINT
)wParam
, (LPRECT
)lParam
);
9771 case LVM_GETTEXTBKCOLOR
:
9772 return infoPtr
->clrTextBk
;
9774 case LVM_GETTEXTCOLOR
:
9775 return infoPtr
->clrText
;
9777 /* case LVM_GETTILEINFO: */
9779 /* case LVM_GETTILEVIEWINFO: */
9781 case LVM_GETTOOLTIPS
:
9782 if( !infoPtr
->hwndToolTip
)
9783 infoPtr
->hwndToolTip
= COMCTL32_CreateToolTip( hwnd
);
9784 return (LRESULT
)infoPtr
->hwndToolTip
;
9786 case LVM_GETTOPINDEX
:
9787 return LISTVIEW_GetTopIndex(infoPtr
);
9789 case LVM_GETUNICODEFORMAT
:
9790 return (infoPtr
->notifyFormat
== NFR_UNICODE
);
9792 /* case LVM_GETVIEW: */
9794 case LVM_GETVIEWRECT
:
9795 return LISTVIEW_GetViewRect(infoPtr
, (LPRECT
)lParam
);
9797 case LVM_GETWORKAREAS
:
9798 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9801 /* case LVM_HASGROUP: */
9804 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, FALSE
, FALSE
);
9806 case LVM_INSERTCOLUMNA
:
9807 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9809 case LVM_INSERTCOLUMNW
:
9810 return LISTVIEW_InsertColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9812 /* case LVM_INSERTGROUP: */
9814 /* case LVM_INSERTGROUPSORTED: */
9816 case LVM_INSERTITEMA
:
9817 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9819 case LVM_INSERTITEMW
:
9820 return LISTVIEW_InsertItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9822 /* case LVM_INSERTMARKHITTEST: */
9824 /* case LVM_ISGROUPVIEWENABLED: */
9826 /* case LVM_MAPIDTOINDEX: */
9828 /* case LVM_MAPINDEXTOID: */
9830 /* case LVM_MOVEGROUP: */
9832 /* case LVM_MOVEITEMTOGROUP: */
9834 case LVM_REDRAWITEMS
:
9835 return LISTVIEW_RedrawItems(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9837 /* case LVM_REMOVEALLGROUPS: */
9839 /* case LVM_REMOVEGROUP: */
9842 return LISTVIEW_Scroll(infoPtr
, (INT
)wParam
, (INT
)lParam
);
9844 case LVM_SETBKCOLOR
:
9845 return LISTVIEW_SetBkColor(infoPtr
, (COLORREF
)lParam
);
9847 /* case LVM_SETBKIMAGE: */
9849 case LVM_SETCALLBACKMASK
:
9850 infoPtr
->uCallbackMask
= (UINT
)wParam
;
9853 case LVM_SETCOLUMNA
:
9854 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, FALSE
);
9856 case LVM_SETCOLUMNW
:
9857 return LISTVIEW_SetColumnT(infoPtr
, (INT
)wParam
, (LPLVCOLUMNW
)lParam
, TRUE
);
9859 case LVM_SETCOLUMNORDERARRAY
:
9860 return LISTVIEW_SetColumnOrderArray(infoPtr
, (INT
)wParam
, (LPINT
)lParam
);
9862 case LVM_SETCOLUMNWIDTH
:
9863 return LISTVIEW_SetColumnWidth(infoPtr
, (INT
)wParam
, (short)LOWORD(lParam
));
9865 case LVM_SETEXTENDEDLISTVIEWSTYLE
:
9866 return LISTVIEW_SetExtendedListViewStyle(infoPtr
, (DWORD
)wParam
, (DWORD
)lParam
);
9868 /* case LVM_SETGROUPINFO: */
9870 /* case LVM_SETGROUPMETRICS: */
9872 case LVM_SETHOTCURSOR
:
9873 return (LRESULT
)LISTVIEW_SetHotCursor(infoPtr
, (HCURSOR
)lParam
);
9875 case LVM_SETHOTITEM
:
9876 return LISTVIEW_SetHotItem(infoPtr
, (INT
)wParam
);
9878 case LVM_SETHOVERTIME
:
9879 return LISTVIEW_SetHoverTime(infoPtr
, (DWORD
)wParam
);
9881 case LVM_SETICONSPACING
:
9882 return LISTVIEW_SetIconSpacing(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
9884 case LVM_SETIMAGELIST
:
9885 return (LRESULT
)LISTVIEW_SetImageList(infoPtr
, (INT
)wParam
, (HIMAGELIST
)lParam
);
9887 /* case LVM_SETINFOTIP: */
9889 /* case LVM_SETINSERTMARK: */
9891 /* case LVM_SETINSERTMARKCOLOR: */
9894 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, FALSE
);
9897 return LISTVIEW_SetItemT(infoPtr
, (LPLVITEMW
)lParam
, TRUE
);
9899 case LVM_SETITEMCOUNT
:
9900 return LISTVIEW_SetItemCount(infoPtr
, (INT
)wParam
, (DWORD
)lParam
);
9902 case LVM_SETITEMPOSITION
:
9905 pt
.x
= (short)LOWORD(lParam
);
9906 pt
.y
= (short)HIWORD(lParam
);
9907 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, pt
);
9910 case LVM_SETITEMPOSITION32
:
9911 if (lParam
== 0) return FALSE
;
9912 return LISTVIEW_SetItemPosition(infoPtr
, (INT
)wParam
, *((POINT
*)lParam
));
9914 case LVM_SETITEMSTATE
:
9915 return LISTVIEW_SetItemState(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
);
9917 case LVM_SETITEMTEXTA
:
9918 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, FALSE
);
9920 case LVM_SETITEMTEXTW
:
9921 return LISTVIEW_SetItemTextT(infoPtr
, (INT
)wParam
, (LPLVITEMW
)lParam
, TRUE
);
9923 /* case LVM_SETOUTLINECOLOR: */
9925 /* case LVM_SETSELECTEDCOLUMN: */
9927 case LVM_SETSELECTIONMARK
:
9928 return LISTVIEW_SetSelectionMark(infoPtr
, (INT
)lParam
);
9930 case LVM_SETTEXTBKCOLOR
:
9931 return LISTVIEW_SetTextBkColor(infoPtr
, (COLORREF
)lParam
);
9933 case LVM_SETTEXTCOLOR
:
9934 return LISTVIEW_SetTextColor(infoPtr
, (COLORREF
)lParam
);
9936 /* case LVM_SETTILEINFO: */
9938 /* case LVM_SETTILEVIEWINFO: */
9940 /* case LVM_SETTILEWIDTH: */
9942 case LVM_SETTOOLTIPS
:
9943 return (LRESULT
)LISTVIEW_SetToolTips(infoPtr
, (HWND
)lParam
);
9945 case LVM_SETUNICODEFORMAT
:
9946 return LISTVIEW_SetUnicodeFormat(infoPtr
, wParam
);
9948 /* case LVM_SETVIEW: */
9950 /* case LVM_SETWORKAREAS: */
9952 /* case LVM_SORTGROUPS: */
9955 return LISTVIEW_SortItems(infoPtr
, (PFNLVCOMPARE
)lParam
, (LPARAM
)wParam
);
9957 /* LVM_SORTITEMSEX: */
9959 case LVM_SUBITEMHITTEST
:
9960 return LISTVIEW_HitTest(infoPtr
, (LPLVHITTESTINFO
)lParam
, TRUE
, FALSE
);
9963 return LISTVIEW_Update(infoPtr
, (INT
)wParam
);
9966 return LISTVIEW_ProcessLetterKeys( infoPtr
, wParam
, lParam
);
9969 return LISTVIEW_Command(infoPtr
, wParam
, lParam
);
9972 return LISTVIEW_NCCreate(hwnd
, (LPCREATESTRUCTW
)lParam
);
9975 return LISTVIEW_Create(hwnd
, (LPCREATESTRUCTW
)lParam
);
9978 return LISTVIEW_Destroy(infoPtr
);
9981 return LISTVIEW_Enable(infoPtr
, (BOOL
)wParam
);
9984 return LISTVIEW_EraseBkgnd(infoPtr
, (HDC
)wParam
);
9987 return DLGC_WANTCHARS
| DLGC_WANTARROWS
;
9990 return (LRESULT
)infoPtr
->hFont
;
9993 return LISTVIEW_HScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
9996 return LISTVIEW_KeyDown(infoPtr
, (INT
)wParam
, (LONG
)lParam
);
9999 return LISTVIEW_KillFocus(infoPtr
);
10001 case WM_LBUTTONDBLCLK
:
10002 return LISTVIEW_LButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10004 case WM_LBUTTONDOWN
:
10005 return LISTVIEW_LButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10008 return LISTVIEW_LButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10011 return LISTVIEW_MouseMove (infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10013 case WM_MOUSEHOVER
:
10014 return LISTVIEW_MouseHover(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10017 return LISTVIEW_NCDestroy(infoPtr
);
10020 if (LISTVIEW_NCPaint(infoPtr
, (HRGN
)wParam
))
10025 if (lParam
&& ((LPNMHDR
)lParam
)->hwndFrom
== infoPtr
->hwndHeader
)
10026 return LISTVIEW_HeaderNotification(infoPtr
, (LPNMHEADERW
)lParam
);
10029 case WM_NOTIFYFORMAT
:
10030 return LISTVIEW_NotifyFormat(infoPtr
, (HWND
)wParam
, (INT
)lParam
);
10032 case WM_PRINTCLIENT
:
10033 return LISTVIEW_PrintClient(infoPtr
, (HDC
)wParam
, (DWORD
)lParam
);
10036 return LISTVIEW_Paint(infoPtr
, (HDC
)wParam
);
10038 case WM_RBUTTONDBLCLK
:
10039 return LISTVIEW_RButtonDblClk(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10041 case WM_RBUTTONDOWN
:
10042 return LISTVIEW_RButtonDown(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10045 return LISTVIEW_RButtonUp(infoPtr
, (WORD
)wParam
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
10048 if(LISTVIEW_SetCursor(infoPtr
, (HWND
)wParam
, LOWORD(lParam
), HIWORD(lParam
)))
10053 return LISTVIEW_SetFocus(infoPtr
, (HWND
)wParam
);
10056 return LISTVIEW_SetFont(infoPtr
, (HFONT
)wParam
, (WORD
)lParam
);
10059 return LISTVIEW_SetRedraw(infoPtr
, (BOOL
)wParam
);
10062 return LISTVIEW_Size(infoPtr
, (short)LOWORD(lParam
), (short)HIWORD(lParam
));
10064 case WM_STYLECHANGED
:
10065 return LISTVIEW_StyleChanged(infoPtr
, wParam
, (LPSTYLESTRUCT
)lParam
);
10067 case WM_SYSCOLORCHANGE
:
10068 COMCTL32_RefreshSysColors();
10071 /* case WM_TIMER: */
10072 case WM_THEMECHANGED
:
10073 return LISTVIEW_ThemeChanged(infoPtr
);
10076 return LISTVIEW_VScroll(infoPtr
, (INT
)LOWORD(wParam
), 0, (HWND
)lParam
);
10078 case WM_MOUSEWHEEL
:
10079 if (wParam
& (MK_SHIFT
| MK_CONTROL
))
10080 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10081 return LISTVIEW_MouseWheel(infoPtr
, (short int)HIWORD(wParam
));
10083 case WM_WINDOWPOSCHANGED
:
10084 if (!(((WINDOWPOS
*)lParam
)->flags
& SWP_NOSIZE
))
10086 UINT uView
= infoPtr
->dwStyle
& LVS_TYPEMASK
;
10087 SetWindowPos(infoPtr
->hwndSelf
, 0, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
|
10088 SWP_NOZORDER
| SWP_NOMOVE
| SWP_NOSIZE
);
10090 if ((infoPtr
->dwStyle
& LVS_OWNERDRAWFIXED
) && (uView
== LVS_REPORT
))
10092 MEASUREITEMSTRUCT mis
;
10093 mis
.CtlType
= ODT_LISTVIEW
;
10094 mis
.CtlID
= GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_ID
);
10098 mis
.itemHeight
= infoPtr
->nItemHeight
;
10099 SendMessageW(infoPtr
->hwndNotify
, WM_MEASUREITEM
, mis
.CtlID
, (LPARAM
)&mis
);
10100 if (infoPtr
->nItemHeight
!= max(mis
.itemHeight
, 1))
10101 infoPtr
->nMeasureItemHeight
= infoPtr
->nItemHeight
= max(mis
.itemHeight
, 1);
10104 LISTVIEW_UpdateSize(infoPtr
);
10105 LISTVIEW_UpdateScroll(infoPtr
);
10107 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10109 /* case WM_WININICHANGE: */
10112 if ((uMsg
>= WM_USER
) && (uMsg
< WM_APP
) && !COMCTL32_IsReflectedMessage(uMsg
))
10113 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg
, wParam
, lParam
);
10116 /* call default window procedure */
10117 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
10124 * Registers the window class.
10132 void LISTVIEW_Register(void)
10134 WNDCLASSW wndClass
;
10136 ZeroMemory(&wndClass
, sizeof(WNDCLASSW
));
10137 wndClass
.style
= CS_GLOBALCLASS
| CS_DBLCLKS
;
10138 wndClass
.lpfnWndProc
= LISTVIEW_WindowProc
;
10139 wndClass
.cbClsExtra
= 0;
10140 wndClass
.cbWndExtra
= sizeof(LISTVIEW_INFO
*);
10141 wndClass
.hCursor
= LoadCursorW(0, (LPWSTR
)IDC_ARROW
);
10142 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_WINDOW
+ 1);
10143 wndClass
.lpszClassName
= WC_LISTVIEWW
;
10144 RegisterClassW(&wndClass
);
10149 * Unregisters the window class.
10157 void LISTVIEW_Unregister(void)
10159 UnregisterClassW(WC_LISTVIEWW
, NULL
);
10164 * Handle any WM_COMMAND messages
10167 * [I] infoPtr : valid pointer to the listview structure
10168 * [I] wParam : the first message parameter
10169 * [I] lParam : the second message parameter
10174 static LRESULT
LISTVIEW_Command(const LISTVIEW_INFO
*infoPtr
, WPARAM wParam
, LPARAM lParam
)
10176 switch (HIWORD(wParam
))
10181 * Adjust the edit window size
10183 WCHAR buffer
[1024];
10184 HDC hdc
= GetDC(infoPtr
->hwndEdit
);
10185 HFONT hFont
, hOldFont
= 0;
10189 if (!infoPtr
->hwndEdit
|| !hdc
) return 0;
10190 GetWindowTextW(infoPtr
->hwndEdit
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]));
10191 GetWindowRect(infoPtr
->hwndEdit
, &rect
);
10193 /* Select font to get the right dimension of the string */
10194 hFont
= (HFONT
)SendMessageW(infoPtr
->hwndEdit
, WM_GETFONT
, 0, 0);
10197 hOldFont
= SelectObject(hdc
, hFont
);
10200 if (GetTextExtentPoint32W(hdc
, buffer
, lstrlenW(buffer
), &sz
))
10202 TEXTMETRICW textMetric
;
10204 /* Add Extra spacing for the next character */
10205 GetTextMetricsW(hdc
, &textMetric
);
10206 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10214 rect
.bottom
- rect
.top
,
10215 SWP_DRAWFRAME
|SWP_NOMOVE
);
10218 SelectObject(hdc
, hOldFont
);
10220 ReleaseDC(infoPtr
->hwndEdit
, hdc
);
10226 return SendMessageW (infoPtr
->hwndNotify
, WM_COMMAND
, wParam
, lParam
);
10235 * Subclassed edit control windproc function
10238 * [I] hwnd : the edit window handle
10239 * [I] uMsg : the message that is to be processed
10240 * [I] wParam : first message parameter
10241 * [I] lParam : second message parameter
10242 * [I] isW : TRUE if input is Unicode
10247 static LRESULT
EditLblWndProcT(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL isW
)
10249 LISTVIEW_INFO
*infoPtr
= (LISTVIEW_INFO
*)GetWindowLongPtrW(GetParent(hwnd
), 0);
10250 BOOL cancel
= FALSE
;
10252 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10253 hwnd
, uMsg
, wParam
, lParam
, isW
);
10257 case WM_GETDLGCODE
:
10258 return DLGC_WANTARROWS
| DLGC_WANTALLKEYS
;
10265 WNDPROC editProc
= infoPtr
->EditWndProc
;
10266 infoPtr
->EditWndProc
= 0;
10267 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (DWORD_PTR
)editProc
);
10268 return CallWindowProcT(editProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10272 if (VK_ESCAPE
== (INT
)wParam
)
10277 else if (VK_RETURN
== (INT
)wParam
)
10281 return CallWindowProcT(infoPtr
->EditWndProc
, hwnd
, uMsg
, wParam
, lParam
, isW
);
10284 /* kill the edit */
10285 if (infoPtr
->hwndEdit
)
10287 LPWSTR buffer
= NULL
;
10289 infoPtr
->hwndEdit
= 0;
10292 DWORD len
= isW
? GetWindowTextLengthW(hwnd
) : GetWindowTextLengthA(hwnd
);
10296 if ( (buffer
= Alloc((len
+1) * (isW
? sizeof(WCHAR
) : sizeof(CHAR
)))) )
10298 if (isW
) GetWindowTextW(hwnd
, buffer
, len
+1);
10299 else GetWindowTextA(hwnd
, (CHAR
*)buffer
, len
+1);
10303 LISTVIEW_EndEditLabelT(infoPtr
, buffer
, isW
);
10308 SendMessageW(hwnd
, WM_CLOSE
, 0, 0);
10314 * Subclassed edit control Unicode windproc function
10317 * [I] hwnd : the edit window handle
10318 * [I] uMsg : the message that is to be processed
10319 * [I] wParam : first message parameter
10320 * [I] lParam : second message parameter
10324 static LRESULT CALLBACK
EditLblWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10326 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, TRUE
);
10331 * Subclassed edit control ANSI windproc function
10334 * [I] hwnd : the edit window handle
10335 * [I] uMsg : the message that is to be processed
10336 * [I] wParam : first message parameter
10337 * [I] lParam : second message parameter
10341 static LRESULT CALLBACK
EditLblWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
10343 return EditLblWndProcT(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
10348 * Creates a subclassed edit control
10351 * [I] infoPtr : valid pointer to the listview structure
10352 * [I] text : initial text for the edit
10353 * [I] style : the window style
10354 * [I] isW : TRUE if input is Unicode
10358 static HWND
CreateEditLabelT(LISTVIEW_INFO
*infoPtr
, LPCWSTR text
, DWORD style
,
10359 INT x
, INT y
, INT width
, INT height
, BOOL isW
)
10361 WCHAR editName
[5] = { 'E', 'd', 'i', 't', '\0' };
10366 TEXTMETRICW textMetric
;
10367 HINSTANCE hinst
= (HINSTANCE
)GetWindowLongPtrW(infoPtr
->hwndSelf
, GWLP_HINSTANCE
);
10369 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text
, isW
), isW
);
10371 style
|= WS_CHILDWINDOW
|WS_CLIPSIBLINGS
|ES_LEFT
|ES_AUTOHSCROLL
|WS_BORDER
;
10372 hdc
= GetDC(infoPtr
->hwndSelf
);
10374 /* Select the font to get appropriate metric dimensions */
10375 if(infoPtr
->hFont
!= 0)
10376 hOldFont
= SelectObject(hdc
, infoPtr
->hFont
);
10378 /*Get String Length in pixels */
10379 GetTextExtentPoint32W(hdc
, text
, lstrlenW(text
), &sz
);
10381 /*Add Extra spacing for the next character */
10382 GetTextMetricsW(hdc
, &textMetric
);
10383 sz
.cx
+= (textMetric
.tmMaxCharWidth
* 2);
10385 if(infoPtr
->hFont
!= 0)
10386 SelectObject(hdc
, hOldFont
);
10388 ReleaseDC(infoPtr
->hwndSelf
, hdc
);
10390 hedit
= CreateWindowW(editName
, text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10392 hedit
= CreateWindowA("Edit", (LPCSTR
)text
, style
, x
, y
, sz
.cx
, height
, infoPtr
->hwndSelf
, 0, hinst
, 0);
10394 if (!hedit
) return 0;
10396 infoPtr
->EditWndProc
= (WNDPROC
)
10397 (isW
? SetWindowLongPtrW(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcW
) :
10398 SetWindowLongPtrA(hedit
, GWLP_WNDPROC
, (DWORD_PTR
)EditLblWndProcA
) );
10400 SendMessageW(hedit
, WM_SETFONT
, (WPARAM
)infoPtr
->hFont
, FALSE
);